From 65c527340628bd24c5b478d15f22d811ddb0d437 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 10 Oct 2012 16:36:29 -0400 Subject: Packages: updated pulp repo handling to latest Pulp v1 API --- .../Bcfg2/Server/Plugins/Packages/Collection.py | 16 ++- src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 146 +++++++++++++++++++-- src/lib/Bcfg2/Server/Plugins/Packages/__init__.py | 9 +- 3 files changed, 156 insertions(+), 15 deletions(-) (limited to 'src/lib/Bcfg2/Server/Plugins/Packages') diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py index a0646d101..ec9ba550b 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py @@ -93,7 +93,8 @@ class Collection(list, Bcfg2.Server.Plugin.Debuggable): #: Whether or not this Packages backend supports package groups __package_groups__ = False - def __init__(self, metadata, sources, basepath, debug=False): + def __init__(self, metadata, sources, cachepath, basepath, fam, + debug=False): """ :param metadata: The client metadata for this collection :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata @@ -103,9 +104,16 @@ class Collection(list, Bcfg2.Server.Plugin.Debuggable): :type sources: list of :class:`Bcfg2.Server.Plugins.Packages.Source.Source` objects - :param basepath: The base filesystem path where cache and - other temporary data will be stored + :param cachepath: The filesystem path where cache and other temporary + data will be stored + :type cachepath: string + :param basepath: The filesystem path to the Packages plugin + directory, where more permanent data can be + stored :type basepath: string + :param fam: A file monitor object to use if this Collection + needs to monitor for file activity + :type fam: Bcfg2.Server.FileMonitor.FileMonitor :param debug: Enable debugging output :type debug: bool @@ -116,7 +124,9 @@ class Collection(list, Bcfg2.Server.Plugin.Debuggable): self.debug_flag = debug self.metadata = metadata self.basepath = basepath + self.cachepath = cachepath self.virt_pkgs = dict() + self.fam = fam try: self.setup = sources[0].setup diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index c01f8359b..17decc68e 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -54,6 +54,7 @@ import os import re import sys import copy +import errno import socket import logging import lxml.etree @@ -144,6 +145,111 @@ def _setup_pulp(setup): return PULPSERVER +class PulpCertificateData(Bcfg2.Server.Plugin.SpecificData): + """ Handle pulp consumer certificate data for + :class:`PulpCertificateSet` """ + + def bind_entry(self, entry, _): + """ Given an abstract entry, add data to it and return it. + :class:`PulpCertificateSet` handles binding entry metadata. + + :param entry: The abstract entry to bind data to + :type entry: lxml.etree._Element + :returns: lxml.etree._Element - the bound entry + """ + entry.set("type", "file") + if self.data: + entry.text = self.data + else: + entry.set("empty", "true") + return entry + + +class PulpCertificateSet(Bcfg2.Server.Plugin.EntrySet): + """ Handle Pulp consumer certificates. """ + + #: The path to certificates on consumer machines + certpath = "/etc/pki/consumer/cert.pem" + + def __init__(self, path, fam): + """ + :param path: The path to the directory where Pulp consumer + certificates will be stored + :type path: string + """ + Bcfg2.Server.Plugin.EntrySet.__init__(self, + os.path.basename(self.certpath), + path, + PulpCertificateData, + "UTF-8") + self.metadata = dict(owner='root', + group='root', + perms='0644', + secontext='__default__', + important='true', + sensitive='true', + paranoid=self.metadata['paranoid']) + self.fam = fam + self.fam.AddMonitor(path, self) + + def HandleEvent(self, event): + """ Handle FAM events on certificate files. + + :param event: The event to handle + :type event: Bcfg2.Server.FileMonitor.Event """ + if event.filename != self.path: + return self.handle_event(event) + + def write_data(self, data, metadata): + """ Write a new certificate to the filesystem. + + :param data: The new certificate data + :type data: string + :param metadata: Metadata for the client to write the + certificate for + :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata + """ + specific = "%s.H_%s" % (os.path.basename(self.certpath), + metadata.hostname) + fileloc = os.path.join(self.path, specific) + + self.logger.info("Packages: Writing certificate data for %s to %s" % + (metadata.hostname, fileloc)) + try: + open(fileloc, 'wb').write(data) + except IOError: + err = sys.exc_info()[1] + self.logger.error("Could not write %s: %s" % (fileloc, err)) + return + self.verify_file(specific) + + def verify_file(self, filename): + """ Service the FAM events queued up by the key generation so + the data structure entries will be available for binding. + + NOTE: We wait for up to ten seconds. There is some potential + for race condition, because if the file monitor doesn't get + notified about the new key files in time, those entries won't + be available for binding. In practice, this seems "good + enough." + + :param filename: The filename to check for events on + :type filename: string + """ + tries = 0 + updated = False + while not updated: + if tries >= 10: + self.logger.error("%s still not registered" % filename) + return + self.fam.handle_events_in_interval(1) + if filename in self.entries: + break + else: + tries += 1 + continue + + class YumCollection(Collection): """ Handle collections of Yum sources. If we're using the yum Python libraries, then this becomes a very full-featured @@ -159,14 +265,19 @@ class YumCollection(Collection): #: yum.conf we write out option_blacklist = ["use_yum_libraries", "helper"] - def __init__(self, metadata, sources, basepath, debug=False): - Collection.__init__(self, metadata, sources, basepath, debug=debug) - self.keypath = os.path.join(self.basepath, "keys") + #: :class:`PulpCertificateSet` object used to handle Pulp certs + pulp_cert_set = None + + def __init__(self, metadata, sources, cachepath, basepath, fam, + debug=False): + Collection.__init__(self, metadata, sources, cachepath, basepath, fam, + debug=debug) + self.keypath = os.path.join(self.cachepath, "keys") if self.use_yum: #: Define a unique cache file for this collection to use #: for cached yum metadata - self.cachefile = os.path.join(self.basepath, + self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) @@ -180,6 +291,22 @@ class YumCollection(Collection): if HAS_PULP and self.has_pulp_sources: _setup_pulp(self.setup) + if self.pulp_cert_set is None: + certdir = os.path.join( + self.basepath, + "pulp", + os.path.basename(PulpCertificateSet.certpath)) + try: + os.makedirs(certdir) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EEXIST: + pass + else: + self.logger.error("Could not create Pulp consumer " + "cert directory at %s: %s" % + (certdir, err)) + self.pulp_cert_set = PulpCertificateSet(certdir, self.fam) self._helper = None @@ -409,11 +536,14 @@ class YumCollection(Collection): consumer = self._get_pulp_consumer(consumerapi=consumerapi) if consumer is None: consumer = consumerapi.create(self.metadata.hostname, - self.metadata.hostname) + self.metadata.hostname, + capabilities=dict(bind=False)) lxml.etree.SubElement(independent, "BoundAction", name="pulp-update", timing="pre", when="always", status="check", command="pulp-consumer consumer update") + self.pulp_cert_set.write_data(consumer['certificate'], + self.metadata) for source in self: # each pulp source can only have one arch, so we don't @@ -423,10 +553,8 @@ class YumCollection(Collection): consumerapi.bind(self.metadata.hostname, source.pulp_id) crt = lxml.etree.SubElement(independent, "BoundPath", - name="/etc/pki/consumer/cert.pem", - type="file", owner="root", - group="root", perms="0644") - crt.text = consumerapi.certificate(self.metadata.hostname) + name=self.pulp_cert_set.certpath) + self.pulp_cert_set.bind_entry(crt, self.metadata) @Bcfg2.Server.Plugin.track_statistics() def _get_pulp_consumer(self, consumerapi=None): diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py index 224866d9c..1c33affb3 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py @@ -116,6 +116,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, def toggle_debug(self): rv = Bcfg2.Server.Plugin.Plugin.toggle_debug(self) self.sources.toggle_debug() + for collection in self.collections.values(): + collection.toggle_debug() return rv toggle_debug.__doc__ = Bcfg2.Server.Plugin.Plugin.toggle_debug.__doc__ @@ -463,7 +465,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, if not self.sources.loaded: # if sources.xml has not received a FAM event yet, defer; # instantiate a dummy Collection object - return Collection(metadata, [], self.cachepath) + return Collection(metadata, [], self.cachepath, self.data, + self.core.fam) if metadata.hostname in self.clients: return self.collections[self.clients[metadata.hostname]] @@ -493,8 +496,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, self.logger.error("Packages: Using %s for Collection of sources " "for %s" % (cclass.__name__, metadata.hostname)) - collection = cclass(metadata, relevant, self.cachepath, - debug=self.debug_flag) + collection = cclass(metadata, relevant, self.cachepath, self.data, + self.core.fam, debug=self.debug_flag) ckey = collection.cachekey self.clients[metadata.hostname] = ckey self.collections[ckey] = collection -- cgit v1.2.3-1-g7c22