diff options
-rw-r--r-- | ChangeLog | 6 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rwxr-xr-x | cloud-init.py | 42 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_update_etc_hosts.py | 41 | ||||
-rw-r--r-- | cloudinit/util.py | 38 | ||||
-rw-r--r-- | config/cloud.cfg | 10 | ||||
-rw-r--r-- | config/cloud.cfg.d/05_logging.cfg (renamed from config/logging.cfg) | 0 | ||||
-rw-r--r-- | config/cloud.cfg.d/README | 3 | ||||
-rw-r--r-- | debian.trunk/changelog | 2 | ||||
-rw-r--r-- | doc/examples/cloud-config.txt | 7 | ||||
-rwxr-xr-x | setup.py | 3 | ||||
-rw-r--r-- | templates/hosts.tmpl | 17 | ||||
-rw-r--r-- | upstart/cloud-init-nonet.conf | 22 | ||||
-rw-r--r-- | upstart/cloud-init.conf | 3 |
14 files changed, 166 insertions, 29 deletions
@@ -16,6 +16,12 @@ - make DataSourceEc2 retries and timeout configurable - add helper routines for apt-get update and install - add 'bootcmd' like 'runcmd' to cloud-config syntax for running things early + - move from '#opt_include' in config file format to conf_d. + ie, now files in /etc/cloud.cfg.d/ is read rather than reading + '#opt_include <filename>' or '#include <filename>' in cloud.cfg + - allow /etc/hosts to be written from hosts.tmpl. which allows + getting local-hostname into /etc/hosts (LP: #720440) + - better handle startup if there is no eth0 (LP: #714807) 0.6.0: - change permissions of /var/log/cloud-init.log to accomodate syslog writing to it (LP: #704509) @@ -1,4 +1,3 @@ -- conf.d rather than 'opt_include' (or #include) - consider 'failsafe' DataSource If all others fail, setting a default that - sets the user password, writing it to console diff --git a/cloud-init.py b/cloud-init.py index e8454eaa..3c9df13e 100755 --- a/cloud-init.py +++ b/cloud-init.py @@ -27,6 +27,7 @@ import cloudinit.DataSource as ds import time import logging import errno +import os def warn(wstr): sys.stderr.write("WARN:%s" % wstr) @@ -75,29 +76,50 @@ def main(): except Exception as e: warn("Failed to get and set output config: %s\n" % e) - msg = "cloud-init %s running: %s. up %s seconds" % (cmd, now, uptime) - sys.stderr.write(msg + "\n") - sys.stderr.flush() - cloudinit.logging_set_from_cfg(cfg) log = logging.getLogger() - log.info(msg) try: cloudinit.initfs() except Exception as e: warn("failed to initfs, likely bad things to come: %s\n" % str(e)) - - # cache is not instance specific, so it has to be purged - # but we want 'start' to benefit from a cache if - # a previous start-local populated one - if cmd == "start-local": + nonet_path = "%s/%s" % (cloudinit.get_cpath("data"), "no-net") + + if cmd == "start": + stop_files = ( cloudinit.get_ipath_cur("obj_pkl"), nonet_path ) + # if starting as the network start, there are cases + # where everything is already done for us, and it makes + # most sense to exit early and silently + for f in stop_files: + try: + fp = open("/var/lib/cloud/instance/obj.pkl","r") + fp.close() + except: + continue + + log.debug("no need for cloud-init start to run (%s)\n", f) + sys.exit(0) + elif cmd == "start-local": + # cache is not instance specific, so it has to be purged + # but we want 'start' to benefit from a cache if + # a previous start-local populated one manclean = util.get_cfg_option_bool(cfg, 'manual_cache_clean',False) if manclean: log.debug("not purging cache, manual_cache_clean = True") cloudinit.purge_cache(not manclean) + try: + os.unlink(nonet_path) + except OSError as e: + if e.errno != errno.ENOENT: raise + + msg = "cloud-init %s running: %s. up %s seconds" % (cmd, now, uptime) + sys.stderr.write(msg + "\n") + sys.stderr.flush() + + log.info(msg) + cloud = cloudinit.CloudInit(ds_deps=deps[cmd]) try: diff --git a/cloudinit/CloudConfig/cc_update_etc_hosts.py b/cloudinit/CloudConfig/cc_update_etc_hosts.py new file mode 100644 index 00000000..856cbae1 --- /dev/null +++ b/cloudinit/CloudConfig/cc_update_etc_hosts.py @@ -0,0 +1,41 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2011 Canonical Ltd. +# +# Author: Scott Moser <scott.moser@canonical.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/>. +import cloudinit.util as util +from cloudinit.CloudConfig import per_always + +frequency = per_always + +def handle(name,cfg,cloud,log,args): + if not util.get_cfg_option_bool(cfg,"manage_etc_hosts",False): + log.debug("manage_etc_hosts is not set. not modifying /etc/hosts") + return + + try: + hostname = util.get_cfg_option_str(cfg,"hostname",cloud.get_hostname()) + if not hostname: + hostname = cloud.get_hostname() + + if not hostname: + log.info("manage_etc_hosts was set, but no hostname found") + return + + util.render_to_file('hosts', '/etc/hosts', { 'hostname' : hostname }) + + except Exception as e: + log.warn("failed to update /etc/hosts") + raise diff --git a/cloudinit/util.py b/cloudinit/util.py index 72db58f9..3a40cc99 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -44,9 +44,7 @@ def get_base_cfg(cfgfile,cfg_builtin="", parsed_cfgs=None): if parsed_cfgs and cfgfile in parsed_cfgs: return(parsed_cfgs[cfgfile]) - contents = read_file_with_includes(cfgfile) - if contents: - syscfg = yaml.load(contents) + syscfg = read_conf_with_confd(cfgfile) kern_contents = read_cc_from_cmdline() if kern_contents: @@ -260,6 +258,40 @@ def read_file_with_includes(fname, rel = ".", stack=[], patt = None): stack.pop() return(contents) +def read_conf_d(confd): + # get reverse sorted list (later trumps newer) + confs = sorted(os.listdir(confd),reverse=True) + + # remove anything not ending in '.cfg' + confs = filter(lambda f: f.endswith(".cfg"), confs) + + # remove anything not a file + confs = filter(lambda f: os.path.isfile("%s/%s" % (confd,f)),confs) + + cfg = { } + for conf in confs: + cfg = mergedict(cfg,read_conf("%s/%s" % (confd,conf))) + + return(cfg) + +def read_conf_with_confd(cfgfile): + cfg = read_conf(cfgfile) + confd = False + if "conf_d" in cfg: + if cfg['conf_d'] is not None: + confd = cfg['conf_d'] + if not isinstance(confd,str): + raise Exception("cfgfile %s contains 'conf_d' with non-string" % cfgfile) + elif os.path.isdir("%s.d" % cfgfile): + confd = "%s.d" % cfgfile + + if not confd: return(cfg) + + confd_cfg = read_conf_d(confd) + + return(mergedict(confd_cfg,cfg)) + + def get_cmdline(): if 'DEBUG_PROC_CMDLINE' in os.environ: cmdline = os.environ["DEBUG_PROC_CMDLINE"] diff --git a/config/cloud.cfg b/config/cloud.cfg index c27cc5e8..f8e18ac4 100644 --- a/config/cloud.cfg +++ b/config/cloud.cfg @@ -8,6 +8,7 @@ cloud_init_modules: - resizefs - set_hostname - update_hostname + - update_etc_hosts - rsyslog cloud_config_modules: @@ -32,12 +33,3 @@ cloud_final_modules: - keys-to-console - phone-home - final-message - -## logging.cfg contains info on logging output for cloud-init -#include logging.cfg - -## distro.cfg contains the local distro specific values -#opt_include distro.cfg - -##local.cfg is for local overrides of any of the above -#opt_include local.cfg diff --git a/config/logging.cfg b/config/cloud.cfg.d/05_logging.cfg index 2e7ac2ed..2e7ac2ed 100644 --- a/config/logging.cfg +++ b/config/cloud.cfg.d/05_logging.cfg diff --git a/config/cloud.cfg.d/README b/config/cloud.cfg.d/README new file mode 100644 index 00000000..60702e9d --- /dev/null +++ b/config/cloud.cfg.d/README @@ -0,0 +1,3 @@ +# All files in this directory will be read by cloud-init +# They are read in lexical order. Later files overwrite values in +# earlier files. diff --git a/debian.trunk/changelog b/debian.trunk/changelog index 33f97f28..300d77a4 100644 --- a/debian.trunk/changelog +++ b/debian.trunk/changelog @@ -1,4 +1,4 @@ -cloud-init (0.6.0) UNRELEASED; urgency=low +cloud-init (0.6.1) UNRELEASED; urgency=low * build diff --git a/doc/examples/cloud-config.txt b/doc/examples/cloud-config.txt index 22be848d..c1d0b278 100644 --- a/doc/examples/cloud-config.txt +++ b/doc/examples/cloud-config.txt @@ -402,4 +402,9 @@ ssh_pwauth: True # this option to 'True', and maintain (remove) that link before the image # will be booted as a new instance. # default is False -manual_cache_clean = False +manual_cache_clean: False + +# if you wish to have /etc/hosts written from /etc/cloud/templates/hosts.tmpl +# on a per-always basis (to account for ebs stop/start), then set +# manage_etc_hosts to True. The default is 'False' +manage_etc_hosts: False @@ -27,7 +27,7 @@ def is_f(p): return(os.path.isfile(p)) setup(name='cloud-init', - version='0.6.0', + version='0.6.1', description='EC2 initialisation magic', author='Scott Moser', author_email='scott.moser@canonical.com', @@ -38,6 +38,7 @@ setup(name='cloud-init', 'cloud-init-cfg.py', ], data_files=[('/etc/cloud', glob('config/*.cfg')), + ('/etc/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')), ('/etc/cloud/templates', glob('templates/*')), ('/etc/init', glob('upstart/*.conf')), ('/usr/share/cloud-init', []), diff --git a/templates/hosts.tmpl b/templates/hosts.tmpl index 642e7a7e..36db43b5 100644 --- a/templates/hosts.tmpl +++ b/templates/hosts.tmpl @@ -1,5 +1,20 @@ -127.0.0.1 localhost +## This file (/etc/cloud/templates/hosts.tmpl) is only utilized +## if enabled in cloud-config. Specifically, in order to enable it +## you need to add the following to config: +## manage_etc_hosts: True +## +## Note, double-hash commented lines will not appear in /etc/hosts +# +# Your system has configured 'manage_etc_hosts' as True. +# As a result, if you wish for changes to this file to persist +# then you will need to either +# a.) make changes to the master file in /etc/cloud/templates/hosts.tmpl +# b.) change or remove the value of 'manage_etc_hosts' in +# /etc/cloud/cloud.cfg or cloud-config from user-data +# +## The value '$hostname' will be replaced with the local-hostname 127.0.1.1 $hostname +127.0.0.1 localhost # The following lines are desirable for IPv6 capable hosts ::1 ip6-localhost ip6-loopback diff --git a/upstart/cloud-init-nonet.conf b/upstart/cloud-init-nonet.conf new file mode 100644 index 00000000..2ac98286 --- /dev/null +++ b/upstart/cloud-init-nonet.conf @@ -0,0 +1,22 @@ +# cloud-init-no-net +# the purpose of this job is +# * to block running of cloud-init until a non 'lo' interface is up +# * timeout if one doens't come up in a reasonable amount of time +start on mounted MOUNTPOINT=/ and stopped cloud-init-local +stop on net-device-up IFACE!=lo +task + +console output + +script + # if a non 'lo' interface is up, exit immediately + grep -qv '^lo' /var/run/network/ifstate && exit 0 + + [ -f /var/lib/cloud/instance/obj.pkl ] && exit 0 + sleep 10 + echo $UPSTART_JOB "waiting for a network device." + sleep 60 + echo $UPSTART_JOB "gave up waiting for a network device." + : > /var/lib/cloud/data/no-net +end script +# EOF diff --git a/upstart/cloud-init.conf b/upstart/cloud-init.conf index cb2b437b..b9be5981 100644 --- a/upstart/cloud-init.conf +++ b/upstart/cloud-init.conf @@ -1,7 +1,6 @@ # cloud-init - the initial cloud-init job # crawls metadata service, emits cloud-config -start on (mounted MOUNTPOINT=/ and net-device-up IFACE=eth0 and \ - stopped cloud-init-local ) +start on mounted MOUNTPOINT=/ and stopped cloud-init-nonet task |