From c508fa0c4159f93df077a2d46ed481fd5d8c8803 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 6 Nov 2012 15:28:29 -0500 Subject: tools/Z99-cloud-locale-test.sh: avoid warning when shell is zsh LP: #1073077 --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index d03c0293..1243ce7f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,6 +6,8 @@ - cc_landscape: restart landscape after install or config (LP: #1070345) - multipart/archive. do not fail on unknown headers in multipart mime or cloud-archive config (LP: #1065116). + - tools/Z99-cloud-locale-test.sh: avoid warning when user's shell is + zsh (LP: #1073077) 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 0da58fe113726c7654bca54b365d95044b44ef87 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 7 Nov 2012 10:17:50 -0500 Subject: add ChangeLog entry --- ChangeLog | 3 +++ 1 file changed, 3 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 4287d58f..2896f71a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,9 @@ - config-drive: map hostname to local-hostname (LP: #1061964) - landscape: install landscape-client package if not installed. only take action if cloud-config is present (LP: #1066115) + - split 'apt-update-upgrade' config module into 'apt-configure' and + 'package-update-upgrade-install'. The 'package-update-upgrade-install' + will be a cross distro module. 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From efa2dfa699bc9222105850641a2820ceda9bfe67 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Sat, 10 Nov 2012 21:57:06 -0500 Subject: update ChangeLog LP: #1076811 --- ChangeLog | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 8f304db3..52f47e3c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,12 @@ - Remove usage of paths.join, as all code should run through util helpers - Fix pylint complaining about tests folder 'helpers.py' not being found - Add a pylintrc file that is used instead options hidden in 'run_pylint' + - fix bug where cloud-config from user-data could not affect system_info + settings [revno 703] (LP: #1076811) + - for write fqdn to system config for rh/fedora [revno 704] + - add yaml/cloud config examples checking tool [revno 706] + - Fix the merging of group configuration when that group configuration is a + dict => members. [revno 707] 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 7fd838c187ad004d124c9293d91fdb4fca083f66 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 12 Nov 2012 09:55:48 -0500 Subject: add ChangeLog entry --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 5408a941..a208ab0d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,6 +23,8 @@ - Fix the merging of group configuration when that group configuration is a dict => members. [revno 707] - add yum_add_repo configuration module for adding additional yum repos + - work around the lazy loading of get_instance_metadata in boto >= 2.6.0 + by fully walking the dictionary. (LP: #1068801) 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 7ba753720cd95bfca61c82445cf9c7882fe5d6f1 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 12 Nov 2012 12:26:49 -0500 Subject: config-drive-v2: support public keys This does a couple things: * separates out the 'normalize_public_keys' from the DataSource's get_public_ssh_keys * uses that from config-drive datasource * supports config drive v1 or v2 public-keys * adds a test. LP: #1077700 --- ChangeLog | 1 + cloudinit/sources/DataSourceConfigDrive.py | 7 +-- cloudinit/sources/__init__.py | 56 ++++++++++++---------- .../unittests/test_datasource/test_configdrive.py | 26 ++++++++++ 4 files changed, 61 insertions(+), 29 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index de1bcbff..a68e196e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,7 @@ - work around the lazy loading of get_instance_metadata in boto >= 2.6.0 by fully walking the dictionary. (LP: #1068801) Added dependency on distribute's python-pkg-resources + - fix public key importing with config-drive-v2 datasource (LP: #1077700) 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index 9729cfb9..c7826851 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -219,9 +219,10 @@ class DataSourceConfigDrive(sources.DataSource): return True def get_public_ssh_keys(self): - if not 'public-keys' in self.metadata: - return [] - return self.metadata['public-keys'] + name = "public_keys" + if self.version == 1: + name = "public-keys" + return sources.normalize_pubkey_data(self.metadata.get(name)) class DataSourceConfigDriveNet(DataSourceConfigDrive): diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index 745627d0..96baff90 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -100,32 +100,7 @@ class DataSource(object): return {} def get_public_ssh_keys(self): - keys = [] - - if not self.metadata or 'public-keys' not in self.metadata: - return keys - - if isinstance(self.metadata['public-keys'], (basestring, str)): - return str(self.metadata['public-keys']).splitlines() - - if isinstance(self.metadata['public-keys'], (list, set)): - return list(self.metadata['public-keys']) - - if isinstance(self.metadata['public-keys'], (dict)): - for (_keyname, klist) in self.metadata['public-keys'].iteritems(): - # lp:506332 uec metadata service responds with - # data that makes boto populate a string for 'klist' rather - # than a list. - if isinstance(klist, (str, basestring)): - klist = [klist] - if isinstance(klist, (list, set)): - for pkey in klist: - # There is an empty string at - # the end of the keylist, trim it - if pkey: - keys.append(pkey) - - return keys + return normalize_pubkey_data(self.metadata.get('public-keys')) def _remap_device(self, short_name): # LP: #611137 @@ -208,6 +183,35 @@ class DataSource(object): availability_zone=self.availability_zone) +def normalize_pubkey_data(pubkey_data): + keys = [] + + if not pubkey_data: + return keys + + if isinstance(pubkey_data, (basestring, str)): + return str(pubkey_data).splitlines() + + if isinstance(pubkey_data, (list, set)): + return list(pubkey_data) + + if isinstance(pubkey_data, (dict)): + for (_keyname, klist) in pubkey_data.iteritems(): + # lp:506332 uec metadata service responds with + # data that makes boto populate a string for 'klist' rather + # than a list. + if isinstance(klist, (str, basestring)): + klist = [klist] + if isinstance(klist, (list, set)): + for pkey in klist: + # There is an empty string at + # the end of the keylist, trim it + if pkey: + keys.append(pkey) + + return keys + + def find_source(sys_cfg, distro, paths, ds_deps, cfg_list, pkg_list): ds_list = list_sources(cfg_list, ds_deps, pkg_list) ds_names = [util.obj_name(f) for f in ds_list] diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py index 00379e03..aa5b98ed 100644 --- a/tests/unittests/test_datasource/test_configdrive.py +++ b/tests/unittests/test_datasource/test_configdrive.py @@ -288,6 +288,32 @@ class TestConfigDriveDataSource(MockerTestCase): finally: util.find_devs_with = orig_find_devs_with + def test_pubkeys_v2(self): + """Verify that public-keys work in config-drive-v2.""" + populate_dir(self.tmp, CFG_DRIVE_FILES_V2) + myds = cfg_ds_from_dir(self.tmp) + self.assertEqual(myds.get_public_ssh_keys(), + [OSTACK_META['public_keys']['mykey']]) + + +def cfg_ds_from_dir(seed_d): + found = ds.read_config_drive_dir(seed_d) + cfg_ds = ds.DataSourceConfigDrive(settings.CFG_BUILTIN, None, + helpers.Paths({})) + populate_ds_from_read_config(cfg_ds, seed_d, found) + return cfg_ds + + +def populate_ds_from_read_config(cfg_ds, source, results): + """Patch the DataSourceConfigDrive from the results of + read_config_drive_dir hopefully in line with what it would have + if cfg_ds.get_data had been successfully called""" + cfg_ds.source = source + cfg_ds.metadata = results.get('metadata') + cfg_ds.ec2_metadata = results.get('ec2-metadata') + cfg_ds.userdata_raw = results.get('userdata') + cfg_ds.version = results.get('cfgdrive_ver') + def populate_dir(seed_dir, files): for (name, content) in files.iteritems(): -- cgit v1.2.3 From 5a084e641dea06b440f4a539147254bc775640a9 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 12 Nov 2012 13:07:12 -0500 Subject: mark changelog entry for lp:1075980 --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index a68e196e..6d80671a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,8 @@ by fully walking the dictionary. (LP: #1068801) Added dependency on distribute's python-pkg-resources - fix public key importing with config-drive-v2 datasource (LP: #1077700) + - handle renaming and fixing up of marker names (LP: 1075980) [revno 710] + this relieves that burden from the distro/packaging. 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 9267bfbed5129060ea3365d5f91b57fee2639647 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 12 Nov 2012 13:53:46 -0800 Subject: Update changelog for previous commits. Add in entry for LP: 1068801 and LP: 1075756 as well as LP: 1077245 and add in reason why we are switching back to using 'None' as the initial datasource value. --- ChangeLog | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 6d80671a..4cae8b32 100644 --- a/ChangeLog +++ b/ChangeLog @@ -29,6 +29,23 @@ - fix public key importing with config-drive-v2 datasource (LP: #1077700) - handle renaming and fixing up of marker names (LP: 1075980) [revno 710] this relieves that burden from the distro/packaging. + - datasource: move back to using the value 'None' for the initial datasource + since it causes early failure if used, instead of transient and + not easily-detectable later failure + - group config: fix how group members weren't being translated correctly + when the group: [member, member...] format was used (LP: #1077245) + - sysconfig: fix how the /etc/sysconfig/network should be using the fully + qualified domain name instead of the partially qualified domain name + which is used in the ubuntu/debian case (LP: #1076759) + - fix how string escaping was not working when the string was a unicode + string which was causing the warning message not to be written + out (LP: #1075756) + - for boto > 0.6.0 there was a lazy load of the metadata added, when cloud-init + runs the usage of this lazy loading is hidden and since that lazy loading + will be performed on future attribute access we must traverse the lazy loaded + dictionary and force it to full expand so that if cloud-init blocks the ec2 + metadata port the lazy loaded dictionary will continue working properly + instead of trying to make additional url calls which will fail (LP: #1068801) 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 29b4adef881852169c6a0c5c95168c768cb6ffc5 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 12 Nov 2012 14:34:01 -0800 Subject: Append changelog additions. LP: #1078097 --- ChangeLog | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 672482ba..1582dfb5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,15 @@ 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) + - use a set of helper/parsing classes to perform system configuration + file modification in a manner that provides a nice object oriented + interface to those objects as well as makes it possible to test + those parsing entities without having to invoke distro class code. + - Created parsers for: + - /etc/sysconfig + - /etc/hostname + - resolv.conf + - /etc/hosts 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 949e1759342b1e60c100855aaf250165bcb9997e Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 12 Nov 2012 22:15:29 -0500 Subject: add 'finalcmd' module for running code after cloud-init-final This allows the user to easily run stuff even after cloud-init-final has finished. The initial reason for it is to be able to run /sbin/poweroff and not have cloud-init complain loudly that it is being killed. LP: #1064665 --- ChangeLog | 2 + cloudinit/config/cc_finalcmd.py | 139 ++++++++++++++++++++++++++++++++++++++++ config/cloud.cfg | 1 + doc/examples/cloud-config.txt | 18 ++++++ 4 files changed, 160 insertions(+) create mode 100644 cloudinit/config/cc_finalcmd.py (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 4cae8b32..93c3af04 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,6 +46,8 @@ dictionary and force it to full expand so that if cloud-init blocks the ec2 metadata port the lazy loaded dictionary will continue working properly instead of trying to make additional url calls which will fail (LP: #1068801) + - add 'finalcmd' config module to execute 'finalcmd' entries like + 'runcmd' but detached from cloud-init (LP: #1064665) 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. diff --git a/cloudinit/config/cc_finalcmd.py b/cloudinit/config/cc_finalcmd.py new file mode 100644 index 00000000..442ad12b --- /dev/null +++ b/cloudinit/config/cc_finalcmd.py @@ -0,0 +1,139 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2011 Canonical Ltd. +# +# Author: Scott Moser +# +# 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 . + +from cloudinit.settings import PER_INSTANCE +from cloudinit import util + +import errno +import os +import subprocess +import sys +import time + +frequency = PER_INSTANCE + + +def handle(_name, cfg, _cloud, log, _args): + + finalcmds = cfg.get("finalcmd") + + if not finalcmds: + log.debug("No final commands") + return + + mypid = os.getpid() + cmdline = util.load_file("/proc/%s/cmdline") + + if not cmdline: + log.warn("Failed to get cmdline of current process") + return + + try: + timeout = float(cfg.get("finalcmd_timeout", 30.0)) + except ValueError: + log.warn("failed to convert finalcmd_timeout '%s' to float" % + cfg.get("finalcmd_timeout", 30.0)) + return + + devnull_fp = open("/dev/null", "w") + + shellcode = util.shellify(finalcmds) + + # note, after the fork, we do not use any of cloud-init's functions + # that would attempt to log. The primary reason for that is + # to allow the 'finalcmd' the ability to do just about anything + # and not depend on syslog services. + # Basically, it should "just work" to have finalcmd of: + # - sleep 30 + # - /sbin/poweroff + finalcmd_d = os.path.join(cloud.get_ipath_cur(), "finalcmds") + + util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, + runfinal, (shellcode, finalcmd_d, devnull_fp)) + + +def execmd(exe_args, data_in=None, output=None): + try: + proc = subprocess.Popen(exe_args, stdin=subprocess.PIPE, + stdout=output, stderr=subprocess.STDERR) + proc.communicate(data_in) + except Exception as e: + return 254 + return proc.returncode() + + +def runfinal(shellcode, finalcmd_d, output=None): + ret = execmd(("/bin/sh",), data_in=shellcode, output=output) + if not (finalcmd_d and os.path.isdir(finalcmd_d)): + sys.exit(ret) + + fails = 0 + if ret != 0: + fails = 1 + + # now runparts the final command dir + for exe_name in sorted(os.listdir(finalcmd_d)): + exe_path = os.path.join(finalcmd_d, exe_name) + if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK): + ret = execmd(exe_path, data_in=None, output=output) + if ret != 0: + fails += 1 + sys.exit(fails) + + +def run_after_pid_gone(pid, pidcmdline, timeout, func, args): + # wait until pid, with /proc/pid/cmdline contents of pidcmdline + # is no longer alive. After it is gone, or timeout has passed + # execute func(args) + msg = "ERROR: Uncaught error" + end_time = time.time() + timeout + + cmdline_f = "/proc/%s/cmdline" % pid + + while True: + if time.time() > end_time: + msg = "timeout reached before %s ended" % pid + break + + try: + cmdline = "" + with open(cmdline_f) as fp: + cmdline = fp.read() + if cmdline != pidcmdline: + msg = "cmdline changed for %s [now: %s]" % (pid, cmdline) + break + + except IOError as ioerr: + if ioerr.errno == errno.ENOENT: + msg = "pidfile '%s' gone" % cmdline_f + else: + msg = "ERROR: IOError: %s" % ioerr + raise + break + + except Exception as e: + msg = "ERROR: Exception: %s" % e + raise + + if msg.startswith("ERROR:"): + sys.stderr.write(msg) + sys.stderr.write("Not executing finalcmd") + sys.exit(1) + + sys.stderr.write("calling %s with %s\n" % (func, args)) + sys.exit(func(*args)) diff --git a/config/cloud.cfg b/config/cloud.cfg index ad100fff..249a593d 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -69,6 +69,7 @@ cloud_final_modules: - keys-to-console - phone-home - final-message + - finalcmd # System and/or distro specific settings # (not accessible to handlers/transforms) diff --git a/doc/examples/cloud-config.txt b/doc/examples/cloud-config.txt index 12bf2c91..4fc5f351 100644 --- a/doc/examples/cloud-config.txt +++ b/doc/examples/cloud-config.txt @@ -256,6 +256,24 @@ bootcmd: - echo 192.168.1.130 us.archive.ubuntu.com > /etc/hosts - [ cloud-init-per, once, mymkfs, mkfs, /dev/vdb ] +# final commands +# default: none +# This can be used to execute commands after and fully detached from +# a cloud-init stage. The initial purpose of it was to allow 'poweroff' +# detached from cloud-init. If poweroff was run from 'runcmd' or userdata +# then messages may be spewed from cloud-init about logging failing or other +# issues as a result of the system being turned off. +# +# You probably are better off using 'runcmd' for this. +# +# The output of finalcmd will redirected redirected to /dev/null +# If you want output to be seen, take care to do so in your commands +# themselves. See example. +finalcmd: + - sleep 30 + - "echo $(date -R): powering off > /dev/console" + - /sbin/poweroff + # cloud_config_modules: # default: # cloud_config_modules: -- cgit v1.2.3 From 8d5e1e108b0cdd3af872383da7654bec91355b5f Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 12 Nov 2012 19:17:48 -0800 Subject: Remove dup and un-needed entries. --- ChangeLog | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 4cae8b32..e577b618 100644 --- a/ChangeLog +++ b/ChangeLog @@ -23,15 +23,9 @@ - Fix the merging of group configuration when that group configuration is a dict => members. [revno 707] - add yum_add_repo configuration module for adding additional yum repos - - work around the lazy loading of get_instance_metadata in boto >= 2.6.0 - by fully walking the dictionary. (LP: #1068801) - Added dependency on distribute's python-pkg-resources - fix public key importing with config-drive-v2 datasource (LP: #1077700) - handle renaming and fixing up of marker names (LP: 1075980) [revno 710] this relieves that burden from the distro/packaging. - - datasource: move back to using the value 'None' for the initial datasource - since it causes early failure if used, instead of transient and - not easily-detectable later failure - group config: fix how group members weren't being translated correctly when the group: [member, member...] format was used (LP: #1077245) - sysconfig: fix how the /etc/sysconfig/network should be using the fully @@ -46,6 +40,7 @@ dictionary and force it to full expand so that if cloud-init blocks the ec2 metadata port the lazy loaded dictionary will continue working properly instead of trying to make additional url calls which will fail (LP: #1068801) + - Added dependency on distribute's python-pkg-resources 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 76005c391a5e59e794db42a9f9a4d46fec679854 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 12 Nov 2012 22:18:21 -0800 Subject: Update the changelog with functions moved. --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 13b5a8ed..20b81aa2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -50,6 +50,10 @@ - /etc/hostname - resolv.conf - /etc/hosts + - Moved duplicated functionality into the root level distro class including: +  - apply_hostname +  - set_hostname +  - *various shared configuration file names/paths* 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. -- cgit v1.2.3 From 2113e89b6816d2c9d442103698414cd189ca3412 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 13 Nov 2012 11:18:22 -0500 Subject: implement power_state with tests. --- ChangeLog | 4 +- cloudinit/config/cc_power_state_change.py | 122 +++++++++++---------- doc/examples/cloud-config.txt | 39 ++++--- .../test_handler/test_handler_power_state.py | 88 +++++++++++++++ 4 files changed, 175 insertions(+), 78 deletions(-) create mode 100644 tests/unittests/test_handler/test_handler_power_state.py (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 57bfc1a4..23612228 100644 --- a/ChangeLog +++ b/ChangeLog @@ -43,8 +43,8 @@ - Added dependency on distribute's python-pkg-resources - use a set of helper/parsing classes to perform system configuration for easier test. (/etc/sysconfig, /etc/hostname, resolv.conf, /etc/hosts) - - add 'finalcmd' config module to execute 'finalcmd' entries like - 'runcmd' but detached from cloud-init (LP: #1064665) + - add power_state_change config module for shutting down stystem after + cloud-init finishes. (LP: #1064665) 0.7.0: - add a 'exception_cb' argument to 'wait_for_url'. If provided, this method will be called back with the exception received and the message. diff --git a/cloudinit/config/cc_power_state_change.py b/cloudinit/config/cc_power_state_change.py index 67e0316b..07de548c 100644 --- a/cloudinit/config/cc_power_state_change.py +++ b/cloudinit/config/cc_power_state_change.py @@ -21,6 +21,7 @@ from cloudinit import util import errno import os +import re import subprocess import sys import time @@ -28,83 +29,91 @@ import time frequency = PER_INSTANCE -def handle(_name, cfg, cloud, log, _args): +def handle(_name, cfg, _cloud, log, _args): - finalcmds = cfg.get("finalcmd") - - if not finalcmds: - log.debug("No final commands") + try: + (args, timeout) = load_power_state(cfg) + if args is None: + log.debug("no power_state provided. doing nothing") + return + except Exception as e: + log.warn("%s Not performing power state change!" % str(e)) return mypid = os.getpid() cmdline = util.load_file("/proc/%s/cmdline") if not cmdline: - log.warn("Failed to get cmdline of current process") - return - - try: - timeout = float(cfg.get("finalcmd_timeout", 30.0)) - except ValueError: - log.warn("failed to convert finalcmd_timeout '%s' to float" % - cfg.get("finalcmd_timeout", 30.0)) + log.warn("power_state: failed to get cmdline of current process") return devnull_fp = open("/dev/null", "w") - shellcode = util.shellify(finalcmds) + log.debug("After pid %s ends, will execute: %s" % (mypid, ' '.join(args))) + + util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, log, execmd, + [args, devnull_fp]) + + +def load_power_state(cfg): + # returns a tuple of shutdown_command, timeout + # shutdown_command is None if no config found + pstate = cfg.get('power_state') + + if pstate is None: + return (None, None) + + if not isinstance(pstate, dict): + raise TypeError("power_state is not a dict.") - # note, after the fork, we do not use any of cloud-init's functions - # that would attempt to log. The primary reason for that is - # to allow the 'finalcmd' the ability to do just about anything - # and not depend on syslog services. - # Basically, it should "just work" to have finalcmd of: - # - sleep 30 - # - /sbin/poweroff - finalcmd_d = os.path.join(cloud.get_ipath_cur(), "finalcmds") + opt_map = {'halt': '-H', 'poweroff': '-P', 'reboot': '-r'} - util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, - runfinal, (shellcode, finalcmd_d, devnull_fp)) + mode = pstate.get("mode") + if mode not in opt_map: + raise TypeError("power_state[mode] required, must be one of: %s." % + ','.join(opt_map.keys())) + delay = pstate.get("delay", "now") + if delay != "now" and not re.match("\+[0-9]+", delay): + raise TypeError("power_state[delay] must be 'now' or '+m' (minutes).") -def execmd(exe_args, data_in=None, output=None): + args = ["shutdown", opt_map[mode], delay] + if pstate.get("message"): + args.append(pstate.get("message")) + + try: + timeout = float(pstate.get('timeout', 30.0)) + except ValueError: + raise ValueError("failed to convert timeout '%s' to float." % + pstate['timeout']) + + return (args, timeout) + + +def execmd(exe_args, output=None, data_in=None): try: proc = subprocess.Popen(exe_args, stdin=subprocess.PIPE, stdout=output, stderr=subprocess.STDOUT) proc.communicate(data_in) except Exception: - return 254 - return proc.returncode() + sys.exit(254) + sys.exit(proc.returncode()) -def runfinal(shellcode, finalcmd_d, output=None): - ret = execmd(["/bin/sh"], data_in=shellcode, output=output) - if not (finalcmd_d and os.path.isdir(finalcmd_d)): - sys.exit(ret) - - fails = 0 - if ret != 0: - fails = 1 - - # now runparts the final command dir - for exe_name in sorted(os.listdir(finalcmd_d)): - exe_path = os.path.join(finalcmd_d, exe_name) - if os.path.isfile(exe_path) and os.access(exe_path, os.X_OK): - ret = execmd([exe_path], data_in=None, output=output) - if ret != 0: - fails += 1 - sys.exit(fails) - - -def run_after_pid_gone(pid, pidcmdline, timeout, func, args): +def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args): # wait until pid, with /proc/pid/cmdline contents of pidcmdline # is no longer alive. After it is gone, or timeout has passed # execute func(args) - msg = "ERROR: Uncaught error" + msg = None end_time = time.time() + timeout cmdline_f = "/proc/%s/cmdline" % pid + def fatal(msg): + if log: + log.warn(msg) + sys.exit(254) + while True: if time.time() > end_time: msg = "timeout reached before %s ended" % pid @@ -122,18 +131,15 @@ def run_after_pid_gone(pid, pidcmdline, timeout, func, args): if ioerr.errno == errno.ENOENT: msg = "pidfile '%s' gone" % cmdline_f else: - msg = "ERROR: IOError: %s" % ioerr - raise + fatal("IOError during wait: %s" % ioerr) break except Exception as e: - msg = "ERROR: Exception: %s" % e - raise + fatal("Unexpected Exception: %s" % e) - if msg.startswith("ERROR:"): - sys.stderr.write(msg) - sys.stderr.write("Not executing finalcmd") - sys.exit(1) + if not msg: + fatal("Unexpected error in run_after_pid_gone") - sys.stderr.write("calling %s with %s\n" % (func, args)) - sys.exit(func(*args)) + if log: + log.debug(msg) + func(*args) diff --git a/doc/examples/cloud-config.txt b/doc/examples/cloud-config.txt index 4fc5f351..09298655 100644 --- a/doc/examples/cloud-config.txt +++ b/doc/examples/cloud-config.txt @@ -256,24 +256,6 @@ bootcmd: - echo 192.168.1.130 us.archive.ubuntu.com > /etc/hosts - [ cloud-init-per, once, mymkfs, mkfs, /dev/vdb ] -# final commands -# default: none -# This can be used to execute commands after and fully detached from -# a cloud-init stage. The initial purpose of it was to allow 'poweroff' -# detached from cloud-init. If poweroff was run from 'runcmd' or userdata -# then messages may be spewed from cloud-init about logging failing or other -# issues as a result of the system being turned off. -# -# You probably are better off using 'runcmd' for this. -# -# The output of finalcmd will redirected redirected to /dev/null -# If you want output to be seen, take care to do so in your commands -# themselves. See example. -finalcmd: - - sleep 30 - - "echo $(date -R): powering off > /dev/console" - - /sbin/poweroff - # cloud_config_modules: # default: # cloud_config_modules: @@ -596,3 +578,24 @@ manual_cache_clean: False # A list of key types (first token of a /etc/ssh/ssh_key_*.pub file) # that should be skipped when outputting key fingerprints and keys # to the console respectively. + +## poweroff or reboot system after finished +# default: none +# +# power_state can be used to make the system shutdown, reboot or +# halt after boot is finished. This same thing can be acheived by +# user-data scripts or by runcmd by simply invoking 'shutdown'. +# +# Doing it this way ensures that cloud-init is entirely finished with +# modules that would be executed, and avoids any error/log messages +# that may go to the console as a result of system services like +# syslog being taken down while cloud-init is running. +# +# delay: form accepted by shutdown. default is 'now'. other format +# accepted is +m (m in minutes) +# mode: required. must be one of 'poweroff', 'halt', 'reboot' +# message: provided as the message argument to 'shutdown'. default is none. +power_state: + delay: 30 + mode: poweroff + message: Bye Bye diff --git a/tests/unittests/test_handler/test_handler_power_state.py b/tests/unittests/test_handler/test_handler_power_state.py new file mode 100644 index 00000000..1149fedc --- /dev/null +++ b/tests/unittests/test_handler/test_handler_power_state.py @@ -0,0 +1,88 @@ +from unittest import TestCase + +from cloudinit.config import cc_power_state_change as psc + + +class TestLoadPowerState(TestCase): + def setUp(self): + super(self.__class__, self).setUp() + + def test_no_config(self): + # completely empty config should mean do nothing + (cmd, _timeout) = psc.load_power_state({}) + self.assertEqual(cmd, None) + + def test_irrelevant_config(self): + # no power_state field in config should return None for cmd + (cmd, _timeout) = psc.load_power_state({'foo': 'bar'}) + self.assertEqual(cmd, None) + + def test_invalid_mode(self): + cfg = {'power_state': {'mode': 'gibberish'}} + self.assertRaises(TypeError, psc.load_power_state, cfg) + + cfg = {'power_state': {'mode': ''}} + self.assertRaises(TypeError, psc.load_power_state, cfg) + + def test_empty_mode(self): + cfg = {'power_state': {'message': 'goodbye'}} + self.assertRaises(TypeError, psc.load_power_state, cfg) + + def test_valid_modes(self): + cfg = {'power_state': {}} + for mode in ('halt', 'poweroff', 'reboot'): + cfg['power_state']['mode'] = mode + check_lps_ret(psc.load_power_state(cfg), mode=mode) + + def test_invalid_delay(self): + cfg = {'power_state': {'mode': 'poweroff', 'delay': 'goodbye'}} + self.assertRaises(TypeError, psc.load_power_state, cfg) + + def test_valid_delay(self): + cfg = {'power_state': {'mode': 'poweroff', 'delay': ''}} + for delay in ("now", "+1", "+30"): + cfg['power_state']['delay'] = delay + check_lps_ret(psc.load_power_state(cfg)) + + def test_message_present(self): + cfg = {'power_state': {'mode': 'poweroff', 'message': 'GOODBYE'}} + ret = psc.load_power_state(cfg) + check_lps_ret(psc.load_power_state(cfg)) + self.assertIn(cfg['power_state']['message'], ret[0]) + + def test_no_message(self): + # if message is not present, then no argument should be passed for it + cfg = {'power_state': {'mode': 'poweroff'}} + (cmd, _timeout) = psc.load_power_state(cfg) + self.assertNotIn("", cmd) + check_lps_ret(psc.load_power_state(cfg)) + self.assertTrue(len(cmd) == 3) + + +def check_lps_ret(psc_return, mode=None): + if len(psc_return) != 2: + raise TypeError("length returned = %d" % len(psc_return)) + + errs = [] + cmd = psc_return[0] + timeout = psc_return[1] + + if not 'shutdown' in psc_return[0][0]: + errs.append("string 'shutdown' not in cmd") + + if mode is not None: + opt = {'halt': '-H', 'poweroff': '-P', 'reboot': '-r'}[mode] + if opt not in psc_return[0]: + errs.append("opt '%s' not in cmd: %s" % (opt, cmd)) + + if len(cmd) != 3 and len(cmd) != 4: + errs.append("Invalid command length: %s" % len(cmd)) + + try: + float(timeout) + except: + errs.append("timeout failed convert to float") + + if len(errs): + lines = ["Errors in result: %s" % str(psc_return)] + errs + raise Exception('\n'.join(lines)) -- cgit v1.2.3 From 517f52af3c7fa39b0303f337681f77e2d2a4ff6f Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 13 Nov 2012 12:08:45 -0500 Subject: add dependency for 'pkg_resources' --- ChangeLog | 2 +- packages/bddeb | 1 + packages/brpm | 1 + packages/debian/control.in | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 21584bb7..14efc4b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -40,7 +40,7 @@ dictionary and force it to full expand so that if cloud-init blocks the ec2 metadata port the lazy loaded dictionary will continue working properly instead of trying to make additional url calls which will fail (LP: #1068801) - - Added dependency on distribute's python-pkg-resources + - Added dependency: python-pkg-resources (deb), python-setuptools (rpm) - use a set of helper/parsing classes to perform system configuration for easier test. (/etc/sysconfig, /etc/hostname, resolv.conf, /etc/hosts) 0.7.0: diff --git a/packages/bddeb b/packages/bddeb index bda3170d..a92dad70 100755 --- a/packages/bddeb +++ b/packages/bddeb @@ -33,6 +33,7 @@ PKG_MP = { 'cheetah': 'python-cheetah', 'configobj': 'python-configobj', 'oauth': 'python-oauth', + 'pkg_resources': 'python-pkg-resources', 'prettytable': 'python-prettytable', 'pyyaml': 'python-yaml', } diff --git a/packages/brpm b/packages/brpm index eea2a046..0b1e33cf 100755 --- a/packages/brpm +++ b/packages/brpm @@ -39,6 +39,7 @@ PKG_MP = { 'cheetah': 'python-cheetah', 'configobj': 'python-configobj', 'oauth': 'python-oauth', + 'pkg_resources': 'python-setuptools', 'prettytable': 'python-prettytable', 'pyyaml': 'PyYAML', } diff --git a/packages/debian/control.in b/packages/debian/control.in index dace0356..edb5aff5 100644 --- a/packages/debian/control.in +++ b/packages/debian/control.in @@ -19,7 +19,6 @@ Standards-Version: 3.9.3 Package: cloud-init Architecture: all Depends: cloud-utils, - distribute, procps, python, #for $r in $requires -- cgit v1.2.3 From 5ce32eb77572d93d37ec01af452955151c913e9c Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 14 Nov 2012 15:27:47 -0500 Subject: open 0.7.2 --- ChangeLog | 1 + 1 file changed, 1 insertion(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index cc0dc607..427aadc5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +0.7.2: 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) -- cgit v1.2.3 From f97befb58244a4b3e72e73fe6d90772a4b3584c9 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 14 Nov 2012 15:28:16 -0500 Subject: add debian watch --- ChangeLog | 1 + packages/debian/watch | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 packages/debian/watch (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 427aadc5..cac92bce 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ 0.7.2: + - add a debian watch file 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/packages/debian/watch b/packages/debian/watch new file mode 100644 index 00000000..0f7a600b --- /dev/null +++ b/packages/debian/watch @@ -0,0 +1,2 @@ +version=3 +https://launchpad.net/cloud-init/+download .*/\+download/cloud-init-(.+)\.tar.gz -- cgit v1.2.3 From 7a7cf335453146a775c8566e5a4ed4716a03a874 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 19 Nov 2012 09:25:09 -0500 Subject: add 'sudo' entry for default user in default config/cloud.cfg LP: #1080717 --- ChangeLog | 1 + config/cloud.cfg | 1 + 2 files changed, 2 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index cac92bce..767b13d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 0.7.2: - add a debian watch file + - add 'sudo' entry to ubuntu's default user (LP: #1080717) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/config/cloud.cfg b/config/cloud.cfg index e0306a49..a8c74486 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -82,6 +82,7 @@ system_info: lock_passwd: True gecos: Ubuntu groups: [adm, audio, cdrom, dialout, floppy, video, plugdev, dip, netdev] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] shell: /bin/bash # Other config here will be given to the distro class and/or path classes paths: -- cgit v1.2.3 From d324a2cb0b10a4cd1b1b05dd23d0040ab3e9621c Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Tue, 20 Nov 2012 01:05:36 -0500 Subject: fix "resize_root: noblock" resize_root: noblock has been broken in the 0.7.0 series. Using it would disable resizing. LP: #1080985 --- ChangeLog | 1 + cloudinit/config/cc_resizefs.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 767b13d6..bd52f182 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 0.7.2: - add a debian watch file - add 'sudo' entry to ubuntu's default user (LP: #1080717) + - fix resizefs module when 'noblock' was provided (LP: #1080985) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py index b958f332..70294eda 100644 --- a/cloudinit/config/cc_resizefs.py +++ b/cloudinit/config/cc_resizefs.py @@ -32,6 +32,8 @@ RESIZE_FS_PREFIXES_CMDS = [ ('xfs', 'xfs_growfs'), ] +NOBLOCK = "noblock" + def nodeify_path(devpth, where, log): try: @@ -68,7 +70,7 @@ def handle(name, cfg, _cloud, log, args): else: resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True) - if not util.translate_bool(resize_root): + if not util.translate_bool(resize_root, addons=[NOBLOCK]): log.debug("Skipping module named %s, resizing disabled", name) return @@ -110,7 +112,7 @@ def handle(name, cfg, _cloud, log, args): log.debug("Resizing %s (%s) using %s", resize_what, fs_type, resizer) resize_cmd = [resizer, devpth] - if resize_root == "noblock": + if resize_root == NOBLOCK: # Fork to a child that will run # the resize command util.fork_cb(do_resize, resize_cmd, log) @@ -120,7 +122,7 @@ def handle(name, cfg, _cloud, log, args): do_resize(resize_cmd, log) action = 'Resized' - if resize_root == "noblock": + if resize_root == NOBLOCK: action = 'Resizing (via forking)' log.debug("%s root filesystem (type=%s, maj=%i, min=%i, val=%s)", action, fs_type, os.major(st_dev), os.minor(st_dev), resize_root) -- cgit v1.2.3 From 974e76eab2e43718802c8ef845e6696637e46930 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Sat, 1 Dec 2012 21:46:27 -0500 Subject: make sure no blank lines before cloud-init entry in ca-certificates.conf when /etc/ca-certificates.conf is read by update-ca-certificates lines after a blank line get ignored. Here, ensure that there are no blank lines, and no duplicate entries for cloud-init are added. LP: #1077020 --- ChangeLog | 2 + cloudinit/config/cc_ca_certs.py | 9 +++- .../test_handler/test_handler_ca_certs.py | 50 +++++++++++++++++++--- 3 files changed, 55 insertions(+), 6 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index bd52f182..13afb2c2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ - add a debian watch file - add 'sudo' entry to ubuntu's default user (LP: #1080717) - fix resizefs module when 'noblock' was provided (LP: #1080985) + - make sure there is no blank line before cloud-init entry in + there are no blank lines in /etc/ca-certificates.conf (LP: #1077020) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/cloudinit/config/cc_ca_certs.py b/cloudinit/config/cc_ca_certs.py index 20f24357..4f2a46a1 100644 --- a/cloudinit/config/cc_ca_certs.py +++ b/cloudinit/config/cc_ca_certs.py @@ -45,8 +45,15 @@ def add_ca_certs(certs): # First ensure they are strings... cert_file_contents = "\n".join([str(c) for c in certs]) util.write_file(CA_CERT_FULL_PATH, cert_file_contents, mode=0644) + # Append cert filename to CA_CERT_CONFIG file. - util.write_file(CA_CERT_CONFIG, "\n%s" % CA_CERT_FILENAME, omode="ab") + # We have to strip the content because blank lines in the file + # causes subsequent entries to be ignored. (LP: #1077020) + orig = util.load_file(CA_CERT_CONFIG) + cur_cont = '\n'.join([l for l in orig.splitlines() + if l != CA_CERT_FILENAME]) + out = "%s\n%s\n" % (cur_cont.rstrip(), CA_CERT_FILENAME) + util.write_file(CA_CERT_CONFIG, out, omode="wb") def remove_default_ca_certs(): diff --git a/tests/unittests/test_handler/test_handler_ca_certs.py b/tests/unittests/test_handler/test_handler_ca_certs.py index d73c9fa9..0558023a 100644 --- a/tests/unittests/test_handler/test_handler_ca_certs.py +++ b/tests/unittests/test_handler/test_handler_ca_certs.py @@ -138,15 +138,47 @@ class TestAddCaCerts(MockerTestCase): self.mocker.replay() cc_ca_certs.add_ca_certs([]) - def test_single_cert(self): - """Test adding a single certificate to the trusted CAs.""" + def test_single_cert_trailing_cr(self): + """Test adding a single certificate to the trusted CAs + when existing ca-certificates has trailing newline""" cert = "CERT1\nLINE2\nLINE3" + ca_certs_content = "line1\nline2\ncloud-init-ca-certs.crt\nline3\n" + expected = "line1\nline2\nline3\ncloud-init-ca-certs.crt\n" + + mock_write = self.mocker.replace(util.write_file, passthrough=False) + mock_load = self.mocker.replace(util.load_file, passthrough=False) + + mock_write("/usr/share/ca-certificates/cloud-init-ca-certs.crt", + cert, mode=0644) + + mock_load("/etc/ca-certificates.conf") + self.mocker.result(ca_certs_content) + + mock_write("/etc/ca-certificates.conf", expected, omode="wb") + self.mocker.replay() + + cc_ca_certs.add_ca_certs([cert]) + + def test_single_cert_no_trailing_cr(self): + """Test adding a single certificate to the trusted CAs + when existing ca-certificates has no trailing newline""" + cert = "CERT1\nLINE2\nLINE3" + + ca_certs_content = "line1\nline2\nline3" + mock_write = self.mocker.replace(util.write_file, passthrough=False) + mock_load = self.mocker.replace(util.load_file, passthrough=False) + mock_write("/usr/share/ca-certificates/cloud-init-ca-certs.crt", cert, mode=0644) + + mock_load("/etc/ca-certificates.conf") + self.mocker.result(ca_certs_content) + mock_write("/etc/ca-certificates.conf", - "\ncloud-init-ca-certs.crt", omode="ab") + "%s\n%s\n" % (ca_certs_content, "cloud-init-ca-certs.crt"), + omode="wb") self.mocker.replay() cc_ca_certs.add_ca_certs([cert]) @@ -157,10 +189,18 @@ class TestAddCaCerts(MockerTestCase): expected_cert_file = "\n".join(certs) mock_write = self.mocker.replace(util.write_file, passthrough=False) + mock_load = self.mocker.replace(util.load_file, passthrough=False) + mock_write("/usr/share/ca-certificates/cloud-init-ca-certs.crt", expected_cert_file, mode=0644) - mock_write("/etc/ca-certificates.conf", - "\ncloud-init-ca-certs.crt", omode="ab") + + ca_certs_content = "line1\nline2\nline3" + mock_load("/etc/ca-certificates.conf") + self.mocker.result(ca_certs_content) + + out = "%s\n%s\n" % (ca_certs_content, "cloud-init-ca-certs.crt") + mock_write("/etc/ca-certificates.conf", out, omode="wb") + self.mocker.replay() cc_ca_certs.add_ca_certs(certs) -- cgit v1.2.3 From 1e7b96743314f566814848ad05c5bc7271a5de91 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 3 Dec 2012 20:56:36 -0500 Subject: ChangeLog: mention fix of lp:1079002 --- ChangeLog | 1 + 1 file changed, 1 insertion(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 13afb2c2..c02334a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ - fix resizefs module when 'noblock' was provided (LP: #1080985) - make sure there is no blank line before cloud-init entry in there are no blank lines in /etc/ca-certificates.conf (LP: #1077020) + - fix sudoers writing when entry is a string (LP: #1079002) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) -- cgit v1.2.3 From cbd1ca764ed265460c3a79729a27ca8e3841390c Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 12 Dec 2012 10:39:43 -0500 Subject: add 'omnibus' as an install type for chef. Thanks to Anatoliy Dobrosynets --- ChangeLog | 1 + cloudinit/config/cc_chef.py | 15 ++++++++++++++- doc/examples/cloud-config-chef.txt | 9 ++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index fbfd3385..af1e024d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ - fix sudoers writing when entry is a string (LP: #1079002) - tools/write-ssh-key-fingerprints: use '-s' rather than '--stderr' option (LP: #1083715) + - support omnibus installer for chef [Anatoliy Dobrosynets] 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py index 7a3d6a31..607f789e 100644 --- a/cloudinit/config/cc_chef.py +++ b/cloudinit/config/cc_chef.py @@ -22,6 +22,7 @@ import json import os from cloudinit import templater +from cloudinit import url_helper from cloudinit import util RUBY_VERSION_DEFAULT = "1.8" @@ -35,6 +36,8 @@ CHEF_DIRS = [ '/var/run/chef', ] +OMNIBUS_URL = "https://www.opscode.com/chef/install.sh" + def handle(name, cfg, cloud, log, _args): @@ -83,7 +86,9 @@ def handle(name, cfg, cloud, log, _args): util.write_file('/etc/chef/firstboot.json', json.dumps(initial_json)) # If chef is not installed, we install chef based on 'install_type' - if not os.path.isfile('/usr/bin/chef-client'): + if (not os.path.isfile('/usr/bin/chef-client') or + util.get_cfg_option_bool(chef_cfg, 'force_install', default=False)): + install_type = util.get_cfg_option_str(chef_cfg, 'install_type', 'packages') if install_type == "gems": @@ -99,6 +104,14 @@ def handle(name, cfg, cloud, log, _args): elif install_type == 'packages': # this will install and run the chef-client from packages cloud.distro.install_packages(('chef',)) + elif install_type == 'omnibus': + url = util.get_cfg_option_str(chef_cfg, "omnibus_url", OMNIBUS_URL) + content = url_helper.readurl(url=url, retries=5) + with util.tempdir() as tmpd: + # use tmpd over tmpfile to avoid 'Text file busy' on execute + tmpf = "%s/chef-omnibus-install" % tmpd + util.write_file(tmpf, content, mode=0700) + util.subp([tmpf], capture=False) else: log.warn("Unknown chef install type %s", install_type) diff --git a/doc/examples/cloud-config-chef.txt b/doc/examples/cloud-config-chef.txt index f87472ec..4edad653 100644 --- a/doc/examples/cloud-config-chef.txt +++ b/doc/examples/cloud-config-chef.txt @@ -47,9 +47,13 @@ apt_sources: chef: - # Valid values are 'gems' and 'packages' + # Valid values are 'gems' and 'packages' and 'omnibus' install_type: "packages" + # Boolean: run 'install_type' code even if chef-client + # appears already installed. + force_install: false + # Chef settings server_url: "https://chef.yourorg.com:4000" @@ -80,6 +84,9 @@ chef: maxclients: 100 keepalive: "off" + # if install_type is 'omnibus', change the url to download + omnibus_url: "https://www.opscode.com/chef/install.sh" + # Capture all subprocess output into a logfile # Useful for troubleshooting cloud-init issues -- cgit v1.2.3 From c196afccda0293613e3586922347749c33dfddbf Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 17 Dec 2012 08:41:11 -0500 Subject: ensure a datasource's 'distro' and sys_cfg are updated After parsing and merging datasource's config, the changes in were not making it into the datasource's 'distro. The end result was that the when a config module was called, it's 'cloud' argument would be updated in 'cloud.distro', but not in 'cloud.datasource.distro'. This path was required for getting mirror settings to take affect, because they include information from the datasource. Ie: cc_apt_configure had mirror_info = cloud.datasource.get_package_mirror_info() the datasource then used *its* copy of sys_cfg to call self.distro.get_package_mirror_info and *that* distro's sys_cfg had not been updated. LP: #1090482 --- ChangeLog | 2 ++ cloudinit/stages.py | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index e4bcdd76..64a6e618 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,8 @@ option (LP: #1083715) - make install of puppet configurable (LP: #1090205) [Craig Tracey] - support omnibus installer for chef [Anatoliy Dobrosynets] + - fix bug where cloud-config in user-data could not modify system_info + settings (LP: #1090482) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/cloudinit/stages.py b/cloudinit/stages.py index d9391f39..8d3213b4 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -63,23 +63,29 @@ class Init(object): # Changed only when a fetch occurs self.datasource = NULL_DATA_SOURCE - def _reset(self, ds=False): + def _reset(self, reset_ds=False): # Recreated on access self._cfg = None self._paths = None self._distro = None - if ds: + if reset_ds: self.datasource = NULL_DATA_SOURCE @property def distro(self): if not self._distro: # Try to find the right class to use - scfg = self._extract_cfg('system') - name = scfg.pop('distro', 'ubuntu') - cls = distros.fetch(name) - LOG.debug("Using distro class %s", cls) - self._distro = cls(name, scfg, self.paths) + system_config = self._extract_cfg('system') + distro_name = system_config.pop('distro', 'ubuntu') + distro_cls = distros.fetch(distro_name) + LOG.debug("Using distro class %s", distro_cls) + self._distro = distro_cls(distro_name, system_config, self.paths) + # If we have an active datasource we need to adjust + # said datasource and move its distro/system config + # from whatever it was to a new set... + if self.datasource is not NULL_DATA_SOURCE: + self.datasource.distro = self._distro + self.datasource.sys_cfg = system_config return self._distro @property -- cgit v1.2.3 From 3569e71a1579b97f4e33fb46ab3fcef08a4ddad4 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 19 Dec 2012 10:09:39 -0500 Subject: add ChangeLog entry for previous commit --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 64a6e618..31a19996 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,6 +11,8 @@ - support omnibus installer for chef [Anatoliy Dobrosynets] - fix bug where cloud-config in user-data could not modify system_info settings (LP: #1090482) + - fix CloudStack DataSource to use Virtual Router as found in + /var/lib/dhcpclient rather than default gateway (LP: #1089989) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) -- cgit v1.2.3 From 4fde399a38765fa9641b3177b966ad6c8ec9750f Mon Sep 17 00:00:00 2001 From: Gerard Dethier Date: Mon, 7 Jan 2013 12:20:58 -0500 Subject: DataSourceCloudStack: fallback to default route if no virtual router found Changes in revision 753 broke cloud-init on ubuntu, as it has a different dhclient directory than Fedora where the change was developed and tested. This change does 2 things: * searches multiple directories (including /var/lib/dhcp) for the lease files. * adds a fallback to the old code path of choosing the default route as the virtual router if there were no virtual routers found in the lease files. LP: #1089989 --- ChangeLog | 6 ++- cloudinit/sources/DataSourceCloudStack.py | 82 ++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 20 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 9534be26..18e25725 100644 --- a/ChangeLog +++ b/ChangeLog @@ -11,8 +11,10 @@ - support omnibus installer for chef [Anatoliy Dobrosynets] - fix bug where cloud-config in user-data could not modify system_info settings (LP: #1090482) - - fix CloudStack DataSource to use Virtual Router as found in - /var/lib/dhcpclient rather than default gateway (LP: #1089989) + - fix CloudStack DataSource to use Virtual Router as described by + CloudStack documentation if it is available by searching through dhclient + lease files. If it is not available, then fall back to the default + gateway. (LP: #1089989) - fix redaction of password field in log (LP: #1096417) - fix to cloud-config user setup. Previously, lock_passwd was broken and all accounts would be locked unless 'system' was given (LP: #1096423). 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(" 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 -- cgit v1.2.3 From e561742aeab1e8090467f0fa304ee06e82e85f2c Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 16 Jan 2013 19:46:30 -0500 Subject: DataSourceConfigDrive: consider CD rom as valid config-drive source. previously, there was an attempt in the config drive source to limit the source device to a "full block device" rather than a partition. This was done by a simplistic approach of checking that the last character of the name was not a number. That was filtering out CD-rom devices (sr0). Now, we have a bit more sophisticated approach to that same problem. We filter out block devices that have a 'partition' entry in /sys/class/block/DEVICE_NAME/partition . LP: #1100545 --- ChangeLog | 2 ++ cloudinit/sources/DataSourceConfigDrive.py | 2 +- cloudinit/util.py | 7 +++++++ tests/unittests/test_datasource/test_configdrive.py | 17 ++++++++++++----- 4 files changed, 22 insertions(+), 6 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 544032a2..f076a27f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -20,6 +20,8 @@ all accounts would be locked unless 'system' was given (LP: #1096423). - Allow 'sr0' (or sr[0-9]) to be specified without /dev/ as a source for mounts. [Vlastimil Holer] + - allow config-drive-data to come from a CD device by more correctly + filtering out partitions. (LP: #1100545) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) 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/util.py b/cloudinit/util.py index ab918433..c0ea8d91 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -1553,3 +1553,10 @@ 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) diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py index 6751a679..930086db 100644 --- a/tests/unittests/test_datasource/test_configdrive.py +++ b/tests/unittests/test_datasource/test_configdrive.py @@ -257,19 +257,25 @@ class TestConfigDriveDataSource(MockerTestCase): ds.read_config_drive_dir, my_d) def test_find_candidates(self): - devs_with_answers = { - "TYPE=vfat": [], - "TYPE=iso9660": ["/dev/vdb"], - "LABEL=config-2": ["/dev/vdb"], - } + devs_with_answers = {} def my_devs_with(criteria): return devs_with_answers[criteria] + def my_is_partition(dev): + return dev[-1] in "0123456789" and not dev.startswith("sr") + try: orig_find_devs_with = util.find_devs_with util.find_devs_with = my_devs_with + orig_is_partition = util.is_partition + util.is_partition = my_is_partition + + devs_with_answers = {"TYPE=vfat": [], + "TYPE=iso9660": ["/dev/vdb"], + "LABEL=config-2": ["/dev/vdb"], + } self.assertEqual(["/dev/vdb"], ds.find_candidate_devs()) # add a vfat item @@ -285,6 +291,7 @@ class TestConfigDriveDataSource(MockerTestCase): finally: util.find_devs_with = orig_find_devs_with + util.is_partition = orig_is_partition def test_pubkeys_v2(self): """Verify that public-keys work in config-drive-v2.""" -- cgit v1.2.3 From 7656c36ef194bac61286466f86187ff8affff26e Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Mon, 28 Jan 2013 09:55:29 -0500 Subject: add entries to ChangeLog --- ChangeLog | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index f076a27f..8d750ac1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,16 @@ mounts. [Vlastimil Holer] - allow config-drive-data to come from a CD device by more correctly filtering out partitions. (LP: #1100545) + - setup docs to be available on read-the-docs + https://cloudinit.readthedocs.org/en/latest/ (LP: #1093039) + - add HACKING file for information on contributing + - handle the legacy 'user:' configuration better, making it affect the + configured OS default user (LP: #1100920) + - Adding a resolv.conf configuration module (LP: #1100434). Currently only + working on redhat systems (no support for resolvconf) + - support grouping linux distros into "os_families". This allows a module + to operate on the family (redhat or debian) rather than the distro (ubuntu, + debian, fedora, rhel) (LP: #1100029) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) -- cgit v1.2.3 From 50222a4a387e5e013bc48df7a7a208698368a527 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jan 2013 08:14:54 -0500 Subject: doc: fix example in cloud-config-write-files.txt The write_files documentation incorrectly used 'perms' rather than 'permissions'. LP: #1111205 --- ChangeLog | 2 ++ doc/examples/cloud-config-write-files.txt | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index e1b08d30..3a688ad0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -34,6 +34,8 @@ debian, fedora, rhel) (LP: #1100029) - fix /etc/hosts writing when templates are used (LP: #1100036) - add package versioning logic to package installation functionality (LP: #1108047) + - fix documentation for write_files to correctly list 'permissions' + rather than 'perms' (LP: #1111205) 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/doc/examples/cloud-config-write-files.txt b/doc/examples/cloud-config-write-files.txt index 9c4e3998..ec98bc93 100644 --- a/doc/examples/cloud-config-write-files.txt +++ b/doc/examples/cloud-config-write-files.txt @@ -12,7 +12,7 @@ write_files: content: CiMgVGhpcyBmaWxlIGNvbnRyb2xzIHRoZSBzdGF0ZSBvZiBTRUxpbnV4... owner: root:root path: /etc/sysconfig/selinux - perms: '0644' + permissions: '0644' - content: | # My new /etc/sysconfig/samba file @@ -24,10 +24,10 @@ write_files: AAAAAAAAAwAAAAQAAAAAAgAAAAAAAAACQAAAAAAAAAJAAAAAAAAcAAAAAAAAABwAAAAAAAAAAQAA .... path: /bin/arch - perms: '0555' + permissions: '0555' - encoding: gzip content: !!binary | H4sIAIDb/U8C/1NW1E/KzNMvzuBKTc7IV8hIzcnJVyjPL8pJ4QIA6N+MVxsAAAA= path: /usr/bin/hello - perms: '0755' + permissions: '0755' -- cgit v1.2.3 From c26f0e086959634ddf41c843c5c9dd73998b7c7e Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jan 2013 14:48:32 -0500 Subject: Changelog: reformat to limit to 80 chars wide --- ChangeLog | 71 +++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 32 deletions(-) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 3a688ad0..4745a327 100644 --- a/ChangeLog +++ b/ChangeLog @@ -33,7 +33,8 @@ to operate on the family (redhat or debian) rather than the distro (ubuntu, debian, fedora, rhel) (LP: #1100029) - fix /etc/hosts writing when templates are used (LP: #1100036) - - add package versioning logic to package installation functionality (LP: #1108047) + - add package versioning logic to package installation + functionality (LP: #1108047) - fix documentation for write_files to correctly list 'permissions' rather than 'perms' (LP: #1111205) 0.7.1: @@ -72,12 +73,13 @@ - fix how string escaping was not working when the string was a unicode string which was causing the warning message not to be written out (LP: #1075756) - - for boto > 0.6.0 there was a lazy load of the metadata added, when cloud-init - runs the usage of this lazy loading is hidden and since that lazy loading - will be performed on future attribute access we must traverse the lazy loaded - dictionary and force it to full expand so that if cloud-init blocks the ec2 - metadata port the lazy loaded dictionary will continue working properly - instead of trying to make additional url calls which will fail (LP: #1068801) + - for boto > 0.6.0 there was a lazy load of the metadata added, when + cloud-init runs the usage of this lazy loading is hidden and since that lazy + loading will be performed on future attribute access we must traverse the + lazy loaded dictionary and force it to full expand so that if cloud-init + blocks the ec2 metadata port the lazy loaded dictionary will continue + working properly instead of trying to make additional url calls which will + fail (LP: #1068801) - use a set of helper/parsing classes to perform system configuration for easier test. (/etc/sysconfig, /etc/hostname, resolv.conf, /etc/hosts) - add power_state_change config module for shutting down stystem after @@ -92,7 +94,8 @@ - do not 'start networking' in cloud-init-nonet, but add cloud-init-container job that runs only if in container and emits net-device-added (LP: #1031065) - - search only top level dns for 'instance-data' in DataSourceEc2 (LP: #1040200) + - search only top level dns for 'instance-data' in + DataSourceEc2 (LP: #1040200) - add support for config-drive-v2 (LP:#1037567) - support creating users, including the default user. [Ben Howard] (LP: #1028503) @@ -182,8 +185,8 @@ detailed information on python 2.6 and 2.7 - forced all code loading, moving, chmod, writing files and other system level actions to go through standard set of util functions, this greatly - helps in debugging and determining exactly which system actions cloud-init is - performing + helps in debugging and determining exactly which system actions cloud-init + is performing - adjust url fetching and url trying to go through a single function that reads urls in the new 'url helper' file, this helps in tracing, debugging and knowing which urls are being called and/or posted to from with-in @@ -219,28 +222,30 @@ very simple set of ec2 meta-data back to callers - useful for testing or for understanding what the ec2 meta-data service can provide in terms of data or functionality - - for ssh key and authorized key file parsing add in classes and util functions - that maintain the state of individual lines, allowing for a clearer - separation of parsing and modification (useful for testing and tracing) + - for ssh key and authorized key file parsing add in classes and util + functions that maintain the state of individual lines, allowing for a + clearer separation of parsing and modification (useful for testing and + tracing) - add a set of 'base' init.d scripts that can be used on systems that do not have full upstart or systemd support (or support that does not match the standard fedora/ubuntu implementation) - currently these are being tested on RHEL 6.2 - separate the datasources into there own subdirectory (instead of being - a top-level item), this matches how config 'modules' and user-data 'handlers' - are also in there own subdirectory (thus helping new developers and others - understand the code layout in a quicker manner) + a top-level item), this matches how config 'modules' and user-data + 'handlers' are also in there own subdirectory (thus helping new developers + and others understand the code layout in a quicker manner) - add the building of rpms based off a new cli tool and template 'spec' file that will templatize and perform the necessary commands to create a source - and binary package to be used with a cloud-init install on a 'rpm' supporting - system + and binary package to be used with a cloud-init install on a 'rpm' + supporting system - uses the new standard set of requires and converts those pypi requirements into a local set of package requirments (that are known to exist on RHEL systems but should also exist on fedora systems) - - adjust the bdeb builder to be a python script (instead of a shell script) and - make its 'control' file a template that takes in the standard set of pypi - dependencies and uses a local mapping (known to work on ubuntu) to create the - packages set of dependencies (that should also work on ubuntu-like systems) + - adjust the bdeb builder to be a python script (instead of a shell script) + and make its 'control' file a template that takes in the standard set of + pypi dependencies and uses a local mapping (known to work on ubuntu) to + create the packages set of dependencies (that should also work on + ubuntu-like systems) - pythonify a large set of various pieces of code - remove wrapping return statements with () when it has no effect - upper case all constants used @@ -251,8 +256,8 @@ there own equality) - use context managers on locks, tempdir, chdir, file, selinux, umask, unmounting commands so that these actions do not have to be closed and/or - cleaned up manually in finally blocks, which is typically not done and will - eventually be a bug in the future + cleaned up manually in finally blocks, which is typically not done and + will eventually be a bug in the future - use the 'abc' module for abstract classes base where possible - applied in the datasource root class, the distro root class, and the user-data v2 root class @@ -282,17 +287,18 @@ config without sections better (and it also maintains comments instead of removing them) - use the new defaulting config parser (that will not raise errors on sections - that do not exist or return errors when values are fetched that do not exist) - in the 'puppet' module + that do not exist or return errors when values are fetched that do not + exist) in the 'puppet' module - for config 'modules' add in the ability for the module to provide a list of - distro names which it is known to work with, if when ran and the distro being - used name does not match one of those in this list, a warning will be written - out saying that this module may not work correctly on this distrobution + distro names which it is known to work with, if when ran and the distro + being used name does not match one of those in this list, a warning will be + written out saying that this module may not work correctly on this + distrobution - for all dynamically imported modules ensure that they are fixed up before they are used by ensuring that they have certain attributes, if they do not have those attributes they will be set to a sensible set of defaults instead - adjust all 'config' modules and handlers to use the adjusted util functions - and the new distro objects where applicable so that those pieces of code can + and the new distro objects where applicable so that those pieces of code can benefit from the unified and enhanced functionality being provided in that util module - fix a potential bug whereby when a #includeonce was encountered it would @@ -300,8 +306,8 @@ it would continue checking against that cache, instead of refetching (which would likely be the expected case) - add a openstack/nova based pep8 extension utility ('hacking.py') that allows - for custom checks (along with the standard pep8 checks) to occur when running - 'make pep8' and its derivatives + for custom checks (along with the standard pep8 checks) to occur when + running 'make pep8' and its derivatives 0.6.4: - support relative path in AuthorizedKeysFile (LP: #970071). - make apt-get update run with --quiet (suitable for logging) (LP: #1012613) @@ -489,3 +495,4 @@ - make the message on 'disable_root' more clear (LP: #672417) - do not require public key if private is given in ssh cloud-config (LP: #648905) +# vi: syntax=text textwidth=79 -- cgit v1.2.3 From 1bb72070b70edaa960b3158feba936fbc3687b1f Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Thu, 31 Jan 2013 14:49:22 -0500 Subject: upstart/cloud-init-container.conf: ensure /run/network exists ifquery will exit failure if there is no /run/network directory. normally that would get created by one of network-interface.conf or networking.conf. But, it is possible that we're running before either of those have. --- ChangeLog | 1 + upstart/cloud-init-container.conf | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'ChangeLog') diff --git a/ChangeLog b/ChangeLog index 4745a327..639d5560 100644 --- a/ChangeLog +++ b/ChangeLog @@ -37,6 +37,7 @@ functionality (LP: #1108047) - fix documentation for write_files to correctly list 'permissions' rather than 'perms' (LP: #1111205) + - cloud-init-container.conf: ensure /run/network before running ifquery 0.7.1: - sysvinit: fix missing dependency in cloud-init job for RHEL 5.6 - config-drive: map hostname to local-hostname (LP: #1061964) diff --git a/upstart/cloud-init-container.conf b/upstart/cloud-init-container.conf index 051c6e50..6bdbe77e 100644 --- a/upstart/cloud-init-container.conf +++ b/upstart/cloud-init-container.conf @@ -21,6 +21,12 @@ script # if the all static network interfaces are already up, nothing to do [ -f "$MARK_STATIC_NETWORK_EMITTED" ] && exit 0 + # ifquery will exit failure if there is no /run/network directory. + # normally that would get created by one of network-interface.conf + # or networking.conf. But, it is possible that we're running + # before either of those have. + mkdir -p /run/network + # get list of all 'auto' interfaces. if there are none, nothing to do. auto_list=$(ifquery --list --allow auto 2>/dev/null) || : [ -z "$auto_list" ] && exit 0 -- cgit v1.2.3