summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorVlastimil Holer <vlastimil.holer@gmail.com>2013-02-19 16:30:06 +0100
committerVlastimil Holer <vlastimil.holer@gmail.com>2013-02-19 16:30:06 +0100
commit6b0652745129808dc0669354cb3e0dc53962d6ea (patch)
tree6ec307c7c245cf68d28ef05e3f1a9f7d075ff8bc /cloudinit
parente18f0f8a382729cc7c9f8df3ad0573af7eeb8f47 (diff)
parent174bc39e6b2c1cac3f73f67f25fad87cab16fa42 (diff)
downloadvyos-cloud-init-6b0652745129808dc0669354cb3e0dc53962d6ea.tar.gz
vyos-cloud-init-6b0652745129808dc0669354cb3e0dc53962d6ea.zip
Merged trunk lp:cloud-init
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/config/__init__.py4
-rw-r--r--cloudinit/config/cc_landscape.py2
-rw-r--r--cloudinit/config/cc_mounts.py4
-rw-r--r--cloudinit/config/cc_puppet.py10
-rw-r--r--cloudinit/config/cc_resolv_conf.py107
-rw-r--r--cloudinit/config/cc_salt_minion.py2
-rw-r--r--cloudinit/config/cc_update_etc_hosts.py5
-rw-r--r--cloudinit/distros/__init__.py125
-rw-r--r--cloudinit/distros/debian.py18
-rw-r--r--cloudinit/distros/rhel.py17
-rw-r--r--cloudinit/sources/DataSourceCloudStack.py82
-rw-r--r--cloudinit/sources/DataSourceConfigDrive.py2
-rw-r--r--cloudinit/sources/DataSourceNoCloud.py74
-rw-r--r--cloudinit/stages.py9
-rw-r--r--cloudinit/util.py39
15 files changed, 388 insertions, 112 deletions
diff --git a/cloudinit/config/__init__.py b/cloudinit/config/__init__.py
index 69a8cc68..d57453be 100644
--- a/cloudinit/config/__init__.py
+++ b/cloudinit/config/__init__.py
@@ -52,5 +52,7 @@ def fixup_module(mod, def_freq=PER_INSTANCE):
if freq and freq not in FREQUENCIES:
LOG.warn("Module %s has an unknown frequency %s", mod, freq)
if not hasattr(mod, 'distros'):
- setattr(mod, 'distros', None)
+ setattr(mod, 'distros', [])
+ if not hasattr(mod, 'osfamilies'):
+ setattr(mod, 'osfamilies', [])
return mod
diff --git a/cloudinit/config/cc_landscape.py b/cloudinit/config/cc_landscape.py
index 02610dd0..2efdff79 100644
--- a/cloudinit/config/cc_landscape.py
+++ b/cloudinit/config/cc_landscape.py
@@ -62,7 +62,7 @@ def handle(_name, cfg, cloud, log, _args):
if not ls_cloudcfg:
return
- cloud.distro.install_packages(["landscape-client"])
+ cloud.distro.install_packages(('landscape-client',))
merge_data = [
LSC_BUILTIN_CFG,
diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py
index cb772c86..9010d97f 100644
--- a/cloudinit/config/cc_mounts.py
+++ b/cloudinit/config/cc_mounts.py
@@ -24,8 +24,8 @@ import re
from cloudinit import util
-# Shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1
-SHORTNAME_FILTER = r"^[x]{0,1}[shv]d[a-z][0-9]*$"
+# Shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1, sr0
+SHORTNAME_FILTER = r"^([x]{0,1}[shv]d[a-z][0-9]*|sr[0-9]+)$"
SHORTNAME = re.compile(SHORTNAME_FILTER)
WS = re.compile("[%s]+" % (whitespace))
FSTAB_PATH = "/etc/fstab"
diff --git a/cloudinit/config/cc_puppet.py b/cloudinit/config/cc_puppet.py
index e9a0a0f4..471a1a8a 100644
--- a/cloudinit/config/cc_puppet.py
+++ b/cloudinit/config/cc_puppet.py
@@ -59,8 +59,14 @@ def handle(name, cfg, cloud, log, _args):
# Start by installing the puppet package if necessary...
install = util.get_cfg_option_bool(puppet_cfg, 'install', True)
- if install:
- cloud.distro.install_packages(["puppet"])
+ version = util.get_cfg_option_str(puppet_cfg, 'version', None)
+ if not install and version:
+ log.warn(("Puppet install set false but version supplied,"
+ " doing nothing."))
+ elif install:
+ log.debug(("Attempting to install puppet %s,"),
+ version if version else 'latest')
+ cloud.distro.install_packages(('puppet', version))
# ... and then update the puppet configuration
if 'conf' in puppet_cfg:
diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py
new file mode 100644
index 00000000..8a460f7e
--- /dev/null
+++ b/cloudinit/config/cc_resolv_conf.py
@@ -0,0 +1,107 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2013 Craig Tracey
+#
+# Author: Craig Tracey <craigtracey@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Note:
+# This module is intended to manage resolv.conf in environments where
+# early configuration of resolv.conf is necessary for further
+# bootstrapping and/or where configuration management such as puppet or
+# chef own dns configuration. As Debian/Ubuntu will, by default, utilize
+# resovlconf, and similarly RedHat will use sysconfig, this module is
+# likely to be of little use unless those are configured correctly.
+#
+# For RedHat with sysconfig, be sure to set PEERDNS=no for all DHCP
+# enabled NICs. And, in Ubuntu/Debian it is recommended that DNS
+# be configured via the standard /etc/network/interfaces configuration
+# file.
+#
+#
+# Usage Example:
+#
+# #cloud-config
+# manage_resolv_conf: true
+#
+# resolv_conf:
+# nameservers: ['8.8.4.4', '8.8.8.8']
+# searchdomains:
+# - foo.example.com
+# - bar.example.com
+# domain: example.com
+# options:
+# rotate: true
+# timeout: 1
+#
+
+
+from cloudinit.settings import PER_INSTANCE
+from cloudinit import templater
+from cloudinit import util
+
+frequency = PER_INSTANCE
+
+distros = ['fedora', 'rhel']
+
+
+def generate_resolv_conf(cloud, log, params):
+ template_fn = cloud.get_template_filename('resolv.conf')
+ if not template_fn:
+ log.warn("No template found, not rendering /etc/resolv.conf")
+ return
+
+ flags = []
+ false_flags = []
+ if 'options' in params:
+ for key, val in params['options'].iteritems():
+ if type(val) == bool:
+ if val:
+ flags.append(key)
+ else:
+ false_flags.append(key)
+
+ for flag in flags + false_flags:
+ del params['options'][flag]
+
+ params['flags'] = flags
+ log.debug("Writing resolv.conf from template %s" % template_fn)
+ templater.render_to_file(template_fn, '/etc/resolv.conf', params)
+
+
+def handle(name, cfg, _cloud, log, _args):
+ """
+ Handler for resolv.conf
+
+ @param name: The module name "resolv-conf" from cloud.cfg
+ @param cfg: A nested dict containing the entire cloud config contents.
+ @param cloud: The L{CloudInit} object in use.
+ @param log: Pre-initialized Python logger object to use for logging.
+ @param args: Any module arguments from cloud.cfg
+ """
+ if "manage_resolv_conf" not in cfg:
+ log.debug(("Skipping module named %s,"
+ " no 'manage_resolv_conf' key in configuration"), name)
+ return
+
+ if not util.get_cfg_option_bool(cfg, "manage_resolv_conf", False):
+ log.debug(("Skipping module named %s,"
+ " 'manage_resolv_conf' present but set to False"), name)
+ return
+
+ if not "resolv_conf" in cfg:
+ log.warn("manage_resolv_conf True but no parameters provided!")
+
+ generate_resolv_conf(_cloud, log, cfg["resolv_conf"])
+ return
diff --git a/cloudinit/config/cc_salt_minion.py b/cloudinit/config/cc_salt_minion.py
index f3eede18..53013dcb 100644
--- a/cloudinit/config/cc_salt_minion.py
+++ b/cloudinit/config/cc_salt_minion.py
@@ -31,7 +31,7 @@ def handle(name, cfg, cloud, log, _args):
salt_cfg = cfg['salt_minion']
# Start by installing the salt package ...
- cloud.distro.install_packages(["salt-minion"])
+ cloud.distro.install_packages(('salt-minion',))
# Ensure we can configure files at the right dir
config_dir = salt_cfg.get("config_dir", '/etc/salt')
diff --git a/cloudinit/config/cc_update_etc_hosts.py b/cloudinit/config/cc_update_etc_hosts.py
index 96103615..d3dd1f32 100644
--- a/cloudinit/config/cc_update_etc_hosts.py
+++ b/cloudinit/config/cc_update_etc_hosts.py
@@ -37,10 +37,11 @@ def handle(name, cfg, cloud, log, _args):
# Render from a template file
tpl_fn_name = cloud.get_template_filename("hosts.%s" %
- (cloud.distro.name))
+ (cloud.distro.osfamily))
if not tpl_fn_name:
raise RuntimeError(("No hosts template could be"
- " found for distro %s") % (cloud.distro.name))
+ " found for distro %s") %
+ (cloud.distro.osfamily))
templater.render_to_file(tpl_fn_name, '/etc/hosts',
{'hostname': hostname, 'fqdn': fqdn})
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index ae81be50..35577466 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -35,6 +35,11 @@ from cloudinit import util
from cloudinit.distros.parsers import hosts
+OSFAMILIES = {
+ 'debian': ['debian', 'ubuntu'],
+ 'redhat': ['fedora', 'rhel']
+}
+
LOG = logging.getLogger(__name__)
@@ -147,6 +152,16 @@ class Distro(object):
def _select_hostname(self, hostname, fqdn):
raise NotImplementedError()
+ @staticmethod
+ def expand_osfamily(family_list):
+ distros = []
+ for family in family_list:
+ if not family in OSFAMILIES:
+ raise ValueError("No distibutions found for osfamily %s"
+ % (family))
+ distros.extend(OSFAMILIES[family])
+ return distros
+
def update_hostname(self, hostname, fqdn, prev_hostname_fn):
applying_hostname = hostname
@@ -301,22 +316,26 @@ class Distro(object):
"no_create_home": "-M",
}
+ redact_fields = ['passwd']
+
# Now check the value and create the command
for option in kwargs:
value = kwargs[option]
if option in adduser_opts and value \
and isinstance(value, str):
adduser_cmd.extend([adduser_opts[option], value])
-
- # Redact the password field from the logs
- if option != "password":
- x_adduser_cmd.extend([adduser_opts[option], value])
- else:
+ # Redact certain fields from the logs
+ if option in redact_fields:
x_adduser_cmd.extend([adduser_opts[option], 'REDACTED'])
-
+ else:
+ x_adduser_cmd.extend([adduser_opts[option], value])
elif option in adduser_opts_flags and value:
adduser_cmd.append(adduser_opts_flags[option])
- x_adduser_cmd.append(adduser_opts_flags[option])
+ # Redact certain fields from the logs
+ if option in redact_fields:
+ x_adduser_cmd.append('REDACTED')
+ else:
+ x_adduser_cmd.append(adduser_opts_flags[option])
# Default to creating home directory unless otherwise directed
# Also, we do not create home directories for system users.
@@ -338,10 +357,9 @@ class Distro(object):
if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']:
self.set_passwd(name, kwargs['plain_text_passwd'])
- # Default locking down the account.
- if ('lock_passwd' not in kwargs and
- ('lock_passwd' in kwargs and kwargs['lock_passwd']) or
- 'system' not in kwargs):
+ # Default locking down the account. 'lock_passwd' defaults to True.
+ # lock account unless lock_password is False.
+ if kwargs.get('lock_passwd', True):
try:
util.subp(['passwd', '--lock', name])
except Exception as e:
@@ -706,41 +724,68 @@ def _normalize_users(u_cfg, def_user_cfg=None):
def normalize_users_groups(cfg, distro):
if not cfg:
cfg = {}
+
users = {}
groups = {}
if 'groups' in cfg:
groups = _normalize_groups(cfg['groups'])
- # Handle the previous style of doing this...
- old_user = None
+ # Handle the previous style of doing this where the first user
+ # overrides the concept of the default user if provided in the user: XYZ
+ # format.
+ old_user = {}
if 'user' in cfg and cfg['user']:
- old_user = str(cfg['user'])
- if not 'users' in cfg:
- cfg['users'] = old_user
- old_user = None
- if 'users' in cfg:
- default_user_config = None
- try:
- default_user_config = distro.get_default_user()
- except NotImplementedError:
- LOG.warn(("Distro has not implemented default user "
- "access. No default user will be normalized."))
- base_users = cfg['users']
- if old_user:
- if isinstance(base_users, (list)):
- if len(base_users):
- # The old user replaces user[0]
- base_users[0] = {'name': old_user}
- else:
- # Just add it on at the end...
- base_users.append({'name': old_user})
- elif isinstance(base_users, (dict)):
- if old_user not in base_users:
- base_users[old_user] = True
- elif isinstance(base_users, (str, basestring)):
- # Just append it on to be re-parsed later
- base_users += ",%s" % (old_user)
- users = _normalize_users(base_users, default_user_config)
+ old_user = cfg['user']
+ # Translate it into the format that is more useful
+ # going forward
+ if isinstance(old_user, (basestring, str)):
+ old_user = {
+ 'name': old_user,
+ }
+ if not isinstance(old_user, (dict)):
+ LOG.warn(("Format for 'user' key must be a string or "
+ "dictionary and not %s"), util.obj_name(old_user))
+ old_user = {}
+
+ # If no old user format, then assume the distro
+ # provides what the 'default' user maps to, but notice
+ # that if this is provided, we won't automatically inject
+ # a 'default' user into the users list, while if a old user
+ # format is provided we will.
+ distro_user_config = {}
+ try:
+ distro_user_config = distro.get_default_user()
+ except NotImplementedError:
+ LOG.warn(("Distro has not implemented default user "
+ "access. No distribution provided default user"
+ " will be normalized."))
+
+ # Merge the old user (which may just be an empty dict when not
+ # present with the distro provided default user configuration so
+ # that the old user style picks up all the distribution specific
+ # attributes (if any)
+ default_user_config = util.mergemanydict([old_user, distro_user_config])
+
+ base_users = cfg.get('users', [])
+ if not isinstance(base_users, (list, dict, str, basestring)):
+ LOG.warn(("Format for 'users' key must be a comma separated string"
+ " or a dictionary or a list and not %s"),
+ util.obj_name(base_users))
+ base_users = []
+
+ if old_user:
+ # Ensure that when user: is provided that this user
+ # always gets added (as the default user)
+ if isinstance(base_users, (list)):
+ # Just add it on at the end...
+ base_users.append({'name': 'default'})
+ elif isinstance(base_users, (dict)):
+ base_users['default'] = base_users.get('default', True)
+ elif isinstance(base_users, (str, basestring)):
+ # Just append it on to be re-parsed later
+ base_users += ",default"
+
+ users = _normalize_users(base_users, default_user_config)
return (users, groups)
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index 7422f4f0..1a8e927b 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -48,6 +48,7 @@ class Distro(distros.Distro):
# calls from repeatly happening (when they
# should only happen say once per instance...)
self._runner = helpers.Runners(paths)
+ self.osfamily = 'debian'
def apply_locale(self, locale, out_fn=None):
if not out_fn:
@@ -64,7 +65,7 @@ class Distro(distros.Distro):
def install_packages(self, pkglist):
self.update_package_sources()
- self.package_command('install', pkglist)
+ self.package_command('install', pkgs=pkglist)
def _write_network(self, settings):
util.write_file(self.network_conf_fn, settings)
@@ -141,15 +142,24 @@ class Distro(distros.Distro):
# This ensures that the correct tz will be used for the system
util.copy(tz_file, self.tz_local_fn)
- def package_command(self, command, args=None):
+ def package_command(self, command, args=None, pkgs=[]):
e = os.environ.copy()
# See: http://tiny.cc/kg91fw
# Or: http://tiny.cc/mh91fw
e['DEBIAN_FRONTEND'] = 'noninteractive'
cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold',
- '--assume-yes', '--quiet', command]
- if args:
+ '--assume-yes', '--quiet']
+
+ if args and isinstance(args, str):
+ cmd.append(args)
+ elif args and isinstance(args, list):
cmd.extend(args)
+
+ cmd.append(command)
+
+ pkglist = util.expand_package_list('%s=%s', pkgs)
+ cmd.extend(pkglist)
+
# Allow the output of this to flow outwards (ie not be captured)
util.subp(cmd, env=e, capture=False)
diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py
index bc0877d5..2f91e386 100644
--- a/cloudinit/distros/rhel.py
+++ b/cloudinit/distros/rhel.py
@@ -60,9 +60,10 @@ class Distro(distros.Distro):
# calls from repeatly happening (when they
# should only happen say once per instance...)
self._runner = helpers.Runners(paths)
+ self.osfamily = 'redhat'
def install_packages(self, pkglist):
- self.package_command('install', pkglist)
+ self.package_command('install', pkgs=pkglist)
def _adjust_resolve(self, dns_servers, search_servers):
try:
@@ -207,7 +208,7 @@ class Distro(distros.Distro):
# This ensures that the correct tz will be used for the system
util.copy(tz_file, self.tz_local_fn)
- def package_command(self, command, args=None):
+ def package_command(self, command, args=None, pkgs=[]):
cmd = ['yum']
# If enabled, then yum will be tolerant of errors on the command line
# with regard to packages.
@@ -218,9 +219,17 @@ class Distro(distros.Distro):
# Determines whether or not yum prompts for confirmation
# of critical actions. We don't want to prompt...
cmd.append("-y")
- cmd.append(command)
- if args:
+
+ if args and isinstance(args, str):
+ cmd.append(args)
+ elif args and isinstance(args, list):
cmd.extend(args)
+
+ cmd.append(command)
+
+ pkglist = util.expand_package_list('%s-%s', pkgs)
+ cmd.extend(pkglist)
+
# Allow the output of this to flow outwards (ie not be captured)
util.subp(cmd, capture=False)
diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py
index 82e1e130..275caf0d 100644
--- a/cloudinit/sources/DataSourceCloudStack.py
+++ b/cloudinit/sources/DataSourceCloudStack.py
@@ -30,6 +30,8 @@ from cloudinit import log as logging
from cloudinit import sources
from cloudinit import url_helper as uhelp
from cloudinit import util
+from socket import inet_ntoa
+from struct import pack
LOG = logging.getLogger(__name__)
@@ -122,26 +124,70 @@ class DataSourceCloudStack(sources.DataSource):
return self.metadata['availability-zone']
+def get_default_gateway():
+ # Returns the default gateway ip address in the dotted format.
+ lines = util.load_file("/proc/net/route").splitlines()
+ for line in lines:
+ items = line.split("\t")
+ if items[1] == "00000000":
+ # Found the default route, get the gateway
+ gw = inet_ntoa(pack("<L", int(items[2], 16)))
+ LOG.debug("Found default route, gateway is %s", gw)
+ return gw
+ return None
+
+
+def get_dhclient_d():
+ # find lease files directory
+ supported_dirs = ["/var/lib/dhclient", "/var/lib/dhcp"]
+ for d in supported_dirs:
+ if os.path.exists(d):
+ LOG.debug("Using %s lease directory", d)
+ return d
+ return None
+
+
+def get_latest_lease():
+ # find latest lease file
+ lease_d = get_dhclient_d()
+ if not lease_d:
+ return None
+ lease_files = os.listdir(lease_d)
+ latest_mtime = -1
+ latest_file = None
+ for file_name in lease_files:
+ if file_name.endswith(".lease") or file_name.endswith(".leases"):
+ abs_path = os.path.join(lease_d, file_name)
+ mtime = os.path.getmtime(abs_path)
+ if mtime > latest_mtime:
+ latest_mtime = mtime
+ latest_file = abs_path
+ return latest_file
+
+
def get_vr_address():
- # get the address of the virtual router via dhcp responses
+ # Get the address of the virtual router via dhcp leases
# see http://bit.ly/T76eKC for documentation on the virtual router.
- dhclient_d = "/var/lib/dhclient"
- addresses = set()
- dhclient_files = os.listdir(dhclient_d)
- for file_name in dhclient_files:
- if file_name.endswith(".lease") or file_name.endswith(".leases"):
- with open(os.path.join(dhclient_d, file_name), "r") as fd:
- for line in fd:
- if "dhcp-server-identifier" in line:
- words = line.strip(" ;\r\n").split(" ")
- if len(words) > 2:
- dhcp = words[2]
- LOG.debug("Found DHCP identifier %s", dhcp)
- addresses.add(dhcp)
- if len(addresses) != 1:
- # No unique virtual router found
- return None
- return addresses.pop()
+ # If no virtual router is detected, fallback on default gateway.
+ lease_file = get_latest_lease()
+ if not lease_file:
+ LOG.debug("No lease file found, using default gateway")
+ return get_default_gateway()
+
+ latest_address = None
+ with open(lease_file, "r") as fd:
+ for line in fd:
+ if "dhcp-server-identifier" in line:
+ words = line.strip(" ;\r\n").split(" ")
+ if len(words) > 2:
+ dhcp = words[2]
+ LOG.debug("Found DHCP identifier %s", dhcp)
+ latest_address = dhcp
+ if not latest_address:
+ # No virtual router found, fallback on default gateway
+ LOG.debug("No DHCP found, using default gateway")
+ return get_default_gateway()
+ return latest_address
# Used to match classes to dependencies
diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py
index c7826851..ec016a1d 100644
--- a/cloudinit/sources/DataSourceConfigDrive.py
+++ b/cloudinit/sources/DataSourceConfigDrive.py
@@ -270,7 +270,7 @@ def find_candidate_devs():
combined = (by_label + [d for d in by_fstype if d not in by_label])
# We are looking for block device (sda, not sda1), ignore partitions
- combined = [d for d in combined if d[-1] not in "0123456789"]
+ combined = [d for d in combined if not util.is_partition(d)]
return combined
diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py
index bed500a2..097bbc52 100644
--- a/cloudinit/sources/DataSourceNoCloud.py
+++ b/cloudinit/sources/DataSourceNoCloud.py
@@ -77,37 +77,47 @@ class DataSourceNoCloud(sources.DataSource):
found.append("ds_config")
md["seedfrom"] = self.ds_cfg['seedfrom']
- fslist = util.find_devs_with("TYPE=vfat")
- fslist.extend(util.find_devs_with("TYPE=iso9660"))
-
- label_list = util.find_devs_with("LABEL=cidata")
- devlist = list(set(fslist) & set(label_list))
- devlist.sort(reverse=True)
-
- for dev in devlist:
- try:
- LOG.debug("Attempting to use data from %s", dev)
-
- (newmd, newud) = util.mount_cb(dev, util.read_seeded)
- md = util.mergedict(newmd, md)
- ud = newud
-
- # For seed from a device, the default mode is 'net'.
- # that is more likely to be what is desired.
- # If they want dsmode of local, then they must
- # specify that.
- if 'dsmode' not in md:
- md['dsmode'] = "net"
-
- LOG.debug("Using data from %s", dev)
- found.append(dev)
- break
- except OSError as e:
- if e.errno != errno.ENOENT:
- raise
- except util.MountFailedError:
- util.logexc(LOG, ("Failed to mount %s"
- " when looking for data"), dev)
+ # if ds_cfg has 'user-data' and 'meta-data'
+ if 'user-data' in self.ds_cfg and 'meta-data' in self.ds_cfg:
+ if self.ds_cfg['user-data']:
+ ud = self.ds_cfg['user-data']
+ if self.ds_cfg['meta-data'] is not False:
+ md = util.mergedict(md, self.ds_cfg['meta-data'])
+ if 'ds_config' not in found:
+ found.append("ds_config")
+
+ if self.ds_cfg.get('fs_label', "cidata"):
+ fslist = util.find_devs_with("TYPE=vfat")
+ fslist.extend(util.find_devs_with("TYPE=iso9660"))
+
+ label = self.ds_cfg.get('fs_label')
+ label_list = util.find_devs_with("LABEL=%s" % label)
+ devlist = list(set(fslist) & set(label_list))
+ devlist.sort(reverse=True)
+
+ for dev in devlist:
+ try:
+ LOG.debug("Attempting to use data from %s", dev)
+
+ (newmd, newud) = util.mount_cb(dev, util.read_seeded)
+ md = util.mergedict(newmd, md)
+ ud = newud
+
+ # For seed from a device, the default mode is 'net'.
+ # that is more likely to be what is desired. If they want
+ # dsmode of local, then they must specify that.
+ if 'dsmode' not in md:
+ md['dsmode'] = "net"
+
+ LOG.debug("Using data from %s", dev)
+ found.append(dev)
+ break
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ except util.MountFailedError:
+ util.logexc(LOG, ("Failed to mount %s"
+ " when looking for data"), dev)
# There was no indication on kernel cmdline or data
# in the seeddir suggesting this handler should be used.
@@ -195,6 +205,8 @@ def parse_cmdline_data(ds_id, fill, cmdline=None):
# short2long mapping to save cmdline typing
s2l = {"h": "local-hostname", "i": "instance-id", "s": "seedfrom"}
for item in kvpairs:
+ if item == "":
+ continue
try:
(k, v) = item.split("=", 1)
except:
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 8d3213b4..d7d1dea0 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -529,11 +529,16 @@ class Modules(object):
freq = mod.frequency
if not freq in FREQUENCIES:
freq = PER_INSTANCE
- worked_distros = mod.distros
+
+ worked_distros = set(mod.distros)
+ worked_distros.update(
+ distros.Distro.expand_osfamily(mod.osfamilies))
+
if (worked_distros and d_name not in worked_distros):
LOG.warn(("Module %s is verified on %s distros"
" but not on %s distro. It may or may not work"
- " correctly."), name, worked_distros, d_name)
+ " correctly."), name, list(worked_distros),
+ d_name)
# Use the configs logger and not our own
# TODO(harlowja): possibly check the module
# for having a LOG attr and just give it back
diff --git a/cloudinit/util.py b/cloudinit/util.py
index e334559c..7b1202a2 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -402,10 +402,9 @@ def get_cfg_option_list(yobj, key, default=None):
return []
val = yobj[key]
if isinstance(val, (list)):
- # Should we ensure they are all strings??
- cval = [str(v) for v in val]
+ cval = [v for v in val]
return cval
- if not isinstance(val, (str, basestring)):
+ if not isinstance(val, (basestring)):
val = str(val)
return [val]
@@ -1560,3 +1559,37 @@ def keyval_str_to_dict(kvstring):
val = True
ret[key] = val
return ret
+
+
+def is_partition(device):
+ if device.startswith("/dev/"):
+ device = device[5:]
+
+ return os.path.isfile("/sys/class/block/%s/partition" % device)
+
+
+def expand_package_list(version_fmt, pkgs):
+ # we will accept tuples, lists of tuples, or just plain lists
+ if not isinstance(pkgs, list):
+ pkgs = [pkgs]
+
+ pkglist = []
+ for pkg in pkgs:
+ if isinstance(pkg, basestring):
+ pkglist.append(pkg)
+ continue
+
+ if isinstance(pkg, (tuple, list)):
+ if len(pkg) < 1 or len(pkg) > 2:
+ raise RuntimeError("Invalid package & version tuple.")
+
+ if len(pkg) == 2 and pkg[1]:
+ pkglist.append(version_fmt % tuple(pkg))
+ continue
+
+ pkglist.append(pkg[0])
+
+ else:
+ raise RuntimeError("Invalid package type.")
+
+ return pkglist