Blame dnf/cli/cli.py

Packit 6f3914
# Copyright 2005 Duke University
Packit 6f3914
# Copyright (C) 2012-2016 Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
# This program is free software; you can redistribute it and/or modify
Packit 6f3914
# it under the terms of the GNU General Public License as published by
Packit 6f3914
# the Free Software Foundation; either version 2 of the License, or
Packit 6f3914
# (at your option) any later version.
Packit 6f3914
#
Packit 6f3914
# This program is distributed in the hope that it will be useful,
Packit 6f3914
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6f3914
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6f3914
# GNU Library General Public License for more details.
Packit 6f3914
#
Packit 6f3914
# You should have received a copy of the GNU General Public License
Packit 6f3914
# along with this program; if not, write to the Free Software
Packit 6f3914
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit 6f3914
#
Packit 6f3914
# Written by Seth Vidal
Packit 6f3914
Packit 6f3914
"""
Packit 6f3914
Command line interface yum class and related.
Packit 6f3914
"""
Packit 6f3914
Packit 6f3914
from __future__ import print_function
Packit 6f3914
from __future__ import absolute_import
Packit 6f3914
from __future__ import unicode_literals
Packit 6f3914
Packit 6f3914
try:
Packit 6f3914
    from collections.abc import Sequence
Packit 6f3914
except ImportError:
Packit 6f3914
    from collections import Sequence
Packit 6f3914
import datetime
Packit 6f3914
import logging
Packit 6f3914
import operator
Packit 6f3914
import os
Packit 6f3914
import random
Packit 6f3914
import rpm
Packit 6f3914
import sys
Packit 6f3914
import time
Packit 6f3914
Packit 6f3914
import hawkey
Packit 6f3914
import libdnf.transaction
Packit 6f3914
Packit 6f3914
from . import output
Packit 6f3914
from dnf.cli import CliError
Packit 6f3914
from dnf.i18n import ucd, _
Packit 6f3914
import dnf
Packit 6f3914
import dnf.cli.aliases
Packit 6f3914
import dnf.cli.commands
Packit 6f3914
import dnf.cli.commands.alias
Packit 6f3914
import dnf.cli.commands.autoremove
Packit 6f3914
import dnf.cli.commands.check
Packit 6f3914
import dnf.cli.commands.clean
Packit 6f3914
import dnf.cli.commands.deplist
Packit 6f3914
import dnf.cli.commands.distrosync
Packit 6f3914
import dnf.cli.commands.downgrade
Packit 6f3914
import dnf.cli.commands.group
Packit 6f3914
import dnf.cli.commands.install
Packit 6f3914
import dnf.cli.commands.makecache
Packit 6f3914
import dnf.cli.commands.mark
Packit 6f3914
import dnf.cli.commands.module
Packit 6f3914
import dnf.cli.commands.reinstall
Packit 6f3914
import dnf.cli.commands.remove
Packit 6f3914
import dnf.cli.commands.repolist
Packit 6f3914
import dnf.cli.commands.repoquery
Packit 6f3914
import dnf.cli.commands.search
Packit 6f3914
import dnf.cli.commands.shell
Packit 6f3914
import dnf.cli.commands.swap
Packit 6f3914
import dnf.cli.commands.updateinfo
Packit 6f3914
import dnf.cli.commands.upgrade
Packit 6f3914
import dnf.cli.commands.upgrademinimal
Packit 6f3914
import dnf.cli.demand
Packit 6f3914
import dnf.cli.format
Packit 6f3914
import dnf.cli.option_parser
Packit 6f3914
import dnf.conf
Packit 6f3914
import dnf.conf.substitutions
Packit 6f3914
import dnf.const
Packit 6f3914
import dnf.db.history
Packit 6f3914
import dnf.exceptions
Packit 6f3914
import dnf.logging
Packit 6f3914
import dnf.persistor
Packit 6f3914
import dnf.plugin
Packit 6f3914
import dnf.rpm
Packit 6f3914
import dnf.sack
Packit 6f3914
import dnf.transaction
Packit 6f3914
import dnf.util
Packit 6f3914
import dnf.yum.misc
Packit 6f3914
Packit 6f3914
logger = logging.getLogger('dnf')
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _add_pkg_simple_list_lens(data, pkg, indent=''):
Packit 6f3914
    """ Get the length of each pkg's column. Add that to data.
Packit 6f3914
        This "knows" about simpleList and printVer. """
Packit 6f3914
    na = len(pkg.name) + 1 + len(pkg.arch) + len(indent)
Packit 6f3914
    ver = len(pkg.evr)
Packit 6f3914
    rid = len(pkg._from_repo)
Packit 6f3914
    for (d, v) in (('na', na), ('ver', ver), ('rid', rid)):
Packit 6f3914
        data[d].setdefault(v, 0)
Packit 6f3914
        data[d][v] += 1
Packit 6f3914
Packit 6f3914
Packit 6f3914
def _list_cmd_calc_columns(output, ypl):
Packit 6f3914
    """ Work out the dynamic size of the columns to pass to fmtColumns. """
Packit 6f3914
    data = {'na' : {}, 'ver' : {}, 'rid' : {}}
Packit 6f3914
    for lst in (ypl.installed, ypl.available, ypl.extras, ypl.autoremove,
Packit 6f3914
                ypl.updates, ypl.recent):
Packit 6f3914
        for pkg in lst:
Packit 6f3914
            _add_pkg_simple_list_lens(data, pkg)
Packit 6f3914
    if len(ypl.obsoletes) > 0:
Packit 6f3914
        for (npkg, opkg) in ypl.obsoletesTuples:
Packit 6f3914
            _add_pkg_simple_list_lens(data, npkg)
Packit 6f3914
            _add_pkg_simple_list_lens(data, opkg, indent=" " * 4)
Packit 6f3914
Packit 6f3914
    data = [data['na'], data['ver'], data['rid']]
Packit 6f3914
    columns = output.calcColumns(data, remainder_column=1)
Packit 6f3914
    return (-columns[0], -columns[1], -columns[2])
Packit 6f3914
Packit 6f3914
Packit 6f3914
def print_versions(pkgs, base, output):
Packit 6f3914
    def sm_ui_time(x):
Packit 6f3914
        return time.strftime("%c", time.gmtime(x))
Packit 6f3914
Packit 6f3914
    rpmdb_sack = dnf.sack.rpmdb_sack(base)
Packit 6f3914
    done = False
Packit 6f3914
    for pkg in rpmdb_sack.query().installed().filterm(name=pkgs):
Packit 6f3914
        if done:
Packit 6f3914
            print("")
Packit 6f3914
        done = True
Packit 6f3914
        if pkg.epoch == '0':
Packit 6f3914
            ver = '%s-%s.%s' % (pkg.version, pkg.release, pkg.arch)
Packit 6f3914
        else:
Packit 6f3914
            ver = '%s:%s-%s.%s' % (pkg.epoch,
Packit 6f3914
                                   pkg.version, pkg.release, pkg.arch)
Packit 6f3914
        name = output.term.bold(pkg.name)
Packit 6f3914
        print(_("  Installed: %s-%s at %s") %(name, ver,
Packit 6f3914
                                              sm_ui_time(pkg.installtime)))
Packit 6f3914
        print(_("  Built    : %s at %s") % (pkg.packager if pkg.packager else "",
Packit 6f3914
                                            sm_ui_time(pkg.buildtime)))
Packit 6f3914
        # :hawkey, no changelist information yet
Packit 6f3914
        # print(_("  Committed: %s at %s") % (pkg.committer,
Packit 6f3914
        #                                    sm_ui_date(pkg.committime)))
Packit 6f3914
Packit 6f3914
Packit 6f3914
def report_module_switch(switchedModules):
Packit 6f3914
    msg1 = _("The operation would result in switching of module '{0}' stream '{1}' to "
Packit 6f3914
             "stream '{2}'")
Packit 6f3914
    for moduleName, streams in switchedModules.items():
Packit 6f3914
        logger.warning(msg1.format(moduleName, streams[0], streams[1]))
Packit 6f3914
Packit 6f3914
Packit 6f3914
class BaseCli(dnf.Base):
Packit 6f3914
    """This is the base class for yum cli."""
Packit 6f3914
Packit 6f3914
    def __init__(self, conf=None):
Packit 6f3914
        conf = conf or dnf.conf.Conf()
Packit 6f3914
        super(BaseCli, self).__init__(conf=conf)
Packit 6f3914
        self.output = output.Output(self, self.conf)
Packit 6f3914
Packit 6f3914
    def do_transaction(self, display=()):
Packit 6f3914
        """Take care of package downloading, checking, user
Packit 6f3914
        confirmation and actually running the transaction.
Packit 6f3914
Packit 6f3914
        :param display: `rpm.callback.TransactionProgress` object(s)
Packit 6f3914
        :return: history database transaction ID or None
Packit 6f3914
        """
Packit 6f3914
        if dnf.base.WITH_MODULES:
Packit 6f3914
            switchedModules = dict(self._moduleContainer.getSwitchedStreams())
Packit 6f3914
            if switchedModules:
Packit 6f3914
                report_module_switch(switchedModules)
Packit 6f3914
                msg = _("It is not possible to switch enabled streams of a module.\n"
Packit 6f3914
                        "It is recommended to remove all installed content from the module, and "
Packit 6f3914
                        "reset the module using '{prog} module reset <module_name>' command. After "
Packit 6f3914
                        "you reset the module, you can install the other stream.").format(
Packit 6f3914
                    prog=dnf.util.MAIN_PROG)
Packit 6f3914
                raise dnf.exceptions.Error(msg)
Packit 6f3914
Packit 6f3914
        trans = self.transaction
Packit 6f3914
        pkg_str = self.output.list_transaction(trans)
Packit 6f3914
        if pkg_str:
Packit 6f3914
            logger.info(pkg_str)
Packit 6f3914
Packit 6f3914
        if trans:
Packit 6f3914
            # Check which packages have to be downloaded
Packit 6f3914
            install_pkgs = []
Packit 6f3914
            rmpkgs = []
Packit 6f3914
            install_only = True
Packit 6f3914
            for tsi in trans:
Packit 6f3914
                if tsi.action in dnf.transaction.FORWARD_ACTIONS:
Packit 6f3914
                    install_pkgs.append(tsi.pkg)
Packit 6f3914
                elif tsi.action in dnf.transaction.BACKWARD_ACTIONS:
Packit 6f3914
                    install_only = False
Packit 6f3914
                    rmpkgs.append(tsi.pkg)
Packit 6f3914
Packit 6f3914
            # Close the connection to the rpmdb so that rpm doesn't hold the
Packit 6f3914
            # SIGINT handler during the downloads.
Packit 6f3914
            del self._ts
Packit 6f3914
Packit 6f3914
            # report the total download size to the user
Packit 6f3914
            if not install_pkgs:
Packit 6f3914
                self.output.reportRemoveSize(rmpkgs)
Packit 6f3914
            else:
Packit 6f3914
                self.output.reportDownloadSize(install_pkgs, install_only)
Packit 6f3914
Packit 6f3914
        if trans or self._moduleContainer.isChanged() or \
Packit 6f3914
                (self._history and (self._history.group or self._history.env)):
Packit 6f3914
            # confirm with user
Packit 6f3914
            if self.conf.downloadonly:
Packit 6f3914
                logger.info(_("{prog} will only download packages for the transaction.").format(
Packit 6f3914
                    prog=dnf.util.MAIN_PROG_UPPER))
Packit 6f3914
            elif 'test' in self.conf.tsflags:
Packit 6f3914
                logger.info(_("{prog} will only download packages, install gpg keys, and check the "
Packit 6f3914
                              "transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
Packit 6f3914
            if self._promptWanted():
Packit 6f3914
                if self.conf.assumeno or not self.output.userconfirm():
Packit 6f3914
                    raise CliError(_("Operation aborted."))
Packit 6f3914
        else:
Packit 6f3914
            logger.info(_('Nothing to do.'))
Packit 6f3914
            return
Packit 6f3914
Packit 6f3914
        if trans:
Packit 6f3914
            if install_pkgs:
Packit 6f3914
                logger.info(_('Downloading Packages:'))
Packit 6f3914
                try:
Packit 6f3914
                    total_cb = self.output.download_callback_total_cb
Packit 6f3914
                    self.download_packages(install_pkgs, self.output.progress, total_cb)
Packit 6f3914
                except dnf.exceptions.DownloadError as e:
Packit 6f3914
                    specific = dnf.cli.format.indent_block(ucd(e))
Packit 6f3914
                    errstr = _('Error downloading packages:') + '\n%s' % specific
Packit 6f3914
                    # setting the new line to prevent next chars being eaten up
Packit 6f3914
                    # by carriage returns
Packit 6f3914
                    print()
Packit 6f3914
                    raise dnf.exceptions.Error(errstr)
Packit 6f3914
            # Check GPG signatures
Packit 6f3914
            self.gpgsigcheck(install_pkgs)
Packit 6f3914
Packit 6f3914
        if self.conf.downloadonly:
Packit 6f3914
            return
Packit 6f3914
Packit 6f3914
        if not isinstance(display, Sequence):
Packit 6f3914
            display = [display]
Packit 6f3914
        display = [output.CliTransactionDisplay()] + list(display)
Packit 6f3914
        tid = super(BaseCli, self).do_transaction(display)
Packit 6f3914
Packit 6f3914
        # display last transaction (which was closed during do_transaction())
Packit 6f3914
        if tid is not None:
Packit 6f3914
            trans = self.history.old([tid])[0]
Packit 6f3914
            trans = dnf.db.group.RPMTransaction(self.history, trans._trans)
Packit 6f3914
        else:
Packit 6f3914
            trans = None
Packit 6f3914
Packit 6f3914
        if trans:
Packit 6f3914
            msg = self.output.post_transaction_output(trans)
Packit 6f3914
            logger.info(msg)
Packit 6f3914
            for tsi in trans:
Packit 6f3914
                if tsi.state == libdnf.transaction.TransactionItemState_ERROR:
Packit 6f3914
                    raise dnf.exceptions.Error(_('Transaction failed'))
Packit 6f3914
Packit 6f3914
        return tid
Packit 6f3914
Packit 6f3914
    def gpgsigcheck(self, pkgs):
Packit 6f3914
        """Perform GPG signature verification on the given packages,
Packit 6f3914
        installing keys if possible.
Packit 6f3914
Packit 6f3914
        :param pkgs: a list of package objects to verify the GPG
Packit 6f3914
           signatures of
Packit 6f3914
        :raises: Will raise :class:`Error` if there's a problem
Packit 6f3914
        """
Packit 6f3914
        error_messages = []
Packit 6f3914
        for po in pkgs:
Packit 6f3914
            result, errmsg = self._sig_check_pkg(po)
Packit 6f3914
Packit 6f3914
            if result == 0:
Packit 6f3914
                # Verified ok, or verify not req'd
Packit 6f3914
                continue
Packit 6f3914
Packit 6f3914
            elif result == 1:
Packit 6f3914
                ay = self.conf.assumeyes and not self.conf.assumeno
Packit 6f3914
                if (not sys.stdin or not sys.stdin.isatty()) and not ay:
Packit 6f3914
                    raise dnf.exceptions.Error(_('Refusing to automatically import keys when running ' \
Packit 6f3914
                            'unattended.\nUse "-y" to override.'))
Packit 6f3914
Packit 6f3914
                # the callback here expects to be able to take options which
Packit 6f3914
                # userconfirm really doesn't... so fake it
Packit 6f3914
                fn = lambda x, y, z: self.output.userconfirm()
Packit 6f3914
                try:
Packit 6f3914
                    self._get_key_for_package(po, fn)
Packit 6f3914
                except dnf.exceptions.Error as e:
Packit 6f3914
                    error_messages.append(str(e))
Packit 6f3914
Packit 6f3914
            else:
Packit 6f3914
                # Fatal error
Packit 6f3914
                error_messages.append(errmsg)
Packit 6f3914
Packit 6f3914
        if error_messages:
Packit 6f3914
            for msg in error_messages:
Packit 6f3914
                logger.critical(msg)
Packit 6f3914
            raise dnf.exceptions.Error(_("GPG check FAILED"))
Packit 6f3914
Packit 6f3914
    def latest_changelogs(self, package):
Packit 6f3914
        """Return list of changelogs for package newer then installed version"""
Packit 6f3914
        newest = None
Packit 6f3914
        # find the date of the newest changelog for installed version of package
Packit 6f3914
        # stored in rpmdb
Packit 6f3914
        for mi in self._rpmconn.readonly_ts.dbMatch('name', package.name):
Packit 6f3914
            changelogtimes = mi[rpm.RPMTAG_CHANGELOGTIME]
Packit 6f3914
            if changelogtimes:
Packit 6f3914
                newest = datetime.date.fromtimestamp(changelogtimes[0])
Packit 6f3914
                break
Packit 6f3914
        chlogs = [chlog for chlog in package.changelogs
Packit 6f3914
                  if newest is None or chlog['timestamp'] > newest]
Packit 6f3914
        return chlogs
Packit 6f3914
Packit 6f3914
    def format_changelog(self, changelog):
Packit 6f3914
        """Return changelog formatted as in spec file"""
Packit 6f3914
        chlog_str = '* %s %s\n%s\n' % (
Packit 6f3914
            changelog['timestamp'].strftime("%a %b %d %X %Y"),
Packit 6f3914
            dnf.i18n.ucd(changelog['author']),
Packit 6f3914
            dnf.i18n.ucd(changelog['text']))
Packit 6f3914
        return chlog_str
Packit 6f3914
Packit 6f3914
    def print_changelogs(self, packages):
Packit 6f3914
        # group packages by src.rpm to avoid showing duplicate changelogs
Packit 6f3914
        bysrpm = dict()
Packit 6f3914
        for p in packages:
Packit 6f3914
            # there are packages without source_name, use name then.
Packit 6f3914
            bysrpm.setdefault(p.source_name or p.name, []).append(p)
Packit 6f3914
        for source_name in sorted(bysrpm.keys()):
Packit 6f3914
            bin_packages = bysrpm[source_name]
Packit 6f3914
            print(_("Changelogs for {}").format(', '.join([str(pkg) for pkg in bin_packages])))
Packit 6f3914
            for chl in self.latest_changelogs(bin_packages[0]):
Packit 6f3914
                print(self.format_changelog(chl))
Packit 6f3914
Packit 6f3914
    def check_updates(self, patterns=(), reponame=None, print_=True, changelogs=False):
Packit 6f3914
        """Check updates matching given *patterns* in selected repository."""
Packit 6f3914
        ypl = self.returnPkgLists('upgrades', patterns, reponame=reponame)
Packit 6f3914
        if self.conf.obsoletes or self.conf.verbose:
Packit 6f3914
            typl = self.returnPkgLists('obsoletes', patterns, reponame=reponame)
Packit 6f3914
            ypl.obsoletes = typl.obsoletes
Packit 6f3914
            ypl.obsoletesTuples = typl.obsoletesTuples
Packit 6f3914
Packit 6f3914
        if print_:
Packit 6f3914
            columns = _list_cmd_calc_columns(self.output, ypl)
Packit 6f3914
            if len(ypl.updates) > 0:
Packit 6f3914
                local_pkgs = {}
Packit 6f3914
                highlight = self.output.term.MODE['bold']
Packit 6f3914
                if highlight:
Packit 6f3914
                    # Do the local/remote split we get in "yum updates"
Packit 6f3914
                    for po in sorted(ypl.updates):
Packit 6f3914
                        local = po.localPkg()
Packit 6f3914
                        if os.path.exists(local) and po.verifyLocalPkg():
Packit 6f3914
                            local_pkgs[(po.name, po.arch)] = po
Packit 6f3914
Packit 6f3914
                cul = self.conf.color_update_local
Packit 6f3914
                cur = self.conf.color_update_remote
Packit 6f3914
                self.output.listPkgs(ypl.updates, '', outputType='list',
Packit 6f3914
                              highlight_na=local_pkgs, columns=columns,
Packit 6f3914
                              highlight_modes={'=' : cul, 'not in' : cur})
Packit 6f3914
                if changelogs:
Packit 6f3914
                    self.print_changelogs(ypl.updates)
Packit 6f3914
Packit 6f3914
            if len(ypl.obsoletes) > 0:
Packit 6f3914
                print(_('Obsoleting Packages'))
Packit 6f3914
                # The tuple is (newPkg, oldPkg) ... so sort by new
Packit 6f3914
                for obtup in sorted(ypl.obsoletesTuples,
Packit 6f3914
                                    key=operator.itemgetter(0)):
Packit 6f3914
                    self.output.updatesObsoletesList(obtup, 'obsoletes',
Packit 6f3914
                                                     columns=columns)
Packit 6f3914
Packit 6f3914
        return ypl.updates or ypl.obsoletes
Packit 6f3914
Packit 6f3914
    def distro_sync_userlist(self, userlist):
Packit 6f3914
        """ Upgrade or downgrade packages to match the latest versions available
Packit 6f3914
            in the enabled repositories.
Packit 6f3914
Packit 6f3914
            :return: (exit_code, [ errors ])
Packit 6f3914
Packit 6f3914
            exit_code is::
Packit 6f3914
                0 = we're done, exit
Packit 6f3914
                1 = we've errored, exit with error string
Packit 6f3914
                2 = we've got work yet to do, onto the next stage
Packit 6f3914
        """
Packit 6f3914
        oldcount = self._goal.req_length()
Packit 6f3914
        if len(userlist) == 0:
Packit 6f3914
            self.distro_sync()
Packit 6f3914
        else:
Packit 6f3914
            for pkg_spec in userlist:
Packit 6f3914
                self.distro_sync(pkg_spec)
Packit 6f3914
Packit 6f3914
        cnt = self._goal.req_length() - oldcount
Packit 6f3914
        if cnt <= 0 and not self._goal.req_has_distupgrade_all():
Packit 6f3914
            msg = _('No packages marked for distribution synchronization.')
Packit 6f3914
            raise dnf.exceptions.Error(msg)
Packit 6f3914
Packit 6f3914
    def downgradePkgs(self, specs=[], file_pkgs=[], strict=False):
Packit 6f3914
        """Attempt to take the user specified list of packages or
Packit 6f3914
        wildcards and downgrade them. If a complete version number is
Packit 6f3914
        specified, attempt to downgrade them to the specified version
Packit 6f3914
Packit 6f3914
        :param specs: a list of names or wildcards specifying packages to downgrade
Packit 6f3914
        :param file_pkgs: a list of pkg objects from local files
Packit 6f3914
        """
Packit 6f3914
Packit Service 8b25b4
        result = False
Packit 6f3914
        for pkg in file_pkgs:
Packit 6f3914
            try:
Packit 6f3914
                self.package_downgrade(pkg, strict=strict)
Packit Service 8b25b4
                result = True
Packit 6f3914
            except dnf.exceptions.MarkingError as e:
Packit 6f3914
                logger.info(_('No match for argument: %s'),
Packit 6f3914
                            self.output.term.bold(pkg.location))
Packit 6f3914
Packit 6f3914
        for arg in specs:
Packit 6f3914
            try:
Packit 6f3914
                self.downgrade_to(arg, strict=strict)
Packit Service 8b25b4
                result = True
Packit 6f3914
            except dnf.exceptions.PackageNotFoundError as err:
Packit 6f3914
                msg = _('No package %s available.')
Packit 6f3914
                logger.info(msg, self.output.term.bold(arg))
Packit 6f3914
            except dnf.exceptions.PackagesNotInstalledError as err:
Packit 6f3914
                logger.info(_('Packages for argument %s available, but not installed.'),
Packit 6f3914
                            self.output.term.bold(err.pkg_spec))
Packit 6f3914
            except dnf.exceptions.MarkingError:
Packit 6f3914
                assert False
Packit Service 8b25b4
Packit Service 8b25b4
        if not result:
Packit 6f3914
            raise dnf.exceptions.Error(_('No packages marked for downgrade.'))
Packit 6f3914
Packit 6f3914
    def output_packages(self, basecmd, pkgnarrow='all', patterns=(), reponame=None):
Packit 6f3914
        """Output selection *pkgnarrow* of packages matching *patterns* and *repoid*."""
Packit 6f3914
        try:
Packit 6f3914
            highlight = self.output.term.MODE['bold']
Packit 6f3914
            ypl = self.returnPkgLists(
Packit 6f3914
                pkgnarrow, patterns, installed_available=highlight, reponame=reponame)
Packit 6f3914
        except dnf.exceptions.Error as e:
Packit 6f3914
            return 1, [str(e)]
Packit 6f3914
        else:
Packit 6f3914
            update_pkgs = {}
Packit 6f3914
            inst_pkgs = {}
Packit 6f3914
            local_pkgs = {}
Packit 6f3914
Packit 6f3914
            columns = None
Packit 6f3914
            if basecmd == 'list':
Packit 6f3914
                # Dynamically size the columns
Packit 6f3914
                columns = _list_cmd_calc_columns(self.output, ypl)
Packit 6f3914
Packit 6f3914
            if highlight and ypl.installed:
Packit 6f3914
                #  If we have installed and available lists, then do the
Packit 6f3914
                # highlighting for the installed packages so you can see what's
Packit 6f3914
                # available to update, an extra, or newer than what we have.
Packit 6f3914
                for pkg in (ypl.hidden_available +
Packit 6f3914
                            ypl.reinstall_available +
Packit 6f3914
                            ypl.old_available):
Packit 6f3914
                    key = (pkg.name, pkg.arch)
Packit 6f3914
                    if key not in update_pkgs or pkg > update_pkgs[key]:
Packit 6f3914
                        update_pkgs[key] = pkg
Packit 6f3914
Packit 6f3914
            if highlight and ypl.available:
Packit 6f3914
                #  If we have installed and available lists, then do the
Packit 6f3914
                # highlighting for the available packages so you can see what's
Packit 6f3914
                # available to install vs. update vs. old.
Packit 6f3914
                for pkg in ypl.hidden_installed:
Packit 6f3914
                    key = (pkg.name, pkg.arch)
Packit 6f3914
                    if key not in inst_pkgs or pkg > inst_pkgs[key]:
Packit 6f3914
                        inst_pkgs[key] = pkg
Packit 6f3914
Packit 6f3914
            if highlight and ypl.updates:
Packit 6f3914
                # Do the local/remote split we get in "yum updates"
Packit 6f3914
                for po in sorted(ypl.updates):
Packit 6f3914
                    if po.reponame != hawkey.SYSTEM_REPO_NAME:
Packit 6f3914
                        local_pkgs[(po.name, po.arch)] = po
Packit 6f3914
Packit 6f3914
            # Output the packages:
Packit 6f3914
            clio = self.conf.color_list_installed_older
Packit 6f3914
            clin = self.conf.color_list_installed_newer
Packit 6f3914
            clir = self.conf.color_list_installed_reinstall
Packit 6f3914
            clie = self.conf.color_list_installed_extra
Packit 6f3914
            rip = self.output.listPkgs(ypl.installed, _('Installed Packages'), basecmd,
Packit 6f3914
                                highlight_na=update_pkgs, columns=columns,
Packit 6f3914
                                highlight_modes={'>' : clio, '<' : clin,
Packit 6f3914
                                                 '=' : clir, 'not in' : clie})
Packit 6f3914
            clau = self.conf.color_list_available_upgrade
Packit 6f3914
            clad = self.conf.color_list_available_downgrade
Packit 6f3914
            clar = self.conf.color_list_available_reinstall
Packit 6f3914
            clai = self.conf.color_list_available_install
Packit 6f3914
            rap = self.output.listPkgs(ypl.available, _('Available Packages'), basecmd,
Packit 6f3914
                                highlight_na=inst_pkgs, columns=columns,
Packit 6f3914
                                highlight_modes={'<' : clau, '>' : clad,
Packit 6f3914
                                                 '=' : clar, 'not in' : clai})
Packit 6f3914
            raep = self.output.listPkgs(ypl.autoremove, _('Autoremove Packages'),
Packit 6f3914
                                basecmd, columns=columns)
Packit 6f3914
            rep = self.output.listPkgs(ypl.extras, _('Extra Packages'), basecmd,
Packit 6f3914
                                columns=columns)
Packit 6f3914
            cul = self.conf.color_update_local
Packit 6f3914
            cur = self.conf.color_update_remote
Packit 6f3914
            rup = self.output.listPkgs(ypl.updates, _('Available Upgrades'), basecmd,
Packit 6f3914
                                highlight_na=local_pkgs, columns=columns,
Packit 6f3914
                                highlight_modes={'=' : cul, 'not in' : cur})
Packit 6f3914
Packit 6f3914
            # XXX put this into the ListCommand at some point
Packit 6f3914
            if len(ypl.obsoletes) > 0 and basecmd == 'list':
Packit 6f3914
            # if we've looked up obsolete lists and it's a list request
Packit 6f3914
                rop = [0, '']
Packit 6f3914
                print(_('Obsoleting Packages'))
Packit 6f3914
                for obtup in sorted(ypl.obsoletesTuples,
Packit 6f3914
                                    key=operator.itemgetter(0)):
Packit 6f3914
                    self.output.updatesObsoletesList(obtup, 'obsoletes',
Packit 6f3914
                                                     columns=columns)
Packit 6f3914
            else:
Packit 6f3914
                rop = self.output.listPkgs(ypl.obsoletes, _('Obsoleting Packages'),
Packit 6f3914
                                    basecmd, columns=columns)
Packit 6f3914
            rrap = self.output.listPkgs(ypl.recent, _('Recently Added Packages'),
Packit 6f3914
                                 basecmd, columns=columns)
Packit 6f3914
            if len(patterns) and \
Packit 6f3914
                rrap[0] and rop[0] and rup[0] and rep[0] and rap[0] and \
Packit 6f3914
                raep[0] and rip[0]:
Packit 6f3914
                raise dnf.exceptions.Error(_('No matching Packages to list'))
Packit 6f3914
Packit 6f3914
    def returnPkgLists(self, pkgnarrow='all', patterns=None,
Packit 6f3914
                       installed_available=False, reponame=None):
Packit 6f3914
        """Return a :class:`dnf.yum.misc.GenericHolder` object containing
Packit 6f3914
        lists of package objects that match the given names or wildcards.
Packit 6f3914
Packit 6f3914
        :param pkgnarrow: a string specifying which types of packages
Packit 6f3914
           lists to produce, such as updates, installed, available, etc.
Packit 6f3914
        :param patterns: a list of names or wildcards specifying
Packit 6f3914
           packages to list
Packit 6f3914
        :param installed_available: whether the available package list
Packit 6f3914
           is present as .hidden_available when doing all, available,
Packit 6f3914
           or installed
Packit 6f3914
        :param reponame: limit packages list to the given repository
Packit 6f3914
Packit 6f3914
        :return: a :class:`dnf.yum.misc.GenericHolder` instance with the
Packit 6f3914
           following lists defined::
Packit 6f3914
Packit 6f3914
             available = list of packageObjects
Packit 6f3914
             installed = list of packageObjects
Packit 6f3914
             upgrades = tuples of packageObjects (updating, installed)
Packit 6f3914
             extras = list of packageObjects
Packit 6f3914
             obsoletes = tuples of packageObjects (obsoleting, installed)
Packit 6f3914
             recent = list of packageObjects
Packit 6f3914
        """
Packit 6f3914
Packit 6f3914
        done_hidden_available = False
Packit 6f3914
        done_hidden_installed = False
Packit 6f3914
        if installed_available and pkgnarrow == 'installed':
Packit 6f3914
            done_hidden_available = True
Packit 6f3914
            pkgnarrow = 'all'
Packit 6f3914
        elif installed_available and pkgnarrow == 'available':
Packit 6f3914
            done_hidden_installed = True
Packit 6f3914
            pkgnarrow = 'all'
Packit 6f3914
Packit 6f3914
        ypl = self._do_package_lists(
Packit 6f3914
            pkgnarrow, patterns, ignore_case=True, reponame=reponame)
Packit 6f3914
        if self.conf.showdupesfromrepos:
Packit 6f3914
            for pkg in ypl.reinstall_available:
Packit 6f3914
                if not pkg.installed and not done_hidden_available:
Packit 6f3914
                    ypl.available.append(pkg)
Packit 6f3914
Packit 6f3914
        if installed_available:
Packit 6f3914
            ypl.hidden_available = ypl.available
Packit 6f3914
            ypl.hidden_installed = ypl.installed
Packit 6f3914
        if done_hidden_available:
Packit 6f3914
            ypl.available = []
Packit 6f3914
        if done_hidden_installed:
Packit 6f3914
            ypl.installed = []
Packit 6f3914
        return ypl
Packit 6f3914
Packit 6f3914
    def provides(self, args):
Packit 6f3914
        """Print out a list of packages that provide the given file or
Packit 6f3914
        feature.  This a cli wrapper to the provides methods in the
Packit 6f3914
        rpmdb and pkgsack.
Packit 6f3914
Packit 6f3914
        :param args: the name of a file or feature to search for
Packit 6f3914
        :return: (exit_code, [ errors ])
Packit 6f3914
Packit 6f3914
        exit_code is::
Packit 6f3914
Packit 6f3914
            0 = we're done, exit
Packit 6f3914
            1 = we've errored, exit with error string
Packit 6f3914
            2 = we've got work yet to do, onto the next stage
Packit 6f3914
        """
Packit 6f3914
        # always in showdups mode
Packit 6f3914
        old_sdup = self.conf.showdupesfromrepos
Packit 6f3914
        self.conf.showdupesfromrepos = True
Packit 6f3914
Packit 6f3914
        matches = []
Packit 6f3914
        used_search_strings = []
Packit 6f3914
        for spec in args:
Packit 6f3914
            query, used_search_string = super(BaseCli, self).provides(spec)
Packit 6f3914
            matches.extend(query)
Packit 6f3914
            used_search_strings.extend(used_search_string)
Packit 6f3914
        for pkg in sorted(matches):
Packit 6f3914
            self.output.matchcallback_verbose(pkg, used_search_strings, args)
Packit 6f3914
        self.conf.showdupesfromrepos = old_sdup
Packit 6f3914
Packit 6f3914
        if not matches:
Packit 6f3914
            raise dnf.exceptions.Error(_('No Matches found'))
Packit 6f3914
Packit 6f3914
    def _promptWanted(self):
Packit 6f3914
        # shortcut for the always-off/always-on options
Packit 6f3914
        if self.conf.assumeyes and not self.conf.assumeno:
Packit 6f3914
            return False
Packit 6f3914
        return True
Packit 6f3914
Packit 6f3914
    def _history_get_transactions(self, extcmds):
Packit 6f3914
        if not extcmds:
Packit 6f3914
            logger.critical(_('No transaction ID given'))
Packit 6f3914
            return None
Packit 6f3914
Packit 6f3914
        old = self.history.old(extcmds)
Packit 6f3914
        if not old:
Packit 6f3914
            logger.critical(_('Not found given transaction ID'))
Packit 6f3914
            return None
Packit 6f3914
        return old
Packit 6f3914
Packit 6f3914
    def history_get_transaction(self, extcmds):
Packit 6f3914
        old = self._history_get_transactions(extcmds)
Packit 6f3914
        if old is None:
Packit 6f3914
            return None
Packit 6f3914
        if len(old) > 1:
Packit 6f3914
            logger.critical(_('Found more than one transaction ID!'))
Packit 6f3914
        return old[0]
Packit 6f3914
Packit 6f3914
    def history_rollback_transaction(self, extcmd):
Packit 6f3914
        """Rollback given transaction."""
Packit 6f3914
        old = self.history_get_transaction((extcmd,))
Packit 6f3914
        if old is None:
Packit 6f3914
            return 1, ['Failed history rollback, no transaction']
Packit 6f3914
        last = self.history.last()
Packit 6f3914
        if last is None:
Packit 6f3914
            return 1, ['Failed history rollback, no last?']
Packit 6f3914
        if old.tid == last.tid:
Packit 6f3914
            return 0, ['Rollback to current, nothing to do']
Packit 6f3914
Packit 6f3914
        mobj = None
Packit 6f3914
        for trans in self.history.old(list(range(old.tid + 1, last.tid + 1))):
Packit 6f3914
            if trans.altered_lt_rpmdb:
Packit 6f3914
                logger.warning(_('Transaction history is incomplete, before %u.'), trans.tid)
Packit 6f3914
            elif trans.altered_gt_rpmdb:
Packit 6f3914
                logger.warning(_('Transaction history is incomplete, after %u.'), trans.tid)
Packit 6f3914
Packit 6f3914
            if mobj is None:
Packit 6f3914
                mobj = dnf.db.history.MergedTransactionWrapper(trans)
Packit 6f3914
            else:
Packit 6f3914
                mobj.merge(trans)
Packit 6f3914
Packit 6f3914
        tm = dnf.util.normalize_time(old.beg_timestamp)
Packit 6f3914
        print("Rollback to transaction %u, from %s" % (old.tid, tm))
Packit 6f3914
        print(self.output.fmtKeyValFill("  Undoing the following transactions: ",
Packit 6f3914
                                        ", ".join((str(x) for x in mobj.tids()))))
Packit 6f3914
        self.output.historyInfoCmdPkgsAltered(mobj)  # :todo
Packit 6f3914
Packit 6f3914
#        history = dnf.history.open_history(self.history)  # :todo
Packit 6f3914
#        m = libdnf.transaction.MergedTransaction()
Packit 6f3914
Packit 6f3914
#        return
Packit 6f3914
Packit 6f3914
#        operations = dnf.history.NEVRAOperations()
Packit 6f3914
#        for id_ in range(old.tid + 1, last.tid + 1):
Packit 6f3914
#            operations += history.transaction_nevra_ops(id_)
Packit 6f3914
Packit 6f3914
        try:
Packit 6f3914
            self._history_undo_operations(mobj, old.tid + 1, True, strict=self.conf.strict)
Packit 6f3914
        except dnf.exceptions.PackagesNotInstalledError as err:
Packit 6f3914
            raise
Packit 6f3914
            logger.info(_('No package %s installed.'),
Packit 6f3914
                        self.output.term.bold(ucd(err.pkg_spec)))
Packit 6f3914
            return 1, ['A transaction cannot be undone']
Packit 6f3914
        except dnf.exceptions.PackagesNotAvailableError as err:
Packit 6f3914
            raise
Packit 6f3914
            logger.info(_('No package %s available.'),
Packit 6f3914
                        self.output.term.bold(ucd(err.pkg_spec)))
Packit 6f3914
            return 1, ['A transaction cannot be undone']
Packit 6f3914
        except dnf.exceptions.MarkingError:
Packit 6f3914
            raise
Packit 6f3914
            assert False
Packit 6f3914
        else:
Packit 6f3914
            return 2, ["Rollback to transaction %u" % (old.tid,)]
Packit 6f3914
Packit 6f3914
    def history_undo_transaction(self, extcmd):
Packit 6f3914
        """Undo given transaction."""
Packit 6f3914
        old = self.history_get_transaction((extcmd,))
Packit 6f3914
        if old is None:
Packit 6f3914
            return 1, ['Failed history undo']
Packit 6f3914
Packit 6f3914
        tm = dnf.util.normalize_time(old.beg_timestamp)
Packit 6f3914
        msg = _("Undoing transaction {}, from {}").format(old.tid, ucd(tm))
Packit 6f3914
        logger.info(msg)
Packit 6f3914
        self.output.historyInfoCmdPkgsAltered(old)  # :todo
Packit 6f3914
Packit 6f3914
Packit 6f3914
        mobj = dnf.db.history.MergedTransactionWrapper(old)
Packit 6f3914
Packit 6f3914
        try:
Packit 6f3914
            self._history_undo_operations(mobj, old.tid, strict=self.conf.strict)
Packit 6f3914
        except dnf.exceptions.PackagesNotInstalledError as err:
Packit 6f3914
            logger.info(_('No package %s installed.'),
Packit 6f3914
                        self.output.term.bold(ucd(err.pkg_spec)))
Packit 6f3914
            return 1, ['An operation cannot be undone']
Packit 6f3914
        except dnf.exceptions.PackagesNotAvailableError as err:
Packit 6f3914
            logger.info(_('No package %s available.'),
Packit 6f3914
                        self.output.term.bold(ucd(err.pkg_spec)))
Packit 6f3914
            return 1, ['An operation cannot be undone']
Packit 6f3914
        except dnf.exceptions.MarkingError:
Packit 6f3914
            raise
Packit 6f3914
        else:
Packit 6f3914
            return 2, ["Undoing transaction %u" % (old.tid,)]
Packit 6f3914
Packit 6f3914
class Cli(object):
Packit 6f3914
    def __init__(self, base):
Packit 6f3914
        self.base = base
Packit 6f3914
        self.cli_commands = {}
Packit 6f3914
        self.command = None
Packit 6f3914
        self.demands = dnf.cli.demand.DemandSheet()  # :api
Packit 6f3914
Packit 6f3914
        self.register_command(dnf.cli.commands.alias.AliasCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.autoremove.AutoremoveCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.check.CheckCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.clean.CleanCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.distrosync.DistroSyncCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.deplist.DeplistCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.downgrade.DowngradeCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.group.GroupCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.install.InstallCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.makecache.MakeCacheCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.mark.MarkCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.module.ModuleCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.reinstall.ReinstallCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.remove.RemoveCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.repolist.RepoListCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.repoquery.RepoQueryCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.search.SearchCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.shell.ShellCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.swap.SwapCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.updateinfo.UpdateInfoCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.upgrade.UpgradeCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.upgrademinimal.UpgradeMinimalCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.InfoCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.ListCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.ProvidesCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.CheckUpdateCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.RepoPkgsCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.HelpCommand)
Packit 6f3914
        self.register_command(dnf.cli.commands.HistoryCommand)
Packit 6f3914
Packit 6f3914
    def _configure_repos(self, opts):
Packit 6f3914
        self.base.read_all_repos(opts)
Packit 6f3914
        if opts.repofrompath:
Packit 6f3914
            for label, path in opts.repofrompath.items():
Packit 6f3914
                this_repo = self.base.repos.add_new_repo(label, self.base.conf, baseurl=[path])
Packit 6f3914
                this_repo._configure_from_options(opts)
Packit 6f3914
                # do not let this repo to be disabled
Packit 6f3914
                opts.repos_ed.append((label, "enable"))
Packit 6f3914
Packit 6f3914
        if opts.repo:
Packit 6f3914
            opts.repos_ed.insert(0, ("*", "disable"))
Packit 6f3914
            opts.repos_ed.extend([(r, "enable") for r in opts.repo])
Packit 6f3914
Packit 6f3914
        notmatch = set()
Packit 6f3914
Packit 6f3914
        # Process repo enables and disables in order
Packit 6f3914
        try:
Packit 6f3914
            for (repo, operation) in opts.repos_ed:
Packit 6f3914
                repolist = self.base.repos.get_matching(repo)
Packit 6f3914
                if not repolist:
Packit 6f3914
                    if self.base.conf.strict and operation == "enable":
Packit 6f3914
                        msg = _("Unknown repo: '%s'")
Packit 6f3914
                        raise dnf.exceptions.RepoError(msg % repo)
Packit 6f3914
                    notmatch.add(repo)
Packit 6f3914
Packit 6f3914
                if operation == "enable":
Packit 6f3914
                    repolist.enable()
Packit 6f3914
                else:
Packit 6f3914
                    repolist.disable()
Packit 6f3914
        except dnf.exceptions.ConfigError as e:
Packit 6f3914
            logger.critical(e)
Packit 6f3914
            self.optparser.print_help()
Packit 6f3914
            sys.exit(1)
Packit 6f3914
Packit 6f3914
        for repo in notmatch:
Packit 6f3914
            logger.warning(_("No repository match: %s"), repo)
Packit 6f3914
Packit 6f3914
        for rid in self.base._repo_persistor.get_expired_repos():
Packit 6f3914
            repo = self.base.repos.get(rid)
Packit 6f3914
            if repo:
Packit 6f3914
                repo._repo.expire()
Packit 6f3914
Packit 6f3914
        # setup the progress bars/callbacks
Packit 6f3914
        (bar, self.base._ds_callback) = self.base.output.setup_progress_callbacks()
Packit 6f3914
        self.base.repos.all().set_progress_bar(bar)
Packit 6f3914
        key_import = output.CliKeyImport(self.base, self.base.output)
Packit 6f3914
        self.base.repos.all()._set_key_import(key_import)
Packit 6f3914
Packit 6f3914
    def _log_essentials(self):
Packit 6f3914
        logger.debug('{prog} version: %s'.format(prog=dnf.util.MAIN_PROG_UPPER),
Packit 6f3914
                     dnf.const.VERSION)
Packit 6f3914
        logger.log(dnf.logging.DDEBUG,
Packit 6f3914
                        'Command: %s', self.cmdstring)
Packit 6f3914
        logger.log(dnf.logging.DDEBUG,
Packit 6f3914
                        'Installroot: %s', self.base.conf.installroot)
Packit 6f3914
        logger.log(dnf.logging.DDEBUG, 'Releasever: %s',
Packit 6f3914
                        self.base.conf.releasever)
Packit 6f3914
        logger.debug("cachedir: %s", self.base.conf.cachedir)
Packit 6f3914
Packit 6f3914
    def _process_demands(self):
Packit 6f3914
        demands = self.demands
Packit 6f3914
        repos = self.base.repos
Packit 6f3914
Packit 6f3914
        if demands.root_user:
Packit 6f3914
            if not dnf.util.am_i_root():
Packit Service 8b25b4
                raise dnf.exceptions.Error(
Packit Service 8b25b4
                    _('This command has to be run with superuser privileges '
Packit Service 8b25b4
                        '(under the root user on most systems).'))
Packit 6f3914
Packit 6f3914
        if demands.changelogs:
Packit 6f3914
            for repo in repos.iter_enabled():
Packit 6f3914
                repo.load_metadata_other = True
Packit 6f3914
Packit 6f3914
        if demands.cacheonly or self.base.conf.cacheonly:
Packit 6f3914
            self.base.conf.cacheonly = True
Packit 6f3914
            for repo in repos.values():
Packit 6f3914
                repo._repo.setSyncStrategy(dnf.repo.SYNC_ONLY_CACHE)
Packit 6f3914
        else:
Packit 6f3914
            if demands.freshest_metadata:
Packit 6f3914
                for repo in repos.iter_enabled():
Packit 6f3914
                    repo._repo.expire()
Packit 6f3914
            elif not demands.fresh_metadata:
Packit 6f3914
                for repo in repos.values():
Packit 6f3914
                    repo._repo.setSyncStrategy(dnf.repo.SYNC_LAZY)
Packit 6f3914
Packit 6f3914
        if demands.sack_activation:
Packit 6f3914
            self.base.fill_sack(
Packit 6f3914
                load_system_repo='auto' if self.demands.load_system_repo else False,
Packit 6f3914
                load_available_repos=self.demands.available_repos)
Packit 6f3914
Packit 6f3914
    def _parse_commands(self, opts, args):
Packit 6f3914
        """Check that the requested CLI command exists."""
Packit 6f3914
Packit 6f3914
        basecmd = opts.command
Packit 6f3914
        command_cls = self.cli_commands.get(basecmd)
Packit 6f3914
        if command_cls is None:
Packit 6f3914
            logger.critical(_('No such command: %s. Please use %s --help'),
Packit 6f3914
                            basecmd, sys.argv[0])
Packit 6f3914
            if self.base.conf.plugins:
Packit 6f3914
                logger.critical(_("It could be a {PROG} plugin command, "
Packit 6f3914
                                  "try: \"{prog} install 'dnf-command(%s)'\"").format(
Packit 6f3914
                    prog=dnf.util.MAIN_PROG, PROG=dnf.util.MAIN_PROG_UPPER), basecmd)
Packit 6f3914
            else:
Packit 6f3914
                logger.critical(_("It could be a {prog} plugin command, "
Packit 6f3914
                                  "but loading of plugins is currently disabled.").format(
Packit 6f3914
                    prog=dnf.util.MAIN_PROG_UPPER))
Packit 6f3914
            raise CliError
Packit 6f3914
        self.command = command_cls(self)
Packit 6f3914
Packit 6f3914
        logger.log(dnf.logging.DDEBUG, 'Base command: %s', basecmd)
Packit 6f3914
        logger.log(dnf.logging.DDEBUG, 'Extra commands: %s', args)
Packit 6f3914
Packit 6f3914
    def configure(self, args, option_parser=None):
Packit 6f3914
        """Parse command line arguments, and set up :attr:`self.base.conf` and
Packit 6f3914
        :attr:`self.cmds`, as well as logger objects in base instance.
Packit 6f3914
Packit 6f3914
        :param args: a list of command line arguments
Packit 6f3914
        :param option_parser: a class for parsing cli options
Packit 6f3914
        """
Packit 6f3914
        aliases = dnf.cli.aliases.Aliases()
Packit 6f3914
        args = aliases.resolve(args)
Packit 6f3914
Packit 6f3914
        self.optparser = dnf.cli.option_parser.OptionParser() \
Packit 6f3914
            if option_parser is None else option_parser
Packit 6f3914
        opts = self.optparser.parse_main_args(args)
Packit 6f3914
Packit 6f3914
        # Just print out the version if that's what the user wanted
Packit 6f3914
        if opts.version:
Packit 6f3914
            print(dnf.const.VERSION)
Packit 6f3914
            print_versions(self.base.conf.history_record_packages, self.base,
Packit 6f3914
                           self.base.output)
Packit 6f3914
            sys.exit(0)
Packit 6f3914
Packit 6f3914
        if opts.quiet:
Packit 6f3914
            opts.debuglevel = 0
Packit 6f3914
            opts.errorlevel = 2
Packit 6f3914
        if opts.verbose:
Packit 6f3914
            opts.debuglevel = opts.errorlevel = dnf.const.VERBOSE_LEVEL
Packit 6f3914
Packit 6f3914
        # Read up configuration options and initialize plugins
Packit 6f3914
        try:
Packit 6f3914
            if opts.cacheonly:
Packit 6f3914
                self.base.conf._set_value("cachedir", self.base.conf.system_cachedir,
Packit 6f3914
                                          dnf.conf.PRIO_DEFAULT)
Packit 6f3914
                self.demands.cacheonly = True
Packit 6f3914
            self.base.conf._configure_from_options(opts)
Packit 6f3914
            self._read_conf_file(opts.releasever)
Packit 6f3914
            if 'arch' in opts:
Packit 6f3914
                self.base.conf.arch = opts.arch
Packit 6f3914
            self.base.conf._adjust_conf_options()
Packit 6f3914
        except (dnf.exceptions.ConfigError, ValueError) as e:
Packit 6f3914
            logger.critical(_('Config error: %s'), e)
Packit 6f3914
            sys.exit(1)
Packit 6f3914
        except IOError as e:
Packit 6f3914
            e = '%s: %s' % (ucd(str(e)), repr(e.filename))
Packit 6f3914
            logger.critical(_('Config error: %s'), e)
Packit 6f3914
            sys.exit(1)
Packit 6f3914
        if opts.destdir is not None:
Packit 6f3914
            self.base.conf.destdir = opts.destdir
Packit 6f3914
            if not self.base.conf.downloadonly and opts.command not in (
Packit 6f3914
                    'download', 'system-upgrade', 'reposync'):
Packit 6f3914
                logger.critical(_('--destdir or --downloaddir must be used with --downloadonly '
Packit 6f3914
                                  'or download or system-upgrade command.')
Packit 6f3914
                )
Packit 6f3914
                sys.exit(1)
Packit 6f3914
        if (opts.set_enabled or opts.set_disabled) and opts.command != 'config-manager':
Packit 6f3914
            logger.critical(
Packit 6f3914
                _('--enable, --set-enabled and --disable, --set-disabled '
Packit 6f3914
                  'must be used with config-manager command.'))
Packit 6f3914
            sys.exit(1)
Packit 6f3914
Packit 6f3914
        if opts.sleeptime is not None:
Packit 6f3914
            time.sleep(random.randrange(opts.sleeptime * 60))
Packit 6f3914
Packit 6f3914
        # store the main commands & summaries, before plugins are loaded
Packit 6f3914
        self.optparser.add_commands(self.cli_commands, 'main')
Packit 6f3914
        # store the plugin commands & summaries
Packit 6f3914
        self.base.init_plugins(opts.disableplugin, opts.enableplugin, self)
Packit 6f3914
        self.optparser.add_commands(self.cli_commands,'plugin')
Packit 6f3914
Packit 6f3914
        # show help if no command specified
Packit 6f3914
        # this is done here, because we first have the full
Packit 6f3914
        # usage info after the plugins are loaded.
Packit 6f3914
        if not opts.command:
Packit 6f3914
            self.optparser.print_help()
Packit 6f3914
            sys.exit(0)
Packit 6f3914
Packit 6f3914
        # save our original args out
Packit 6f3914
        self.base.args = args
Packit 6f3914
        # save out as a nice command string
Packit 6f3914
        self.cmdstring = self.optparser.prog + ' '
Packit 6f3914
        for arg in self.base.args:
Packit 6f3914
            self.cmdstring += '%s ' % arg
Packit 6f3914
Packit 6f3914
        self._log_essentials()
Packit 6f3914
        try:
Packit 6f3914
            self._parse_commands(opts, args)
Packit 6f3914
        except CliError:
Packit 6f3914
            sys.exit(1)
Packit 6f3914
Packit 6f3914
        # show help for dnf <command> --help / --help-cmd
Packit 6f3914
        if opts.help:
Packit 6f3914
            self.optparser.print_help(self.command)
Packit 6f3914
            sys.exit(0)
Packit 6f3914
Packit 6f3914
        opts = self.optparser.parse_command_args(self.command, args)
Packit 6f3914
Packit 6f3914
        if opts.allowerasing:
Packit 6f3914
            self.demands.allow_erasing = opts.allowerasing
Packit 6f3914
            self.base._allow_erasing = True
Packit 6f3914
        if opts.freshest_metadata:
Packit 6f3914
            self.demands.freshest_metadata = opts.freshest_metadata
Packit 6f3914
        if opts.debugsolver:
Packit 6f3914
            self.base.conf.debug_solver = True
Packit 6f3914
        if opts.obsoletes:
Packit 6f3914
            self.base.conf.obsoletes = True
Packit 6f3914
        self.command.pre_configure()
Packit 6f3914
        self.base.pre_configure_plugins()
Packit 6f3914
Packit 6f3914
        # with cachedir in place we can configure stuff depending on it:
Packit 6f3914
        self.base._activate_persistor()
Packit 6f3914
Packit 6f3914
        self._configure_repos(opts)
Packit 6f3914
Packit 6f3914
        self.base.configure_plugins()
Packit 6f3914
Packit 6f3914
        self.base.conf._configure_from_options(opts)
Packit 6f3914
Packit 6f3914
        self.command.configure()
Packit 6f3914
Packit 6f3914
        if self.base.conf.destdir:
Packit 6f3914
            dnf.util.ensure_dir(self.base.conf.destdir)
Packit 6f3914
            self.base.repos.all().pkgdir = self.base.conf.destdir
Packit 6f3914
Packit 6f3914
        if self.base.conf.color != 'auto':
Packit 6f3914
            self.base.output.term.reinit(color=self.base.conf.color)
Packit 6f3914
Packit 6f3914
        if rpm.expandMacro('%_pkgverify_level') in ('signature', 'all'):
Packit 6f3914
            forcing = False
Packit 6f3914
            for repo in self.base.repos.iter_enabled():
Packit 6f3914
                if repo.gpgcheck:
Packit 6f3914
                    continue
Packit 6f3914
                repo.gpgcheck = True
Packit 6f3914
                forcing = True
Packit 6f3914
            if not self.base.conf.localpkg_gpgcheck:
Packit 6f3914
                self.base.conf.localpkg_gpgcheck = True
Packit 6f3914
                forcing = True
Packit 6f3914
            if forcing:
Packit 6f3914
                logger.warning(
Packit 6f3914
                    _("Warning: Enforcing GPG signature check globally "
Packit 6f3914
                      "as per active RPM security policy (see 'gpgcheck' in "
Packit 6f3914
                      "dnf.conf(5) for how to squelch this message)"
Packit 6f3914
                      )
Packit 6f3914
                )
Packit 6f3914
Packit 6f3914
    def _read_conf_file(self, releasever=None):
Packit 6f3914
        timer = dnf.logging.Timer('config')
Packit 6f3914
        conf = self.base.conf
Packit 6f3914
Packit 6f3914
        # replace remote config path with downloaded file
Packit 6f3914
        conf._check_remote_file('config_file_path')
Packit 6f3914
Packit 6f3914
        # search config file inside the installroot first
Packit 6f3914
        conf._search_inside_installroot('config_file_path')
Packit 6f3914
Packit 6f3914
        # check whether a config file is requested from command line and the file exists
Packit 6f3914
        filename = conf._get_value('config_file_path')
Packit 6f3914
        if (conf._get_priority('config_file_path') == dnf.conf.PRIO_COMMANDLINE) and \
Packit 6f3914
                not os.path.isfile(filename):
Packit 6f3914
            raise dnf.exceptions.ConfigError(_('Config file "{}" does not exist').format(filename))
Packit 6f3914
Packit 6f3914
        # read config
Packit 6f3914
        conf.read(priority=dnf.conf.PRIO_MAINCONFIG)
Packit 6f3914
Packit 6f3914
        # search reposdir file inside the installroot first
Packit 6f3914
        from_root = conf._search_inside_installroot('reposdir')
Packit 6f3914
        # Update vars from same root like repos were taken
Packit 6f3914
        if conf._get_priority('varsdir') == dnf.conf.PRIO_COMMANDLINE:
Packit 6f3914
            from_root = "/"
Packit 6f3914
        subst = conf.substitutions
Packit 6f3914
        subst.update_from_etc(from_root, varsdir=conf._get_value('varsdir'))
Packit 6f3914
        # cachedir, logs, releasever, and gpgkey are taken from or stored in installroot
Packit 6f3914
        if releasever is None and conf.releasever is None:
Packit 6f3914
            releasever = dnf.rpm.detect_releasever(conf.installroot)
Packit 6f3914
        elif releasever == '/':
Packit 6f3914
            releasever = dnf.rpm.detect_releasever(releasever)
Packit 6f3914
        if releasever is not None:
Packit 6f3914
            conf.releasever = releasever
Packit 6f3914
        if conf.releasever is None:
Packit 6f3914
            logger.warning(_("Unable to detect release version (use '--releasever' to specify "
Packit 6f3914
                             "release version)"))
Packit 6f3914
Packit 6f3914
        for opt in ('cachedir', 'logdir', 'persistdir'):
Packit 6f3914
            conf.prepend_installroot(opt)
Packit 6f3914
Packit 6f3914
        self.base._logging._setup_from_dnf_conf(conf)
Packit 6f3914
Packit 6f3914
        timer()
Packit 6f3914
        return conf
Packit 6f3914
Packit 6f3914
    def _populate_update_security_filter(self, opts, query, cmp_type='eq', all=None):
Packit 6f3914
        """
Packit 6f3914
Packit 6f3914
        :param opts:
Packit 6f3914
        :param query: base package set for filters
Packit 6f3914
        :param cmp_type: string like "eq", "gt", "gte", "lt", "lte"
Packit 6f3914
        :param all:
Packit 6f3914
        :return:
Packit 6f3914
        """
Packit 6f3914
        if (opts is None) and (all is None):
Packit 6f3914
            return
Packit 6f3914
        filters = []
Packit 6f3914
        if opts.bugfix or all:
Packit 6f3914
            key = {'advisory_type__' + cmp_type: 'bugfix'}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.enhancement or all:
Packit 6f3914
            key = {'advisory_type__' + cmp_type: 'enhancement'}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.newpackage or all:
Packit 6f3914
            key = {'advisory_type__' + cmp_type: 'newpackage'}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.security or all:
Packit 6f3914
            key = {'advisory_type__' + cmp_type: 'security'}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.advisory:
Packit 6f3914
            key = {'advisory__' + cmp_type: opts.advisory}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.bugzilla:
Packit 6f3914
            key = {'advisory_bug__' + cmp_type: opts.bugzilla}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.cves:
Packit 6f3914
            key = {'advisory_cve__' + cmp_type: opts.cves}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        if opts.severity:
Packit 6f3914
            key = {'advisory_severity__' + cmp_type: opts.severity}
Packit 6f3914
            filters.append(query.filter(**key))
Packit 6f3914
        self.base._update_security_filters = filters
Packit 6f3914
Packit 6f3914
    def redirect_logger(self, stdout=None, stderr=None):
Packit 6f3914
        # :api
Packit 6f3914
        """
Packit 6f3914
        Change minimal logger level for terminal output to stdout and stderr according to specific
Packit 6f3914
        command requirements
Packit 6f3914
        @param stdout: logging.INFO, logging.WARNING, ...
Packit 6f3914
        @param stderr:logging.INFO, logging.WARNING, ...
Packit 6f3914
        """
Packit 6f3914
        if stdout is not None:
Packit 6f3914
            self.base._logging.stdout_handler.setLevel(stdout)
Packit 6f3914
        if stderr is not None:
Packit 6f3914
            self.base._logging.stderr_handler.setLevel(stderr)
Packit 6f3914
Packit 6f3914
    def redirect_repo_progress(self, fo=sys.stderr):
Packit 6f3914
        progress = dnf.cli.progress.MultiFileProgressMeter(fo)
Packit 6f3914
        self.base.output.progress = progress
Packit 6f3914
        self.base.repos.all().set_progress_bar(progress)
Packit 6f3914
Packit 6f3914
    def _check_running_kernel(self):
Packit 6f3914
        kernel = self.base.sack.get_running_kernel()
Packit 6f3914
        if kernel is None:
Packit 6f3914
            return
Packit 6f3914
Packit 6f3914
        q = self.base.sack.query().filterm(provides=kernel.name)
Packit 6f3914
        q = q.installed()
Packit 6f3914
        q.filterm(advisory_type='security')
Packit 6f3914
Packit 6f3914
        ikpkg = kernel
Packit 6f3914
        for pkg in q:
Packit 6f3914
            if pkg > ikpkg:
Packit 6f3914
                ikpkg = pkg
Packit 6f3914
Packit 6f3914
        if ikpkg > kernel:
Packit 6f3914
            print('Security: %s is an installed security update' % ikpkg)
Packit 6f3914
            print('Security: %s is the currently running version' % kernel)
Packit 6f3914
Packit 6f3914
    def _option_conflict(self, option_string_1, option_string_2):
Packit 6f3914
        print(self.optparser.print_usage())
Packit 6f3914
        raise dnf.exceptions.Error(_("argument {}: not allowed with argument {}".format(
Packit 6f3914
            option_string_1, option_string_2)))
Packit 6f3914
Packit 6f3914
    def register_command(self, command_cls):
Packit 6f3914
        """Register a Command. :api"""
Packit 6f3914
        for name in command_cls.aliases:
Packit 6f3914
            if name in self.cli_commands:
Packit 6f3914
                raise dnf.exceptions.ConfigError(_('Command "%s" already defined') % name)
Packit 6f3914
            self.cli_commands[name] = command_cls
Packit 6f3914
Packit 6f3914
    def run(self):
Packit 6f3914
        """Call the base command, and pass it the extended commands or
Packit 6f3914
           arguments.
Packit 6f3914
Packit 6f3914
        :return: (exit_code, [ errors ])
Packit 6f3914
Packit 6f3914
        exit_code is::
Packit 6f3914
Packit 6f3914
            0 = we're done, exit
Packit 6f3914
            1 = we've errored, exit with error string
Packit 6f3914
            2 = we've got work yet to do, onto the next stage
Packit 6f3914
        """
Packit 6f3914
        self._process_demands()
Packit 6f3914
Packit 6f3914
        # Reports about excludes and includes (but not from plugins)
Packit 6f3914
        if self.base.conf.excludepkgs:
Packit 6f3914
            logger.debug(
Packit 6f3914
                _('Excludes in dnf.conf: ') + ", ".join(sorted(set(self.base.conf.excludepkgs))))
Packit 6f3914
        if self.base.conf.includepkgs:
Packit 6f3914
            logger.debug(
Packit 6f3914
                _('Includes in dnf.conf: ') + ", ".join(sorted(set(self.base.conf.includepkgs))))
Packit 6f3914
        for repo in self.base.repos.iter_enabled():
Packit 6f3914
            if repo.excludepkgs:
Packit 6f3914
                logger.debug(_('Excludes in repo ') + repo.id + ": "
Packit 6f3914
                             + ", ".join(sorted(set(repo.excludepkgs))))
Packit 6f3914
            if repo.includepkgs:
Packit 6f3914
                logger.debug(_('Includes in repo ') + repo.id + ": "
Packit 6f3914
                             + ", ".join(sorted(set(repo.includepkgs))))
Packit 6f3914
Packit 6f3914
        return self.command.run()