From d445836b3ec9ca94b26edd3eb4df9f4a53e67bc6 Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Thu, 20 Jun 2013 15:53:16 +0200 Subject: Cleanup Distro.create_user() method Move adding of a user and locking of a password to their own methods so that distro handlers can override them. --- cloudinit/distros/__init__.py | 101 ++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 44 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index e99cb16f..c5990960 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -281,15 +281,16 @@ class Distro(object): def get_default_user(self): return self.get_option('default_user') - def create_user(self, name, **kwargs): + def add_user(self, name, **kwargs): """ - Creates users for the system using the GNU passwd tools. This - will work on an GNU system. This should be overriden on - distros where useradd is not desirable or not available. + Add a user to the system using standard GNU tools """ + if util.is_user(name): + LOG.info("User %s already exists, skipping." % name) + return adduser_cmd = ['useradd', name] - x_adduser_cmd = ['useradd', name] + log_adduser_cmd = ['useradd', name] # Since we are creating users, we want to carefully validate the # inputs. If something goes wrong, we can end up with a system @@ -306,63 +307,65 @@ class Distro(object): "selinux_user": '--selinux-user', } - adduser_opts_flags = { + adduser_flags = { "no_user_group": '--no-user-group', "system": '--system', "no_log_init": '--no-log-init', - "no_create_home": "-M", } - redact_fields = ['passwd'] + redact_opts = ['passwd'] + + # Check the values and create the command + for key, val in kwargs.iteritems(): + + if key in adduser_opts and val and isinstance(val, str): + adduser_cmd.extend([adduser_opts[key], val]) - # Now check the value and create the command - for option in kwargs: - value = kwargs[option] - if option in adduser_opts and value \ - and isinstance(value, str): - adduser_cmd.extend([adduser_opts[option], value]) - # Redact certain fields from the logs - if option in redact_fields: - x_adduser_cmd.extend([adduser_opts[option], 'REDACTED']) - else: - x_adduser_cmd.extend([adduser_opts[option], value]) - elif option in adduser_opts_flags and value: - adduser_cmd.append(adduser_opts_flags[option]) # Redact certain fields from the logs - if option in redact_fields: - x_adduser_cmd.append('REDACTED') + if key in redact_opts: + log_adduser_cmd.extend([adduser_opts[key], 'REDACTED']) else: - x_adduser_cmd.append(adduser_opts_flags[option]) + log_adduser_cmd.extend([adduser_opts[key], val]) - # Default to creating home directory unless otherwise directed - # Also, we do not create home directories for system users. - if "no_create_home" not in kwargs and "system" not in kwargs: - adduser_cmd.append('-m') + elif key in adduser_flags and val: + adduser_cmd.append(adduser_flags[key]) + log_adduser_cmd.append(adduser_flags[key]) - # Create the user - if util.is_user(name): - LOG.warn("User %s already exists, skipping." % name) + # Don't create the home directory if directed so or if the user is a + # system user + if 'no_create_home' in kwargs or 'system' in kwargs: + adduser_cmd.append('-M') + log_adduser_cmd.append('-M') else: - LOG.debug("Adding user named %s", name) - try: - util.subp(adduser_cmd, logstring=x_adduser_cmd) - except Exception as e: - util.logexc(LOG, "Failed to create user %s", name) - raise e + adduser_cmd.append('-m') + log_adduser_cmd.append('-m') + + # Run the command + LOG.debug("Adding user %s", name) + try: + util.subp(adduser_cmd, logstring=log_adduser_cmd) + except Exception as e: + util.logexc(LOG, "Failed to create user %s", name) + raise e + + def create_user(self, name, **kwargs): + """ + Creates users for the system using the GNU passwd tools. This + will work on an GNU system. This should be overriden on + distros where useradd is not desirable or not available. + """ + + # Add the user + self.add_user(name, **kwargs) # Set password if plain-text password provided - if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']: + if 'plain_text_passwd' in kwargs: self.set_passwd(name, kwargs['plain_text_passwd']) # Default locking down the account. 'lock_passwd' defaults to True. # lock account unless lock_password is False. if kwargs.get('lock_passwd', True): - try: - util.subp(['passwd', '--lock', name]) - except Exception as e: - util.logexc(LOG, "Failed to disable password logins for " - "user %s", name) - raise e + self.lock_passwd(name) # Configure sudo access if 'sudo' in kwargs: @@ -375,6 +378,16 @@ class Distro(object): return True + def lock_passwd(self, name): + """ + Lock the password of a user, i.e., disable password logins + """ + try: + util.subp(['passwd', '--lock', name]) + except Exception as e: + util.logexc(LOG, 'Failed to disable password for user %s', name) + raise e + def set_passwd(self, user, passwd, hashed=False): pass_string = '%s:%s' % (user, passwd) cmd = ['chpasswd'] -- cgit v1.2.3 From ff4e9912bde07fb88de90f2dda2e8657ef779679 Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Tue, 25 Jun 2013 08:49:35 +0200 Subject: Move some RHEL distro methods to their own new file So that they can be used by other handlers. --- cloudinit/distros/rhel.py | 165 +++---------------------------------- cloudinit/distros/rhel_util.py | 179 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 153 deletions(-) create mode 100644 cloudinit/distros/rhel_util.py (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 0727ecd1..4fd4239c 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -3,10 +3,12 @@ # Copyright (C) 2012 Canonical Ltd. # Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P. # Copyright (C) 2012 Yahoo! Inc. +# Copyright (C) 2013 SUSE LLC # # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow +# Author: Robert Schweikert # # 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 @@ -23,14 +25,11 @@ import os from cloudinit import distros - -from cloudinit.distros.parsers.resolv_conf import ResolvConf -from cloudinit.distros.parsers.sys_conf import SysConf - from cloudinit import helpers from cloudinit import log as logging from cloudinit import util +from cloudinit.distros import rhel_util from cloudinit.settings import PER_INSTANCE LOG = logging.getLogger(__name__) @@ -67,32 +66,9 @@ class Distro(distros.Distro): def install_packages(self, pkglist): self.package_command('install', pkgs=pkglist) - def _adjust_resolve(self, dns_servers, search_servers): - try: - r_conf = ResolvConf(util.load_file(self.resolve_conf_fn)) - r_conf.parse() - except IOError: - util.logexc(LOG, "Failed at parsing %s reverting to an empty " - "instance", self.resolve_conf_fn) - r_conf = ResolvConf('') - r_conf.parse() - if dns_servers: - for s in dns_servers: - try: - r_conf.add_nameserver(s) - except ValueError: - util.logexc(LOG, "Failed at adding nameserver %s", s) - if search_servers: - for s in search_servers: - try: - r_conf.add_search_domain(s) - except ValueError: - util.logexc(LOG, "Failed at adding search domain %s", s) - util.write_file(self.resolve_conf_fn, str(r_conf), 0644) - def _write_network(self, settings): # TODO(harlowja) fix this... since this is the ubuntu format - entries = translate_network(settings) + entries = rhel_util.translate_network(settings) LOG.debug("Translated ubuntu style network settings %s into %s", settings, entries) # Make the intermediate format as the rhel format... @@ -111,41 +87,21 @@ class Distro(distros.Distro): 'MACADDR': info.get('hwaddress'), 'ONBOOT': _make_sysconfig_bool(info.get('auto')), } - self._update_sysconfig_file(net_fn, net_cfg) + rhel_util.update_sysconfig_file(net_fn, net_cfg) if 'dns-nameservers' in info: nameservers.extend(info['dns-nameservers']) if 'dns-search' in info: searchservers.extend(info['dns-search']) if nameservers or searchservers: - self._adjust_resolve(nameservers, searchservers) + rhel_util.update_resolve_conf_file(self.resolve_conf_fn, + nameservers, searchservers) if dev_names: net_cfg = { 'NETWORKING': _make_sysconfig_bool(True), } - self._update_sysconfig_file(self.network_conf_fn, net_cfg) + rhel_util.update_sysconfig_file(self.network_conf_fn, net_cfg) return dev_names - def _update_sysconfig_file(self, fn, adjustments, allow_empty=False): - if not adjustments: - return - (exists, contents) = self._read_conf(fn) - updated_am = 0 - for (k, v) in adjustments.items(): - if v is None: - continue - v = str(v) - if len(v) == 0 and not allow_empty: - continue - contents[k] = v - updated_am += 1 - if updated_am: - lines = [ - str(contents), - ] - if not exists: - lines.insert(0, util.make_header()) - util.write_file(fn, "\n".join(lines) + "\n", 0644) - def _dist_uses_systemd(self): # Fedora 18 and RHEL 7 were the first adopters in their series (dist, vers) = util.system_info()['dist'][:2] @@ -164,7 +120,7 @@ class Distro(distros.Distro): locale_cfg = { 'LANG': locale, } - self._update_sysconfig_file(out_fn, locale_cfg) + rhel_util.update_sysconfig_file(out_fn, locale_cfg) def _write_hostname(self, hostname, out_fn): if self._dist_uses_systemd(): @@ -173,7 +129,7 @@ class Distro(distros.Distro): host_cfg = { 'HOSTNAME': hostname, } - self._update_sysconfig_file(out_fn, host_cfg) + rhel_util.update_sysconfig_file(out_fn, host_cfg) def _select_hostname(self, hostname, fqdn): # See: http://bit.ly/TwitgL @@ -197,22 +153,12 @@ class Distro(distros.Distro): else: return default else: - (_exists, contents) = self._read_conf(filename) + (_exists, contents) = rhel_util.read_sysconfig_file(filename) if 'HOSTNAME' in contents: return contents['HOSTNAME'] else: return default - def _read_conf(self, fn): - exists = False - try: - contents = util.load_file(fn).splitlines() - exists = True - except IOError: - contents = [] - return (exists, - SysConf(contents)) - def _bring_up_interfaces(self, device_names): if device_names and 'all' in device_names: raise RuntimeError(('Distro %s can not translate ' @@ -236,7 +182,7 @@ class Distro(distros.Distro): clock_cfg = { 'ZONE': str(tz), } - self._update_sysconfig_file(self.clock_conf_fn, clock_cfg) + rhel_util.update_sysconfig_file(self.clock_conf_fn, clock_cfg) # This ensures that the correct tz will be used for the system util.copy(tz_file, self.tz_local_fn) @@ -271,90 +217,3 @@ class Distro(distros.Distro): def update_package_sources(self): self._runner.run("update-sources", self.package_command, ["makecache"], freq=PER_INSTANCE) - - -# 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(harlowja) 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 - # Name server info provided?? - if 'dns-nameservers' in info: - iface_info['dns-nameservers'] = info['dns-nameservers'].split() - # Name server search info provided?? - if 'dns-search' in info: - iface_info['dns-search'] = info['dns-search'].split() - # 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 diff --git a/cloudinit/distros/rhel_util.py b/cloudinit/distros/rhel_util.py new file mode 100644 index 00000000..504b5d2c --- /dev/null +++ b/cloudinit/distros/rhel_util.py @@ -0,0 +1,179 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2012 Canonical Ltd. +# Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P. +# Copyright (C) 2012 Yahoo! Inc. +# Copyright (C) 2013 SUSE LLC +# +# Author: Scott Moser +# Author: Juerg Haefliger +# Author: Joshua Harlow +# Author: Robert Schweikert +# +# 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.parsers.resolv_conf import ResolvConf +from cloudinit.distros.parsers.sys_conf import SysConf + +from cloudinit import log as logging +from cloudinit import util + +LOG = logging.getLogger(__name__) + + +# This is a util function to translate Debian based distro interface blobs as +# given in /etc/network/interfaces to an equivalent format for distributions +# that use ifcfg-* style (Red Hat and SUSE). +# TODO(harlowja) 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 + # Name server info provided?? + if 'dns-nameservers' in info: + iface_info['dns-nameservers'] = info['dns-nameservers'].split() + # Name server search info provided?? + if 'dns-search' in info: + iface_info['dns-search'] = info['dns-search'].split() + # 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 + + +# Helper function to update a RHEL/SUSE /etc/sysconfig/* file +def update_sysconfig_file(fn, adjustments, allow_empty=False): + if not adjustments: + return + (exists, contents) = read_sysconfig_file(fn) + updated_am = 0 + for (k, v) in adjustments.items(): + if v is None: + continue + v = str(v) + if len(v) == 0 and not allow_empty: + continue + contents[k] = v + updated_am += 1 + if updated_am: + lines = [ + str(contents), + ] + if not exists: + lines.insert(0, util.make_header()) + util.write_file(fn, "\n".join(lines) + "\n", 0644) + + +# Helper function to read a RHEL/SUSE /etc/sysconfig/* file +def read_sysconfig_file(fn): + exists = False + try: + contents = util.load_file(fn).splitlines() + exists = True + except IOError: + contents = [] + return (exists, SysConf(contents)) + + +# Helper function to update RHEL/SUSE /etc/resolv.conf +def update_resolve_conf_file(fn, dns_servers, search_servers): + try: + r_conf = ResolvConf(util.load_file(fn)) + r_conf.parse() + except IOError: + util.logexc(LOG, "Failed at parsing %s reverting to an empty " + "instance", fn) + r_conf = ResolvConf('') + r_conf.parse() + if dns_servers: + for s in dns_servers: + try: + r_conf.add_nameserver(s) + except ValueError: + util.logexc(LOG, "Failed at adding nameserver %s", s) + if search_servers: + for s in search_servers: + try: + r_conf.add_search_domain(s) + except ValueError: + util.logexc(LOG, "Failed at adding search domain %s", s) + util.write_file(fn, str(r_conf), 0644) -- cgit v1.2.3 From e05e747a7293f991843ca4b7ba5e0736cb5f043f Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Tue, 25 Jun 2013 08:51:21 +0200 Subject: Add a SLES distro handler --- cloudinit/config/cc_resolv_conf.py | 6 +- cloudinit/distros/__init__.py | 5 +- cloudinit/distros/sles.py | 226 +++++++++++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 cloudinit/distros/sles.py (limited to 'cloudinit/distros') diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py index 8a460f7e..d4fead12 100644 --- a/cloudinit/config/cc_resolv_conf.py +++ b/cloudinit/config/cc_resolv_conf.py @@ -1,8 +1,12 @@ # vi: ts=4 expandtab # # Copyright (C) 2013 Craig Tracey +# Copyright (C) 2013 SUSE LLC +# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. # # Author: Craig Tracey +# Author: Robert Schweikert +# Author: Juerg Haefliger # # 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 @@ -53,7 +57,7 @@ from cloudinit import util frequency = PER_INSTANCE -distros = ['fedora', 'rhel'] +distros = ['fedora', 'rhel', 'sles'] def generate_resolv_conf(cloud, log, params): diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index c5990960..ef5db86b 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -3,11 +3,13 @@ # Copyright (C) 2012 Canonical Ltd. # Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P. # Copyright (C) 2012 Yahoo! Inc. +# Copyright (C) 2013 SUSE LLC # # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow # Author: Ben Howard +# Author: Robert Schweikert # # 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 @@ -38,7 +40,8 @@ from cloudinit.distros.parsers import hosts OSFAMILIES = { 'debian': ['debian', 'ubuntu'], - 'redhat': ['fedora', 'rhel'] + 'redhat': ['fedora', 'rhel'], + 'suse': ['sles'] } LOG = logging.getLogger(__name__) diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py new file mode 100644 index 00000000..e068b4bd --- /dev/null +++ b/cloudinit/distros/sles.py @@ -0,0 +1,226 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2013 SUSE LLC +# Copyright (C) 2013 Hewlett-Packard Development Company, L.P. +# +# Author: Robert Schweikert +# Author: Juerg Haefliger +# +# Leaning very heavily on the RHEL and Debian implementation +# +# 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.distros.parsers.hostname import HostnameConf + +from cloudinit import helpers +from cloudinit import log as logging +from cloudinit import util + +from cloudinit.distros import rhel_util +from cloudinit.settings import PER_INSTANCE + +LOG = logging.getLogger(__name__) + + +class Distro(distros.Distro): + clock_conf_fn = '/etc/sysconfig/clock' + locale_conf_fn = '/etc/sysconfig/language' + network_conf_fn = '/etc/sysconfig/network' + hostname_conf_fn = '/etc/HOSTNAME' + network_script_tpl = '/etc/sysconfig/network/ifcfg-%s' + resolve_conf_fn = '/etc/resolv.conf' + tz_local_fn = '/etc/localtime' + tz_zone_dir = '/usr/share/zoneinfo' + + 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) + self.osfamily = 'suse' + + def install_packages(self, pkglist): + self.package_command('install', args='-l', pkgs=pkglist) + + def _write_network(self, settings): + # Convert debian settings to ifcfg format + entries = rhel_util.translate_network(settings) + LOG.debug("Translated ubuntu style network settings %s into %s", + settings, entries) + # Make the intermediate format as the suse format... + nameservers = [] + searchservers = [] + dev_names = entries.keys() + for (dev, info) in entries.iteritems(): + net_fn = self.network_script_tpl % (dev) + mode = info.get('auto') + if mode and mode.lower() == 'true': + mode = 'auto' + else: + mode = 'manual' + net_cfg = { + 'BOOTPROTO': info.get('bootproto'), + 'BROADCAST': info.get('broadcast'), + 'GATEWAY': info.get('gateway'), + 'IPADDR': info.get('address'), + 'LLADDR': info.get('hwaddress'), + 'NETMASK': info.get('netmask'), + 'STARTMODE': mode, + 'USERCONTROL': 'no' + } + if dev != 'lo': + net_cfg['ETHERDEVICE'] = dev + net_cfg['ETHTOOL_OPTIONS'] = '' + else: + net_cfg['FIREWALL'] = 'no' + rhel_util.update_sysconfig_file(net_fn, net_cfg, True) + if 'dns-nameservers' in info: + nameservers.extend(info['dns-nameservers']) + if 'dns-search' in info: + searchservers.extend(info['dns-search']) + if nameservers or searchservers: + rhel_util.update_resolve_conf_file(self.resolve_conf_fn, + nameservers, searchservers) + return dev_names + + def apply_locale(self, locale, out_fn=None): + if not out_fn: + out_fn = self.locale_conf_fn + locale_cfg = { + 'RC_LANG': locale, + } + rhel_util.update_sysconfig_file(out_fn, locale_cfg) + + def _write_hostname(self, hostname, out_fn): + conf = None + try: + # Try to update the previous one + # so lets see if we can read it first. + conf = self._read_hostname_conf(out_fn) + except IOError: + pass + if not conf: + conf = HostnameConf('') + conf.set_hostname(hostname) + util.write_file(out_fn, str(conf), 0644) + + 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 _read_system_hostname(self): + host_fn = self.hostname_conf_fn + return (host_fn, self._read_hostname(host_fn)) + + def _read_hostname_conf(self, filename): + conf = HostnameConf(util.load_file(filename)) + conf.parse() + return conf + + def _read_hostname(self, filename, default=None): + hostname = None + try: + conf = self._read_hostname_conf(filename) + hostname = conf.hostname + except IOError: + pass + if not hostname: + return default + return hostname + + def _bring_up_interfaces(self, device_names): + if device_names and 'all' in device_names: + raise RuntimeError(('Distro %s can not translate ' + 'the device name "all"') % (self.name)) + return distros.Distro._bring_up_interfaces(self, device_names) + + def set_timezone(self, tz): + # TODO(harlowja): move this code into + # the parent distro... + tz_file = os.path.join(self.tz_zone_dir, str(tz)) + if not os.path.isfile(tz_file): + raise RuntimeError(("Invalid timezone %s," + " no file found at %s") % (tz, tz_file)) + # Adjust the sysconfig clock zone setting + clock_cfg = { + 'TIMEZONE': str(tz), + } + rhel_util.update_sysconfig_file(self.clock_conf_fn, clock_cfg) + # This ensures that the correct tz will be used for the system + util.copy(tz_file, self.tz_local_fn) + + def package_command(self, command, args=None, pkgs=None): + if pkgs is None: + pkgs = [] + + cmd = ['zypper'] + # No user interaction possible, enable non-interactive mode + cmd.append('-t') + # Do not check the keys, we assume that the initial repos configured + # in the image can be trusted + cmd.append('--no-gpg-checks') + + # Comand is the operation, such as install + cmd.append(command) + + # args are the arguments to the command, not global options + if args and isinstance(args, str): + cmd.append(args) + elif args and isinstance(args, list): + cmd.extend(args) + + pkglist = util.expand_package_list('%s-%s', pkgs) + cmd.extend(pkglist) + + # 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, + ['refresh'], freq=PER_INSTANCE) + + # Copied from parent class and modified to use short option names since + # the SLES command doesn't support long names (yet). This method can be + # removed when SLES finally catches up. + def lock_passwd(self, name): + """ + Lock the password of a user, i.e., disable password logins + """ + try: + util.subp(['passwd', '-l', name]) + except Exception as e: + util.logexc(LOG, 'Failed to disable password for user %s', name) + raise e + + # Copied from parent class and modified to use short option names since + # the SLES command doesn't support long names (yet). This method can be + # removed when SLES finally catches up. + def set_passwd(self, user, passwd, hashed=False): + pass_string = '%s:%s' % (user, passwd) + cmd = ['chpasswd'] + if hashed: + cmd.append('-e') + try: + util.subp(cmd, pass_string, logstring="chpasswd for %s" % user) + except Exception as e: + util.logexc(LOG, "Failed to set password for %s", user) + raise e + return True -- cgit v1.2.3 From c8eb622ae0c3f9fab2b25112aa87a2dbf39788db Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Wed, 26 Jun 2013 09:51:59 +0200 Subject: Use short option names for passwd utilities SLES 11 doesn't support long option names for the passwd utilities. Use the short option names in the parent distro class and remove the custom SLES methods. --- cloudinit/distros/__init__.py | 10 ++++++++-- cloudinit/distros/sles.py | 28 ---------------------------- 2 files changed, 8 insertions(+), 30 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index ef5db86b..f8727fc1 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -386,7 +386,10 @@ class Distro(object): Lock the password of a user, i.e., disable password logins """ try: - util.subp(['passwd', '--lock', name]) + # Need to use the short option name '-l' instead of '--lock' + # (which would be more descriptive) since SLES 11 doesn't know + # about long names. + util.subp(['passwd', '-l', name]) except Exception as e: util.logexc(LOG, 'Failed to disable password for user %s', name) raise e @@ -396,7 +399,10 @@ class Distro(object): cmd = ['chpasswd'] if hashed: - cmd.append('--encrypted') + # Need to use the short option name '-e' instead of '--encrypted' + # (which would be more descriptive) since SLES 11 doesn't know + # about long names. + cmd.append('-e') try: util.subp(cmd, pass_string, logstring="chpasswd for %s" % user) diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py index e068b4bd..95bc411a 100644 --- a/cloudinit/distros/sles.py +++ b/cloudinit/distros/sles.py @@ -196,31 +196,3 @@ class Distro(distros.Distro): def update_package_sources(self): self._runner.run("update-sources", self.package_command, ['refresh'], freq=PER_INSTANCE) - - # Copied from parent class and modified to use short option names since - # the SLES command doesn't support long names (yet). This method can be - # removed when SLES finally catches up. - def lock_passwd(self, name): - """ - Lock the password of a user, i.e., disable password logins - """ - try: - util.subp(['passwd', '-l', name]) - except Exception as e: - util.logexc(LOG, 'Failed to disable password for user %s', name) - raise e - - # Copied from parent class and modified to use short option names since - # the SLES command doesn't support long names (yet). This method can be - # removed when SLES finally catches up. - def set_passwd(self, user, passwd, hashed=False): - pass_string = '%s:%s' % (user, passwd) - cmd = ['chpasswd'] - if hashed: - cmd.append('-e') - try: - util.subp(cmd, pass_string, logstring="chpasswd for %s" % user) - except Exception as e: - util.logexc(LOG, "Failed to set password for %s", user) - raise e - return True -- cgit v1.2.3 From 467d45906c4575c1d231af268c47e812356657b8 Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Thu, 27 Jun 2013 13:50:33 +0200 Subject: Remove 'Copyright SUSE' from the headers Per discussion with Robert @ SUSE since he can't sign the CCA. --- cloudinit/config/cc_resolv_conf.py | 2 -- cloudinit/distros/__init__.py | 2 -- cloudinit/distros/rhel.py | 2 -- cloudinit/distros/rhel_util.py | 2 -- cloudinit/distros/sles.py | 2 -- 5 files changed, 10 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py index d4fead12..879b62b1 100644 --- a/cloudinit/config/cc_resolv_conf.py +++ b/cloudinit/config/cc_resolv_conf.py @@ -1,11 +1,9 @@ # vi: ts=4 expandtab # # Copyright (C) 2013 Craig Tracey -# Copyright (C) 2013 SUSE LLC # Copyright (C) 2013 Hewlett-Packard Development Company, L.P. # # Author: Craig Tracey -# Author: Robert Schweikert # Author: Juerg Haefliger # # This program is free software: you can redistribute it and/or modify diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index f8727fc1..cda2c6af 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -3,13 +3,11 @@ # Copyright (C) 2012 Canonical Ltd. # Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P. # Copyright (C) 2012 Yahoo! Inc. -# Copyright (C) 2013 SUSE LLC # # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow # Author: Ben Howard -# Author: Robert Schweikert # # 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 diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index 4fd4239c..a022ca60 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -3,12 +3,10 @@ # Copyright (C) 2012 Canonical Ltd. # Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P. # Copyright (C) 2012 Yahoo! Inc. -# Copyright (C) 2013 SUSE LLC # # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow -# Author: Robert Schweikert # # 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 diff --git a/cloudinit/distros/rhel_util.py b/cloudinit/distros/rhel_util.py index 504b5d2c..1aba58b8 100644 --- a/cloudinit/distros/rhel_util.py +++ b/cloudinit/distros/rhel_util.py @@ -3,12 +3,10 @@ # Copyright (C) 2012 Canonical Ltd. # Copyright (C) 2012, 2013 Hewlett-Packard Development Company, L.P. # Copyright (C) 2012 Yahoo! Inc. -# Copyright (C) 2013 SUSE LLC # # Author: Scott Moser # Author: Juerg Haefliger # Author: Joshua Harlow -# Author: Robert Schweikert # # 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 diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py index 95bc411a..d0c15feb 100644 --- a/cloudinit/distros/sles.py +++ b/cloudinit/distros/sles.py @@ -1,9 +1,7 @@ # vi: ts=4 expandtab # -# Copyright (C) 2013 SUSE LLC # Copyright (C) 2013 Hewlett-Packard Development Company, L.P. # -# Author: Robert Schweikert # Author: Juerg Haefliger # # Leaning very heavily on the RHEL and Debian implementation -- cgit v1.2.3 From 0c7c5b999e09acf8795c6db2f1b50a801c0eae8f Mon Sep 17 00:00:00 2001 From: Juerg Haefliger Date: Thu, 27 Jun 2013 14:14:51 +0200 Subject: Fix SLES zypper command usage --- cloudinit/distros/sles.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'cloudinit/distros') diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py index d0c15feb..904e931a 100644 --- a/cloudinit/distros/sles.py +++ b/cloudinit/distros/sles.py @@ -171,10 +171,7 @@ class Distro(distros.Distro): cmd = ['zypper'] # No user interaction possible, enable non-interactive mode - cmd.append('-t') - # Do not check the keys, we assume that the initial repos configured - # in the image can be trusted - cmd.append('--no-gpg-checks') + cmd.append('--non-interactive') # Comand is the operation, such as install cmd.append(command) -- cgit v1.2.3 From 67162bca0c49d415f92aefa22972fd3ffe179da6 Mon Sep 17 00:00:00 2001 From: Scott Moser Date: Wed, 17 Jul 2013 15:35:01 -0400 Subject: plain text password of '' or None should not trigger setting --- 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 cda2c6af..249e1b19 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -359,8 +359,8 @@ class Distro(object): # Add the user self.add_user(name, **kwargs) - # Set password if plain-text password provided - if 'plain_text_passwd' in kwargs: + # Set password if plain-text password provided and non-empty + if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']: self.set_passwd(name, kwargs['plain_text_passwd']) # Default locking down the account. 'lock_passwd' defaults to True. -- cgit v1.2.3