From 84197ecd42fecd51919c578e3f10b3c85b50bf84 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 9 Jun 2012 12:30:57 -0700 Subject: Start adding place where distro specifics can go. --- cloudinit/distros/__init__.py | 0 cloudinit/distros/ubuntu.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 cloudinit/distros/__init__.py create mode 100644 cloudinit/distros/ubuntu.py (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3 From 70640d62e1dffeabceacf16b52a3122fe914c297 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 11 Jun 2012 17:13:19 -0700 Subject: Adding in the root distro class + a util function to fetch various distros. --- cloudinit/distros/__init__.py | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index e69de29b..f7f48d1f 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -0,0 +1,45 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser +# Author: Juerg Haefliger +# Author: Joshua Harlow +# +# 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 . + +import contextlib + +from cloudinit import importer + + +class Distro(object): + def __init__(self, cloud): + self.cloud = cloud + + def install_packages(self, pkglist): + raise NotImplementedError() + + def apply_network(self, settings): + raise NotImplementedError() + + +def fetch(cfg, cloud): + sys_info = cfg.get('system_info', {}) + distro = sys_info.get('distro', 'ubuntu') + mod_name = "%s.%s" % (__name__, distro) + mod = importer.import_module(mod_name) + distro_cls = getattr(mod, 'Distro') + return distro_cls(cloud) \ No newline at end of file -- cgit v1.2.3 From 3b964024040bed3c217b6d4f043af55d0dbc5469 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 11 Jun 2012 17:14:46 -0700 Subject: Adding in the base of a ubuntu distro subclass that can install pkgs (right now). --- cloudinit/distros/ubuntu.py | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index e69de29b..d19ef63e 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -0,0 +1,55 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser +# Author: Juerg Haefliger +# Author: Joshua Harlow +# +# 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 . + +import os + +from cloudinit import distros +from cloudinit import log as logging +from cloudinit import util + +from cloudinit.settings import (PER_INSTANCE) + + +LOG = logging.getLogger(__name__) + + +class Distro(distros.Distro): + + def install_packages(self, pkglist): + self.update_package_sources() + self.apt_get('install', pkglist) + + def apply_network(self, settings): + pass + + # apt_get top level command (install, update...), and args to pass it + def apt_get(self, tlc, args=None): + e = os.environ.copy() + e['DEBIAN_FRONTEND'] = 'noninteractive' + cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold', + '--assume-yes', tlc] + if args: + cmd.extend(args) + util.subp(cmd, env=e) + + def update_package_sources(self): + self.cloud.run("update-sources", self.apt_get, ["update"], freq=PER_INSTANCE) \ No newline at end of file -- cgit v1.2.3 From 2a17d8fa939a3b85b5592b81d9b0bd146197578a Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 11 Jun 2012 18:01:45 -0700 Subject: Add initial network writing here. --- cloudinit/distros/ubuntu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index d19ef63e..ccf2cec4 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -39,7 +39,7 @@ class Distro(distros.Distro): self.apt_get('install', pkglist) def apply_network(self, settings): - pass + util.write_file("/etc/network/interfaces", settings) # apt_get top level command (install, update...), and args to pass it def apt_get(self, tlc, args=None): -- cgit v1.2.3 From 37c0976f5d94c5abf47a848d92acd76feaf150a7 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 15 Jun 2012 17:48:09 -0700 Subject: Continued adding distro specific functionality to this new parent distro class as needed. --- cloudinit/distros/__init__.py | 133 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 10 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index f7f48d1f..90607668 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -20,26 +20,139 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import contextlib +import abc +import copy from cloudinit import importer +from cloudinit import util + +from StringIO import StringIO + +# TODO: Make this via config?? +IFACE_ACTIONS = { + 'up': ['ifup', '--all'], + 'down': ['ifdown', '--all'], +} class Distro(object): - def __init__(self, cloud): - self.cloud = cloud + __metaclass__ = abc.ABCMeta + + def __init__(self, cfg, runner): + self._runner = runner + self._cfg = util.get_cfg_by_path(cfg, ('system_info', ), {}) + self.name = self._cfg.pop("distro", 'generic') + + @abc.abstractmethod def install_packages(self, pkglist): raise NotImplementedError() - def apply_network(self, settings): + @abc.abstractmethod + def _write_network(self, settings): + # In the future use the python-netcf + # to write this blob out in a distro format + raise NotImplementedError() + + def get_option(self, opt_name, default=None): + return self._cfg.get(opt_name, default) + + @abc.abstractmethod + def set_hostname(self, hostname): raise NotImplementedError() + @abc.abstractmethod + def update_hostname(self, hostname, prev_hostname_fn): + raise NotImplementedError() + + @abc.abstractmethod + def package_command(self, cmd, args=None): + raise NotImplementedError() + + def get_package_mirror(self): + return self.get_option('package_mirror') + + def get_paths(self): + paths = self.get_option("paths") or {} + return copy.deepcopy(paths) + + def apply_network(self, settings, bring_up=True): + # Write it out + self._write_network(settings) + # Now try to bring them up + if bring_up: + self._interface_action('up') + + @abc.abstractmethod + def set_timezone(self, tz): + raise NotImplementedError() + + def _get_localhost_ip(self): + return "127.0.0.1" + + def update_etc_hosts(self, hostname, fqdn): + # Format defined at + # http://unixhelp.ed.ac.uk/CGI/man-cgi?hosts + header = "# Added by cloud-init" + real_header = "%s on %s" % (header, util.time_rfc2822()) + local_ip = self._get_localhost_ip() + hosts_line = "%s\t%s %s" % (local_ip, fqdn, hostname) + new_etchosts = StringIO() + need_write = False + need_change = True + for line in util.load_file("/etc/hosts").splitlines(): + if line.strip().startswith(header): + continue + if not line.strip() or line.strip().startswith("#"): + new_etchosts.write("%s\n" % (line)) + continue + split_line = [s.strip() for s in line.split()] + if len(split_line) < 2: + new_etchosts.write("%s\n" % (line)) + continue + (ip, hosts) = split_line[0], split_line[1:] + if ip == local_ip: + if sorted([hostname, fqdn]) == sorted(hosts): + need_change = False + if need_change: + line = "%s\n%s" % (real_header, hosts_line) + need_change = False + need_write = True + new_etchosts.write("%s\n" % (line)) + if need_change: + new_etchosts.write("%s\n%s\n" % (real_header, hosts_line)) + need_write = True + if need_write: + contents = new_etchosts.getvalue() + util.write_file("/etc/hosts", contents) + + def _interface_action(self, action): + if action not in IFACE_ACTIONS: + raise NotImplementedError("Unknown interface action %s" % (action)) + cmd = IFACE_ACTIONS[action] + try: + LOG.info("Attempting to run %s interface action using command %s", + action, cmd) + (_out, err) = util.subp(cmd) + if len(err): + LOG.warn("Running %s resulted in stderr output: %s", + IF_UP_CMD, err) + return True + except util.ProcessExecutionError as exc: + util.logexc(LOG, "Running %s failed", cmd) + return False + -def fetch(cfg, cloud): - sys_info = cfg.get('system_info', {}) - distro = sys_info.get('distro', 'ubuntu') - mod_name = "%s.%s" % (__name__, distro) - mod = importer.import_module(mod_name) +def fetch(distro_name, mods=(__name__, )): + mod = None + for m in mods: + try: + mod_name = "%s.%s" % (m, distro_name) + mod = importer.import_module(mod_name) + except RuntimeError: + pass + if not mod: + raise RuntimeError("No distribution found for distro %s" % (distro_name)) distro_cls = getattr(mod, 'Distro') - return distro_cls(cloud) \ No newline at end of file + return distro_cls + -- cgit v1.2.3 From db4614f3ea4eecd8283b75283effd3e369fb0e48 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 15 Jun 2012 17:49:28 -0700 Subject: Moved as many distro specific actions to here as should be needed. Mainly this is the following: 1. Hostname updating 2. Package command invocation 3. Network settings writings 4. Timezone setting (needs cleanup) --- cloudinit/distros/ubuntu.py | 81 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 8 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index ccf2cec4..b8aff03c 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -20,36 +20,101 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from StringIO import StringIO + import os +import socket from cloudinit import distros from cloudinit import log as logging +from cloudinit import templater from cloudinit import util from cloudinit.settings import (PER_INSTANCE) - LOG = logging.getLogger(__name__) class Distro(distros.Distro): def install_packages(self, pkglist): - self.update_package_sources() - self.apt_get('install', pkglist) + self._update_package_sources() + self._apt_get('install', pkglist) - def apply_network(self, settings): + def _write_network(self, settings): util.write_file("/etc/network/interfaces", settings) + def package_command(self, command, args=None): + self._apt_get(command, args) + + def set_hostname(self, hostname): + util.write_file("/etc/hostname", "%s\n" % hostname, 0644) + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def update_hostname(self, hostname, prev_file): + hostname_prev = None + prev_name = self._read_hostname(prev_file) + hostname_in_etc = self._read_hostname("/etc/hostname") + 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/hostname") + for fn in update_files: + try: + util.write_file(fn, "%s\n" % hostname, 0644) + except: + util.logexc(LOG, "Failed to write hostname %s to %s", hostname, fn) + if hostname_in_etc and hostname_prev and hostname_in_etc != hostname_prev: + LOG.debug(("%s differs from /etc/hostname." + " Assuming user maintained hostname."), prev_file) + if "/etc/hostname" in update_files: + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _read_hostname(filename, default=None): + contents = util.load_file(filename, quiet=True) + for line in contents.splitlines(): + hpos = line.find("#") + if hpos != -1: + line = line[0:hpos] + line = line.rstrip() + if line: + return line + return default + + def _get_localhost_ip(self): + # Note: http://www.leonardoborda.com/blog/127-0-1-1-ubuntu-debian/ + return "127.0.1.1" + + def set_timezone(self, tz): + tz_file = os.path.join("/usr/share/zoneinfo", tz) + if not os.path.isfile(tz_file): + raise Exception("Invalid timezone %s, no file found at %s" % (tz, tz_file)) + tz_contents = "%s\n" % tz + util.write_file("/etc/timezone", tz_contents) + # TODO, this should be in a rhel distro subclass?? + if os.path.exists("/etc/sysconfig/clock"): + tz_contents = '"%s"\n' % tz + util.write_file("/etc/sysconfig/clock", tz_contents) + # This ensures that the correct tz will be used for the system + util.copy(tz_file, "/etc/localtime") + + def name(self): + return "ubuntu" + # apt_get top level command (install, update...), and args to pass it - def apt_get(self, tlc, args=None): + def _apt_get(self, tlc, args=None): e = os.environ.copy() e['DEBIAN_FRONTEND'] = 'noninteractive' cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold', '--assume-yes', tlc] if args: cmd.extend(args) - util.subp(cmd, env=e) + # Allow the output of this to flow outwards (ie not be captured) + util.subp(cmd, env=e, capture=False) - def update_package_sources(self): - self.cloud.run("update-sources", self.apt_get, ["update"], freq=PER_INSTANCE) \ No newline at end of file + def _update_package_sources(self): + self.runner.run("update-sources", self._apt_get, ["update"], freq=PER_INSTANCE) \ No newline at end of file -- cgit v1.2.3 From f6b08a5dbad79ff62608626e055ff4c30d3c7492 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 15 Jun 2012 18:37:05 -0700 Subject: Variable name mismatch --- cloudinit/distros/ubuntu.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index b8aff03c..e68f3064 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -53,8 +53,7 @@ class Distro(distros.Distro): util.subp(['hostname', hostname]) def update_hostname(self, hostname, prev_file): - hostname_prev = None - prev_name = self._read_hostname(prev_file) + hostname_prev = self._read_hostname(prev_file) hostname_in_etc = self._read_hostname("/etc/hostname") update_files = [] if not hostname_prev or hostname_prev != hostname: -- cgit v1.2.3 From b7b4d8543780579e902cca4e2485e3ef8347f0df Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 15 Jun 2012 18:38:10 -0700 Subject: Self variable missing --- cloudinit/distros/ubuntu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index e68f3064..6b0aff47 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -73,7 +73,7 @@ class Distro(distros.Distro): LOG.debug("Setting hostname to %s", hostname) util.subp(['hostname', hostname]) - def _read_hostname(filename, default=None): + def _read_hostname(self, filename, default=None): contents = util.load_file(filename, quiet=True) for line in contents.splitlines(): hpos = line.find("#") -- cgit v1.2.3 From 450261a1fcf1f8929a2f7a25c2c278ba40689289 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 15 Jun 2012 21:33:55 -0700 Subject: Fixups to ensure that pylint does not find anything major wrong. --- cloudinit/cloud.py | 2 - cloudinit/distros/__init__.py | 21 +++--- cloudinit/distros/ubuntu.py | 23 +++--- cloudinit/handlers/boot_hook.py | 14 ++-- cloudinit/handlers/cloud_config.py | 4 +- cloudinit/handlers/shell_script.py | 5 +- cloudinit/handlers/upstart_job.py | 7 +- cloudinit/helpers.py | 41 ++--------- cloudinit/log.py | 2 +- cloudinit/sources/DataSourceCloudStack.py | 2 +- cloudinit/sources/DataSourceConfigDrive.py | 4 +- cloudinit/sources/DataSourceMAAS.py | 14 ++-- cloudinit/sources/DataSourceNoCloud.py | 3 +- cloudinit/sources/DataSourceOVF.py | 12 +-- cloudinit/sources/__init__.py | 23 +++--- cloudinit/stages.py | 98 ++++++++++++++++--------- cloudinit/transforms/__init__.py | 2 +- cloudinit/transforms/cc_apt_update_upgrade.py | 12 ++- cloudinit/transforms/cc_bootcmd.py | 8 +- cloudinit/transforms/cc_byobu.py | 2 +- cloudinit/transforms/cc_ca_certs.py | 13 +++- cloudinit/transforms/cc_chef.py | 3 +- cloudinit/transforms/cc_disable_ec2_metadata.py | 3 +- cloudinit/transforms/cc_final_message.py | 4 +- cloudinit/transforms/cc_foo.py | 6 +- cloudinit/transforms/cc_grub_dpkg.py | 4 +- cloudinit/transforms/cc_keys_to_console.py | 23 ++++-- cloudinit/transforms/cc_landscape.py | 3 +- cloudinit/transforms/cc_locale.py | 2 +- cloudinit/transforms/cc_mcollective.py | 9 +-- cloudinit/transforms/cc_mounts.py | 15 ++-- cloudinit/transforms/cc_phone_home.py | 36 ++++++--- cloudinit/transforms/cc_puppet.py | 17 +++-- cloudinit/transforms/cc_resizefs.py | 38 ++++++---- cloudinit/transforms/cc_rightscale_userdata.py | 16 ++-- cloudinit/transforms/cc_rsyslog.py | 14 ++-- cloudinit/transforms/cc_runcmd.py | 9 ++- cloudinit/transforms/cc_salt_minion.py | 7 +- cloudinit/transforms/cc_scripts_per_boot.py | 5 +- cloudinit/transforms/cc_scripts_per_instance.py | 5 +- cloudinit/transforms/cc_scripts_per_once.py | 5 +- cloudinit/transforms/cc_scripts_user.py | 9 ++- cloudinit/transforms/cc_set_hostname.py | 2 +- cloudinit/transforms/cc_set_passwords.py | 10 ++- cloudinit/transforms/cc_ssh.py | 22 +++--- cloudinit/transforms/cc_ssh_import_id.py | 8 +- cloudinit/transforms/cc_timezone.py | 8 +- cloudinit/transforms/cc_update_etc_hosts.py | 22 ++++-- cloudinit/transforms/cc_update_hostname.py | 4 +- cloudinit/transforms/cc_welcome.py | 6 +- cloudinit/user_data.py | 13 ++-- cloudinit/util.py | 48 ++++++------ 52 files changed, 388 insertions(+), 300 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/cloud.py b/cloudinit/cloud.py index b2dfc749..8372d123 100644 --- a/cloudinit/cloud.py +++ b/cloudinit/cloud.py @@ -23,8 +23,6 @@ import copy import os -from cloudinit import distros -from cloudinit import helpers from cloudinit import log as logging LOG = logging.getLogger(__name__) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 90607668..fd4c70c1 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -20,29 +20,32 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from StringIO import StringIO + import abc import copy from cloudinit import importer +from cloudinit import log as logging from cloudinit import util -from StringIO import StringIO - # TODO: Make this via config?? IFACE_ACTIONS = { 'up': ['ifup', '--all'], 'down': ['ifdown', '--all'], } +LOG = logging.getLogger(__name__) + class Distro(object): __metaclass__ = abc.ABCMeta - def __init__(self, cfg, runner): + def __init__(self, name, cfg, runner): self._runner = runner - self._cfg = util.get_cfg_by_path(cfg, ('system_info', ), {}) - self.name = self._cfg.pop("distro", 'generic') + self._cfg = cfg + self.name = name @abc.abstractmethod def install_packages(self, pkglist): @@ -135,10 +138,9 @@ class Distro(object): action, cmd) (_out, err) = util.subp(cmd) if len(err): - LOG.warn("Running %s resulted in stderr output: %s", - IF_UP_CMD, err) + LOG.warn("Running %s resulted in stderr output: %s", cmd, err) return True - except util.ProcessExecutionError as exc: + except util.ProcessExecutionError: util.logexc(LOG, "Running %s failed", cmd) return False @@ -152,7 +154,8 @@ def fetch(distro_name, mods=(__name__, )): except RuntimeError: pass if not mod: - raise RuntimeError("No distribution found for distro %s" % (distro_name)) + raise RuntimeError("No distribution found for distro %s" + % (distro_name)) distro_cls = getattr(mod, 'Distro') return distro_cls diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 6b0aff47..9252a1c4 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -20,17 +20,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from StringIO import StringIO - import os -import socket from cloudinit import distros from cloudinit import log as logging -from cloudinit import templater from cloudinit import util -from cloudinit.settings import (PER_INSTANCE) +from cloudinit.settings import PER_INSTANCE LOG = logging.getLogger(__name__) @@ -65,9 +61,11 @@ class Distro(distros.Distro): try: util.write_file(fn, "%s\n" % hostname, 0644) except: - util.logexc(LOG, "Failed to write hostname %s to %s", hostname, fn) - if hostname_in_etc and hostname_prev and hostname_in_etc != hostname_prev: - LOG.debug(("%s differs from /etc/hostname." + util.logexc(LOG, "Failed to write hostname %s to %s", + hostname, fn) + if (hostname_in_etc and hostname_prev and + hostname_in_etc != hostname_prev): + LOG.debug(("%s differs from /etc/hostname." " Assuming user maintained hostname."), prev_file) if "/etc/hostname" in update_files: LOG.debug("Setting hostname to %s", hostname) @@ -91,7 +89,8 @@ class Distro(distros.Distro): def set_timezone(self, tz): tz_file = os.path.join("/usr/share/zoneinfo", tz) if not os.path.isfile(tz_file): - raise Exception("Invalid timezone %s, no file found at %s" % (tz, tz_file)) + raise Exception(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) tz_contents = "%s\n" % tz util.write_file("/etc/timezone", tz_contents) # TODO, this should be in a rhel distro subclass?? @@ -101,9 +100,6 @@ class Distro(distros.Distro): # This ensures that the correct tz will be used for the system util.copy(tz_file, "/etc/localtime") - def name(self): - return "ubuntu" - # apt_get top level command (install, update...), and args to pass it def _apt_get(self, tlc, args=None): e = os.environ.copy() @@ -116,4 +112,5 @@ class Distro(distros.Distro): util.subp(cmd, env=e, capture=False) def _update_package_sources(self): - self.runner.run("update-sources", self._apt_get, ["update"], freq=PER_INSTANCE) \ No newline at end of file + self._runner.run("update-sources", self._apt_get, + ["update"], freq=PER_INSTANCE) \ No newline at end of file diff --git a/cloudinit/handlers/boot_hook.py b/cloudinit/handlers/boot_hook.py index c75aeb72..b3aab366 100644 --- a/cloudinit/handlers/boot_hook.py +++ b/cloudinit/handlers/boot_hook.py @@ -32,9 +32,9 @@ LOG = logging.getLogger(__name__) class BootHookPartHandler(ud.PartHandler): - def __init__(self, boothook_dir, instance_id): + def __init__(self, paths, instance_id, **_kwargs): ud.PartHandler.__init__(self, PER_ALWAYS) - self.boothook_dir = boothook_dir + self.boothook_dir = paths.get_ipath("boothooks") self.instance_id = instance_id def list_types(self): @@ -54,13 +54,15 @@ class BootHookPartHandler(ud.PartHandler): start = len(prefix) + 1 filepath = os.path.join(self.boothook_dir, filename) - util.write_file(filepath, payload[start:], 0700) + contents = payload[start:] + util.write_file(filepath, contents, 0700) try: env = os.environ.copy() - env['INSTANCE_ID'] = str(self.instance_id) + if self.instance_id: + env['INSTANCE_ID'] = str(self.instance_id) util.subp([filepath], env=env) - except util.ProcessExecutionError as e: + except util.ProcessExecutionError: util.logexc(LOG, "Boothooks script %s execution error", filepath) - except Exception as e: + except Exception: util.logexc(LOG, ("Boothooks unknown " "error when running %s"), filepath) diff --git a/cloudinit/handlers/cloud_config.py b/cloudinit/handlers/cloud_config.py index f0e88eeb..12d1bd96 100644 --- a/cloudinit/handlers/cloud_config.py +++ b/cloudinit/handlers/cloud_config.py @@ -30,10 +30,10 @@ LOG = logging.getLogger(__name__) class CloudConfigPartHandler(ud.PartHandler): - def __init__(self, cloud_fn): + def __init__(self, paths, **_kwargs): ud.PartHandler.__init__(self, PER_ALWAYS) self.cloud_buf = [] - self.cloud_fn = cloud_fn + self.cloud_fn = paths.get_ipath("cloud_config") def list_types(self): return [ diff --git a/cloudinit/handlers/shell_script.py b/cloudinit/handlers/shell_script.py index 564e4623..f6e2ef16 100644 --- a/cloudinit/handlers/shell_script.py +++ b/cloudinit/handlers/shell_script.py @@ -32,10 +32,9 @@ LOG = logging.getLogger(__name__) class ShellScriptPartHandler(ud.PartHandler): - - def __init__(self, script_dir): + def __init__(self, paths, **_kwargs): ud.PartHandler.__init__(self, PER_ALWAYS) - self.script_dir = script_dir + self.script_dir = paths.get_ipath_cur('scripts') def list_types(self): return [ diff --git a/cloudinit/handlers/upstart_job.py b/cloudinit/handlers/upstart_job.py index 568a644a..059a4851 100644 --- a/cloudinit/handlers/upstart_job.py +++ b/cloudinit/handlers/upstart_job.py @@ -33,9 +33,9 @@ LOG = logging.getLogger(__name__) class UpstartJobPartHandler(ud.PartHandler): - def __init__(self, upstart_dir): + def __init__(self, paths, **_kwargs): ud.PartHandler.__init__(self, PER_INSTANCE) - self.upstart_dir = upstart_dir + self.upstart_dir = paths.upstart_conf_d def list_types(self): return [ @@ -46,6 +46,9 @@ class UpstartJobPartHandler(ud.PartHandler): if ctype in ud.CONTENT_SIGNALS: return + if not self.upstart_dir: + return + filename = util.clean_filename(filename) (_name, ext) = os.path.splitext(filename) if not ext: diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py index 2ecda3e9..c276a54c 100644 --- a/cloudinit/helpers.py +++ b/cloudinit/helpers.py @@ -30,11 +30,6 @@ from cloudinit.settings import (PER_INSTANCE, PER_ALWAYS, PER_ONCE) from cloudinit import log as logging from cloudinit import util -from cloudinit.user_data import boot_hook as bh_part -from cloudinit.user_data import cloud_config as cc_part -from cloudinit.user_data import shell_script as ss_part -from cloudinit.user_data import upstart_job as up_part - LOG = logging.getLogger(__name__) @@ -77,7 +72,7 @@ class FileSemaphores(object): sem_file = self._get_path(name, freq) try: util.del_file(sem_file) - except (IOError, OSError) as e: + except (IOError, OSError): util.logexc(LOG, "Failed deleting semaphore %s", sem_file) return False return True @@ -99,7 +94,7 @@ class FileSemaphores(object): contents = "%s: %s\n" % (os.getpid(), time()) try: util.write_file(sem_file, contents) - except (IOError, OSError) as e: + except (IOError, OSError): util.logexc(LOG, "Failed writing semaphore file %s", sem_file) return None return sem_file @@ -162,9 +157,10 @@ class Runners(object): class ContentHandlers(object): - def __init__(self, paths): + def __init__(self, paths, iid=None): self.paths = paths self.registered = {} + self.iid = iid def __contains__(self, item): return self.is_registered(item) @@ -191,34 +187,9 @@ class ContentHandlers(object): def iteritems(self): return self.registered.iteritems() - def _get_default_handlers(self): - def_handlers = [] - - cc_path = self.paths.get_ipath("cloud_config") - if cc_path: - cc_h = cc_part.CloudConfigPartHandler(cc_path) - def_handlers.append(cc_h) - - sc_path = self.paths.get_ipath_cur('scripts') - if sc_path: - ss_h = ss_part.ShellScriptPartHandler(sc_path) - def_handlers.append(ss_h) - - bh_path = self.paths.get_ipath("boothooks") - if bh_path: - bh_h = bh_part.BootHookPartHandler(bh_path) - def_handlers.append(bh_h) - - upstart_pth = self.paths.upstart_conf_d - if upstart_pth: - up_h = up_part.UpstartJobPartHandler(upstart_pth) - def_handlers.append(up_h) - - return def_handlers - - def register_defaults(self): + def register_defaults(self, defs): registered = set() - for mod in self._get_default_handlers(): + for mod in defs: for t in mod.list_types(): if not self.is_registered(t): self.registered[t] = mod diff --git a/cloudinit/log.py b/cloudinit/log.py index c247eb9e..5fcb77ef 100644 --- a/cloudinit/log.py +++ b/cloudinit/log.py @@ -56,7 +56,7 @@ def setupBasicLogging(): cfile = logging.FileHandler('/var/log/cloud-init.log') cfile.setFormatter(logging.Formatter(DEF_CON_FORMAT)) cfile.setLevel(DEBUG) - root.addHandle(cfile) + root.addHandler(cfile) except (IOError, OSError): # Likely that u can't write to that file... # Make console now have DEBUG?? diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py index 791df68f..27217e65 100644 --- a/cloudinit/sources/DataSourceCloudStack.py +++ b/cloudinit/sources/DataSourceCloudStack.py @@ -79,7 +79,7 @@ class DataSourceCloudStack(sources.DataSource): tot_time = (time.time() - start) LOG.debug("Crawl of metadata service took %s", int(tot_time)) return True - except Exception as e: + except Exception: util.logexc(LOG, ('Failed fetching from metadata ' 'service %s'), self.metadata_address) return False diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index 176b62b0..7450572f 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -51,8 +51,8 @@ class DataSourceConfigDrive(sources.DataSource): self.seed_dir = os.path.join(paths.seed_dir, 'config_drive') def __str__(self): - mstr = "%s[%s]" % (util.obj_name(self), self.dsmode) - mstr += " [seed=%s]" % (self.seed) + mstr = "%s [%s]" % (util.obj_name(self), self.dsmode) + mstr += "[seed=%s]" % (self.seed) return mstr def get_data(self): diff --git a/cloudinit/sources/DataSourceMAAS.py b/cloudinit/sources/DataSourceMAAS.py index 27196265..9e639649 100644 --- a/cloudinit/sources/DataSourceMAAS.py +++ b/cloudinit/sources/DataSourceMAAS.py @@ -18,9 +18,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os import errno import oauth.oauth as oauth +import os import time import urllib2 @@ -48,7 +48,7 @@ class DataSourceMAAS(sources.DataSource): self.seed_dir = os.path.join(paths.seed_dir, 'maas') def __str__(self): - return "%s[%s]" % (util.obj_name(self), self.base_url) + return "%s [%s]" % (util.obj_name(self), self.base_url) def get_data(self): mcfg = self.ds_cfg @@ -122,9 +122,10 @@ class DataSourceMAAS(sources.DataSource): starttime = time.time() check_url = "%s/%s/meta-data/instance-id" % (url, MD_VERSION) - url = util.wait_for_url(urls=[check_url], max_wait=max_wait, - timeout=timeout, status_cb=LOG.warn, - headers_cb=self.md_headers) + urls = [check_url] + url = uhelp.wait_for_url(urls=urls, max_wait=max_wait, + timeout=timeout, status_cb=LOG.warn, + headers_cb=self.md_headers) if url: LOG.info("Using metadata source: '%s'", url) @@ -185,7 +186,8 @@ def read_maas_seed_url(seed_url, header_cb=None, timeout=None, headers = {} try: (resp, sc) = uhelp.readurl(url, headers=headers, timeout=timeout) - md[name] = resp + if uhelp.ok_http_code(sc): + md[name] = resp except urllib2.HTTPError as e: if e.code != 404: raise diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py index 84d0f99d..2b016d1c 100644 --- a/cloudinit/sources/DataSourceNoCloud.py +++ b/cloudinit/sources/DataSourceNoCloud.py @@ -106,7 +106,8 @@ class DataSourceNoCloud(sources.DataSource): if e.errno != errno.ENOENT: raise except util.MountFailedError: - util.logexc(LOG, "Failed to mount %s when looking for seed", dev) + util.logexc(LOG, ("Failed to mount %s" + " when looking for data"), dev) # There was no indication on kernel cmdline or data # in the seeddir suggesting this handler should be used. diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index bb0f46c2..258d8d03 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -21,10 +21,10 @@ # along with this program. If not, see . from xml.dom import minidom + import base64 import os import re -import tempfile from cloudinit import log as logging from cloudinit import sources @@ -51,7 +51,7 @@ class DataSourceOVF(sources.DataSource): ud = "" defaults = { - "instance-id": "iid-dsovf" + "instance-id": "iid-dsovf", } (seedfile, contents) = get_ovf_env(self.paths.seed_dir) @@ -198,7 +198,7 @@ def transport_iso9660(require_iso=True): for dev in devs: fullp = os.path.join("/dev/", dev) - if (fullp in mounted or + if (fullp in mounts or not cdmatch.match(dev) or os.path.isdir(fullp)): continue @@ -210,7 +210,8 @@ def transport_iso9660(require_iso=True): continue try: - (fname, contents) = utils.mount_cb(fullp, get_ovf_env, mtype="iso9660") + (fname, contents) = util.mount_cb(fullp, + get_ovf_env, mtype="iso9660") except util.MountFailedError: util.logexc(LOG, "Failed mounting %s", fullp) continue @@ -265,7 +266,8 @@ def get_properties(contents): raise XmlError("No 'PropertySection's") props = {} - propElems = find_child(propSections[0], lambda n: n.localName == "Property") + propElems = find_child(propSections[0], + (lambda n: n.localName == "Property")) for elem in propElems: key = elem.attributes.getNamedItemNS(envNsURI, "key").value diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index 08669f5d..beb0f3d7 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -22,10 +22,9 @@ from cloudinit import importer from cloudinit import log as logging +from cloudinit import user_data as ud from cloudinit import util -from cloudinit.user_data import processor as ud_proc - DEP_FILESYSTEM = "FILESYSTEM" DEP_NETWORK = "NETWORK" DS_PREFIX = 'DataSource' @@ -42,7 +41,6 @@ class DataSource(object): self.sys_cfg = sys_cfg self.distro = distro self.paths = paths - self.userdata_proc = ud_proc.UserDataProcessor(paths) self.userdata = None self.metadata = None self.userdata_raw = None @@ -55,7 +53,7 @@ class DataSource(object): def get_userdata(self): if self.userdata is None: raw_data = self.get_userdata_raw() - self.userdata = self.userdata_proc.process(raw_data) + self.userdata = ud.UserDataProcessor(self.paths).process(raw_data) return self.userdata def get_userdata_raw(self): @@ -73,7 +71,7 @@ class DataSource(object): if not self.metadata or 'public-keys' not in self.metadata: return keys - if isinstance(self.metadata['public-keys'], (str)): + if isinstance(self.metadata['public-keys'], (basestring, str)): return str(self.metadata['public-keys']).splitlines() if isinstance(self.metadata['public-keys'], (list, set)): @@ -84,11 +82,12 @@ class DataSource(object): # lp:506332 uec metadata service responds with # data that makes boto populate a string for 'klist' rather # than a list. - if isinstance(klist, (str)): + 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 + # There is an empty string at + # the end of the keylist, trim it if pkey: keys.append(pkey) @@ -159,13 +158,14 @@ 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] LOG.info("Searching for data source in: %s", ds_names) + for cls in ds_list: ds = util.obj_name(cls) try: s = cls(distro, sys_cfg, paths) if s.get_data(): return (s, ds) - except Exception as e: + except Exception: util.logexc(LOG, "Getting data from %s failed", ds) msg = "Did not find any data source, searched classes: %s" % (ds_names) @@ -178,7 +178,8 @@ def find_source(sys_cfg, distro, paths, ds_deps, cfg_list, pkg_list): # return an ordered list of classes that match def list_sources(cfg_list, depends, pkg_list): src_list = [] - LOG.info("Looking for for data source in: %s, %s that match %s", cfg_list, pkg_list, depends) + LOG.info(("Looking for for data source in: %s," + " %s that matches %s"), cfg_list, pkg_list, depends) for ds_coll in cfg_list: ds_name = str(ds_coll) if not ds_name.startswith(DS_PREFIX): @@ -201,8 +202,8 @@ def list_sources(cfg_list, depends, pkg_list): if not cls_matches: continue src_list.extend(cls_matches) - LOG.debug("Found a match for data source %s in %s with matches %s", - ds_name, mod, cls_matches) + LOG.debug(("Found a match for data source %s" + " in %s with matches %s"), ds_name, mod, cls_matches) break return src_list diff --git a/cloudinit/stages.py b/cloudinit/stages.py index 2615d59f..b9076881 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -31,19 +31,23 @@ try: except ImportError: ConfigObj = None -from cloudinit.settings import (PER_INSTANCE, FREQUENCIES) from cloudinit.settings import (OLD_CLOUD_CONFIG) +from cloudinit.settings import (PER_INSTANCE, FREQUENCIES) + +from cloudinit.handlers import boot_hook as bh_part +from cloudinit.handlers import cloud_config as cc_part +from cloudinit.handlers import shell_script as ss_part +from cloudinit.handlers import upstart_job as up_part from cloudinit import cloud from cloudinit import distros -from cloudinit import modules from cloudinit import helpers from cloudinit import importer from cloudinit import log as logging from cloudinit import sources -from cloudinit import util - +from cloudinit import transforms from cloudinit import user_data as ud +from cloudinit import util LOG = logging.getLogger(__name__) @@ -73,12 +77,19 @@ class Init(object): def distro(self): if not self._distro: d_cfg = util.get_cfg_by_path(self.cfg, ('system_info'), {}) + # Ensure its a dictionary + if not isinstance(d_cfg, (dict)): + d_cfg = {} # Ensure not modified indirectly d_cfg = copy.deepcopy(d_cfg) + # Remove this since its path config, not distro config d_cfg.pop('paths', None) - distro_cls = distros.fetch(sys_cfg.pop('distro', 'ubuntu')) + # Try to find the right class to use + distro_name = d_cfg.pop('distro', 'ubuntu') + distro_cls = distros.fetch(distro_name) LOG.debug("Using distro class %s", distro_cls) - distro = distro_cls(d_cfg, helpers.Runners(self.paths)) + distro = distro_cls(distro_name, d_cfg, + helpers.Runners(self.paths)) self._distro = distro return self._distro @@ -93,7 +104,8 @@ class Init(object): @property def paths(self): if not self._paths: - path_info = util.get_cfg_by_path(self.cfg, ('system_info', 'paths'), {}) + path_info = util.get_cfg_by_path(self.cfg, + ('system_info', 'paths'), {}) # Ensure not modified indirectly path_info = copy.deepcopy(path_info) self._paths = helpers.Paths(path_info, self.datasource) @@ -156,7 +168,7 @@ class Init(object): # by using the instance link, if purge_cache was called # the file wont exist return pickle.loads(util.load_file(pickled_fn)) - except Exception as e: + except Exception: util.logexc(LOG, "Failed loading pickled datasource from %s", pickled_fn) return None @@ -166,7 +178,7 @@ class Init(object): try: contents = pickle.dumps(self.datasource) util.write_file(pickled_fn, contents, mode=0400) - except Exception as e: + except Exception: util.logexc(LOG, "Failed pickling datasource to %s", pickled_fn) return False @@ -192,7 +204,8 @@ class Init(object): # (which will affect user-data handlers down the line...) sys_cfg = copy.deepcopy(self.cfg) ds_deps = copy.deepcopy(self.ds_deps) - (ds, dsname) = sources.find_source(sys_cfg, self.distro, self.paths, + (ds, dsname) = sources.find_source(sys_cfg, self.distro, + self.paths, ds_deps, cfg_list, pkg_list) LOG.debug("Loaded datasource %s - %s", dsname, ds) self.datasource = ds @@ -270,6 +283,20 @@ class Init(object): processed_ud = "%s" % (self.datasource.get_userdata()) util.write_file(self.paths.get_ipath('userdata'), processed_ud, 0600) + def _default_userdata_handlers(self): + opts = { + 'paths': self.paths, + 'instance_id': self.datasource.get_instance_id(), + } + # TODO Hmmm, should we dynamically import these?? + def_handlers = [ + cc_part.CloudConfigPartHandler(**opts), + ss_part.ShellScriptPartHandler(**opts), + bh_part.BootHookPartHandler(**opts), + up_part.UpstartJobPartHandler(**opts), + ] + return def_handlers + def consume(self, frequency=PER_INSTANCE): cdir = self.paths.get_cpath("handlers") idir = self.paths.get_ipath("handlers") @@ -279,8 +306,11 @@ class Init(object): sys.path.insert(0, cdir) sys.path.insert(0, idir) + # Ensure datasource fetched before activation (just incase) + ud_obj = self.datasource.get_userdata() + # This keeps track of all the active handlers - c_handlers = helpers.ContentHandlers(self.paths) + c_handlers = helpers.ContentHandlers(paths=self.paths) # Add handlers in cdir potential_handlers = util.find_modules(cdir) @@ -292,13 +322,10 @@ class Init(object): except: util.logexc(LOG, "Failed to register handler from %s", fname) - def_handlers = c_handlers.register_defaults() - if def_handlers: - LOG.debug("Registered default handlers for %s", def_handlers) - - - # Ensure userdata fetched before activation (just incase) - ud_obj = self.datasource.get_userdata() + def_handlers = self._default_userdata_handlers() + applied_def_handlers = c_handlers.register_defaults(def_handlers) + if applied_def_handlers: + LOG.debug("Registered default handlers: %s", applied_def_handlers) # Form our cloud interface data = self.cloudify() @@ -334,11 +361,11 @@ class Init(object): class Transforms(object): - def __init__(self, cloudobj, cfgfile=None): - self.datasource = cloudobj.datasource + def __init__(self, init, cfgfile=None): + self.datasource = init.fetch() self.cfgfile = cfgfile - self.basecfg = copy.deepcopy(cloudobj.cfg) - self.cloudobj = cloudobj + self.basecfg = copy.deepcopy(init.cfg) + self.init = init # Created on first use self._cachedcfg = None @@ -409,25 +436,28 @@ class Transforms(object): (item, util.obj_name(item))) return module_list - def _transforms_modules(self, raw_mods): + def _fixup_transforms(self, raw_mods): mostly_mods = [] for raw_mod in raw_mods: raw_name = raw_mod['mod'] freq = raw_mod.get('freq') run_args = raw_mod.get('args') or [] - mod_name = modules.form_module_name(raw_name) + mod_name = transforms.form_module_name(raw_name) if not mod_name: continue if freq and freq not in FREQUENCIES: - LOG.warn("Config specified module %s has an unknown frequency %s", raw_name, freq) + LOG.warn(("Config specified transform %s" + " has an unknown frequency %s"), raw_name, freq) # Reset it so when ran it will get set to a known value freq = None - mod = modules.fixup_module(importer.import_module(mod_name)) + mod = transforms.fixup_module(importer.import_module(mod_name)) mostly_mods.append([mod, raw_name, freq, run_args]) return mostly_mods def _run_transforms(self, mostly_mods): failures = [] + d_name = self.init.distro.name + c_cloud = self.init.cloudify() for (mod, name, freq, args) in mostly_mods: try: # Try the modules frequency, otherwise fallback to a known one @@ -436,17 +466,17 @@ class Transforms(object): if not freq in FREQUENCIES: freq = PER_INSTANCE worked_distros = mod.distros - if worked_distros and self.cloud.distro.name() not in worked_distros: - LOG.warn(("Module %s is verified on %s distros" + if (worked_distros and d_name not in worked_distros): + LOG.warn(("Transform %s is verified on %s distros" " but not on %s distro. It may or may not work" - " correctly."), name, worked_distros, - self.cloud.distro.name()) + " correctly."), name, worked_distros, d_name) # Deep copy the config so that modules can't alter it + # Use the transforms logger and not our own func_args = [name, copy.deepcopy(self.cfg), - self.cloudobj, LOG, args] - # This name will affect the semphapore name created + c_cloud, transforms.LOG, args] + # This name will affect the semaphore name created run_name = "config-%s" % (name) - self.cloudobj.run(run_name, mod.handle, func_args, freq=freq) + c_cloud.run(run_name, mod.handle, func_args, freq=freq) except Exception as e: util.logexc(LOG, "Running %s failed", mod) failures.append((name, e)) @@ -454,5 +484,5 @@ class Transforms(object): def run(self, name): raw_mods = self._read_transforms(name) - mostly_mods = self._transforms_modules(raw_mods) + mostly_mods = self._fixup_transforms(raw_mods) return self._run_transforms(mostly_mods) diff --git a/cloudinit/transforms/__init__.py b/cloudinit/transforms/__init__.py index 8275b375..40affc4b 100644 --- a/cloudinit/transforms/__init__.py +++ b/cloudinit/transforms/__init__.py @@ -44,7 +44,7 @@ def fixup_module(mod, def_freq=PER_INSTANCE): else: freq = mod.frequency if freq and freq not in FREQUENCIES: - LOG.warn("Module %s has an unknown frequency %s", mod, freq) + LOG.warn("Transform %s has an unknown frequency %s", mod, freq) if not hasattr(mod, 'handle'): def empty_handle(_name, _cfg, _cloud, _log, _args): pass diff --git a/cloudinit/transforms/cc_apt_update_upgrade.py b/cloudinit/transforms/cc_apt_update_upgrade.py index c4a543ed..a4e058c6 100644 --- a/cloudinit/transforms/cc_apt_update_upgrade.py +++ b/cloudinit/transforms/cc_apt_update_upgrade.py @@ -71,7 +71,7 @@ def handle(_name, cfg, cloud, log, _args): except: util.logexc(log, "Failed to run debconf-set-selections") - pkglist = util.get_cfg_option_list_or_str(cfg, 'packages', []) + pkglist = util.get_cfg_option_list(cfg, 'packages', []) errors = [] if update or len(pkglist) or upgrade: @@ -96,7 +96,9 @@ def handle(_name, cfg, cloud, log, _args): errors.append(e) if len(errors): - raise errors[0] + log.warn("%s failed with exceptions, re-raising the last one", + len(errors)) + raise errors[-1] def mirror2lists_fileprefix(mirror): @@ -186,7 +188,8 @@ def add_sources(srclist, template_params=None): try: util.write_file(ent['filename'], source + "\n", omode="ab") except: - errorlist.append([source, "failed write to file %s" % ent['filename']]) + errorlist.append([source, + "failed write to file %s" % ent['filename']]) return errorlist @@ -219,9 +222,10 @@ def find_apt_mirror(cloud, cfg): doms.extend((".localdomain", "",)) mirror_list = [] + distro = cloud.distro.name mirrorfmt = "http://%s-mirror%s/%s" % (distro, "%s", distro) for post in doms: - mirror_list.append(mirrorfmt % post) + mirror_list.append(mirrorfmt % (post)) mirror = util.search_for_mirror(mirror_list) diff --git a/cloudinit/transforms/cc_bootcmd.py b/cloudinit/transforms/cc_bootcmd.py index a2efad32..80afb5e7 100644 --- a/cloudinit/transforms/cc_bootcmd.py +++ b/cloudinit/transforms/cc_bootcmd.py @@ -30,7 +30,8 @@ frequency = PER_ALWAYS def handle(name, cfg, cloud, log, _args): if "bootcmd" not in cfg: - log.debug("Skipping module named %s, no 'bootcomd' key in configuration", name) + log.debug(("Skipping transform named %s," + " no 'bootcomd' key in configuration"), name) return with tempfile.NamedTemporaryFile(suffix=".sh") as tmpf: @@ -39,7 +40,7 @@ def handle(name, cfg, cloud, log, _args): tmpf.write(content) tmpf.flush() except: - log.warn("Failed to shellify bootcmd") + util.logexc(log, "Failed to shellify bootcmd") raise try: @@ -48,5 +49,6 @@ def handle(name, cfg, cloud, log, _args): cmd = ['/bin/sh', tmpf.name] util.subp(cmd, env=env, capture=False) except: - log.warn("Failed to run commands from bootcmd") + util.logexc(log, + ("Failed to run bootcmd transform %s"), name) raise diff --git a/cloudinit/transforms/cc_byobu.py b/cloudinit/transforms/cc_byobu.py index 38586174..741aa934 100644 --- a/cloudinit/transforms/cc_byobu.py +++ b/cloudinit/transforms/cc_byobu.py @@ -30,7 +30,7 @@ def handle(name, cfg, _cloud, log, args): value = util.get_cfg_option_str(cfg, "byobu_by_default", "") if not value: - log.debug("Skipping module named %s, no 'byobu' values found", name) + log.debug("Skipping transform named %s, no 'byobu' values found", name) return if value == "user" or value == "system": diff --git a/cloudinit/transforms/cc_ca_certs.py b/cloudinit/transforms/cc_ca_certs.py index 8ca9a200..e0802bfe 100644 --- a/cloudinit/transforms/cc_ca_certs.py +++ b/cloudinit/transforms/cc_ca_certs.py @@ -23,6 +23,8 @@ CA_CERT_FILENAME = "cloud-init-ca-certs.crt" CA_CERT_CONFIG = "/etc/ca-certificates.conf" CA_CERT_SYSTEM_PATH = "/etc/ssl/certs/" +distros = ['ubuntu'] + def update_ca_certs(): """ @@ -70,22 +72,25 @@ def handle(name, cfg, _cloud, log, _args): """ # If there isn't a ca-certs section in the configuration don't do anything if "ca-certs" not in cfg: - log.debug("Skipping module named %s, no 'ca-certs' key in configuration", name) + log.debug(("Skipping transform named %s," + " no 'ca-certs' key in configuration"), name) return + ca_cert_cfg = cfg['ca-certs'] # If there is a remove-defaults option set to true, remove the system # default trusted CA certs first. if ca_cert_cfg.get("remove-defaults", False): - log.debug("removing default certificates") + log.debug("Removing default certificates") remove_default_ca_certs() # If we are given any new trusted CA certs to add, add them. if "trusted" in ca_cert_cfg: - trusted_certs = util.get_cfg_option_list_or_str(ca_cert_cfg, "trusted") + trusted_certs = util.get_cfg_option_list(ca_cert_cfg, "trusted") if trusted_certs: - log.debug("adding %d certificates" % len(trusted_certs)) + log.debug("Adding %d certificates" % len(trusted_certs)) add_ca_certs(trusted_certs) # Update the system with the new cert configuration. + log.debug("Updating certificates") update_ca_certs() diff --git a/cloudinit/transforms/cc_chef.py b/cloudinit/transforms/cc_chef.py index 12c2f539..473e5f8b 100644 --- a/cloudinit/transforms/cc_chef.py +++ b/cloudinit/transforms/cc_chef.py @@ -31,7 +31,8 @@ def handle(name, cfg, cloud, log, _args): # If there isn't a chef key in the configuration don't do anything if 'chef' not in cfg: - log.debug("Skipping module named %s, no 'chef' key in configuration", name) + log.debug(("Skipping transform named %s," + " no 'chef' key in configuration"), name) return chef_cfg = cfg['chef'] diff --git a/cloudinit/transforms/cc_disable_ec2_metadata.py b/cloudinit/transforms/cc_disable_ec2_metadata.py index 4d2a7f55..3c0dd57b 100644 --- a/cloudinit/transforms/cc_disable_ec2_metadata.py +++ b/cloudinit/transforms/cc_disable_ec2_metadata.py @@ -28,5 +28,6 @@ reject_cmd = ['route', 'add', '-host', '169.254.169.254', 'reject'] def handle(_name, cfg, _cloud, _log, _args): - if util.get_cfg_option_bool(cfg, "disable_ec2_metadata", False): + disabled = util.get_cfg_option_bool(cfg, "disable_ec2_metadata", False) + if disabled: util.subp(reject_cmd) diff --git a/cloudinit/transforms/cc_final_message.py b/cloudinit/transforms/cc_final_message.py index dc4ae34c..c257b6d0 100644 --- a/cloudinit/transforms/cc_final_message.py +++ b/cloudinit/transforms/cc_final_message.py @@ -32,7 +32,7 @@ final_message_def = ("Cloud-init v. {{version}} finished at {{timestamp}}." " Up {{uptime}} seconds.") -def handle(name, cfg, cloud, log, args): +def handle(_name, cfg, cloud, log, args): msg_in = None if len(args) != 0: @@ -60,7 +60,7 @@ def handle(name, cfg, cloud, log, args): # Use stdout, stderr or the logger?? content = templater.render_string(msg_in, subs) sys.stderr.write("%s\n" % (content)) - except Exception as e: + except Exception: util.logexc(log, "Failed to render final message template") boot_fin_fn = cloud.paths.boot_finished diff --git a/cloudinit/transforms/cc_foo.py b/cloudinit/transforms/cc_foo.py index 8007f981..99135704 100644 --- a/cloudinit/transforms/cc_foo.py +++ b/cloudinit/transforms/cc_foo.py @@ -45,8 +45,8 @@ from cloudinit.settings import PER_INSTANCE # informational purposes. If non existent all distros are assumed and # no warning occurs. -frequency = settings.PER_INSTANCE +frequency = PER_INSTANCE -def handle(name, _cfg, _cloud, _log, _args): - print("Hi from %s" % (name)) +def handle(name, _cfg, _cloud, log, _args): + log.debug("Hi from transform %s", name) diff --git a/cloudinit/transforms/cc_grub_dpkg.py b/cloudinit/transforms/cc_grub_dpkg.py index c048d5cc..02f05ce3 100644 --- a/cloudinit/transforms/cc_grub_dpkg.py +++ b/cloudinit/transforms/cc_grub_dpkg.py @@ -54,9 +54,9 @@ def handle(_name, cfg, _cloud, log, _args): # now idevs and idevs_empty are set to determined values # or, those set by user - dconf_sel = ("grub-pc grub-pc/install_devices string %s\n" + dconf_sel = (("grub-pc grub-pc/install_devices string %s\n" "grub-pc grub-pc/install_devices_empty boolean %s\n") % - (idevs, idevs_empty) + (idevs, idevs_empty)) log.debug("Setting grub debconf-set-selections with '%s','%s'" % (idevs, idevs_empty)) diff --git a/cloudinit/transforms/cc_keys_to_console.py b/cloudinit/transforms/cc_keys_to_console.py index 2f2a5297..e974375f 100644 --- a/cloudinit/transforms/cc_keys_to_console.py +++ b/cloudinit/transforms/cc_keys_to_console.py @@ -18,23 +18,34 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os + from cloudinit.settings import PER_INSTANCE from cloudinit import util frequency = PER_INSTANCE +# This is a tool that cloud init provides +helper_tool = '/usr/lib/cloud-init/write-ssh-key-fingerprints' + -def handle(_name, cfg, _cloud, log, _args): - cmd = ['/usr/lib/cloud-init/write-ssh-key-fingerprints'] - fp_blacklist = util.get_cfg_option_list_or_str(cfg, +def handle(name, cfg, _cloud, log, _args): + if not os.path.exists(helper_tool): + log.warn(("Unable to activate transform %s," + " helper tool not found at %s"), name, helper_tool) + return + + fp_blacklist = util.get_cfg_option_list(cfg, "ssh_fp_console_blacklist", []) - key_blacklist = util.get_cfg_option_list_or_str(cfg, + key_blacklist = util.get_cfg_option_list(cfg, "ssh_key_console_blacklist", ["ssh-dss"]) + try: + cmd = [helper_tool] cmd.append(','.join(fp_blacklist)) cmd.append(','.join(key_blacklist)) - (stdout, stderr) = util.subp(cmd) + (stdout, _stderr) = util.subp(cmd) util.write_file('/dev/console', stdout) except: - log.warn("Writing keys to console failed!") + log.warn("Writing keys to /dev/console failed!") raise diff --git a/cloudinit/transforms/cc_landscape.py b/cloudinit/transforms/cc_landscape.py index 48491992..19948d0e 100644 --- a/cloudinit/transforms/cc_landscape.py +++ b/cloudinit/transforms/cc_landscape.py @@ -55,7 +55,8 @@ def handle(name, cfg, _cloud, log, _args): /etc/landscape/client.conf """ if not ConfigObj: - log.warn("'ConfigObj' support not enabled, running %s disabled", name) + log.warn(("'ConfigObj' support not available," + " running transform %s disabled"), name) return ls_cloudcfg = cfg.get("landscape", {}) diff --git a/cloudinit/transforms/cc_locale.py b/cloudinit/transforms/cc_locale.py index 3fb4c5d9..7f273123 100644 --- a/cloudinit/transforms/cc_locale.py +++ b/cloudinit/transforms/cc_locale.py @@ -49,7 +49,7 @@ def handle(name, cfg, cloud, log, args): "/etc/default/locale") if not locale: - log.debug(("Skipping module named %s, " + log.debug(("Skipping transform named %s, " "no 'locale' configuration found"), name) return diff --git a/cloudinit/transforms/cc_mcollective.py b/cloudinit/transforms/cc_mcollective.py index aeeda9d2..5464fe8c 100644 --- a/cloudinit/transforms/cc_mcollective.py +++ b/cloudinit/transforms/cc_mcollective.py @@ -19,13 +19,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from ConfigParser import ConfigParser from StringIO import StringIO -import os - +from cloudinit import cfg as config from cloudinit import util -from cloudinit import cfg pubcert_file = "/etc/mcollective/ssl/server-public.pem" pricert_file = "/etc/mcollective/ssl/server-private.pem" @@ -35,7 +32,7 @@ def handle(name, cfg, cloud, log, _args): # If there isn't a mcollective key in the configuration don't do anything if 'mcollective' not in cfg: - log.debug(("Skipping module named %s, " + log.debug(("Skipping transform named %s, " "no 'mcollective' key in configuration"), name) return @@ -47,7 +44,7 @@ def handle(name, cfg, cloud, log, _args): # ... and then update the mcollective configuration if 'conf' in mcollective_cfg: # Create object for reading server.cfg values - mcollective_config = cfg.DefaultingConfigParser() + mcollective_config = config.DefaultingConfigParser() # Read server.cfg values from original file in order to be able to mix # the rest up old_contents = util.load_file('/etc/mcollective/server.cfg') diff --git a/cloudinit/transforms/cc_mounts.py b/cloudinit/transforms/cc_mounts.py index babcbda1..44182b87 100644 --- a/cloudinit/transforms/cc_mounts.py +++ b/cloudinit/transforms/cc_mounts.py @@ -20,7 +20,6 @@ from string import whitespace # pylint: disable=W0402 -import os import re from cloudinit import util @@ -28,7 +27,7 @@ from cloudinit import util # shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1 shortname_filter = r"^[x]{0,1}[shv]d[a-z][0-9]*$" shortname = re.compile(shortname_filter) -ws = re.compile("[%s]+" % whitespace) +ws = re.compile("[%s]+" % (whitespace)) def is_mdname(name): @@ -65,13 +64,14 @@ def handle(_name, cfg, cloud, log, _args): continue startname = str(cfgmnt[i][0]) - LOG.debug("Attempting to determine the real name of %s", startname) + log.debug("Attempting to determine the real name of %s", startname) # workaround, allow user to specify 'ephemeral' # rather than more ec2 correct 'ephemeral0' if startname == "ephemeral": cfgmnt[i][0] = "ephemeral0" - log.debug("Adjusted mount option %s name from ephemeral to ephemeral0", (i + 1)) + log.debug(("Adjusted mount option %s " + "name from ephemeral to ephemeral0"), (i + 1)) if is_mdname(startname): newname = cloud.device_name_to_device(startname) @@ -136,7 +136,8 @@ def handle(_name, cfg, cloud, log, _args): break if cfgmnt_has: - log.debug("Not including %s, already previously included", startname) + log.debug(("Not including %s, already" + " previously included"), startname) continue cfgmnt.append(defmnt) @@ -159,7 +160,7 @@ def handle(_name, cfg, cloud, log, _args): dirs = [] for line in actlist: # write 'comment' in the fs_mntops, entry, claiming this - line[3] = "%s,comment=cloudconfig" % line[3] + line[3] = "%s,%s" % (line[3], comment) if line[2] == "swap": needswap = True if line[1].startswith("/"): @@ -168,7 +169,7 @@ def handle(_name, cfg, cloud, log, _args): fstab_lines = [] fstab = util.load_file("/etc/fstab") - for line in fstab.read().splitlines(): + for line in fstab.splitlines(): try: toks = ws.split(line) if toks[3].find(comment) != -1: diff --git a/cloudinit/transforms/cc_phone_home.py b/cloudinit/transforms/cc_phone_home.py index 36af6dfa..98ff2b85 100644 --- a/cloudinit/transforms/cc_phone_home.py +++ b/cloudinit/transforms/cc_phone_home.py @@ -24,9 +24,8 @@ from cloudinit import util from cloudinit.settings import PER_INSTANCE -from time import sleep - frequency = PER_INSTANCE + post_list_all = ['pub_key_dsa', 'pub_key_rsa', 'pub_key_ecdsa', 'instance_id', 'hostname'] @@ -49,7 +48,7 @@ def handle(name, cfg, cloud, log, args): ph_cfg = cfg['phone_home'] if 'url' not in ph_cfg: - log.warn(("Skipping module named %s, " + log.warn(("Skipping transform named %s, " "no 'url' found in 'phone_home' configuration"), name) return @@ -60,7 +59,8 @@ def handle(name, cfg, cloud, log, args): tries = int(tries) except: tries = 10 - util.logexc(log, "Configuration entry 'tries' is not an integer, using %s", tries) + util.logexc(log, ("Configuration entry 'tries'" + " is not an integer, using %s instead"), tries) if post_list == "all": post_list = post_list_all @@ -75,23 +75,37 @@ def handle(name, cfg, cloud, log, args): 'pub_key_ecdsa': '/etc/ssh/ssh_host_ecdsa_key.pub', } - for n, path in pubkeys.iteritems(): + for (n, path) in pubkeys.iteritems(): try: all_keys[n] = util.load_file(path) except: - util.logexc(log, "%s: failed to open, can not phone home that data", path) + util.logexc(log, ("%s: failed to open, can not" + " phone home that data"), path) submit_keys = {} for k in post_list: if k in all_keys: submit_keys[k] = all_keys[k] else: - submit_keys[k] = "N/A" - log.warn("Requested key %s from 'post' configuration list not available", k) + submit_keys[k] = None + log.warn(("Requested key %s from 'post'" + " configuration list not available"), k) - url = templater.render_string(url, {'INSTANCE_ID': all_keys['instance_id']}) + # Get them read to be posted + real_submit_keys = {} + for (k, v) in submit_keys.iteritems(): + if v is None: + real_submit_keys[k] = 'N/A' + else: + real_submit_keys[k] = str(v) + # Incase the url is parameterized + url_params = { + 'INSTANCE_ID': all_keys['instance_id'], + } + url = templater.render_string(url, url_params) try: - uhelp.readurl(url, data=submit_keys, retries=tries, sec_between=3) + uhelp.readurl(url, data=real_submit_keys, retries=tries, sec_between=3) except: - util.logexc(log, "Failed to post phone home data to %s in %s tries", url, tries) + util.logexc(log, ("Failed to post phone home data to" + " %s in %s tries"), url, tries) diff --git a/cloudinit/transforms/cc_puppet.py b/cloudinit/transforms/cc_puppet.py index 0a21a929..76cc9732 100644 --- a/cloudinit/transforms/cc_puppet.py +++ b/cloudinit/transforms/cc_puppet.py @@ -24,31 +24,32 @@ import os import pwd import socket +from cloudinit import cfg as config from cloudinit import util -from cloudinit import cfg def handle(name, cfg, cloud, log, _args): # If there isn't a puppet key in the configuration don't do anything if 'puppet' not in cfg: - log.debug(("Skipping module named %s," + log.debug(("Skipping transform named %s," " no 'puppet' configuration found"), name) return puppet_cfg = cfg['puppet'] # Start by installing the puppet package ... - cloud.distro.install_packages(("puppet",)) + cloud.distro.install_packages(["puppet"]) # ... and then update the puppet configuration if 'conf' in puppet_cfg: # Add all sections from the conf object to puppet.conf contents = util.load_file('/etc/puppet/puppet.conf') # Create object for reading puppet.conf values - puppet_config = cfg.DefaultingConfigParser() + puppet_config = config.DefaultingConfigParser() # Read puppet.conf values from original file in order to be able to # mix the rest up. First clean them up (TODO is this really needed??) - cleaned_contents = '\n'.join([i.lstrip() for i in contents.splitlines()]) + cleaned_lines = [i.lstrip() for i in contents.splitlines()] + cleaned_contents = '\n'.join(cleaned_lines) puppet_config.readfp(StringIO(cleaned_contents), filename='/etc/puppet/puppet.conf') for (cfg_name, cfg) in puppet_cfg['conf'].iteritems(): @@ -81,7 +82,8 @@ def handle(name, cfg, cloud, log, _args): puppet_config.set(cfg_name, o, v) # We got all our config as wanted we'll rename # the previous puppet.conf and create our new one - util.rename('/etc/puppet/puppet.conf', '/etc/puppet/puppet.conf.old') + util.rename('/etc/puppet/puppet.conf', + '/etc/puppet/puppet.conf.old') contents = puppet_config.stringify() util.write_file('/etc/puppet/puppet.conf', contents) @@ -91,7 +93,8 @@ def handle(name, cfg, cloud, log, _args): '-e', 's/^START=.*/START=yes/', '/etc/default/puppet'], capture=False) elif os.path.exists('/bin/systemctl'): - util.subp(['/bin/systemctl', 'enable', 'puppet.service'], capture=False) + util.subp(['/bin/systemctl', 'enable', 'puppet.service'], + capture=False) elif os.path.exists('/sbin/chkconfig'): util.subp(['/sbin/chkconfig', 'puppet', 'on'], capture=False) else: diff --git a/cloudinit/transforms/cc_resizefs.py b/cloudinit/transforms/cc_resizefs.py index daaf4da9..fe012417 100644 --- a/cloudinit/transforms/cc_resizefs.py +++ b/cloudinit/transforms/cc_resizefs.py @@ -18,11 +18,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import errno import os import stat -import sys -import tempfile import time from cloudinit import util @@ -46,15 +43,18 @@ def nodeify_path(devpth, where, log): if util.is_container(): log.debug("Inside container, ignoring mknod failure in resizefs") return - log.warn("Failed to make device node to resize %s at %s", where, devpth) + log.warn("Failed to make device node to resize %s at %s", + where, devpth) raise def get_fs_type(st_dev, path, log): try: - fs_type = util.find_devs_with(tag='TYPE', oformat='value', + dev_entries = util.find_devs_with(tag='TYPE', oformat='value', no_cache=True, path=path) - return fs_type + if not dev_entries: + return None + return dev_entries[0].strip() except util.ProcessExecutionError: util.logexc(log, ("Failed to get filesystem type" " of maj=%s, min=%s for path %s"), @@ -69,12 +69,16 @@ def handle(name, cfg, _cloud, log, args): resize_root = util.get_cfg_option_str(cfg, "resize_rootfs", True) if not util.translate_bool(resize_root): - log.debug("Skipping module named %s, resizing disabled", name) + log.debug("Skipping transform named %s, resizing disabled", name) return # TODO is the directory ok to be used?? resize_root_d = util.get_cfg_option_str(cfg, "resize_rootfs_tmp", "/run") util.ensure_dir(resize_root_d) + + # TODO: allow what is to be resized to + # be configurable?? + resize_what = "/" with util.SilentTemporaryFile(prefix="cloudinit.resizefs.", dir=resize_root_d, delete=True) as tfh: devpth = tfh.name @@ -86,23 +90,25 @@ def handle(name, cfg, _cloud, log, args): # auto deletion tfh.unlink_now() - # TODO: allow what is to be resized to - # be configurable?? - st_dev = nodeify_path(devpth, "/", log) - fs_type = get_fs_type(st_dev, devpath, log) + st_dev = nodeify_path(devpth, resize_what, log) + fs_type = get_fs_type(st_dev, devpth, log) + if not fs_type: + log.warn("Could not determine filesystem type of %s", resize_what) + return resizer = None - fstype_lc = fstype.lower() + fstype_lc = fs_type.lower() for (pfix, root_cmd) in resize_fs_prefixes_cmds: if fstype_lc.startswith(pfix): resizer = root_cmd break if not resizer: - log.warn("Not resizing unknown filesystem type %s", fs_type) + log.warn("Not resizing unknown filesystem type %s for %s", + fs_type, resize_what) return - log.debug("Resizing using %s", resizer) + log.debug("Resizing %s (%s) using %s", resize_what, fs_type, resizer) resize_cmd = [resizer, devpth] if resize_root == "noblock": @@ -125,8 +131,8 @@ def do_resize(resize_cmd, log): start = time.time() try: util.subp(resize_cmd) - except util.ProcessExecutionError as e: - util.logexc(log, "Failed to resize filesystem (using %s)", resize_cmd) + except util.ProcessExecutionError: + util.logexc(log, "Failed to resize filesystem (cmd=%s)", resize_cmd) raise tot_time = int(time.time() - start) log.debug("Resizing took %s seconds", tot_time) diff --git a/cloudinit/transforms/cc_rightscale_userdata.py b/cloudinit/transforms/cc_rightscale_userdata.py index cde11b54..40d76c89 100644 --- a/cloudinit/transforms/cc_rightscale_userdata.py +++ b/cloudinit/transforms/cc_rightscale_userdata.py @@ -53,16 +53,19 @@ def handle(name, _cfg, cloud, log, _args): try: ud = cloud.get_userdata_raw() except: - log.warn("Failed to get raw userdata in module %s", name) + log.warn("Failed to get raw userdata in transform %s", name) return try: mdict = parse_qs(ud) if not mdict or not my_hookname in mdict: - log.debug("Skipping module %s, did not find %s in parsed raw userdata", name, my_hookname) + log.debug(("Skipping transform %s, " + "did not find %s in parsed" + " raw userdata"), name, my_hookname) return except: - log.warn("Failed to parse query string %s into a dictionary", ud) + util.logexc(log, ("Failed to parse query string %s" + " into a dictionary"), ud) raise wrote_fns = [] @@ -83,7 +86,8 @@ def handle(name, _cfg, cloud, log, _args): wrote_fns.append(fname) except Exception as e: captured_excps.append(e) - util.logexc(log, "%s failed to read %s and write %s", my_name, url, fname) + util.logexc(log, "%s failed to read %s and write %s", + my_name, url, fname) if wrote_fns: log.debug("Wrote out rightscale userdata to %s files", len(wrote_fns)) @@ -93,6 +97,6 @@ def handle(name, _cfg, cloud, log, _args): log.debug("%s urls were skipped or failed", skipped) if captured_excps: - log.warn("%s failed with exceptions, re-raising the last one", len(captured_excps)) + log.warn("%s failed with exceptions, re-raising the last one", + len(captured_excps)) raise captured_excps[-1] - diff --git a/cloudinit/transforms/cc_rsyslog.py b/cloudinit/transforms/cc_rsyslog.py index ccbe68ff..71b74711 100644 --- a/cloudinit/transforms/cc_rsyslog.py +++ b/cloudinit/transforms/cc_rsyslog.py @@ -36,7 +36,8 @@ def handle(name, cfg, cloud, log, _args): # process 'rsyslog' if not 'rsyslog' in cfg: - log.debug("Skipping module named %s, no 'rsyslog' key in configuration", name) + log.debug(("Skipping transform named %s," + " no 'rsyslog' key in configuration"), name) return def_dir = cfg.get('rsyslog_dir', DEF_DIR) @@ -62,15 +63,16 @@ def handle(name, cfg, cloud, log, _args): if not filename.startswith("/"): filename = os.path.join(def_dir, filename) + # Truncate filename first time you see it omode = "ab" - # truncate filename first time you see it if filename not in files: omode = "wb" files.append(filename) try: - util.write_file(filename, content + "\n", omode=omode) - except Exception as e: + contents = "%s\n" % (content) + util.write_file(filename, contents, omode=omode) + except Exception: util.logexc(log, "Failed to write to %s", filename) # Attempt to restart syslogd @@ -87,8 +89,8 @@ def handle(name, cfg, cloud, log, _args): log.debug("Restarting rsyslog") util.subp(['service', 'rsyslog', 'restart']) restarted = True - except Exception as e: - util.logexc("Failed restarting rsyslog") + except Exception: + util.logexc(log, "Failed restarting rsyslog") if restarted: # This only needs to run if we *actually* restarted diff --git a/cloudinit/transforms/cc_runcmd.py b/cloudinit/transforms/cc_runcmd.py index 19c0e721..31a254a5 100644 --- a/cloudinit/transforms/cc_runcmd.py +++ b/cloudinit/transforms/cc_runcmd.py @@ -25,13 +25,14 @@ from cloudinit import util def handle(name, cfg, cloud, log, _args): if "runcmd" not in cfg: - log.debug("Skipping module named %s, no 'runcmd' key in configuration", name) + log.debug(("Skipping transform named %s," + " no 'runcmd' key in configuration"), name) return - outfile = os.path.join(cloud.get_ipath('scripts'), "runcmd") + out_fn = os.path.join(cloud.get_ipath('scripts'), "runcmd") cmd = cfg["runcmd"] try: content = util.shellify(cmd) - util.write_file(outfile, content, 0700) + util.write_file(out_fn, content, 0700) except: - util.logexc(log, "Failed to shellify %s into file %s", cmd, outfile) + util.logexc(log, "Failed to shellify %s into file %s", cmd, out_fn) diff --git a/cloudinit/transforms/cc_salt_minion.py b/cloudinit/transforms/cc_salt_minion.py index 47cbc194..d05d2a1e 100644 --- a/cloudinit/transforms/cc_salt_minion.py +++ b/cloudinit/transforms/cc_salt_minion.py @@ -21,16 +21,17 @@ from cloudinit import util # Note: see http://saltstack.org/topics/installation/ -def handle(name, cfg, cloud, _log, _args): +def handle(name, cfg, cloud, log, _args): # If there isn't a salt key in the configuration don't do anything if 'salt_minion' not in cfg: - log.debug("Skipping module named %s, no 'salt_minion' key in configuration", name) + log.debug(("Skipping transform named %s," + " no 'salt_minion' key in configuration"), name) return salt_cfg = cfg['salt_minion'] # Start by installing the salt package ... - cloud.distro.install_packages(("salt",)) + cloud.distro.install_packages(["salt"]) # Ensure we can configure files at the right dir config_dir = salt_cfg.get("config_dir", '/etc/salt') diff --git a/cloudinit/transforms/cc_scripts_per_boot.py b/cloudinit/transforms/cc_scripts_per_boot.py index bcdf4400..364e1d02 100644 --- a/cloudinit/transforms/cc_scripts_per_boot.py +++ b/cloudinit/transforms/cc_scripts_per_boot.py @@ -29,12 +29,13 @@ frequency = PER_ALWAYS script_subdir = 'per-boot' -def handle(_name, _cfg, cloud, log, _args): +def handle(name, _cfg, cloud, log, _args): # Comes from the following: # https://forums.aws.amazon.com/thread.jspa?threadID=96918 runparts_path = os.path.join(cloud.get_cpath(), 'scripts', script_subdir) try: util.runparts(runparts_path) except: - log.warn("Failed to run-parts(%s) in %s", script_subdir, runparts_path) + log.warn("Failed to run transform %s (%s in %s)", + name, script_subdir, runparts_path) raise diff --git a/cloudinit/transforms/cc_scripts_per_instance.py b/cloudinit/transforms/cc_scripts_per_instance.py index 8d6609a1..d75ab47d 100644 --- a/cloudinit/transforms/cc_scripts_per_instance.py +++ b/cloudinit/transforms/cc_scripts_per_instance.py @@ -29,12 +29,13 @@ frequency = PER_INSTANCE script_subdir = 'per-instance' -def handle(_name, _cfg, cloud, log, _args): +def handle(name, _cfg, cloud, log, _args): # Comes from the following: # https://forums.aws.amazon.com/thread.jspa?threadID=96918 runparts_path = os.path.join(cloud.get_cpath(), 'scripts', script_subdir) try: util.runparts(runparts_path) except: - log.warn("Failed to run-parts(%s) in %s", script_subdir, runparts_path) + log.warn("Failed to run transform %s (%s in %s)", + name, script_subdir, runparts_path) raise diff --git a/cloudinit/transforms/cc_scripts_per_once.py b/cloudinit/transforms/cc_scripts_per_once.py index dbcec05d..80f8c325 100644 --- a/cloudinit/transforms/cc_scripts_per_once.py +++ b/cloudinit/transforms/cc_scripts_per_once.py @@ -29,12 +29,13 @@ frequency = PER_ONCE script_subdir = 'per-once' -def handle(_name, _cfg, cloud, log, _args): +def handle(name, _cfg, cloud, log, _args): # Comes from the following: # https://forums.aws.amazon.com/thread.jspa?threadID=96918 runparts_path = os.path.join(cloud.get_cpath(), 'scripts', script_subdir) try: util.runparts(runparts_path) except: - log.warn("Failed to run-parts(%s) in %s", script_subdir, runparts_path) + log.warn("Failed to run transform %s (%s in %s)", + name, script_subdir, runparts_path) raise diff --git a/cloudinit/transforms/cc_scripts_user.py b/cloudinit/transforms/cc_scripts_user.py index 1e438ee6..f4fe3a2a 100644 --- a/cloudinit/transforms/cc_scripts_user.py +++ b/cloudinit/transforms/cc_scripts_user.py @@ -26,14 +26,17 @@ from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE +script_subdir = 'scripts' -def handle(_name, _cfg, cloud, log, _args): + +def handle(name, _cfg, cloud, log, _args): # This is written to by the user data handlers # Ie, any custom shell scripts that come down # go here... - runparts_path = os.path.join(cloud.get_ipath_cur(), "scripts") + runparts_path = os.path.join(cloud.get_ipath_cur(), script_subdir) try: util.runparts(runparts_path) except: - log.warn("Failed to run-parts(%s) in %s", "user-data", runparts_path) + log.warn("Failed to run transform %s (%s in %s)", + name, script_subdir, runparts_path) raise diff --git a/cloudinit/transforms/cc_set_hostname.py b/cloudinit/transforms/cc_set_hostname.py index fa2b59c2..3ac8a8fa 100644 --- a/cloudinit/transforms/cc_set_hostname.py +++ b/cloudinit/transforms/cc_set_hostname.py @@ -24,7 +24,7 @@ from cloudinit import util def handle(name, cfg, cloud, log, _args): if util.get_cfg_option_bool(cfg, "preserve_hostname", False): log.debug(("Configuration option 'preserve_hostname' is set," - " not setting the hostname in %s"), name) + " not setting the hostname in transform %s"), name) return (hostname, _fqdn) = util.get_hostname_fqdn(cfg, cloud) diff --git a/cloudinit/transforms/cc_set_passwords.py b/cloudinit/transforms/cc_set_passwords.py index 4f2cdb97..c0cc4e84 100644 --- a/cloudinit/transforms/cc_set_passwords.py +++ b/cloudinit/transforms/cc_set_passwords.py @@ -22,7 +22,7 @@ import sys from cloudinit import util -from string import letters, digits +from string import letters, digits # pylint: disable=W0402 # We are removing certain 'painful' letters/numbers pw_set = (letters.translate(None, 'loLOI') + @@ -71,11 +71,13 @@ def handle(_name, cfg, cloud, log, args): util.subp(['chpasswd'], ch_in) except Exception as e: errors.append(e) - util.logexc(log, "Failed to set passwords with chpasswd for %s", users) + util.logexc(log, + "Failed to set passwords with chpasswd for %s", users) if len(randlist): - sys.stderr.write("%s\n%s\n" % ("Set the following 'random' passwords\n", - '\n'.join(randlist))) + blurb = ("Set the following 'random' passwords\n", + '\n'.join(randlist)) + sys.stderr.write("%s\n%s\n" % blurb) if expire: expired_users = [] diff --git a/cloudinit/transforms/cc_ssh.py b/cloudinit/transforms/cc_ssh.py index db6848d9..3c2b3622 100644 --- a/cloudinit/transforms/cc_ssh.py +++ b/cloudinit/transforms/cc_ssh.py @@ -65,8 +65,7 @@ def handle(_name, cfg, cloud, log, _args): tgt_fn = key2file[key][0] tgt_perms = key2file[key][1] util.write_file(tgt_fn, val, tgt_perms) - - cmd = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"' + for priv, pub in priv2pub.iteritems(): if pub in cfg['ssh_keys'] or not priv in cfg['ssh_keys']: continue @@ -78,11 +77,15 @@ def handle(_name, cfg, cloud, log, _args): util.subp(cmd, capture=False) log.debug("Generated a key for %s from %s", pair[0], pair[1]) except: - util.logexc(log, "Failed generated a key for %s from %s", pair[0], pair[1]) + util.logexc(log, ("Failed generated a key" + " for %s from %s"), pair[0], pair[1]) else: # if not, generate them - for keytype in util.get_cfg_option_list_or_str(cfg, 'ssh_genkeytypes', generate_keys): - keyfile = '/etc/ssh/ssh_host_%s_key' % keytype + genkeys = util.get_cfg_option_list(cfg, + 'ssh_genkeytypes', + generate_keys) + for keytype in genkeys: + keyfile = '/etc/ssh/ssh_host_%s_key' % (keytype) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: @@ -90,26 +93,27 @@ def handle(_name, cfg, cloud, log, _args): with util.SeLinuxGuard("/etc/ssh", recursive=True): util.subp(cmd, capture=False) except: - util.logexc(log, "Failed generating key type %s to file %s", keytype, keyfile) + util.logexc(log, ("Failed generating key type" + " %s to file %s"), keytype, keyfile) try: user = util.get_cfg_option_str(cfg, 'user') disable_root = util.get_cfg_option_bool(cfg, "disable_root", True) disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts", - DISABLE_ROOT_OPTS) + DISABLE_ROOT_OPTS) keys = cloud.get_public_ssh_keys() or [] if "ssh_authorized_keys" in cfg: cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) - apply_credentials(keys, user, disable_root, disable_root_opts, log) + apply_credentials(keys, user, disable_root, disable_root_opts) except: util.logexc(log, "Applying ssh credentials failed!") def apply_credentials(keys, user, disable_root, - disable_root_opts=DISABLE_ROOT_OPTS, log=None): + disable_root_opts=DISABLE_ROOT_OPTS): keys = set(keys) if user: diff --git a/cloudinit/transforms/cc_ssh_import_id.py b/cloudinit/transforms/cc_ssh_import_id.py index 019413d4..d57e4665 100644 --- a/cloudinit/transforms/cc_ssh_import_id.py +++ b/cloudinit/transforms/cc_ssh_import_id.py @@ -33,10 +33,14 @@ def handle(name, cfg, _cloud, log, args): ids = args[1:] else: user = util.get_cfg_option_str(cfg, "user", "ubuntu") - ids = util.get_cfg_option_list_or_str(cfg, "ssh_import_id", []) + ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) if len(ids) == 0: - log.debug("Skipping module named %s, no ids found to import", name) + log.debug("Skipping transform named %s, no ids found to import", name) + return + + if not user: + log.debug("Skipping transform named %s, no user found to import", name) return cmd = ["sudo", "-Hu", user, "ssh-import-id"] + ids diff --git a/cloudinit/transforms/cc_timezone.py b/cloudinit/transforms/cc_timezone.py index 02cbf2dc..747c436c 100644 --- a/cloudinit/transforms/cc_timezone.py +++ b/cloudinit/transforms/cc_timezone.py @@ -18,20 +18,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from cloudinit import util + from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE -def handle(_name, cfg, cloud, log, args): +def handle(name, cfg, cloud, log, args): if len(args) != 0: timezone = args[0] else: timezone = util.get_cfg_option_str(cfg, "timezone", False) if not timezone: - log.debug("Skipping module named %s, no 'timezone' specified", name) + log.debug("Skipping transform named %s, no 'timezone' specified", name) return - + # Let the distro handle settings its timezone cloud.distro.set_timezone(timezone) diff --git a/cloudinit/transforms/cc_update_etc_hosts.py b/cloudinit/transforms/cc_update_etc_hosts.py index 361097a6..d0e56183 100644 --- a/cloudinit/transforms/cc_update_etc_hosts.py +++ b/cloudinit/transforms/cc_update_etc_hosts.py @@ -30,22 +30,30 @@ def handle(name, cfg, cloud, log, _args): manage_hosts = util.get_cfg_option_str(cfg, "manage_etc_hosts", False) if util.translate_bool(manage_hosts, addons=['template']): (hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud) - # Render from template file if not hostname: - log.warn("Option 'manage_etc_hosts' was set, but no hostname was found") + log.warn(("Option 'manage_etc_hosts' was set," + " but no hostname was found")) return - tpl_fn_name = cloud.get_template_filename("hosts.%s" % (cloud.distro.name())) + + # Render from a template file + distro_n = cloud.distro.name + tpl_fn_name = cloud.get_template_filename("hosts.%s" % (distro_n)) if not tpl_fn_name: - raise Exception("No hosts template could be found for distro %s" % (cloud.distro.name())) + raise Exception(("No hosts template could be" + " found for distro %s") % (distro_n)) + templater.render_to_file(tpl_fn_name, '/etc/hosts', {'hostname': hostname, 'fqdn': fqdn}) + elif manage_hosts == "localhost": - log.debug("Managing localhost in /etc/hosts") (hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud) if not hostname: - log.warn("Option 'manage_etc_hosts' was set, but no hostname was found") + log.warn(("Option 'manage_etc_hosts' was set," + " but no hostname was found")) return + + log.debug("Managing localhost in /etc/hosts") cloud.distro.update_etc_hosts(hostname, fqdn) else: log.debug(("Configuration option 'manage_etc_hosts' is not set," - " not managing /etc/hosts in %s"), name) + " not managing /etc/hosts in transform %s"), name) diff --git a/cloudinit/transforms/cc_update_hostname.py b/cloudinit/transforms/cc_update_hostname.py index 439bdcb3..58444fab 100644 --- a/cloudinit/transforms/cc_update_hostname.py +++ b/cloudinit/transforms/cc_update_hostname.py @@ -18,6 +18,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os + from cloudinit import util from cloudinit.settings import PER_ALWAYS @@ -27,7 +29,7 @@ frequency = PER_ALWAYS def handle(name, cfg, cloud, log, _args): if util.get_cfg_option_bool(cfg, "preserve_hostname", False): log.debug(("Configuration option 'preserve_hostname' is set," - " not updating the hostname in %s"), name) + " not updating the hostname in transform %s"), name) return (hostname, _fqdn) = util.get_hostname_fqdn(cfg, cloud) diff --git a/cloudinit/transforms/cc_welcome.py b/cloudinit/transforms/cc_welcome.py index 0db71125..04691d21 100644 --- a/cloudinit/transforms/cc_welcome.py +++ b/cloudinit/transforms/cc_welcome.py @@ -35,9 +35,9 @@ welcome_message_def = ("Cloud-init v. {{version}} starting stage {{stage}} at " frequency = PER_ALWAYS -def handle(name, cfg, cloud, log, args): +def handle(_name, cfg, cloud, log, args): - welcome_msg = util.get_cfg_option_str(cfg, "welcome_msg"): + welcome_msg = util.get_cfg_option_str(cfg, "welcome_msg") if not welcome_msg: tpl_fn = cloud.get_template_filename("welcome_msg") if tpl_fn: @@ -54,7 +54,7 @@ def handle(name, cfg, cloud, log, args): 'stage': stage, 'version': version.version_string(), 'uptime': util.uptime(), - 'timestamp', util.time_rfc2822(), + 'timestamp': util.time_rfc2822(), } try: contents = templater.render_string(welcome_msg, tpl_params) diff --git a/cloudinit/user_data.py b/cloudinit/user_data.py index 64fc2734..9915b8b0 100644 --- a/cloudinit/user_data.py +++ b/cloudinit/user_data.py @@ -22,14 +22,15 @@ import os -import glob import email - +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText from email.mime.base import MIMEBase from cloudinit import importer from cloudinit import log as logging +from cloudinit import url_helper from cloudinit import util from cloudinit.settings import (PER_ALWAYS, PER_INSTANCE, FREQUENCIES) @@ -86,7 +87,7 @@ class UserDataProcessor(object): self.paths = paths def process(self, blob): - base_msg = ud.convert_string(blob) + base_msg = convert_string(blob) process_msg = MIMEMultipart() self._process_msg(base_msg, process_msg) return process_msg @@ -105,7 +106,7 @@ class UserDataProcessor(object): ctype_orig = UNDEF_TYPE if ctype_orig in TYPE_NEEDED: - ctype = ud.type_from_starts_with(payload) + ctype = type_from_starts_with(payload) if ctype is None: ctype = ctype_orig @@ -158,7 +159,7 @@ class UserDataProcessor(object): if not url_helper.ok_http_code(st): content = '' - new_msg = ud.convert_string(content) + new_msg = convert_string(content) self._process_msg(new_msg, append_msg) def _explode_archive(self, archive, append_msg): @@ -179,7 +180,7 @@ class UserDataProcessor(object): content = ent.get('content', '') mtype = ent.get('type') if not mtype: - mtype = ud.type_from_starts_with(content, ARCHIVE_UNDEF_TYPE) + mtype = type_from_starts_with(content, ARCHIVE_UNDEF_TYPE) maintype, subtype = mtype.split('/', 1) if maintype == "text": diff --git a/cloudinit/util.py b/cloudinit/util.py index 7259d933..1f884df8 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -22,8 +22,8 @@ from StringIO import StringIO +import copy as obj_copy import contextlib -import copy import errno import glob import grp @@ -35,12 +35,11 @@ import pwd import random import shutil import socket -import string +import string # pylint: disable=W0402 import subprocess import sys import tempfile import time -import traceback import types import urlparse @@ -171,7 +170,8 @@ def fork_cb(child_cb, *args): child_cb(*args) os._exit(0) # pylint: disable=W0212 except: - logexc(LOG, "Failed forking and calling callback %s", obj_name(child_cb)) + logexc(LOG, ("Failed forking and" + " calling callback %s"), obj_name(child_cb)) os._exit(1) # pylint: disable=W0212 else: LOG.debug("Forked child %s who will run callback %s", @@ -549,10 +549,11 @@ def load_yaml(blob, default=None, allowed=(dict,)): converted = yaml.load(blob) if not isinstance(converted, allowed): # Yes this will just be caught, but thats ok for now... - raise TypeError("Yaml load allows %s root types, but got %s instead" % + raise TypeError(("Yaml load allows %s root types," + " but got %s instead") % (allowed, obj_name(converted))) loaded = converted - except (yaml.YAMLError, TypeError, ValueError) as exc: + except (yaml.YAMLError, TypeError, ValueError): logexc(LOG, "Failed loading yaml blob") return loaded @@ -833,15 +834,12 @@ def find_devs_with(criteria=None, oformat='device', options.append(path) cmd = blk_id_cmd + options (out, _err) = subp(cmd) - if path: - return out.strip() - else: - entries = [] - for line in out.splitlines(): - line = line.strip() - if line: - entries.append(line) - return entries + entries = [] + for line in out.splitlines(): + line = line.strip() + if line: + entries.append(line) + return entries def load_file(fname, read_cb=None, quiet=False): @@ -1109,7 +1107,7 @@ def mount_cb(device, callback, data=None, rw=False, mtype=None): def get_builtin_cfg(): # Deep copy so that others can't modify - return copy.deepcopy(CFG_BUILTIN) + return obj_copy.deepcopy(CFG_BUILTIN) def sym_link(source, link): @@ -1140,16 +1138,14 @@ def time_rfc2822(): def uptime(): + uptime_str = '??' try: - uptimef = load_file("/proc/uptime").strip() - if not uptimef: - uptime = 'na' - else: - uptime = uptimef.split()[0] + contents = load_file("/proc/uptime").strip() + if contents: + uptime_str = contents.split()[0] except: logexc(LOG, "Unable to read uptime from /proc/uptime") - uptime = '??' - return uptime + return uptime_str def ensure_file(path): @@ -1261,7 +1257,8 @@ def shellify(cmdlist, add_header=True): content = "%s%s\n" % (content, args) else: raise RuntimeError(("Unable to shellify type %s" - " which is not a list or string") % (obj_name(args))) + " which is not a list or string") + % (obj_name(args))) LOG.debug("Shellified %s to %s", cmdlist, content) return content @@ -1275,8 +1272,7 @@ def is_container(): try: # try to run a helper program. if it returns true/zero # then we're inside a container. otherwise, no - cmd = [helper] - subp(cmd, allowed_rc=[0]) + subp([helper]) return True except (IOError, OSError): pass -- cgit v1.2.3 From 86fb18047eeddb5c4d3950ffaa6927ddfba90565 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 16 Jun 2012 08:53:09 -0700 Subject: 1. Remove path function, since we have a path class 2. Be explict with file perms (even though its duplicated) --- cloudinit/distros/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index fd4c70c1..e85e702e 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -75,10 +75,6 @@ class Distro(object): def get_package_mirror(self): return self.get_option('package_mirror') - def get_paths(self): - paths = self.get_option("paths") or {} - return copy.deepcopy(paths) - def apply_network(self, settings, bring_up=True): # Write it out self._write_network(settings) @@ -127,7 +123,7 @@ class Distro(object): need_write = True if need_write: contents = new_etchosts.getvalue() - util.write_file("/etc/hosts", contents) + util.write_file("/etc/hosts", contents, mode=0644) def _interface_action(self, action): if action not in IFACE_ACTIONS: -- cgit v1.2.3 From 5e51d844015160a63c1b4aa5afd39fc0701ed71f Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 16 Jun 2012 08:53:51 -0700 Subject: Have the contents formed on a previous line which seems prettier. --- cloudinit/distros/ubuntu.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 9252a1c4..ad12400a 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -44,7 +44,8 @@ class Distro(distros.Distro): self._apt_get(command, args) def set_hostname(self, hostname): - util.write_file("/etc/hostname", "%s\n" % hostname, 0644) + contents = "%s\n" % (hostname) + util.write_file("/etc/hostname", contents, 0644) LOG.debug("Setting hostname to %s", hostname) util.subp(['hostname', hostname]) @@ -59,7 +60,8 @@ class Distro(distros.Distro): update_files.append("/etc/hostname") for fn in update_files: try: - util.write_file(fn, "%s\n" % hostname, 0644) + contents = "%s\n" % (hostname) + util.write_file(fn, contents, 0644) except: util.logexc(LOG, "Failed to write hostname %s to %s", hostname, fn) -- cgit v1.2.3 From 1a803c9f1f55095e1cf69bd4ca12bda0299b64b4 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 16 Jun 2012 12:33:31 -0700 Subject: Have the top level distro class take paths instead of a runner. This allows the following: 1. Let the ubuntu subclass construct its own runner with those paths (since not every subclass may want it) Adjust the base class + subclass to reflect this, adjust stages as well to reflect the constructor changes. --- cloudinit/distros/__init__.py | 4 ++-- cloudinit/distros/ubuntu.py | 8 ++++++++ cloudinit/stages.py | 12 +++++------- 3 files changed, 15 insertions(+), 9 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index e85e702e..0ee7f06b 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -42,8 +42,8 @@ class Distro(object): __metaclass__ = abc.ABCMeta - def __init__(self, name, cfg, runner): - self._runner = runner + def __init__(self, name, cfg, paths): + self._paths = paths self._cfg = cfg self.name = name diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index ad12400a..9b743b55 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -23,6 +23,7 @@ import os from cloudinit import distros +from cloudinit import helpers from cloudinit import log as logging from cloudinit import util @@ -33,6 +34,13 @@ LOG = logging.getLogger(__name__) class Distro(distros.Distro): + def __init__(self, name, cfg, paths): + distros.Distro.__init__(self, name, cfg, paths) + # This will be used to restrict certain + # calls from repeatly happening (when they + # should only happen say once per instance...) + self._runner = helpers.Runners(paths) + def install_packages(self, pkglist): self._update_package_sources() self._apt_get('install', pkglist) diff --git a/cloudinit/stages.py b/cloudinit/stages.py index 9d8ff2bb..8fa9d6d3 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -77,13 +77,11 @@ class Init(object): def distro(self): if not self._distro: # Try to find the right class to use - d_cfg = self._extract_cfg('system') - distro_name = d_cfg.pop('distro', 'ubuntu') - distro_cls = distros.fetch(distro_name) - LOG.debug("Using distro class %s", distro_cls) - distro = distro_cls(distro_name, d_cfg, - helpers.Runners(self.paths)) - self._distro = distro + 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) return self._distro @property -- cgit v1.2.3 From 76f722a9412a88dc8bfc38004f3676a3f2b37835 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 16 Jun 2012 12:40:58 -0700 Subject: If bringing up, return whatever the bring up result is, otherwise false. --- cloudinit/distros/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 0ee7f06b..79d441ac 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -80,7 +80,8 @@ class Distro(object): self._write_network(settings) # Now try to bring them up if bring_up: - self._interface_action('up') + return self._interface_action('up') + return False @abc.abstractmethod def set_timezone(self, tz): -- cgit v1.2.3 From a730063635236c4343f73e55ec491e28c4a2acfe Mon Sep 17 00:00:00 2001 From: harlowja Date: Sun, 17 Jun 2012 13:37:49 -0700 Subject: 1. Remove rhel sysconfig for timezone, now have a rhel distro subclass 2. Remove private apt_get function, since it can just be the package_command public one. --- cloudinit/distros/ubuntu.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 9b743b55..ec4d6b5b 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -43,14 +43,11 @@ class Distro(distros.Distro): def install_packages(self, pkglist): self._update_package_sources() - self._apt_get('install', pkglist) + self.package_command('install', pkglist) def _write_network(self, settings): util.write_file("/etc/network/interfaces", settings) - def package_command(self, command, args=None): - self._apt_get(command, args) - def set_hostname(self, hostname): contents = "%s\n" % (hostname) util.write_file("/etc/hostname", contents, 0644) @@ -103,24 +100,20 @@ class Distro(distros.Distro): " no file found at %s") % (tz, tz_file)) tz_contents = "%s\n" % tz util.write_file("/etc/timezone", tz_contents) - # TODO, this should be in a rhel distro subclass?? - if os.path.exists("/etc/sysconfig/clock"): - tz_contents = '"%s"\n' % tz - util.write_file("/etc/sysconfig/clock", tz_contents) - # This ensures that the correct tz will be used for the system util.copy(tz_file, "/etc/localtime") - # apt_get top level command (install, update...), and args to pass it - def _apt_get(self, tlc, args=None): + def package_command(self, command, args=None): e = os.environ.copy() + # See: http://tiny.cc/kg91fw + # Or: http://tiny.cc/mh91fw e['DEBIAN_FRONTEND'] = 'noninteractive' cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold', - '--assume-yes', tlc] + '--assume-yes', command] if args: cmd.extend(args) # Allow the output of this to flow outwards (ie not be captured) util.subp(cmd, env=e, capture=False) def _update_package_sources(self): - self._runner.run("update-sources", self._apt_get, + self._runner.run("update-sources", self.package_command, ["update"], freq=PER_INSTANCE) \ No newline at end of file -- cgit v1.2.3 From 4e5e0dadf07d52d92711ee674d98ee0ef523ea6a Mon Sep 17 00:00:00 2001 From: harlowja Date: Sun, 17 Jun 2012 13:38:58 -0700 Subject: Added a new rhel distro that does the following. 1. Translate (some of) the ubuntu network format to the rhel format (until we have python netcf active) and then write the different ifcfg files to /etc/sysconfig/network-scripts as needed to bring the network online 2. Apply the hostname to the write /etc/sysconfig/network file (appending to it or adjusting that file if it exists) 3. Write to the /etc/sysconfig/clock file to adjust the timezone (or adjust that file instead of appending) 4. Runs yum commands (to match the packaging commands in the apt-get flavor) 5. Update the hostname in /etc/sysconfig/network (or a previous file) for the update hostname transform --- cloudinit/distros/rhel.py | 294 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 cloudinit/distros/rhel.py (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py new file mode 100644 index 00000000..85283b23 --- /dev/null +++ b/cloudinit/distros/rhel.py @@ -0,0 +1,294 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser +# Author: Juerg Haefliger +# Author: Joshua Harlow +# +# 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 . + +import os + +from cloudinit import distros +from cloudinit import log as logging +from cloudinit import util + +LOG = logging.getLogger(__name__) + +NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' + + +class Distro(distros.Distro): + + def __init__(self, name, cfg, paths): + distros.Distro.__init__(self, name, cfg, paths) + + def install_packages(self, pkglist): + self.package_command('install', pkglist) + + def _write_network(self, settings): + # TODO fix this... since this is the ubuntu format + entries = translate_network(settings) + LOG.debug("Translated ubuntu style network settings %s into %s", + settings, entries) + # Make the intermediate format as the rhel format... + for (dev, info) in entries.iteritems(): + lines = [] + lines.append("DEVICE=%s" % (dev)) + boot_proto = info.get('bootproto') + if boot_proto: + lines.append("BOOTPROTO=%s" % (boot_proto)) + net_mask = info.get('netmask') + if net_mask: + lines.append("NETMASK=%s" % (net_mask)) + addr = info.get('address') + if addr: + lines.append("IPADDR=%s" % (addr)) + if info.get('auto'): + lines.append("ONBOOT=yes") + else: + lines.append("ONBOOT=no") + gtway = info.get('gateway') + if gtway: + lines.append("GATEWAY=%s" % (gtway)) + bcast = info.get('broadcast') + if bcast: + lines.append("BROADCAST=%s" % (bcast)) + mac_addr = info.get('hwaddress') + if mac_addr: + lines.append("MACADDR=%s" % (mac_addr)) + contents = "\n".join(lines) + net_fn = NETWORK_FN_TPL % (dev) + util.write_file(net_fn, contents, 0644) + + def set_hostname(self, hostname): + self._write_hostname(hostname, "/etc/sysconfig/network") + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _write_hostname(self, hostname, out_fn): + old_contents = [] + if os.path.isfile(out_fn): + old_contents = self._read_conf(out_fn) + # Update the 'HOSTNAME' if it exists instead of appending + new_contents = [] + adjusted = False + for entry in old_contents: + if not entry: + continue + if len(entry) == 1: + new_contents.append(entry[0]) + continue + (cmd, args) = entry + cmd_c = cmd.strip().lower() + if cmd_c == 'hostname': + args = "%s" % (hostname) + adjusted = True + new_contents.append("=".join([cmd, args])) + # Guess not found, append it + if not adjusted: + new_contents.append("HOSTNAME=%s" % (hostname)) + contents = "\n".join(new_contents) + util.write_file(out_fn, contents, 0644) + + def update_hostname(self, hostname, prev_file): + hostname_prev = self._read_hostname(prev_file) + hostname_in_sys = self._read_hostname("/etc/sysconfig/network") + update_files = [] + if not hostname_prev or hostname_prev != hostname: + update_files.append(prev_file) + if (not hostname_in_sys or + (hostname_in_sys == hostname_prev and hostname_in_sys != hostname)): + update_files.append("/etc/sysconfig/network") + for fn in update_files: + try: + self._write_hostname(hostname, fn) + except: + util.logexc(LOG, "Failed to write hostname %s to %s", + hostname, fn) + if (hostname_in_sys and hostname_prev and + hostname_in_sys != hostname_prev): + LOG.debug(("%s differs from /etc/sysconfig/network." + " Assuming user maintained hostname."), prev_file) + if "/etc/sysconfig/network" in update_files: + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _read_hostname(self, filename, default=None): + contents = self._read_conf(filename) + for c in contents: + if len(c) != 2: + continue + (cmd, args) = c + cmd_c = cmd.lower().strip() + if cmd_c == 'hostname': + args_c = args.strip() + if args_c: + return args_c + return default + + def _read_conf(self, filename): + contents = util.load_file(filename, quiet=True) + conf_lines = [] + for line in contents.splitlines(): + c_line = line.strip() + if not c_line or c_line.startswith("#"): + conf_lines.append([line]) + continue + # Handle inline comments + c_pos = c_line.find("#") + if c_pos != -1: + c_line = c_line[0:c_pos].strip() + if not c_line: + conf_lines.append([line]) + continue + # Format should be CMD=ARG1 ARG2... + pieces = c_line.split("=", 1) + if not pieces or len(pieces) == 1: + conf_lines.append([line]) + continue + (cmd, args) = pieces + cmd = cmd.strip() + conf_lines.append([cmd, args]) + return conf_lines + + def set_timezone(self, tz): + tz_file = os.path.join("/usr/share/zoneinfo", tz) + if not os.path.isfile(tz_file): + raise Exception(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) + # Adjust the sysconfig clock zone setting + old_contents = self._read_conf("/etc/sysconfig/clock") + new_contents = [] + zone_added = False + # Update the 'ZONE' if it exists instead of appending + for entry in old_contents: + if not entry: + continue + if len(entry) == 1: + new_contents.append(entry[0]) + continue + (cmd, args) = entry + cmd_c = cmd.lower().strip() + if cmd_c == 'zone': + args = '"%s"' % (tz) + zone_added = True + new_contents.append("=".join([cmd, args])) + # Guess not found, append it + if not zone_added: + new_contents.append('ZONE="%s"' % (tz)) + tz_contents = "\n".join(new_contents) + util.write_file("/etc/sysconfig/clock", tz_contents) + # This ensures that the correct tz will be used for the system + util.copy(tz_file, "/etc/localtime") + + def package_command(self, command, args=None): + cmd = ['yum'] + # If enabled, then yum will be tolerant of errors on the command line + # with regard to packages. + # For example: if you request to install foo, bar and baz and baz is + # installed; yum won't error out complaining that baz is already + # installed. + cmd.append("-t") + # Determines whether or not yum prompts for confirmation + # of critical actions. We don't want to prompt... + cmd.append("-y") + cmd.append(command) + if args: + cmd.extend(args) + # Allow the output of this to flow outwards (ie not be captured) + util.subp(cmd, capture=False) + + +# This is a util function to translate a ubuntu /etc/network/interfaces 'blob' +# to a rhel equiv. that can then be written to /etc/sysconfig/network-scripts/ +# TODO remove when we have python-netcf active... +def translate_network(settings): + # Get the standard cmd, args from the ubuntu format + entries = [] + for line in settings.splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue + split_up = line.split(None, 1) + if len(split_up) <= 1: + continue + entries.append(split_up) + # Figure out where each iface section is + ifaces = [] + consume = {} + for (cmd, args) in entries: + if cmd == 'iface': + if consume: + ifaces.append(consume) + consume = {} + consume[cmd] = args + else: + consume[cmd] = args + # Check if anything left over to consume + absorb = False + for (cmd, args) in consume.iteritems(): + if cmd == 'iface': + absorb = True + if absorb: + ifaces.append(consume) + # Now translate + real_ifaces = {} + for info in ifaces: + if 'iface' not in info: + continue + iface_details = info['iface'].split(None) + dev_name = None + if len(iface_details) >= 1: + dev = iface_details[0].strip().lower() + if dev: + dev_name = dev + if not dev_name: + continue + iface_info = {} + if len(iface_details) >= 3: + proto_type = iface_details[2].strip().lower() + # Seems like this can be 'loopback' which we don't + # really care about + if proto_type in ['dhcp', 'static']: + iface_info['bootproto'] = proto_type + # These can just be copied over + for k in ['netmask', 'address', 'gateway', 'broadcast']: + if k in info: + val = info[k].strip().lower() + if val: + iface_info[k] = val + # Is any mac address spoofing going on?? + if 'hwaddress' in info: + hw_info = info['hwaddress'].lower().strip() + hw_split = hw_info.split(None, 1) + if len(hw_split) == 2 and hw_split[0].startswith('ether'): + hw_addr = hw_split[1] + if hw_addr: + iface_info['hwaddress'] = hw_addr + real_ifaces[dev_name] = iface_info + # Check for those that should be started on boot via 'auto' + for (cmd, args) in entries: + if cmd == 'auto': + # Seems like auto can be like 'auto eth0 eth0:1' so just get the + # first part out as the device name + args = args.split(None) + if not args: + continue + dev_name = args[0].strip().lower() + if dev_name in real_ifaces: + real_ifaces[dev_name]['auto'] = True + return real_ifaces -- cgit v1.2.3 From 47f9f64a03c90ea08b6e363f786dfee921345682 Mon Sep 17 00:00:00 2001 From: harlowja Date: Sun, 17 Jun 2012 13:48:53 -0700 Subject: 1. Adjust the ubuntu network hostname writing to go through a standard write function 2. Add comment as to why we search for "#" when reading --- cloudinit/distros/ubuntu.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index ec4d6b5b..786974ad 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -49,11 +49,14 @@ class Distro(distros.Distro): util.write_file("/etc/network/interfaces", settings) def set_hostname(self, hostname): - contents = "%s\n" % (hostname) - util.write_file("/etc/hostname", contents, 0644) + self._write_hostname(hostname, "/etc/hostname") LOG.debug("Setting hostname to %s", hostname) util.subp(['hostname', hostname]) + def _write_hostname(self, hostname, out_fn): + contents = "%s\n" % (hostname) + util.write_file(out_fn, contents, 0644) + def update_hostname(self, hostname, prev_file): hostname_prev = self._read_hostname(prev_file) hostname_in_etc = self._read_hostname("/etc/hostname") @@ -65,8 +68,7 @@ class Distro(distros.Distro): update_files.append("/etc/hostname") for fn in update_files: try: - contents = "%s\n" % (hostname) - util.write_file(fn, contents, 0644) + self._write_hostname(hostname, fn) except: util.logexc(LOG, "Failed to write hostname %s to %s", hostname, fn) @@ -82,11 +84,12 @@ class Distro(distros.Distro): contents = util.load_file(filename, quiet=True) for line in contents.splitlines(): hpos = line.find("#") + # Handle inline comments if hpos != -1: line = line[0:hpos] - line = line.rstrip() - if line: - return line + line_c = line.strip() + if line_c: + return line_c return default def _get_localhost_ip(self): -- cgit v1.2.3 From bc5322d2bc81b2421ae8dfe1bb02fa2fd61fed51 Mon Sep 17 00:00:00 2001 From: harlowja Date: Sun, 17 Jun 2012 18:21:30 -0700 Subject: Update the variable for comment positioning to be 'c_pos' (comment position) instead of 'hpos' (??) --- cloudinit/distros/ubuntu.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 786974ad..c0345c1d 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -83,10 +83,10 @@ class Distro(distros.Distro): def _read_hostname(self, filename, default=None): contents = util.load_file(filename, quiet=True) for line in contents.splitlines(): - hpos = line.find("#") + c_pos = line.find("#") # Handle inline comments - if hpos != -1: - line = line[0:hpos] + if c_pos != -1: + line = line[0:c_pos] line_c = line.strip() if line_c: return line_c -- cgit v1.2.3 From 1d6c6b6a97ca06f0044e7a3987d76519b4feee9e Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 18 Jun 2012 17:24:32 -0700 Subject: Include link to netcf library in the comment. --- cloudinit/distros/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 79d441ac..483642f3 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -53,7 +53,7 @@ class Distro(object): @abc.abstractmethod def _write_network(self, settings): - # In the future use the python-netcf + # In the future use the http://fedorahosted.org/netcf/ # to write this blob out in a distro format raise NotImplementedError() -- cgit v1.2.3 From 24b818fd7c26ccc4c31191c1ae274a5fff506038 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 18 Jun 2012 17:25:10 -0700 Subject: Add created by or added by cloud-init lines when we modify or create files. --- cloudinit/distros/rhel.py | 3 +++ cloudinit/distros/ubuntu.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 85283b23..e0ca5909 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -70,6 +70,7 @@ class Distro(distros.Distro): mac_addr = info.get('hwaddress') if mac_addr: lines.append("MACADDR=%s" % (mac_addr)) + lines.insert(0, '# Created by cloud-init') contents = "\n".join(lines) net_fn = NETWORK_FN_TPL % (dev) util.write_file(net_fn, contents, 0644) @@ -100,6 +101,7 @@ class Distro(distros.Distro): new_contents.append("=".join([cmd, args])) # Guess not found, append it if not adjusted: + new_contents.append("# Added by cloud-init") new_contents.append("HOSTNAME=%s" % (hostname)) contents = "\n".join(new_contents) util.write_file(out_fn, contents, 0644) @@ -189,6 +191,7 @@ class Distro(distros.Distro): new_contents.append("=".join([cmd, args])) # Guess not found, append it if not zone_added: + new_contents.append("# Added by cloud-init") new_contents.append('ZONE="%s"' % (tz)) tz_contents = "\n".join(new_contents) util.write_file("/etc/sysconfig/clock", tz_contents) diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index c0345c1d..24724d83 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -54,7 +54,10 @@ class Distro(distros.Distro): util.subp(['hostname', hostname]) def _write_hostname(self, hostname, out_fn): - contents = "%s\n" % (hostname) + lines = [] + lines.append("# Created by cloud-init") + lines.append(str(hostname)) + contents = "\n".join(lines) util.write_file(out_fn, contents, 0644) def update_hostname(self, hostname, prev_file): -- cgit v1.2.3 From 43eb6d5aace53bef2116dde0796807befef1d8ff Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 19 Jun 2012 17:58:41 -0700 Subject: Make most of all the places use the paths join() function so that testing with non-real read/write paths is easier. --- cloudinit/distros/rhel.py | 6 ++++- cloudinit/distros/ubuntu.py | 7 ++++-- cloudinit/helpers.py | 34 +++++++++++++++++++---------- cloudinit/ssh_util.py | 10 ++++++--- cloudinit/transforms/apt_pipelining.py | 15 +++++-------- cloudinit/transforms/apt_update_upgrade.py | 21 +++++++++++------- cloudinit/transforms/ca_certs.py | 23 ++++++++++--------- cloudinit/transforms/chef.py | 31 +++++++++++++++++--------- cloudinit/transforms/keys_to_console.py | 9 ++++---- cloudinit/transforms/landscape.py | 20 ++++++++++------- cloudinit/transforms/mcollective.py | 25 +++++++++++---------- cloudinit/transforms/mounts.py | 6 ++--- cloudinit/transforms/phone_home.py | 2 +- cloudinit/transforms/puppet.py | 34 +++++++++++++++++------------ cloudinit/transforms/resizefs.py | 8 +++---- cloudinit/transforms/rightscale_userdata.py | 1 + cloudinit/transforms/rsyslog.py | 3 ++- cloudinit/transforms/runcmd.py | 2 +- cloudinit/transforms/salt_minion.py | 2 ++ cloudinit/transforms/set_passwords.py | 4 ++-- cloudinit/transforms/ssh.py | 13 ++++++----- 21 files changed, 167 insertions(+), 109 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index e0ca5909..aef7f6f3 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -73,6 +73,7 @@ class Distro(distros.Distro): lines.insert(0, '# Created by cloud-init') contents = "\n".join(lines) net_fn = NETWORK_FN_TPL % (dev) + net_fn = self._paths.join(False, net_fn) util.write_file(net_fn, contents, 0644) def set_hostname(self, hostname): @@ -104,6 +105,7 @@ class Distro(distros.Distro): new_contents.append("# Added by cloud-init") new_contents.append("HOSTNAME=%s" % (hostname)) contents = "\n".join(new_contents) + out_fn = self._paths.join(False, out_fn) util.write_file(out_fn, contents, 0644) def update_hostname(self, hostname, prev_file): @@ -143,6 +145,7 @@ class Distro(distros.Distro): return default def _read_conf(self, filename): + filename = self._paths.join(True, filename) contents = util.load_file(filename, quiet=True) conf_lines = [] for line in contents.splitlines(): @@ -194,7 +197,8 @@ class Distro(distros.Distro): new_contents.append("# Added by cloud-init") new_contents.append('ZONE="%s"' % (tz)) tz_contents = "\n".join(new_contents) - util.write_file("/etc/sysconfig/clock", tz_contents) + tz_fn = self._paths.join(False, "/etc/sysconfig/clock") + util.write_file(tz_fn, tz_contents) # This ensures that the correct tz will be used for the system util.copy(tz_file, "/etc/localtime") diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 24724d83..94565b14 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -46,7 +46,8 @@ class Distro(distros.Distro): self.package_command('install', pkglist) def _write_network(self, settings): - util.write_file("/etc/network/interfaces", settings) + n_fn = self._paths.join(False, "/etc/network/interfaces") + util.write_file(n_fn, settings) def set_hostname(self, hostname): self._write_hostname(hostname, "/etc/hostname") @@ -84,6 +85,7 @@ class Distro(distros.Distro): util.subp(['hostname', hostname]) def _read_hostname(self, filename, default=None): + filename = self._paths.join(True, filename) contents = util.load_file(filename, quiet=True) for line in contents.splitlines(): c_pos = line.find("#") @@ -105,7 +107,8 @@ class Distro(distros.Distro): raise Exception(("Invalid timezone %s," " no file found at %s") % (tz, tz_file)) tz_contents = "%s\n" % tz - util.write_file("/etc/timezone", tz_contents) + tz_fn = self._paths.join(False, "/etc/timezone") + util.write_file(tz_fn, tz_contents) util.copy(tz_file, "/etc/localtime") def package_command(self, command, args=None): diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py index 3938e7ee..9f55a984 100644 --- a/cloudinit/helpers.py +++ b/cloudinit/helpers.py @@ -208,17 +208,18 @@ class Paths(object): def __init__(self, path_cfgs, ds=None): self.cfgs = path_cfgs # Populate all the initial paths - self.cloud_dir = self.join_paths(False, - path_cfgs.get('cloud_dir', - '/var/lib/cloud')) + self.cloud_dir = self.join(False, + path_cfgs.get('cloud_dir', + '/var/lib/cloud')) self.instance_link = os.path.join(self.cloud_dir, 'instance') self.boot_finished = os.path.join(self.instance_link, "boot-finished") self.upstart_conf_d = path_cfgs.get('upstart_dir') - template_dir = self.join_paths(True, - path_cfgs.get('templates_dir', - '/etc/cloud/templates/')) - self.template_tpl = os.path.join(template_dir, '%s.tmpl') + if self.upstart_conf_d: + self.upstart_conf_d = self.join(False, self.upstart_conf_d) self.seed_dir = os.path.join(self.cloud_dir, 'seed') + # This one isn't joined, since it should just be read-only + template_dir = path_cfgs.get('templates_dir', '/etc/cloud/templates/') + self.template_tpl = os.path.join(template_dir, '%s.tmpl') self.lookups = { "handlers": "handlers", "scripts": "scripts", @@ -235,16 +236,25 @@ class Paths(object): # joins the paths but also appends a read # or write root if available - def join_paths(self, read_only, *paths): + def join(self, read_only, *paths): if read_only: - root = self.cfgs.get('read_root', '/') + root = self.cfgs.get('read_root') else: - root = self.cfgs.get('write_root', '/') + root = self.cfgs.get('write_root') if not paths: return root - joined = os.path.join(*paths) + if len(paths) > 1: + joined = os.path.join(*paths) + else: + joined = paths[0] if root: - joined = os.path.join(root, joined.lstrip("/")) + pre_joined = joined + # Need to remove any starting '/' since this + # will confuse os.path.join + joined = joined.lstrip("/") + joined = os.path.join(root, joined) + LOG.debug("Translated %s to adjusted path %s (%s)", + pre_joined, joined, read_only) return joined # get_ipath_cur: get the current instance path for an item diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py index 13adbb09..96143d32 100644 --- a/cloudinit/ssh_util.py +++ b/cloudinit/ssh_util.py @@ -210,9 +210,12 @@ def update_authorized_keys(fname, keys): return '\n'.join(lines) -def setup_user_keys(keys, user, key_prefix, sshd_config_fn=DEF_SSHD_CFG): +def setup_user_keys(keys, user, key_prefix, paths): + + # Make sure the users .ssh dir is setup accordingly pwent = pwd.getpwnam(user) ssh_dir = os.path.join(pwent.pw_dir, '.ssh') + ssh_dir = paths.join(False, ssh_dir) if not os.path.exists(ssh_dir): util.ensure_dir(ssh_dir, mode=0700) util.chownbyid(ssh_dir, pwent.pw_uid, pwent.pw_gid) @@ -223,6 +226,7 @@ def setup_user_keys(keys, user, key_prefix, sshd_config_fn=DEF_SSHD_CFG): for k in keys: key_entries.append(parser.parse(str(k), def_opt=key_prefix)) + sshd_conf_fn = paths.join(True, DEF_SSHD_CFG) with util.SeLinuxGuard(ssh_dir, recursive=True): try: # AuthorizedKeysFile may contain tokens @@ -230,7 +234,7 @@ def setup_user_keys(keys, user, key_prefix, sshd_config_fn=DEF_SSHD_CFG): # The following tokens are defined: %% is replaced by a literal # '%', %h is replaced by the home directory of the user being # authenticated and %u is replaced by the username of that user. - ssh_cfg = parse_ssh_config(sshd_config_fn) + ssh_cfg = parse_ssh_config(sshd_conf_fn) akeys = ssh_cfg.get("authorizedkeysfile", '') akeys = akeys.strip() if not akeys: @@ -247,7 +251,7 @@ def setup_user_keys(keys, user, key_prefix, sshd_config_fn=DEF_SSHD_CFG): " in ssh config" " from %s, using 'AuthorizedKeysFile' file" " %s instead"), - sshd_config_fn, authorized_keys) + sshd_conf_fn, authorized_keys) content = update_authorized_keys(authorized_keys, key_entries) util.ensure_dir(os.path.dirname(authorized_keys), mode=0700) diff --git a/cloudinit/transforms/apt_pipelining.py b/cloudinit/transforms/apt_pipelining.py index d8e574b9..f460becb 100644 --- a/cloudinit/transforms/apt_pipelining.py +++ b/cloudinit/transforms/apt_pipelining.py @@ -23,7 +23,7 @@ frequency = PER_INSTANCE distros = ['ubuntu', 'debian'] -default_file = "/etc/apt/apt.conf.d/90cloud-init-pipelining" +DEFAULT_FILE = "/etc/apt/apt.conf.d/90cloud-init-pipelining" # Acquire::http::Pipeline-Depth can be a value # from 0 to 5 indicating how many outstanding requests APT should send. @@ -31,30 +31,27 @@ default_file = "/etc/apt/apt.conf.d/90cloud-init-pipelining" # on TCP connections - otherwise data corruption will occur. -def handle(_name, cfg, _cloud, log, _args): +def handle(_name, cfg, cloud, log, _args): apt_pipe_value = util.get_cfg_option_str(cfg, "apt_pipelining", False) apt_pipe_value_s = str(apt_pipe_value).lower().strip() if apt_pipe_value_s == "false": - write_apt_snippet("0", log) - + write_apt_snippet(cloud, "0", log, DEFAULT_FILE) elif apt_pipe_value_s in ("none", "unchanged", "os"): return - elif apt_pipe_value_s in [str(b) for b in xrange(0, 6)]: - write_apt_snippet(apt_pipe_value_s, log) - + write_apt_snippet(cloud, apt_pipe_value_s, log, DEFAULT_FILE) else: log.warn("Invalid option for apt_pipeling: %s", apt_pipe_value) -def write_apt_snippet(setting, log, f_name=default_file): +def write_apt_snippet(cloud, setting, log, f_name): """ Writes f_name with apt pipeline depth 'setting' """ file_contents = ("//Written by cloud-init per 'apt_pipelining'\n" 'Acquire::http::Pipeline-Depth "%s";\n') % (setting) - util.write_file(f_name, file_contents) + util.write_file(cloud.paths.join(False, f_name), file_contents) log.debug("Wrote %s with apt pipeline depth setting %s", f_name, setting) diff --git a/cloudinit/transforms/apt_update_upgrade.py b/cloudinit/transforms/apt_update_upgrade.py index d49d8bd2..29bbc1ae 100644 --- a/cloudinit/transforms/apt_update_upgrade.py +++ b/cloudinit/transforms/apt_update_upgrade.py @@ -26,6 +26,8 @@ from cloudinit import util distros = ['ubuntu', 'debian'] +PROXY_TPL = "Acquire::HTTP::Proxy \"%s\";\n" + def handle(_name, cfg, cloud, log, _args): update = util.get_cfg_option_bool(cfg, 'apt_update', False) @@ -44,22 +46,23 @@ def handle(_name, cfg, cloud, log, _args): "archive.ubuntu.com/ubuntu") rename_apt_lists(old_mir, mirror) - # set up proxy + # Set up any apt proxy proxy = cfg.get("apt_proxy", None) proxy_filename = "/etc/apt/apt.conf.d/95cloud-init-proxy" if proxy: try: - # See http://linux.die.net/man/5/apt.conf - contents = "Acquire::HTTP::Proxy \"%s\";\n" - util.write_file(proxy_filename, contents % (proxy)) + # See man 'apt.conf' + contents = PROXY_TPL % (proxy) + util.write_file(cloud.paths.join(False, proxy_filename), + contents) except Exception as e: util.logexc(log, "Failed to write proxy to %s", proxy_filename) elif os.path.isfile(proxy_filename): util.del_file(proxy_filename) - # process 'apt_sources' + # Process 'apt_sources' if 'apt_sources' in cfg: - errors = add_sources(cfg['apt_sources'], + errors = add_sources(cloud, cfg['apt_sources'], {'MIRROR': mirror, 'RELEASE': release}) for e in errors: log.warn("Source Error: %s", ':'.join(e)) @@ -138,7 +141,7 @@ def generate_sources_list(codename, mirror, cloud, log): log.warn("No template found, not rendering /etc/apt/sources.list") -def add_sources(srclist, template_params=None): +def add_sources(cloud, srclist, template_params=None): """ add entries in /etc/apt/sources.list.d for each abbreviated sources.list entry in 'srclist'. When rendering template, also @@ -187,7 +190,9 @@ def add_sources(srclist, template_params=None): errorlist.append([source, "failed add key"]) try: - util.write_file(ent['filename'], "%s\n" % (source), omode="ab") + contents = "%s\n" % (source) + util.write_file(cloud.paths.join(False, ent['filename']), + contents, omode="ab") except: errorlist.append([source, "failed write to file %s" % ent['filename']]) diff --git a/cloudinit/transforms/ca_certs.py b/cloudinit/transforms/ca_certs.py index e0802bfe..56c41561 100644 --- a/cloudinit/transforms/ca_certs.py +++ b/cloudinit/transforms/ca_certs.py @@ -33,7 +33,7 @@ def update_ca_certs(): util.subp(["update-ca-certificates"]) -def add_ca_certs(certs): +def add_ca_certs(cloud, certs): """ Adds certificates to the system. To actually apply the new certificates you must also call L{update_ca_certs}. @@ -41,26 +41,29 @@ def add_ca_certs(certs): @param certs: A list of certificate strings. """ if certs: - cert_file_contents = "\n".join(certs) + # First ensure they are strings... + cert_file_contents = "\n".join([str(c) for c in certs]) cert_file_fullpath = os.path.join(CA_CERT_PATH, CA_CERT_FILENAME) + cert_file_fullpath = cloud.paths.join(False, cert_file_fullpath) util.write_file(cert_file_fullpath, 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") + util.write_file(cloud.paths.join(False, CA_CERT_CONFIG), + "\n%s" % CA_CERT_FILENAME, omode="ab") -def remove_default_ca_certs(): +def remove_default_ca_certs(cloud): """ Removes all default trusted CA certificates from the system. To actually apply the change you must also call L{update_ca_certs}. """ - util.delete_dir_contents(CA_CERT_PATH) - util.delete_dir_contents(CA_CERT_SYSTEM_PATH) - util.write_file(CA_CERT_CONFIG, "", mode=0644) + util.delete_dir_contents(cloud.paths.join(False, CA_CERT_PATH)) + util.delete_dir_contents(cloud.paths.join(False, CA_CERT_SYSTEM_PATH)) + util.write_file(cloud.paths.join(False, CA_CERT_CONFIG), "", mode=0644) debconf_sel = "ca-certificates ca-certificates/trust_new_crts select no" util.subp(('debconf-set-selections', '-'), debconf_sel) -def handle(name, cfg, _cloud, log, _args): +def handle(name, cfg, cloud, log, _args): """ Call to handle ca-cert sections in cloud-config file. @@ -82,14 +85,14 @@ def handle(name, cfg, _cloud, log, _args): # default trusted CA certs first. if ca_cert_cfg.get("remove-defaults", False): log.debug("Removing default certificates") - remove_default_ca_certs() + remove_default_ca_certs(cloud) # If we are given any new trusted CA certs to add, add them. if "trusted" in ca_cert_cfg: trusted_certs = util.get_cfg_option_list(ca_cert_cfg, "trusted") if trusted_certs: log.debug("Adding %d certificates" % len(trusted_certs)) - add_ca_certs(trusted_certs) + add_ca_certs(cloud, trusted_certs) # Update the system with the new cert configuration. log.debug("Updating certificates") diff --git a/cloudinit/transforms/chef.py b/cloudinit/transforms/chef.py index 31bfb85f..4e8ef346 100644 --- a/cloudinit/transforms/chef.py +++ b/cloudinit/transforms/chef.py @@ -36,30 +36,40 @@ def handle(name, cfg, cloud, log, _args): return chef_cfg = cfg['chef'] - # ensure the chef directories we use exist - util.ensure_dirs(['/etc/chef', '/var/log/chef', '/var/lib/chef', - '/var/cache/chef', '/var/backups/chef', '/var/run/chef']) + # Ensure the chef directories we use exist + c_dirs = [ + '/etc/chef', + '/var/log/chef', + '/var/lib/chef', + '/var/cache/chef', + '/var/backups/chef', + '/var/run/chef', + ] + for d in c_dirs: + util.ensure_dir(cloud.paths.join(False, d)) - # set the validation key based on the presence of either 'validation_key' + # Set the validation key based on the presence of either 'validation_key' # or 'validation_cert'. In the case where both exist, 'validation_key' # takes precedence for key in ('validation_key', 'validation_cert'): if key in chef_cfg and chef_cfg[key]: - util.write_file('/etc/chef/validation.pem', chef_cfg[key]) + v_fn = cloud.paths.join(False, '/etc/chef/validation.pem') + util.write_file(v_fn, chef_cfg[key]) break - # create the chef config from template + # Create the chef config from template template_fn = cloud.get_template_filename('chef_client.rb') if template_fn: + iid = str(cloud.datasource.get_instance_id()) params = { 'server_url': chef_cfg['server_url'], - 'node_name': util.get_cfg_option_str(chef_cfg, 'node_name', - cloud.datasource.get_instance_id()), + 'node_name': util.get_cfg_option_str(chef_cfg, 'node_name', iid), 'environment': util.get_cfg_option_str(chef_cfg, 'environment', '_default'), 'validation_name': chef_cfg['validation_name'] } - templater.render_to_file(template_fn, '/etc/chef/client.rb', params) + out_fn = cloud.paths.join(False, '/etc/chef/client.rb') + templater.render_to_file(template_fn, out_fn, params) else: log.warn("No template found, not rendering to /etc/chef/client.rb") @@ -71,7 +81,8 @@ def handle(name, cfg, cloud, log, _args): initial_attributes = chef_cfg['initial_attributes'] for k in list(initial_attributes.keys()): initial_json[k] = initial_attributes[k] - util.write_file('/etc/chef/firstboot.json', json.dumps(initial_json)) + firstboot_fn = cloud.paths.join(False, '/etc/chef/firstboot.json') + util.write_file(firstboot_fn, 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'): diff --git a/cloudinit/transforms/keys_to_console.py b/cloudinit/transforms/keys_to_console.py index e974375f..40758198 100644 --- a/cloudinit/transforms/keys_to_console.py +++ b/cloudinit/transforms/keys_to_console.py @@ -29,23 +29,24 @@ frequency = PER_INSTANCE helper_tool = '/usr/lib/cloud-init/write-ssh-key-fingerprints' -def handle(name, cfg, _cloud, log, _args): +def handle(name, cfg, cloud, log, _args): if not os.path.exists(helper_tool): log.warn(("Unable to activate transform %s," " helper tool not found at %s"), name, helper_tool) return fp_blacklist = util.get_cfg_option_list(cfg, - "ssh_fp_console_blacklist", []) + "ssh_fp_console_blacklist", []) key_blacklist = util.get_cfg_option_list(cfg, - "ssh_key_console_blacklist", ["ssh-dss"]) + "ssh_key_console_blacklist", + ["ssh-dss"]) try: cmd = [helper_tool] cmd.append(','.join(fp_blacklist)) cmd.append(','.join(key_blacklist)) (stdout, _stderr) = util.subp(cmd) - util.write_file('/dev/console', stdout) + util.write_file(cloud.paths.join(False, '/dev/console'), stdout) except: log.warn("Writing keys to /dev/console failed!") raise diff --git a/cloudinit/transforms/landscape.py b/cloudinit/transforms/landscape.py index 19948d0e..29ce41b9 100644 --- a/cloudinit/transforms/landscape.py +++ b/cloudinit/transforms/landscape.py @@ -33,12 +33,12 @@ from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE -lsc_client_cfg_file = "/etc/landscape/client.conf" +LSC_CLIENT_CFG_FILE = "/etc/landscape/client.conf" distros = ['ubuntu'] # defaults taken from stock client.conf in landscape-client 11.07.1.1-0ubuntu2 -lsc_builtincfg = { +LSC_BUILTIN_CFG = { 'client': { 'log_level': "info", 'url': "https://landscape.canonical.com/message-system", @@ -48,7 +48,7 @@ lsc_builtincfg = { } -def handle(name, cfg, _cloud, log, _args): +def handle(name, cfg, cloud, log, _args): """ Basically turn a top level 'landscape' entry with a 'client' dict and render it to ConfigObj format under '[client]' section in @@ -66,15 +66,19 @@ def handle(name, cfg, _cloud, log, _args): " but not a dictionary type," " is a %s instead"), util.obj_name(ls_cloudcfg)) - merged = merge_together([lsc_builtincfg, lsc_client_cfg_file, ls_cloudcfg]) + lsc_client_fn = cloud.paths.join(True, LSC_CLIENT_CFG_FILE) + merged = merge_together([LSC_BUILTIN_CFG, lsc_client_fn, ls_cloudcfg]) - if not os.path.isdir(os.path.dirname(lsc_client_cfg_file)): - util.ensure_dir(os.path.dirname(lsc_client_cfg_file)) + lsc_dir = cloud.paths.join(False, os.path.dirname(lsc_client_fn)) + if not os.path.isdir(lsc_dir): + util.ensure_dir(lsc_dir) contents = StringIO() merged.write(contents) - util.write_file(lsc_client_cfg_file, contents.getvalue()) - log.debug("Wrote landscape config file to %s", lsc_client_cfg_file) + contents.flush() + + util.write_file(lsc_client_fn, contents.getvalue()) + log.debug("Wrote landscape config file to %s", lsc_client_fn) def merge_together(objs): diff --git a/cloudinit/transforms/mcollective.py b/cloudinit/transforms/mcollective.py index 5464fe8c..9754d6b8 100644 --- a/cloudinit/transforms/mcollective.py +++ b/cloudinit/transforms/mcollective.py @@ -24,8 +24,8 @@ from StringIO import StringIO from cloudinit import cfg as config from cloudinit import util -pubcert_file = "/etc/mcollective/ssl/server-public.pem" -pricert_file = "/etc/mcollective/ssl/server-private.pem" +PUBCERT_FILE = "/etc/mcollective/ssl/server-public.pem" +PRICERT_FILE = "/etc/mcollective/ssl/server-private.pem" def handle(name, cfg, cloud, log, _args): @@ -47,7 +47,8 @@ def handle(name, cfg, cloud, log, _args): mcollective_config = config.DefaultingConfigParser() # Read server.cfg values from original file in order to be able to mix # the rest up - old_contents = util.load_file('/etc/mcollective/server.cfg') + server_cfg_fn = cloud.paths.join(True, '/etc/mcollective/server.cfg') + old_contents = util.load_file(server_cfg_fn) # It doesn't contain any sections so just add one temporarily # Use a hash id based off the contents, # just incase of conflicts... (try to not have any...) @@ -61,17 +62,19 @@ def handle(name, cfg, cloud, log, _args): section_head = section_tpl % (attempts) sectioned_contents = "%s\n%s" % (section_head, old_contents) mcollective_config.readfp(StringIO(sectioned_contents), - filename='/etc/mcollective/server.cfg') + filename=server_cfg_fn) for (cfg_name, cfg) in mcollective_cfg['conf'].iteritems(): if cfg_name == 'public-cert': - util.write_file(pubcert_file, cfg, mode=0644) + pubcert_fn = cloud.paths.join(True, PUBCERT_FILE) + util.write_file(pubcert_fn, cfg, mode=0644) mcollective_config.set(cfg_name, - 'plugin.ssl_server_public', pubcert_file) + 'plugin.ssl_server_public', pubcert_fn) mcollective_config.set(cfg_name, 'securityprovider', 'ssl') elif cfg_name == 'private-cert': - util.write_file(pricert_file, cfg, mode=0600) + pricert_fn = cloud.paths.join(True, PRICERT_FILE) + util.write_file(pricert_fn, cfg, mode=0600) mcollective_config.set(cfg_name, - 'plugin.ssl_server_private', pricert_file) + 'plugin.ssl_server_private', pricert_fn) mcollective_config.set(cfg_name, 'securityprovider', 'ssl') else: # Iterate throug the config items, we'll use ConfigParser.set @@ -80,15 +83,15 @@ def handle(name, cfg, cloud, log, _args): mcollective_config.set(cfg_name, o, v) # We got all our config as wanted we'll rename # the previous server.cfg and create our new one - util.rename('/etc/mcollective/server.cfg', - '/etc/mcollective/server.cfg.old') + old_fn = "%s.old" % (server_cfg_fn) + util.rename(server_cfg_fn, old_fn) # Now we got the whole file, write to disk except the section # we added so that config parser won't error out when trying to read. # Note below, that we've just used ConfigParser because it generally # works. Below, we remove the initial 'nullsection' header. contents = mcollective_config.stringify() contents = contents.replace("%s\n" % (section_head), "") - util.write_file('/etc/mcollective/server.cfg', contents, mode=0644) + util.write_file(server_cfg_fn, contents, mode=0644) # Start mcollective util.subp(['service', 'mcollective', 'start'], capture=False) diff --git a/cloudinit/transforms/mounts.py b/cloudinit/transforms/mounts.py index 44182b87..700fbc44 100644 --- a/cloudinit/transforms/mounts.py +++ b/cloudinit/transforms/mounts.py @@ -168,7 +168,7 @@ def handle(_name, cfg, cloud, log, _args): cc_lines.append('\t'.join(line)) fstab_lines = [] - fstab = util.load_file("/etc/fstab") + fstab = util.load_file(cloud.paths.join(True, "/etc/fstab")) for line in fstab.splitlines(): try: toks = ws.split(line) @@ -180,7 +180,7 @@ def handle(_name, cfg, cloud, log, _args): fstab_lines.extend(cc_lines) contents = "%s\n" % ('\n'.join(fstab_lines)) - util.write_file("/etc/fstab", contents) + util.write_file(cloud.paths.join(False, "/etc/fstab"), contents) if needswap: try: @@ -190,7 +190,7 @@ def handle(_name, cfg, cloud, log, _args): for d in dirs: try: - util.ensure_dir(d) + util.ensure_dir(cloud.paths.join(False, d)) except: util.logexc(log, "Failed to make '%s' config-mount", d) diff --git a/cloudinit/transforms/phone_home.py b/cloudinit/transforms/phone_home.py index 98ff2b85..a8752527 100644 --- a/cloudinit/transforms/phone_home.py +++ b/cloudinit/transforms/phone_home.py @@ -77,7 +77,7 @@ def handle(name, cfg, cloud, log, args): for (n, path) in pubkeys.iteritems(): try: - all_keys[n] = util.load_file(path) + all_keys[n] = util.load_file(cloud.paths.join(True, path)) except: util.logexc(log, ("%s: failed to open, can not" " phone home that data"), path) diff --git a/cloudinit/transforms/puppet.py b/cloudinit/transforms/puppet.py index 76cc9732..d55118ea 100644 --- a/cloudinit/transforms/puppet.py +++ b/cloudinit/transforms/puppet.py @@ -43,7 +43,8 @@ def handle(name, cfg, cloud, log, _args): # ... and then update the puppet configuration if 'conf' in puppet_cfg: # Add all sections from the conf object to puppet.conf - contents = util.load_file('/etc/puppet/puppet.conf') + puppet_conf_fn = cloud.paths.join(False, '/etc/puppet/puppet.conf') + contents = util.load_file(puppet_conf_fn) # Create object for reading puppet.conf values puppet_config = config.DefaultingConfigParser() # Read puppet.conf values from original file in order to be able to @@ -51,21 +52,27 @@ def handle(name, cfg, cloud, log, _args): cleaned_lines = [i.lstrip() for i in contents.splitlines()] cleaned_contents = '\n'.join(cleaned_lines) puppet_config.readfp(StringIO(cleaned_contents), - filename='/etc/puppet/puppet.conf') + filename=puppet_conf_fn) for (cfg_name, cfg) in puppet_cfg['conf'].iteritems(): - # ca_cert configuration is a special case - # Dump the puppetmaster ca certificate in the correct place + # Cert configuration is a special case + # Dump the puppet master ca certificate in the correct place if cfg_name == 'ca_cert': # Puppet ssl sub-directory isn't created yet # Create it with the proper permissions and ownership - util.ensure_dir('/var/lib/puppet/ssl', 0771) - util.chownbyid('/var/lib/puppet/ssl', + pp_ssl_dir = cloud.paths.join(False, '/var/lib/puppet/ssl') + util.ensure_dir(pp_ssl_dir, 0771) + util.chownbyid(pp_ssl_dir, pwd.getpwnam('puppet').pw_uid, 0) - util.ensure_dir('/var/lib/puppet/ssl/certs/') - util.chownbyid('/var/lib/puppet/ssl/certs/', + pp_ssl_certs = cloud.paths.join(False, + '/var/lib/puppet/ssl/certs/') + util.ensure_dir(pp_ssl_certs) + util.chownbyid(pp_ssl_certs, pwd.getpwnam('puppet').pw_uid, 0) - util.write_file('/var/lib/puppet/ssl/certs/ca.pem', cfg) - util.chownbyid('/var/lib/puppet/ssl/certs/ca.pem', + pp_ssl_ca_certs = cloud.paths.join(False, + ('/var/lib/puppet/' + 'ssl/certs/ca.pem')) + util.write_file(pp_ssl_ca_certs, cfg) + util.chownbyid(pp_ssl_ca_certs, pwd.getpwnam('puppet').pw_uid, 0) else: # Iterate throug the config items, we'll use ConfigParser.set @@ -82,10 +89,9 @@ def handle(name, cfg, cloud, log, _args): puppet_config.set(cfg_name, o, v) # We got all our config as wanted we'll rename # the previous puppet.conf and create our new one - util.rename('/etc/puppet/puppet.conf', - '/etc/puppet/puppet.conf.old') - contents = puppet_config.stringify() - util.write_file('/etc/puppet/puppet.conf', contents) + puppet_conf_old_fn = "%s.old" % (puppet_conf_fn) + util.rename(puppet_conf_fn, puppet_conf_old_fn) + util.write_file(puppet_conf_fn, puppet_config.stringify()) # Set puppet to automatically start if os.path.exists('/etc/default/puppet'): diff --git a/cloudinit/transforms/resizefs.py b/cloudinit/transforms/resizefs.py index fe012417..fd2bb9e1 100644 --- a/cloudinit/transforms/resizefs.py +++ b/cloudinit/transforms/resizefs.py @@ -62,7 +62,7 @@ def get_fs_type(st_dev, path, log): raise -def handle(name, cfg, _cloud, log, args): +def handle(name, cfg, cloud, log, args): if len(args) != 0: resize_root = args[0] else: @@ -74,11 +74,11 @@ def handle(name, cfg, _cloud, log, args): # TODO is the directory ok to be used?? resize_root_d = util.get_cfg_option_str(cfg, "resize_rootfs_tmp", "/run") + resize_root_d = cloud.paths.join(False, resize_root_d) util.ensure_dir(resize_root_d) - # TODO: allow what is to be resized to - # be configurable?? - resize_what = "/" + # TODO: allow what is to be resized to be configurable?? + resize_what = cloud.paths.join(False) with util.SilentTemporaryFile(prefix="cloudinit.resizefs.", dir=resize_root_d, delete=True) as tfh: devpth = tfh.name diff --git a/cloudinit/transforms/rightscale_userdata.py b/cloudinit/transforms/rightscale_userdata.py index 8dfd845f..dc06f9ec 100644 --- a/cloudinit/transforms/rightscale_userdata.py +++ b/cloudinit/transforms/rightscale_userdata.py @@ -78,6 +78,7 @@ def handle(name, _cfg, cloud, log, _args): urls = mdict[my_hookname] for (i, url) in enumerate(urls): fname = os.path.join(scripts_d, "rightscale-%02i" % (i)) + fname = cloud.paths.join(False, fname) try: resp = uhelp.readurl(url) # Ensure its a valid http response (and something gotten) diff --git a/cloudinit/transforms/rsyslog.py b/cloudinit/transforms/rsyslog.py index 71b74711..f2c1de1e 100644 --- a/cloudinit/transforms/rsyslog.py +++ b/cloudinit/transforms/rsyslog.py @@ -71,7 +71,8 @@ def handle(name, cfg, cloud, log, _args): try: contents = "%s\n" % (content) - util.write_file(filename, contents, omode=omode) + util.write_file(cloud.paths.join(False, filename), + contents, omode=omode) except Exception: util.logexc(log, "Failed to write to %s", filename) diff --git a/cloudinit/transforms/runcmd.py b/cloudinit/transforms/runcmd.py index 31a254a5..f121484b 100644 --- a/cloudinit/transforms/runcmd.py +++ b/cloudinit/transforms/runcmd.py @@ -33,6 +33,6 @@ def handle(name, cfg, cloud, log, _args): cmd = cfg["runcmd"] try: content = util.shellify(cmd) - util.write_file(out_fn, content, 0700) + util.write_file(cloud.paths.join(False, out_fn), content, 0700) except: util.logexc(log, "Failed to shellify %s into file %s", cmd, out_fn) diff --git a/cloudinit/transforms/salt_minion.py b/cloudinit/transforms/salt_minion.py index d05d2a1e..16f5286d 100644 --- a/cloudinit/transforms/salt_minion.py +++ b/cloudinit/transforms/salt_minion.py @@ -35,6 +35,7 @@ def handle(name, cfg, cloud, log, _args): # Ensure we can configure files at the right dir config_dir = salt_cfg.get("config_dir", '/etc/salt') + config_dir = cloud.paths.join(False, config_dir) util.ensure_dir(config_dir) # ... and then update the salt configuration @@ -47,6 +48,7 @@ def handle(name, cfg, cloud, log, _args): # ... copy the key pair if specified if 'public_key' in salt_cfg and 'private_key' in salt_cfg: pki_dir = salt_cfg.get('pki_dir', '/etc/salt/pki') + pki_dir = cloud.paths.join(pki_dir) with util.umask(077): util.ensure_dir(pki_dir) pub_name = os.path.join(pki_dir, 'minion.pub') diff --git a/cloudinit/transforms/set_passwords.py b/cloudinit/transforms/set_passwords.py index c0cc4e84..e7049f22 100644 --- a/cloudinit/transforms/set_passwords.py +++ b/cloudinit/transforms/set_passwords.py @@ -130,8 +130,8 @@ def handle(_name, cfg, cloud, log, args): replaced_auth = True new_lines.append(replacement) - new_contents = "\n".join(new_lines) - util.write_file('/etc/ssh/sshd_config', new_contents) + util.write_file(cloud.paths.join(False, '/etc/ssh/sshd_config'), + "\n".join(new_lines)) try: cmd = ['service'] diff --git a/cloudinit/transforms/ssh.py b/cloudinit/transforms/ssh.py index b1f2ce89..33d4bb54 100644 --- a/cloudinit/transforms/ssh.py +++ b/cloudinit/transforms/ssh.py @@ -64,7 +64,8 @@ def handle(_name, cfg, cloud, log, _args): if key in key2file: tgt_fn = key2file[key][0] tgt_perms = key2file[key][1] - util.write_file(tgt_fn, val, tgt_perms) + util.write_file(cloud.paths.join(False, tgt_fn), + val, tgt_perms) for (priv, pub) in priv2pub.iteritems(): if pub in cfg['ssh_keys'] or not priv in cfg['ssh_keys']: @@ -86,6 +87,7 @@ def handle(_name, cfg, cloud, log, _args): generate_keys) for keytype in genkeys: keyfile = '/etc/ssh/ssh_host_%s_key' % (keytype) + keyfile = cloud.paths.join(False, keyfile) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: @@ -107,20 +109,21 @@ def handle(_name, cfg, cloud, log, _args): cfgkeys = cfg["ssh_authorized_keys"] keys.extend(cfgkeys) - apply_credentials(keys, user, disable_root, disable_root_opts) + apply_credentials(keys, user, cloud.paths, + disable_root, disable_root_opts) except: util.logexc(log, "Applying ssh credentials failed!") -def apply_credentials(keys, user, disable_root, disable_root_opts): +def apply_credentials(keys, user, paths, disable_root, disable_root_opts): keys = set(keys) if user: - ssh_util.setup_user_keys(keys, user, '') + ssh_util.setup_user_keys(keys, user, '', paths) if disable_root and user: key_prefix = disable_root_opts.replace('$USER', user) else: key_prefix = '' - ssh_util.setup_user_keys(keys, 'root', key_prefix) + ssh_util.setup_user_keys(keys, 'root', key_prefix, paths) -- cgit v1.2.3 From 319412b9fac28ae0a0ae059f44e668bc7d3bf857 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 19 Jun 2012 18:07:47 -0700 Subject: More paths missed (durn), but now fixed. --- cloudinit/distros/ubuntu.py | 2 +- cloudinit/transforms/ssh.py | 4 +++- cloudinit/transforms/update_etc_hosts.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 94565b14..eeda2921 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -59,7 +59,7 @@ class Distro(distros.Distro): lines.append("# Created by cloud-init") lines.append(str(hostname)) contents = "\n".join(lines) - util.write_file(out_fn, contents, 0644) + util.write_file(self._paths.join(False, out_fn), contents, 0644) def update_hostname(self, hostname, prev_file): hostname_prev = self._read_hostname(prev_file) diff --git a/cloudinit/transforms/ssh.py b/cloudinit/transforms/ssh.py index 33d4bb54..e5e99560 100644 --- a/cloudinit/transforms/ssh.py +++ b/cloudinit/transforms/ssh.py @@ -52,7 +52,8 @@ def handle(_name, cfg, cloud, log, _args): # remove the static keys from the pristine image if cfg.get("ssh_deletekeys", True): - for f in glob.glob("/etc/ssh/ssh_host_*key*"): + key_pth = cloud.paths.join(False, "/etc/ssh/", "ssh_host_*key*") + for f in glob.glob(key_pth): try: util.del_file(f) except: @@ -88,6 +89,7 @@ def handle(_name, cfg, cloud, log, _args): for keytype in genkeys: keyfile = '/etc/ssh/ssh_host_%s_key' % (keytype) keyfile = cloud.paths.join(False, keyfile) + util.ensure_dir(os.path.dirname(keyfile)) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: diff --git a/cloudinit/transforms/update_etc_hosts.py b/cloudinit/transforms/update_etc_hosts.py index d0e56183..75615db1 100644 --- a/cloudinit/transforms/update_etc_hosts.py +++ b/cloudinit/transforms/update_etc_hosts.py @@ -42,7 +42,8 @@ def handle(name, cfg, cloud, log, _args): raise Exception(("No hosts template could be" " found for distro %s") % (distro_n)) - templater.render_to_file(tpl_fn_name, '/etc/hosts', + out_fn = cloud.paths.join(False, '/etc/hosts') + templater.render_to_file(tpl_fn_name, out_fn, {'hostname': hostname, 'fqdn': fqdn}) elif manage_hosts == "localhost": -- cgit v1.2.3 From 39e3649f0cd863ffcb82b1ea83c147272b1784a2 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 19 Jun 2012 18:30:28 -0700 Subject: More cleanups around read/write roots --- cloudinit/distros/__init__.py | 6 ++++-- cloudinit/distros/rhel.py | 32 ++++++++++++++++++-------------- cloudinit/distros/ubuntu.py | 35 ++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 31 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 483642f3..6a98fdb1 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -100,7 +100,8 @@ class Distro(object): new_etchosts = StringIO() need_write = False need_change = True - for line in util.load_file("/etc/hosts").splitlines(): + hosts_ro_fn = self._paths.join(True, "/etc/hosts") + for line in util.load_file(hosts_ro_fn).splitlines(): if line.strip().startswith(header): continue if not line.strip() or line.strip().startswith("#"): @@ -124,7 +125,8 @@ class Distro(object): need_write = True if need_write: contents = new_etchosts.getvalue() - util.write_file("/etc/hosts", contents, mode=0644) + util.write_file(self._paths.join(False, "/etc/hosts"), + contents, mode=0644) def _interface_action(self, action): if action not in IFACE_ACTIONS: diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index aef7f6f3..e9f3f5d9 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -73,13 +73,15 @@ class Distro(distros.Distro): lines.insert(0, '# Created by cloud-init') contents = "\n".join(lines) net_fn = NETWORK_FN_TPL % (dev) - net_fn = self._paths.join(False, net_fn) - util.write_file(net_fn, contents, 0644) + util.write_file(self._paths.join(False, net_fn), contents, 0644) def set_hostname(self, hostname): - self._write_hostname(hostname, "/etc/sysconfig/network") - LOG.debug("Setting hostname to %s", hostname) - util.subp(['hostname', hostname]) + out_fn = self._paths.join(False, '/etc/sysconfig/network') + self._write_hostname(hostname, out_fn) + if out_fn == '/etc/sysconfig/network': + # Only do this if we are running in non-adjusted root mode + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) def _write_hostname(self, hostname, out_fn): old_contents = [] @@ -105,18 +107,19 @@ class Distro(distros.Distro): new_contents.append("# Added by cloud-init") new_contents.append("HOSTNAME=%s" % (hostname)) contents = "\n".join(new_contents) - out_fn = self._paths.join(False, out_fn) util.write_file(out_fn, contents, 0644) def update_hostname(self, hostname, prev_file): hostname_prev = self._read_hostname(prev_file) - hostname_in_sys = self._read_hostname("/etc/sysconfig/network") + read_fn = self._paths.join(True, "/etc/sysconfig/network") + hostname_in_sys = self._read_hostname(read_fn) update_files = [] if not hostname_prev or hostname_prev != hostname: update_files.append(prev_file) if (not hostname_in_sys or - (hostname_in_sys == hostname_prev and hostname_in_sys != hostname)): - update_files.append("/etc/sysconfig/network") + (hostname_in_sys == hostname_prev and hostname_in_sys != hostname)): + write_fn = self._paths.join(False, "/etc/sysconfig/network") + update_files.append(write_fn) for fn in update_files: try: self._write_hostname(hostname, fn) @@ -128,6 +131,7 @@ class Distro(distros.Distro): LOG.debug(("%s differs from /etc/sysconfig/network." " Assuming user maintained hostname."), prev_file) if "/etc/sysconfig/network" in update_files: + # Only do this if we are running in non-adjusted root mode LOG.debug("Setting hostname to %s", hostname) util.subp(['hostname', hostname]) @@ -145,7 +149,6 @@ class Distro(distros.Distro): return default def _read_conf(self, filename): - filename = self._paths.join(True, filename) contents = util.load_file(filename, quiet=True) conf_lines = [] for line in contents.splitlines(): @@ -176,7 +179,8 @@ class Distro(distros.Distro): raise Exception(("Invalid timezone %s," " no file found at %s") % (tz, tz_file)) # Adjust the sysconfig clock zone setting - old_contents = self._read_conf("/etc/sysconfig/clock") + read_fn = self._paths.join(True, "/etc/sysconfig/clock") + old_contents = self._read_conf(read_fn) new_contents = [] zone_added = False # Update the 'ZONE' if it exists instead of appending @@ -197,10 +201,10 @@ class Distro(distros.Distro): new_contents.append("# Added by cloud-init") new_contents.append('ZONE="%s"' % (tz)) tz_contents = "\n".join(new_contents) - tz_fn = self._paths.join(False, "/etc/sysconfig/clock") - util.write_file(tz_fn, tz_contents) + write_fn = self._paths.join(False, "/etc/sysconfig/clock") + util.write_file(write_fn, tz_contents) # This ensures that the correct tz will be used for the system - util.copy(tz_file, "/etc/localtime") + util.copy(tz_file, self._paths.join(False, "/etc/localtime")) def package_command(self, command, args=None): cmd = ['yum'] diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index eeda2921..515b59c8 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -46,30 +46,35 @@ class Distro(distros.Distro): self.package_command('install', pkglist) def _write_network(self, settings): - n_fn = self._paths.join(False, "/etc/network/interfaces") - util.write_file(n_fn, settings) + net_fn = self._paths.join(False, "/etc/network/interfaces") + util.write_file(net_fn, settings) def set_hostname(self, hostname): - self._write_hostname(hostname, "/etc/hostname") - LOG.debug("Setting hostname to %s", hostname) - util.subp(['hostname', hostname]) + out_fn = self._paths.join(False, "/etc/hostname") + self._write_hostname(hostname, out_fn) + if out_fn == '/etc/hostname': + # Only do this if we are running in non-adjusted root mode + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) def _write_hostname(self, hostname, out_fn): lines = [] lines.append("# Created by cloud-init") lines.append(str(hostname)) contents = "\n".join(lines) - util.write_file(self._paths.join(False, out_fn), contents, 0644) + util.write_file(out_fn, contents, 0644) - def update_hostname(self, hostname, prev_file): - hostname_prev = self._read_hostname(prev_file) - hostname_in_etc = self._read_hostname("/etc/hostname") + def update_hostname(self, hostname, prev_fn): + hostname_prev = self._read_hostname(prev_fn) + read_fn = self._paths.join(True, "/etc/hostname") + hostname_in_etc = self._read_hostname(read_fn) update_files = [] if not hostname_prev or hostname_prev != hostname: - update_files.append(prev_file) + update_files.append(prev_fn) if (not hostname_in_etc or - (hostname_in_etc == hostname_prev and hostname_in_etc != hostname)): - update_files.append("/etc/hostname") + (hostname_in_etc == hostname_prev and hostname_in_etc != hostname)): + write_fn = self._paths.join(False, "/etc/hostname") + update_files.append(write_fn) for fn in update_files: try: self._write_hostname(hostname, fn) @@ -79,13 +84,13 @@ class Distro(distros.Distro): if (hostname_in_etc and hostname_prev and hostname_in_etc != hostname_prev): LOG.debug(("%s differs from /etc/hostname." - " Assuming user maintained hostname."), prev_file) + " Assuming user maintained hostname."), prev_fn) if "/etc/hostname" in update_files: + # Only do this if we are running in non-adjusted root mode LOG.debug("Setting hostname to %s", hostname) util.subp(['hostname', hostname]) def _read_hostname(self, filename, default=None): - filename = self._paths.join(True, filename) contents = util.load_file(filename, quiet=True) for line in contents.splitlines(): c_pos = line.find("#") @@ -109,7 +114,7 @@ class Distro(distros.Distro): tz_contents = "%s\n" % tz tz_fn = self._paths.join(False, "/etc/timezone") util.write_file(tz_fn, tz_contents) - util.copy(tz_file, "/etc/localtime") + util.copy(tz_file, self._paths.join(False, "/etc/localtime")) def package_command(self, command, args=None): e = os.environ.copy() -- cgit v1.2.3 From 1f138d244b1402298d31adcbc6815eca12c89da9 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Tue, 19 Jun 2012 21:49:58 -0700 Subject: Pylint line length fixups. --- cloudinit/distros/rhel.py | 3 ++- cloudinit/distros/ubuntu.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index e9f3f5d9..b67ae5b8 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -117,7 +117,8 @@ class Distro(distros.Distro): if not hostname_prev or hostname_prev != hostname: update_files.append(prev_file) if (not hostname_in_sys or - (hostname_in_sys == hostname_prev and hostname_in_sys != hostname)): + (hostname_in_sys == hostname_prev + and hostname_in_sys != hostname)): write_fn = self._paths.join(False, "/etc/sysconfig/network") update_files.append(write_fn) for fn in update_files: diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 515b59c8..5a1b572e 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -72,7 +72,8 @@ class Distro(distros.Distro): if not hostname_prev or hostname_prev != hostname: update_files.append(prev_fn) if (not hostname_in_etc or - (hostname_in_etc == hostname_prev and hostname_in_etc != hostname)): + (hostname_in_etc == hostname_prev and + hostname_in_etc != hostname)): write_fn = self._paths.join(False, "/etc/hostname") update_files.append(write_fn) for fn in update_files: -- cgit v1.2.3 From 7f5f2c1a9396cb24a97c412fbe3422f10e485b46 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 20 Jun 2012 16:39:56 -0700 Subject: Catch the import error rather than the runtime error. --- cloudinit/distros/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 6a98fdb1..45dd85ec 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -147,10 +147,10 @@ class Distro(object): def fetch(distro_name, mods=(__name__, )): mod = None for m in mods: + mod_name = "%s.%s" % (m, distro_name) try: - mod_name = "%s.%s" % (m, distro_name) mod = importer.import_module(mod_name) - except RuntimeError: + except ImportError: pass if not mod: raise RuntimeError("No distribution found for distro %s" -- cgit v1.2.3 From ec4bdc4fb8d8d3a8f8b4f498eb47eac740485ede Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 20 Jun 2012 17:13:55 -0700 Subject: Massive pylint + pep8 fixups! --- bin/cloud-init | 20 +++++++-------- cloudinit/cloud.py | 3 ++- cloudinit/config/__init__.py | 2 +- cloudinit/config/cc_chef.py | 14 +++++------ cloudinit/config/cc_disable_ec2_metadata.py | 4 +-- cloudinit/config/cc_final_message.py | 8 +++--- cloudinit/config/cc_foo.py | 8 +++--- cloudinit/config/cc_keys_to_console.py | 8 +++--- cloudinit/config/cc_landscape.py | 2 +- cloudinit/config/cc_mcollective.py | 4 +-- cloudinit/config/cc_mounts.py | 13 +++++----- cloudinit/config/cc_phone_home.py | 11 +++++--- cloudinit/config/cc_puppet.py | 2 +- cloudinit/config/cc_resizefs.py | 10 ++++---- cloudinit/config/cc_salt_minion.py | 2 +- cloudinit/config/cc_scripts_per_boot.py | 6 ++--- cloudinit/config/cc_scripts_per_instance.py | 6 ++--- cloudinit/config/cc_scripts_per_once.py | 6 ++--- cloudinit/config/cc_scripts_user.py | 6 ++--- cloudinit/config/cc_set_passwords.py | 4 +-- cloudinit/config/cc_ssh.py | 39 +++++++++++++++-------------- cloudinit/distros/__init__.py | 1 - cloudinit/distros/rhel.py | 14 +++++------ cloudinit/distros/ubuntu.py | 6 ++--- cloudinit/handlers/__init__.py | 8 +++--- cloudinit/helpers.py | 6 ++--- cloudinit/log.py | 2 -- cloudinit/settings.py | 2 +- cloudinit/sources/DataSourceCloudStack.py | 2 +- cloudinit/sources/DataSourceConfigDrive.py | 2 +- cloudinit/sources/DataSourceEc2.py | 6 ++--- cloudinit/sources/DataSourceMAAS.py | 1 + cloudinit/sources/DataSourceNoCloud.py | 2 +- cloudinit/ssh_util.py | 5 ++-- cloudinit/stages.py | 6 ++--- cloudinit/url_helper.py | 14 +++++------ cloudinit/user_data.py | 28 +++++++++------------ cloudinit/util.py | 37 ++++++++++++++------------- 38 files changed, 159 insertions(+), 161 deletions(-) (limited to 'cloudinit/distros') diff --git a/bin/cloud-init b/bin/cloud-init index 032d5f39..c1788ef4 100755 --- a/bin/cloud-init +++ b/bin/cloud-init @@ -75,6 +75,7 @@ def welcome(action): sys.stderr.flush() LOG.info(welcome_msg) + def extract_fns(args): # Files are already opened so lets just pass that along # since it would of broke if it couldn't have @@ -329,11 +330,11 @@ def main_single(name, args): def main(): parser = argparse.ArgumentParser() - + # Top level args - parser.add_argument('--version', '-v', action='version', + parser.add_argument('--version', '-v', action='version', version='%(prog)s ' + (version.version_string())) - parser.add_argument('--file', '-f', action='append', + parser.add_argument('--file', '-f', action='append', dest='files', help=('additional yaml configuration' ' files to use'), @@ -345,18 +346,18 @@ def main(): subparsers = parser.add_subparsers() # Each action and its sub-options (if any) - parser_init = subparsers.add_parser('init', + parser_init = subparsers.add_parser('init', help=('initializes cloud-init and' ' performs initial modules')) parser_init.add_argument("--local", '-l', action='store_true', help="start in local mode (default: %(default)s)", default=False) - # This is used so that we can know which action is selected + + # This is used so that we can know which action is selected + # the functor to use to run this subcommand parser_init.set_defaults(action=('init', main_init)) # These settings are used for the 'config' and 'final' stages - parser_mod = subparsers.add_parser('modules', + parser_mod = subparsers.add_parser('modules', help=('activates modules ' 'using a given configuration key')) parser_mod.add_argument("--mode", '-m', action='store', @@ -368,7 +369,7 @@ def main(): # These settings are used when you want to query information # stored in the cloud-init data objects/directories/files - parser_query = subparsers.add_parser('query', + parser_query = subparsers.add_parser('query', help=('query information stored ' 'in cloud-init')) parser_query.add_argument("--name", '-n', action="store", @@ -378,7 +379,7 @@ def main(): parser_query.set_defaults(action=('query', main_query)) # This subcommand allows you to run a single module - parser_single = subparsers.add_parser('single', + parser_single = subparsers.add_parser('single', help=('run a single module ')) parser_single.set_defaults(action=('single', main_single)) parser_single.add_argument("--name", '-n', action="store", @@ -394,10 +395,10 @@ def main(): ' pass to this module')) parser_single.set_defaults(action=('single', main_single)) - args = parser.parse_args() # Setup basic logging to start (until reinitialized) + # iff in debug mode... if args.debug: logging.setupBasicLogging() @@ -407,4 +408,3 @@ def main(): if __name__ == '__main__': sys.exit(main()) - diff --git a/cloudinit/cloud.py b/cloudinit/cloud.py index 90679202..6cdcb76a 100644 --- a/cloudinit/cloud.py +++ b/cloudinit/cloud.py @@ -38,6 +38,7 @@ LOG = logging.getLogger(__name__) # as providing a backwards compatible object that can be maintained # while the stages/other objects can be worked on independently... + class Cloud(object): def __init__(self, datasource, paths, cfg, distro, runners): self.datasource = datasource @@ -71,7 +72,7 @@ class Cloud(object): # The rest of thes are just useful proxies def get_userdata(self): return self.datasource.get_userdata() - + def get_instance_id(self): return self.datasource.get_instance_id() diff --git a/cloudinit/config/__init__.py b/cloudinit/config/__init__.py index 74e2f275..02e32462 100644 --- a/cloudinit/config/__init__.py +++ b/cloudinit/config/__init__.py @@ -25,7 +25,7 @@ from cloudinit import log as logging LOG = logging.getLogger(__name__) -# This prefix is used to make it less +# This prefix is used to make it less # of a change that when importing # we will not find something else with the same # name in the lookup path... diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py index 4e8ef346..74af2a7e 100644 --- a/cloudinit/config/cc_chef.py +++ b/cloudinit/config/cc_chef.py @@ -24,7 +24,7 @@ import os from cloudinit import templater from cloudinit import util -ruby_version_default = "1.8" +RUBY_VERSION_DEFAULT = "1.8" def handle(name, cfg, cloud, log, _args): @@ -38,11 +38,11 @@ def handle(name, cfg, cloud, log, _args): # Ensure the chef directories we use exist c_dirs = [ - '/etc/chef', - '/var/log/chef', - '/var/lib/chef', - '/var/cache/chef', - '/var/backups/chef', + '/etc/chef', + '/var/log/chef', + '/var/lib/chef', + '/var/cache/chef', + '/var/backups/chef', '/var/run/chef', ] for d in c_dirs: @@ -92,7 +92,7 @@ def handle(name, cfg, cloud, log, _args): # this will install and run the chef-client from gems chef_version = util.get_cfg_option_str(chef_cfg, 'version', None) ruby_version = util.get_cfg_option_str(chef_cfg, 'ruby_version', - ruby_version_default) + RUBY_VERSION_DEFAULT) install_chef_from_gems(cloud.distro, ruby_version, chef_version) # and finally, run chef-client log.debug('Running chef-client') diff --git a/cloudinit/config/cc_disable_ec2_metadata.py b/cloudinit/config/cc_disable_ec2_metadata.py index c7d26029..62cca7cc 100644 --- a/cloudinit/config/cc_disable_ec2_metadata.py +++ b/cloudinit/config/cc_disable_ec2_metadata.py @@ -24,13 +24,13 @@ from cloudinit.settings import PER_ALWAYS frequency = PER_ALWAYS -reject_cmd = ['route', 'add', '-host', '169.254.169.254', 'reject'] +REJECT_CMD = ['route', 'add', '-host', '169.254.169.254', 'reject'] def handle(name, cfg, _cloud, log, _args): disabled = util.get_cfg_option_bool(cfg, "disable_ec2_metadata", False) if disabled: - util.subp(reject_cmd) + util.subp(REJECT_CMD) else: log.debug(("Skipping transform named %s," " disabling the ec2 route not enabled"), name) diff --git a/cloudinit/config/cc_final_message.py b/cloudinit/config/cc_final_message.py index c257b6d0..fd59aa1e 100644 --- a/cloudinit/config/cc_final_message.py +++ b/cloudinit/config/cc_final_message.py @@ -28,7 +28,7 @@ from cloudinit.settings import PER_ALWAYS frequency = PER_ALWAYS -final_message_def = ("Cloud-init v. {{version}} finished at {{timestamp}}." +FINAL_MESSAGE_DEF = ("Cloud-init v. {{version}} finished at {{timestamp}}." " Up {{uptime}} seconds.") @@ -39,21 +39,21 @@ def handle(_name, cfg, cloud, log, args): msg_in = args[0] else: msg_in = util.get_cfg_option_str(cfg, "final_message") - + if not msg_in: template_fn = cloud.get_template_filename('final_message') if template_fn: msg_in = util.load_file(template_fn) if not msg_in: - msg_in = final_message_def + msg_in = FINAL_MESSAGE_DEF uptime = util.uptime() ts = util.time_rfc2822() cver = version.version_string() try: subs = { - 'uptime': uptime, + 'uptime': uptime, 'timestamp': ts, 'version': cver, } diff --git a/cloudinit/config/cc_foo.py b/cloudinit/config/cc_foo.py index 99135704..e81e7faa 100644 --- a/cloudinit/config/cc_foo.py +++ b/cloudinit/config/cc_foo.py @@ -30,19 +30,19 @@ from cloudinit.settings import PER_INSTANCE # as well as any datasource provided configuration # c) A cloud object that can be used to access various # datasource and paths for the given distro and data provided -# by the various datasource instance types. +# by the various datasource instance types. # d) A argument list that may or may not be empty to this module. # Typically those are from module configuration where the module # is defined with some extra configuration that will eventually # be translated from yaml into arguments to this module. # 2. A optional 'frequency' that defines how often this module should be ran. -# Typically one of PER_INSTANCE, PER_ALWAYS, PER_ONCE. If not -# provided PER_INSTANCE will be assumed. +# Typically one of PER_INSTANCE, PER_ALWAYS, PER_ONCE. If not +# provided PER_INSTANCE will be assumed. # See settings.py for these constants. # 3. A optional 'distros' array/set/tuple that defines the known distros # this module will work with (if not all of them). This is used to write # a warning out if a module is being ran on a untested distribution for -# informational purposes. If non existent all distros are assumed and +# informational purposes. If non existent all distros are assumed and # no warning occurs. frequency = PER_INSTANCE diff --git a/cloudinit/config/cc_keys_to_console.py b/cloudinit/config/cc_keys_to_console.py index 40758198..a8fb3ba7 100644 --- a/cloudinit/config/cc_keys_to_console.py +++ b/cloudinit/config/cc_keys_to_console.py @@ -26,13 +26,13 @@ from cloudinit import util frequency = PER_INSTANCE # This is a tool that cloud init provides -helper_tool = '/usr/lib/cloud-init/write-ssh-key-fingerprints' +HELPER_TOOL = '/usr/lib/cloud-init/write-ssh-key-fingerprints' def handle(name, cfg, cloud, log, _args): - if not os.path.exists(helper_tool): + if not os.path.exists(HELPER_TOOL): log.warn(("Unable to activate transform %s," - " helper tool not found at %s"), name, helper_tool) + " helper tool not found at %s"), name, HELPER_TOOL) return fp_blacklist = util.get_cfg_option_list(cfg, @@ -42,7 +42,7 @@ def handle(name, cfg, cloud, log, _args): ["ssh-dss"]) try: - cmd = [helper_tool] + cmd = [HELPER_TOOL] cmd.append(','.join(fp_blacklist)) cmd.append(','.join(key_blacklist)) (stdout, _stderr) = util.subp(cmd) diff --git a/cloudinit/config/cc_landscape.py b/cloudinit/config/cc_landscape.py index 29ce41b9..599276a7 100644 --- a/cloudinit/config/cc_landscape.py +++ b/cloudinit/config/cc_landscape.py @@ -62,7 +62,7 @@ def handle(name, cfg, cloud, log, _args): ls_cloudcfg = cfg.get("landscape", {}) if not isinstance(ls_cloudcfg, dict): - raise Exception(("'landscape' key existed in config," + raise Exception(("'landscape' key existed in config," " but not a dictionary type," " is a %s instead"), util.obj_name(ls_cloudcfg)) diff --git a/cloudinit/config/cc_mcollective.py b/cloudinit/config/cc_mcollective.py index 4cec6494..ba5e13ca 100644 --- a/cloudinit/config/cc_mcollective.py +++ b/cloudinit/config/cc_mcollective.py @@ -52,7 +52,7 @@ def handle(name, cfg, cloud, log, _args): # It doesn't contain any sections so just add one temporarily # Use a hash id based off the contents, # just incase of conflicts... (try to not have any...) - # This is so that an error won't occur when reading (and no + # This is so that an error won't occur when reading (and no # sections exist in the file) section_tpl = "[nullsection_%s]" attempts = 0 @@ -85,7 +85,7 @@ def handle(name, cfg, cloud, log, _args): # the previous server.cfg and create our new one old_fn = "%s.old" % (server_cfg_fn) util.rename(server_cfg_fn, old_fn) - # Now we got the whole file, write to disk except the section + # Now we got the whole file, write to disk except the section # we added so that config parser won't error out when trying to read. # Note below, that we've just used ConfigParser because it generally # works. Below, we remove the initial 'nullsection' header. diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py index 700fbc44..ab097c2a 100644 --- a/cloudinit/config/cc_mounts.py +++ b/cloudinit/config/cc_mounts.py @@ -24,10 +24,10 @@ import re from cloudinit import util -# shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1 -shortname_filter = r"^[x]{0,1}[shv]d[a-z][0-9]*$" -shortname = re.compile(shortname_filter) -ws = re.compile("[%s]+" % (whitespace)) +# Shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1 +SHORTNAME_FILTER = r"^[x]{0,1}[shv]d[a-z][0-9]*$" +SHORTNAME = re.compile(SHORTNAME_FILTER) +WS = re.compile("[%s]+" % (whitespace)) def is_mdname(name): @@ -55,7 +55,6 @@ def handle(_name, cfg, cloud, log, _args): if "mounts" in cfg: cfgmnt = cfg["mounts"] - for i in range(len(cfgmnt)): # skip something that wasn't a list if not isinstance(cfgmnt[i], list): @@ -85,7 +84,7 @@ def handle(_name, cfg, cloud, log, _args): cfgmnt[i][0] = renamed log.debug("Mapped metadata name %s to %s", startname, renamed) else: - if shortname.match(startname): + if SHORTNAME.match(startname): renamed = "/dev/%s" % startname log.debug("Mapped shortname name %s to %s", startname, renamed) cfgmnt[i][0] = renamed @@ -171,7 +170,7 @@ def handle(_name, cfg, cloud, log, _args): fstab = util.load_file(cloud.paths.join(True, "/etc/fstab")) for line in fstab.splitlines(): try: - toks = ws.split(line) + toks = WS.split(line) if toks[3].find(comment) != -1: continue except: diff --git a/cloudinit/config/cc_phone_home.py b/cloudinit/config/cc_phone_home.py index a8752527..dcb07b66 100644 --- a/cloudinit/config/cc_phone_home.py +++ b/cloudinit/config/cc_phone_home.py @@ -26,8 +26,13 @@ from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE -post_list_all = ['pub_key_dsa', 'pub_key_rsa', 'pub_key_ecdsa', - 'instance_id', 'hostname'] +POST_LIST_ALL = [ + 'pub_key_dsa', + 'pub_key_rsa', + 'pub_key_ecdsa', + 'instance_id', + 'hostname' +] # phone_home: @@ -63,7 +68,7 @@ def handle(name, cfg, cloud, log, args): " is not an integer, using %s instead"), tries) if post_list == "all": - post_list = post_list_all + post_list = POST_LIST_ALL all_keys = {} all_keys['instance_id'] = cloud.get_instance_id() diff --git a/cloudinit/config/cc_puppet.py b/cloudinit/config/cc_puppet.py index 5fb88bf2..5154efba 100644 --- a/cloudinit/config/cc_puppet.py +++ b/cloudinit/config/cc_puppet.py @@ -63,7 +63,7 @@ def handle(name, cfg, cloud, log, _args): util.ensure_dir(pp_ssl_dir, 0771) util.chownbyid(pp_ssl_dir, pwd.getpwnam('puppet').pw_uid, 0) - pp_ssl_certs = cloud.paths.join(False, + pp_ssl_certs = cloud.paths.join(False, '/var/lib/puppet/ssl/certs/') util.ensure_dir(pp_ssl_certs) util.chownbyid(pp_ssl_certs, diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py index 1690094a..c019989e 100644 --- a/cloudinit/config/cc_resizefs.py +++ b/cloudinit/config/cc_resizefs.py @@ -27,7 +27,7 @@ from cloudinit.settings import PER_ALWAYS frequency = PER_ALWAYS -resize_fs_prefixes_cmds = [ +RESIZE_FS_PREFIXES_CMDS = [ ('ext', 'resize2fs'), ('xfs', 'xfs_growfs'), ] @@ -89,16 +89,16 @@ def handle(name, cfg, cloud, log, args): # occurs this temporary file will still benefit from # auto deletion tfh.unlink_now() - + st_dev = nodeify_path(devpth, resize_what, log) fs_type = get_fs_type(st_dev, devpth, log) if not fs_type: log.warn("Could not determine filesystem type of %s", resize_what) return - + resizer = None fstype_lc = fs_type.lower() - for (pfix, root_cmd) in resize_fs_prefixes_cmds: + for (pfix, root_cmd) in RESIZE_FS_PREFIXES_CMDS: if fstype_lc.startswith(pfix): resizer = root_cmd break @@ -112,7 +112,7 @@ def handle(name, cfg, cloud, log, args): resize_cmd = [resizer, devpth] if resize_root == "noblock": - # Fork to a child that will run + # Fork to a child that will run # the resize command util.fork_cb(do_resize, resize_cmd, log) # Don't delete the file now in the parent diff --git a/cloudinit/config/cc_salt_minion.py b/cloudinit/config/cc_salt_minion.py index 16f5286d..986e6db6 100644 --- a/cloudinit/config/cc_salt_minion.py +++ b/cloudinit/config/cc_salt_minion.py @@ -32,7 +32,7 @@ def handle(name, cfg, cloud, log, _args): # Start by installing the salt package ... cloud.distro.install_packages(["salt"]) - + # Ensure we can configure files at the right dir config_dir = salt_cfg.get("config_dir", '/etc/salt') config_dir = cloud.paths.join(False, config_dir) diff --git a/cloudinit/config/cc_scripts_per_boot.py b/cloudinit/config/cc_scripts_per_boot.py index 364e1d02..d3c47442 100644 --- a/cloudinit/config/cc_scripts_per_boot.py +++ b/cloudinit/config/cc_scripts_per_boot.py @@ -26,16 +26,16 @@ from cloudinit.settings import PER_ALWAYS frequency = PER_ALWAYS -script_subdir = 'per-boot' +SCRIPT_SUBDIR = 'per-boot' def handle(name, _cfg, cloud, log, _args): # Comes from the following: # https://forums.aws.amazon.com/thread.jspa?threadID=96918 - runparts_path = os.path.join(cloud.get_cpath(), 'scripts', script_subdir) + runparts_path = os.path.join(cloud.get_cpath(), 'scripts', SCRIPT_SUBDIR) try: util.runparts(runparts_path) except: log.warn("Failed to run transform %s (%s in %s)", - name, script_subdir, runparts_path) + name, SCRIPT_SUBDIR, runparts_path) raise diff --git a/cloudinit/config/cc_scripts_per_instance.py b/cloudinit/config/cc_scripts_per_instance.py index d75ab47d..8e428ac2 100644 --- a/cloudinit/config/cc_scripts_per_instance.py +++ b/cloudinit/config/cc_scripts_per_instance.py @@ -26,16 +26,16 @@ from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE -script_subdir = 'per-instance' +SCRIPT_SUBDIR = 'per-instance' def handle(name, _cfg, cloud, log, _args): # Comes from the following: # https://forums.aws.amazon.com/thread.jspa?threadID=96918 - runparts_path = os.path.join(cloud.get_cpath(), 'scripts', script_subdir) + runparts_path = os.path.join(cloud.get_cpath(), 'scripts', SCRIPT_SUBDIR) try: util.runparts(runparts_path) except: log.warn("Failed to run transform %s (%s in %s)", - name, script_subdir, runparts_path) + name, SCRIPT_SUBDIR, runparts_path) raise diff --git a/cloudinit/config/cc_scripts_per_once.py b/cloudinit/config/cc_scripts_per_once.py index 80f8c325..e7a29a44 100644 --- a/cloudinit/config/cc_scripts_per_once.py +++ b/cloudinit/config/cc_scripts_per_once.py @@ -26,16 +26,16 @@ from cloudinit.settings import PER_ONCE frequency = PER_ONCE -script_subdir = 'per-once' +SCRIPT_SUBDIR = 'per-once' def handle(name, _cfg, cloud, log, _args): # Comes from the following: # https://forums.aws.amazon.com/thread.jspa?threadID=96918 - runparts_path = os.path.join(cloud.get_cpath(), 'scripts', script_subdir) + runparts_path = os.path.join(cloud.get_cpath(), 'scripts', SCRIPT_SUBDIR) try: util.runparts(runparts_path) except: log.warn("Failed to run transform %s (%s in %s)", - name, script_subdir, runparts_path) + name, SCRIPT_SUBDIR, runparts_path) raise diff --git a/cloudinit/config/cc_scripts_user.py b/cloudinit/config/cc_scripts_user.py index f4fe3a2a..1ff05aae 100644 --- a/cloudinit/config/cc_scripts_user.py +++ b/cloudinit/config/cc_scripts_user.py @@ -26,17 +26,17 @@ from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE -script_subdir = 'scripts' +SCRIPT_SUBDIR = 'scripts' def handle(name, _cfg, cloud, log, _args): # This is written to by the user data handlers # Ie, any custom shell scripts that come down # go here... - runparts_path = os.path.join(cloud.get_ipath_cur(), script_subdir) + runparts_path = os.path.join(cloud.get_ipath_cur(), SCRIPT_SUBDIR) try: util.runparts(runparts_path) except: log.warn("Failed to run transform %s (%s in %s)", - name, script_subdir, runparts_path) + name, SCRIPT_SUBDIR, runparts_path) raise diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py index e7049f22..ce17f357 100644 --- a/cloudinit/config/cc_set_passwords.py +++ b/cloudinit/config/cc_set_passwords.py @@ -25,7 +25,7 @@ from cloudinit import util from string import letters, digits # pylint: disable=W0402 # We are removing certain 'painful' letters/numbers -pw_set = (letters.translate(None, 'loLOI') + +PW_SET = (letters.translate(None, 'loLOI') + digits.translate(None, '01')) @@ -148,4 +148,4 @@ def handle(_name, cfg, cloud, log, args): def rand_user_password(pwlen=9): - return util.rand_str(pwlen, select_from=pw_set) + return util.rand_str(pwlen, select_from=PW_SET) diff --git a/cloudinit/config/cc_ssh.py b/cloudinit/config/cc_ssh.py index e5e99560..4019ae90 100644 --- a/cloudinit/config/cc_ssh.py +++ b/cloudinit/config/cc_ssh.py @@ -24,11 +24,11 @@ import glob from cloudinit import util from cloudinit import ssh_util -DISABLE_ROOT_OPTS = ( "no-port-forwarding,no-agent-forwarding," -"no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\" " +DISABLE_ROOT_OPTS = ("no-port-forwarding,no-agent-forwarding," +"no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\" " "rather than the user \\\"root\\\".\';echo;sleep 10\"") -key2file = { +KEY_2_FILE = { "rsa_private": ("/etc/ssh/ssh_host_rsa_key", 0600), "rsa_public": ("/etc/ssh/ssh_host_rsa_key.pub", 0644), "dsa_private": ("/etc/ssh/ssh_host_dsa_key", 0600), @@ -37,15 +37,17 @@ key2file = { "ecdsa_public": ("/etc/ssh/ssh_host_ecdsa_key.pub", 0644), } -priv2pub = { - 'rsa_private': 'rsa_public', +PRIV_2_PUB = { + 'rsa_private': 'rsa_public', 'dsa_private': 'dsa_public', 'ecdsa_private': 'ecdsa_public', } -key_gen_tpl = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"' +KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"' -generate_keys = ['rsa', 'dsa', 'ecdsa'] +GENERATE_KEY_NAMES = ['rsa', 'dsa', 'ecdsa'] + +KEY_FILE_TPL = '/etc/ssh/ssh_host_%s_key' def handle(_name, cfg, cloud, log, _args): @@ -58,21 +60,21 @@ def handle(_name, cfg, cloud, log, _args): util.del_file(f) except: util.logexc(log, "Failed deleting key file %s", f) - + if "ssh_keys" in cfg: # if there are keys in cloud-config, use them for (key, val) in cfg["ssh_keys"].iteritems(): - if key in key2file: - tgt_fn = key2file[key][0] - tgt_perms = key2file[key][1] + if key in KEY_2_FILE: + tgt_fn = KEY_2_FILE[key][0] + tgt_perms = KEY_2_FILE[key][1] util.write_file(cloud.paths.join(False, tgt_fn), val, tgt_perms) - for (priv, pub) in priv2pub.iteritems(): + for (priv, pub) in PRIV_2_PUB.iteritems(): if pub in cfg['ssh_keys'] or not priv in cfg['ssh_keys']: continue - pair = (key2file[priv][0], key2file[pub][0]) - cmd = ['sh', '-xc', key_gen_tpl % pair] + pair = (KEY_2_FILE[priv][0], KEY_2_FILE[pub][0]) + cmd = ['sh', '-xc', KEY_GEN_TPL % pair] try: # TODO: Is this guard needed? with util.SeLinuxGuard("/etc/ssh", recursive=True): @@ -84,12 +86,11 @@ def handle(_name, cfg, cloud, log, _args): else: # if not, generate them genkeys = util.get_cfg_option_list(cfg, - 'ssh_genkeytypes', - generate_keys) + 'ssh_genkeytypes', + GENERATE_KEY_NAMES) for keytype in genkeys: - keyfile = '/etc/ssh/ssh_host_%s_key' % (keytype) - keyfile = cloud.paths.join(False, keyfile) - util.ensure_dir(os.path.dirname(keyfile)) + keyfile = cloud.paths.join(False, KEY_FILE_TPL % (keytype)) + util.ensure_dir(os.path.dirname(keyfile)) if not os.path.exists(keyfile): cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile] try: diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 45dd85ec..25a60c52 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -157,4 +157,3 @@ def fetch(distro_name, mods=(__name__, )): % (distro_name)) distro_cls = getattr(mod, 'Distro') return distro_cls - diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index b67ae5b8..5cbefa6e 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -35,7 +35,7 @@ class Distro(distros.Distro): def __init__(self, name, cfg, paths): distros.Distro.__init__(self, name, cfg, paths) - + def install_packages(self, pkglist): self.package_command('install', pkglist) @@ -210,12 +210,12 @@ class Distro(distros.Distro): def package_command(self, command, args=None): cmd = ['yum'] # If enabled, then yum will be tolerant of errors on the command line - # with regard to packages. - # For example: if you request to install foo, bar and baz and baz is + # with regard to packages. + # For example: if you request to install foo, bar and baz and baz is # installed; yum won't error out complaining that baz is already - # installed. + # installed. cmd.append("-t") - # Determines whether or not yum prompts for confirmation + # Determines whether or not yum prompts for confirmation # of critical actions. We don't want to prompt... cmd.append("-y") cmd.append(command) @@ -223,8 +223,8 @@ class Distro(distros.Distro): cmd.extend(args) # Allow the output of this to flow outwards (ie not be captured) util.subp(cmd, capture=False) - - + + # This is a util function to translate a ubuntu /etc/network/interfaces 'blob' # to a rhel equiv. that can then be written to /etc/sysconfig/network-scripts/ # TODO remove when we have python-netcf active... diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 5a1b572e..fd7b7b8d 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -36,11 +36,11 @@ class Distro(distros.Distro): def __init__(self, name, cfg, paths): distros.Distro.__init__(self, name, cfg, paths) - # This will be used to restrict certain + # This will be used to restrict certain # calls from repeatly happening (when they # should only happen say once per instance...) self._runner = helpers.Runners(paths) - + def install_packages(self, pkglist): self._update_package_sources() self.package_command('install', pkglist) @@ -131,4 +131,4 @@ class Distro(distros.Distro): def _update_package_sources(self): self._runner.run("update-sources", self.package_command, - ["update"], freq=PER_INSTANCE) \ No newline at end of file + ["update"], freq=PER_INSTANCE) diff --git a/cloudinit/handlers/__init__.py b/cloudinit/handlers/__init__.py index c6f2119c..d52b1cba 100644 --- a/cloudinit/handlers/__init__.py +++ b/cloudinit/handlers/__init__.py @@ -104,7 +104,7 @@ def run_part(mod, data, ctype, filename, payload, frequency): except: mod_ver = 1 try: - LOG.debug("Calling handler %s (%s, %s, %s) with frequency %s", + LOG.debug("Calling handler %s (%s, %s, %s) with frequency %s", mod, ctype, filename, mod_ver, frequency) if mod_ver >= 2: # Treat as v. 2 which does get a frequency @@ -114,7 +114,7 @@ def run_part(mod, data, ctype, filename, payload, frequency): mod.handle_part(data, ctype, filename, payload) except: util.logexc(LOG, ("Failed calling handler %s (%s, %s, %s)" - " with frequency %s"), + " with frequency %s"), mod, ctype, filename, mod_ver, frequency) @@ -178,7 +178,7 @@ def walker_callback(pdata, ctype, filename, payload): payload, pdata['frequency']) -# Callback is a function that will be called with +# Callback is a function that will be called with # (data, content_type, filename, payload) def walk(msg, callback, data): partnum = 0 @@ -226,5 +226,3 @@ def type_from_starts_with(payload, default=None): if payload_lc.startswith(text): return INCLUSION_TYPES_MAP[text] return default - - diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py index 45633e0f..4447d1ee 100644 --- a/cloudinit/helpers.py +++ b/cloudinit/helpers.py @@ -84,7 +84,7 @@ class FileSemaphores(object): try: util.del_dir(self.sem_path) except (IOError, OSError): - util.logexc(LOG, "Failed deleting semaphore directory %s", + util.logexc(LOG, "Failed deleting semaphore directory %s", self.sem_path) def _acquire(self, name, freq): @@ -212,7 +212,7 @@ class Paths(object): self.cfgs = path_cfgs # Populate all the initial paths self.cloud_dir = self.join(False, - path_cfgs.get('cloud_dir', + path_cfgs.get('cloud_dir', '/var/lib/cloud')) self.instance_link = os.path.join(self.cloud_dir, 'instance') self.boot_finished = os.path.join(self.instance_link, "boot-finished") @@ -237,7 +237,7 @@ class Paths(object): # Set when a datasource becomes active self.datasource = ds - # joins the paths but also appends a read + # joins the paths but also appends a read # or write root if available def join(self, read_only, *paths): if read_only: diff --git a/cloudinit/log.py b/cloudinit/log.py index 478946f8..fc1428a2 100644 --- a/cloudinit/log.py +++ b/cloudinit/log.py @@ -20,7 +20,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . - import logging import logging.handlers import logging.config @@ -53,7 +52,6 @@ def setupBasicLogging(): root.setLevel(DEBUG) - def setupLogging(cfg=None): # See if the config provides any logging conf... if not cfg: diff --git a/cloudinit/settings.py b/cloudinit/settings.py index 8a1eaeb3..fac9b862 100644 --- a/cloudinit/settings.py +++ b/cloudinit/settings.py @@ -47,7 +47,7 @@ CFG_BUILTIN = { 'paths': { 'cloud_dir': '/var/lib/cloud', 'templates_dir': '/etc/cloud/templates/', - }, + }, 'distro': 'ubuntu', }, } diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py index b1817654..83c577e6 100644 --- a/cloudinit/sources/DataSourceCloudStack.py +++ b/cloudinit/sources/DataSourceCloudStack.py @@ -121,7 +121,7 @@ class DataSourceCloudStack(sources.DataSource): None, self.metadata_address) self.metadata = boto_utils.get_instance_metadata(self.api_ver, self.metadata_address) - LOG.debug("Crawl of metadata service took %s seconds", + LOG.debug("Crawl of metadata service took %s seconds", int(time.time() - start_time)) return True except Exception: diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index 5da1ffea..9905dad4 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -36,7 +36,7 @@ CFG_DRIVE_FILES = [ "meta.js", ] DEFAULT_METADATA = { - "instance-id": DEFAULT_IID, + "instance-id": DEFAULT_IID, "dsmode": DEFAULT_MODE, } CFG_DRIVE_DEV_ENV = 'CLOUD_INIT_CONFIG_DRIVE_DEVICE' diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py index 55447102..0598dfa2 100644 --- a/cloudinit/sources/DataSourceEc2.py +++ b/cloudinit/sources/DataSourceEc2.py @@ -38,7 +38,7 @@ DEF_MD_URL = "http://169.254.169.254" DEF_MD_VERSION = '2009-04-04' # Default metadata urls that will be used if none are provided -# They will be checked for 'resolveability' and some of the +# They will be checked for 'resolveability' and some of the # following may be discarded if they do not resolve DEF_MD_URLS = [DEF_MD_URL, "http://instance-data:8773"] @@ -69,7 +69,7 @@ class DataSourceEc2(sources.DataSource): None, self.metadata_address) self.metadata = boto_utils.get_instance_metadata(self.api_ver, self.metadata_address) - LOG.debug("Crawl of metadata service took %s seconds", + LOG.debug("Crawl of metadata service took %s seconds", int(time.time() - start_time)) return True except Exception: @@ -201,7 +201,7 @@ class DataSourceEc2(sources.DataSource): return None # Example: - # 'block-device-mapping': + # 'block-device-mapping': # {'ami': '/dev/sda1', # 'ephemeral0': '/dev/sdb', # 'root': '/dev/sda1'} diff --git a/cloudinit/sources/DataSourceMAAS.py b/cloudinit/sources/DataSourceMAAS.py index bb8fbac1..104e7a54 100644 --- a/cloudinit/sources/DataSourceMAAS.py +++ b/cloudinit/sources/DataSourceMAAS.py @@ -251,6 +251,7 @@ datasources = [ (DataSourceMAAS, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] + # Return a list of data sources that match this set of dependencies def get_datasource_list(depends): return sources.list_from_depends(depends, datasources) diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py index 2b016d1c..8499a97c 100644 --- a/cloudinit/sources/DataSourceNoCloud.py +++ b/cloudinit/sources/DataSourceNoCloud.py @@ -154,7 +154,7 @@ class DataSourceNoCloud(sources.DataSource): (self.dsmode in ("local", seeded_interfaces))): LOG.info("Updating network interfaces from %s", self) self.distro.apply_network(md['network-interfaces']) - + if md['dsmode'] == self.dsmode: self.seed = ",".join(found) self.metadata = md diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py index fef3d40f..45dd5535 100644 --- a/cloudinit/ssh_util.py +++ b/cloudinit/ssh_util.py @@ -103,10 +103,10 @@ class AuthKeyLineParser(object): elif curc == '"': quoted = not quoted i = i + 1 - + options = ent[0:i] options_lst = [] - + # Now use a csv parser to pull the options # out of the above string that we just found an endpoint for. # @@ -211,7 +211,6 @@ def update_authorized_keys(fname, keys): def setup_user_keys(keys, user, key_prefix, paths): - # Make sure the users .ssh dir is setup accordingly pwent = pwd.getpwnam(user) ssh_dir = os.path.join(pwent.pw_dir, '.ssh') diff --git a/cloudinit/stages.py b/cloudinit/stages.py index ae6e2de5..84a965c2 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -287,7 +287,7 @@ class Init(object): def cloudify(self): # Form the needed options to cloudify our members - return cloud.Cloud(self.datasource, + return cloud.Cloud(self.datasource, self.paths, self.cfg, self.distro, helpers.Runners(self.paths)) @@ -318,7 +318,7 @@ class Init(object): def consume(self, frequency=PER_INSTANCE): cdir = self.paths.get_cpath("handlers") idir = self.paths.get_ipath("handlers") - + # Add the path to the plugins dir to the top of our list for import # instance dir should be read before cloud-dir if cdir and cdir not in sys.path: @@ -417,7 +417,7 @@ class Modules(object): except: util.logexc(LOG, ("Failed loading of datasource" " config object from %s"), self.datasource) - + if self.base_cfg: t_cfgs.append(self.base_cfg) diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py index 1c583eba..223278ce 100644 --- a/cloudinit/url_helper.py +++ b/cloudinit/url_helper.py @@ -47,11 +47,11 @@ class UrlResponse(object): @property def contents(self): return self._contents - + @property def headers(self): return self._headers - + def __str__(self): if not self.contents: return '' @@ -66,7 +66,7 @@ class UrlResponse(object): return True else: return False - + def readurl(url, data=None, timeout=None, retries=0, sec_between=1, headers=None): @@ -89,8 +89,8 @@ def readurl(url, data=None, timeout=None, excepts = [] LOG.info(("Attempting to open '%s' with %s attempts" - " (%s retries, timeout=%s) to be performed"), - url, attempts, retries, timeout) + " (%s retries, timeout=%s) to be performed"), + url, attempts, retries, timeout) open_args = {} if timeout is not None: open_args['timeout'] = int(timeout) @@ -112,7 +112,7 @@ def readurl(url, data=None, timeout=None, excepts.append(e) except urllib2.URLError as e: # This can be a message string or - # another exception instance + # another exception instance # (socket.error for remote URLs, OSError for local URLs). if (isinstance(e.reason, (OSError)) and e.reason.errno == errno.ENOENT): @@ -128,7 +128,7 @@ def readurl(url, data=None, timeout=None, # Didn't work out LOG.warn("Failed reading from %s after %s attempts", url, attempts) - + # It must of errored at least once for code # to get here so re-raise the last error LOG.debug("%s errors occured, re-raising the last one", len(excepts)) diff --git a/cloudinit/user_data.py b/cloudinit/user_data.py index b7902d44..4babb8e5 100644 --- a/cloudinit/user_data.py +++ b/cloudinit/user_data.py @@ -65,33 +65,33 @@ class UserDataProcessor(object): # multipart/* are just containers if part.get_content_maintype() == 'multipart': continue - + ctype = None ctype_orig = part.get_content_type() payload = part.get_payload(decode=True) - + if not ctype_orig: ctype_orig = UNDEF_TYPE - + if ctype_orig in TYPE_NEEDED: ctype = handlers.type_from_starts_with(payload) - + if ctype is None: ctype = ctype_orig - + if ctype in INCLUDE_TYPES: self._do_include(payload, append_msg) continue - + if ctype in ARCHIVE_TYPES: self._explode_archive(payload, append_msg) continue - + if 'Content-Type' in base_msg: base_msg.replace_header('Content-Type', ctype) else: base_msg['Content-Type'] = ctype - + self._attach_part(append_msg, part) def _get_include_once_filename(self, entry): @@ -108,8 +108,8 @@ class UserDataProcessor(object): lc_line = line.lower() if lc_line.startswith("#include-once"): line = line[len("#include-once"):].lstrip() - # Every following include will now - # not be refetched.... but will be + # Every following include will now + # not be refetched.... but will be # re-read from a local urlcache (if it worked) include_once_on = True elif lc_line.startswith("#include"): @@ -190,10 +190,10 @@ class UserDataProcessor(object): """ if ATTACHMENT_FIELD not in outer_msg: outer_msg[ATTACHMENT_FIELD] = '0' - + if new_count is not None: outer_msg.replace_header(ATTACHMENT_FIELD, str(new_count)) - + fetched_count = 0 try: fetched_count = int(outer_msg.get(ATTACHMENT_FIELD)) @@ -234,7 +234,3 @@ def convert_string(raw_data, headers=None): msg = MIMEBase(maintype, subtype, *headers) msg.set_payload(data) return msg - - - - diff --git a/cloudinit/util.py b/cloudinit/util.py index 91d20a76..56c01fab 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -35,7 +35,7 @@ import pwd import random import shutil import socket -import string # pylint: disable=W0402 +import string # pylint: disable=W0402 import subprocess import sys import tempfile @@ -153,13 +153,15 @@ def SilentTemporaryFile(**kwargs): # file to unlink has been unlinked elsewhere.. LOG.debug("Created temporary file %s", fh.name) fh.unlink = del_file - # Add a new method that will unlink + + # Add a new method that will unlink # right 'now' but still lets the exit # method attempt to remove it (which will # not throw due to our del file being quiet # about files that are not there) def unlink_now(): fh.unlink(fh.name) + setattr(fh, 'unlink_now', unlink_now) return fh @@ -199,7 +201,7 @@ def is_false_str(val, addons=None): def translate_bool(val, addons=None): if not val: - # This handles empty lists and false and + # This handles empty lists and false and # other things that python believes are false return False # If its already a boolean skip @@ -214,7 +216,6 @@ def rand_str(strlen=32, select_from=None): return "".join([random.choice(select_from) for _x in range(0, strlen)]) - def read_conf(fname): try: return load_yaml(load_file(fname), default={}) @@ -275,7 +276,7 @@ def is_ipv4(instr): def merge_base_cfg(cfgfile, cfg_builtin=None): syscfg = read_conf_with_confd(cfgfile) - + kern_contents = read_cc_from_cmdline() kerncfg = {} if kern_contents: @@ -575,7 +576,7 @@ def load_yaml(blob, default=None, allowed=(dict,)): try: blob = str(blob) LOG.debug(("Attempting to load yaml from string " - "of length %s with allowed root types %s"), + "of length %s with allowed root types %s"), len(blob), allowed) converted = yaml.load(blob) if not isinstance(converted, allowed): @@ -625,7 +626,7 @@ def read_conf_d(confd): # remove anything not ending in '.cfg' confs = [f for f in confs if f.endswith(".cfg")] - + # remove anything not a file confs = [f for f in confs if os.path.isfile(os.path.join(confd, f))] @@ -726,9 +727,9 @@ def get_fqdn_from_hosts(hostname, filename="/etc/hosts"): """ For each host a single line should be present with the following information: - - IP_address canonical_hostname [aliases...] - + + IP_address canonical_hostname [aliases...] + Fields of the entry are separated by any number of blanks and/or tab characters. Text from a "#" character until the end of the line is a comment, and is ignored. Host names may contain only alphanumeric @@ -747,7 +748,7 @@ def get_fqdn_from_hosts(hostname, filename="/etc/hosts"): if not line: continue - # If there there is less than 3 entries + # If there there is less than 3 entries # (IP_address, canonical_hostname, alias) # then ignore this line toks = line.split() @@ -829,7 +830,7 @@ def close_stdin(): os.dup2(fp.fileno(), sys.stdin.fileno()) -def find_devs_with(criteria=None, oformat='device', +def find_devs_with(criteria=None, oformat='device', tag=None, no_cache=False, path=None): """ find devices matching given criteria (via blkid) @@ -841,23 +842,23 @@ def find_devs_with(criteria=None, oformat='device', blk_id_cmd = ['blkid'] options = [] if criteria: - # Search for block devices with tokens named NAME that + # Search for block devices with tokens named NAME that # have the value 'value' and display any devices which are found. # Common values for NAME include TYPE, LABEL, and UUID. # If there are no devices specified on the command line, - # all block devices will be searched; otherwise, + # all block devices will be searched; otherwise, # only search the devices specified by the user. options.append("-t%s" % (criteria)) if tag: # For each (specified) device, show only the tags that match tag. options.append("-s%s" % (tag)) if no_cache: - # If you want to start with a clean cache - # (i.e. don't report devices previously scanned + # If you want to start with a clean cache + # (i.e. don't report devices previously scanned # but not necessarily available at this time), specify /dev/null. options.extend(["-c", "/dev/null"]) if oformat: - # Display blkid's output using the specified format. + # Display blkid's output using the specified format. # The format parameter may be: # full, value, list, device, udev, export options.append('-o%s' % (oformat)) @@ -1104,7 +1105,7 @@ def mounts(): (dev, mp, fstype, opts, _freq, _passno) = mpline.split() except: continue - # If the name of the mount point contains spaces these + # If the name of the mount point contains spaces these # can be escaped as '\040', so undo that.. mp = mp.replace("\\040", " ") mounted[dev] = { -- cgit v1.2.3 From 95e0fa29af3656c1011c41ab0f35dc4e9317269c Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 20 Jun 2012 23:40:00 -0700 Subject: 1. Add an importer function that will search for a given module in a set of search module 'prefixes' that also has a potential set of required attributes. 2. Use this new importer to find the distro class, the userdata handler modules, the config modules and the datasource modules, if none can be found error out accordingly. --- cloudinit/config/__init__.py | 4 ---- cloudinit/distros/__init__.py | 23 ++++++++++------------ cloudinit/handlers/__init__.py | 8 -------- cloudinit/importer.py | 44 +++++++++++++++++++++++++++++++++--------- cloudinit/sources/__init__.py | 34 +++++++++++--------------------- cloudinit/stages.py | 22 +++++++++++++++++---- 6 files changed, 74 insertions(+), 61 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/config/__init__.py b/cloudinit/config/__init__.py index 02e32462..ab13045f 100644 --- a/cloudinit/config/__init__.py +++ b/cloudinit/config/__init__.py @@ -51,10 +51,6 @@ def fixup_module(mod, def_freq=PER_INSTANCE): freq = mod.frequency if freq and freq not in FREQUENCIES: LOG.warn("Module %s has an unknown frequency %s", mod, freq) - if not hasattr(mod, 'handle'): - def empty_handle(_name, _cfg, _cloud, _log, _args): - pass - setattr(mod, 'handle', empty_handle) if not hasattr(mod, 'distros'): setattr(mod, 'distros', None) return mod diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 25a60c52..e0ef6ee0 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -144,16 +144,13 @@ class Distro(object): return False -def fetch(distro_name, mods=(__name__, )): - mod = None - for m in mods: - mod_name = "%s.%s" % (m, distro_name) - try: - mod = importer.import_module(mod_name) - except ImportError: - pass - if not mod: - raise RuntimeError("No distribution found for distro %s" - % (distro_name)) - distro_cls = getattr(mod, 'Distro') - return distro_cls +def fetch(name): + locs = importer.find_module(name, + ['', __name__], + ['Distro']) + if not locs: + raise ImportError("No distribution found for distro %s" + % (name)) + mod = importer.import_module(locs[0]) + cls = getattr(mod, 'Distro') + return cls diff --git a/cloudinit/handlers/__init__.py b/cloudinit/handlers/__init__.py index d52b1cba..0f7432e5 100644 --- a/cloudinit/handlers/__init__.py +++ b/cloudinit/handlers/__init__.py @@ -202,20 +202,12 @@ def walk(msg, callback, data): def fixup_handler(mod, def_freq=PER_INSTANCE): if not hasattr(mod, "handler_version"): setattr(mod, "handler_version", 1) - if not hasattr(mod, 'list_types'): - def empty_types(): - return [] - setattr(mod, 'list_types', empty_types) if not hasattr(mod, 'frequency'): setattr(mod, 'frequency', def_freq) else: freq = mod.frequency if freq and freq not in FREQUENCIES: LOG.warn("Handler %s has an unknown frequency %s", mod, freq) - if not hasattr(mod, 'handle_part'): - def empty_handler(_data, _ctype, _filename, _payload): - pass - setattr(mod, 'handle_part', empty_handler) return mod diff --git a/cloudinit/importer.py b/cloudinit/importer.py index a36b87bc..71cf2726 100644 --- a/cloudinit/importer.py +++ b/cloudinit/importer.py @@ -23,17 +23,43 @@ import sys from cloudinit import log as logging -from cloudinit import util LOG = logging.getLogger(__name__) -# Simple wrapper that allows us to add more logging in... def import_module(module_name): - try: - LOG.debug("Attempting to import module %s", module_name) - __import__(module_name) - return sys.modules[module_name] - except: - util.logexc(LOG, 'Failed at importing %s', module_name) - raise + __import__(module_name) + return sys.modules[module_name] + + +def find_module(base_name, search_paths, required_attrs=None): + found_places = [] + if not required_attrs: + required_attrs = [] + real_paths = [] + for path in search_paths: + real_path = [] + if path: + real_path.extend(path.split(".")) + real_path.append(base_name) + full_path = '.'.join(real_path) + real_paths.append(full_path) + LOG.debug("Looking for modules %s that have attributes %s", + real_paths, required_attrs) + for full_path in real_paths: + mod = None + try: + mod = import_module(full_path) + except ImportError: + pass + if not mod: + continue + found_attrs = 0 + for attr in required_attrs: + if hasattr(mod, attr): + found_attrs += 1 + if found_attrs == len(required_attrs): + found_places.append(full_path) + LOG.debug("Found %s with attributes %s in %s", base_name, + required_attrs, found_places) + return found_places diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index 8ab7cf54..42e924b0 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -191,31 +191,19 @@ def list_sources(cfg_list, depends, pkg_list): LOG.info(("Looking for for data source in: %s," " via packages %s that matches dependencies %s"), cfg_list, pkg_list, depends) - for ds_coll in cfg_list: - ds_name = str(ds_coll) + for ds_name in cfg_list: if not ds_name.startswith(DS_PREFIX): ds_name = '%s%s' % (DS_PREFIX, ds_name) - for pkg in pkg_list: - pkg_name = [] - if pkg: - # Any package name given, this affects - # the lookup path - pkg_name.append(str(pkg)) - pkg_name.append(ds_name) - try: - mod = importer.import_module(".".join(pkg_name)) - except ImportError: - continue - lister = getattr(mod, "get_datasource_list", None) - if not lister: - continue - cls_matches = lister(depends) - if not cls_matches: - continue - src_list.extend(cls_matches) - LOG.debug(("Found a match" - " in %s with matches %s"), mod, cls_matches) - break + m_locs = importer.find_module(ds_name, + pkg_list, + ['get_datasource_list']) + for m_loc in m_locs: + mod = importer.import_module(m_loc) + lister = getattr(mod, "get_datasource_list") + matches = lister(depends) + if matches: + src_list.extend(matches) + break return src_list diff --git a/cloudinit/stages.py b/cloudinit/stages.py index 84a965c2..1997301a 100644 --- a/cloudinit/stages.py +++ b/cloudinit/stages.py @@ -205,7 +205,7 @@ class Init(object): # Any config provided??? pkg_list = self.cfg.get('datasource_pkg_list') or [] # Add the defaults at the end - for n in [util.obj_name(sources), '']: + for n in ['', util.obj_name(sources)]: if n not in pkg_list: pkg_list.append(n) cfg_list = self.cfg.get('datasource_list') or [] @@ -334,9 +334,17 @@ class Init(object): # Add handlers in cdir potential_handlers = util.find_modules(cdir) - for (fname, modname) in potential_handlers.iteritems(): + for (fname, mod_name) in potential_handlers.iteritems(): try: - mod = handlers.fixup_handler(importer.import_module(modname)) + mod_locs = importer.find_module(mod_name, [''], + ['list_types', + 'handle_part']) + if not mod_locs: + LOG.warn(("Could not find a valid user-data handler" + " named %s in file %s"), mod_name, fname) + continue + mod = importer.import_module(mod_locs[0]) + mod = handlers.fixup_handler(mod) types = c_handlers.register(mod) LOG.debug("Added handler for %s from %s", types, fname) except: @@ -482,7 +490,13 @@ class Modules(object): " has an unknown frequency %s"), raw_name, freq) # Reset it so when ran it will get set to a known value freq = None - mod = config.fixup_module(importer.import_module(mod_name)) + mod_locs = importer.find_module(mod_name, + ['', util.obj_name(config)], + ['handle']) + if not mod_locs: + LOG.warn("Could not find module named %s", mod_name) + continue + mod = config.fixup_module(importer.import_module(mod_locs[0])) mostly_mods.append([mod, raw_name, freq, run_args]) return mostly_mods -- cgit v1.2.3 From f1cab0d88cbcfa7eaa698db7dcc252bb6543d6c0 Mon Sep 17 00:00:00 2001 From: harlowja Date: Thu, 21 Jun 2012 08:38:12 -0700 Subject: 1. Move all info() logging methods to debug() 2. Adjust comment on sources list from depends 3. For the /etc/timezone 'writing', add a header that says created by cloud-init --- bin/cloud-init | 3 +-- cloudinit/distros/__init__.py | 6 +++--- cloudinit/distros/ubuntu.py | 6 +++++- cloudinit/helpers.py | 2 +- cloudinit/sources/DataSourceCloudStack.py | 2 +- cloudinit/sources/DataSourceConfigDrive.py | 6 ++---- cloudinit/sources/DataSourceEc2.py | 2 +- cloudinit/sources/DataSourceMAAS.py | 2 +- cloudinit/sources/DataSourceNoCloud.py | 2 +- cloudinit/sources/__init__.py | 22 +++++++++++----------- cloudinit/url_helper.py | 14 +++++++------- 11 files changed, 34 insertions(+), 33 deletions(-) (limited to 'cloudinit/distros') diff --git a/bin/cloud-init b/bin/cloud-init index a2f15c4b..d193272e 100755 --- a/bin/cloud-init +++ b/bin/cloud-init @@ -73,8 +73,7 @@ def welcome(action): } welcome_msg = "%s" % (templater.render_string(msg, tpl_params)) sys.stderr.write("%s\n" % (welcome_msg)) - sys.stderr.flush() - LOG.info(welcome_msg) + LOG.debug(welcome_msg) def extract_fns(args): diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index e0ef6ee0..6325257c 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -133,14 +133,14 @@ class Distro(object): raise NotImplementedError("Unknown interface action %s" % (action)) cmd = IFACE_ACTIONS[action] try: - LOG.info("Attempting to run %s interface action using command %s", - action, cmd) + LOG.debug("Attempting to run %s interface action using command %s", + action, cmd) (_out, err) = util.subp(cmd) if len(err): LOG.warn("Running %s resulted in stderr output: %s", cmd, err) return True except util.ProcessExecutionError: - util.logexc(LOG, "Running %s failed", cmd) + util.logexc(LOG, "Running interface command %s failed", cmd) return False diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index fd7b7b8d..15af2e7f 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -112,7 +112,11 @@ class Distro(distros.Distro): if not os.path.isfile(tz_file): raise Exception(("Invalid timezone %s," " no file found at %s") % (tz, tz_file)) - tz_contents = "%s\n" % tz + tz_lines = [ + "# Created by cloud-init", + str(tz), + ] + tz_contents = "\n".join(tz_lines) tz_fn = self._paths.join(False, "/etc/timezone") util.write_file(tz_fn, tz_contents) util.copy(tz_file, self._paths.join(False, "/etc/localtime")) diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py index b6974f3c..6751f4a5 100644 --- a/cloudinit/helpers.py +++ b/cloudinit/helpers.py @@ -161,7 +161,7 @@ class Runners(object): if not args: args = [] if sem.has_run(name, freq): - LOG.info("%s already ran (freq=%s)", name, freq) + LOG.debug("%s already ran (freq=%s)", name, freq) return (False, None) with sem.lock(name, freq, clear_on_fail) as lk: if not lk: diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py index 83c577e6..751bef4f 100644 --- a/cloudinit/sources/DataSourceCloudStack.py +++ b/cloudinit/sources/DataSourceCloudStack.py @@ -98,7 +98,7 @@ class DataSourceCloudStack(sources.DataSource): timeout=timeout, status_cb=LOG.warn) if url: - LOG.info("Using metadata source: '%s'", url) + LOG.debug("Using metadata source: '%s'", url) else: LOG.critical(("Giving up on waiting for the metadata from %s" " after %s seconds"), diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py index 9905dad4..320dd1d1 100644 --- a/cloudinit/sources/DataSourceConfigDrive.py +++ b/cloudinit/sources/DataSourceConfigDrive.py @@ -87,10 +87,8 @@ class DataSourceConfigDrive(sources.DataSource): # Update interfaces and ifup only on the local datasource # this way the DataSourceConfigDriveNet doesn't do it also. if 'network-interfaces' in md and self.dsmode == "local": - if md['dsmode'] == "pass": - LOG.info("Updating network interfaces from configdrive") - else: - LOG.debug("Updating network interfaces from configdrive") + LOG.debug("Updating network interfaces from config drive (%s)", + md['dsmode']) self.distro.apply_network(md['network-interfaces']) self.seed = found diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py index 0598dfa2..cb460de1 100644 --- a/cloudinit/sources/DataSourceEc2.py +++ b/cloudinit/sources/DataSourceEc2.py @@ -169,7 +169,7 @@ class DataSourceEc2(sources.DataSource): timeout=timeout, status_cb=LOG.warn) if url: - LOG.info("Using metadata source: '%s'", url2base[url]) + LOG.debug("Using metadata source: '%s'", url2base[url]) else: LOG.critical("Giving up on md from %s after %s seconds", urls, int(time.time() - start_time)) diff --git a/cloudinit/sources/DataSourceMAAS.py b/cloudinit/sources/DataSourceMAAS.py index 104e7a54..22c90b7c 100644 --- a/cloudinit/sources/DataSourceMAAS.py +++ b/cloudinit/sources/DataSourceMAAS.py @@ -128,7 +128,7 @@ class DataSourceMAAS(sources.DataSource): headers_cb=self.md_headers) if url: - LOG.info("Using metadata source: '%s'", url) + LOG.debug("Using metadata source: '%s'", url) else: LOG.critical("Giving up on md from %s after %i seconds", urls, int(time.time() - starttime)) diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py index 8499a97c..bed500a2 100644 --- a/cloudinit/sources/DataSourceNoCloud.py +++ b/cloudinit/sources/DataSourceNoCloud.py @@ -152,7 +152,7 @@ class DataSourceNoCloud(sources.DataSource): # ('local' for NoCloud, 'net' for NoCloudNet') if ('network-interfaces' in md and (self.dsmode in ("local", seeded_interfaces))): - LOG.info("Updating network interfaces from %s", self) + LOG.debug("Updating network interfaces from %s", self) self.distro.apply_network(md['network-interfaces']) if md['dsmode'] == self.dsmode: diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index 42e924b0..b25724a5 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -166,7 +166,7 @@ class DataSource(object): 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] - LOG.info("Searching for data source in: %s", ds_names) + LOG.debug("Searching for data source in: %s", ds_names) for cls in ds_list: try: @@ -188,9 +188,9 @@ def find_source(sys_cfg, distro, paths, ds_deps, cfg_list, pkg_list): # Return an ordered list of classes that match (if any) def list_sources(cfg_list, depends, pkg_list): src_list = [] - LOG.info(("Looking for for data source in: %s," - " via packages %s that matches dependencies %s"), - cfg_list, pkg_list, depends) + LOG.debug(("Looking for for data source in: %s," + " via packages %s that matches dependencies %s"), + cfg_list, pkg_list, depends) for ds_name in cfg_list: if not ds_name.startswith(DS_PREFIX): ds_name = '%s%s' % (DS_PREFIX, ds_name) @@ -207,17 +207,17 @@ def list_sources(cfg_list, depends, pkg_list): return src_list -# depends is a list of dependencies (DEP_FILESYSTEM) -# dslist is a list of 2 item lists -# dslist = [ +# 'depends' is a list of dependencies (DEP_FILESYSTEM) +# ds_list is a list of 2 item lists +# ds_list = [ # ( class, ( depends-that-this-class-needs ) ) # } -# it returns a list of 'class' that matched these deps exactly -# it is a helper function for DataSourceCollections -def list_from_depends(depends, dslist): +# It returns a list of 'class' that matched these deps exactly +# It mainly is a helper function for DataSourceCollections +def list_from_depends(depends, ds_list): ret_list = [] depset = set(depends) - for (cls, deps) in dslist: + for (cls, deps) in ds_list: if depset == set(deps): ret_list.append(cls) return ret_list diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py index 223278ce..dbf72392 100644 --- a/cloudinit/url_helper.py +++ b/cloudinit/url_helper.py @@ -88,8 +88,8 @@ def readurl(url, data=None, timeout=None, attempts = retries + 1 excepts = [] - LOG.info(("Attempting to open '%s' with %s attempts" - " (%s retries, timeout=%s) to be performed"), + LOG.debug(("Attempting to open '%s' with %s attempts" + " (%s retries, timeout=%s) to be performed"), url, attempts, retries, timeout) open_args = {} if timeout is not None: @@ -105,8 +105,8 @@ def readurl(url, data=None, timeout=None, headers = {} if rh.headers: headers = dict(rh.headers) - LOG.info("Read from %s (%s, %sb) after %s attempts", - url, status, len(content), (i + 1)) + LOG.debug("Read from %s (%s, %sb) after %s attempts", + url, status, len(content), (i + 1)) return UrlResponse(status, content, headers) except urllib2.HTTPError as e: excepts.append(e) @@ -165,7 +165,7 @@ def wait_for_url(urls, max_wait=None, timeout=None, start_time = time.time() def log_status_cb(msg): - LOG.info(msg) + LOG.debug(msg) if status_cb is None: status_cb = log_status_cb @@ -219,8 +219,8 @@ def wait_for_url(urls, max_wait=None, timeout=None, break loop_n = loop_n + 1 - LOG.info("Please wait %s seconds while we wait to try again", - sleep_time) + LOG.debug("Please wait %s seconds while we wait to try again", + sleep_time) time.sleep(sleep_time) return False -- cgit v1.2.3 From 18e4af9955d0b62871792ec4d270f99b2051d932 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 21 Jun 2012 12:34:25 -0700 Subject: Remove another unused import --- cloudinit/distros/__init__.py | 1 - 1 file changed, 1 deletion(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 6325257c..f9a97da7 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -23,7 +23,6 @@ from StringIO import StringIO import abc -import copy from cloudinit import importer from cloudinit import log as logging -- cgit v1.2.3 From c49507a221464ce0f9747d4371f8e3d1d1b30abd Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 21 Jun 2012 19:46:16 -0700 Subject: Updated so that the locale that is being written out currently in 'cc_locale' now will be done by the distro classes (since its not the same for rhel and ubuntu). Remove the template also since it will just be created by the ubuntu distro class (its just one line). --- cloudinit/config/cc_locale.py | 25 ++----------------------- cloudinit/distros/__init__.py | 4 ++++ cloudinit/distros/rhel.py | 31 +++++++++++++++++++++++++++++++ cloudinit/distros/ubuntu.py | 11 +++++++++++ templates/default-locale.tmpl | 1 - 5 files changed, 48 insertions(+), 24 deletions(-) delete mode 100644 templates/default-locale.tmpl (limited to 'cloudinit/distros') diff --git a/cloudinit/config/cc_locale.py b/cloudinit/config/cc_locale.py index 3fb4c5d9..6feaae9d 100644 --- a/cloudinit/config/cc_locale.py +++ b/cloudinit/config/cc_locale.py @@ -18,41 +18,20 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os - -from cloudinit import templater from cloudinit import util -def apply_locale(locale, cfgfile, cloud, log): - # TODO this command might not work on RH... - if os.path.exists('/usr/sbin/locale-gen'): - util.subp(['locale-gen', locale], capture=False) - if os.path.exists('/usr/sbin/update-locale'): - util.subp(['update-locale', locale], capture=False) - if not cfgfile: - return - template_fn = cloud.get_template_filename('default-locale') - if not template_fn: - log.warn("No template filename found to write to %s", cfgfile) - else: - templater.render_to_file(template_fn, cfgfile, {'locale': locale}) - - def handle(name, cfg, cloud, log, args): if len(args) != 0: locale = args[0] else: locale = util.get_cfg_option_str(cfg, "locale", cloud.get_locale()) - locale_cfgfile = util.get_cfg_option_str(cfg, "locale_configfile", - "/etc/default/locale") - if not locale: log.debug(("Skipping module named %s, " "no 'locale' configuration found"), name) return log.debug("Setting locale to %s", locale) - - apply_locale(locale, locale_cfgfile, cloud, log) + locale_cfgfile = util.get_cfg_option_str(cfg, "locale_configfile") + cloud.distro.apply_locale(locale, locale_cfgfile) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index f9a97da7..c324ddf6 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -82,6 +82,10 @@ class Distro(object): return self._interface_action('up') return False + @abc.abstractmethod + def apply_locale(self, locale, out_fn=None): + raise NotImplementedError() + @abc.abstractmethod def set_timezone(self, tz): raise NotImplementedError() diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 5cbefa6e..df63d559 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -30,6 +30,10 @@ LOG = logging.getLogger(__name__) NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' +# See: http://tiny.cc/6r99fw +# For what alot of these files that are being written +# are and the format of them + class Distro(distros.Distro): @@ -83,6 +87,33 @@ class Distro(distros.Distro): LOG.debug("Setting hostname to %s", hostname) util.subp(['hostname', hostname]) + def apply_locale(self, locale, out_fn=None): + if not out_fn: + out_fn = self._paths.join(False, '/etc/sysconfig/i18n') + ro_fn = self._paths.join(True, '/etc/sysconfig/i18n') + # Update the 'LANG' if it exists instead of appending + old_contents = self._read_conf(ro_fn) + adjusted = False + new_contents = [] + for entry in old_contents: + if not entry: + continue + if len(entry) == 1: + new_contents.append(entry[0]) + continue + (cmd, args) = entry + cmd_c = cmd.strip().lower() + if cmd_c == 'lang': + args = "%s" % (locale) + adjusted = True + new_contents.append("=".join([cmd, args])) + # Guess not found, append it + if not adjusted: + new_contents.append("# Added by cloud-init") + new_contents.append('LANG="%s"' % (locale)) + contents = "\n".join(new_contents) + util.write_file(out_fn, contents, 0644) + def _write_hostname(self, hostname, out_fn): old_contents = [] if os.path.isfile(out_fn): diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 15af2e7f..e8b95374 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -41,6 +41,17 @@ class Distro(distros.Distro): # should only happen say once per instance...) self._runner = helpers.Runners(paths) + def apply_locale(self, locale, out_fn=None): + if not out_fn: + out_fn = self._paths.join(False, '/etc/default/locale') + util.subp(['locale-gen', locale], capture=False) + util.subp(['update-locale', locale], capture=False) + contents = [ + "# Created by cloud-init", + 'LANG="%s"' % (locale), + ] + util.write_file(out_fn, "\n".join(contents)) + def install_packages(self, pkglist): self._update_package_sources() self.package_command('install', pkglist) diff --git a/templates/default-locale.tmpl b/templates/default-locale.tmpl deleted file mode 100644 index 5ee7e454..00000000 --- a/templates/default-locale.tmpl +++ /dev/null @@ -1 +0,0 @@ -LANG="{{locale}}" -- cgit v1.2.3 From 23cf7e35bf9aa1cffc9d1bb2f20d362b57110723 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 21 Jun 2012 20:28:21 -0700 Subject: Use RuntimeError subclass instead of top level Exception --- cloudinit/config/cc_landscape.py | 8 ++++---- cloudinit/config/cc_update_etc_hosts.py | 4 ++-- cloudinit/distros/rhel.py | 4 ++-- cloudinit/distros/ubuntu.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/config/cc_landscape.py b/cloudinit/config/cc_landscape.py index d45c9203..99a958b0 100644 --- a/cloudinit/config/cc_landscape.py +++ b/cloudinit/config/cc_landscape.py @@ -61,10 +61,10 @@ def handle(name, cfg, cloud, log, _args): ls_cloudcfg = cfg.get("landscape", {}) - if not isinstance(ls_cloudcfg, dict): - raise Exception(("'landscape' key existed in config," - " but not a dictionary type," - " is a %s instead"), util.obj_name(ls_cloudcfg)) + if not isinstance(ls_cloudcfg, (dict)): + raise RuntimeError(("'landscape' key existed in config," + " but not a dictionary type," + " is a %s instead"), util.obj_name(ls_cloudcfg)) merge_data = [ LSC_BUILTIN_CFG, diff --git a/cloudinit/config/cc_update_etc_hosts.py b/cloudinit/config/cc_update_etc_hosts.py index 6820ac4f..c148b12e 100644 --- a/cloudinit/config/cc_update_etc_hosts.py +++ b/cloudinit/config/cc_update_etc_hosts.py @@ -39,8 +39,8 @@ def handle(name, cfg, cloud, log, _args): distro_n = cloud.distro.name tpl_fn_name = cloud.get_template_filename("hosts.%s" % (distro_n)) if not tpl_fn_name: - raise Exception(("No hosts template could be" - " found for distro %s") % (distro_n)) + raise RuntimeError(("No hosts template could be" + " found for distro %s") % (distro_n)) out_fn = cloud.paths.join(False, '/etc/hosts') templater.render_to_file(tpl_fn_name, out_fn, diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index df63d559..dff76075 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -208,8 +208,8 @@ class Distro(distros.Distro): def set_timezone(self, tz): tz_file = os.path.join("/usr/share/zoneinfo", tz) if not os.path.isfile(tz_file): - raise Exception(("Invalid timezone %s," - " no file found at %s") % (tz, tz_file)) + raise RuntimeError(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) # Adjust the sysconfig clock zone setting read_fn = self._paths.join(True, "/etc/sysconfig/clock") old_contents = self._read_conf(read_fn) diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index e8b95374..b23945d8 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -121,8 +121,8 @@ class Distro(distros.Distro): def set_timezone(self, tz): tz_file = os.path.join("/usr/share/zoneinfo", tz) if not os.path.isfile(tz_file): - raise Exception(("Invalid timezone %s," - " no file found at %s") % (tz, tz_file)) + raise RuntimeError(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) tz_lines = [ "# Created by cloud-init", str(tz), -- cgit v1.2.3 From bbb9f6682a717fddef16b56ea5de82792fb7f32e Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 22 Jun 2012 12:35:36 -0700 Subject: Use configobj support for now to keep the comments and other k/v pairs that may already exist in these files --- cloudinit/distros/rhel.py | 160 +++++++++++++--------------------------------- 1 file changed, 45 insertions(+), 115 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index dff76075..4ed9d43f 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -34,6 +34,10 @@ NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' # For what alot of these files that are being written # are and the format of them +# This library is used to parse/write +# out the various sysconfig files edited +from configobj import ConfigObj + class Distro(distros.Distro): @@ -50,34 +54,38 @@ class Distro(distros.Distro): settings, entries) # Make the intermediate format as the rhel format... for (dev, info) in entries.iteritems(): - lines = [] - lines.append("DEVICE=%s" % (dev)) + net_fn = NETWORK_FN_TPL % (dev) + net_ro_fn = self._paths.join(True, net_fn) + (prev_exist, net_cfg) = self._read_conf(net_ro_fn) + net_cfg['DEVICE'] = dev boot_proto = info.get('bootproto') if boot_proto: - lines.append("BOOTPROTO=%s" % (boot_proto)) + net_cfg['BOOTPROTO'] = boot_proto net_mask = info.get('netmask') if net_mask: - lines.append("NETMASK=%s" % (net_mask)) + net_cfg["NETMASK"] = net_mask addr = info.get('address') if addr: - lines.append("IPADDR=%s" % (addr)) + net_cfg["IPADDR"] = addr if info.get('auto'): - lines.append("ONBOOT=yes") + net_cfg['ONBOOT'] = 'yes' else: - lines.append("ONBOOT=no") + net_cfg['ONBOOT'] = 'no' gtway = info.get('gateway') if gtway: - lines.append("GATEWAY=%s" % (gtway)) + net_cfg["GATEWAY"] = gtway bcast = info.get('broadcast') if bcast: - lines.append("BROADCAST=%s" % (bcast)) + net_cfg["BROADCAST"] = bcast mac_addr = info.get('hwaddress') if mac_addr: - lines.append("MACADDR=%s" % (mac_addr)) - lines.insert(0, '# Created by cloud-init') - contents = "\n".join(lines) - net_fn = NETWORK_FN_TPL % (dev) - util.write_file(self._paths.join(False, net_fn), contents, 0644) + net_cfg["MACADDR"] = mac_addr + lines = net_cfg.write() + if not prev_exist: + lines.insert(0, '# Created by cloud-init') + w_contents = "\n".join(lines) + net_rw_fn = self._paths.join(False, net_fn) + util.write_file(net_rw_fn, w_contents, 0644) def set_hostname(self, hostname): out_fn = self._paths.join(False, '/etc/sysconfig/network') @@ -91,54 +99,16 @@ class Distro(distros.Distro): if not out_fn: out_fn = self._paths.join(False, '/etc/sysconfig/i18n') ro_fn = self._paths.join(True, '/etc/sysconfig/i18n') - # Update the 'LANG' if it exists instead of appending - old_contents = self._read_conf(ro_fn) - adjusted = False - new_contents = [] - for entry in old_contents: - if not entry: - continue - if len(entry) == 1: - new_contents.append(entry[0]) - continue - (cmd, args) = entry - cmd_c = cmd.strip().lower() - if cmd_c == 'lang': - args = "%s" % (locale) - adjusted = True - new_contents.append("=".join([cmd, args])) - # Guess not found, append it - if not adjusted: - new_contents.append("# Added by cloud-init") - new_contents.append('LANG="%s"' % (locale)) - contents = "\n".join(new_contents) - util.write_file(out_fn, contents, 0644) + (_exists, contents) = self._read_conf(ro_fn) + contents['LANG'] = locale + w_contents = "\n".join(contents.write()) + util.write_file(out_fn, w_contents, 0644) def _write_hostname(self, hostname, out_fn): - old_contents = [] - if os.path.isfile(out_fn): - old_contents = self._read_conf(out_fn) - # Update the 'HOSTNAME' if it exists instead of appending - new_contents = [] - adjusted = False - for entry in old_contents: - if not entry: - continue - if len(entry) == 1: - new_contents.append(entry[0]) - continue - (cmd, args) = entry - cmd_c = cmd.strip().lower() - if cmd_c == 'hostname': - args = "%s" % (hostname) - adjusted = True - new_contents.append("=".join([cmd, args])) - # Guess not found, append it - if not adjusted: - new_contents.append("# Added by cloud-init") - new_contents.append("HOSTNAME=%s" % (hostname)) - contents = "\n".join(new_contents) - util.write_file(out_fn, contents, 0644) + (_exists, contents) = self._read_conf(out_fn) + contents['HOSTNAME'] = hostname + w_contents = "\n".join(contents.write()) + util.write_file(out_fn, w_contents, 0644) def update_hostname(self, hostname, prev_file): hostname_prev = self._read_hostname(prev_file) @@ -168,42 +138,20 @@ class Distro(distros.Distro): util.subp(['hostname', hostname]) def _read_hostname(self, filename, default=None): - contents = self._read_conf(filename) - for c in contents: - if len(c) != 2: - continue - (cmd, args) = c - cmd_c = cmd.lower().strip() - if cmd_c == 'hostname': - args_c = args.strip() - if args_c: - return args_c - return default + (_exists, contents) = self._read_conf(filename) + if 'HOSTNAME' in contents: + return contents['HOSTNAME'] + else: + return default def _read_conf(self, filename): - contents = util.load_file(filename, quiet=True) - conf_lines = [] - for line in contents.splitlines(): - c_line = line.strip() - if not c_line or c_line.startswith("#"): - conf_lines.append([line]) - continue - # Handle inline comments - c_pos = c_line.find("#") - if c_pos != -1: - c_line = c_line[0:c_pos].strip() - if not c_line: - conf_lines.append([line]) - continue - # Format should be CMD=ARG1 ARG2... - pieces = c_line.split("=", 1) - if not pieces or len(pieces) == 1: - conf_lines.append([line]) - continue - (cmd, args) = pieces - cmd = cmd.strip() - conf_lines.append([cmd, args]) - return conf_lines + exists = False + if os.path.isfile(filename): + contents = util.load_file(filename).splitlines() + exists = True + else: + contents = [] + return (exists, ConfigObj(contents)) def set_timezone(self, tz): tz_file = os.path.join("/usr/share/zoneinfo", tz) @@ -212,27 +160,9 @@ class Distro(distros.Distro): " no file found at %s") % (tz, tz_file)) # Adjust the sysconfig clock zone setting read_fn = self._paths.join(True, "/etc/sysconfig/clock") - old_contents = self._read_conf(read_fn) - new_contents = [] - zone_added = False - # Update the 'ZONE' if it exists instead of appending - for entry in old_contents: - if not entry: - continue - if len(entry) == 1: - new_contents.append(entry[0]) - continue - (cmd, args) = entry - cmd_c = cmd.lower().strip() - if cmd_c == 'zone': - args = '"%s"' % (tz) - zone_added = True - new_contents.append("=".join([cmd, args])) - # Guess not found, append it - if not zone_added: - new_contents.append("# Added by cloud-init") - new_contents.append('ZONE="%s"' % (tz)) - tz_contents = "\n".join(new_contents) + (_exists, contents) = self._read_conf(read_fn) + contents['ZONE'] = tz + tz_contents = "\n".join(contents.write()) write_fn = self._paths.join(False, "/etc/sysconfig/clock") util.write_file(write_fn, tz_contents) # This ensures that the correct tz will be used for the system -- cgit v1.2.3 From c55aa553b2f7141d539810bf17ba1aebb1a50bc4 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 22 Jun 2012 12:52:26 -0700 Subject: 1. Add a ubuntu and fedora subclass of the RH/debian root classes 2. Move the ubuntu class to the debian class (since thats really what it is) --- cloudinit/distros/debian.py | 149 ++++++++++++++++++++++++++++++++++++++++++++ cloudinit/distros/fedora.py | 31 +++++++++ cloudinit/distros/ubuntu.py | 124 +----------------------------------- 3 files changed, 183 insertions(+), 121 deletions(-) create mode 100644 cloudinit/distros/debian.py create mode 100644 cloudinit/distros/fedora.py (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py new file mode 100644 index 00000000..b23945d8 --- /dev/null +++ b/cloudinit/distros/debian.py @@ -0,0 +1,149 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser +# Author: Juerg Haefliger +# Author: Joshua Harlow +# +# 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 . + +import os + +from cloudinit import distros +from cloudinit import helpers +from cloudinit import log as logging +from cloudinit import util + +from cloudinit.settings import PER_INSTANCE + +LOG = logging.getLogger(__name__) + + +class Distro(distros.Distro): + + def __init__(self, name, cfg, paths): + distros.Distro.__init__(self, name, cfg, paths) + # This will be used to restrict certain + # calls from repeatly happening (when they + # should only happen say once per instance...) + self._runner = helpers.Runners(paths) + + def apply_locale(self, locale, out_fn=None): + if not out_fn: + out_fn = self._paths.join(False, '/etc/default/locale') + util.subp(['locale-gen', locale], capture=False) + util.subp(['update-locale', locale], capture=False) + contents = [ + "# Created by cloud-init", + 'LANG="%s"' % (locale), + ] + util.write_file(out_fn, "\n".join(contents)) + + def install_packages(self, pkglist): + self._update_package_sources() + self.package_command('install', pkglist) + + def _write_network(self, settings): + net_fn = self._paths.join(False, "/etc/network/interfaces") + util.write_file(net_fn, settings) + + def set_hostname(self, hostname): + out_fn = self._paths.join(False, "/etc/hostname") + self._write_hostname(hostname, out_fn) + if out_fn == '/etc/hostname': + # Only do this if we are running in non-adjusted root mode + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _write_hostname(self, hostname, out_fn): + lines = [] + lines.append("# Created by cloud-init") + lines.append(str(hostname)) + contents = "\n".join(lines) + util.write_file(out_fn, contents, 0644) + + def update_hostname(self, hostname, prev_fn): + hostname_prev = self._read_hostname(prev_fn) + read_fn = self._paths.join(True, "/etc/hostname") + hostname_in_etc = self._read_hostname(read_fn) + update_files = [] + if not hostname_prev or hostname_prev != hostname: + update_files.append(prev_fn) + if (not hostname_in_etc or + (hostname_in_etc == hostname_prev and + hostname_in_etc != hostname)): + write_fn = self._paths.join(False, "/etc/hostname") + update_files.append(write_fn) + for fn in update_files: + try: + self._write_hostname(hostname, fn) + except: + util.logexc(LOG, "Failed to write hostname %s to %s", + hostname, fn) + if (hostname_in_etc and hostname_prev and + hostname_in_etc != hostname_prev): + LOG.debug(("%s differs from /etc/hostname." + " Assuming user maintained hostname."), prev_fn) + if "/etc/hostname" in update_files: + # Only do this if we are running in non-adjusted root mode + LOG.debug("Setting hostname to %s", hostname) + util.subp(['hostname', hostname]) + + def _read_hostname(self, filename, default=None): + contents = util.load_file(filename, quiet=True) + for line in contents.splitlines(): + c_pos = line.find("#") + # Handle inline comments + if c_pos != -1: + line = line[0:c_pos] + line_c = line.strip() + if line_c: + return line_c + return default + + def _get_localhost_ip(self): + # Note: http://www.leonardoborda.com/blog/127-0-1-1-ubuntu-debian/ + return "127.0.1.1" + + def set_timezone(self, tz): + tz_file = os.path.join("/usr/share/zoneinfo", tz) + if not os.path.isfile(tz_file): + raise RuntimeError(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) + tz_lines = [ + "# Created by cloud-init", + str(tz), + ] + tz_contents = "\n".join(tz_lines) + tz_fn = self._paths.join(False, "/etc/timezone") + util.write_file(tz_fn, tz_contents) + util.copy(tz_file, self._paths.join(False, "/etc/localtime")) + + def package_command(self, command, args=None): + e = os.environ.copy() + # See: http://tiny.cc/kg91fw + # Or: http://tiny.cc/mh91fw + e['DEBIAN_FRONTEND'] = 'noninteractive' + cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold', + '--assume-yes', command] + if args: + cmd.extend(args) + # Allow the output of this to flow outwards (ie not be captured) + util.subp(cmd, env=e, capture=False) + + def _update_package_sources(self): + self._runner.run("update-sources", self.package_command, + ["update"], freq=PER_INSTANCE) diff --git a/cloudinit/distros/fedora.py b/cloudinit/distros/fedora.py new file mode 100644 index 00000000..c777845d --- /dev/null +++ b/cloudinit/distros/fedora.py @@ -0,0 +1,31 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# +# Author: Scott Moser +# Author: Juerg Haefliger +# Author: Joshua Harlow +# +# 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.distros import rhel + +from cloudinit import log as logging + +LOG = logging.getLogger(__name__) + + +class Distro(rhel.Distro): + pass diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index b23945d8..77c2aff4 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -20,130 +20,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os +from cloudinit.distros import debian -from cloudinit import distros -from cloudinit import helpers from cloudinit import log as logging -from cloudinit import util - -from cloudinit.settings import PER_INSTANCE LOG = logging.getLogger(__name__) -class Distro(distros.Distro): - - def __init__(self, name, cfg, paths): - distros.Distro.__init__(self, name, cfg, paths) - # This will be used to restrict certain - # calls from repeatly happening (when they - # should only happen say once per instance...) - self._runner = helpers.Runners(paths) - - def apply_locale(self, locale, out_fn=None): - if not out_fn: - out_fn = self._paths.join(False, '/etc/default/locale') - util.subp(['locale-gen', locale], capture=False) - util.subp(['update-locale', locale], capture=False) - contents = [ - "# Created by cloud-init", - 'LANG="%s"' % (locale), - ] - util.write_file(out_fn, "\n".join(contents)) - - def install_packages(self, pkglist): - self._update_package_sources() - self.package_command('install', pkglist) - - def _write_network(self, settings): - net_fn = self._paths.join(False, "/etc/network/interfaces") - util.write_file(net_fn, settings) - - def set_hostname(self, hostname): - out_fn = self._paths.join(False, "/etc/hostname") - self._write_hostname(hostname, out_fn) - if out_fn == '/etc/hostname': - # Only do this if we are running in non-adjusted root mode - LOG.debug("Setting hostname to %s", hostname) - util.subp(['hostname', hostname]) - - def _write_hostname(self, hostname, out_fn): - lines = [] - lines.append("# Created by cloud-init") - lines.append(str(hostname)) - contents = "\n".join(lines) - util.write_file(out_fn, contents, 0644) - - def update_hostname(self, hostname, prev_fn): - hostname_prev = self._read_hostname(prev_fn) - read_fn = self._paths.join(True, "/etc/hostname") - hostname_in_etc = self._read_hostname(read_fn) - update_files = [] - if not hostname_prev or hostname_prev != hostname: - update_files.append(prev_fn) - if (not hostname_in_etc or - (hostname_in_etc == hostname_prev and - hostname_in_etc != hostname)): - write_fn = self._paths.join(False, "/etc/hostname") - update_files.append(write_fn) - for fn in update_files: - try: - self._write_hostname(hostname, fn) - except: - util.logexc(LOG, "Failed to write hostname %s to %s", - hostname, fn) - if (hostname_in_etc and hostname_prev and - hostname_in_etc != hostname_prev): - LOG.debug(("%s differs from /etc/hostname." - " Assuming user maintained hostname."), prev_fn) - if "/etc/hostname" in update_files: - # Only do this if we are running in non-adjusted root mode - LOG.debug("Setting hostname to %s", hostname) - util.subp(['hostname', hostname]) - - def _read_hostname(self, filename, default=None): - contents = util.load_file(filename, quiet=True) - for line in contents.splitlines(): - c_pos = line.find("#") - # Handle inline comments - if c_pos != -1: - line = line[0:c_pos] - line_c = line.strip() - if line_c: - return line_c - return default - - def _get_localhost_ip(self): - # Note: http://www.leonardoborda.com/blog/127-0-1-1-ubuntu-debian/ - return "127.0.1.1" - - def set_timezone(self, tz): - tz_file = os.path.join("/usr/share/zoneinfo", tz) - if not os.path.isfile(tz_file): - raise RuntimeError(("Invalid timezone %s," - " no file found at %s") % (tz, tz_file)) - tz_lines = [ - "# Created by cloud-init", - str(tz), - ] - tz_contents = "\n".join(tz_lines) - tz_fn = self._paths.join(False, "/etc/timezone") - util.write_file(tz_fn, tz_contents) - util.copy(tz_file, self._paths.join(False, "/etc/localtime")) - - def package_command(self, command, args=None): - e = os.environ.copy() - # See: http://tiny.cc/kg91fw - # Or: http://tiny.cc/mh91fw - e['DEBIAN_FRONTEND'] = 'noninteractive' - cmd = ['apt-get', '--option', 'Dpkg::Options::=--force-confold', - '--assume-yes', command] - if args: - cmd.extend(args) - # Allow the output of this to flow outwards (ie not be captured) - util.subp(cmd, env=e, capture=False) - - def _update_package_sources(self): - self._runner.run("update-sources", self.package_command, - ["update"], freq=PER_INSTANCE) +class Distro(debian.Distro): + pass -- cgit v1.2.3 From a716d8a6a82231d71c80e43e4c7734513de8ba2b Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 22 Jun 2012 18:07:05 -0700 Subject: 1. Fix the cfgobj to make sure it handles the quoting of the keys and values in sysconfig specially by ensuring that it does the quoting so that the files written out can be sourced into bash scripts, which is typically what these files are used for. --- cloudinit/distros/rhel.py | 61 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 4 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 4ed9d43f..2c5bcab9 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -36,9 +36,24 @@ NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' # This library is used to parse/write # out the various sysconfig files edited +# +# It has to be slightly modified though +# to ensure that all values are quoted +# since these configs are usually sourced into +# bash scripts... from configobj import ConfigObj +# See: http://tiny.cc/oezbgw +D_QUOTE_CHARS = { + "\"": "\\\"", + "(": "\\(", + ")": "\\)", + "$": '\$', + '`': '\`', +} + + class Distro(distros.Distro): def __init__(self, name, cfg, paths): @@ -144,14 +159,14 @@ class Distro(distros.Distro): else: return default - def _read_conf(self, filename): + def _read_conf(self, fn): exists = False - if os.path.isfile(filename): - contents = util.load_file(filename).splitlines() + if os.path.isfile(fn): + contents = util.load_file(fn).splitlines() exists = True else: contents = [] - return (exists, ConfigObj(contents)) + return (exists, QuotingConfigObj(contents)) def set_timezone(self, tz): tz_file = os.path.join("/usr/share/zoneinfo", tz) @@ -186,6 +201,44 @@ class Distro(distros.Distro): util.subp(cmd, capture=False) +# This class helps adjust the configobj +# writing to ensure that when writing a k/v +# on a line, that they are properly quoted +# and have no spaces between the '=' sign. +# - This is mainly due to the fact that +# the sysconfig scripts are often sourced +# directly into bash/shell scripts so ensure +# that it works for those types of use cases. +class QuotingConfigObj(ConfigObj): + def __init__(self, lines): + ConfigObj.__init__(self, lines, + interpolation=False, + write_empty_values=True) + + def _quote_posix(self, text): + if not text: + return '""' + for (k, v) in D_QUOTE_CHARS.iteritems(): + text = text.replace(k, v) + return '"%s"' % (text) + + def _write_line(self, indent_string, entry, this_entry, comment): + # Ensure it is formatted fine for + # how these sysconfig scripts are used + val = self._decode_element(self._quote(this_entry)) + if not val.startswith("'"): + # Not already quoted, double quote + # it for safety + val = self._quote_posix(val) + key = self._decode_element(self._quote(entry, multiline=False)) + cmnt = self._decode_element(comment) + return '%s%s%s%s%s' % (indent_string, + key, + "=", + val, + cmnt) + + # This is a util function to translate a ubuntu /etc/network/interfaces 'blob' # to a rhel equiv. that can then be written to /etc/sysconfig/network-scripts/ # TODO remove when we have python-netcf active... -- cgit v1.2.3 From e18191a05b417b6f6aa9374ee288e79712320dee Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 22 Jun 2012 18:17:29 -0700 Subject: 1. Adjust for the cases of 'yes', 'no', 'true', 'false' by leaving those unquoted 2. Add more comments as to why we are leaving single quoted strings alone. --- cloudinit/distros/rhel.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 2c5bcab9..87d5b7a8 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -43,7 +43,6 @@ NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' # bash scripts... from configobj import ConfigObj - # See: http://tiny.cc/oezbgw D_QUOTE_CHARS = { "\"": "\\\"", @@ -217,19 +216,26 @@ class QuotingConfigObj(ConfigObj): def _quote_posix(self, text): if not text: - return '""' + return '' for (k, v) in D_QUOTE_CHARS.iteritems(): text = text.replace(k, v) return '"%s"' % (text) + def _quote_special(self, text): + if text.lower() in ['yes', 'no', 'true', 'false']: + return text + else: + return self._quote_posix(text) + def _write_line(self, indent_string, entry, this_entry, comment): # Ensure it is formatted fine for # how these sysconfig scripts are used val = self._decode_element(self._quote(this_entry)) + # Single quoted strings should + # always work. if not val.startswith("'"): - # Not already quoted, double quote - # it for safety - val = self._quote_posix(val) + # Perform any special quoting + val = self._quote_special(val) key = self._decode_element(self._quote(entry, multiline=False)) cmnt = self._decode_element(comment) return '%s%s%s%s%s' % (indent_string, -- cgit v1.2.3 From d95662579c59f8866721483e2b2c8b4ca0099eae Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Fri, 29 Jun 2012 20:06:32 -0400 Subject: add update_package_sources to distro class Previously update_package_sources was a private class (_update_package_sources). The apt_update_upgrade class called it. It does make sense that things would want to call this independently of installing packages. Therefore, expose it as a non hidden method. --- cloudinit/distros/__init__.py | 4 ++++ cloudinit/distros/debian.py | 4 ++-- cloudinit/distros/rhel.py | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index c324ddf6..da4d0180 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -71,6 +71,10 @@ class Distro(object): def package_command(self, cmd, args=None): raise NotImplementedError() + @abc.abstractmethod + def update_package_sources(self): + raise NotImplementedError() + def get_package_mirror(self): return self.get_option('package_mirror') diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py index 3a0cae19..3247d7ce 100644 --- a/cloudinit/distros/debian.py +++ b/cloudinit/distros/debian.py @@ -53,7 +53,7 @@ class Distro(distros.Distro): util.write_file(out_fn, "\n".join(contents)) def install_packages(self, pkglist): - self._update_package_sources() + self.update_package_sources() self.package_command('install', pkglist) def _write_network(self, settings): @@ -144,6 +144,6 @@ class Distro(distros.Distro): # Allow the output of this to flow outwards (ie not be captured) util.subp(cmd, env=e, capture=False) - def _update_package_sources(self): + def update_package_sources(self): self._runner.run("update-sources", self.package_command, ["update"], freq=PER_INSTANCE) diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 87d5b7a8..342d4c79 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -26,6 +26,8 @@ from cloudinit import distros from cloudinit import log as logging from cloudinit import util +from cloudinit.settings import PER_INSTANCE + LOG = logging.getLogger(__name__) NETWORK_FN_TPL = '/etc/sysconfig/network-scripts/ifcfg-%s' @@ -57,6 +59,10 @@ class Distro(distros.Distro): def __init__(self, name, cfg, paths): distros.Distro.__init__(self, name, cfg, paths) + # This will be used to restrict certain + # calls from repeatly happening (when they + # should only happen say once per instance...) + self._runner = helpers.Runners(paths) def install_packages(self, pkglist): self.package_command('install', pkglist) @@ -199,6 +205,10 @@ class Distro(distros.Distro): # Allow the output of this to flow outwards (ie not be captured) util.subp(cmd, capture=False) + def update_package_sources(self): + self._runner.run("update-sources", self.package_command, + ["update"], freq=PER_INSTANCE) + # This class helps adjust the configobj # writing to ensure that when writing a k/v -- cgit v1.2.3 From 3d933c525e90d0e01932d93e965e81526dcfb68a Mon Sep 17 00:00:00 2001 From: harlowja Date: Fri, 29 Jun 2012 20:38:19 -0700 Subject: Include the helpers import. --- cloudinit/distros/rhel.py | 1 + 1 file changed, 1 insertion(+) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 342d4c79..7fa69f03 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -23,6 +23,7 @@ import os from cloudinit import distros +from cloudinit import helpers from cloudinit import log as logging from cloudinit import util -- cgit v1.2.3