diff options
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugins/Packages')
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Packages/Apt.py | 17 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py | 14 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Packages/Source.py | 14 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Packages/Yum.py | 75 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Packages/__init__.py | 39 |
5 files changed, 101 insertions, 58 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py index 27f493677..a82a183d8 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Apt.py @@ -30,8 +30,8 @@ class AptCollection(Collection): """ Get an APT configuration file (i.e., ``sources.list``). :returns: string """ - lines = ["# This config was generated automatically by the Bcfg2 " \ - "Packages plugin", ''] + lines = ["# This config was generated automatically by the Bcfg2 " + "Packages plugin", ''] for source in self: if source.rawurl: @@ -40,6 +40,11 @@ class AptCollection(Collection): else: lines.append("deb %s %s %s" % (source.url, source.version, " ".join(source.components))) + if source.debsrc: + lines.append("deb-src %s %s %s" % + (source.url, + source.version, + " ".join(source.components))) lines.append("") return "\n".join(lines) @@ -93,6 +98,8 @@ class AptSource(Source): self.logger.error("Packages: Failed to read file %s" % fname) raise for line in reader.readlines(): + if not isinstance(line, str): + line = line.decode('utf-8') words = str(line.strip()).split(':', 1) if words[0] == 'Package': pkgname = words[1].strip().rstrip() @@ -104,8 +111,8 @@ class AptSource(Source): vindex = 0 for dep in words[1].split(','): if '|' in dep: - cdeps = [re.sub('\s+', '', - re.sub('\(.*\)', '', cdep)) + cdeps = [re.sub(r'\s+', '', + re.sub(r'\(.*\)', '', cdep)) for cdep in dep.split('|')] dyn_dname = "choice-%s-%s-%s" % (pkgname, barch, @@ -114,7 +121,7 @@ class AptSource(Source): bdeps[barch][pkgname].append(dyn_dname) bprov[barch][dyn_dname] = set(cdeps) else: - raw_dep = re.sub('\(.*\)', '', dep) + raw_dep = re.sub(r'\(.*\)', '', dep) raw_dep = raw_dep.rstrip().strip() bdeps[barch][pkgname].append(raw_dep) elif words[0] == 'Provides': diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py index 2735e389a..332f0bbab 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py @@ -7,6 +7,7 @@ import Bcfg2.Server.Plugin from Bcfg2.Server.Plugins.Packages.Source import SourceInitError +# pylint: disable=E0012,R0924 class PackagesSources(Bcfg2.Server.Plugin.StructFile, Bcfg2.Server.Plugin.Debuggable): """ PackagesSources handles parsing of the @@ -16,6 +17,7 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile, each ``Source`` tag. """ __identifier__ = None + create = "Sources" def __init__(self, filename, cachepath, fam, packages, setup): """ @@ -39,14 +41,8 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile, If ``sources.xml`` cannot be read """ Bcfg2.Server.Plugin.Debuggable.__init__(self) - try: - Bcfg2.Server.Plugin.StructFile.__init__(self, filename, fam=fam, - should_monitor=True) - except OSError: - err = sys.exc_info()[1] - msg = "Packages: Failed to read configuration file: %s" % err - self.logger.error(msg) - raise Bcfg2.Server.Plugin.PluginInitError(msg) + Bcfg2.Server.Plugin.StructFile.__init__(self, filename, fam=fam, + should_monitor=True) #: The full path to the directory where #: :class:`Bcfg2.Server.Plugins.Packages.Source.Source` data @@ -129,7 +125,7 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile, """ Create a :class:`Bcfg2.Server.Plugins.Packages.Source.Source` subclass object from XML representation of a source in ``sources.xml``. - ``source_from-xml`` determines the appropriate subclass of + ``source_from_xml`` determines the appropriate subclass of ``Source`` to instantiate according to the ``type`` attribute of the ``Source`` tag. diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py index 985405e65..22073493c 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py @@ -52,8 +52,8 @@ import re import sys import Bcfg2.Server.Plugin from Bcfg2.Compat import HTTPError, HTTPBasicAuthHandler, \ - HTTPPasswordMgrWithDefaultRealm, install_opener, build_opener, \ - urlopen, cPickle, md5 + HTTPPasswordMgrWithDefaultRealm, install_opener, build_opener, urlopen, \ + cPickle, md5 def fetch_url(url): @@ -65,7 +65,7 @@ def fetch_url(url): :raises: URLError - Failure fetching URL :returns: string - the content of the page at the given URL """ if '@' in url: - mobj = re.match('(\w+://)([^:]+):([^@]+)@(.*)$', url) + mobj = re.match(r'(\w+://)([^:]+):([^@]+)@(.*)$', url) if not mobj: raise ValueError("Invalid URL") user = mobj.group(2) @@ -158,6 +158,10 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 #: this source self.whitelist = [item.text for item in xsource.findall('Whitelist')] + #: Whether or not to include deb-src lines in the generated APT + #: configuration + self.debsrc = xsource.get('debsrc', 'false') == 'true' + #: A dict of repository options that will be included in the #: configuration generated on the server side (if such is #: applicable; most backends do not generate any sort of @@ -315,7 +319,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 :raises: OSError - If the saved data cannot be read :raises: cPickle.UnpicklingError - If the saved data is corrupt """ - data = open(self.cachefile) + data = open(self.cachefile, 'rb') (self.pkgnames, self.deps, self.provides, self.essentialpkgs) = cPickle.load(data) @@ -615,7 +619,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 self.logger.info("Packages: Updating %s" % url) fname = self.escape_url(url) try: - open(fname, 'w').write(fetch_url(url)) + open(fname, 'wb').write(fetch_url(url)) except ValueError: self.logger.error("Packages: Bad url string %s" % url) raise diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py index 6b8ed1f7d..4608bcca5 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py @@ -66,7 +66,7 @@ from Bcfg2.Compat import StringIO, cPickle, HTTPError, URLError, \ # pylint: enable=W0622 from Bcfg2.Server.Plugins.Packages.Collection import Collection from Bcfg2.Server.Plugins.Packages.Source import SourceInitError, Source, \ - fetch_url + fetch_url LOGGER = logging.getLogger(__name__) @@ -281,7 +281,7 @@ class YumCollection(Collection): #: Define a unique cache file for this collection to use #: for cached yum metadata self.cachefile = os.path.join(self.cachepath, - "cache-%s" % self.cachekey) + "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) @@ -422,7 +422,7 @@ class YumCollection(Collection): config.add_section(reponame) added = True except ConfigParser.DuplicateSectionError: - match = re.search("-(\d+)", reponame) + match = re.search(r'-(\d+)', reponame) if match: rid = int(match.group(1)) + 1 else: @@ -675,7 +675,10 @@ class YumCollection(Collection): gdicts.append(dict(group=group, type=ptype)) if self.use_yum: - return self.call_helper("get_groups", inputdata=gdicts) + try: + return self.call_helper("get_groups", inputdata=gdicts) + except ValueError: + return dict() else: pkgs = dict() for gdict in gdicts: @@ -838,12 +841,13 @@ class YumCollection(Collection): return Collection.complete(self, packagelist) if packagelist: - result = \ - self.call_helper("complete", - dict(packages=list(packagelist), - groups=list(self.get_relevant_groups()))) - if not result: - # some sort of error, reported by call_helper() + try: + result = self.call_helper( + "complete", + dict(packages=list(packagelist), + groups=list(self.get_relevant_groups()))) + except ValueError: + # error reported by call_helper() return set(), packagelist # json doesn't understand sets or tuples, so we get back a # lists of lists (packages) and a list of unicode strings @@ -874,11 +878,16 @@ class YumCollection(Collection): ``bcfg2-yum-helper`` command. """ cmd = [self.helper, "-c", self.cfgfile] - verbose = self.debug_flag or self.setup['verbose'] - if verbose: + if self.setup['verbose']: + cmd.append("-v") + if self.debug_flag: + if not self.setup['verbose']: + # ensure that running in debug gets -vv, even if + # verbose is not enabled + cmd.append("-v") cmd.append("-v") cmd.append(command) - self.debug_log("Packages: running %s" % " ".join(cmd), flag=verbose) + self.debug_log("Packages: running %s" % " ".join(cmd)) try: helper = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) except OSError: @@ -893,19 +902,27 @@ class YumCollection(Collection): else: (stdout, stderr) = helper.communicate() rv = helper.wait() + errlines = stderr.splitlines() if rv: + if not errlines: + errlines.append("No error output") self.logger.error("Packages: error running bcfg2-yum-helper " - "(returned %d): %s" % (rv, stderr)) - else: + "(returned %d): %s" % (rv, errlines[0])) + for line in errlines[1:]: + self.logger.error("Packages: %s" % line) + elif errlines: self.debug_log("Packages: debug info from bcfg2-yum-helper: %s" % - stderr, flag=verbose) + errlines[0]) + for line in errlines[1:]: + self.debug_log("Packages: %s" % line) + try: return json.loads(stdout) except ValueError: err = sys.exc_info()[1] self.logger.error("Packages: error reading bcfg2-yum-helper " "output: %s" % err) - return None + raise def setup_data(self, force_update=False): """ Do any collection-level data setup tasks. This is called @@ -931,13 +948,21 @@ class YumCollection(Collection): if force_update: # we call this twice: one to clean up data from the old # config, and once to clean up data from the new config - self.call_helper("clean") + try: + self.call_helper("clean") + except ValueError: + # error reported by call_helper + pass os.unlink(self.cfgfile) self.write_config() if force_update: - self.call_helper("clean") + try: + self.call_helper("clean") + except ValueError: + # error reported by call_helper + pass class YumSource(Source): @@ -1120,9 +1145,9 @@ class YumSource(Source): self.packages['global'] = copy.deepcopy(sdata.pop()) except IndexError: self.logger.error("Packages: No packages in repo") + self.packages['global'] = set() while sdata: - self.packages['global'] = \ - self.packages['global'].intersection(sdata.pop()) + self.packages['global'].update(sdata.pop()) for key in self.packages: if key == 'global': @@ -1169,7 +1194,7 @@ class YumSource(Source): if entry.get('name').startswith('/'): self.needed_paths.add(entry.get('name')) pro = pdata.find(RP + 'provides') - if pro != None: + if pro is not None: for entry in pro.getchildren(): prov = entry.get('name') if prov not in self.provides[arch]: @@ -1185,9 +1210,9 @@ class YumSource(Source): try: groupid = group.xpath('id')[0].text self.yumgroups[groupid] = {'mandatory': list(), - 'default': list(), - 'optional': list(), - 'conditional': list()} + 'default': list(), + 'optional': list(), + 'conditional': list()} except IndexError: continue try: diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py index c3eadc6bb..f82b8a392 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py @@ -9,7 +9,7 @@ import shutil import lxml.etree import Bcfg2.Logger import Bcfg2.Server.Plugin -from Bcfg2.Compat import ConfigParser, urlopen, HTTPError +from Bcfg2.Compat import ConfigParser, urlopen, HTTPError, URLError from Bcfg2.Server.Plugins.Packages.Collection import Collection, \ get_collection_class from Bcfg2.Server.Plugins.Packages.PackagesSources import PackagesSources @@ -18,7 +18,8 @@ from Bcfg2.Server.Plugins.Packages.PackagesSources import PackagesSources YUM_CONFIG_DEFAULT = "/etc/yum.repos.d/bcfg2.repo" #: The default path for generated apt configs -APT_CONFIG_DEFAULT = "/etc/apt/sources.d/bcfg2" +APT_CONFIG_DEFAULT = \ + "/etc/apt/sources.list.d/bcfg2-packages-generated-sources.list" class Packages(Bcfg2.Server.Plugin.Plugin, @@ -184,6 +185,14 @@ class Packages(Bcfg2.Server.Plugin.Plugin, for (key, value) in list(attrib.items()): entry.attrib.__setitem__(key, value) + def get_config(self, metadata): + """ Get yum/apt config, as a string, for the specified client. + + :param metadata: The client to create the config for. + :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata + """ + return self.get_collection(metadata).get_config() + def HandleEntry(self, entry, metadata): """ Bind configuration entries. ``HandleEntry`` handles entries two different ways: @@ -239,14 +248,14 @@ class Packages(Bcfg2.Server.Plugin.Plugin, return True elif entry.tag == 'Path': # managed entries for yum/apt configs - if (entry.get("name") == \ - self.core.setup.cfp.get("packages", - "yum_config", - default=YUM_CONFIG_DEFAULT) or - entry.get("name") == \ - self.core.setup.cfp.get("packages", - "apt_config", - default=APT_CONFIG_DEFAULT)): + if (entry.get("name") == + self.core.setup.cfp.get("packages", + "yum_config", + default=YUM_CONFIG_DEFAULT) or + entry.get("name") == + self.core.setup.cfp.get("packages", + "apt_config", + default=APT_CONFIG_DEFAULT)): return True return False @@ -450,7 +459,7 @@ class Packages(Bcfg2.Server.Plugin.Plugin, try: open(localfile, 'w').write(urlopen(key).read()) keys.append(key) - except HTTPError: + except (URLError, HTTPError): err = sys.exc_info()[1] self.logger.error("Packages: Error downloading %s: %s" % (key, err)) @@ -518,8 +527,9 @@ class Packages(Bcfg2.Server.Plugin.Plugin, 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 + if cclass != Collection: + self.clients[metadata.hostname] = ckey + self.collections[ckey] = collection return collection def get_additional_data(self, metadata): @@ -536,7 +546,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, :return: dict of lists of ``url_map`` data """ collection = self.get_collection(metadata) - return dict(sources=collection.get_additional_data()) + return dict(sources=collection.get_additional_data(), + get_config=self.get_config) def end_client_run(self, metadata): """ Hook to clear the cache for this client in |