[Yum-devel] [PATCH] Forward port all the UI and core for installed groups.
James Antill
james at and.org
Fri Jan 20 21:22:07 UTC 2012
---
cli.py | 4 +-
output.py | 70 ++++++++++++++++++---
yum/__init__.py | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
yum/config.py | 1 +
yumcommands.py | 81 +++++++++++++++++++++++--
5 files changed, 310 insertions(+), 31 deletions(-)
diff --git a/cli.py b/cli.py
index ac9522b..919218c 100755
--- a/cli.py
+++ b/cli.py
@@ -1647,7 +1647,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
return 0, []
- def installGroups(self, grouplist):
+ def installGroups(self, grouplist, upgrade=False):
"""Mark the packages in the given groups for installation.
:param grouplist: a list of names or wildcards specifying
@@ -1669,7 +1669,7 @@ class YumBaseCli(yum.YumBase, output.YumOutput):
try:
- txmbrs = self.selectGroup(group.groupid)
+ txmbrs = self.selectGroup(group.groupid, upgrade=upgrade)
except yum.Errors.GroupsError:
self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
continue
diff --git a/output.py b/output.py
index 01f0c40..6e38868 100755
--- a/output.py
+++ b/output.py
@@ -1017,27 +1017,58 @@ class YumOutput:
return ret
def _calcDataPkgColumns(self, data, pkg_names, pkg_names2pkgs,
- indent=' '):
+ indent=' ', igroup_data=None):
for item in pkg_names:
if item not in pkg_names2pkgs:
continue
for (apkg, ipkg) in pkg_names2pkgs[item]:
pkg = ipkg or apkg
envra = utf8_width(str(pkg)) + utf8_width(indent)
+ if igroup_data:
+ envra += 1
rid = len(pkg.ui_from_repo)
for (d, v) in (('envra', envra), ('rid', rid)):
data[d].setdefault(v, 0)
data[d][v] += 1
def _displayPkgsFromNames(self, pkg_names, verbose, pkg_names2pkgs,
- indent=' ', columns=None):
+ indent=' ', columns=None, igroup_data=None):
+
+ def _get_igrp_data(item, indent):
+ if not igroup_data:
+ return indent
+
+ assert item in igroup_data
+ if item not in igroup_data or igroup_data[item] == 'available':
+ indent += '+' # Group up/in will install i
+ elif igroup_data[item] == 'installed':
+ indent += '=' # Installed via. group
+ elif igroup_data[item] == 'blacklisted-installed':
+ if False: # Not sure it's worth listing these...
+ return None # On the other hand, there's mark-packages
+ indent += ' ' # Installed, not via. group
+ else:
+ assert igroup_data[item] == 'blacklisted-available'
+ if False: # Not sure it's worth listing these...
+ return None
+ indent += '-' # Not installed, and won't be
+ return indent
+
if not verbose:
for item in sorted(pkg_names):
- print '%s%s' % (indent, item)
+ pindent = _get_igrp_data(item, indent)
+ if pindent is None:
+ continue
+
+ print '%s%s' % (pindent, item)
else:
for item in sorted(pkg_names):
+ pindent = _get_igrp_data(item, indent)
+ if pindent is None:
+ continue
+
if item not in pkg_names2pkgs:
- print '%s%s' % (indent, item)
+ print '%s%s' % (pindent, item)
continue
for (apkg, ipkg) in sorted(pkg_names2pkgs[item],
key=lambda x: x[1] or x[0]):
@@ -1048,7 +1079,7 @@ class YumOutput:
else:
highlight = False
self.simpleEnvraList(ipkg or apkg, ui_overflow=True,
- indent=indent, highlight=highlight,
+ indent=pindent, highlight=highlight,
columns=columns)
def displayPkgsInGroups(self, group):
@@ -1061,9 +1092,25 @@ class YumOutput:
verb = self.verbose_logger.isEnabledFor(logginglevels.DEBUG_3)
if verb:
print _(' Group-Id: %s') % to_unicode(group.groupid)
+
+ igroup_data = self._groupInstalledData(group)
+ igrp_only = set()
+ for pkg_name in igroup_data:
+ if igroup_data[pkg_name] == 'installed':
+ igrp_only.add(pkg_name)
+ igrp_only.difference_update(group.packages)
+ all_pkgs = group.packages + list(igrp_only)
+
pkg_names2pkgs = None
if verb:
- pkg_names2pkgs = self._group_names2aipkgs(group.packages)
+ pkg_names2pkgs = self._group_names2aipkgs(all_pkgs)
+ else:
+ pkg_names2pkgs = {}
+ for ipkg in self.rpmdb.searchNames(all_pkgs):
+ if ipkg.name not in pkg_names2pkgs:
+ pkg_names2pkgs[ipkg.name] = []
+ pkg_names2pkgs[ipkg.name].append(ipkg)
+
if group.ui_description:
print _(' Description: %s') % to_unicode(group.ui_description)
if group.langonly:
@@ -1077,7 +1124,8 @@ class YumOutput:
if verb:
data = {'envra' : {}, 'rid' : {}}
for (section_name, pkg_names) in sections:
- self._calcDataPkgColumns(data, pkg_names, pkg_names2pkgs)
+ self._calcDataPkgColumns(data, pkg_names, pkg_names2pkgs,
+ igroup_data=igroup_data)
data = [data['envra'], data['rid']]
columns = self.calcColumns(data)
columns = (-columns[0], -columns[1])
@@ -1086,7 +1134,13 @@ class YumOutput:
if len(pkg_names) > 0:
print section_name
self._displayPkgsFromNames(pkg_names, verb, pkg_names2pkgs,
- columns=columns)
+ columns=columns,
+ igroup_data=igroup_data)
+ if igrp_only:
+ print _(' Installed Packages:')
+ self._displayPkgsFromNames(igrp_only, verb, pkg_names2pkgs,
+ columns=columns,
+ igroup_data=igroup_data)
def depListOutput(self, results):
"""Format and output a list of findDeps results
diff --git a/yum/__init__.py b/yum/__init__.py
index e6f3e57..bd30546 100644
--- a/yum/__init__.py
+++ b/yum/__init__.py
@@ -73,6 +73,7 @@ import logginglevels
import yumRepo
import callbacks
import yum.history
+import yum.igroups
import warnings
warnings.simplefilter("ignore", Errors.YumFutureDeprecationWarning)
@@ -181,6 +182,7 @@ class YumBase(depsolve.Depsolve):
self._up = None
self._comps = None
self._history = None
+ self._igroups = None
self._pkgSack = None
self._lockfile = None
self._tags = None
@@ -223,6 +225,9 @@ class YumBase(depsolve.Depsolve):
if self._history is not None:
self.history.close()
+ if self._igroups is not None:
+ self.igroups.close()
+
if self._repos:
self._repos.close()
@@ -949,6 +954,14 @@ class YumBase(depsolve.Depsolve):
releasever=self.conf.yumvar['releasever'])
return self._history
+ def _getIGroups(self):
+ """auto create the installed groups object that to access/change the
+ installed groups information. """
+ if self._igroups is None:
+ pdb_path = self.conf.persistdir + "/groups"
+ self._igroups = yum.igroups.InstalledGroups(db_path=pdb_path)
+ return self._igroups
+
# properties so they auto-create themselves with defaults
repos = property(fget=lambda self: self._getRepos(),
fset=lambda self, value: setattr(self, "_repos", value),
@@ -986,6 +999,11 @@ class YumBase(depsolve.Depsolve):
fdel=lambda self: setattr(self, "_history", None),
doc="Yum History Object")
+ igroups = property(fget=lambda self: self._getIGroups(),
+ fset=lambda self, value: setattr(self, "_igroups",value),
+ fdel=lambda self: setattr(self, "_igroups", None),
+ doc="Yum Installed Groups Object")
+
pkgtags = property(fget=lambda self: self._getTags(),
fset=lambda self, value: setattr(self, "_tags",value),
fdel=lambda self: setattr(self, "_tags", None),
@@ -1671,6 +1689,8 @@ class YumBase(depsolve.Depsolve):
if hasattr(cb, 'verify_txmbr'):
vTcb = cb.verify_txmbr
self.verifyTransaction(resultobject, vTcb)
+ if self.conf.group_command == 'objects':
+ self.igroups.save()
return resultobject
def verifyTransaction(self, resultobject=None, txmbr_cb=None):
@@ -1748,6 +1768,10 @@ class YumBase(depsolve.Depsolve):
if md:
po.yumdb_info.from_repo_timestamp = str(md.timestamp)
+ if hasattr(txmbr, 'group_member'):
+ # FIXME:
+ po.yumdb_info.group_member = txmbr.group_member
+
loginuid = misc.getloginuid()
if txmbr.updates or txmbr.downgrades or txmbr.reinstall:
if txmbr.updates:
@@ -1758,6 +1782,8 @@ class YumBase(depsolve.Depsolve):
opo = po
if 'installed_by' in opo.yumdb_info:
po.yumdb_info.installed_by = opo.yumdb_info.installed_by
+ if 'group_member' in opo.yumdb_info:
+ po.yumdb_info.group_member = opo.yumdb_info.group_member
if loginuid is not None:
po.yumdb_info.changed_by = str(loginuid)
elif loginuid is not None:
@@ -3079,6 +3105,58 @@ class YumBase(depsolve.Depsolve):
return matches
+ def _groupInstalledData(self, group):
+ """ Return a dict of
+ pkg_name =>
+ (installed, available,
+ backlisted-installed, blacklisted-available). """
+ ret = {}
+ if not group or self.conf.group_command != 'objects':
+ return ret
+
+ pkg_names = {}
+ if group.groupid in self.igroups.groups:
+ pkg_names = self.igroups.groups[group.groupid].pkg_names
+
+ for pkg_name in set(group.packages + list(pkg_names)):
+ ipkgs = self.rpmdb.searchNames([pkg_name])
+ if pkg_name not in pkg_names and not ipkgs:
+ ret[pkg_name] = 'available'
+ continue
+
+ if not ipkgs:
+ ret[pkg_name] = 'blacklisted-available'
+ continue
+
+ for ipkg in ipkgs:
+ # Multiarch, if any are installed for the group we count "both"
+ if ipkg.yumdb_info.get('group_member', '') != group.groupid:
+ continue
+ ret[pkg_name] = 'installed'
+ break
+ else:
+ ret[pkg_name] = 'blacklisted-installed'
+
+ return ret
+
+ def _groupReturnGroups(self, patterns=None, ignore_case=True):
+ igrps = None
+ if patterns is None:
+ grps = self.comps.groups
+ if self.conf.group_command == 'objects':
+ igrps = self.igroups.groups.values()
+ return igrps, grps
+
+ pats = ",".join(patterns)
+ cs = not ignore_case
+ grps = self.comps.return_groups(pats, case_sensitive=cs)
+ # Because we want name matches too, and we don't store group names
+ # we need to add the groupid's we've found:
+ if self.conf.group_command == 'objects':
+ pats += "," + ",".join([grp.groupid for grp in grps])
+ igrps = self.igroups.return_groups(pats, case_sensitive=cs)
+ return igrps, grps
+
def doGroupLists(self, uservisible=0, patterns=None, ignore_case=True):
"""Return two lists of groups: installed groups and available
groups.
@@ -3097,13 +3175,23 @@ class YumBase(depsolve.Depsolve):
if self.comps.compscount == 0:
raise Errors.GroupsError, _('No group data available for configured repositories')
- if patterns is None:
- grps = self.comps.groups
- else:
- grps = self.comps.return_groups(",".join(patterns),
- case_sensitive=not ignore_case)
+ igrps, grps = self._groupReturnGroups(patterns, ignore_case)
+
+ if igrps is not None:
+ digrps = {}
+ for igrp in igrps:
+ digrps[igrp.gid] = igrp
+ igrps = digrps
+
for grp in grps:
- if grp.installed:
+ if igrps is None:
+ grp_installed = grp.installed
+ else:
+ grp_installed = grp.groupid in igrps
+ if grp_installed:
+ del igrps[grp.groupid]
+
+ if grp_installed:
if uservisible:
if grp.user_visible:
installed.append(grp)
@@ -3116,9 +3204,21 @@ class YumBase(depsolve.Depsolve):
else:
available.append(grp)
+ if igrps is None:
+ return sorted(installed), sorted(available)
+
+ for igrp in igrps.values():
+ # These are installed groups that aren't in comps anymore. so we
+ # create fake comps groups for them.
+ grp = comps.Group()
+ grp.installed = True
+ grp.name = grp.groupid
+ for pkg_name in igrp.pkg_names:
+ grp.mandatory_packages[pkg_name] = 1
+ installed.append(grp)
+
return sorted(installed), sorted(available)
-
def groupRemove(self, grpid):
"""Mark all the packages in the given group to be removed.
@@ -3134,13 +3234,20 @@ class YumBase(depsolve.Depsolve):
raise Errors.GroupsError, _("No Group named %s exists") % to_unicode(grpid)
for thisgroup in thesegroups:
+ igroup_data = self._groupInstalledData(thisgroup)
+
thisgroup.toremove = True
pkgs = thisgroup.packages
for pkg in thisgroup.packages:
+ if pkg in igroup_data and igroup_data[pkg] != 'installed':
+ continue
+
txmbrs = self.remove(name=pkg, silence_warnings=True)
txmbrs_used.extend(txmbrs)
for txmbr in txmbrs:
txmbr.groups.append(thisgroup.groupid)
+ if igroup_data:
+ self.igroups.del_group(thisgroup.groupid)
return txmbrs_used
@@ -3172,7 +3279,8 @@ class YumBase(depsolve.Depsolve):
self.tsInfo.remove(txmbr.po.pkgtup)
- def selectGroup(self, grpid, group_package_types=[], enable_group_conditionals=None):
+ def selectGroup(self, grpid, group_package_types=[],
+ enable_group_conditionals=None, upgrade=False):
"""Mark all the packages in the given group to be installed.
:param grpid: the name of the group containing the packages to
@@ -3212,12 +3320,47 @@ class YumBase(depsolve.Depsolve):
if 'optional' in package_types:
pkgs.extend(thisgroup.optional_packages)
+ igroup_data = self._groupInstalledData(thisgroup)
+ igrp = None
+ if igroup_data:
+ if thisgroup.groupid in self.igroups.groups:
+ igrp = self.igroups.groups[thisgroup.groupid]
+ else:
+ self.igroups.add_group(thisgroup.groupid,thisgroup.packages)
+ pkgs.extend(list(igroup_data.keys()))
+
old_txmbrs = len(txmbrs_used)
for pkg in pkgs:
+ if self.conf.group_command == 'objects':
+ assert pkg in igroup_data
+ if (pkg not in igroup_data or
+ igroup_data[pkg].startswith('blacklisted')):
+ # (upgrade and igroup_data[pkg] == 'available')):
+ msg = _('Skipping package %s from group %s'),
+ self.verbose_logger.log(logginglevels.DEBUG_2,
+ msg, pkg, thisgroup.groupid)
+ continue
+
self.verbose_logger.log(logginglevels.DEBUG_2,
_('Adding package %s from group %s'), pkg, thisgroup.groupid)
+
+ if igrp is not None:
+ igrp.pkg_names.add(pkg)
+ self.igroups.changed = True
+
+ txmbrs = []
try:
- txmbrs = self.install(name=pkg, pkg_warning_level='debug2')
+ if (upgrade and
+ (self.conf.group_command == 'simple' or
+ (igroup_data and igroup_data[pkg] == 'installed'))):
+ txmbrs = self.update(name = pkg)
+ elif igroup_data and igroup_data[pkg] == 'installed':
+ pass # Don't upgrade on install.
+ else:
+ txmbrs = self.install(name = pkg,
+ pkg_warning_level='debug2')
+ for txmbr in txmbrs:
+ txmbr.group_member = thisgroup.groupid
except Errors.InstallError, e:
self.verbose_logger.debug(_('No package named %s available to be installed'),
pkg)
@@ -3231,6 +3374,7 @@ class YumBase(depsolve.Depsolve):
group_conditionals = enable_group_conditionals
count_cond_test = 0
+ # FIXME: What do we do about group conditionals when group==objects
if group_conditionals:
for condreq, cond in thisgroup.conditional_packages.iteritems():
if self.isPackageInstalled(cond):
@@ -3715,20 +3859,24 @@ class YumBase(depsolve.Depsolve):
if next == slow:
return None
- def _at_groupinstall(self, pattern):
- " Do groupinstall via. leading @ on the cmd line, for install/update."
+ def _at_groupinstall(self, pattern, upgrade=False):
+ " Do groupinstall via. leading @ on the cmd line, for install."
assert pattern[0] == '@'
group_string = pattern[1:]
tx_return = []
for group in self.comps.return_groups(group_string):
try:
- txmbrs = self.selectGroup(group.groupid)
+ txmbrs = self.selectGroup(group.groupid, upgrade=upgrade)
tx_return.extend(txmbrs)
except yum.Errors.GroupsError:
self.logger.critical(_('Warning: Group %s does not exist.'), group_string)
continue
return tx_return
-
+
+ def _at_groupupgrade(self, pattern):
+ " Do group upgrade via. leading @ on the cmd line, for update."
+ return self._at_groupinstall(pattern, upgrade=True)
+
def _at_groupremove(self, pattern):
" Do groupremove via. leading @ on the cmd line, for remove."
assert pattern[0] == '@'
@@ -4117,7 +4265,7 @@ class YumBase(depsolve.Depsolve):
be run if it will update the given package to the given
version. For example, if the package foo-1-2 is installed,::
- updatePkgs(["foo-1-2], update_to=False)
+ updatePkgs(["foo-1-2"], update_to=False)
will work identically to::
updatePkgs(["foo"])
@@ -4169,7 +4317,12 @@ class YumBase(depsolve.Depsolve):
if new is None:
continue
tx_return.extend(self.update(po=new))
-
+
+ # Upgrade the installed groups, as part of generic "yum upgrade"
+ if self.conf.group_command == 'objects':
+ for igrp in self.igroups.groups:
+ tx_return.extend(self._at_groupupgrade(igrp))
+
return tx_return
# complications
@@ -4191,7 +4344,7 @@ class YumBase(depsolve.Depsolve):
return self._minus_deselect(kwargs['pattern'])
if kwargs['pattern'] and kwargs['pattern'][0] == '@':
- return self._at_groupinstall(kwargs['pattern'])
+ return self._at_groupupgrade(kwargs['pattern'])
arg = kwargs['pattern']
if not update_to:
diff --git a/yum/config.py b/yum/config.py
index 6c09ee9..06dfeb0 100644
--- a/yum/config.py
+++ b/yum/config.py
@@ -775,6 +775,7 @@ class YumConf(StartupConf):
enable_group_conditionals = BoolOption(True)
groupremove_leaf_only = BoolOption(False)
group_package_types = ListOption(['mandatory', 'default'])
+ group_command = SelectionOption('compat', ('compat', 'objects', 'simple'))
timeout = FloatOption(30.0) # FIXME: Should use variation of SecondsOption
diff --git a/yumcommands.py b/yumcommands.py
index fef5c59..6977850 100644
--- a/yumcommands.py
+++ b/yumcommands.py
@@ -818,25 +818,40 @@ class GroupsCommand(YumCommand):
if cmd in ('install', 'remove',
'mark-install', 'mark-remove',
- 'mark-members', 'info', 'mark-members-sync'):
+ 'info',
+ 'mark-packages', 'mark-packages-force', 'unmark-packages',
+ 'mark-packages-sync', 'mark-packages-sync-force'):
checkGroupArg(base, cmd, extcmds)
if cmd in ('install', 'remove', 'upgrade',
'mark-install', 'mark-remove',
- 'mark-members', 'mark-members-sync'):
+ 'mark-packages', 'mark-packages-force', 'unmark-packages',
+ 'mark-packages-sync', 'mark-packages-sync-force'):
checkRootUID(base)
if cmd in ('install', 'upgrade'):
checkGPGKey(base)
- cmds = ('list', 'info', 'remove', 'install', 'upgrade', 'summary',
- 'mark-install', 'mark-remove',
- 'mark-members', 'mark-members-sync')
+ cmds = set(('list', 'info', 'remove', 'install', 'upgrade', 'summary'))
+ if base.conf.group_command == 'objects':
+ ocmds = ('mark-install', 'mark-remove',
+ 'mark-packages', 'mark-packages-force', 'unmark-packages',
+ 'mark-packages-sync', 'mark-packages-sync-force')
+ cmds.update(ocmds)
+
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(base.igroups.filename):
+ base.logger.critical(_("There is no installed groups file."))
+ 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.
@@ -870,6 +885,60 @@ class GroupsCommand(YumCommand):
if cmd == 'remove':
return base.removeGroups(extcmds)
+ if cmd == 'mark-install':
+ for strng in extcmds:
+ for group in base.comps.return_groups(strng):
+ base.igroups.add_group(group.groupid, group.packages)
+ base.igroups.save()
+ return 0, ['Marked install: ' + ','.join(extcmds)]
+
+ if cmd in ('mark-packages', 'mark-packages-force'):
+ if len(extcmds) < 2:
+ return 1, ['No group or package given']
+ igrps, grps = base._groupReturnGroups([extcmds[0]],
+ ignore_case=False)
+ if igrps is not None and 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'):
+ igrps, grps = base._groupReturnGroups(extcmds,ignore_case=False)
+ 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 == 'mark-remove':
+ for strng in extcmds:
+ for group in base.comps.return_groups(strng):
+ base.igroups.del_group(group.groupid)
+ base.igroups.save()
+ return 0, ['Marked remove: ' + ','.join(extcmds)]
+
+
except yum.Errors.YumBaseError, e:
return 1, [str(e)]
@@ -887,6 +956,8 @@ class GroupsCommand(YumCommand):
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):
--
1.7.6.4
More information about the Yum-devel
mailing list