Ejemplo n.º 1
0
def get_avail_subs():
    """Get list of all the subscriptions available to the user so they are
    accessible by index once the user chooses one.
    """
    loggerinst = logging.getLogger(__name__)
    # Get multiline string holding all the subscriptions available to the
    # logged-in user
    subs_raw, ret_code = utils.run_subprocess("subscription-manager list"
                                              " --available",
                                              print_output=False)
    if ret_code != 0:
        loggerinst.critical("Unable to get list of available subscriptions:"
                            "\n%s" % subs_raw)
    return list(get_sub(subs_raw))
Ejemplo n.º 2
0
def attach_subscription():
    """Attach a specific subscription to the registered OS. If no
    subscription ID has been provided through command line, let the user
    interactively choose one.
    """
    # TODO: Support attaching multiple pool IDs.
    # TODO: Support the scenario when the passed activation key attaches
    #       all the appropriate subscriptions during registration already.

    loggerinst = logging.getLogger(__name__)

    if tool_opts.activation_key:
        return True

    if tool_opts.auto_attach:
        pool = "--auto"
        tool_opts.pool = "-a"
        loggerinst.info(
            "Auto-attaching compatible subscriptions to the system ...")
    elif tool_opts.pool:
        # The subscription pool ID has been passed through a command line
        # option
        pool = "--pool %s" % tool_opts.pool
        tool_opts.pool = pool
        loggerinst.info("Attaching provided subscription pool ID to the"
                        " system ...")
    else:
        # Let the user choose the subscription appropriate for the conversion
        loggerinst.info(
            "Manually select subscription appropriate for the conversion")
        subs_list = get_avail_subs()
        if len(subs_list) == 0:
            loggerinst.warning("No subscription available for the conversion.")
            return False

        print_avail_subs(subs_list)
        sub_num = utils.let_user_choose_item(len(subs_list), "subscription")
        pool = "--pool " + subs_list[sub_num].pool_id
        tool_opts.pool = pool
        loggerinst.info(
            "Attaching subscription with pool ID %s to the system ..." %
            subs_list[sub_num].pool_id)

    _, ret_code = utils.run_subprocess("subscription-manager attach %s" % pool)
    if ret_code != 0:
        # Unsuccessful attachment, e.g. the pool ID is incorrect or the
        # number of purchased attachments has been depleted.
        loggerinst.critical("Unsuccessful attachment of a subscription.")
    return True
Ejemplo n.º 3
0
    def _generate_rpm_va(self):
        """Verify the installed files on the system with the rpm metadata
        for debug and support purposes."""
        if tool_opts.no_rpm_va:
            self.logger.info("Skipping execution of 'rpm -Va'.")
            return

        self.logger.info("Running the 'rpm -Va' command which can take several"
                         " minutes. It can be disabled by using the"
                         " --no-rpm-va option.")
        rpm_va, _ = utils.run_subprocess("rpm -Va", print_output=False)
        output_file = os.path.join(logger.LOG_DIR, "rpm_va.log")
        utils.store_content_to_file(output_file, rpm_va)
        self.logger.info("The 'rpm -Va' output has been stored in the %s file"
                         % output_file)
Ejemplo n.º 4
0
def _get_blk_device(device):
    """Get the block device.

    In case of the block device itself (e.g. /dev/sda), return just the block
    device. In case of a partition, return its block device:
        /dev/sda  -> /dev/sda
        /dev/sda1 -> /dev/sda

    Raise the BootloaderError when unable to get the block device.
    """
    output, ecode = utils.run_subprocess(["lsblk", "-spnlo", "name", device], print_output=False)
    if ecode:
        logger.debug("lsblk output:\n-----\n%s\n-----" % output)
        raise BootloaderError("Unable to get a block device for '%s'." % device)

    return output.strip().splitlines()[-1].strip()
Ejemplo n.º 5
0
def replace_non_rhel_installed_kernel(version):
    """Replace the installed non-RHEL kernel with RHEL kernel with same version."""
    loggerinst.warning(
        "The convert2rhel utlity is going to force-replace the only"
        " kernel installed, which has the same NEVRA as the"
        " only available RHEL kernel. If anything goes wrong"
        " with such replacement, the system will become"
        " unbootable. If you want the convert2rhel utility to install"
        " the RHEL kernel in a safer manner, you can install a"
        " different version of kernel first and then run"
        " convert2rhel again.")
    utils.ask_to_continue()

    pkg = "kernel-%s" % version

    # For downloading the RHEL kernel we need to use the RHEL repositories.
    repos_to_enable = system_info.get_enabled_rhel_repos()
    path = utils.download_pkg(
        pkg=pkg,
        dest=utils.TMP_DIR,
        disable_repos=tool_opts.disablerepo,
        enable_repos=repos_to_enable,
    )
    if not path:
        loggerinst.critical("Unable to download the RHEL kernel package.")

    loggerinst.info(
        "Replacing %s %s with RHEL kernel with the same NEVRA ... " %
        (system_info.name, pkg))
    output, ret_code = utils.run_subprocess(
        # The --nodeps is needed as some kernels depend on system-release (alias for redhat-release) and that package
        # is not installed at this stage.
        [
            "rpm",
            "-i",
            "--force",
            "--nodeps",
            "--replacepkgs",
            "%s*" % os.path.join(utils.TMP_DIR, pkg),
        ],
        print_output=False,
    )
    if ret_code != 0:
        loggerinst.critical("Unable to replace the kernel package: %s" %
                            output)

    loggerinst.info("\nRHEL %s installed.\n" % pkg)
Ejemplo n.º 6
0
    def generate_rpm_va(self, log_filename=PRE_RPM_VA_LOG_FILENAME):
        """RPM is able to detect if any file installed as part of a package has been changed in any way after the
        package installation.

        Here we are getting a list of changed package files of all the installed packages. Such a list is useful for
        debug and support purposes. It's being saved to the default log folder as log_filename."""
        if tool_opts.no_rpm_va:
            self.logger.info("Skipping the execution of 'rpm -Va'.")
            return

        self.logger.info("Running the 'rpm -Va' command which can take several"
                         " minutes. It can be disabled by using the"
                         " --no-rpm-va option.")
        rpm_va, _ = utils.run_subprocess("rpm -Va", print_output=False)
        output_file = os.path.join(logger.LOG_DIR, log_filename)
        utils.store_content_to_file(output_file, rpm_va)
        self.logger.info("The 'rpm -Va' output has been stored in the %s file" % output_file)
Ejemplo n.º 7
0
    def __init__(self):
        if not is_efi():
            raise EFINotUsed("Unable to collect data about UEFI on a BIOS system.")
        bootmgr_output, ecode = utils.run_subprocess(["/usr/sbin/efibootmgr", "-v"], print_output=False)
        if ecode:
            raise BootloaderError("Unable to get information about UEFI boot entries.")

        self.current_bootnum = None
        """The boot number (str) of the current boot."""
        self.boot_order = tuple()
        """The tuple of the UEFI boot loader entries in the boot order."""
        self.entries = {}
        """The UEFI boot loader entries {'boot_number': EFIBootLoader}"""

        self._parse_efi_boot_entries(bootmgr_output)
        self._parse_current_bootnum(bootmgr_output)
        self._parse_boot_order(bootmgr_output)
        self._print_loaded_info()
Ejemplo n.º 8
0
def disable_repos():
    """Before enabling specific repositories, all repositories should be
    disabled. This can be overriden by the --disablerepo option.
    """

    disable_cmd = ""
    for repo in tool_opts.disablerepo:
        disable_cmd += " --disable=%s" % repo
    if not disable_cmd:
        # Default is to disable all repos to make clean environment for
        # enabling repos later
        disable_cmd = " --disable='*'"
    output, ret_code = utils.run_subprocess("subscription-manager repos%s"
                                            % disable_cmd, print_output=False)
    if ret_code != 0:
        loggerinst.critical("Repos were not possible to disable through"
                            " subscription-manager:\n%s" % output)
    loggerinst.info("Repositories disabled.")
    return
Ejemplo n.º 9
0
def enable_repos(rhel_repoids):
    """By default, enable the standard Red Hat CDN RHEL repository IDs using subscription-manager.
    This can be overriden by the --enablerepo option.
    """
    if tool_opts.enablerepo:
        repos_to_enable = tool_opts.enablerepo
    else:
        repos_to_enable = rhel_repoids

    enable_cmd = ""
    for repo in repos_to_enable:
        enable_cmd += " --enable=%s" % repo
    output, ret_code = utils.run_subprocess("subscription-manager repos%s"
                                            % enable_cmd, print_output=False)
    if ret_code != 0:
        loggerinst.critical("Repos were not possible to enable through"
                            " subscription-manager:\n%s" % output)
    loggerinst.info("Repositories enabled through subscription-manager")

    system_info.submgr_enabled_repos = repos_to_enable
Ejemplo n.º 10
0
def _bad_kernel_package_signature(kernel_release):
    """Return True if the booted kernel is not signed by the original OS vendor, i.e. it's a custom kernel."""
    kernel_pkg = run_subprocess(
        ["rpm", "-qf", "--qf", "%{NAME}",
         "/boot/vmlinuz-%s" % kernel_release],
        print_output=False)[0]
    logger.debug("Booted kernel package name: {0}".format(kernel_pkg))
    kernel_pkg_obj = get_installed_pkg_objects(kernel_pkg)[0]
    kernel_pkg_gpg_fingerprint = get_pkg_fingerprint(kernel_pkg_obj)
    bad_signature = system_info.cfg_content[
        "gpg_fingerprints"] != kernel_pkg_gpg_fingerprint
    # e.g. Oracle Linux Server -> Oracle
    os_vendor = system_info.name.split()[0]
    if bad_signature:
        logger.warning(
            "Custom kernel detected. The booted kernel needs to be signed by %s."
            % os_vendor)
        return True
    logger.debug("The booted kernel is signed by %s." % os_vendor)
    return False
Ejemplo n.º 11
0
def enable_repos(repos_needed):
    """By default, enable just the repos identified by the tool as needed and
    disable any other using subscription-manager. This can be overriden by the
    --enablerepo option.
    """
    loggerinst = logging.getLogger(__name__)
    if tool_opts.enablerepo:
        repos_to_enable = tool_opts.enablerepo
    else:
        repos_to_enable = repos_needed

    enable_cmd = ""
    for repo in repos_to_enable:
        enable_cmd += " --enable=%s" % repo
    output, ret_code = utils.run_subprocess("subscription-manager repos%s"
                                            % enable_cmd, print_output=False)
    if ret_code != 0:
        loggerinst.critical("Repos were not possible to enable through"
                            " subscription-manager:\n%s" % output)
    loggerinst.info("Repositories enabled through subscription-manager")
    return
Ejemplo n.º 12
0
def ensure_compatibility_of_kmods():
    """Ensure if the host kernel modules are compatible with RHEL."""
    host_kmods = get_installed_kmods()
    rhel_supported_kmods = get_rhel_supported_kmods()
    if is_unsupported_kmod_installed(host_kmods, rhel_supported_kmods):
        kernel_version = run_subprocess("uname -r")[0].rstrip("\n")
        not_supported_kmods = "\n".join(
            map(
                lambda kmod: "/lib/modules/{kver}/{kmod}".format(
                    kver=kernel_version, kmod=kmod),
                host_kmods - rhel_supported_kmods,
            ))
        # TODO logger.critical("message %s, %s", "what should be under s")
        #  doesn't work. We have `%s` as output instead. Make it work
        logger.critical(("The following kernel modules are not "
                         "supported in RHEL:\n{kmods}\n"
                         "Uninstall or disable them and run convert2rhel "
                         "again to continue with the conversion.").format(
                             kmods=not_supported_kmods))
    else:
        logger.debug("Kernel modules are compatible.")
Ejemplo n.º 13
0
def call_yum_cmd(command,
                 args="",
                 enablerepo=None,
                 disablerepo=None,
                 print_output=True):
    """Call yum command and optionally print its output."""
    loggerinst = logging.getLogger(__name__)

    cmd = "yum %s -y" % (command)
    # disablerepo parameter must be added before the enablerepo parameter

    if disablerepo is None:
        disablerepo = []
    if disablerepo:
        repos = disablerepo
    else:
        repos = tool_opts.disablerepo
    for repo in repos:
        cmd += " --disablerepo=%s " % repo

    if enablerepo is None:
        enablerepo = []
    if enablerepo:
        repos = enablerepo
    else:
        repos = tool_opts.enablerepo
    for repo in repos:
        cmd += " --enablerepo=%s " % repo
    if args:
        cmd += " " + args

    stdout, returncode = utils.run_subprocess(cmd, print_output=print_output)
    # handle when yum returns non-zero code when there is nothing to do
    nothing_to_do_error_exists = stdout.endswith("Error: Nothing to do\n")
    if returncode == 1 and nothing_to_do_error_exists:
        loggerinst.info("Return code 1 however nothing to do. Returning code 0"
                        " ... ")
        returncode = 0
    return stdout, returncode
Ejemplo n.º 14
0
def check_tainted_kmods():
    """Stop the conversion when a loaded tainted kernel module is detected.

    Tainted kmods ends with (...) in /proc/modules, for example:
        multipath 20480 0 - Live 0x0000000000000000
        linear 20480 0 - Live 0x0000000000000000
        system76_io 16384 0 - Live 0x0000000000000000 (OE)  <<<<<< Tainted
        system76_acpi 16384 0 - Live 0x0000000000000000 (OE) <<<<< Tainted
    """
    unsigned_modules, _ = run_subprocess("grep '(' /proc/modules")
    module_names = "\n  ".join(
        [mod.split(" ")[0] for mod in unsigned_modules.splitlines()])
    if unsigned_modules:
        logger.critical(
            "Tainted kernel module(s) detected. "
            "Third-party components are not supported per our "
            "software support policy\n%s\n\n"
            "Uninstall or disable the following module(s) and run convert2rhel "
            "again to continue with the conversion:\n  %s",
            LINK_KMODS_RH_POLICY,
            module_names,
        )
Ejemplo n.º 15
0
def lock_releasever_in_rhel_repositories():
    """Lock the releasever in the RHEL repositories located under /etc/yum.repos.d/redhat.repo

    After converting to a RHEL EUS minor version, we need to lock the releasever in the redhat.repo file
    to prevent future errors such as, running `yum update` and not being able to find the repositories metadata.

    .. note::
        This function should only run if the system corresponds to a RHEL EUS version to make sure the converted system
        keeps receiving updates for the specific EUS minor version instead of the latest minor version which is the
        default.
    """

    # We only lock the releasever on rhel repos if we detect that the running system is an EUS correspondent and if
    # rhsm is used, otherwise, there's no need to lock the releasever as the subscription-manager won't be available.
    if system_info.corresponds_to_rhel_eus_release() and not tool_opts.no_rhsm:
        loggerinst.info(
            "Updating /etc/yum.repos.d/rehat.repo to point to RHEL %s instead of the default latest minor version."
            % system_info.releasever)
        cmd = [
            "subscription-manager", "release",
            "--set=%s" % system_info.releasever
        ]

        output, ret_code = utils.run_subprocess(cmd, print_output=False)
        if ret_code != 0:
            loggerinst.warning(
                "Locking RHEL repositories failed with return code %d and message:\n%s",
                ret_code,
                output,
            )
        else:
            loggerinst.info(
                "RHEL repositories locked to the %s minor version." %
                system_info.releasever)
    else:
        loggerinst.info(
            "Skipping locking RHEL repositories to a specific EUS minor version."
        )
Ejemplo n.º 16
0
def replace_non_rhel_installed_kernel(version):
    """Replace the installed non-RHEL kernel with RHEL kernel with same
    version.
    """
    loggerinst = logging.getLogger(__name__)
    loggerinst.warning("The convert2rhel is going to force-replace the only"
                       " kernel installed, which has the same NEVRA as the"
                       " only available RHEL kernel. If anything goes wrong"
                       " with such replacement, the system will become"
                       " unbootable. If you want the convert2rhel to install"
                       " the RHEL kernel in a safer manner, you can install a"
                       " different version of kernel first and then run"
                       " convert2rhel again.")
    utils.ask_to_continue()

    pkg = "kernel-%s" % version

    ret_code = utils.download_pkg(pkg=pkg,
                                  dest=utils.tmp_dir,
                                  disablerepo=tool_opts.disablerepo,
                                  enablerepo=tool_opts.enablerepo)
    if ret_code != 0:
        loggerinst.critical("Unable to download %s from RHEL repository" % pkg)
        return

    loggerinst.info(
        "Replacing %s %s with RHEL kernel with the same NEVRA ... " %
        (system_info.name, pkg))
    output, ret_code = utils.run_subprocess(
        'rpm -i --force --replacepkgs %s*' % os.path.join(utils.tmp_dir, pkg),
        print_output=False)
    if ret_code != 0:
        loggerinst.critical("Unable to replace kernel package: %s" % output)
        return

    loggerinst.info("\nRHEL %s installed.\n" % pkg)
Ejemplo n.º 17
0
 def factory(*args, **kwargs):
     for kws, result in stubs:
         if all(kw in args[0] for kw in kws):
             return result
     else:
         return run_subprocess(*args, **kwargs)
Ejemplo n.º 18
0
 def is_rpm_installed(name):
     _, return_code = run_subprocess(["rpm", "-q", name],
                                     print_cmd=False,
                                     print_output=False)
     return return_code == 0
Ejemplo n.º 19
0
def call_registration_cmd(registration_cmd):
    """Wrapper for run_subprocess that avoids leaking password in the log."""
    loggerinst = logging.getLogger(__name__)
    loggerinst.debug("Calling command '%s'" % hide_password(registration_cmd))
    _, ret_code = utils.run_subprocess(registration_cmd, print_cmd=False)
    return ret_code
Ejemplo n.º 20
0
def is_loaded_kernel_latest():
    """Check if the loaded kernel is behind or of the same version as in yum repos."""
    logger.task(
        "Prepare: Checking if the loaded kernel version is the most recent")

    if system_info.id == "oracle" and system_info.corresponds_to_rhel_eus_release(
    ):
        logger.info(
            "Skipping the check because there are no publicly available %s %d.%d repositories available."
            % (system_info.name, system_info.version.major,
               system_info.version.minor))
        return

    reposdir = get_hardcoded_repofiles_dir()

    if reposdir and not system_info.has_internet_access:
        logger.warning(
            "Skipping the check as no internet connection has been detected.")
        return

    cmd = [
        "repoquery", "--quiet", "--qf",
        '"%{BUILDTIME}\\t%{VERSION}-%{RELEASE}\\t%{REPOID}"'
    ]

    # If the reposdir variable is not empty, meaning that it detected the hardcoded repofiles, we should use that
    # instead of the system repositories located under /etc/yum.repos.d
    if reposdir:
        cmd.append("--setopt=reposdir=%s" % reposdir)

    # For Oracle/CentOS Linux 8 the `kernel` is just a meta package, instead, we check for `kernel-core`.
    # But for 6 and 7 releases, the correct way to check is using `kernel`.
    package_to_check = "kernel-core" if system_info.version.major >= 8 else "kernel"

    # Append the package name as the last item on the list
    cmd.append(package_to_check)

    # Search for available kernel package versions available in different repositories using the `repoquery` command.
    # If convert2rhel is running on a EUS system, then repoquery will use the hardcoded repofiles available under
    # /usr/share/convert2rhel/repos, meaning that the tool will fetch only the latest kernels available for that EUS
    # version, and not the most updated version from other newer versions.
    # If the case is that convert2rhel is not running on a EUS system, for example, Oracle Linux 8.5, then it will use
    # the system repofiles.
    repoquery_output, _ = run_subprocess(cmd, print_output=False)

    # Repoquery doesn't return any text at all when it can't find any matches for the query (when used with --quiet)
    if len(repoquery_output) > 0:
        # Convert to an tuple split with `buildtime` and `kernel` version.
        # We are also detecting if the sentence "listed more than once in the configuration"
        # appears in the repoquery output. If it does, we ignore it.
        # This later check is supposed to avoid duplicate repofiles being defined in the system,
        # this is a super corner case and should not happen everytime, but if it does, we are aware now.
        packages = [
            tuple(str(line).replace('"', "").split("\t"))
            for line in repoquery_output.split("\n")
            if (line.strip() and "listed more than once in the configuration"
                not in line.lower())
        ]

        # Sort out for the most recent kernel with reverse order
        # In case `repoquery` returns more than one kernel in the output
        # We display the latest one to the user.
        packages.sort(key=lambda x: x[0], reverse=True)

        _, latest_kernel, repoid = packages[0]

        # The loaded kernel version
        uname_output, _ = run_subprocess(["uname", "-r"], print_output=False)
        loaded_kernel = uname_output.rsplit(".", 1)[0]
        match = compare_package_versions(latest_kernel, str(loaded_kernel))

        if match == 0:
            logger.info(
                "The currently loaded kernel is at the latest version.")
        else:
            repos_message = ("in the enabled system repositories"
                             if not reposdir else
                             "in repositories defined in the %s folder" %
                             reposdir)
            logger.critical(
                "The version of the loaded kernel is different from the latest version %s.\n"
                " Latest kernel version available in %s: %s\n"
                " Loaded kernel version: %s\n\n"
                "To proceed with the conversion, update the kernel version by executing the following step:\n\n"
                "1. yum install %s-%s -y\n"
                "2. reboot" % (repos_message, repoid, latest_kernel,
                               loaded_kernel, package_to_check, latest_kernel))
    else:
        # Repoquery failed to detected any kernel or kernel-core packages in it's repositories
        # we allow the user to provide a environment variable to override the functionality and proceed
        # with the conversion, otherwise, we just throw an critical logging to them.
        unsupported_skip = os.environ.get(
            "CONVERT2RHEL_UNSUPPORTED_SKIP_KERNEL_CURRENCY_CHECK", "0")
        if unsupported_skip == "1":
            logger.warning(
                "Detected 'CONVERT2RHEL_UNSUPPORTED_SKIP_KERNEL_CURRENCY_CHECK' environment variable, we will skip "
                "the %s comparison.\n"
                "Beware, this could leave your system in a broken state. " %
                package_to_check)
        else:
            logger.critical(
                "Could not find any %s from repositories to compare against the loaded kernel.\n"
                "Please, check if you have any vendor repositories enabled to proceed with the conversion.\n"
                "If you wish to ignore this message, set the environment variable "
                "'CONVERT2RHEL_UNSUPPORTED_SKIP_KERNEL_CURRENCY_CHECK' to 1." %
                package_to_check)
Ejemplo n.º 21
0
 def is_rpm_installed(name):
     _, return_code = run_subprocess("rpm -q '%s'" % name,
                                     print_cmd=False,
                                     print_output=False)
     return return_code == 0
Ejemplo n.º 22
0
 def _get_architecture(self):
     arch, _ = utils.run_subprocess("uname -i", print_output=False)
     arch = arch.strip()  # Remove newline
     self.logger.info("%-20s %s" % ("Architecture:", arch))
     return arch
Ejemplo n.º 23
0
def call_yum_cmd(command,
                 args="",
                 print_output=True,
                 enable_repos=None,
                 disable_repos=None,
                 set_releasever=True):
    """Call yum command and optionally print its output.

    The enable_repos and disable_repos function parameters accept lists and they override the default use of repos,
    which is:
    * --disablerepo yum option = "*" by default OR passed through a CLI option by the user
    * --enablerepo yum option = is the repo enabled through subscription-manager based on a convert2rhel configuration
      file for the particular system OR passed through a CLI option by the user

    YUM/DNF typically expands the $releasever variable used in repofiles. However it fails to do so after we remove the
    release packages (centos-release, oraclelinux-release, etc.) and before the redhat-release package is installed.
    By default, for the above reason, we provide the --releasever option to each yum call. However before we remove the
    release package, we need YUM/DNF to expand the variable by itself (for that, use set_releasever=False).
    """
    loggerinst = logging.getLogger(__name__)

    cmd = "yum %s -y" % command

    # The --disablerepo yum option must be added before --enablerepo,
    #   otherwise the enabled repo gets disabled if --disablerepo="*" is used
    repos_to_disable = []
    if isinstance(disable_repos, list):
        repos_to_disable = disable_repos
    else:
        repos_to_disable = tool_opts.disablerepo

    for repo in repos_to_disable:
        cmd += " --disablerepo=%s" % repo

    if set_releasever and system_info.releasever:
        cmd += " --releasever=%s" % system_info.releasever

    # Without the release package installed, dnf can't determine the modularity platform ID.
    if system_info.version.major == 8:
        cmd += " --setopt=module_platform_id=platform:el8"

    repos_to_enable = []
    if isinstance(enable_repos, list):
        repos_to_enable = enable_repos
    else:
        # When using subscription-manager for the conversion, use those repos for the yum call that have been enabled
        # through subscription-manager
        repos_to_enable = system_info.submgr_enabled_repos if not tool_opts.disable_submgr else tool_opts.enablerepo

    for repo in repos_to_enable:
        cmd += " --enablerepo=%s" % repo

    if args:
        cmd += " " + args

    stdout, returncode = utils.run_subprocess(cmd, print_output=print_output)
    # handle when yum returns non-zero code when there is nothing to do
    nothing_to_do_error_exists = stdout.endswith("Error: Nothing to do\n")
    if returncode == 1 and nothing_to_do_error_exists:
        loggerinst.debug("Yum has nothing to do. Ignoring.")
        returncode = 0
    return stdout, returncode