summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorScott Moser <smoser@ubuntu.com>2011-01-21 21:49:55 -0500
committerScott Moser <smoser@ubuntu.com>2011-01-21 21:49:55 -0500
commit7e2e87f2de893835900eb5a96458a865f36c624c (patch)
tree9f4e168b418b18eb4df0bedaaad5ed57bc09cbd3 /cloudinit
parent154722cc740299c6c9d1b3bc9d1e96088f9259eb (diff)
downloadvyos-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__.py48
-rw-r--r--cloudinit/CloudConfig/cc_rsyslog.py27
-rw-r--r--cloudinit/CloudConfig/cc_set_hostname.py38
-rw-r--r--cloudinit/CloudConfig/cc_update_hostname.py94
-rw-r--r--cloudinit/__init__.py8
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]))