diff options
author | Scott Moser <smoser@ubuntu.com> | 2011-01-21 21:49:55 -0500 |
---|---|---|
committer | Scott Moser <smoser@ubuntu.com> | 2011-01-21 21:49:55 -0500 |
commit | 7e2e87f2de893835900eb5a96458a865f36c624c (patch) | |
tree | 9f4e168b418b18eb4df0bedaaad5ed57bc09cbd3 /cloudinit | |
parent | 154722cc740299c6c9d1b3bc9d1e96088f9259eb (diff) | |
download | vyos-cloud-init-7e2e87f2de893835900eb5a96458a865f36c624c.tar.gz vyos-cloud-init-7e2e87f2de893835900eb5a96458a865f36c624c.zip |
add function to cloud-init to run cloud-config style modules
add 'hostname' cloud-config option for setting hostname
make rsyslog and resizefs run at cloud-init time
Diffstat (limited to 'cloudinit')
-rw-r--r-- | cloudinit/CloudConfig/__init__.py | 48 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_rsyslog.py | 27 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_set_hostname.py | 38 | ||||
-rw-r--r-- | cloudinit/CloudConfig/cc_update_hostname.py | 94 | ||||
-rw-r--r-- | cloudinit/__init__.py | 8 |
5 files changed, 206 insertions, 9 deletions
diff --git a/cloudinit/CloudConfig/__init__.py b/cloudinit/CloudConfig/__init__.py index 0a91059a..bfed44b7 100644 --- a/cloudinit/CloudConfig/__init__.py +++ b/cloudinit/CloudConfig/__init__.py @@ -29,8 +29,11 @@ class CloudConfig(): cfgfile = None cfg = None - def __init__(self,cfgfile): - self.cloud = cloudinit.CloudInit() + def __init__(self,cfgfile, cloud=None): + if cloud == None: + self.cloud = cloudinit.CloudInit() + else: + self.cloud = cloud self.cfg = self.get_config_obj(cfgfile) self.cloud.get_data_source() @@ -38,6 +41,7 @@ class CloudConfig(): try: cfg = util.read_conf(cfgfile) except: + # TODO: this 'log' could/should be passed in cloudinit.log.critical("Failed loading of cloud config '%s'. Continuing with empty config\n" % cfgfile) cloudinit.log.debug(traceback.format_exc() + "\n") cfg = None @@ -58,3 +62,43 @@ class CloudConfig(): except: raise +# reads a cloudconfig module list, returns +# a 2 dimensional array suitable to pass to run_cc_modules +def read_cc_modules(cfg,name): + if name not in cfg: return([]) + module_list = [] + # create 'module_list', an array of arrays + # where array[0] = config + # array[1] = freq + # array[2:] = arguemnts + for item in cfg[name]: + if isinstance(item,str): + module_list.append((item,)) + elif isinstance(item,list): + module_list.append(item) + else: + raise TypeError("failed to read '%s' item in config") + return(module_list) + +def run_cc_modules(cc,module_list,log): + failures = [] + for cfg_mod in module_list: + name = cfg_mod[0] + freq = None + run_args = [ ] + if len(cfg_mod) > 1: + freq = cfg_mod[1] + if len(cfg_mod) > 2: + run_args = cfg_mod[2:] + + try: + log.debug("handling %s with freq=%s and args=%s" % + (name, freq, run_args )) + cc.handle(name, run_args, freq=freq) + except: + log.warn(traceback.format_exc()) + log.err("config handling of %s, %s, %s failed\n" % + (name,freq,run_args)) + failures.append(name) + + return(failures) diff --git a/cloudinit/CloudConfig/cc_rsyslog.py b/cloudinit/CloudConfig/cc_rsyslog.py index 53fa1d23..3320dbb2 100644 --- a/cloudinit/CloudConfig/cc_rsyslog.py +++ b/cloudinit/CloudConfig/cc_rsyslog.py @@ -33,7 +33,7 @@ def handle(name,cfg,cloud,log,args): # *.* @@syslogd.example.com # process 'rsyslog' - if not 'rsyslog' in cfg: return True + if not 'rsyslog' in cfg: return def_dir = cfg.get('rsyslog_dir', DEF_DIR) def_fname = cfg.get('rsyslog_filename', DEF_FILENAME) @@ -69,18 +69,31 @@ def handle(name,cfg,cloud,log,args): elst.append((content, "failed to write to %s" % filename)) # need to restart syslogd + restarted = False try: + # if this config module is running at cloud-init time + # (before rsyslog is running) we don't actually have to + # restart syslog. + # + # upstart actually does what we want here, in that it doesn't + # start a service that wasn't running already on 'restart' + # it will also return failure on the attempt, so 'restarted' + # won't get set log.debug("restarting rsyslog") p = util.subp(['service', 'rsyslog', 'restart']) + restarted = True + except Exception, e: elst.append(("restart", str(e))) + if restarted: + # this only needs to run if we *actually* restarted + # syslog above. + cloudinit.logging_set_from_cfg_file() + log = logging.getLogger() + log.debug("rsyslog configured %s" % files) + for e in elst: log.warn("rsyslog error: %s\n" % ':'.join(e)) - return False - - cloudinit.logging_set_from_cfg_file() - log = logging.getLogger() - log.debug("rsyslog configured %s" % files) - return True + return diff --git a/cloudinit/CloudConfig/cc_set_hostname.py b/cloudinit/CloudConfig/cc_set_hostname.py new file mode 100644 index 00000000..34621e97 --- /dev/null +++ b/cloudinit/CloudConfig/cc_set_hostname.py @@ -0,0 +1,38 @@ +# 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 +import subprocess + +def handle(name,cfg,cloud,log,args): + if util.get_cfg_option_bool(cfg,"preserve_hostname",False): + log.debug("preserve_hostname is set. not setting hostname") + return(True) + + try: + hostname = util.get_cfg_option_str(cfg,"hostname",cloud.get_hostname()) + set_hostname(hostname, log) + except Exception, e: + util.logexc(log) + log.warn("failed to set hostname\n") + + return(True) + +def set_hostname(hostname, log): + subprocess.Popen(['hostname', hostname]).communicate() + util.write_file("/etc/hostname","%s\n" % hostname, 0644) + log.debug("populated /etc/hostname with %s on first boot", hostname) diff --git a/cloudinit/CloudConfig/cc_update_hostname.py b/cloudinit/CloudConfig/cc_update_hostname.py new file mode 100644 index 00000000..c06a434b --- /dev/null +++ b/cloudinit/CloudConfig/cc_update_hostname.py @@ -0,0 +1,94 @@ +# 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 +import subprocess +from cloudinit.CloudConfig import per_always + +frequency = per_always + +def handle(name,cfg,cloud,log,args): + if util.get_cfg_option_bool(cfg,"preserve_hostname",False): + log.debug("preserve_hostname is set. not updating hostname") + return + + try: + hostname = util.get_cfg_option_str(cfg,"hostname",cloud.get_hostname()) + prev ="%s/%s" % (cloud.get_cpath('datadir'),"previous-hostname") + update_hostname(hostname, prev, log) + except Exception, e: + log.warn("failed to set hostname\n") + raise + +# read hostname from a 'hostname' file +# allow for comments and stripping line endings. +# if file doesn't exist, or no contents, return default +def read_hostname(filename, default=None): + try: + fp = open(filename,"r") + lines = fp.readlines() + fp.close() + for line in lines: + hpos = line.find("#") + if hpos != -1: + line = line[0:hpos] + line = line.rstrip() + if line: + return line + except IOError, e: + if e.errno == errno.ENOENT: pass + return default + +def update_hostname(hostname, previous, log): + etc_file = "/etc/hostname" + + hostname_prev = None + hostname_in_etc = None + + try: + hostname_prev = read_hostname(prev_file) + except: + log.warn("Failed to open %s" % prev_file) + + try: + hostname_in_etc = read_hostname(etc_file) + except: + log.warn("Failed to open %s" % etc_file) + + update_files = [] + if not hostname_prev or hostname_prev != hostname: + update_files.append(prev_file) + + if (not hostname_in_etc or + (hostname_in_etc == hostname_prev and hostname_in_etc != hostname)): + update_files.append(etc_file) + + try: + for fname in update_files: + util.write_file(fname,"%s\n" % hostname, 0644) + log.debug("wrote %s to %s" % (hostname,fname)) + except: + log.warn("failed to write hostname to %s" % fname) + + if hostname_in_etc and hostname_prev and hostname_in_etc != hostname_prev: + log.debug("%s differs from %s. assuming user maintained" % + (prev_file,etc_file)) + + if etc_file in update_files: + log.debug("setting hostname to %s" % hostname) + subprocess.Popen(['hostname', hostname]).communicate() + diff --git a/cloudinit/__init__.py b/cloudinit/__init__.py index 889455d2..12bd0fe1 100644 --- a/cloudinit/__init__.py +++ b/cloudinit/__init__.py @@ -478,6 +478,13 @@ class CloudInit: def device_name_to_device(self,name): return(self.datasource.device_name_to_device(name)) + # I really don't know if this should be here or not, but + # I needed it in cc_update_hostname, where that code had a valid 'cloud' + # reference, but did not have a cloudinit handle + # (ie, no cloudinit.get_cpath()) + def get_cpath(self,name=None): + return(get_cpath,name) + def initfs(): subds = [ 'scripts/per-instance', 'scripts/per-once', 'scripts/per-boot', @@ -507,6 +514,7 @@ def purge_cache(): return(False) return(True) +# get_ipath_cur: get the current instance path for an item def get_ipath_cur(name=None): return("%s/instance/%s" % (varlibdir, pathmap[name])) |