diff options
Diffstat (limited to 'cloudinit/distros')
-rw-r--r-- | cloudinit/distros/__init__.py | 181 | ||||
-rw-r--r-- | cloudinit/distros/debian.py | 13 | ||||
-rw-r--r-- | cloudinit/distros/fedora.py | 2 | ||||
-rw-r--r-- | cloudinit/distros/rhel.py | 12 | ||||
-rw-r--r-- | cloudinit/distros/ubuntu.py | 5 |
5 files changed, 140 insertions, 73 deletions
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 4bde2393..464ae550 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -24,6 +24,7 @@ from StringIO import StringIO import abc +import collections import itertools import os import re @@ -39,12 +40,12 @@ LOG = logging.getLogger(__name__) class Distro(object): - __metaclass__ = abc.ABCMeta default_user = None default_user_groups = None hosts_fn = "/etc/hosts" ci_sudoers_fn = "/etc/sudoers.d/90-cloud-init-users" + hostname_conf_fn = "/etc/hostname" def __init__(self, name, cfg, paths): self._paths = paths @@ -64,9 +65,10 @@ class Distro(object): def get_option(self, opt_name, default=None): return self._cfg.get(opt_name, default) - @abc.abstractmethod - def set_hostname(self, hostname): - raise NotImplementedError() + def set_hostname(self, hostname, fqdn=None): + writeable_hostname = self._select_hostname(hostname, fqdn) + self._write_hostname(writeable_hostname, self.hostname_conf_fn) + self._apply_hostname(hostname) @abc.abstractmethod def package_command(self, cmd, args=None): @@ -84,13 +86,13 @@ class Distro(object): def _get_arch_package_mirror_info(self, arch=None): mirror_info = self.get_option("package_mirrors", []) - if arch == None: + if not arch: arch = self.get_primary_arch() return _get_arch_package_mirror_info(mirror_info, arch) def get_package_mirror_info(self, arch=None, availability_zone=None): - # this resolves the package_mirrors config option + # This resolves the package_mirrors config option # down to a single dict of {mirror_name: mirror_url} arch_info = self._get_arch_package_mirror_info(arch) return _get_package_mirror_info(availability_zone=availability_zone, @@ -140,10 +142,14 @@ class Distro(object): util.logexc(LOG, ("Failed to non-persistently adjust" " the system hostname to %s"), hostname) - def update_hostname(self, hostname, prev_hostname_fn): - if not hostname: - return + @abc.abstractmethod + def _select_hostname(self, hostname, fqdn): + raise NotImplementedError() + def update_hostname(self, hostname, fqdn, + previous_hostname_filename): + applying_hostname = hostname + hostname = self._select_hostname(hostname, fqdn) prev_hostname = self._read_hostname(prev_hostname_fn) (sys_fn, sys_hostname) = self._read_system_hostname() update_files = [] @@ -171,7 +177,7 @@ class Distro(object): prev_hostname_fn, sys_fn) if sys_fn in update_files: - self._apply_hostname(hostname) + self._apply_hostname(applying_hostname) def update_etc_hosts(self, hostname, fqdn): header = '' @@ -239,22 +245,7 @@ class Distro(object): return False def get_default_user(self): - if not self.default_user: - return None - user_cfg = { - 'name': self.default_user, - 'plain_text_passwd': self.default_user, - 'home': "/home/%s" % (self.default_user), - 'shell': "/bin/bash", - 'lock_passwd': True, - 'gecos': "%s" % (self.default_user.title()), - 'sudo': "ALL=(ALL) NOPASSWD:ALL", - } - def_groups = self.default_user_groups - if not def_groups: - def_groups = [] - user_cfg['groups'] = util.uniq_merge_sorted(def_groups) - return user_cfg + return self.get_option('default_user') def create_user(self, name, **kwargs): """ @@ -270,23 +261,23 @@ class Distro(object): # inputs. If something goes wrong, we can end up with a system # that nobody can login to. adduser_opts = { - "gecos": '--comment', - "homedir": '--home', - "primary_group": '--gid', - "groups": '--groups', - "passwd": '--password', - "shell": '--shell', - "expiredate": '--expiredate', - "inactive": '--inactive', - "selinux_user": '--selinux-user', - } + "gecos": '--comment', + "homedir": '--home', + "primary_group": '--gid', + "groups": '--groups', + "passwd": '--password', + "shell": '--shell', + "expiredate": '--expiredate', + "inactive": '--inactive', + "selinux_user": '--selinux-user', + } adduser_opts_flags = { - "no_user_group": '--no-user-group', - "system": '--system', - "no_log_init": '--no-log-init', - "no_create_home": "-M", - } + "no_user_group": '--no-user-group', + "system": '--system', + "no_log_init": '--no-log-init', + "no_create_home": "-M", + } # Now check the value and create the command for option in kwargs: @@ -314,7 +305,7 @@ class Distro(object): if util.is_user(name): LOG.warn("User %s already exists, skipping." % name) else: - LOG.debug("Creating name %s" % name) + LOG.debug("Adding user named %s", name) try: util.subp(adduser_cmd, logstring=x_adduser_cmd) except Exception as e: @@ -343,7 +334,7 @@ class Distro(object): # Import SSH keys if 'ssh_authorized_keys' in kwargs: keys = set(kwargs['ssh_authorized_keys']) or [] - ssh_util.setup_user_keys(keys, name, None, self._paths) + ssh_util.setup_user_keys(keys, name, key_prefix=None) return True @@ -362,32 +353,79 @@ class Distro(object): return True + def ensure_sudo_dir(self, path, sudo_base='/etc/sudoers'): + # Ensure the dir is included and that + # it actually exists as a directory + sudoers_contents = '' + base_exists = False + if os.path.exists(sudo_base): + sudoers_contents = util.load_file(sudo_base) + base_exists = True + found_include = False + for line in sudoers_contents.splitlines(): + line = line.strip() + include_match = re.search(r"^#includedir\s+(.*)$", line) + if not include_match: + continue + included_dir = include_match.group(1).strip() + if not included_dir: + continue + included_dir = os.path.abspath(included_dir) + if included_dir == path: + found_include = True + break + if not found_include: + try: + if not base_exists: + lines = [('# See sudoers(5) for more information' + ' on "#include" directives:'), '', + util.make_header(base="added"), + "#includedir %s" % (path), ''] + sudoers_contents = "\n".join(lines) + util.write_file(sudo_base, sudoers_contents, 0440) + else: + lines = ['', util.make_header(base="added"), + "#includedir %s" % (path), ''] + sudoers_contents = "\n".join(lines) + util.append_file(sudo_base, sudoers_contents) + LOG.debug("Added '#includedir %s' to %s" % (path, sudo_base)) + except IOError as e: + util.logexc(LOG, "Failed to write %s" % sudo_base, e) + raise e + util.ensure_dir(path, 0750) + def write_sudo_rules(self, user, rules, sudo_file=None): if not sudo_file: sudo_file = self.ci_sudoers_fn - content_header = "# User rules for %s" % user - content = "%s\n%s %s\n\n" % (content_header, user, rules) - - if isinstance(rules, (list, tuple, set)): - content = "%s\n" % content_header + lines = [ + '', + "# User rules for %s" % user, + ] + if isinstance(rules, collections.Iterable): for rule in rules: - content += "%s %s\n" % (user, rule) - content += "\n" + lines.append("%s %s" % (user, rule)) + else: + lines.append("%s %s" % (user, rules)) + content = "\n".join(lines) + self.ensure_sudo_dir(os.path.dirname(sudo_file)) if not os.path.exists(sudo_file): contents = [ util.make_header(), content, ] - util.write_file(sudo_file, "\n".join(contents), 0440) - else: try: - with open(sudo_file, 'a') as f: - f.write(content) + util.write_file(sudo_file, "\n".join(contents), 0440) except IOError as e: util.logexc(LOG, "Failed to write sudoers file %s", sudo_file) raise e + else: + try: + util.append_file(sudo_file, content) + except IOError as e: + util.logexc(LOG, "Failed to append sudoers file %s", sudo_file) + raise e def create_group(self, name, members): group_add_cmd = ['groupadd', name] @@ -476,12 +514,36 @@ def _get_arch_package_mirror_info(package_mirrors, arch): # is the standard form used in the rest # of cloud-init def _normalize_groups(grp_cfg): - if isinstance(grp_cfg, (str, basestring, list)): + if isinstance(grp_cfg, (str, basestring)): + grp_cfg = grp_cfg.strip().split(",") + if isinstance(grp_cfg, (list)): c_grp_cfg = {} - for i in util.uniq_merge(grp_cfg): - c_grp_cfg[i] = [] + for i in grp_cfg: + if isinstance(i, (dict)): + for k, v in i.items(): + if k not in c_grp_cfg: + if isinstance(v, (list)): + c_grp_cfg[k] = list(v) + elif isinstance(v, (basestring, str)): + c_grp_cfg[k] = [v] + else: + raise TypeError("Bad group member type %s" % + util.obj_name(v)) + else: + if isinstance(v, (list)): + c_grp_cfg[k].extend(v) + elif isinstance(v, (basestring, str)): + c_grp_cfg[k].append(v) + else: + raise TypeError("Bad group member type %s" % + util.obj_name(v)) + elif isinstance(i, (str, basestring)): + if i not in c_grp_cfg: + c_grp_cfg[i] = [] + else: + raise TypeError("Unknown group name type %s" % + util.obj_name(i)) grp_cfg = c_grp_cfg - groups = {} if isinstance(grp_cfg, (dict)): for (grp_name, grp_members) in grp_cfg.items(): @@ -503,7 +565,7 @@ def _normalize_groups(grp_cfg): # configuration. # # The output is a dictionary of user -# names => user config which is the standard +# names => user config which is the standard # form used in the rest of cloud-init. Note # the default user will have a special config # entry 'default' which will be marked as true @@ -568,6 +630,7 @@ def _normalize_users(u_cfg, def_user_cfg=None): # Pickup what the default 'real name' is # and any groups that are provided by the # default config + def_user_cfg = def_user_cfg.copy() def_user = def_user_cfg.pop('name') def_groups = def_user_cfg.pop('groups', []) # Pickup any config + groups for that user name diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py index 0d5cbac7..b6e7654f 100644 --- a/cloudinit/distros/debian.py +++ b/cloudinit/distros/debian.py @@ -80,9 +80,12 @@ class Distro(distros.Distro): else: return distros.Distro._bring_up_interfaces(self, device_names) - def set_hostname(self, hostname): - self._write_hostname(hostname, self.hostname_conf_fn) - self._apply_hostname(hostname) + def _select_hostname(self, hostname, fqdn): + # Prefer the short hostname over the long + # fully qualified domain name + if not hostname: + return fqdn + return hostname def _write_hostname(self, your_hostname, out_fn): conf = self._read_hostname_conf(out_fn) @@ -128,10 +131,10 @@ class Distro(distros.Distro): if not os.path.isfile(tz_file): raise RuntimeError(("Invalid timezone %s," " no file found at %s") % (tz, tz_file)) - # "" provides trailing newline during join + # Note: "" provides trailing newline during join tz_lines = [ util.make_header(), - str(tz), + str(tz), "", ] util.write_file(self.tz_conf_fn, "\n".join(tz_lines)) diff --git a/cloudinit/distros/fedora.py b/cloudinit/distros/fedora.py index f65a820d..c777845d 100644 --- a/cloudinit/distros/fedora.py +++ b/cloudinit/distros/fedora.py @@ -28,4 +28,4 @@ LOG = logging.getLogger(__name__) class Distro(rhel.Distro): - default_user = 'ec2-user' + pass diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 039215c8..7df01c62 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -48,6 +48,7 @@ class Distro(distros.Distro): clock_conf_fn = "/etc/sysconfig/clock" locale_conf_fn = '/etc/sysconfig/i18n' network_conf_fn = "/etc/sysconfig/network" + hostname_conf_fn = "/etc/sysconfig/network" network_script_tpl = '/etc/sysconfig/network-scripts/ifcfg-%s' resolve_conf_fn = "/etc/resolv.conf" tz_local_fn = "/etc/localtime" @@ -143,10 +144,6 @@ class Distro(distros.Distro): lines.insert(0, util.make_header()) util.write_file(fn, "\n".join(lines), 0644) - def set_hostname(self, hostname): - self._write_hostname(hostname, self.network_conf_fn) - self._apply_hostname(hostname) - def apply_locale(self, locale, out_fn=None): if not out_fn: out_fn = self.locale_conf_fn @@ -161,6 +158,13 @@ class Distro(distros.Distro): } self._update_sysconfig_file(out_fn, host_cfg) + def _select_hostname(self, hostname, fqdn): + # See: http://bit.ly/TwitgL + # Should be fqdn if we can use it + if fqdn: + return fqdn + return hostname + def _read_system_hostname(self): return (self.network_conf_fn, self._read_hostname(self.network_conf_fn)) diff --git a/cloudinit/distros/ubuntu.py b/cloudinit/distros/ubuntu.py index 4e697f82..c527f248 100644 --- a/cloudinit/distros/ubuntu.py +++ b/cloudinit/distros/ubuntu.py @@ -28,7 +28,4 @@ LOG = logging.getLogger(__name__) class Distro(debian.Distro): - - default_user = 'ubuntu' - default_user_groups = ("adm,audio,cdrom,dialout,floppy,video," - "plugdev,dip,netdev,sudo") + pass |