def create_group(self, name, members=None): group_add_cmd = ['groupadd', name] if util.system_is_snappy(): group_add_cmd.append('--extrausers') if not members: members = [] # Check if group exists, and then add it doesn't if util.is_group(name): LOG.warning("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(LOG, "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.warning("Unable to add group member '%s' to group '%s'" "; user does not exist.", member, name) continue util.subp(['usermod', '-a', '-G', name, member]) LOG.info("Added user '%s' to group '%s'", member, name)
def test_id_in_os_release(self): """os-release containing ID=ubuntu-core is snappy.""" orcontent = '\n'.join(['ID=ubuntu-core', '']) root_d = self.tmp_dir() helpers.populate_dir(root_d, {'etc/os-release': orcontent}) self.reRoot(root_d) self.assertTrue(util.system_is_snappy())
def _should_configure_on_empty_apt(): # if no config was provided, should apt configuration be done? if util.system_is_snappy(): return False, "system is snappy." if not (util.which('apt-get') or util.which('apt')): return False, "no apt commands." return True, "Apt is available."
def create_group(self, name, members=None): group_add_cmd = ['groupadd', name] if util.system_is_snappy(): group_add_cmd.append('--extrausers') if not members: members = [] # Check if group exists, and then add it doesn't if util.is_group(name): LOG.warning("Skipping creation of existing group '%s'", name) else: try: subp.subp(group_add_cmd) LOG.info("Created new group %s", name) except Exception: util.logexc(LOG, "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.warning( "Unable to add group member '%s' to group '%s'" "; user does not exist.", member, name) continue subp.subp(['usermod', '-a', '-G', name, member]) LOG.info("Added user '%s' to group '%s'", member, name)
def handle(name, cfg, cloud, log, args): cfgin = cfg.get('snappy') if not cfgin: LOG.debug('No snappy config provided, skipping') return if not(util.system_is_snappy()): LOG.debug("%s: system not snappy", name) return assertions = cfgin.get('assertions', []) if len(assertions) > 0: LOG.debug('Importing user-provided snap assertions') add_assertions(assertions) # Create a snap user if requested. # Snap systems contact the store with a user's email # and extract information needed to create a local user. # A user may provide a 'system-user' assertion which includes # the required information. Using such an assertion to create # a local user requires specifying 'known: true' in the supplied # user-data. usercfg = add_snap_user(cfg=cfgin) if usercfg: cloud.distro.create_user(usercfg.get('snapuser'), **usercfg)
def handle(name, cfg, cloud, log, args): cfgin = cfg.get('snappy') if not cfgin: LOG.debug('No snappy config provided, skipping') return log.warning( 'DEPRECATION: snap_config module will be dropped in 18.3 release.' ' Use snap module instead') if not(util.system_is_snappy()): LOG.debug("%s: system not snappy", name) return assertions = cfgin.get('assertions', []) if len(assertions) > 0: LOG.debug('Importing user-provided snap assertions') add_assertions(assertions) # Create a snap user if requested. # Snap systems contact the store with a user's email # and extract information needed to create a local user. # A user may provide a 'system-user' assertion which includes # the required information. Using such an assertion to create # a local user requires specifying 'known: true' in the supplied # user-data. usercfg = add_snap_user(cfg=cfgin) if usercfg: cloud.distro.create_user(usercfg.get('snapuser'), **usercfg)
def handle(name, cfg, cloud, log, args): cfgin = cfg.get('snappy') if not cfgin: cfgin = {} mycfg = util.mergemanydict([cfgin, BUILTIN_CFG]) sys_snappy = str(mycfg.get("system_snappy", "auto")) if util.is_false(sys_snappy): LOG.debug("%s: System is not snappy. disabling", name) return if sys_snappy.lower() == "auto" and not (util.system_is_snappy()): LOG.debug("%s: 'auto' mode, and system not snappy", name) return log.warning('DEPRECATION: snappy module will be dropped in 18.3 release.' ' Use snap module instead') set_snappy_command() pkg_ops = get_package_ops(packages=mycfg['packages'], configs=mycfg['config'], fspath=mycfg['packages_dir']) fails = [] for pkg_op in pkg_ops: try: render_snap_op(**pkg_op) except Exception as e: fails.append(( pkg_op, e, )) LOG.warning("'%s' failed for '%s': %s", pkg_op['op'], pkg_op['name'], e) # Default to disabling SSH ssh_enabled = mycfg.get('ssh_enabled', "auto") # If the user has not explicitly enabled or disabled SSH, then enable it # when password SSH authentication is requested or there are SSH keys if ssh_enabled == "auto": user_ssh_keys = cloud.get_public_ssh_keys() or None password_auth_enabled = cfg.get('ssh_pwauth', False) if user_ssh_keys: LOG.debug("Enabling SSH, ssh keys found in datasource") ssh_enabled = True elif cfg.get('ssh_authorized_keys'): LOG.debug("Enabling SSH, ssh keys found in config") elif password_auth_enabled: LOG.debug("Enabling SSH, password authentication requested") ssh_enabled = True elif ssh_enabled not in (True, False): LOG.warning("Unknown value '%s' in ssh_enabled", ssh_enabled) disable_enable_ssh(ssh_enabled) if fails: raise Exception("failed to install/configure snaps")
def test_bad_content_in_os_release_no_effect(self, m_cmdline): """malformed os-release should not raise exception.""" m_cmdline.return_value = 'root=/dev/sda' orcontent = '\n'.join(['IDubuntu-core', '']) root_d = self.tmp_dir() helpers.populate_dir(root_d, {'etc/os-release': orcontent}) self.reRoot() self.assertFalse(util.system_is_snappy())
def test_system_image_config_dir_is_snappy(self, m_cmdline): """Existence of /etc/system-image/config.d indicates snappy.""" m_cmdline.return_value = 'root=/dev/sda' root_d = self.tmp_dir() helpers.populate_dir( root_d, {'etc/system-image/config.d/my.file': "_unused"}) self.reRoot(root_d) self.assertTrue(util.system_is_snappy())
def test_channel_ini_with_snappy_is_snappy(self, m_cmdline): """A Channel.ini file with 'ubuntu-core' indicates snappy.""" m_cmdline.return_value = 'root=/dev/sda' root_d = self.tmp_dir() content = '\n'.join(["[Foo]", "source = 'ubuntu-core'", ""]) helpers.populate_dir(root_d, {'etc/system-image/channel.ini': content}) self.reRoot(root_d) self.assertTrue(util.system_is_snappy())
def test_snap_core_in_cmdline_is_snappy(self, m_cmdline): """The string snap_core= in kernel cmdline indicates snappy.""" cmdline = ("BOOT_IMAGE=(loop)/kernel.img root=LABEL=writable " "snap_core=core_x1.snap snap_kernel=pc-kernel_x1.snap ro " "net.ifnames=0 init=/lib/systemd/systemd console=tty1 " "console=ttyS0 panic=-1") m_cmdline.return_value = cmdline self.assertTrue(util.system_is_snappy()) self.assertTrue(m_cmdline.call_count > 0)
def test_channel_ini_with_snappy_is_snappy(self, m_cmdline): """A Channel.ini file with 'ubuntu-core' indicates snappy.""" m_cmdline.return_value = 'root=/dev/sda' root_d = self.tmp_dir() content = '\n'.join(["[Foo]", "source = 'ubuntu-core'", ""]) helpers.populate_dir( root_d, {'etc/system-image/channel.ini': content}) self.reRoot(root_d) self.assertTrue(util.system_is_snappy())
def test_snap_core_in_cmdline_is_snappy(self, m_cmdline): """The string snap_core= in kernel cmdline indicates snappy.""" cmdline = ( "BOOT_IMAGE=(loop)/kernel.img root=LABEL=writable " "snap_core=core_x1.snap snap_kernel=pc-kernel_x1.snap ro " "net.ifnames=0 init=/lib/systemd/systemd console=tty1 " "console=ttyS0 panic=-1") m_cmdline.return_value = cmdline self.assertTrue(util.system_is_snappy()) self.assertTrue(m_cmdline.call_count > 0)
def preferred_ntp_clients(self): """The preferred ntp client is dependent on the version.""" if not self._preferred_ntp_clients: (_name, _version, codename) = util.system_info()['dist'] # Xenial cloud-init only installed ntp, UbuntuCore has timesyncd. if codename == "xenial" and not util.system_is_snappy(): self._preferred_ntp_clients = ['ntp'] else: self._preferred_ntp_clients = ( copy.deepcopy(PREFERRED_NTP_CLIENTS)) return self._preferred_ntp_clients
def ntp_installable(): """Check if we can install ntp package Ubuntu-Core systems do not have an ntp package available, so we always return False. Other systems require package managers to install the ntp package If we fail to find one of the package managers, then we cannot install ntp. """ if util.system_is_snappy(): return False if any(map(util.which, ['apt-get', 'dnf', 'yum', 'zypper'])): return True return False
def apply_apt(cfg, cloud, target): # cfg is the 'apt' top level dictionary already in 'v3' format. if not cfg: # no config was provided. If apt configuration does not seem # necessary on this system, then return. if util.system_is_snappy(): LOG.debug("Nothing to do: No apt config and running on snappy") return if not (util.which('apt-get') or util.which('apt')): LOG.debug("Nothing to do: No apt config and no apt commands") return LOG.debug("handling apt config: %s", cfg) release = util.lsb_release(target=target)['codename'] arch = util.get_architecture(target) mirrors = find_apt_mirror_info(cfg, cloud, arch=arch) LOG.debug("Apt Mirror info: %s", mirrors) if util.is_false(cfg.get('preserve_sources_list', False)): generate_sources_list(cfg, release, mirrors, cloud) rename_apt_lists(mirrors, target) try: apply_apt_config(cfg, APT_PROXY_FN, APT_CONFIG_FN) except (IOError, OSError): LOG.exception("Failed to apply proxy or apt config info:") # Process 'apt_source -> sources {dict}' if 'sources' in cfg: params = mirrors params['RELEASE'] = release params['MIRROR'] = mirrors["MIRROR"] matcher = None matchcfg = cfg.get('add_apt_repo_match', ADD_APT_REPO_MATCH) if matchcfg: matcher = re.compile(matchcfg).search add_apt_sources(cfg['sources'], cloud, target=target, template_params=params, aa_repo_match=matcher)
def test_nothing_found_is_not_snappy(self, m_cmdline): """If no positive identification, then not snappy.""" m_cmdline.return_value = 'root=/dev/sda' self.reRoot() self.assertFalse(util.system_is_snappy()) self.assertTrue(m_cmdline.call_count > 0)
def add_user(self, name, **kwargs): """ Add a user to the system using standard GNU tools """ if util.is_user(name): LOG.info("User %s already exists, skipping." % name) return if 'create_groups' in kwargs: create_groups = kwargs.pop('create_groups') else: create_groups = True adduser_cmd = ['useradd', name] log_adduser_cmd = ['useradd', name] if util.system_is_snappy(): adduser_cmd.append('--extrausers') log_adduser_cmd.append('--extrausers') # Since we are creating users, we want to carefully validate the # 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', "uid": '--uid', "groups": '--groups', "passwd": '--password', "shell": '--shell', "expiredate": '--expiredate', "inactive": '--inactive', "selinux_user": '******', } adduser_flags = { "no_user_group": '--no-user-group', "system": '--system', "no_log_init": '--no-log-init', } redact_opts = ['passwd'] # support kwargs having groups=[list] or groups="g1,g2" groups = kwargs.get('groups') if groups: if isinstance(groups, (list, tuple)): # kwargs.items loop below wants a comma delimeted string # that can go right through to the command. kwargs['groups'] = ",".join(groups) else: groups = groups.split(",") primary_group = kwargs.get('primary_group') if primary_group: groups.append(primary_group) if create_groups and groups: for group in groups: if not util.is_group(group): self.create_group(group) LOG.debug("created group %s for user %s", name, group) # Check the values and create the command for key, val in kwargs.items(): if key in adduser_opts and val and isinstance(val, str): 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]) elif key in adduser_flags and val: adduser_cmd.append(adduser_flags[key]) log_adduser_cmd.append(adduser_flags[key]) # 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: 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 add_user(self, name, **kwargs): """ Add a user to the system using standard GNU tools """ # XXX need to make add_user idempotent somehow as we # still want to add groups or modify SSH keys on pre-existing # users in the image. if util.is_user(name): LOG.info("User %s already exists, skipping.", name) return if 'create_groups' in kwargs: create_groups = kwargs.pop('create_groups') else: create_groups = True useradd_cmd = ['useradd', name] log_useradd_cmd = ['useradd', name] if util.system_is_snappy(): useradd_cmd.append('--extrausers') log_useradd_cmd.append('--extrausers') # Since we are creating users, we want to carefully validate the # inputs. If something goes wrong, we can end up with a system # that nobody can login to. useradd_opts = { "gecos": '--comment', "homedir": '--home', "primary_group": '--gid', "uid": '--uid', "groups": '--groups', "passwd": '--password', "shell": '--shell', "expiredate": '--expiredate', "inactive": '--inactive', "selinux_user": '******', } useradd_flags = { "no_user_group": '--no-user-group', "system": '--system', "no_log_init": '--no-log-init', } redact_opts = ['passwd'] # support kwargs having groups=[list] or groups="g1,g2" groups = kwargs.get('groups') if groups: if isinstance(groups, str): groups = groups.split(",") # remove any white spaces in group names, most likely # that came in as a string like: groups: group1, group2 groups = [g.strip() for g in groups] # kwargs.items loop below wants a comma delimeted string # that can go right through to the command. kwargs['groups'] = ",".join(groups) primary_group = kwargs.get('primary_group') if primary_group: groups.append(primary_group) if create_groups and groups: for group in groups: if not util.is_group(group): self.create_group(group) LOG.debug("created group '%s' for user '%s'", group, name) # Check the values and create the command for key, val in sorted(kwargs.items()): if key in useradd_opts and val and isinstance(val, str): useradd_cmd.extend([useradd_opts[key], val]) # Redact certain fields from the logs if key in redact_opts: log_useradd_cmd.extend([useradd_opts[key], 'REDACTED']) else: log_useradd_cmd.extend([useradd_opts[key], val]) elif key in useradd_flags and val: useradd_cmd.append(useradd_flags[key]) log_useradd_cmd.append(useradd_flags[key]) # Don't create the home directory if directed so or if the user is a # system user if kwargs.get('no_create_home') or kwargs.get('system'): useradd_cmd.append('-M') log_useradd_cmd.append('-M') else: useradd_cmd.append('-m') log_useradd_cmd.append('-m') # Run the command LOG.debug("Adding user %s", name) try: subp.subp(useradd_cmd, logstring=log_useradd_cmd) except Exception as e: util.logexc(LOG, "Failed to create user %s", name) raise e
def add_user(self, name, **kwargs): """ Add a user to the system using standard GNU tools """ # XXX need to make add_user idempotent somehow as we # still want to add groups or modify ssh keys on pre-existing # users in the image. if util.is_user(name): LOG.info("User %s already exists, skipping.", name) return if 'create_groups' in kwargs: create_groups = kwargs.pop('create_groups') else: create_groups = True adduser_cmd = ['useradd', name] log_adduser_cmd = ['useradd', name] if util.system_is_snappy(): adduser_cmd.append('--extrausers') log_adduser_cmd.append('--extrausers') # Since we are creating users, we want to carefully validate the # 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', "uid": '--uid', "groups": '--groups', "passwd": '--password', "shell": '--shell', "expiredate": '--expiredate', "inactive": '--inactive', "selinux_user": '******', } adduser_flags = { "no_user_group": '--no-user-group', "system": '--system', "no_log_init": '--no-log-init', } redact_opts = ['passwd'] # support kwargs having groups=[list] or groups="g1,g2" groups = kwargs.get('groups') if groups: if isinstance(groups, six.string_types): groups = groups.split(",") # remove any white spaces in group names, most likely # that came in as a string like: groups: group1, group2 groups = [g.strip() for g in groups] # kwargs.items loop below wants a comma delimeted string # that can go right through to the command. kwargs['groups'] = ",".join(groups) primary_group = kwargs.get('primary_group') if primary_group: groups.append(primary_group) if create_groups and groups: for group in groups: if not util.is_group(group): self.create_group(group) LOG.debug("created group '%s' for user '%s'", group, name) # Check the values and create the command for key, val in sorted(kwargs.items()): if key in adduser_opts and val and isinstance(val, str): 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]) elif key in adduser_flags and val: adduser_cmd.append(adduser_flags[key]) log_adduser_cmd.append(adduser_flags[key]) # Don't create the home directory if directed so or if the user is a # system user if kwargs.get('no_create_home') or kwargs.get('system'): adduser_cmd.append('-M') log_adduser_cmd.append('-M') else: 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