# vi: ts=4 expandtab # # 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 time from cloudinit import distros from cloudinit import helpers from cloudinit import log as logging from cloudinit import util from cloudinit import ssh_util from cloudinit.distros import net_util from cloudinit.distros import rhel_util from cloudinit.distros import aix_util from cloudinit.settings import PER_INSTANCE from cloudinit.distros.parsers.hostname import HostnameConf LOG = logging.getLogger(__name__) class Distro(distros.Distro): hostname_conf_fn = "/etc/hosts" resolve_conf_fn = "/etc/resolv.conf" 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 = 'aix' def install_packages(self, pkglist): self.package_command('install', pkgs=pkglist) def apply_network(self, settings, bring_up=True): # Write it out dev_names = self._write_network(settings) # Now try to bring them up if bring_up: self._bring_down_interfaces(dev_names) return self._bring_up_interfaces(dev_names) return False def _write_network(self, settings): entries = net_util.translate_network(settings) aix_util.remove_resolve_conf_file(self.resolve_conf_fn) print("Translated ubuntu style network settings %s into %s" % (settings, entries)) # Make the intermediate format as the rhel format... nameservers = [] searchservers = [] dev_names = entries.keys() create_dhcp_file = True run_dhcpcd = False run_autoconf6 = False ipv6_interface = None # First, make sure the services starts out uncommented in /etc/rc.tcpip aix_util.disable_dhcpcd() aix_util.disable_ndpd_host() aix_util.disable_autoconf6() for (dev, info) in entries.iteritems(): run_cmd = 0 chdev_cmd = ['/usr/sbin/chdev'] log_chdev_cmd = ['/usr/sbin/chdev'] if dev not in 'lo': aix_dev = aix_util.translate_devname(dev) if info.get('bootproto') == 'dhcp': aix_util.config_dhcp(aix_dev, info, create_dhcp_file) create_dhcp_file = False run_dhcpcd = True else: chdev_cmd.extend(['-l', aix_dev]) log_chdev_cmd.extend(['-l', aix_dev]) if info['ipv6'] == True: chdev_opts = { "address" : '-anetaddr6=', "netmask" : '-aprefixlen=', } run_cmd = 1 run_autoconf6 = True if ipv6_interface is None: ipv6_interface = aix_dev else: ipv6_interface = "any" if info['ipv4'] == True: chdev_opts = { "address" : '-anetaddr=', "netmask" : '-anetmask=', } run_cmd = 1 for (key, val) in info.iteritems(): if key in chdev_opts and val and isinstance(val, basestring): chdev_cmd.append(chdev_opts[key] + val) log_chdev_cmd.append(chdev_opts[key] + val) if run_cmd: try: util.subp(chdev_cmd, logstring=log_chdev_cmd) time.sleep(2) except Exception as e: raise e if 'mtu' in info: if info['mtu'] > 1500: util.subp(["/etc/ifconfig", aix_dev, "down", "detach"], capture=False, rcs=[0, 1]) time.sleep(2) aix_adapter = aix_util.logical_adpt_name(aix_dev) util.subp(["/usr/sbin/chdev", "-l", aix_adapter, "-ajumbo_frames=yes"], capture=False, rcs=[0, 1]) time.sleep(2) util.subp(["/usr/sbin/chdev", "-l", aix_dev, "-amtu=" + info['mtu']], capture=False, rcs=[0, 1]) time.sleep(2) if aix_dev == "en0": if info['ipv6'] == True: aix_util.add_route("ipv6", info.get('gateway')) if info['ipv4'] == True: aix_util.add_route("ipv4", info.get('gateway')) if 'dns-nameservers' in info: nameservers.extend(info['dns-nameservers']) if 'dns-search' in info: searchservers.extend(info['dns-search']) if run_dhcpcd: aix_util.enable_dhcpcd() if run_autoconf6: aix_util.enable_ndpd_host() aix_util.enable_autoconf6(ipv6_interface) if ((nameservers and len(nameservers) > 0) or ( searchservers and len(searchservers) > 0)): aix_util.update_resolve_conf_file(self.resolve_conf_fn, nameservers, searchservers) return dev_names def apply_locale(self, locale, out_fn=None): util.subp(['/usr/bin/chlang', '-M', str(locale)]) def _write_hostname(self, hostname, out_fn): # Permanently change the hostname for inet0 device in the ODM util.subp(['/usr/sbin/chdev', '-l', 'inet0', '-a', 'hostname=' + str(hostname)]) shortname = hostname.split('.')[0] # Change the node for the uname process util.subp(['/usr/bin/uname', '-S', str(shortname)[0:32]]) 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(self, filename, default=None): (out, _err) = util.subp(['/usr/bin/hostname']) if len(out): return out else: return default def _bring_up_interface(self, device_name): if device_name in 'lo': return True cmd = ['/usr/sbin/chdev', '-l', aix_util.translate_devname(device_name), '-a', 'state=up'] LOG.debug("Attempting to run bring up interface %s using command %s", device_name, cmd) try: (_out, err) = util.subp(cmd) time.sleep(1) if len(err): LOG.warn("Running %s resulted in stderr output: %s", cmd, err) return True except util.ProcessExecutionError: util.logexc(LOG, "Running interface command %s failed", cmd) return False 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)) for d in device_names: if not self._bring_up_interface(d): return False return True def _bring_down_interface(self, device_name): if device_name in 'lo': return True interface = aix_util.translate_devname(device_name) if aix_util.get_if_attr(interface, "state") == "down": time.sleep(1) return True else: cmd = ['/usr/sbin/chdev', '-l', interface, '-a', 'state=down'] LOG.debug("Attempting to run bring down interface %s using command %s", device_name, cmd) try: (_out, err) = util.subp(cmd, rcs=[0, 1]) time.sleep(1) if len(err): LOG.warn("Running %s resulted in stderr output: %s", cmd, err) return True except util.ProcessExecutionError: util.logexc(LOG, "Running interface command %s failed", cmd) return False def _bring_down_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)) am_failed = 0 for d in device_names: if not self._bring_down_interface(d): am_failed += 1 if am_failed == 0: return True return False def set_timezone(self, tz): cmd = ['/usr/bin/chtz', tz] util.subp(cmd) def package_command(self, command, args=None, pkgs=None): if pkgs is None: pkgs = [] 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") if args and isinstance(args, str): cmd.append(args) elif args and isinstance(args, list): cmd.extend(args) cmd.append(command) 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, ["makecache"], freq=PER_INSTANCE) def add_user(self, name, **kwargs): if util.is_user(name): LOG.info("User %s already exists, skipping.", name) return False adduser_cmd = ['/usr/sbin/useradd'] log_adduser_cmd = ['/usr/sbin/useradd'] adduser_opts = { "homedir": '-d', "gecos": '-c', "primary_group": '-g', "groups": '-G', "shell": '-s', "expiredate" : '-e', } redact_opts = ['passwd'] for key, val in kwargs.iteritems(): if key in adduser_opts and val and isinstance(val, basestring): adduser_cmd.extend([adduser_opts[key], val]) # Redact certain fields from the logs if key in redact_opts: log_adduser_cmd.extend([adduser_opts[key], 'REDACTED']) else: log_adduser_cmd.extend([adduser_opts[key], val]) if 'no_create_home' in kwargs or 'system' in kwargs: adduser_cmd.append('-d/nonexistent') log_adduser_cmd.append('-d/nonexistent') else: adduser_cmd.append('-m') log_adduser_cmd.append('-m') adduser_cmd.append(name) log_adduser_cmd.append(name) # 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 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. # lock account unless lock_password is False. if kwargs.get('lock_passwd', True): self.lock_passwd(name) # Configure sudo access if 'sudo' in kwargs: self.write_sudo_rules(name, kwargs['sudo']) # Import SSH keys if 'ssh_authorized_keys' in kwargs: keys = set(kwargs['ssh_authorized_keys']) or [] ssh_util.setup_user_keys(keys, name, options=None) return True def lock_passwd(self, name): """ Lock the password of a user, i.e., disable password logins """ try: # 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(['/usr/bin/chuser', 'account_locked=true', name]) except Exception as e: util.logexc(LOG, 'Failed to disable password for user %s', name) raise e def create_group(self, name, members): group_add_cmd = ['/usr/bin/mkgroup', name] # Check if group exists, and then add it doesn't if util.is_group(name): LOG.warn("Skipping creation of existing group '%s'" % name) else: try: util.subp(group_add_cmd) LOG.info("Created new group %s" % name) except Exception: util.logexc("Failed to create group %s", name) # Add members to the group, if so defined if len(members) > 0: for member in members: if not util.is_user(member): LOG.warn("Unable to add group member '%s' to group '%s'; user does not exist.", member, name) continue util.subp(['/usr/sbin/usermod', '-G', name, member]) LOG.info("Added user '%s' to group '%s'" % (member, name))