summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/server/plugins/generators/packages.txt90
-rw-r--r--schemas/pkgtype.xsd7
-rw-r--r--src/lib/Bcfg2/Client/Tools/Pacman.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Pac.py142
4 files changed, 188 insertions, 60 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 <https://www.archlinux.org/groups/>`_ are supported:
+
+.. code-block:: xml
+
+ <Package group="base"/>
+
+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
+
+ <Package group="SNMP Support"/>
+ <Package group="system-management-snmp"/>
+
+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
+
+ <Package group="development" type="optional"/>
+ <Package group="Administration Tools" type="mandatory"/>
+
+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
-
- <Package group="SNMP Support"/>
- <Package group="system-management-snmp"/>
-
-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
-
- <Package group="development" type="optional"/>
- <Package group="Administration Tools" type="mandatory"/>
-
-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 @@
<xsd:annotation>
<xsd:documentation>
Install the named package group. Package groups are only
- supported for Yum :xml:element:`Source` repositories, and
- only if the :ref:`yum libraries
- &lt;native-yum-libraries&gt;` 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.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
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):
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
index 0e15d2e15..2661adf67 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Pac.py
@@ -1,10 +1,62 @@
""" Pacman backend for :mod:`Bcfg2.Server.Plugins.Packages` """
+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
+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
@@ -24,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 """
@@ -31,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
@@ -45,13 +120,10 @@ class PacSource(Source):
else:
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')
+ def read_files(self): # pylint: disable=R0912
+ bdeps = {}
+ brecs = {}
+ bprov = {}
for fname in self.files:
if not self.rawurl:
@@ -62,8 +134,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 +144,52 @@ 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)
+
+ 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)
+ 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__