summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--TODO1
-rwxr-xr-xcloud-init.py42
-rw-r--r--cloudinit/CloudConfig/cc_update_etc_hosts.py41
-rw-r--r--cloudinit/util.py38
-rw-r--r--config/cloud.cfg10
-rw-r--r--config/cloud.cfg.d/05_logging.cfg (renamed from config/logging.cfg)0
-rw-r--r--config/cloud.cfg.d/README3
-rw-r--r--debian.trunk/changelog2
-rw-r--r--doc/examples/cloud-config.txt7
-rwxr-xr-xsetup.py3
-rw-r--r--templates/hosts.tmpl17
-rw-r--r--upstart/cloud-init-nonet.conf22
-rw-r--r--upstart/cloud-init.conf3
14 files changed, 166 insertions, 29 deletions
diff --git a/ChangeLog b/ChangeLog
index de9468a1..01282954 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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)
diff --git a/TODO b/TODO
index 424587b5..568bdb07 100644
--- a/TODO
+++ b/TODO
@@ -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
diff --git a/setup.py b/setup.py
index 3eed7b7c..fc66abbf 100755
--- a/setup.py
+++ b/setup.py
@@ -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