From e0bc4708a3f7cc7675995177c85eb59fdb5dd677 Mon Sep 17 00:00:00 2001 From: Dmitri Iouchtchenko Date: Sun, 11 Sep 2016 00:42:46 -0400 Subject: Packages/Pac: Parse dependencies --- src/lib/Bcfg2/Server/Plugins/Packages/Pac.py | 103 +++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py index 0e15d2e15..2080fb58c 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py @@ -1,10 +1,61 @@ """ Pacman backend for :mod:`Bcfg2.Server.Plugins.Packages` """ +import os import tarfile from Bcfg2.Server.Plugins.Packages.Collection import Collection from Bcfg2.Server.Plugins.Packages.Source import Source +def parse_db_file(pkgfile): + """ Parse a Pacman database file, returning a dictionary with + section headings for keys and lists of strings for values. + (Reference: ``sync_db_read`` in ``lib/libalpm/be_sync.c``) + """ + + pkg = {} + section = None + + for line in pkgfile: + line = line.strip() + + if section is not None: + if not line: + section = None + else: + pkg[section].append(line) + elif len(line) >= 2 and line[0] == line[-1] == '%': + section = line + pkg[section] = [] + + return pkg + + +def parse_dep(dep): + """ Parse a Pacman dependency string, returning the package name, + version restriction (or ``None``), and description (or ``None``). + (Reference: ``alpm_dep_from_string`` in ``lib/libalpm/deps.c``) + """ + + rest_desc = dep.split(': ', 1) + if len(rest_desc) == 1: + rest, desc = rest_desc[0], None + else: + rest, desc = rest_desc + + # Search for '=' last, since '<=' and '>=' are possible. + for symb in ['<', '>', '=']: + idx = rest.find(symb) + if idx >= 0: + name = rest[:idx] + version = rest[idx:] + break + else: + name = rest + version = None + + return name, version, desc + + class PacCollection(Collection): """ Handle collections of Pacman sources. This is a no-op object that simply inherits from @@ -46,12 +97,9 @@ class PacSource(Source): raise Exception("PacSource : RAWUrl not supported (yet)") def read_files(self): - bdeps = dict() - bprov = dict() - - depfnames = ['Depends', 'Pre-Depends'] - if self.recommended: - depfnames.append('Recommends') + bdeps = {} + brecs = {} + bprov = {} for fname in self.files: if not self.rawurl: @@ -62,8 +110,9 @@ class PacSource(Source): barch = self.arches[0] if barch not in bdeps: - bdeps[barch] = dict() - bprov[barch] = dict() + bdeps[barch] = {} + brecs[barch] = {} + bprov[barch] = {} try: self.debug_log("Packages: try to read %s" % fname) tar = tarfile.open(fname, "r") @@ -71,11 +120,39 @@ class PacSource(Source): self.logger.error("Packages: Failed to read file %s" % fname) raise + packages = {} for tarinfo in tar: - if tarinfo.isdir(): - self.pkgnames.add(tarinfo.name.rsplit("-", 2)[0]) - self.debug_log("Packages: added %s" % - tarinfo.name.rsplit("-", 2)[0]) + if not tarinfo.isfile(): + continue + prefix = os.path.dirname(tarinfo.name) + if prefix not in packages: + packages[prefix] = {} + pkg = parse_db_file(tar.extractfile(tarinfo)) + packages[prefix].update(pkg) + + for pkg in packages.values(): + pkgname = pkg['%NAME%'][0] + self.pkgnames.add(pkgname) + bdeps[barch][pkgname] = [] + brecs[barch][pkgname] = [] + + if '%DEPENDS%' in pkg: + for dep in pkg['%DEPENDS%']: + dname = parse_dep(dep)[0] + bdeps[barch][pkgname].append(dname) + + if '%OPTDEPENDS%' in pkg: + for dep in pkg['%OPTDEPENDS%']: + dname = parse_dep(dep)[0] + brecs[barch][pkgname].append(dname) + + if '%PROVIDES%' in pkg: + for dep in pkg['%PROVIDES%']: + dname = parse_dep(dep)[0] + if dname not in bprov[barch]: + bprov[barch][dname] = set() + bprov[barch][dname].add(pkgname) + tar.close() - self.process_files(bdeps, bprov) + self.process_files(bdeps, bprov, brecs) read_files.__doc__ = Source.read_files.__doc__ -- cgit v1.2.3-1-g7c22 From e895340e3b1b2355ecc936842f922c6d21791920 Mon Sep 17 00:00:00 2001 From: Dmitri Iouchtchenko Date: Sun, 11 Sep 2016 12:17:52 -0400 Subject: Packages/Pac: Handle groups --- doc/server/plugins/generators/packages.txt | 90 ++++++++++++++++------------ schemas/pkgtype.xsd | 7 +-- src/lib/Bcfg2/Server/Plugins/Packages/Pac.py | 39 +++++++++++- 3 files changed, 94 insertions(+), 42 deletions(-) diff --git a/doc/server/plugins/generators/packages.txt b/doc/server/plugins/generators/packages.txt index eea6c6659..5e14d3be5 100644 --- a/doc/server/plugins/generators/packages.txt +++ b/doc/server/plugins/generators/packages.txt @@ -483,6 +483,59 @@ See :ref:`configuration` for more details on these options. .. _native-yum-libraries: +Package Groups +============== + +Some packaging systems provide package groups. To include a package +group, use the :xml:attribute:`PackageStructure:group` attribute of +the :xml:element:`Package` tag. + +pac +--- + +.. versionadded:: 1.4.0 + +Pacman `groups `_ are supported: + +.. code-block:: xml + + + +yum +--- + +Yum package groups are supported by both the native Yum libraries and +Bcfg2's internal dependency resolver. You can use either the short +group ID or the long group name: + +.. code-block:: xml + + + + +By default, only those packages considered the "default" packages in a +group will be installed. You can change this behavior using the +:xml:attribute:`PackageStructure:type` attribute: + +.. code-block:: xml + + + + +Valid values of "type" are: + +* ``mandatory``: Only install mandatory packages in the group. +* ``default``: Install default packages from the group (the default). +* ``optional`` or ``all``: Install all packages in the group, + including mandatory, default, and optional packages. + +See :xml:type:`PackageStructure` for details. + +You can view the packages in a group by category with the ``yum +groupinfo`` command. More information about the different levels can +be found at +http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation + Using Native Yum Libraries ========================== @@ -546,43 +599,6 @@ generally be overridden: * ``reposdir`` is set to ``/dev/null`` to prevent the server's Yum configuration from being read; do not change this. -Package Groups --------------- - -Yum package groups are supported by both the native Yum libraries and -Bcfg2's internal dependency resolver. To include a package group, use -the :xml:attribute:`PackageStructure:group` attribute of the -:xml:element:`Package` tag. You can use either the short group ID or -the long group name: - -.. code-block:: xml - - - - -By default, only those packages considered the "default" packages in a -group will be installed. You can change this behavior using the -:xml:attribute:`PackageStructure:type` attribute: - -.. code-block:: xml - - - - -Valid values of "type" are: - -* ``mandatory``: Only install mandatory packages in the group. -* ``default``: Install default packages from the group (the default). -* ``optional`` or ``all``: Install all packages in the group, - including mandatory, default, and optional packages. - -See :xml:type:`PackageStructure` for details. - -You can view the packages in a group by category with the ``yum -groupinfo`` command. More information about the different levels can -be found at -http://fedoraproject.org/wiki/How_to_use_and_edit_comps.xml_for_package_groups#Installation - Abstract Package Tags --------------------- diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd index 7ad7606b2..993114a46 100644 --- a/schemas/pkgtype.xsd +++ b/schemas/pkgtype.xsd @@ -31,10 +31,9 @@ Install the named package group. Package groups are only - supported for Yum :xml:element:`Source` repositories, and - only if the :ref:`yum libraries - <native-yum-libraries>` are in use. Either ``group`` - or :xml:attribute:`PackageStructure:name` must be specified. + supported for Pac and Yum :xml:element:`Source` + repositories. Either ``group`` or + :xml:attribute:`PackageStructure:name` must be specified. diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py index 2080fb58c..2661adf67 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py @@ -2,6 +2,7 @@ import os import tarfile +from Bcfg2.Compat import cPickle from Bcfg2.Server.Plugins.Packages.Collection import Collection from Bcfg2.Server.Plugins.Packages.Source import Source @@ -75,6 +76,10 @@ class PacCollection(Collection): debug=debug) __init__.__doc__ = Collection.__init__.__doc__.split(".. -----")[0] + @property + def __package_groups__(self): + return True + class PacSource(Source): """ Handle Pacman sources """ @@ -82,6 +87,25 @@ class PacSource(Source): #: PacSource sets the ``type`` on Package entries to "pacman" ptype = 'pacman' + def __init__(self, basepath, xsource): + self.pacgroups = {} + + Source.__init__(self, basepath, xsource) + __init__.__doc__ = Source.__init__.__doc__ + + def load_state(self): + data = open(self.cachefile, 'rb') + (self.pkgnames, self.deps, self.provides, + self.recommends, self.pacgroups) = cPickle.load(data) + load_state.__doc__ = Source.load_state.__doc__ + + def save_state(self): + cache = open(self.cachefile, 'wb') + cPickle.dump((self.pkgnames, self.deps, self.provides, + self.recommends, self.pacgroups), cache, 2) + cache.close() + save_state.__doc__ = Source.save_state.__doc__ + @property def urls(self): """ A list of URLs to the base metadata file for each @@ -96,7 +120,7 @@ class PacSource(Source): else: raise Exception("PacSource : RAWUrl not supported (yet)") - def read_files(self): + def read_files(self): # pylint: disable=R0912 bdeps = {} brecs = {} bprov = {} @@ -153,6 +177,19 @@ class PacSource(Source): bprov[barch][dname] = set() bprov[barch][dname].add(pkgname) + if '%GROUPS%' in pkg: + for group in pkg['%GROUPS%']: + if group not in self.pacgroups: + self.pacgroups[group] = [] + self.pacgroups[group].append(pkgname) + tar.close() self.process_files(bdeps, bprov, brecs) read_files.__doc__ = Source.read_files.__doc__ + + def get_group(self, metadata, group, ptype=None): + try: + return self.pacgroups[group] + except KeyError: + return [] + get_group.__doc__ = Source.get_group.__doc__ -- cgit v1.2.3-1-g7c22 From 995c35063229fbb371d5987dfbd5052f0b4abfe8 Mon Sep 17 00:00:00 2001 From: Dmitri Iouchtchenko Date: Sun, 11 Sep 2016 12:20:05 -0400 Subject: Client/Tools/Pacman: Decrease logging levels --- src/lib/Bcfg2/Client/Tools/Pacman.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/Pacman.py b/src/lib/Bcfg2/Client/Tools/Pacman.py index ee4ef35af..fba946bfb 100644 --- a/src/lib/Bcfg2/Client/Tools/Pacman.py +++ b/src/lib/Bcfg2/Client/Tools/Pacman.py @@ -24,8 +24,8 @@ class Pacman(Bcfg2.Client.Tools.PkgTool): def VerifyPackage(self, entry, _): '''Verify Package status for entry''' - self.logger.info("VerifyPackage: %s : %s" % (entry.get('name'), - entry.get('version'))) + self.logger.debug("VerifyPackage: %s : %s" % (entry.get('name'), + entry.get('version'))) if 'version' not in entry.attrib: self.logger.info("Cannot verify unversioned package %s" % @@ -42,11 +42,10 @@ class Pacman(Bcfg2.Client.Tools.PkgTool): return True else: entry.set('current_version', self.installed[entry.get('name')]) - self.logger.info("attribname: %s" % (entry.attrib['name'])) - self.logger.info("attribname: %s" % (entry.attrib['name'])) + self.logger.debug("attribname: %s" % (entry.attrib['name'])) return False entry.set('current_exists', 'false') - self.logger.info("attribname: %s" % (entry.attrib['name'])) + self.logger.debug("attribname: %s" % (entry.attrib['name'])) return False def Remove(self, packages): -- cgit v1.2.3-1-g7c22