From 91b078bee2cd7081b83909a2376c8ed01a830295 Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Fri, 22 Feb 2013 13:57:16 -0500 Subject: YUM: Adding functionality to YUM client tool to resolve YUM groups when server-side resolver is off --- src/lib/Bcfg2/Client/Tools/YUM.py | 88 ++++++++++++++++++++--- src/lib/Bcfg2/Server/Plugins/Packages/__init__.py | 5 ++ 2 files changed, 85 insertions(+), 8 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py index 1fe275c2c..b40083e98 100644 --- a/src/lib/Bcfg2/Client/Tools/YUM.py +++ b/src/lib/Bcfg2/Client/Tools/YUM.py @@ -123,7 +123,7 @@ class YUM(Bcfg2.Client.Tools.PkgTool): ('Package', 'rpm'), ('Path', 'ignore')] - __req__ = {'Package': ['name'], + __req__ = {'Package': ['type'], 'Path': ['type']} conflicts = ['YUM24', 'RPM', 'RPMng', 'YUMng'] @@ -287,6 +287,25 @@ class YUM(Bcfg2.Client.Tools.PkgTool): return self.yumbase.rpmdb.returnGPGPubkeyPackages() return self.yumbase.rpmdb.searchNevra(name='gpg-pubkey') + def missing_attrs(self, entry): + """ Implementing from superclass to check for existence of either + name or group attribute for Package entry in the case of a YUM + group. """ + required = self.__req__[entry.tag] + if isinstance(required, dict): + required = ["type"] + try: + required.extend(self.__req__[entry.tag][entry.get("type")]) + except KeyError: + pass + + missing = [attr for attr in required + if attr not in entry.attrib or not entry.attrib[attr]] + + if entry.get('name', None) == None and entry.get('group', None) == None: + missing += ['name', 'group'] + return missing + def _verifyHelper(self, pkg_obj): """ _verifyHelper primarly deals with a yum bug where the pkg_obj.verify() method does not properly take into count multilib @@ -409,8 +428,12 @@ class YUM(Bcfg2.Client.Tools.PkgTool): if entry.get('version', False) == 'auto': self._fixAutoVersion(entry) - self.logger.debug("Verifying package instances for %s" % - entry.get('name')) + if entry.get('group'): + self.logger.debug("Verifying packages for group %s" % + entry.get('group')) + else: + self.logger.debug("Verifying package instances for %s" % + entry.get('name')) self.verify_cache = dict() # Used for checking multilib packages self.modlists[entry] = modlist @@ -423,14 +446,52 @@ class YUM(Bcfg2.Client.Tools.PkgTool): entry.get('pkg_checks', 'true').lower() == 'true' pkg_verify = self.pkg_verify and \ entry.get('pkg_verify', 'true').lower() == 'true' + yum_group = False if entry.get('name') == 'gpg-pubkey': all_pkg_objs = self._getGPGKeysAsPackages() pkg_verify = False # No files here to verify + elif entry.get('group'): + entry.set('name', 'group:%s' % entry.get('group')) + yum_group = True + all_pkg_objs = [] + instances = [] + if self.yumbase.comps.has_group(entry.get('group')): + group = self.yumbase.comps.return_group(entry.get('group')) + group_type = getattr(group, '%s_packages' % + entry.get('choose', 'default')) + group_packages = [p + for p, d in group.mandatory_packages.items() + if d] + \ + [p + for p, d in group_type.items() + if d] + if len(group_packages) == 0: + self.logger.error("No packages found for group %s" % + entry.get("group")) + for pkg in group_packages: + # create package instances for each package in yum group + instance = Bcfg2.Client.XML.SubElement(entry, 'Package') + instance.attrib['name'] = pkg + instance.attrib['type'] = 'yum' + try: + newest = \ + self.yumbase.pkgSack.returnNewestByName(pkg)[0] + instance.attrib['version'] = newest['version'] + instance.attrib['epoch'] = newest['epoch'] + instance.attrib['release'] = newest['release'] + except: + self.logger.info("Error finding newest package " + "for %s" % + pkg) + instance.attrib['version'] = 'any' + instances.append(instance) + else: + self.logger.error("Group not found: %s" % entry.get("group")) else: all_pkg_objs = \ self.yumbase.rpmdb.searchNevra(name=entry.get('name')) - if len(all_pkg_objs) == 0: + if len(all_pkg_objs) == 0 and yum_group != True: # Some sort of virtual capability? Try to resolve it all_pkg_objs = self.yumbase.rpmdb.searchProvides(entry.get('name')) if len(all_pkg_objs) > 0: @@ -441,12 +502,18 @@ class YUM(Bcfg2.Client.Tools.PkgTool): self.logger.info(" %s" % pkg) for inst in instances: - nevra = build_yname(entry.get('name'), inst) + if yum_group: + # the entry is not the name of the package + nevra = build_yname(inst.get('name'), inst) + all_pkg_objs = \ + self.yumbase.rpmdb.searchNevra(name=inst.get('name')) + else: + nevra = build_yname(entry.get('name'), inst) if nevra in pkg_cache: continue # Ignore duplicate instances else: pkg_cache.append(nevra) - + self.logger.debug("Verifying: %s" % nevra2string(nevra)) # Set some defaults here @@ -455,7 +522,10 @@ class YUM(Bcfg2.Client.Tools.PkgTool): stat['version_fail'] = False stat['verify'] = {} stat['verify_fail'] = False - stat['pkg'] = entry + if yum_group: + stat['pkg'] = inst + else: + stat['pkg'] = entry stat['modlist'] = modlist if inst.get('verify_flags'): # this splits on either space or comma @@ -624,7 +694,9 @@ class YUM(Bcfg2.Client.Tools.PkgTool): else: install_only = False - if virt_pkg or (install_only and not self.setup['kevlar']): + if virt_pkg or \ + (install_only and not self.setup['kevlar']) or \ + yum_group: # virtual capability supplied, we are probably dealing # with multiple packages of different names. This check # doesn't make a lot of since in this case. diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py index 94548f4d8..c3eadc6bb 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py @@ -310,6 +310,11 @@ class Packages(Bcfg2.Server.Plugin.Plugin, """ if self.disableResolver: # Config requests no resolver + for struct in structures: + for pkg in struct.xpath('//Package | //BoundPackage'): + if pkg.get("group"): + if pkg.get("type"): + pkg.set("choose", pkg.get("type")) return if collection is None: -- cgit v1.2.3-1-g7c22 From 5786642b8e8df6bdf49626c6eab62ee8c0317c01 Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Fri, 22 Feb 2013 18:25:16 -0500 Subject: Packages: Adding Yum groups to Bcfg2's internal dependency resolver --- src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 85 +++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 13 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index 46231c636..965b28695 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -313,9 +313,7 @@ class YumCollection(Collection): @property def __package_groups__(self): - """ YumCollections support package groups only if - :attr:`use_yum` is True """ - return self.use_yum + return True @property def helper(self): @@ -665,11 +663,6 @@ class YumCollection(Collection): In this implementation the packages may be strings or tuples. See :ref:`yum-pkg-objects` for more information. """ - if not self.use_yum: - self.logger.warning("Packages: Package groups are not supported " - "by Bcfg2's internal Yum dependency generator") - return dict() - if not grouplist: return dict() @@ -680,8 +673,14 @@ class YumCollection(Collection): if not ptype: ptype = "default" gdicts.append(dict(group=group, type=ptype)) - - return self.call_helper("get_groups", inputdata=gdicts) + + if self.use_yum: + return self.call_helper("get_groups", inputdata=gdicts) + else: + pkgs = dict() + for gdict in gdicts: + pkgs[gdict['group']] = Collection.get_group(self, gdict['group'], gdict['type']) + return pkgs def _element_to_pkg(self, el, name): """ Convert a Package or Instance element to a package tuple """ @@ -991,6 +990,7 @@ class YumSource(Source): for x in ['global'] + self.arches]) self.needed_paths = set() self.file_to_arch = dict() + self.yumgroups = dict() __init__.__doc__ = Source.__init__.__doc__ @property @@ -1008,7 +1008,7 @@ class YumSource(Source): if not self.use_yum: cache = open(self.cachefile, 'wb') cPickle.dump((self.packages, self.deps, self.provides, - self.filemap, self.url_map), cache, 2) + self.filemap, self.url_map, self.yumgroups), cache, 2) cache.close() def load_state(self): @@ -1018,7 +1018,7 @@ class YumSource(Source): if not self.use_yum: data = open(self.cachefile) (self.packages, self.deps, self.provides, - self.filemap, self.url_map) = cPickle.load(data) + self.filemap, self.url_map, self.yumgroups) = cPickle.load(data) @property def urls(self): @@ -1073,7 +1073,7 @@ class YumSource(Source): urls = [] for elt in xdata.findall(RPO + 'data'): - if elt.get('type') in ['filelists', 'primary']: + if elt.get('type') in ['filelists', 'primary', 'group']: floc = elt.find(RPO + 'location') fullurl = url + floc.get('href') urls.append(fullurl) @@ -1090,11 +1090,14 @@ class YumSource(Source): # we have to read primary.xml first, and filelists.xml afterwards; primaries = list() filelists = list() + groups = list() for fname in self.files: if fname.endswith('primary.xml.gz'): primaries.append(fname) elif fname.endswith('filelists.xml.gz'): filelists.append(fname) + elif fname.find('comps'): + groups.append(fname) for fname in primaries: farch = self.file_to_arch[fname] @@ -1104,6 +1107,9 @@ class YumSource(Source): farch = self.file_to_arch[fname] fdata = lxml.etree.parse(fname).getroot() self.parse_filelist(fdata, farch) + for fname in groups: + fdata = lxml.etree.parse(fname).getroot() + self.parse_group(fdata) # merge data sdata = list(self.packages.values()) @@ -1167,6 +1173,35 @@ class YumSource(Source): self.provides[arch][prov] = list() self.provides[arch][prov].append(pkgname) + @Bcfg2.Server.Plugin.track_statistics() + def parse_group(self, data): + """ parse comps.xml.gz data """ + for group in data.getchildren(): + if not group.tag.endswith('group'): + continue + try: + groupid = group.xpath('id')[0].text + self.yumgroups[groupid] = {'mandatory': list(), + 'default': list(), + 'optional': list(), + 'conditional': list()} + except IndexError: + continue + try: + packagelist = group.xpath('packagelist')[0] + except IndexError: + continue + for pkgreq in packagelist.getchildren(): + pkgtype = pkgreq.get('type', None) + if pkgtype == 'mandatory': + self.yumgroups[groupid]['mandatory'].append(pkgreq.text) + elif pkgtype == 'default': + self.yumgroups[groupid]['default'].append(pkgreq.text) + elif pkgtype == 'optional': + self.yumgroups[groupid]['optional'].append(pkgreq.text) + elif pkgtype == 'conditional': + self.yumgroups[groupid]['conditional'].append(pkgreq.text) + def is_package(self, metadata, package): arch = [a for a in self.arches if a in metadata.groups] if not arch: @@ -1246,3 +1281,27 @@ class YumSource(Source): return self.pulp_id else: return Source.get_repo_name(self, url_map) + + def get_group(self, metadata, group, ptype=None): # pylint: disable=W0613 + """ Get the list of packages of the given type in a package + group. + + :param group: The name of the group to query + :type group: string + :param ptype: The type of packages to get, for backends that + support multiple package types in package groups + (e.g., "recommended," "optional," etc.) + :type ptype: string + :returns: list of strings - package names + """ + try: + yumgroup = self.yumgroups[group] + except KeyError: + return [] + packages = yumgroup['conditional'] + yumgroup['mandatory'] + if ptype in ['default', 'optional', 'all']: + packages += yumgroup['default'] + if ptype in ['optional', 'all']: + packages += yumgroup['optional'] + return packages + -- cgit v1.2.3-1-g7c22 From 77209f7be86e88604a27b782fd1eae84ef810336 Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Sun, 24 Feb 2013 19:33:55 -0500 Subject: YUM: Fixing long lines --- src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index 965b28695..4cd938651 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -679,7 +679,9 @@ class YumCollection(Collection): else: pkgs = dict() for gdict in gdicts: - pkgs[gdict['group']] = Collection.get_group(self, gdict['group'], gdict['type']) + pkgs[gdict['group']] = Collection.get_group(self, + gdict['group'], + gdict['type']) return pkgs def _element_to_pkg(self, el, name): @@ -1008,7 +1010,8 @@ class YumSource(Source): if not self.use_yum: cache = open(self.cachefile, 'wb') cPickle.dump((self.packages, self.deps, self.provides, - self.filemap, self.url_map, self.yumgroups), cache, 2) + self.filemap, self.url_map, + self.yumgroups), cache, 2) cache.close() def load_state(self): -- cgit v1.2.3-1-g7c22 From c0e43f73cb4e98145537543b6275c4cc6dbd5430 Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Mon, 25 Feb 2013 08:34:52 -0500 Subject: YUM: Cleaner implementation of missing_attrs() from superclass --- src/lib/Bcfg2/Client/Tools/YUM.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py index b40083e98..92568c070 100644 --- a/src/lib/Bcfg2/Client/Tools/YUM.py +++ b/src/lib/Bcfg2/Client/Tools/YUM.py @@ -291,18 +291,10 @@ class YUM(Bcfg2.Client.Tools.PkgTool): """ Implementing from superclass to check for existence of either name or group attribute for Package entry in the case of a YUM group. """ - required = self.__req__[entry.tag] - if isinstance(required, dict): - required = ["type"] - try: - required.extend(self.__req__[entry.tag][entry.get("type")]) - except KeyError: - pass - - missing = [attr for attr in required - if attr not in entry.attrib or not entry.attrib[attr]] - - if entry.get('name', None) == None and entry.get('group', None) == None: + missing = Bcfg2.Client.Tools.PkgTool.missing_attrs(self, entry) + + if entry.get('name', None) == None and + entry.get('group', None) == None: missing += ['name', 'group'] return missing -- cgit v1.2.3-1-g7c22 From 59faa6b5d570d26c00b5c058cb9e866729fd66ee Mon Sep 17 00:00:00 2001 From: Jason Kincl Date: Wed, 27 Feb 2013 13:20:34 -0500 Subject: YUM: Fixed YUM Group package selection with group types (default, optional, all) on client tool --- src/lib/Bcfg2/Client/Tools/YUM.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py index 92568c070..7764a6808 100644 --- a/src/lib/Bcfg2/Client/Tools/YUM.py +++ b/src/lib/Bcfg2/Client/Tools/YUM.py @@ -293,7 +293,7 @@ class YUM(Bcfg2.Client.Tools.PkgTool): group. """ missing = Bcfg2.Client.Tools.PkgTool.missing_attrs(self, entry) - if entry.get('name', None) == None and + if entry.get('name', None) == None and \ entry.get('group', None) == None: missing += ['name', 'group'] return missing @@ -450,14 +450,20 @@ class YUM(Bcfg2.Client.Tools.PkgTool): instances = [] if self.yumbase.comps.has_group(entry.get('group')): group = self.yumbase.comps.return_group(entry.get('group')) - group_type = getattr(group, '%s_packages' % - entry.get('choose', 'default')) group_packages = [p for p, d in group.mandatory_packages.items() - if d] + \ - [p - for p, d in group_type.items() if d] + group_type = entry.get('choose', 'default') + if group_type in ['default', 'optional', 'all']: + group_packages += [p + for p, d in + group.default_packages.items() + if d] + if group_type in ['optional', 'all']: + group_packages += [p + for p, d in + group.optional_packages.items() + if d] if len(group_packages) == 0: self.logger.error("No packages found for group %s" % entry.get("group")) -- cgit v1.2.3-1-g7c22