[ SEA-GHOST MINI SHELL]
#!/usr/bin/python -t
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Copyright 2006 Duke University
# Copyright 2013 Red Hat
# Written by Seth Vidal
"""
Classes for subcommands of the yum command line interface.
"""
import os
import sys
import cli
import rpm
from yum import logginglevels
from yum import _, P_
from yum import misc
import yum.Errors
import operator
import locale
import fnmatch
import time
from yum.i18n import utf8_width, utf8_width_fill, to_unicode, exception2msg
import tempfile
import shutil
import distutils.spawn
import glob
import errno
import yum.config
from yum import updateinfo
from yum.packages import parsePackages
from yum.fssnapshots import LibLVMError, lvmerr2str
def _err_mini_usage(base, basecmd):
if basecmd not in base.yum_cli_commands:
base.usage()
return
cmd = base.yum_cli_commands[basecmd]
txt = base.yum_cli_commands["help"]._makeOutput(cmd)
base.logger.critical(_(' Mini usage:\n'))
base.logger.critical(txt)
def checkRootUID(base):
"""Verify that the program is being run by the root user.
:param base: a :class:`yum.Yumbase` object.
:raises: :class:`cli.CliError`
"""
if base.conf.uid != 0:
base.logger.critical(_('You need to be root to perform this command.'))
raise cli.CliError
def checkGPGKey(base):
"""Verify that there are gpg keys for the enabled repositories in the
rpm database.
:param base: a :class:`yum.Yumbase` object.
:raises: :class:`cli.CliError`
"""
if base._override_sigchecks:
return
if not base.gpgKeyCheck():
for repo in base.repos.listEnabled():
if (repo.gpgcheck or repo.repo_gpgcheck) and not repo.gpgkey:
msg = _("""
You have enabled checking of packages via GPG keys. This is a good thing.
However, you do not have any GPG public keys installed. You need to download
the keys for packages you wish to install and install them.
You can do that by running the command:
rpm --import public.gpg.key
Alternatively you can specify the url to the key you would like to use
for a repository in the 'gpgkey' option in a repository section and yum
will install it for you.
For more information contact your distribution or package provider.
""")
base.logger.critical(msg)
base.logger.critical(_("Problem repository: %s"), repo)
raise cli.CliError
def checkPackageArg(base, basecmd, extcmds):
"""Verify that *extcmds* contains the name of at least one package for
*basecmd* to act on.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
if len(extcmds) == 0:
base.logger.critical(
_('Error: Need to pass a list of pkgs to %s') % basecmd)
_err_mini_usage(base, basecmd)
raise cli.CliError
def checkSwapPackageArg(base, basecmd, extcmds):
"""Verify that *extcmds* contains the name of at least two packages for
*basecmd* to act on.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
min_args = 2
if '--' in extcmds:
min_args = 3
if len(extcmds) < min_args:
base.logger.critical(
_('Error: Need at least two packages to %s') % basecmd)
_err_mini_usage(base, basecmd)
raise cli.CliError
def checkRepoPackageArg(base, basecmd, extcmds):
"""Verify that *extcmds* contains the name of at least one package for
*basecmd* to act on.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
if len(extcmds) < 2: # <repoid> install|remove [pkgs]
base.logger.critical(
_('Error: Need to pass a repoid. and command to %s') % basecmd)
_err_mini_usage(base, basecmd)
raise cli.CliError
repos = base.repos.findRepos(extcmds[0], name_match=True, ignore_case=True)
if not repos:
base.logger.critical(
_('Error: Need to pass a single valid repoid. to %s') % basecmd)
_err_mini_usage(base, basecmd)
raise cli.CliError
if len(repos) > 1:
repos = [r for r in repos if r.isEnabled()]
if len(repos) > 1:
repos = ", ".join([r.ui_id for r in repos])
base.logger.critical(
_('Error: Need to pass only a single valid repoid. to %s, passed: %s') % (basecmd, repos))
_err_mini_usage(base, basecmd)
raise cli.CliError
if not repos[0].isEnabled():
# Might as well just fix this...
base.repos.enableRepo(repos[0].id)
base.verbose_logger.info(
_('Repo %s has been automatically enabled.') % repos[0].ui_id)
def checkItemArg(base, basecmd, extcmds):
"""Verify that *extcmds* contains the name of at least one item for
*basecmd* to act on. Generally, the items are command-line
arguments that are not the name of a package, such as a file name
passed to provides.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
if len(extcmds) == 0:
base.logger.critical(_('Error: Need an item to match'))
_err_mini_usage(base, basecmd)
raise cli.CliError
def checkGroupArg(base, basecmd, extcmds):
"""Verify that *extcmds* contains the name of at least one group for
*basecmd* to act on.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
if len(extcmds) == 0:
base.logger.critical(_('Error: Need a group or list of groups'))
_err_mini_usage(base, basecmd)
raise cli.CliError
def checkCleanArg(base, basecmd, extcmds):
"""Verify that *extcmds* contains at least one argument, and that all
arguments in *extcmds* are valid options for clean.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
VALID_ARGS = ('headers', 'packages', 'metadata', 'dbcache', 'plugins',
'expire-cache', 'rpmdb', 'all')
if len(extcmds) == 0:
base.logger.critical(_('Error: clean requires an option: %s') % (
", ".join(VALID_ARGS)))
raise cli.CliError
for cmd in extcmds:
if cmd not in VALID_ARGS:
base.logger.critical(_('Error: invalid clean argument: %r') % cmd)
_err_mini_usage(base, basecmd)
raise cli.CliError
def checkShellArg(base, basecmd, extcmds):
"""Verify that the arguments given to 'yum shell' are valid. yum
shell can be given either no argument, or exactly one argument,
which is the name of a file.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`
"""
if len(extcmds) == 0:
base.verbose_logger.debug(_("No argument to shell"))
elif len(extcmds) == 1:
base.verbose_logger.debug(_("Filename passed to shell: %s"),
extcmds[0])
if not os.path.isfile(extcmds[0]):
base.logger.critical(
_("File %s given as argument to shell does not exist."),
extcmds[0])
base.usage()
raise cli.CliError
else:
base.logger.critical(
_("Error: more than one file given as argument to shell."))
base.usage()
raise cli.CliError
def checkEnabledRepo(base, possible_local_files=[]):
"""Verify that there is at least one enabled repo.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
:raises: :class:`cli.CliError`:
"""
if base.repos.listEnabled():
return
for lfile in possible_local_files:
if lfile.endswith(".rpm") and (yum.misc.re_remote_url(lfile) or
os.path.exists(lfile)):
return
# runs prereposetup (which "most" plugins currently use to add repos.)
base.pkgSack
if base.repos.listEnabled():
return
msg = _('There are no enabled repos.\n'
' Run "yum repolist all" to see the repos you have.\n'
' To enable Red Hat Subscription Management repositories:\n'
' subscription-manager repos --enable <repo>\n'
' To enable custom repositories:\n'
' yum-config-manager --enable <repo>')
base.logger.critical(msg)
raise cli.CliError
class YumCommand:
"""An abstract base class that defines the methods needed by the cli
to execute a specific command. Subclasses must override at least
:func:`getUsage` and :func:`getSummary`.
"""
def __init__(self):
self.done_command_once = False
self.hidden = False
def doneCommand(self, base, msg, *args):
""" Output *msg* the first time that this method is called, and do
nothing on subsequent calls. This is to prevent duplicate
messages from being printed for the same command.
:param base: a :class:`yum.Yumbase` object
:param msg: the message to be output
:param *args: additional arguments associated with the message
"""
if not self.done_command_once:
base.verbose_logger.info(logginglevels.INFO_2, msg, *args)
self.done_command_once = True
def getNames(self):
"""Return a list of strings that are the names of the command.
The command can be called from the command line by using any
of these names.
:return: a list containing the names of the command
"""
return []
def getUsage(self):
"""Return a usage string for the command, including arguments.
:return: a usage string for the command
"""
raise NotImplementedError
def getSummary(self):
"""Return a one line summary of what the command does.
:return: a one line summary of what the command does
"""
raise NotImplementedError
def doCheck(self, base, basecmd, extcmds):
"""Verify that various conditions are met so that the command
can run.
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being checked for
:param extcmds: a list of arguments passed to *basecmd*
"""
pass
def doCommand(self, base, basecmd, extcmds):
"""Execute the command
:param base: a :class:`yum.Yumbase` object.
:param basecmd: the name of the command being executed
:param extcmds: a list of arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
return 0, [_('Nothing to do')]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before the
command can run
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return True
# Some of this is subjective, esp. between past/present, but roughly use:
#
# write = I'm using package data to alter the rpmdb in anyway.
# read-only:future = I'm providing data that is likely to result in a
# future write, so we might as well do it now.
# Eg. yum check-update && yum update -q -y
# read-only:present = I'm providing data about the present state of
# packages in the repo.
# Eg. yum list yum
# read-only:past = I'm providing context data about past writes, or just
# anything that is available is good enough for me
# (speed is much better than quality).
# Eg. yum history info
# Eg. TAB completion
#
# ...default is write, which does the same thing we always did (obey
# metadata_expire and live with it).
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'write'
class InstallCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
install command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of
these names.
:return: a list containing the names of this command
"""
return ['install', 'install-n', 'install-na', 'install-nevra']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return _("PACKAGE...")
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Install a package or packages on your system")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
that there are enabled repositories with gpg keys, and that
this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkPackageArg(base, basecmd, extcmds)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Setting up Install Process"))
return base.installPkgs(extcmds, basecmd=basecmd)
class UpdateCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
update command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can by called from the command line by using any of
these names.
:return: a list containing the names of this command
"""
return ['update', 'update-to']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return _("[PACKAGE...]")
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Update a package or packages on your system")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that there are enabled repositories with gpg
keys, and that this command is being run by the root user.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Setting up Update Process"))
ret = base.updatePkgs(extcmds, update_to=(basecmd == 'update-to'))
updateinfo.remove_txmbrs(base)
return ret
class DistroSyncCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
distro-synch command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['distribution-synchronization', 'distro-sync', 'distupgrade']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return _("[PACKAGE...]")
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Synchronize installed packages to the latest available versions")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
and that there are enabled repositories with gpg keys.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Setting up Distribution Synchronization Process"))
base.conf.obsoletes = 1
ret = base.distroSyncPkgs(extcmds)
updateinfo.remove_txmbrs(base)
return ret
def _add_pkg_simple_list_lens(data, pkg, indent=''):
""" Get the length of each pkg's column. Add that to data.
This "knows" about simpleList and printVer. """
na = len(pkg.name) + 1 + len(pkg.arch) + len(indent)
ver = len(pkg.version) + 1 + len(pkg.release)
rid = len(pkg.ui_from_repo)
if pkg.epoch != '0':
ver += len(pkg.epoch) + 1
for (d, v) in (('na', na), ('ver', ver), ('rid', rid)):
data[d].setdefault(v, 0)
data[d][v] += 1
def _list_cmd_calc_columns(base, ypl):
""" Work out the dynamic size of the columns to pass to fmtColumns. """
data = {'na' : {}, 'ver' : {}, 'rid' : {}}
for lst in (ypl.installed, ypl.available, ypl.extras,
ypl.updates, ypl.recent):
for pkg in lst:
_add_pkg_simple_list_lens(data, pkg)
if len(ypl.obsoletes) > 0:
for (npkg, opkg) in ypl.obsoletesTuples:
_add_pkg_simple_list_lens(data, npkg)
_add_pkg_simple_list_lens(data, opkg, indent=" " * 4)
data = [data['na'], data['ver'], data['rid']]
columns = base.calcColumns(data, remainder_column=1)
return (-columns[0], -columns[1], -columns[2])
def _cmdline_exclude(pkgs, cmdline_excludes):
""" Do an extra exclude for installed packages that match the cmd line. """
if not cmdline_excludes:
return pkgs
e,m,u = parsePackages(pkgs, cmdline_excludes)
excluded = set(e + m)
return [po for po in pkgs if po not in excluded]
class InfoCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
info command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['info']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[PACKAGE|all|available|installed|updates|distro-extras|extras|obsoletes|recent]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Display details about a package or group of packages")
def doCommand(self, base, basecmd, extcmds, repoid=None):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
if extcmds and extcmds[0] in ('updates', 'obsoletes'):
updateinfo.exclude_updates(base)
else:
updateinfo.exclude_all(base)
if True: # Try, YumBase...
highlight = base.term.MODE['bold']
# If we are doing: "yum info installed blah" don't do the highlight
# because the usability of not accessing the repos. is still higher
# than providing colour for a single line. Usable updatesd/etc. FTW.
if basecmd == 'info' and extcmds and extcmds[0] == 'installed':
highlight = False
ypl = base.returnPkgLists(extcmds, installed_available=highlight,
repoid=repoid)
update_pkgs = {}
inst_pkgs = {}
local_pkgs = {}
columns = None
if basecmd == 'list':
# Dynamically size the columns
columns = _list_cmd_calc_columns(base, ypl)
if highlight and ypl.installed:
# If we have installed and available lists, then do the
# highlighting for the installed packages so you can see what's
# available to update, an extra, or newer than what we have.
for pkg in (ypl.hidden_available +
ypl.reinstall_available +
ypl.old_available):
key = (pkg.name, pkg.arch)
if key not in update_pkgs or pkg.verGT(update_pkgs[key]):
update_pkgs[key] = pkg
if highlight and ypl.available:
# If we have installed and available lists, then do the
# highlighting for the available packages so you can see what's
# available to install vs. update vs. old.
for pkg in ypl.hidden_installed:
key = (pkg.name, pkg.arch)
if key not in inst_pkgs or pkg.verGT(inst_pkgs[key]):
inst_pkgs[key] = pkg
if highlight and ypl.updates:
# Do the local/remote split we get in "yum updates"
for po in sorted(ypl.updates):
if po.repo.id != 'installed' and po.verifyLocalPkg():
local_pkgs[(po.name, po.arch)] = po
# Output the packages:
kern = base.conf.color_list_installed_running_kernel
clio = base.conf.color_list_installed_older
clin = base.conf.color_list_installed_newer
clir = base.conf.color_list_installed_reinstall
clie = base.conf.color_list_installed_extra
if base.conf.query_install_excludes:
ypl.installed = _cmdline_exclude(ypl.installed,
base.cmdline_excludes)
rip = base.listPkgs(ypl.installed, _('Installed Packages'), basecmd,
highlight_na=update_pkgs, columns=columns,
highlight_modes={'>' : clio, '<' : clin,
'kern' : kern,
'=' : clir, 'not in' : clie})
kern = base.conf.color_list_available_running_kernel
clau = base.conf.color_list_available_upgrade
clad = base.conf.color_list_available_downgrade
clar = base.conf.color_list_available_reinstall
clai = base.conf.color_list_available_install
rap = base.listPkgs(ypl.available, _('Available Packages'), basecmd,
highlight_na=inst_pkgs, columns=columns,
highlight_modes={'<' : clau, '>' : clad,
'kern' : kern,
'=' : clar, 'not in' : clai})
rep = base.listPkgs(ypl.extras, _('Extra Packages'), basecmd,
columns=columns)
cul = base.conf.color_update_local
cur = base.conf.color_update_remote
rup = base.listPkgs(ypl.updates, _('Updated Packages'), basecmd,
highlight_na=local_pkgs, columns=columns,
highlight_modes={'=' : cul, 'not in' : cur})
# XXX put this into the ListCommand at some point
if len(ypl.obsoletes) > 0 and basecmd == 'list':
# if we've looked up obsolete lists and it's a list request
rop = [0, '']
print _('Obsoleting Packages')
# The tuple is (newPkg, oldPkg) ... so sort by new
for obtup in sorted(ypl.obsoletesTuples,
key=operator.itemgetter(0)):
base.updatesObsoletesList(obtup, 'obsoletes',
columns=columns, repoid=repoid)
else:
rop = base.listPkgs(ypl.obsoletes, _('Obsoleting Packages'),
basecmd, columns=columns)
rrap = base.listPkgs(ypl.recent, _('Recently Added Packages'),
basecmd, columns=columns)
# extcmds is pop(0)'d if they pass a "special" param like "updates"
# in returnPkgLists(). This allows us to always return "ok" for
# things like "yum list updates".
if len(extcmds) and \
rrap[0] and rop[0] and rup[0] and rep[0] and rap[0] and rip[0]:
return 1, [_('No matching Packages to list')]
return 0, []
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
if len(extcmds) and extcmds[0] == 'installed':
return False
return True
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
if len(extcmds) and extcmds[0] in ('updates', 'obsoletes'):
return 'read-only:future'
if len(extcmds) and extcmds[0] in ('installed', 'distro-extras', 'extras', 'recent'):
return 'read-only:past'
# available/all
return 'read-only:present'
class ListCommand(InfoCommand):
"""A class containing methods needed by the cli to execute the
list command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['list']
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("List a package or groups of packages")
class EraseCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
erase command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['erase', 'remove', 'autoremove',
'erase-n', 'erase-na', 'erase-nevra',
'autoremove-n', 'autoremove-na', 'autoremove-nevra',
'remove-n', 'remove-na', 'remove-nevra']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "PACKAGE..."
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Remove a package or packages from your system")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run. These include that the program is being run by the root
user, and that this command is called with appropriate
arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
if basecmd == 'autoremove':
return
checkPackageArg(base, basecmd, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
pos = False
if basecmd.startswith('autoremove'):
# We have to alter this, as it's used in resolving stage. Which
# sucks. Just be careful in "yum shell".
base.conf.clean_requirements_on_remove = True
basecmd = basecmd[len('auto'):] # pretend it's just remove...
if not extcmds:
pos = True
extcmds = []
for pkg in sorted(base.rpmdb.returnLeafNodes()):
if 'reason' not in pkg.yumdb_info:
continue
if pkg.yumdb_info.reason != 'dep':
continue
extcmds.append(pkg)
self.doneCommand(base, _("Setting up Remove Process"))
ret = base.erasePkgs(extcmds, pos=pos, basecmd=basecmd)
return ret
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
def needTsRemove(self, base, basecmd, extcmds):
"""Return whether a transaction set for removal only must be
set up before this command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a remove-only transaction set is needed, False otherwise
"""
return True
class GroupsCommand(YumCommand):
""" Single sub-command interface for most groups interaction. """
direct_commands = {'grouplist' : 'list',
'groupinstall' : 'install',
'groupupdate' : 'update',
'groupremove' : 'remove',
'grouperase' : 'remove',
'groupinfo' : 'info'}
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['groups', 'group'] + self.direct_commands.keys()
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[list|info|summary|install|upgrade|remove|mark] [GROUP]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Display, or use, the groups information")
def _grp_setup_doCommand(self, base):
self.doneCommand(base, _("Setting up Group Process"))
base.doRepoSetup(dosack=0)
try:
base.doGroupSetup()
except yum.Errors.GroupsError:
return 1, [_('No Groups on which to run command')]
except yum.Errors.YumBaseError, e:
raise
def _grp_cmd(self, basecmd, extcmds):
if basecmd in self.direct_commands:
cmd = self.direct_commands[basecmd]
elif extcmds:
cmd = extcmds[0]
extcmds = extcmds[1:]
else:
cmd = 'summary'
if cmd in ('mark', 'unmark') and extcmds:
cmd = "%s-%s" % (cmd, extcmds[0])
extcmds = extcmds[1:]
remap = {'update' : 'upgrade',
'erase' : 'remove',
'mark-erase' : 'mark-remove',
}
cmd = remap.get(cmd, cmd)
return cmd, extcmds
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
The exact conditions checked will vary depending on the
subcommand that is being called.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
cmd, extcmds = self._grp_cmd(basecmd, extcmds)
checkEnabledRepo(base)
ocmds_all = []
ocmds_arg = []
if base.conf.group_command == 'objects':
ocmds_arg = ('mark-install', 'mark-remove',
'mark-blacklist',
'mark-packages', 'mark-packages-force',
'unmark-packages',
'mark-packages-sync', 'mark-packages-sync-force',
'mark-groups', 'mark-groups-force',
'unmark-groups',
'mark-groups-sync', 'mark-groups-sync-force')
ocmds_all = ('mark-install', 'mark-remove', 'mark-convert',
'mark-convert-whitelist', 'mark-convert-blacklist',
'mark-blacklist',
'mark-packages', 'mark-packages-force',
'unmark-packages',
'mark-packages-sync', 'mark-packages-sync-force',
'mark-groups', 'mark-groups-force',
'unmark-groups',
'mark-groups-sync', 'mark-groups-sync-force')
if cmd in ('install', 'remove', 'info') or cmd in ocmds_arg:
checkGroupArg(base, cmd, extcmds)
if cmd in ('install', 'remove', 'upgrade') or cmd in ocmds_all:
checkRootUID(base)
if cmd in ('install', 'upgrade'):
checkGPGKey(base)
cmds = set(('list', 'info', 'remove', 'install', 'upgrade', 'summary'))
if base.conf.group_command == 'objects':
cmds.update(ocmds_all)
if cmd not in cmds:
base.logger.critical(_('Invalid groups sub-command, use: %s.'),
", ".join(cmds))
raise cli.CliError
if base.conf.group_command != 'objects':
pass
elif not os.path.exists(os.path.dirname(base.igroups.filename)):
base.logger.critical(_("There is no installed groups file."))
base.logger.critical(_("Maybe run: yum groups mark convert (see man yum)"))
elif not os.access(os.path.dirname(base.igroups.filename), os.R_OK):
base.logger.critical(_("You don't have access to the groups DBs."))
raise cli.CliError
elif not os.path.exists(base.igroups.filename):
base.logger.critical(_("There is no installed groups file."))
base.logger.critical(_("Maybe run: yum groups mark convert (see man yum)"))
elif not os.access(base.igroups.filename, os.R_OK):
base.logger.critical(_("You don't have access to the groups DB."))
raise cli.CliError
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
cmd, extcmds = self._grp_cmd(basecmd, extcmds)
self._grp_setup_doCommand(base)
if cmd == 'summary':
return base.returnGroupSummary(extcmds)
if cmd == 'list':
return base.returnGroupLists(extcmds)
if True: # Try, YumBase...
if cmd == 'info':
return base.returnGroupInfo(extcmds)
if cmd == 'install':
return base.installGroups(extcmds)
if cmd == 'upgrade':
ret = base.installGroups(extcmds, upgrade=True)
updateinfo.remove_txmbrs(base)
return ret
if cmd == 'remove':
return base.removeGroups(extcmds)
if cmd == 'mark-install':
gRG = base._groupReturnGroups(extcmds,ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
for evgrp in evgrps:
base.igroups.add_environment(evgrp.environmentid,
evgrp.allgroups)
for grp in grps:
pkg_names = set() # Only see names that are installed.
for pkg in base.rpmdb.searchNames(grp.packages):
pkg_names.add(pkg.name)
base.igroups.add_group(grp.groupid, pkg_names)
base.igroups.save()
return 0, ['Marked install: ' + ','.join(extcmds)]
if cmd == 'mark-blacklist':
gRG = base._groupReturnGroups(extcmds,ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
for ievgrp in ievgrps:
evgrp = base.comps.return_environment(igrp.evgid)
if not evgrp:
continue
base.igroups.changed = True
ievgrp.grp_names.update(grp.groups)
for igrp in igrps:
grp = base.comps.return_group(igrp.gid)
if not grp:
continue
base.igroups.changed = True
igrp.pkg_names.update(grp.packages)
base.igroups.save()
return 0, ['Marked upgrade blacklist: ' + ','.join(extcmds)]
if cmd in ('mark-packages', 'mark-packages-force'):
if len(extcmds) < 2:
return 1, ['No group or package given']
gRG = base._groupReturnGroups([extcmds[0]],
ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
if igrps is None or len(igrps) != 1:
return 1, ['No group matched']
grp = igrps[0]
force = cmd == 'mark-packages-force'
for pkg in base.rpmdb.returnPackages(patterns=extcmds[1:]):
if not force and 'group_member' in pkg.yumdb_info:
continue
pkg.yumdb_info.group_member = grp.gid
grp.pkg_names.add(pkg.name)
base.igroups.changed = True
base.igroups.save()
return 0, ['Marked packages: ' + ','.join(extcmds[1:])]
if cmd == 'unmark-packages':
for pkg in base.rpmdb.returnPackages(patterns=extcmds):
if 'group_member' in pkg.yumdb_info:
del pkg.yumdb_info.group_member
return 0, ['UnMarked packages: ' + ','.join(extcmds)]
if cmd in ('mark-packages-sync', 'mark-packages-sync-force'):
gRG = base._groupReturnGroups(extcmds,ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
if not igrps:
return 1, ['No group matched']
force = cmd == 'mark-packages-sync-force'
for grp in igrps:
for pkg in base.rpmdb.searchNames(grp.pkg_names):
if not force and 'group_member' in pkg.yumdb_info:
continue
pkg.yumdb_info.group_member = grp.gid
if force:
return 0, ['Marked packages-sync-force: '+','.join(extcmds)]
else:
return 0, ['Marked packages-sync: ' + ','.join(extcmds)]
if cmd in ('mark-groups', 'mark-groups-force'):
if len(extcmds) < 2:
return 1, ['No environment or group given']
gRG = base._groupReturnGroups([extcmds[0]],
ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
if ievgrps is None or len(ievgrps) != 1:
return 1, ['No environment matched']
evgrp = ievgrps[0]
force = cmd == 'mark-groups-force'
gRG = base._groupReturnGroups(extcmds[1:], ignore_case=False)
for grp in gRG[1]:
# Packages full or empty?
self.igroups.add_group(grp.groupid,
grp.packages, ievgrp)
if force:
for grp in gRG[0]:
grp.environment = evgrp.evgid
base.igroups.changed = True
base.igroups.save()
return 0, ['Marked groups: ' + ','.join(extcmds[1:])]
if cmd == 'unmark-groups':
gRG = base._groupReturnGroups([extcmds[0]],
ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
if igrps is None:
return 1, ['No groups matched']
for grp in igrps:
grp.environment = None
base.igroups.changed = True
base.igroups.save()
return 0, ['UnMarked groups: ' + ','.join(extcmds)]
if cmd in ('mark-groups-sync', 'mark-groups-sync-force'):
gRG = base._groupReturnGroups(extcmds,ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
if not ievgrps:
return 1, ['No environment matched']
force = cmd == 'mark-groups-sync-force'
for evgrp in ievgrps:
grp_names = ",".join(sorted(evgrp.grp_names))
for grp in base.igroups.return_groups(grp_names):
if not force and grp.environment is not None:
continue
grp.environment = evgrp.evgid
base.igroups.changed = True
base.igroups.save()
if force:
return 0, ['Marked groups-sync-force: '+','.join(extcmds)]
else:
return 0, ['Marked groups-sync: ' + ','.join(extcmds)]
# FIXME: This doesn't do environment groups atm.
if cmd in ('mark-convert',
'mark-convert-whitelist', 'mark-convert-blacklist'):
# Convert old style info. into groups as objects.
def _convert_grp(grp):
if not grp.installed:
return
pkg_names = []
for pkg in base.rpmdb.searchNames(grp.packages):
if 'group_member' in pkg.yumdb_info:
continue
pkg.yumdb_info.group_member = grp.groupid
pkg_names.append(pkg.name)
# We only mark the packages installed as a known part of
# the group. This way "group update" will work and install
# any remaining packages, as it would before the conversion.
if cmd == 'mark-convert-whitelist':
base.igroups.add_group(grp.groupid, pkg_names)
else:
base.igroups.add_group(grp.groupid, grp.packages)
# Blank everything.
for gid in base.igroups.groups.keys():
base.igroups.del_group(gid)
for pkg in base.rpmdb:
if 'group_member' in pkg.yumdb_info:
del pkg.yumdb_info.group_member
# Need to do this by hand, when using objects, to setup the
# .installed attribute in comps.
base.comps.compile(base.rpmdb.simplePkgList())
# This is kind of a hack, to work around the biggest problem
# with having pkgs in more than one group. Treat Fedora/EL/etc.
# base/core special. Maybe other groups?
# Not 100% we want to force install "core", as that's then
# "different", but it is better ... so, meh.
special_gids = (('core', True),
('base', False))
for gid, force_installed in special_gids:
grp = base.comps.return_group(gid)
if grp is None:
continue
if force_installed:
grp.installed = True
_convert_grp(grp)
for grp in base.comps.get_groups():
if grp.groupid in special_gids:
continue
_convert_grp(grp)
base.igroups.save()
return 0, ['Converted old style groups to objects.']
if cmd == 'mark-remove':
gRG = base._groupReturnGroups(extcmds,ignore_case=False)
igrps, grps, ievgrps, evgrps = gRG
for evgrp in ievgrps:
base.igroups.del_environment(evgrp.evgid)
for grp in igrps:
base.igroups.del_group(grp.gid)
base.igroups.save()
return 0, ['Marked remove: ' + ','.join(extcmds)]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
cmd, extcmds = self._grp_cmd(basecmd, extcmds)
if cmd in ('list', 'info', 'remove', 'summary'):
return False
if cmd.startswith('mark') or cmd.startswith('unmark'):
return False
return True
def needTsRemove(self, base, basecmd, extcmds):
"""Return whether a transaction set for removal only must be
set up before this command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a remove-only transaction set is needed, False otherwise
"""
cmd, extcmds = self._grp_cmd(basecmd, extcmds)
if cmd in ('remove',):
return True
return False
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
cmd, extcmds = self._grp_cmd(basecmd, extcmds)
if cmd in ('list', 'info', 'summary'):
return 'read-only:past'
if cmd.startswith('mark') or cmd.startswith('unmark'):
return 'read-only:past'
return 'write'
class MakeCacheCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
makecache command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['makecache']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return ""
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Generate the metadata cache")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that there is an enabled repository.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkEnabledRepo(base)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
base.logger.debug(_("Making cache files for all metadata files."))
base.logger.debug(_("This may take a while depending on the speed of this computer"))
# Fast == don't download any extra MD
fast = False
if extcmds and extcmds[0] == 'fast':
fast = True
if True: # Try, YumBase...
for repo in base.repos.sort():
repo.metadata_expire = 0
if not fast:
repo.mdpolicy = "group:all"
base.doRepoSetup(dosack=0)
base.repos.doSetup()
# These convert the downloaded data into usable data,
# we can't remove them until *LoadRepo() can do:
# 1. Download a .sqlite.bz2 and convert to .sqlite
# 2. Download a .xml.gz and convert to .xml.gz.sqlite
if fast:
# Can't easily tell which other metadata each repo. has, so
# just do primary.
base.repos.populateSack(mdtype='metadata', cacheonly=1)
else:
base.repos.populateSack(mdtype='all', cacheonly=1)
# Now decompress stuff, so that -C works, sigh.
fname_map = {'group_gz' : 'groups.xml',
'pkgtags' : 'pkgtags.sqlite',
'updateinfo' : 'updateinfo.xml',
'prestodelta': 'prestodelta.xml',
}
for repo in base.repos.listEnabled():
for MD in repo.repoXML.fileTypes():
if MD not in fname_map:
continue
if MD not in repo.retrieved or not repo.retrieved[MD]:
continue # For fast mode.
misc.repo_gen_decompress(repo.retrieveMD(MD),
fname_map[MD],
cached=repo.cache)
return 0, [_('Metadata Cache Created')]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
class CleanCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
clean command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['clean']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[headers|packages|metadata|dbcache|plugins|expire-cache|all]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Remove cached data")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that there is at least one enabled repository,
and that this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkCleanArg(base, basecmd, extcmds)
checkEnabledRepo(base)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
base.conf.cache = 1
return base.cleanCli(extcmds)
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:past'
class ProvidesCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
provides command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['provides', 'whatprovides']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "SOME_STRING"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Find what package provides the given value")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkItemArg(base, basecmd, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
base.logger.debug("Searching Packages: ")
updateinfo.exclude_updates(base)
return base.provides(extcmds)
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:past'
class CheckUpdateCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
check-update command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['check-update', 'check-updates',
'check-upgrade', 'check-upgrades']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[PACKAGE...]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Check for available package updates")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that there is at least one enabled repository.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkEnabledRepo(base)
def doCommand(self, base, basecmd, extcmds, repoid=None):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
updateinfo.exclude_updates(base)
obscmds = ['obsoletes'] + extcmds
base.extcmds.insert(0, 'updates')
result = 0
if True:
ypl = base.returnPkgLists(extcmds, repoid=repoid)
if (base.conf.obsoletes or
base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)):
typl = base.returnPkgLists(obscmds, repoid=repoid)
ypl.obsoletes = typl.obsoletes
ypl.obsoletesTuples = typl.obsoletesTuples
columns = _list_cmd_calc_columns(base, ypl)
if len(ypl.updates) > 0:
local_pkgs = {}
highlight = base.term.MODE['bold']
if highlight:
# Do the local/remote split we get in "yum updates"
for po in sorted(ypl.updates):
if po.repo.id != 'installed' and po.verifyLocalPkg():
local_pkgs[(po.name, po.arch)] = po
cul = base.conf.color_update_local
cur = base.conf.color_update_remote
base.listPkgs(ypl.updates, '', outputType='list',
highlight_na=local_pkgs, columns=columns,
highlight_modes={'=' : cul, 'not in' : cur})
result = 100
if len(ypl.obsoletes) > 0: # This only happens in verbose mode
print _('Obsoleting Packages')
# The tuple is (newPkg, oldPkg) ... so sort by new
for obtup in sorted(ypl.obsoletesTuples,
key=operator.itemgetter(0)):
base.updatesObsoletesList(obtup, 'obsoletes',
columns=columns, repoid=repoid)
result = 100
# Add check_running_kernel call, if updateinfo is available.
if (base.conf.autocheck_running_kernel and
updateinfo._repos_downloaded(base.repos.listEnabled())):
def _msg(x):
base.verbose_logger.info("%s", x)
updateinfo._check_running_kernel(base, base.upinfo, _msg)
return result, []
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:future'
class SearchCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
search command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['search']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "SOME_STRING"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Search package details for the given string")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkItemArg(base, basecmd, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
base.logger.debug(_("Searching Packages: "))
updateinfo.exclude_updates(base)
return base.search(extcmds)
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:present'
class UpgradeCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
upgrade command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['upgrade', 'upgrade-to']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return 'PACKAGE...'
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Update packages taking obsoletes into account")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run. These include that the program is being run by the root
user, and that there are enabled repositories with gpg.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
base.conf.obsoletes = 1
self.doneCommand(base, _("Setting up Upgrade Process"))
ret = base.updatePkgs(extcmds, update_to=(basecmd == 'upgrade-to'))
updateinfo.remove_txmbrs(base)
return ret
class LocalInstallCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
localinstall command.
"""
def __init__(self):
YumCommand.__init__(self)
self.hidden = True
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['localinstall', 'localupdate']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "FILE"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Install a local RPM")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run. These include that there are enabled repositories with
gpg keys, and that this command is called with appropriate
arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkPackageArg(base, basecmd, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is:
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Setting up Local Package Process"))
updateonly = basecmd == 'localupdate'
return base.localInstall(filelist=extcmds, updateonly=updateonly)
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
class ResolveDepCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
resolvedep command.
"""
def __init__(self):
YumCommand.__init__(self)
self.hidden = True
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['resolvedep']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "DEPENDENCY"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return "repoquery --pkgnarrow=all --whatprovides --qf '%{envra} %{ui_from_repo}'"
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
base.logger.debug(_("Searching Packages for Dependency:"))
updateinfo.exclude_updates(base)
return base.resolveDepCli(extcmds)
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:past'
class ShellCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
shell command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['shell']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[FILENAME]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Run an interactive yum shell")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkShellArg(base, basecmd, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _('Setting up Yum Shell'))
return base.doShell()
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
class DepListCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
deplist command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['deplist']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return 'PACKAGE...'
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("List a package's dependencies")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that this command is called with appropriate
arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkPackageArg(base, basecmd, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Finding dependencies: "))
updateinfo.exclude_updates(base)
return base.deplist(extcmds)
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:past' # read-only ?
class RepoListCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
repolist command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ('repolist', 'repoinfo')
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return '[all|enabled|disabled]'
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _('Display the configured software repositories')
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
def _repo_size(repo):
ret = 0
for pkg in repo.sack.returnPackages():
ret += pkg.packagesize
return base.format_number(ret)
def _repo_match(repo, patterns):
for pat in patterns:
if repo in base.repos.findRepos(pat, name_match=True,
ignore_case=True):
return True
return False
def _num2ui_num(num):
return to_unicode(locale.format("%d", num, True))
if len(extcmds) >= 1 and extcmds[0] in ('all', 'disabled', 'enabled'):
arg = extcmds[0]
extcmds = extcmds[1:]
else:
arg = 'enabled'
extcmds = map(lambda x: x.lower(), extcmds)
if basecmd == 'repoinfo':
verbose = True
else:
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
if arg != 'disabled' or extcmds:
try:
# Setup so len(repo.sack) is correct
base.repos.populateSack()
base.pkgSack # Need to setup the pkgSack, so excludes work
except yum.Errors.RepoError:
if verbose:
raise
# populate them by hand, so one failure doesn't kill everything
# after it.
for repo in base.repos.listEnabled():
try:
base.repos.populateSack(repo.id)
except yum.Errors.RepoError:
pass
repos = base.repos.repos.values()
repos.sort()
enabled_repos = base.repos.listEnabled()
on_ehibeg = base.term.FG_COLOR['green'] + base.term.MODE['bold']
on_dhibeg = base.term.FG_COLOR['red']
on_hiend = base.term.MODE['normal']
tot_num = 0
cols = []
for repo in repos:
if len(extcmds) and not _repo_match(repo, extcmds):
continue
(ehibeg, dhibeg, hiend) = '', '', ''
ui_enabled = ''
ui_endis_wid = 0
ui_num = ""
ui_excludes_num = ''
force_show = False
if arg == 'all' or repo.id in extcmds or repo.name in extcmds:
force_show = True
(ehibeg, dhibeg, hiend) = (on_ehibeg, on_dhibeg, on_hiend)
if repo in enabled_repos:
enabled = True
if arg == 'enabled':
force_show = False
elif arg == 'disabled' and not force_show:
continue
if force_show or verbose:
ui_enabled = ehibeg + _('enabled') + hiend
ui_endis_wid = utf8_width(_('enabled'))
if not verbose:
ui_enabled += ": "
ui_endis_wid += 2
if verbose:
ui_size = _repo_size(repo)
# We don't show status for list disabled
if arg != 'disabled' or verbose:
if verbose or base.conf.exclude or repo.exclude:
num = len(repo.sack.simplePkgList())
else:
num = len(repo.sack)
ui_num = _num2ui_num(num)
excludes = repo.sack._excludes
excludes = len([pid for r,pid in excludes if r == repo])
if excludes:
ui_excludes_num = _num2ui_num(excludes)
if not verbose:
ui_num += "+%s" % ui_excludes_num
tot_num += num
else:
enabled = False
if arg == 'disabled':
force_show = False
elif arg == 'enabled' and not force_show:
continue
ui_enabled = dhibeg + _('disabled') + hiend
ui_endis_wid = utf8_width(_('disabled'))
if not verbose:
rid = repo.ui_id # can't use str()
if repo.metadata_expire >= 0:
if os.path.exists(repo.metadata_cookie):
last = os.stat(repo.metadata_cookie).st_mtime
if last + repo.metadata_expire < time.time():
rid = '!' + rid
if enabled and repo.metalink:
mdts = repo.metalink_data.repomd.timestamp
if mdts > repo.repoXML.timestamp:
rid = '*' + rid
cols.append((rid, repo.name,
(ui_enabled, ui_endis_wid), ui_num))
else:
if enabled:
md = repo.repoXML
else:
md = None
out = [base.fmtKeyValFill(_("Repo-id : "), repo.ui_id),
base.fmtKeyValFill(_("Repo-name : "), repo.name)]
if force_show or extcmds:
out += [base.fmtKeyValFill(_("Repo-status : "),
ui_enabled)]
if md and md.revision is not None:
out += [base.fmtKeyValFill(_("Repo-revision: "),
md.revision)]
if md and md.tags['content']:
tags = md.tags['content']
out += [base.fmtKeyValFill(_("Repo-tags : "),
", ".join(sorted(tags)))]
if md and md.tags['distro']:
for distro in sorted(md.tags['distro']):
tags = md.tags['distro'][distro]
out += [base.fmtKeyValFill(_("Repo-distro-tags: "),
"[%s]: %s" % (distro,
", ".join(sorted(tags))))]
if md:
out += [base.fmtKeyValFill(_("Repo-updated : "),
time.ctime(md.timestamp)),
base.fmtKeyValFill(_("Repo-pkgs : "),ui_num),
base.fmtKeyValFill(_("Repo-size : "),ui_size)]
if hasattr(repo, '_orig_baseurl'):
baseurls = repo._orig_baseurl
else:
baseurls = repo.baseurl
if baseurls:
out += [base.fmtKeyValFill(_("Repo-baseurl : "),
", ".join(baseurls))]
if enabled:
# This needs to be here due to the mirrorlists are
# metalinks hack.
repo.urls
if repo.metalink:
out += [base.fmtKeyValFill(_("Repo-metalink: "),
repo.metalink)]
if enabled:
ts = repo.metalink_data.repomd.timestamp
out += [base.fmtKeyValFill(_(" Updated : "),
time.ctime(ts))]
elif repo.mirrorlist:
out += [base.fmtKeyValFill(_("Repo-mirrors : "),
repo.mirrorlist)]
if enabled and repo.urls and not baseurls:
url = repo.urls[0]
if len(repo.urls) > 1:
url += ' (%d more)' % (len(repo.urls) - 1)
out += [base.fmtKeyValFill(_("Repo-baseurl : "), url)]
if not os.path.exists(repo.metadata_cookie):
last = _("Unknown")
else:
last = os.stat(repo.metadata_cookie).st_mtime
last = time.ctime(last)
if repo.metadata_expire <= -1:
num = _("Never (last: %s)") % last
elif not repo.metadata_expire:
num = _("Instant (last: %s)") % last
else:
num = _num2ui_num(repo.metadata_expire)
num = _("%s second(s) (last: %s)") % (num, last)
out += [base.fmtKeyValFill(_("Repo-expire : "), num),
base.fmtKeyValFill(_(" Filter : "),
repo.metadata_expire_filter),
]
if repo.exclude:
out += [base.fmtKeyValFill(_("Repo-exclude : "),
", ".join(repo.exclude))]
if repo.includepkgs:
out += [base.fmtKeyValFill(_("Repo-include : "),
", ".join(repo.includepkgs))]
if ui_excludes_num:
out += [base.fmtKeyValFill(_("Repo-excluded: "),
ui_excludes_num)]
if repo.repofile:
out += [base.fmtKeyValFill(_("Repo-filename: "),
repo.repofile)]
base.verbose_logger.info("%s\n",
"\n".join(map(misc.to_unicode, out)))
if not verbose and cols:
# Work out the first (id) and last (enabled/disalbed/count),
# then chop the middle (name)...
id_len = utf8_width(_('repo id'))
nm_len = 0
st_len = 0
ui_len = 0
for (rid, rname, (ui_enabled, ui_endis_wid), ui_num) in cols:
if id_len < utf8_width(rid):
id_len = utf8_width(rid)
if nm_len < utf8_width(rname):
nm_len = utf8_width(rname)
if st_len < (ui_endis_wid + len(ui_num)):
st_len = (ui_endis_wid + len(ui_num))
# Need this as well as above for: utf8_width_fill()
if ui_len < len(ui_num):
ui_len = len(ui_num)
if arg == 'disabled': # Don't output a status column.
left = base.term.columns - (id_len + 1)
elif utf8_width(_('status')) > st_len:
left = base.term.columns - (id_len + utf8_width(_('status')) +2)
else:
left = base.term.columns - (id_len + st_len + 2)
if left < nm_len: # Name gets chopped
nm_len = left
else: # Share the extra...
left -= nm_len
id_len += left / 2
nm_len += left - (left / 2)
txt_rid = utf8_width_fill(_('repo id'), id_len)
txt_rnam = utf8_width_fill(_('repo name'), nm_len, nm_len)
if arg == 'disabled': # Don't output a status column.
base.verbose_logger.info("%s %s",
txt_rid, txt_rnam)
else:
base.verbose_logger.info("%s %s %s",
txt_rid, txt_rnam, _('status'))
for (rid, rname, (ui_enabled, ui_endis_wid), ui_num) in cols:
if arg == 'disabled': # Don't output a status column.
base.verbose_logger.info("%s %s",
utf8_width_fill(rid, id_len),
utf8_width_fill(rname, nm_len,
nm_len))
continue
if ui_num:
ui_num = utf8_width_fill(ui_num, ui_len, left=False)
base.verbose_logger.info("%s %s %s%s",
utf8_width_fill(rid, id_len),
utf8_width_fill(rname, nm_len, nm_len),
ui_enabled, ui_num)
return 0, ['repolist: ' +to_unicode(locale.format("%d", tot_num, True))]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:past'
class HelpCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
help command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['help']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "COMMAND"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Display a helpful usage message")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run; namely that this command is called with appropriate
arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
if len(extcmds) == 0:
base.usage()
raise cli.CliError
elif len(extcmds) > 1 or extcmds[0] not in base.yum_cli_commands:
base.usage()
raise cli.CliError
@staticmethod
def _makeOutput(command):
canonical_name = command.getNames()[0]
# Check for the methods in case we have plugins that don't
# implement these.
# XXX Remove this once usage/summary are common enough
try:
usage = command.getUsage()
except (AttributeError, NotImplementedError):
usage = None
try:
summary = command.getSummary()
except (AttributeError, NotImplementedError):
summary = None
# XXX need detailed help here, too
help_output = ""
if usage is not None:
help_output += "%s %s" % (canonical_name, usage)
if summary is not None:
help_output += "\n\n%s" % summary
if usage is None and summary is None:
help_output = _("No help available for %s") % canonical_name
command_names = command.getNames()
if len(command_names) > 1:
if len(command_names) > 2:
help_output += _("\n\naliases: ")
else:
help_output += _("\n\nalias: ")
help_output += ', '.join(command.getNames()[1:])
return help_output
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
if extcmds[0] in base.yum_cli_commands:
command = base.yum_cli_commands[extcmds[0]]
base.verbose_logger.info(self._makeOutput(command))
return 0, []
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
class ReInstallCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
reinstall command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['reinstall']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "PACKAGE..."
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run. These include that the program is being run by the root
user, that there are enabled repositories with gpg keys, and
that this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkPackageArg(base, basecmd, extcmds)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Setting up Reinstall Process"))
return base.reinstallPkgs(extcmds)
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("reinstall a package")
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
class DowngradeCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
downgrade command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['downgrade']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "PACKAGE..."
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run. These include that the program is being run by the root
user, that there are enabled repositories with gpg keys, and
that this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkPackageArg(base, basecmd, extcmds)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
self.doneCommand(base, _("Setting up Downgrade Process"))
return base.downgradePkgs(extcmds)
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("downgrade a package")
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
class VersionCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
version command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['version']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[all|installed|available]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Display a version for the machine and/or available repos.")
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
vcmd = 'installed'
if extcmds:
vcmd = extcmds[0]
if vcmd in ('grouplist', 'groupinfo',
'nogroups', 'nogroups-installed', 'nogroups-available',
'nogroups-all',
'installed', 'all', 'group-installed', 'group-all',
'available', 'all', 'group-available', 'group-all'):
extcmds = extcmds[1:]
else:
vcmd = 'installed'
def _append_repos(cols, repo_data):
for repoid in sorted(repo_data):
cur = repo_data[repoid]
ncols = []
last_rev = None
for rev in sorted(cur):
if rev is None:
continue
last_rev = cur[rev]
ncols.append((" %s/%s" % (repoid, rev), str(cur[rev])))
if None in cur and (not last_rev or cur[None] != last_rev):
cols.append((" %s" % repoid, str(cur[None])))
cols.extend(ncols)
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
groups = {}
if vcmd in ('nogroups', 'nogroups-installed', 'nogroups-available',
'nogroups-all'):
gconf = []
if vcmd == 'nogroups':
vcmd = 'installed'
else:
vcmd = vcmd[len('nogroups-'):]
else:
gconf = yum.config.readVersionGroupsConfig()
for group in gconf:
groups[group] = set(gconf[group].pkglist)
if gconf[group].run_with_packages:
groups[group].update(base.run_with_package_names)
if vcmd == 'grouplist':
print _(" Yum version groups:")
for group in sorted(groups):
print " ", group
return 0, ['version grouplist']
if vcmd == 'groupinfo':
for group in groups:
if group not in extcmds:
continue
print _(" Group :"), group
print _(" Packages:")
if not verbose:
for pkgname in sorted(groups[group]):
print " ", pkgname
else:
data = {'envra' : {}, 'rid' : {}}
pkg_names = groups[group]
pkg_names2pkgs = base._group_names2aipkgs(pkg_names)
base._calcDataPkgColumns(data, pkg_names, pkg_names2pkgs)
data = [data['envra'], data['rid']]
columns = base.calcColumns(data)
columns = (-columns[0], -columns[1])
base._displayPkgsFromNames(pkg_names, True, pkg_names2pkgs,
columns=columns)
return 0, ['version groupinfo']
# Have a way to manually specify a dynamic group of packages, whee.
if not vcmd.startswith("group-") and extcmds:
for dgrp in extcmds:
if '/' not in dgrp:
# It's a package name, add it to the cmd line group...
if '<cmd line>' not in groups:
groups['<cmd line>'] = set()
groups['<cmd line>'].add(dgrp)
else: # It's a file containing a list of packages...
if not os.path.exists(dgrp):
base.logger.warn(_(" File doesn't exist: %s"), dgrp)
else:
pkg_names = open(dgrp).readlines()
pkg_names = set(n.strip() for n in pkg_names)
dgrp = os.path.basename(dgrp)
if dgrp in groups:
for num in range(1, 100):
ndgrp = dgrp + str(num)
if ndgrp in groups:
continue
dgrp = ndgrp
break
groups[dgrp] = pkg_names
rel = base.conf.yumvar['releasever']
ba = base.conf.yumvar['basearch']
cols = []
if vcmd in ('installed', 'all', 'group-installed', 'group-all'):
if True: # Try, YumBase...
data = base.rpmdb.simpleVersion(not verbose, groups=groups)
lastdbv = base.history.last()
if lastdbv is not None:
lastdbv = lastdbv.end_rpmdbversion
if lastdbv is not None and data[0] != lastdbv:
base._rpmdb_warn_checks(warn=lastdbv is not None)
if vcmd not in ('group-installed', 'group-all'):
cols.append(("%s %s/%s" % (_("Installed:"), rel, ba),
str(data[0])))
_append_repos(cols, data[1])
if groups:
for grp in sorted(data[2]):
if (vcmd.startswith("group-") and
extcmds and grp not in extcmds):
continue
cols.append(("%s %s" % (_("Group-Installed:"), grp),
str(data[2][grp])))
_append_repos(cols, data[3][grp])
if vcmd in ('available', 'all', 'group-available', 'group-all'):
if True: # Try, YumBase...
data = base.pkgSack.simpleVersion(not verbose, groups=groups)
if vcmd not in ('group-available', 'group-all'):
cols.append(("%s %s/%s" % (_("Available:"), rel, ba),
str(data[0])))
if verbose:
_append_repos(cols, data[1])
if groups:
for grp in sorted(data[2]):
if (vcmd.startswith("group-") and
extcmds and grp not in extcmds):
continue
cols.append(("%s %s" % (_("Group-Available:"), grp),
str(data[2][grp])))
if verbose:
_append_repos(cols, data[3][grp])
data = {'rid' : {}, 'ver' : {}}
for (rid, ver) in cols:
for (d, v) in (('rid', len(rid)), ('ver', len(ver))):
data[d].setdefault(v, 0)
data[d][v] += 1
data = [data['rid'], data['ver']]
columns = base.calcColumns(data)
columns = (-columns[0], columns[1])
for line in cols:
print base.fmtColumns(zip(line, columns))
return 0, ['version']
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
vcmd = 'installed'
if extcmds:
vcmd = extcmds[0]
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
if vcmd == 'groupinfo' and verbose:
return True
return vcmd in ('available', 'all', 'group-available', 'group-all')
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:present'
class HistoryCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
history command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['history']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[info|list|packages-list|summary|addon-info|redo|undo|rollback|new]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Display, or use, the transaction history")
def _hcmd_redo(self, base, extcmds):
kwargs = {'force_reinstall' : False,
'force_changed_removal' : False,
}
kwargs_map = {'reinstall' : 'force_reinstall',
'force-reinstall' : 'force_reinstall',
'remove' : 'force_changed_removal',
'force-remove' : 'force_changed_removal',
}
while len(extcmds) > 1:
done = False
for arg in extcmds[1].replace(' ', ',').split(','):
if arg not in kwargs_map:
continue
done = True
key = kwargs_map[extcmds[1]]
kwargs[key] = not kwargs[key]
if not done:
break
extcmds = [extcmds[0]] + extcmds[2:]
old = base._history_get_transaction(extcmds)
if old is None:
return 1, ['Failed history redo']
tm = time.ctime(old.beg_timestamp)
print "Repeating transaction %u, from %s" % (old.tid, tm)
base.historyInfoCmdPkgsAltered(old)
if base.history_redo(old, **kwargs):
return 2, ["Repeating transaction %u" % (old.tid,)]
def _hcmd_undo(self, base, extcmds):
old = base._history_get_transaction(extcmds)
if old is None:
return 1, ['Failed history undo']
tm = time.ctime(old.beg_timestamp)
print "Undoing transaction %u, from %s" % (old.tid, tm)
base.historyInfoCmdPkgsAltered(old)
if base.history_undo(old):
return 2, ["Undoing transaction %u" % (old.tid,)]
def _hcmd_rollback(self, base, extcmds):
force = False
if len(extcmds) > 1 and extcmds[1] == 'force':
force = True
extcmds = extcmds[:]
extcmds.pop(0)
old = base._history_get_transaction(extcmds)
if old is None:
return 1, ['Failed history rollback, no transaction']
last = base.history.last()
if last is None:
return 1, ['Failed history rollback, no last?']
if old.tid == last.tid:
return 0, ['Rollback to current, nothing to do']
mobj = None
for tid in base.history.old(range(old.tid + 1, last.tid + 1)):
if not force and (tid.altered_lt_rpmdb or tid.altered_gt_rpmdb):
if tid.altered_lt_rpmdb:
msg = "Transaction history is incomplete, before %u."
else:
msg = "Transaction history is incomplete, after %u."
print msg % tid.tid
print " You can use 'history rollback force', to try anyway."
return 1, ['Failed history rollback, incomplete']
if mobj is None:
mobj = yum.history.YumMergedHistoryTransaction(tid)
else:
mobj.merge(tid)
tm = time.ctime(old.beg_timestamp)
print "Rollback to transaction %u, from %s" % (old.tid, tm)
print base.fmtKeyValFill(" Undoing the following transactions: ",
", ".join((str(x) for x in mobj.tid)))
base.historyInfoCmdPkgsAltered(mobj)
if base.history_undo(mobj):
return 2, ["Rollback to transaction %u" % (old.tid,)]
def _hcmd_new(self, base, extcmds):
base.history._create_db_file()
def _hcmd_stats(self, base, extcmds):
print "File :", base.history._db_file
num = os.stat(base.history._db_file).st_size
print "Size :", locale.format("%d", num, True)
trans_N = base.history.last()
if trans_N is None:
print _("Transactions:"), 0
return
counts = base.history._pkg_stats()
trans_1 = base.history.old("1")[0]
print _("Transactions:"), trans_N.tid
print _("Begin time :"), time.ctime(trans_1.beg_timestamp)
print _("End time :"), time.ctime(trans_N.end_timestamp)
print _("Counts :")
print _(" NEVRAC :"), locale.format("%6d", counts['nevrac'], True)
print _(" NEVRA :"), locale.format("%6d", counts['nevra'], True)
print _(" NA :"), locale.format("%6d", counts['na'], True)
print _(" NEVR :"), locale.format("%6d", counts['nevr'], True)
print _(" rpm DB :"), locale.format("%6d", counts['rpmdb'], True)
print _(" yum DB :"), locale.format("%6d", counts['yumdb'], True)
def _hcmd_sync(self, base, extcmds):
extcmds = extcmds[1:]
if not extcmds:
extcmds = None
for ipkg in sorted(base.rpmdb.returnPackages(patterns=extcmds)):
if base.history.pkg2pid(ipkg, create=False) is None:
continue
print "Syncing rpm/yum DB data for:", ipkg, "...",
if base.history.sync_alldb(ipkg):
print "Done."
else:
print "FAILED."
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can
run. The exact conditions checked will vary depending on the
subcommand that is being called.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
cmds = ('list', 'info', 'summary', 'repeat', 'redo', 'undo', 'new',
'rollback',
'addon', 'addon-info',
'stats', 'statistics', 'sync', 'synchronize'
'pkg', 'pkgs', 'pkg-list', 'pkgs-list',
'package', 'package-list', 'packages', 'packages-list',
'pkg-info', 'pkgs-info', 'package-info', 'packages-info')
if extcmds and extcmds[0] not in cmds:
base.logger.critical(_('Invalid history sub-command, use: %s.'),
", ".join(cmds))
raise cli.CliError
if extcmds and extcmds[0] in ('repeat', 'redo', 'undo', 'rollback', 'new'):
checkRootUID(base)
checkGPGKey(base)
elif not (base.history._db_file and os.access(base.history._db_file, os.R_OK)):
base.logger.critical(_("You don't have access to the history DB."))
raise cli.CliError
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
vcmd = 'list'
if extcmds:
vcmd = extcmds[0]
if False: pass
elif vcmd == 'list':
ret = base.historyListCmd(extcmds)
elif vcmd == 'info':
ret = base.historyInfoCmd(extcmds)
elif vcmd == 'summary':
ret = base.historySummaryCmd(extcmds)
elif vcmd in ('addon', 'addon-info'):
ret = base.historyAddonInfoCmd(extcmds)
elif vcmd in ('pkg', 'pkgs', 'pkg-list', 'pkgs-list',
'package', 'package-list', 'packages', 'packages-list'):
ret = base.historyPackageListCmd(extcmds)
elif vcmd == 'undo':
ret = self._hcmd_undo(base, extcmds)
elif vcmd in ('redo', 'repeat'):
ret = self._hcmd_redo(base, extcmds)
elif vcmd == 'rollback':
ret = self._hcmd_rollback(base, extcmds)
elif vcmd == 'new':
ret = self._hcmd_new(base, extcmds)
elif vcmd in ('stats', 'statistics'):
ret = self._hcmd_stats(base, extcmds)
elif vcmd in ('sync', 'synchronize'):
ret = self._hcmd_sync(base, extcmds)
elif vcmd in ('pkg-info', 'pkgs-info', 'package-info', 'packages-info'):
ret = base.historyPackageInfoCmd(extcmds)
if ret is None:
return 0, ['history %s' % (vcmd,)]
return ret
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
vcmd = 'list'
if extcmds:
vcmd = extcmds[0]
return vcmd in ('repeat', 'redo', 'undo', 'rollback')
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
vcmd = 'list'
if extcmds:
vcmd = extcmds[0]
if vcmd in ('repeat', 'redo', 'undo', 'rollback'):
return 'write'
return 'read-only:past'
class CheckRpmdbCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
check-rpmdb command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['check', 'check-rpmdb']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[dependencies|duplicates|all]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Check for problems in the rpmdb")
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
chkcmd = 'all'
if extcmds:
chkcmd = extcmds
def _out(x):
print to_unicode(x.__str__())
rc = 0
if base._rpmdb_warn_checks(out=_out, warn=False, chkcmd=chkcmd,
header=lambda x: None):
rc = 1
return rc, ['%s %s' % (basecmd, chkcmd)]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
return False
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
return 'read-only:past'
class LoadTransactionCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
load-transaction command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['load-transaction', 'load-ts', 'ts-load']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "filename"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("load a saved transaction from filename")
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
def _pkg_avail(l):
if not l.startswith('mbr:'):
return True # Kind of ...
try:
pkgtup, current_state = l.split(':')[1].strip().split(' ')
current_state = int(current_state.strip())
pkgtup = tuple(pkgtup.strip().split(','))
if current_state == yum.TS_INSTALL:
po = base.getInstalledPackageObject(pkgtup)
elif current_state == yum.TS_AVAILABLE:
po = base.getPackageObject(pkgtup)
else:
return False # Bad...
except:
return False # Bad...
return True
def _pkg_counts(l, counts):
if not l.startswith(' ts_state: '):
return
state = l[len(' ts_state: '):]
if state in ('e', 'od', 'ud'):
counts['remove'] += 1
elif state in ('i', 'u'):
counts['install'] += 1
if not extcmds:
extcmds = [tempfile.gettempdir()]
load_file = extcmds[0]
if os.path.isdir(load_file):
self.doneCommand(base, _("showing transaction files from %s") %
load_file)
yumtxs = sorted(glob.glob("%s/*.yumtx" % load_file))
currpmv = None
done = False
for yumtx in yumtxs:
data = base._load_ts_data(yumtx)
if data[0] is not None:
continue # Bad file...
data = data[1]
rpmv = data[0].strip()
if currpmv is None:
currpmv = str(base.rpmdb.simpleVersion(main_only=True)[0])
if rpmv == currpmv:
current = _('y')
else:
current = ' ' # Not usable is the most common
# See load_ts() for data ...
try:
numrepos = int(data[2].strip())
pkgstart = 3+numrepos
numpkgs = int(data[pkgstart].strip())
pkgstart += 1
except:
continue
counts = {'install' : 0, 'remove' : 0}
for l in data[pkgstart:]:
l = l.rstrip()
_pkg_counts(l, counts)
# Check to see if all the packages are available..
bad = ' '
for l in data[pkgstart:]:
l = l.rstrip()
if _pkg_avail(l):
continue
bad = '*'
break
# assert (counts['install'] + counts['remove']) == numpkgs
current = '%s%s' % (bad, current)
if not done:
pkgititle = _("Install")
pkgilen = utf8_width(pkgititle)
if pkgilen < 6:
pkgilen = 6
pkgititle = utf8_width_fill(pkgititle, pkgilen)
pkgetitle = _("Remove")
pkgelen = utf8_width(pkgetitle)
if pkgelen < 6:
pkgelen = 6
pkgetitle = utf8_width_fill(pkgetitle, pkgelen)
print "?? |", pkgititle, "|", pkgetitle, "|", _("Filename")
done = True
numipkgs = locale.format("%d", counts['install'], True)
numipkgs = "%*s" % (pkgilen, numipkgs)
numepkgs = locale.format("%d", counts['remove'], True)
numepkgs = "%*s" % (pkgelen, numepkgs)
print "%s | %s | %s | %s" % (current, numipkgs, numepkgs,
os.path.basename(yumtx))
return 0, [_('Saved transactions from %s; looked at %u files') %
(load_file, len(yumtxs))]
self.doneCommand(base, _("loading transaction from %s") % load_file)
base.load_ts(load_file)
return 2, [_('Transaction loaded from %s with %s members') % (load_file, len(base.tsInfo.getMembers()))]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
if not extcmds or os.path.isdir(extcmds[0]):
return False
return True
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
if not extcmds or os.path.isdir(extcmds[0]):
return 'read-only:past'
return 'write'
class SwapCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
swap command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['swap']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "[remove|cmd] <pkg|arg(s)> [-- install|cmd] <pkg|arg(s)>"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Simple way to swap packages, instead of using shell")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
that there are enabled repositories with gpg keys, and that
this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkSwapPackageArg(base, basecmd, extcmds)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
if '--' in extcmds:
off = extcmds.index('--')
rextcmds = extcmds[:off]
iextcmds = extcmds[off+1:]
else:
rextcmds = extcmds[:1]
iextcmds = extcmds[1:]
if not (rextcmds and iextcmds):
return 1, ['swap'] # impossible
if rextcmds[0] not in base.yum_cli_commands:
rextcmds = ['remove'] + rextcmds
if iextcmds[0] not in base.yum_cli_commands:
iextcmds = ['install'] + iextcmds
# Very similar to what the shell command does...
ocmds = base.cmds
oline = base.cmdstring
for cmds in (rextcmds, iextcmds):
base.cmdstring = " ".join(cmds)
base.cmds = cmds
# Don't call this atm. as the line has gone through it already,
# also makes it hard to do the "is ?extcmds[0] a cmd" check.
# base.plugins.run('args', args=base.cmds)
# We don't catch exceptions, just pass them up and fail...
base.parseCommands()
cmdret = base.doCommands()
if cmdret[0] != 2:
return cmdret[0], ['%s %s' % (basecmd, " ".join(cmds))]
base.cmds = ocmds
base.cmdstring = oline
return 2, ['%s %s' % (basecmd, " ".join(extcmds))]
class RepoPkgsCommand(YumCommand):
"""A class containing methods needed by the cli to execute the
repo command.
"""
def getNames(self):
"""Return a list containing the names of this command. This
command can be called from the command line by using any of these names.
:return: a list containing the names of this command
"""
return ['repo-pkgs',
'repo-packages', 'repository-pkgs', 'repository-packages']
def getUsage(self):
"""Return a usage string for this command.
:return: a usage string for this command
"""
return "<repoid> <list|info|install|remove|upgrade|reinstall*|remove-or-*> [pkg(s)]"
def getSummary(self):
"""Return a one line summary of this command.
:return: a one line summary of this command
"""
return _("Treat a repo. as a group of packages, so we can install/remove all of them")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
that there are enabled repositories with gpg keys, and that
this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
checkRepoPackageArg(base, basecmd, extcmds)
checkEnabledRepo(base, extcmds)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
def _add_repopkg2txmbrs(txmbrs, repoid):
for txmbr in txmbrs:
txmbr.repopkg = repoid
repoid = extcmds[0]
cmd = extcmds[1]
args = extcmds[2:]
noargs = False
if not args:
noargs = True
args = ['*']
num = 0
remap = {'erase' : 'remove',
'erase-or-reinstall' : 'remove-or-reinstall',
'erase-or-sync' : 'remove-or-sync',
'erase-or-distro-sync' : 'remove-or-sync',
'remove-or-distro-sync' : 'remove-or-sync',
'erase-or-distribution-synchronization' : 'remove-or-sync',
'remove-or-distribution-synchronization' : 'remove-or-sync',
'upgrade' : 'update', # Hack, but meh.
'upgrade-to' : 'update-to', # Hack, but meh.
'check-upgrade' : 'check-update', # Hack, but meh.
'check-upgrades' : 'check-update', # Hack, but meh.
'check-updates' : 'check-update', # Hack, but meh.
}
cmd = remap.get(cmd, cmd)
if False: pass
elif cmd == 'list': # list/info is easiest...
return ListCommand().doCommand(base, cmd, args, repoid=repoid)
elif cmd == 'info':
return InfoCommand().doCommand(base, cmd, args, repoid=repoid)
elif cmd == 'check-update':
return CheckUpdateCommand().doCommand(base, cmd, args,repoid=repoid)
elif cmd == 'install': # install is simpler version of installPkgs...
for arg in args:
txmbrs = base.install(pattern=arg, repoid=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if num:
return 2, P_('%d package to install', '%d packages to install',
num)
elif cmd == 'update': # update is basically the same as install...
for arg in args:
txmbrs = base.update(pattern=arg, repoid=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if num:
updateinfo.remove_txmbrs(base)
return 2, P_('%d package to update', '%d packages to update',
num)
elif cmd == 'update-to': # update is basically the same as install...
for arg in args:
txmbrs = base.update(pattern=arg, update_to=True, repoid=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if num:
updateinfo.remove_txmbrs(base)
return 2, P_('%d package to update', '%d packages to update',
num)
elif cmd in ('reinstall-old', 'reinstall-installed'):
# We have to choose for reinstall, for "reinstall foo" do we mean:
# 1. reinstall the packages that are currently installed from "foo".
# 2. reinstall the packages specified to the ones from "foo"
# This is for installed.from_repo=foo
if noargs:
onot_found_a = base._not_found_a.copy()
for arg in args:
txmbrs = base.reinstall(pattern=arg,
repoid=repoid, repoid_install=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if noargs:
base._not_found_a = onot_found_a
if num:
return 2, P_('%d package to reinstall',
'%d packages to reinstall', num)
elif cmd in ('reinstall-new', 'reinstall-available', 'move-to'):
# This is for move-to the packages from this repo.
if noargs:
onot_found_a = base._not_found_a.copy()
for arg in args:
txmbrs = base.reinstall(pattern=arg, repoid_install=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if noargs:
base._not_found_a = onot_found_a
if num:
return 2, P_('%d package to move to',
'%d packages to move to', num)
elif cmd == 'reinstall':
# This means "guess", so doing the old version unless it produces
# nothing, in which case try switching.
if noargs:
onot_found_a = base._not_found_a.copy()
for arg in args:
try:
txmbrs = base.reinstall(pattern=arg,
repoid=repoid,repoid_install=repoid)
except yum.Errors.ReinstallRemoveError:
continue
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if noargs:
base._not_found_a = onot_found_a.copy()
if num:
return 2, P_('%d package to reinstall',
'%d packages to reinstall', num)
for arg in args:
txmbrs = base.reinstall(pattern=arg, repoid_install=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if noargs:
base._not_found_a = onot_found_a
if num:
return 2, P_('%d package to move to',
'%d packages to move to', num)
elif cmd == 'remove': # Also mostly the same...
for arg in args:
txmbrs = base.remove(pattern=arg, repoid=repoid)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if num:
return 2, P_('%d package to remove', '%d packages to remove',
num)
elif cmd == 'remove-or-reinstall': # More complicated...
for arg in args:
txmbrs = base.remove(pattern=arg, repoid=repoid)
# Add an install() if it's in another repo.
for txmbr in txmbrs[:]:
pkgs = base.pkgSack.searchPkgTuple(txmbr.pkgtup)
for pkg in sorted(pkgs):
if pkg.repoid == repoid:
continue
txmbrs += base.install(po=pkg)
break
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if num:
return 2, P_('%d package to remove/reinstall',
'%d packages to remove/reinstall', num)
elif cmd == 'remove-or-sync': # Even more complicated...
for arg in args:
txmbrs = base.remove(pattern=arg, repoid=repoid)
# Add an install/upgrade/downgrade if a version is in another
# repo.
for txmbr in txmbrs[:]:
pkgs = base.pkgSack.searchNames([txmbr.name])
apkgs = None
for pkg in sorted(pkgs):
if pkg.repoid == repoid: # Backwards filter_pkgs_repoid
continue
if apkgs and pkg.verEQ(apkgs[0]):
apkgs.append(pkg)
else:
apkgs = [pkg]
if apkgs:
for pkg in apkgs:
if pkg.arch != txmbr.arch:
continue
apkgs = [pkg]
break
if len(apkgs) != 1:
apkgs = base.bestPackagesFromList(apkgs)
for toinst in apkgs:
n,a,e,v,r = toinst.pkgtup
if toinst.verEQ(txmbr.po):
txmbrs += base.install(po=toinst)
elif toinst.verGT(txmbr.po):
txmbrs += base.update(po=toinst)
else:
base.tsInfo.remove(txmbr.pkgtup)
txmbrs.remove(txmbr)
txmbrs += base.downgrade(po=toinst)
_add_repopkg2txmbrs(txmbrs, repoid)
num += len(txmbrs)
if num:
return 2, P_('%d package to remove/sync',
'%d packages to remove/sync', num)
else:
return 1, [_('Not a valid sub-command of %s') % basecmd]
return 0, [_('Nothing to do')]
def needTs(self, base, basecmd, extcmds):
"""Return whether a transaction set must be set up before this
command can run.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: True if a transaction set is needed, False otherwise
"""
cmd = 'install'
if len(extcmds) > 1:
cmd = extcmds[1]
if cmd in ('info', 'list'):
return InfoCommand().needTs(base, cmd, extcmds[2:])
return True
def cacheRequirement(self, base, basecmd, extcmds):
"""Return the cache requirements for the remote repos.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: a list of arguments passed to *basecmd*
:return: Type of requirement: read-only:past, read-only:present, read-only:future, write
"""
cmd = 'install'
if len(extcmds) > 1:
cmd = extcmds[1]
if cmd in ('info', 'list'):
return InfoCommand().cacheRequirement(base, cmd, extcmds[2:])
if cmd in ('check-update', 'check-upgrade',
'check-updates', 'check-upgrades'):
return CheckUpdateCommand().cacheRequirement(base, cmd, extcmds[2:])
return 'write'
# Using this a lot, so make it easier...
_upi = updateinfo
class UpdateinfoCommand(YumCommand):
# Old command names...
direct_cmds = {'list-updateinfo' : 'list',
'list-security' : 'list',
'list-sec' : 'list',
'info-updateinfo' : 'info',
'info-security' : 'info',
'info-sec' : 'info',
'summary-updateinfo' : 'summary'}
# Note that this code (instead of using inheritance and multiple
# cmd classes) means that "yum help" only displays the updateinfo command.
# Which is what we want, because the other commands are just backwards
# compatible gunk we don't want the user using).
def getNames(self):
return ['updateinfo'] + sorted(self.direct_cmds.keys())
def getUsage(self):
return "[info|list|...] [security|...] [installed|available|all] [pkgs|id]"
def getSummary(self):
return "Acts on repository update information"
def doCheck(self, base, basecmd, extcmds):
pass
def list_show_pkgs(self, base, md_info, list_type, show_type,
iname2tup, data, msg):
n_maxsize = 0
r_maxsize = 0
t_maxsize = 0
for (notice, pkgtup, pkg) in data:
n_maxsize = max(len(notice['update_id']), n_maxsize)
tn = notice['type']
if tn == 'security' and notice['severity']:
tn = notice['severity'] + '/Sec.'
t_maxsize = max(len(tn), t_maxsize)
if show_type:
for ref in _upi._ysp_safe_refs(notice['references']):
if ref['type'] != show_type:
continue
r_maxsize = max(len(str(ref['id'])), r_maxsize)
for (notice, pkgtup, pkg) in data:
mark = ''
if list_type == 'all':
mark = ' '
if pkgtup[0] in iname2tup and _upi._rpm_tup_vercmp(iname2tup[pkgtup[0]], pkgtup) >= 0:
mark = 'i '
tn = notice['type']
if tn == 'security' and notice['severity']:
tn = notice['severity'] + '/Sec.'
if show_type and _upi._ysp_has_info_md(show_type, notice):
for ref in _upi._ysp_safe_refs(notice['references']):
if ref['type'] != show_type:
continue
msg("%s %-*s %-*s %s" % (mark, r_maxsize, str(ref['id']),
t_maxsize, tn, pkg))
elif hasattr(pkg, 'name'):
print base.fmtKeyValFill("%s: " % pkg.name,
base._enc(pkg.summary))
else:
msg("%s%-*s %-*s %s" % (mark, n_maxsize, notice['update_id'],
t_maxsize, tn, pkg))
def info_show_pkgs(self, base, md_info, list_type, show_type,
iname2tup, data, msg):
show_pkg_info_done = {}
for (notice, pkgtup, pkg) in data:
if notice['update_id'] in show_pkg_info_done:
continue
show_pkg_info_done[notice['update_id']] = notice
if hasattr(notice, 'text'):
debug_log_lvl = yum.logginglevels.DEBUG_3
vlog = base.verbose_logger
if vlog.isEnabledFor(debug_log_lvl):
obj = notice.text(skip_data=[])
else:
obj = notice.text()
else:
# Python-2.4.* doesn't understand str(x) returning unicode
obj = notice.__str__()
if list_type == 'all':
if pkgtup[0] in iname2tup and _upi._rpm_tup_vercmp(iname2tup[pkgtup[0]], pkgtup) >= 0:
obj = obj + "\n Installed : true"
else:
obj = obj + "\n Installed : false"
msg(obj)
def summary_show_pkgs(self, base, md_info, list_type, show_type,
iname2tup, data, msg):
def _msg(x):
base.verbose_logger.info("%s", x)
counts = {}
sev_counts = {}
show_pkg_info_done = {}
for (notice, pkgtup, pkg) in data:
if notice['update_id'] in show_pkg_info_done:
continue
show_pkg_info_done[notice['update_id']] = notice
counts[notice['type']] = counts.get(notice['type'], 0) + 1
if notice['type'] == 'security':
sev = notice['severity']
if sev is None:
sev = ''
sev_counts[sev] = sev_counts.get(sev, 0) + 1
maxsize = 0
for T in ('newpackage', 'security', 'bugfix', 'enhancement'):
if T not in counts:
continue
size = len(str(counts[T]))
if maxsize < size:
maxsize = size
if not maxsize:
if base.conf.autocheck_running_kernel:
_upi._check_running_kernel(base, md_info, _msg)
return
outT = {'newpackage' : 'New Package',
'security' : 'Security',
'bugfix' : 'Bugfix',
'enhancement' : 'Enhancement'}
print "Updates Information Summary:", list_type
for T in ('newpackage', 'security', 'bugfix', 'enhancement'):
if T not in counts:
continue
n = outT[T]
if T == 'security' and len(sev_counts) == 1:
sn = sev_counts.keys()[0]
if sn != '':
n = sn + " " + n
print " %*u %s notice(s)" % (maxsize, counts[T], n)
if T == 'security' and len(sev_counts) != 1:
def _sev_sort_key(key):
# We want these in order, from "highest" to "lowest".
# Anything unknown is "higher". meh.
return {'Critical' : "zz1",
'Important': "zz2",
'Moderate' : "zz3",
'Low' : "zz4",
}.get(key, key)
for sn in sorted(sev_counts, key=_sev_sort_key):
args = (maxsize, sev_counts[sn],sn or '?', outT['security'])
print " %*u %s %s notice(s)" % args
if base.conf.autocheck_running_kernel:
_upi._check_running_kernel(base, md_info, _msg)
self.show_pkg_info_done = {}
def _get_new_pkgs(self, md_info):
for notice in md_info.notices:
if notice['type'] != "newpackage":
continue
for upkg in notice['pkglist']:
for pkg in upkg['packages']:
pkgtup = (pkg['name'], pkg['arch'], pkg['epoch'] or '0',
pkg['version'], pkg['release'])
yield (notice, pkgtup)
_cmd2filt = {"bugzillas" : "bugzilla",
"bugzilla" : "bugzilla",
"bzs" : "bugzilla",
"bz" : "bugzilla",
"sec" : "security",
"cves" : "cve",
"cve" : "cve",
"newpackages" : "newpackage",
"new-packages" : "newpackage",
"newpackage" : "newpackage",
"new-package" : "newpackage",
"new" : "newpackage"}
for filt_type in _upi._update_info_types_:
_cmd2filt[filt_type] = filt_type
def doCommand(self, base, basecmd, extcmds):
if basecmd in self.direct_cmds:
subcommand = self.direct_cmds[basecmd]
elif extcmds and extcmds[0] in ('list', 'info', 'summary',
'remove-pkgs-ts', 'exclude-updates',
'exclude-all',
'check-running-kernel'):
subcommand = extcmds[0]
extcmds = extcmds[1:]
elif extcmds and extcmds[0] in self._cmd2filt:
subcommand = 'list'
elif extcmds:
subcommand = 'info'
else:
subcommand = 'summary'
if subcommand == 'list':
return self.doCommand_li(base, 'updateinfo list', extcmds,
self.list_show_pkgs)
if subcommand == 'info':
return self.doCommand_li(base, 'updateinfo info', extcmds,
self.info_show_pkgs)
if subcommand == 'summary':
return self.doCommand_li(base, 'updateinfo summary', extcmds,
self.summary_show_pkgs)
if subcommand == 'remove-pkgs-ts':
filters = None
if extcmds:
filters = updateinfo._args2filters(extcmds)
updateinfo.remove_txmbrs(base, filters)
return 0, [basecmd + ' ' + subcommand + ' done']
if subcommand == 'exclude-all':
filters = None
if extcmds:
filters = updateinfo._args2filters(extcmds)
updateinfo.exclude_all(base, filters)
return 0, [basecmd + ' ' + subcommand + ' done']
if subcommand == 'exclude-updates':
filters = None
if extcmds:
filters = updateinfo._args2filters(extcmds)
updateinfo.exclude_updates(base, filters)
return 0, [basecmd + ' ' + subcommand + ' done']
if subcommand == 'check-running-kernel':
def _msg(x):
base.verbose_logger.info("%s", x)
updateinfo._check_running_kernel(base, base.upinfo, _msg)
return 0, [basecmd + ' ' + subcommand + ' done']
def doCommand_li_new(self, base, list_type, extcmds, md_info, msg,
show_pkgs, iname2tup):
done_pkgs = set()
data = []
for (notice, pkgtup) in sorted(self._get_new_pkgs(md_info),
key=lambda x: x[1][0]):
if extcmds and not _upi._match_sec_cmd(extcmds, pkgtup[0], notice):
continue
n = pkgtup[0]
if n in done_pkgs:
continue
ipkgs = list(reversed(sorted(base.rpmdb.searchNames([n]))))
if list_type in ('installed', 'updates') and not ipkgs:
done_pkgs.add(n)
continue
if list_type == 'available' and ipkgs:
done_pkgs.add(n)
continue
pkgs = base.pkgSack.searchPkgTuple(pkgtup)
if not pkgs:
continue
if list_type == "updates" and pkgs[0].verLE(ipkgs[0]):
done_pkgs.add(n)
continue
done_pkgs.add(n)
data.append((notice, pkgtup, pkgs[0]))
show_pkgs(base, md_info, list_type, None, iname2tup, data, msg)
def _parse_extcmds(self, extcmds):
filt_type = None
show_type = None
if len(extcmds) >= 1:
filt_type = None
if extcmds[0] in self._cmd2filt:
filt_type = self._cmd2filt[extcmds.pop(0)]
show_type = filt_type
if filt_type and filt_type in _upi._update_info_types_:
show_type = None
return extcmds, show_type, filt_type
def doCommand_li(self, base, basecmd, extcmds, show_pkgs):
md_info = base.upinfo
def msg(x):
# Don't use: logger.log(logginglevels.INFO_2, x)
# or -q deletes everything.
print x
extcmds, show_type, filt_type = self._parse_extcmds(extcmds)
list_type = "available"
list_type = "updates"
if extcmds and extcmds[0] in ("updates","available","installed", "all"):
list_type = extcmds.pop(0)
if filt_type is None:
extcmds, show_type, filt_type = self._parse_extcmds(extcmds)
opts = _upi._ysp_gen_opts(base.updateinfo_filters, extcmds)
used_map = _upi._ysp_gen_used_map(base.updateinfo_filters)
iname2tup = {}
if False: pass
elif list_type == 'installed':
name2tup = _upi._get_name2allpkgtup(base)
iname2tup = _upi._get_name2instpkgtup(base)
elif list_type == 'updates':
name2tup = _upi._get_name2oldpkgtup(base)
elif list_type in ('available', 'all'):
name2tup = _upi._get_name2aallpkgtup(base)
iname2tup = _upi._get_name2instpkgtup(base)
if filt_type == "newpackage":
self.doCommand_li_new(base, list_type, extcmds, md_info, msg,
show_pkgs, iname2tup)
return 0, [basecmd + ' new done']
def _show_pkgtup(pkgtup):
name = pkgtup[0]
notices = reversed(md_info.get_applicable_notices(pkgtup))
for (pkgtup, notice) in notices:
if filt_type and not _upi._ysp_has_info_md(filt_type, notice):
continue
if list_type == 'installed':
# Remove any that are newer than what we have installed
if _upi._rpm_tup_vercmp(iname2tup[name], pkgtup) < 0:
continue
if list_type == 'available':
# Remove any that are installed
if name in iname2tup and _upi._rpm_tup_vercmp(iname2tup[name], pkgtup) >= 0:
continue
if _upi._ysp_should_filter_pkg(opts, name, notice, used_map):
yield (pkgtup, notice)
data = []
for pkgname in sorted(name2tup):
for (pkgtup, notice) in _show_pkgtup(name2tup[pkgname]):
d = {}
(d['n'], d['a'], d['e'], d['v'], d['r']) = pkgtup
if d['e'] == '0':
d['epoch'] = ''
else:
d['epoch'] = "%s:" % d['e']
data.append((notice, pkgtup,
"%(n)s-%(epoch)s%(v)s-%(r)s.%(a)s" % d))
show_pkgs(base, md_info, list_type, show_type, iname2tup, data, msg)
_upi._ysp_chk_used_map(used_map, msg)
return 0, [basecmd + ' done']
class UpdateMinimalCommand(YumCommand):
def getNames(self):
return ['update-minimal', 'upgrade-minimal']
def getUsage(self):
return "[PACKAGE-wildcard]"
def getSummary(self):
return _("Works like upgrade, but goes to the 'newest' package match which fixes a problem that affects your system")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
that there are enabled repositories with gpg keys, and that
this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
checkGPGKey(base)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
num = len(base.tsInfo)
_upi.update_minimal(base, extcmds)
num = len(base.tsInfo) - num
if num > 0:
msg = '%d packages marked for minimal Update' % num
return 2, [msg]
else:
return 0, ['No Packages marked for minimal Update']
class FSSnapshotCommand(YumCommand):
def getNames(self):
return ['fssnapshot', 'fssnap']
def getUsage(self):
return "[]"
def getSummary(self):
return _("Creates filesystem snapshots, or lists/deletes current snapshots.")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
that there are enabled repositories with gpg keys, and that
this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
checkRootUID(base)
@staticmethod
def _li_snaps(base, snaps):
snaps = sorted(snaps, key=lambda x: x['dev'])
max_dev = utf8_width(_('Snapshot'))
max_ori = utf8_width(_('Origin'))
for data in snaps:
max_dev = max(max_dev, len(data['dev']))
max_ori = max(max_ori, len(data['origin']))
done = False
for data in snaps:
if not done:
print ("%s %s %s %s %s %s" %
(utf8_width_fill(_('Snapshot'), max_dev),
utf8_width_fill(_('Size'), 6, left=False),
utf8_width_fill(_('Used'), 6, left=False),
utf8_width_fill(_('Free'), 6, left=False),
utf8_width_fill(_('Origin'), max_ori), _('Tags')))
done = True
print ("%*s %6s %5.1f%% %6s %*s %s" %
(max_dev, data['dev'], base.format_number(data['size']),
data['used'],
base.format_number(data['free']),
max_ori, data['origin'], ",".join(data['tags'])))
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
if extcmds and extcmds[0] in ('list', 'delete', 'create', 'summary',
'have-space', 'has-space'):
subcommand = extcmds[0]
extcmds = extcmds[1:]
else:
subcommand = 'summary'
if not base.fssnap.available:
msg = _("Snapshot support not available, please check your lvm installation.")
if not base.rpmdb.searchNames(['lvm2']):
msg += " " + _("No lvm2 package installed.")
if not base.rpmdb.searchNames(['lvm2-python-libs']):
msg += " " + _("No lvm2-python-libs package installed.")
print msg
return 1, [basecmd + ' ' + subcommand + ' done']
if subcommand == 'list':
try:
snaps = base.fssnap.old_snapshots()
except LibLVMError as e:
return 1, [_("Failed to list snapshots: ") + lvmerr2str(e)]
print _("List of %u snapshosts:") % len(snaps)
self._li_snaps(base, snaps)
if subcommand == 'delete':
msg = _("Failed to delete snapshots: ")
try:
snaps = base.fssnap.old_snapshots()
except LibLVMError as e:
return 1, [msg + lvmerr2str(e)]
devs = [x['dev'] for x in snaps]
snaps = set()
for dev in devs:
if dev in snaps:
continue
for extcmd in extcmds:
if dev == extcmd or fnmatch.fnmatch(dev, extcmd):
snaps.add(dev)
break
try:
snaps = base.fssnap.del_snapshots(devices=snaps)
except LibLVMError as e:
return 1, [msg + lvmerr2str(e)]
print _("Deleted %u snapshosts:") % len(snaps)
self._li_snaps(base, snaps)
if subcommand in ('have-space', 'has-space'):
pc = base.conf.fssnap_percentage
try:
has_space = base.fssnap.has_space(pc)
except LibLVMError as e:
return 1, [_("Could not determine free space on logical volumes: ") + lvmerr2str(e)]
if has_space:
print _("Space available to take a snapshot.")
else:
print _("Not enough space available on logical volumes to take a snapshot.")
if subcommand == 'create':
tags = {'*': ['reason=manual']}
pc = base.conf.fssnap_percentage
msg = _("Failed to create snapshots")
try:
snaps = base.fssnap.snapshot(pc, tags=tags)
except LibLVMError as e:
msg += ": " + lvmerr2str(e)
snaps = []
if not snaps:
print msg
for (odev, ndev) in snaps:
print _("Created snapshot from %s, results is: %s") %(odev,ndev)
if subcommand == 'summary':
try:
snaps = base.fssnap.old_snapshots()
except LibLVMError as e:
return 1, [_("Failed to list snapshots: ") + lvmerr2str(e)]
if not snaps:
print _("No snapshots, LVM version:"), base.fssnap.version
return 0, [basecmd + ' ' + subcommand + ' done']
used = 0
dev_oris = set()
for snap in snaps:
used += snap['used']
dev_oris.add(snap['origin_dev'])
msg = _("Have %u snapshots, using %s space, from %u origins.")
print msg % (len(snaps), base.format_number(used), len(dev_oris))
return 0, [basecmd + ' ' + subcommand + ' done']
class FSCommand(YumCommand):
def getNames(self):
return ['fs']
def getUsage(self):
return "[]"
def getSummary(self):
return _("Acts on the filesystem data of the host, mainly for removing docs/lanuages for minimal hosts.")
def doCheck(self, base, basecmd, extcmds):
"""Verify that conditions are met so that this command can run.
These include that the program is being run by the root user,
that there are enabled repositories with gpg keys, and that
this command is called with appropriate arguments.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
"""
if extcmds and extcmds[0] in ('du', 'status', 'diff'):
# Anyone can go for it...
return
if len(extcmds) == 1 and extcmds[0] in ('filters', 'filter'):
# Can look, but not touch.
return
checkRootUID(base)
def _fs_pkg_walk(self, pkgs, prefix, modified=False, verbose=False):
pfr = {'norm' : {},
'mod' : {},
'ghost' : {},
'miss' : {},
'not' : {}
}
def quick_match(pkgs):
for pkg in pkgs:
for fname in pkg.filelist + pkg.dirlist:
if not fname.startswith(prefix):
continue
pfr['norm'][fname] = pkg
for fname in pkg.ghostlist:
if not fname.startswith(prefix):
continue
pfr['ghost'][fname] = pkg
return pfr
def _quick_match_iter(pkgs):
# Walking the fi information is much slower than filelist/dirlist
for pkg in pkgs:
found = False
for fname in pkg.dirlist:
if fname.startswith(prefix):
yield pkg
found = True
break
if found:
continue
for fname in pkg.filelist:
if fname.startswith(prefix):
yield pkg
found = True
break
if found:
continue
for fname in pkg.ghostlist:
if fname.startswith(prefix):
yield pkg
break
def verify_match(pkgs):
_pfs = []
def scoop_pfs(pfs):
_pfs.append(pfs)
if not modified:
return []
return pfs
if prefix != '/':
pkgs = _quick_match_iter(pkgs)
for pkg in pkgs:
_pfs = []
probs = pkg.verify(patterns=[prefix+'*'], fake_problems=False,
callback=scoop_pfs, failfast=True)
for pf in _pfs[0]:
if pf.filename in probs:
pfr['mod'][pf.filename] = pkg
elif pf.rpmfile_state == 'not installed':
pfr['not'][pf.filename] = pkg
elif 'ghost' in pf.rpmfile_types:
pfr['ghost'][pf.filename] = pkg
elif 'missing ok' in pf.rpmfile_types:
pfr['miss'][pf.filename] = pkg
else:
pfr['norm'][pf.filename] = pkg
return pfr
# return quick_match(pkgs)
return verify_match(pkgs)
def _fs_du(self, base, extcmds):
def _dir_prefixes(path):
while path != '/':
path = os.path.dirname(path)
yield path
def loc_num(x):
""" String of a number in the readable "locale" format. """
return locale.format("%d", int(x), True)
data = {'pkgs_size' : {},
'pkgs_not_size' : {},
'pkgs_ghost_size' : {},
'pkgs_miss_size' : {},
'pkgs_mod_size' : {},
'pres_size' : {},
'data_size' : {},
'data_not_size' : {},
'pkgs_count' : 0,
'pkgs_not_count' : 0,
'pkgs_ghost_count' : 0,
'pkgs_miss_count' : 0,
'pkgs_mod_count' : 0,
'data_count' : 0} # data_not_count == pkgs_not_count
def _add_size(d, v, size):
if v not in d:
d[v] = 0
d[v] += size
def deal_with_file(fpath, need_prefix=True):
size = os.path.getsize(fpath)
if fpath in pfr['norm']:
data['pkgs_count'] += size
_add_size(data['pkgs_size'], pfr['norm'][fpath], size)
elif fpath in pfr['ghost']:
data['pkgs_ghost_count'] += size
_add_size(data['pkgs_ghost_size'], pfr['ghost'][fpath], size)
elif fpath in pfr['not']:
data['pkgs_not_count'] += size
_add_size(data['pkgs_not_size'], pfr['not'][fpath], size)
data['data_not_size'][fpath] = size
elif fpath in pfr['miss']:
data['pkgs_miss_count'] += size
_add_size(data['pkgs_miss_size'], pfr['miss'][fpath], size)
elif fpath in pfr['mod']:
data['pkgs_mod_count'] += size
_add_size(data['pkgs_mod_size'], pfr['mod'][fpath], size)
elif need_prefix and False:
for fpre_path in _dir_prefixes(fpath):
if fpre_path not in pkg_files:
continue
_add_size(data['pres_size'], pkg_files[fpre_path], size)
break
data['data_count'] += size
data['data_size'][fpath] = size
else:
data['data_count'] += size
data['data_size'][fpath] = size
prefix = "."
if extcmds:
prefix = extcmds[0]
extcmds = extcmds[1:]
if not os.path.exists(prefix):
return 1, [_('No such file or directory: ' + prefix)]
max_show_len = 4
if extcmds:
try:
max_show_len = int(extcmds[0])
except:
pass
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
pfr = self._fs_pkg_walk(base.rpmdb, prefix, verbose=verbose)
base.closeRpmDB() # C-c ftw.
num = 0
if os.path.isfile(prefix):
num += 1
deal_with_file(prefix)
for root, dirs, files in os.walk(prefix):
for fname in files:
num += 1
fpath = os.path.normpath(root + '/' + fname)
if os.path.islink(fpath):
continue
deal_with_file(fpath, need_prefix=verbose)
# output
print "Files :", loc_num(num)
tot = 0
tot += data['pkgs_count']
tot += data['pkgs_ghost_count']
tot += data['pkgs_not_count']
tot += data['pkgs_miss_count']
tot += data['pkgs_mod_count']
tot += data['data_count']
print "Total size :", base.format_number(tot)
if not tot:
return
num = data['pkgs_count']
if not verbose:
num += data['pkgs_ghost_count']
num += data['pkgs_miss_count']
num += data['pkgs_mod_count']
print " Pkgs size :", "%-5s" % base.format_number(num),
print "(%3.0f%%)" % ((num * 100.0) / tot)
if verbose:
for (title, num) in ((_(" Ghost pkgs size :"),
data['pkgs_ghost_count']),
(_(" Not pkgs size :"),
data['pkgs_not_count']),
(_(" Miss pkgs size :"),
data['pkgs_miss_count']),
(_(" Mod. pkgs size :"),
data['pkgs_mod_count'])):
if not num:
continue
print title, "%-5s" % base.format_number(num),
print "(%3.0f%%)" % ((num * 100.0) / tot)
num = data['data_count']
if not verbose:
num += data['pkgs_not_count']
print _(" Data size :"), "%-5s" % base.format_number(num),
print "(%3.0f%%)" % ((num * 100.0) / tot)
if verbose:
print ''
print _("Pkgs :"), loc_num(len(data['pkgs_size']))
print _("Ghost Pkgs :"), loc_num(len(data['pkgs_ghost_size']))
print _("Not Pkgs :"), loc_num(len(data['pkgs_not_size']))
print _("Miss. Pkgs :"), loc_num(len(data['pkgs_miss_size']))
print _("Mod. Pkgs :"), loc_num(len(data['pkgs_mod_size']))
def _pkgs(p_size, msg):
tot = min(max_show_len, len(p_size))
if tot:
print ''
print msg % tot
num = 0
for pkg in sorted(p_size, key=lambda x: p_size[x], reverse=True):
num += 1
print _("%*d. %60s %-5s") % (len(str(tot)), num, pkg,
base.format_number(p_size[pkg]))
if num >= tot:
break
if verbose:
_pkgs(data['pkgs_size'], _('Top %d packages:'))
_pkgs(data['pkgs_ghost_size'], _('Top %d ghost packages:'))
_pkgs(data['pkgs_not_size'], _('Top %d not. packages:'))
_pkgs(data['pkgs_miss_size'], _('Top %d miss packages:'))
_pkgs(data['pkgs_mod_size'], _('Top %d mod. packages:'))
_pkgs(data['pres_size'], _('Top %d prefix packages:'))
else:
tmp = {}
tmp.update(data['pkgs_size'])
for d in data['pkgs_ghost_size']:
_add_size(tmp, d, data['pkgs_ghost_size'][d])
for d in data['pkgs_miss_size']:
_add_size(tmp, d, data['pkgs_miss_size'][d])
for d in data['pkgs_mod_size']:
_add_size(tmp, d, data['pkgs_mod_size'][d])
_pkgs(tmp, _('Top %d packages:'))
print ''
if verbose:
data_size = data['data_size']
else:
data_size = {}
data_size.update(data['data_size'])
data_size.update(data['data_not_size'])
tot = min(max_show_len, len(data_size))
if tot:
print _('Top %d non-package files:') % tot
num = 0
for fname in sorted(data_size,
key=lambda x: data_size[x],
reverse=True):
num += 1
dsznum = data_size[fname]
print _("%*d. %60s %-5s") % (len(str(tot)), num, fname,
base.format_number(dsznum))
if num >= tot:
break
def _fs_filters(self, base, extcmds):
def _save(confkey):
writeRawConfigFile = yum.config._writeRawConfigFile
# Always create installroot, so we can change it.
if not os.path.exists(base.conf.installroot + '/etc/yum'):
os.makedirs(base.conf.installroot + '/etc/yum')
fn = base.conf.installroot+'/etc/yum/yum.conf'
if not os.path.exists(fn):
# Try the old default
nfn = base.conf.installroot+'/etc/yum.conf'
if os.path.exists(nfn):
fn = nfn
else:
shutil.copy2(base.conf.config_file_path, fn)
ybc = base.conf
writeRawConfigFile(fn, 'main', ybc.yumvar,
ybc.cfg.options, ybc.iteritems,
ybc.optionobj,
only=[confkey])
if not extcmds:
oil = base.conf.override_install_langs
if not oil:
oil = "rpm: " + rpm.expandMacro("%_install_langs")
print _("File system filters:")
print _(" Nodocs:"), 'nodocs' in base.conf.tsflags
print _(" Languages:"), oil
elif extcmds[0] in ('docs', 'nodocs',
'documentation', 'nodocumentation'):
c_f = 'nodocs' in base.conf.tsflags
n_f = not extcmds[0].startswith('no')
if n_f == c_f:
if n_f:
print _("Already enabled documentation filter.")
else:
print _("Already disabled documentation filter.")
return
if n_f:
print _("Enabling documentation filter.")
else:
print _("Disabling documentation filter.")
nts = base.conf.tsflags
if n_f:
nts = nts + ['nodocs']
else:
nts = [x for x in nts if x != 'nodocs']
base.conf.tsflags = " ".join(nts)
_save('tsflags')
elif extcmds[0] in ('langs', 'nolangs', 'lang', 'nolang',
'languages', 'nolanguages',
'language', 'nolanguage'):
if extcmds[0].startswith('no') or len(extcmds) < 2 or 'all' in extcmds:
val = 'all'
else:
val = ":".join(extcmds[1:])
if val == base.conf.override_install_langs:
if val:
print _("Already filtering languages to: %s") % val
else:
print _("Already disabled language filter.")
return
if val:
print _("Setting language filter to: %s") % val
else:
print _("Disabling language filter.")
base.conf.override_install_langs = val
_save('override_install_langs')
else:
return 1, [_('Not a valid sub-command of fs filter')]
def _fs_refilter(self, base, extcmds):
c_f = 'nodocs' in base.conf.tsflags
# FIXME: C&P from init.
oil = base.conf.override_install_langs
if not oil:
oil = rpm.expandMacro("%_install_langs")
if oil == 'all':
oil = ''
elif oil:
oil = ":".join(sorted(oil.split(':')))
found = False
num = 0
for pkg in base.rpmdb.returnPackages(patterns=extcmds):
if False: pass
elif oil != pkg.yumdb_info.get('ts_install_langs', ''):
txmbrs = base.reinstall(po=pkg)
num += len(txmbrs)
elif c_f != ('true' == pkg.yumdb_info.get('tsflag_nodocs')):
txmbrs = base.reinstall(po=pkg)
num += len(txmbrs)
else:
found = True
if num:
return 2,P_('%d package to reinstall','%d packages to reinstall',
num)
if not found:
return 1, [_('No valid packages: %s') % " ".join(extcmds)]
def _fs_refilter_cleanup(self, base, extcmds):
pkgs = base.rpmdb.returnPackages(patterns=extcmds)
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
pfr = self._fs_pkg_walk(pkgs, "/", verbose=verbose, modified=True)
base.closeRpmDB() # C-c ftw.
for fname in sorted(pfr['not']):
print _('Removing:'), fname
try: # Ignore everything, unlink_f() doesn't.
os.unlink(fname)
except OSError, e:
if e.errno == errno.EISDIR:
try:
os.rmdir(fname)
except:
pass
except:
pass
def _fs_diff(self, base, extcmds):
def deal_with_file(fpath):
if fpath in pfr['norm']:
pass
elif fpath in pfr['ghost']:
pass
elif fpath in pfr['not']:
print >>sys.stderr, _('Not installed:'), fpath
elif fpath in pfr['miss']:
pass
elif fpath in pfr['mod']:
pkg = apkgs[pfr['mod'][fpath].pkgtup]
# Hacky ... but works.
sys.stdout.flush()
extract_cmd = "cd %s; rpm2cpio %s | cpio --quiet -id .%s"
extract_cmd = extract_cmd % (tmpdir, pkg.localPkg(), fpath)
os.system(extract_cmd)
diff_cmd = "diff -ru %s %s" % (tmpdir + fpath, fpath)
print diff_cmd
sys.stdout.flush()
os.system(diff_cmd)
else:
print >>sys.stderr, _('Not packaged?:'), fpath
if not distutils.spawn.find_executable("diff"):
raise yum.Errors.YumBaseError, _("Can't find diff command")
# These just shouldn't happen...
if not distutils.spawn.find_executable("cpio"):
raise yum.Errors.YumBaseError, _("Can't find cpio command")
if not distutils.spawn.find_executable("rpm2cpio"):
raise yum.Errors.YumBaseError, _("Can't find rpm2cpio command")
prefix = "."
if extcmds:
prefix = extcmds[0]
extcmds = extcmds[1:]
pkgs = base.rpmdb.returnPackages(patterns=extcmds)
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
pfr = self._fs_pkg_walk(pkgs, prefix, verbose=verbose, modified=True)
base.closeRpmDB() # C-c ftw.
apkgs = {}
downloadpkgs = []
for ipkg in set(pfr['mod'].values()):
iyi = ipkg.yumdb_info
if 'from_repo' in iyi: # Updates-testing etc.
if iyi.from_repo in base.repos.repos:
repo = base.repos.getRepo(iyi.from_repo)
if not repo.isEnabled():
base.repos.enableRepo(repo.id)
for apkg in base.pkgSack.searchPkgTuple(ipkg.pkgtup):
if ('checksum_type' in iyi and
'checksum_data' in iyi and
iyi.checksum_type == apkg.checksum_type and
iyi.checksum_data == apkg.pkgId):
apkgs[ipkg.pkgtup] = apkg
downloadpkgs.append(apkg)
break
if ipkg.pkgtup not in apkgs:
raise yum.Errors.YumBaseError, _("Can't find package: %s") %ipkg
if downloadpkgs:
tmpdir = tempfile.mkdtemp()
problems = base.downloadPkgs(downloadpkgs, callback_total=base.download_callback_total_cb)
if len(problems) > 0:
errstring = ''
errstring += _('Error downloading packages:\n')
for key in problems:
errors = yum.misc.unique(problems[key])
for error in errors:
errstring += ' %s: %s\n' % (key, error)
raise yum.Errors.YumBaseError, errstring
for root, dirs, files in os.walk(prefix):
for fname in files:
fpath = os.path.normpath(root + '/' + fname)
if os.path.islink(fpath):
continue
deal_with_file(fpath)
if downloadpkgs:
shutil.rmtree(tmpdir)
def _fs_status(self, base, extcmds):
def deal_with_file(fpath):
if fpath in pfr['norm']:
pass
elif fpath in pfr['ghost']:
pass
elif fpath in pfr['not']:
print _('Not installed:'), fpath
elif fpath in pfr['miss']:
pass
elif fpath in pfr['mod']:
print _('Modified:'), fpath
else:
print _('Not packaged?:'), fpath
prefix = "."
if extcmds:
prefix = extcmds[0]
extcmds = extcmds[1:]
pkgs = base.rpmdb.returnPackages(patterns=extcmds)
verbose = base.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
pfr = self._fs_pkg_walk(pkgs, prefix, verbose=verbose, modified=True)
base.closeRpmDB() # C-c ftw.
for root, dirs, files in os.walk(prefix):
for fname in files:
fpath = os.path.normpath(root + '/' + fname)
if os.path.islink(fpath):
continue
deal_with_file(fpath)
def doCommand(self, base, basecmd, extcmds):
"""Execute this command.
:param base: a :class:`yum.Yumbase` object
:param basecmd: the name of the command
:param extcmds: the command line arguments passed to *basecmd*
:return: (exit_code, [ errors ])
exit_code is::
0 = we're done, exit
1 = we've errored, exit with error string
2 = we've got work yet to do, onto the next stage
"""
if extcmds and extcmds[0] in ('filters', 'filter',
'refilter', 'refilter-cleanup',
'du', 'status', 'diff', 'snap'):
subcommand = extcmds[0]
extcmds = extcmds[1:]
else:
subcommand = 'filters'
if False: pass
elif subcommand == 'du':
ret = self._fs_du(base, extcmds)
elif subcommand in ('filter', 'filters'):
ret = self._fs_filters(base, extcmds)
elif subcommand == 'refilter':
ret = self._fs_refilter(base, extcmds)
elif subcommand == 'refilter-cleanup':
ret = self._fs_refilter_cleanup(base, extcmds)
elif subcommand == 'diff':
ret = self._fs_diff(base, extcmds)
elif subcommand == 'status':
ret = self._fs_status(base, extcmds)
elif subcommand == 'snap':
ret = FSSnapshotCommand().doCommand(base, 'fs snap', args)
else:
return 1, [_('Not a valid sub-command of %s') % basecmd]
if ret is not None:
return ret
return 0, [basecmd + ' ' + subcommand + ' done']
SEA-GHOST - SHELL CODING BY SEA-GHOST