Esempio n. 1
0
 def _bringup_router(self):
     """Perform the ip commands to fully setup the router if needed."""
     # Check if a default route exists and exit if it does
     out, _ = subp.subp(['ip', 'route', 'show', '0.0.0.0/0'], capture=True)
     if 'default' in out:
         LOG.debug(
             'Skip ephemeral route setup. %s already has default route: %s',
             self.interface, out.strip())
         return
     subp.subp(
         ['ip', '-4', 'route', 'add', self.router, 'dev', self.interface,
          'src', self.ip], capture=True)
     self.cleanup_cmds.insert(
         0,
         ['ip', '-4', 'route', 'del', self.router, 'dev', self.interface,
          'src', self.ip])
     subp.subp(
         ['ip', '-4', 'route', 'add', 'default', 'via', self.router,
          'dev', self.interface], capture=True)
     self.cleanup_cmds.insert(
         0, ['ip', '-4', 'route', 'del', 'default', 'dev', self.interface])
Esempio n. 2
0
def get_interfaces_by_mac_on_netbsd():
    ret = {}
    re_field_match = (r"(?P<ifname>\w+).*address:\s"
                      r"(?P<mac>([\da-f]{2}[:-]){5}([\da-f]{2})).*")
    (out, _) = subp.subp(['ifconfig', '-a'])
    if_lines = re.sub(r'\n\s+', ' ', out).splitlines()
    for line in if_lines:
        m = re.match(re_field_match, line)
        if m:
            fields = m.groupdict()
            ret[fields['mac']] = fields['ifname']
    return ret
Esempio n. 3
0
def is_lvm_lv(devpath):
    if util.is_Linux():
        # all lvm lvs will have a realpath as a 'dm-*' name.
        rpath = os.path.realpath(devpath)
        if not os.path.basename(rpath).startswith("dm-"):
            return False
        out, _ = subp.subp("udevadm", "info", devpath)
        # lvs should have DM_LV_NAME=<lvmuuid> and also DM_VG_NAME
        return 'DM_LV_NAME=' in out
    else:
        LOG.info("Not an LVM Logical Volume partition")
        return False
Esempio n. 4
0
def get_interfaces_by_mac_on_openbsd(blacklist_drivers=None) -> dict:
    ret = {}
    re_field_match = (r"(?P<ifname>\w+).*lladdr\s"
                      r"(?P<mac>([\da-f]{2}[:-]){5}([\da-f]{2})).*")
    (out, _) = subp.subp(["ifconfig", "-a"])
    if_lines = re.sub(r"\n\s+", " ", out).splitlines()
    for line in if_lines:
        m = re.match(re_field_match, line)
        if m:
            fields = m.groupdict()
            ret[fields["mac"]] = fields["ifname"]
    return ret
Esempio n. 5
0
    def available(self):
        myenv = os.environ.copy()
        myenv['LANG'] = 'C'

        try:
            (_out, err) = subp.subp(["gpart", "help"], env=myenv, rcs=[0, 1])
            if re.search(r"gpart recover ", err):
                return True

        except subp.ProcessExecutionError:
            pass
        return False
Esempio n. 6
0
    def available(self):
        myenv = os.environ.copy()
        myenv['LANG'] = 'C'

        try:
            (out, _err) = subp.subp(["growpart", "--help"], env=myenv)
            if re.search(r"--update\s+", out):
                return True

        except subp.ProcessExecutionError:
            pass
        return False
Esempio n. 7
0
    def package_command(self, command, args=None, pkgs=None):
        if pkgs is None:
            pkgs = []

        cmd = ['apk']
        # Redirect output
        cmd.append("--quiet")

        if args and isinstance(args, str):
            cmd.append(args)
        elif args and isinstance(args, list):
            cmd.extend(args)

        if command:
            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)
        subp.subp(cmd, capture=False)
Esempio n. 8
0
def handle(name, cfg, _cloud, log, _args):

    mycfg = cfg.get("grub_dpkg", cfg.get("grub-dpkg", {}))
    if not mycfg:
        mycfg = {}

    enabled = mycfg.get("enabled", True)
    if util.is_false(enabled):
        log.debug("%s disabled by config grub_dpkg/enabled=%s", name, enabled)
        return

    idevs = util.get_cfg_option_str(mycfg, "grub-pc/install_devices", None)
    if idevs is None:
        idevs = fetch_idevs(log)

    idevs_empty = mycfg.get("grub-pc/install_devices_empty")
    if idevs_empty is None:
        idevs_empty = not idevs
    elif not isinstance(idevs_empty, bool):
        log.warning(
            "DEPRECATED: grub_dpkg: grub-pc/install_devices_empty value of "
            f"'{idevs_empty}' is not boolean. Use of non-boolean values "
            "will be removed in a future version of cloud-init.")
        idevs_empty = util.translate_bool(idevs_empty)
    idevs_empty = str(idevs_empty).lower()

    # 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"
                 "grub-pc grub-pc/install_devices_empty boolean %s\n" %
                 (idevs, idevs_empty))

    log.debug("Setting grub debconf-set-selections with '%s','%s'" %
              (idevs, idevs_empty))

    try:
        subp.subp(["debconf-set-selections"], dconf_sel)
    except Exception:
        util.logexc(log, "Failed to run debconf-set-selections for grub-dpkg")
Esempio n. 9
0
def exec_mkpart_gpt(device, layout):
    try:
        subp.subp([SGDISK_CMD, "-Z", device])
        for index, (partition_type, (start, end)) in enumerate(layout):
            index += 1
            subp.subp(
                [
                    SGDISK_CMD,
                    "-n",
                    "{}:{}:{}".format(index, start, end),
                    device,
                ]
            )
            if partition_type is not None:
                # convert to a 4 char (or more) string right padded with 0
                # 82 -> 8200.  'Linux' -> 'Linux'
                pinput = str(partition_type).ljust(4, "0")
                subp.subp(
                    [SGDISK_CMD, "-t", "{}:{}".format(index, pinput), device]
                )
    except Exception:
        LOG.warning("Failed to partition device %s", device)
        raise

    read_parttbl(device)
Esempio n. 10
0
def netdev_info(empty=""):
    devs = {}
    if util.is_NetBSD():
        (ifcfg_out, _err) = subp.subp(["ifconfig", "-a"], rcs=[0, 1])
        devs = _netdev_info_ifconfig_netbsd(ifcfg_out)
    elif subp.which('ip'):
        # Try iproute first of all
        (ipaddr_out, _err) = subp.subp(["ip", "addr", "show"])
        devs = _netdev_info_iproute(ipaddr_out)
    elif subp.which('ifconfig'):
        # Fall back to net-tools if iproute2 is not present
        (ifcfg_out, _err) = subp.subp(["ifconfig", "-a"], rcs=[0, 1])
        devs = _netdev_info_ifconfig(ifcfg_out)
    else:
        LOG.warning(
            "Could not print networks: missing 'ip' and 'ifconfig' commands")

    if empty == "":
        return devs

    recurse_types = (dict, tuple, list)

    def fill(data, new_val="", empty_vals=("", b"")):
        """Recursively replace 'empty_vals' in data (dict, tuple, list)
           with new_val"""
        if isinstance(data, dict):
            myiter = data.items()
        elif isinstance(data, (tuple, list)):
            myiter = enumerate(data)
        else:
            raise TypeError("Unexpected input to fill")

        for key, val in myiter:
            if val in empty_vals:
                data[key] = new_val
            elif isinstance(val, recurse_types):
                fill(val, new_val)

    fill(devs, new_val=empty)
    return devs
Esempio n. 11
0
def get_underlying_partition(blockdev):
    command = ["dmsetup", "deps", "--options=devname", blockdev]
    dep: str = subp.subp(command)[0]  # pyright: ignore
    # Returned result should look something like:
    # 1 dependencies : (vdb1)
    if not dep.startswith("1 depend"):
        raise RuntimeError(
            f"Expecting '1 dependencies' from 'dmsetup'. Received: {dep}")
    try:
        return f'/dev/{dep.split(": (")[1].split(")")[0]}'
    except IndexError as e:
        raise RuntimeError(
            f"Ran `{command}`, but received unexpected stdout: `{dep}`") from e
Esempio n. 12
0
    def package_command(self, command, args=None, pkgs=None):
        if pkgs is None:
            pkgs = []

        cmd = ["pacman", "-Sy", "--quiet", "--noconfirm"]
        # Redirect output

        if args and isinstance(args, str):
            cmd.append(args)
        elif args and isinstance(args, list):
            cmd.extend(args)

        if command == "upgrade":
            command = "-u"
        if command:
            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)
        subp.subp(cmd, capture=False)
Esempio n. 13
0
    def set_passwd(self, user, passwd, hashed=False):
        if hashed:
            hashed_pw = passwd
        elif not hasattr(crypt, "METHOD_BLOWFISH"):
            # crypt.METHOD_BLOWFISH comes with Python 3.7 which is available
            # on NetBSD 7 and 8.
            LOG.error(
                "Cannot set non-encrypted password for user %s. "
                "Python >= 3.7 is required.",
                user,
            )
            return
        else:
            method = crypt.METHOD_BLOWFISH  # pylint: disable=E1101
            hashed_pw = crypt.crypt(passwd, crypt.mksalt(method))

        try:
            subp.subp(["usermod", "-p", hashed_pw, user])
        except Exception:
            util.logexc(LOG, "Failed to set password for %s", user)
            raise
        self.unlock_passwd(user)
Esempio n. 14
0
def handle(_name, cfg, cloud, log, _args):
    validate_cloudconfig_schema(cfg, schema)
    network_hotplug_enabled = ("updates" in cfg and "network" in cfg["updates"]
                               and "when" in cfg["updates"]["network"] and
                               "hotplug" in cfg["updates"]["network"]["when"])
    hotplug_supported = EventType.HOTPLUG in (
        cloud.datasource.get_supported_events([EventType.HOTPLUG
                                               ]).get(EventScope.NETWORK,
                                                      set()))
    hotplug_enabled = stages.update_event_enabled(
        datasource=cloud.datasource,
        cfg=cfg,
        event_source_type=EventType.HOTPLUG,
        scope=EventScope.NETWORK,
    )
    if not (hotplug_supported and hotplug_enabled):
        if os.path.exists(HOTPLUG_UDEV_PATH):
            log.debug("Uninstalling hotplug, not enabled")
            util.del_file(HOTPLUG_UDEV_PATH)
            subp.subp(["udevadm", "control", "--reload-rules"])
        elif network_hotplug_enabled:
            log.warning("Hotplug is unsupported by current datasource. "
                        "Udev rules will NOT be installed.")
        else:
            log.debug("Skipping hotplug install, not enabled")
        return
    if not subp.which("udevadm"):
        log.debug("Skipping hotplug install, udevadm not found")
        return

    # This may need to turn into a distro property at some point
    libexecdir = "/usr/libexec/cloud-init"
    if not os.path.exists(libexecdir):
        libexecdir = "/usr/lib/cloud-init"
    util.write_file(
        filename=HOTPLUG_UDEV_PATH,
        content=HOTPLUG_UDEV_RULES_TEMPLATE.format(libexecdir=libexecdir),
    )
    subp.subp(["udevadm", "control", "--reload-rules"])
Esempio n. 15
0
 def _bring_up_interface(self, device_name):
     cmd = ['ifup', device_name]
     LOG.debug("Attempting to run bring up interface %s using command %s",
               device_name, cmd)
     try:
         (_out, err) = subp.subp(cmd)
         if len(err):
             LOG.warning("Running %s resulted in stderr output: %s", cmd,
                         err)
         return True
     except subp.ProcessExecutionError:
         util.logexc(LOG, "Running interface command %s failed", cmd)
         return False
Esempio n. 16
0
 def features(self):
     if self._features is None:
         try:
             info_blob, _err = subp.subp(self.NETPLAN_INFO, capture=True)
             info = util.load_yaml(info_blob)
             self._features = info["netplan.io"]["features"]
         except subp.ProcessExecutionError:
             # if the info subcommand is not present then we don't have any
             # new features
             pass
         except (TypeError, KeyError) as e:
             LOG.debug("Failed to list features from netplan info: %s", e)
     return self._features
Esempio n. 17
0
def guestinfo_set_value(key, value, vmware_rpctool=VMWARE_RPCTOOL):
    """
    Sets a guestinfo value for the specified key. Set value to an empty string
    to clear an existing guestinfo key.
    """

    # If value is an empty string then set it to a single space as it is not
    # possible to set a guestinfo key to an empty string. Setting a guestinfo
    # key to a single space is as close as it gets to clearing an existing
    # guestinfo key.
    if value == "":
        value = " "

    LOG.debug("Setting guestinfo key=%s to value=%s", key, value)

    try:
        subp([
            vmware_rpctool,
            ("info-set %s %s" % (get_guestinfo_key_name(key), value)),
        ])
        return True
    except ProcessExecutionError as error:
        util.logexc(
            LOG,
            "Failed to set guestinfo key=%s to value=%s: %s",
            key,
            value,
            error,
        )
    except Exception:
        util.logexc(
            LOG,
            "Unexpected error while trying to set " +
            "guestinfo key=%s to value=%s",
            key,
            value,
        )

    return None
Esempio n. 18
0
def recv_key(key, keyserver, retries=(1, 1)):
    """Receive gpg key from the specified keyserver.

    Retries are done by default because keyservers can be unreliable.
    Additionally, there is no way to determine the difference between
    a non-existant key and a failure.  In both cases gpg (at least 2.2.4)
    exits with status 2 and stderr: "keyserver receive failed: No data"
    It is assumed that a key provided to cloud-init exists on the keyserver
    so re-trying makes better sense than failing.

    @param key: a string key fingerprint (as passed to gpg --recv-keys).
    @param keyserver: the keyserver to request keys from.
    @param retries: an iterable of sleep lengths for retries.
                    Use None to indicate no retries."""
    LOG.debug("Importing key '%s' from keyserver '%s'", key, keyserver)
    cmd = ["gpg", "--keyserver=%s" % keyserver, "--recv-keys", key]
    if retries is None:
        retries = []
    trynum = 0
    error = None
    sleeps = iter(retries)
    while True:
        trynum += 1
        try:
            subp.subp(cmd, capture=True)
            LOG.debug("Imported key '%s' from keyserver '%s' on try %d", key,
                      keyserver, trynum)
            return
        except subp.ProcessExecutionError as e:
            error = e
        try:
            naplen = next(sleeps)
            LOG.debug("Import failed with exit code %d, will try again in %ss",
                      error.exit_code, naplen)
            time.sleep(naplen)
        except StopIteration:
            raise ValueError(
                ("Failed to import key '%s' from keyserver '%s' "
                 "after %d tries: %s") % (key, keyserver, trynum, error))
Esempio n. 19
0
def set_route():
    # Get routes, confirm entry does not exist
    routes = netinfo.route_info()

    # If no tools exist and empty dict is returned
    if "ipv4" not in routes:
        return

    # We only care about IPv4
    routes = routes["ipv4"]

    # Searchable list
    dests = []

    # Parse each route into a more searchable format
    for route in routes:
        dests.append(route["destination"])

    gw_present = "100.64.0.0" in dests or "100.64.0.0/10" in dests
    dest_present = "169.254.169.254" in dests

    # If not IPv6 only (No link local)
    # or the route is already present
    if not gw_present or dest_present:
        return

    # Set metadata route
    if subp.which("ip"):
        subp.subp([
            "ip",
            "route",
            "add",
            "169.254.169.254/32",
            "dev",
            net.find_fallback_nic(),
        ])
    elif subp.which("route"):
        subp.subp(["route", "add", "-net", "169.254.169.254/32", "100.64.0.1"])
Esempio n. 20
0
    def resize(self, diskdev, partnum, partdev):
        """
        GPT disks store metadata at the beginning (primary) and at the
        end (secondary) of the disk. When launching an image with a
        larger disk compared to the original image, the secondary copy
        is lost. Thus, the metadata will be marked CORRUPT, and need to
        be recovered.
        """
        try:
            subp.subp(["gpart", "recover", diskdev])
        except subp.ProcessExecutionError as e:
            if e.exit_code != 0:
                util.logexc(LOG, "Failed: gpart recover %s", diskdev)
                raise ResizeFailedException(e) from e

        before = get_size(partdev)
        try:
            subp.subp(["gpart", "resize", "-i", partnum, diskdev])
        except subp.ProcessExecutionError as e:
            util.logexc(LOG, "Failed: gpart resize -i %s %s", partnum, diskdev)
            raise ResizeFailedException(e) from e

        return (before, get_size(partdev))
Esempio n. 21
0
    def package_command(self, command, args=None, pkgs=None):
        if pkgs is None:
            pkgs = []

        # No user interaction possible, enable non-interactive mode
        cmd = ['zypper', '--non-interactive']

        # Command is the operation, such as install
        if command == 'upgrade':
            command = 'update'
        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)
        subp.subp(cmd, capture=False)
Esempio n. 22
0
def dpkg_reconfigure(packages, target=None):
    # For any packages that are already installed, but have preseed data
    # we populate the debconf database, but the filesystem configuration
    # would be preferred on a subsequent dpkg-reconfigure.
    # so, what we have to do is "know" information about certain packages
    # to unconfigure them.
    unhandled = []
    to_config = []
    for pkg in packages:
        if pkg in CONFIG_CLEANERS:
            LOG.debug("unconfiguring %s", pkg)
            CONFIG_CLEANERS[pkg](target)
            to_config.append(pkg)
        else:
            unhandled.append(pkg)

    if len(unhandled):
        LOG.warning("The following packages were installed and preseeded, "
                    "but cannot be unconfigured: %s", unhandled)

    if len(to_config):
        subp.subp(['dpkg-reconfigure', '--frontend=noninteractive'] +
                  list(to_config), data=None, target=target, capture=True)
Esempio n. 23
0
def assign_ipv4_link_local(nic=None):
    """Bring up NIC using an address using link-local (ip4LL) IPs. On
       DigitalOcean, the link-local domain is per-droplet routed, so there
       is no risk of collisions. However, to be more safe, the ip4LL
       address is random.
    """

    if not nic:
        nic = get_link_local_nic()
        LOG.debug("selected interface '%s' for reading metadata", nic)

    if not nic:
        raise RuntimeError("unable to find interfaces to access the"
                           "meta-data server. This droplet is broken.")

    addr = "169.254.{0}.{1}/16".format(random.randint(1, 168),
                                       random.randint(0, 255))

    ip_addr_cmd = ['ip', 'addr', 'add', addr, 'dev', nic]
    ip_link_cmd = ['ip', 'link', 'set', 'dev', nic, 'up']

    if not subp.which('ip'):
        raise RuntimeError("No 'ip' command available to configure ip4LL "
                           "address")

    try:
        subp.subp(ip_addr_cmd)
        LOG.debug("assigned ip4LL address '%s' to '%s'", addr, nic)
        subp.subp(ip_link_cmd)
        LOG.debug("brought device '%s' up", nic)
    except Exception:
        util.logexc(
            LOG, "ip4LL address assignment of '%s' to '%s' failed."
            " Droplet networking will be broken", addr, nic)
        raise

    return nic
Esempio n. 24
0
def run_commands(commands):
    """Run the provided commands provided in snap:commands configuration.

    Commands are run individually. Any errors are collected and reported
    after attempting all commands.

    @param commands: A list or dict containing commands to run. Keys of a
        dict will be used to order the commands provided as dict values.
    """
    if not commands:
        return
    LOG.debug("Running user-provided snap commands")
    if isinstance(commands, dict):
        # Sort commands based on dictionary key
        commands = [v for _, v in sorted(commands.items())]
    elif not isinstance(commands, list):
        raise TypeError(
            "commands parameter was not a list or dict: {commands}".format(
                commands=commands
            )
        )

    fixed_snap_commands = prepend_base_command("snap", commands)

    cmd_failures = []
    for command in fixed_snap_commands:
        shell = isinstance(command, str)
        try:
            subp.subp(command, shell=shell, status_cb=sys.stderr.write)
        except subp.ProcessExecutionError as e:
            cmd_failures.append(str(e))
    if cmd_failures:
        msg = "Failures running snap commands:\n{cmd_failures}".format(
            cmd_failures=cmd_failures
        )
        util.logexc(LOG, msg)
        raise RuntimeError(msg)
Esempio n. 25
0
def get_pvs_for_lv(devpath):
    myenv = {'LANG': 'C'}

    if not util.is_Linux():
        LOG.info("No support for LVM on %s", platform.system())
        return None
    if not subp.which('lvm'):
        LOG.info("No 'lvm' command present")
        return None

    try:
        (out, _err) = subp.subp(
            ["lvm", "lvs", devpath, "--options=vgname", "--noheadings"],
            update_env=myenv)
        vgname = out.strip()
    except subp.ProcessExecutionError as e:
        if e.exit_code != 0:
            util.logexc(
                LOG, "Failed: can't get Volume Group information "
                "from %s", devpath)
            raise ResizeFailedException(e) from e

    try:
        (out, _err) = subp.subp(
            ["lvm", "vgs", vgname, "--options=pvname", "--noheadings"],
            update_env=myenv)
        pvs = [p.strip() for p in out.splitlines()]
        if len(pvs) > 1:
            LOG.info("Do not know how to resize multiple Physical" " Volumes")
        else:
            return pvs[0]
    except subp.ProcessExecutionError as e:
        if e.exit_code != 0:
            util.logexc(
                LOG, "Failed: can't get Physical Volume "
                "information from Volume Group %s", vgname)
            raise ResizeFailedException(e) from e
Esempio n. 26
0
 def _read_hostname(self, filename, default=None):
     if self.uses_systemd() and filename.endswith("/previous-hostname"):
         return util.load_file(filename).strip()
     elif self.uses_systemd():
         (out, _err) = subp.subp(["hostname"])
         if len(out):
             return out
         else:
             return default
     else:
         (_exists, contents) = rhel_util.read_sysconfig_file(filename)
         if "HOSTNAME" in contents:
             return contents["HOSTNAME"]
         else:
             return default
Esempio n. 27
0
def do_register(
    server,
    profile_name,
    ca_cert_path=def_ca_cert_path,
    proxy=None,
    log=None,
    activation_key=None,
):
    if log is not None:
        log.info(
            "Registering using `rhnreg_ks` profile '%s' into server '%s'",
            profile_name,
            server,
        )
    cmd = ["rhnreg_ks"]
    cmd.extend(["--serverUrl", "https://%s/XMLRPC" % server])
    cmd.extend(["--profilename", str(profile_name)])
    if proxy:
        cmd.extend(["--proxy", str(proxy)])
    if ca_cert_path:
        cmd.extend(["--sslCACert", str(ca_cert_path)])
    if activation_key:
        cmd.extend(["--activationkey", str(activation_key)])
    subp.subp(cmd, capture=False)
    def package_command(self, command, args=None, pkgs=None):
        if pkgs is None:
            pkgs = []

        cmd = ["apk"]
        # Redirect output
        cmd.append("--quiet")

        if args and isinstance(args, str):
            cmd.append(args)
        elif args and isinstance(args, list):
            cmd.extend(args)

        if command:
            cmd.append(command)

        if command == "upgrade":
            cmd.extend(["--update-cache", "--available"])

        pkglist = util.expand_package_list("%s-%s", pkgs)
        cmd.extend(pkglist)

        # Allow the output of this to flow outwards (ie not be captured)
        subp.subp(cmd, capture=False)
Esempio n. 29
0
    def subp(self):
        """
        Make a subp call based on set args and handle errors by setting
        failure code

        :return: whether the subp call failed or not
        """
        try:
            value, err = subp.subp(self.args, capture=True)
            if err:
                return err
            self.epoch = value
            return None
        except Exception as systemctl_fail:
            return systemctl_fail
Esempio n. 30
0
    def create_group(self, name, members=None):
        if util.is_group(name):
            LOG.warning("Skipping creation of existing group '%s'", name)
        else:
            group_add_cmd = self.group_add_cmd_prefix + [name]
            try:
                subp.subp(group_add_cmd)
                LOG.info("Created new group %s", name)
            except Exception:
                util.logexc(LOG, "Failed to create group %s", name)

        if not members:
            members = []
        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
            try:
                subp.subp(self._get_add_member_to_group_cmd(member, name))
                LOG.info("Added user '%s' to group '%s'", member, name)
            except Exception:
                util.logexc(LOG, "Failed to add user '%s' to group '%s'",
                            member, name)