def __init__(self, metadata, sources, basepath, debug=False): Collection.__init__(self, metadata, sources, basepath, debug=debug) self.keypath = os.path.join(self.basepath, "keys") if len(sources): config = sources[0].config self.use_yum = has_yum and config.getboolean( "yum", "use_yum_libraries", default=False) else: self.use_yum = False if self.use_yum: self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.configdir = os.path.join(self.basepath, "yum") if not os.path.exists(self.configdir): os.mkdir(self.configdir) self.cfgfile = os.path.join(self.configdir, "%s-yum.conf" % self.cachekey) self.write_config() self.helper = self.config.get("yum", "helper", default="/usr/sbin/bcfg2-yum-helper") if has_pulp: _setup_pulp(self.config)
def __init__(self, metadata, sources, basepath): Collection.__init__(self, metadata, sources, basepath) self.keypath = os.path.join(self.basepath, "keys") if len(sources): config = sources[0].config self.use_yum = has_yum try: self.use_yum &= config.getboolean("yum", "use_yum_libraries") except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): self.use_yum = False else: self.use_yum = False if self.use_yum: self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.configdir = os.path.join(self.basepath, "yum") if not os.path.exists(self.configdir): os.mkdir(self.configdir) self.cfgfile = os.path.join(self.configdir, "%s-yum.conf" % self.cachekey) self.write_config() try: self.helper = self.config.get("yum", "helper") except ConfigParser.NoOptionError: self.helper = "/usr/sbin/bcfg2-yum-helper" if has_pulp: _setup_pulp(self.config)
def __init__(self, metadata, sources, cachepath, basepath, debug=False): Collection.__init__(self, metadata, sources, cachepath, basepath, debug=debug) self.keypath = os.path.join(self.cachepath, "keys") self._helper = None if self.use_yum: #: 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) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) #: The path to the server-side config file used when #: resolving packages with the Python yum libraries self.cfgfile = os.path.join(self.cachefile, "yum.conf") self.write_config() self.cmd = Executor() else: self.cachefile = None self.cmd = None if HAS_PULP and self.has_pulp_sources: _setup_pulp() 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)
def __init__(self, metadata, sources, basepath, debug=False): Collection.__init__(self, metadata, sources, basepath, debug=debug) self.keypath = os.path.join(self.basepath, "keys") if len(sources): config = sources[0].config self.use_yum = has_yum and config.getboolean("yum", "use_yum_libraries", default=False) else: self.use_yum = False if self.use_yum: self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.configdir = os.path.join(self.basepath, "yum") if not os.path.exists(self.configdir): os.mkdir(self.configdir) self.cfgfile = os.path.join(self.configdir, "%s-yum.conf" % self.cachekey) self.write_config() self.helper = self.config.get("yum", "helper", default="/usr/sbin/bcfg2-yum-helper") if has_pulp: _setup_pulp(self.config)
def __init__(self, metadata, sources, cachepath, basepath, debug=False): # we define an __init__ that just calls the parent __init__, # so that we can set the docstring on __init__ to something # different from the parent __init__ -- namely, the parent # __init__ docstring, minus everything after ``.. -----``, # which we use to delineate the actual docs from the # .. autoattribute hacks we have to do to get private # attributes included in sphinx 1.0 """ Collection.__init__(self, metadata, sources, cachepath, basepath, debug=debug)
def __init__(self, metadata, sources, cachepath, basepath, debug=False): # we define an __init__ that just calls the parent __init__, # so that we can set the docstring on __init__ to something # different from the parent __init__ -- namely, the parent # __init__ docstring, minus everything after ``.. -----``, # which we use to delineate the actual docs from the # .. autoattribute hacks we have to do to get private # attributes included in sphinx 1.0 """ Collection.__init__(self, metadata, sources, cachepath, basepath, debug=debug)
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.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) #: The path to the server-side config file used when #: resolving packages with the Python yum libraries self.cfgfile = os.path.join(self.cachefile, "yum.conf") self.write_config() else: self.cachefile = None 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
def __init__(self, metadata, sources, basepath, debug=False): Collection.__init__(self, metadata, sources, basepath, debug=debug) self.keypath = os.path.join(self.basepath, "keys") if self.use_yum: self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.cfgfile = os.path.join(self.cachefile, "yum.conf") self.write_config() if has_pulp and self.has_pulp_sources: _setup_pulp(self.setup) self._helper = None
def get_deps(self, package): if not self.use_yum: return Collection.get_deps(self, package) else: # this should really never get called; it's just provided # for API completeness return self.call_helper("get_deps", package)
def get_provides(self, required, all=False, silent=False): if not self.use_yum: return Collection.get_provides(self, package) else: # this should really never get called; it's just provided # for API completeness return self.call_helper("get_provides", package)
def setup_data(self, force_update=False): """ Do any collection-level data setup tasks. This is called when sources are loaded or reloaded by :class:`Bcfg2.Server.Plugins.Packages.Packages`. If the builtin yum parsers are in use, this defers to :func:`Bcfg2.Server.Plugins.Packages.Collection.Collection.setup_data`. If using the yum Python libraries, this cleans up cached yum metadata, regenerates the server-side yum config (in order to catch any new sources that have been added to this server), and then cleans up cached yum metadata again, in case the new config has any preexisting cache. :param force_update: Ignore all local cache and setup data from its original upstream sources (i.e., the package repositories) :type force_update: bool """ if not self.use_yum: return Collection.setup_data(self, force_update) 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") os.unlink(self.cfgfile) self.write_config() if force_update: self.call_helper("clean")
def setup_data(self, force_update=False): """ Do any collection-level data setup tasks. This is called when sources are loaded or reloaded by :class:`Bcfg2.Server.Plugins.Packages.Packages`. If the builtin yum parsers are in use, this defers to :func:`Bcfg2.Server.Plugins.Packages.Collection.Collection.setup_data`. If using the yum Python libraries, this cleans up cached yum metadata, regenerates the server-side yum config (in order to catch any new sources that have been added to this server), and then cleans up cached yum metadata again, in case the new config has any preexisting cache. :param force_update: Ignore all local cache and setup data from its original upstream sources (i.e., the package repositories) :type force_update: bool """ if not self.use_yum: return Collection.setup_data(self, force_update) 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") os.unlink(self.cfgfile) self.write_config() if force_update: self.call_helper("clean")
def is_virtual_package(self, package): if not self.use_yum: return Collection.is_virtual_package(self, package) else: # this should really never get called; it's just provided # for API completeness return self.call_helper("is_virtual_package", package)
def setup_data(self, force_update=False): if not self.use_yum: return Collection.setup_data(self, force_update) for cfile in glob.glob(os.path.join(self.configdir, "*-yum.conf")): os.unlink(cfile) self._yb = None self.pkgs_cache.clear() self.deps_cache.clear() self.vpkgs_cache.clear() self.group_cache.clear() self.pkgset_cache.clear() if force_update: for mdtype in ["Headers", "Packages", "Sqlite", "Metadata", "ExpireCache"]: # for reasons that are entirely obvious, all of the # yum API clean* methods return a tuple of 0 (zero, # always zero) and a list containing a single message # about how many files were deleted. so useful. # thanks, yum. self.logger.info("Packages: %s" % getattr(self.yumbase, "clean%s" % mdtype)()[1][0])
def get_deps(self, package): if not self.use_yum: return Collection.get_deps(self, package) else: # this should really never get called; it's just provided # for API completeness return self.call_helper("get_deps", package)
def get_provides(self, required, all=False, silent=False): if not self.use_yum: return Collection.get_provides(self, package) if not isinstance(required, tuple): required = (required, None, (None, None, None)) try: return self.vpkgs_cache[required] except KeyError: pass try: prov = \ self.yumbase.whatProvides(*required).returnNewestByNameArch() except yum.Errors.NoMoreMirrorsRepoError: err = sys.exc_info()[1] self.logger.error("Packages: Temporary failure loading metadata " "for '%s': %s" % (self.get_package_name(required), err)) self.vpkgs_cache[required] = None return [] if prov and not all: prov = self._filter_provides(required, prov) elif not prov and not silent: self.logger.error("Packages: No package provides %s" % self.get_package_name(required)) self.vpkgs_cache[required] = prov return self.vpkgs_cache[required]
def get_provides(self, required, all=False, silent=False): if not self.use_yum: return Collection.get_provides(self, package) else: # this should really never get called; it's just provided # for API completeness return self.call_helper("get_provides", package)
def is_virtual_package(self, package): if not self.use_yum: return Collection.is_virtual_package(self, package) else: # this should really never get called; it's just provided # for API completeness return self.call_helper("is_virtual_package", package)
def cachefiles(self): """ A list of the full path to all cachefiles used by this collection.""" cachefiles = set(Collection.cachefiles(self)) if self.cachefile: cachefiles.add(self.cachefile) return list(cachefiles)
def is_virtual_package(self, package): if self.use_yum: try: return bool(self.vpkgs_cache[package]) except KeyError: return bool(self.get_provides(package, silent=True)) else: return Collection.is_virtual_package(self, package)
def get_collection(self, metadata): """ Get a :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection` object for this client. :param metadata: The client metadata to get a Collection for :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata :returns: An instance of the appropriate subclass of :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection` that contains all relevant sources that apply to the given client """ 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, self.data, self.core.fam) if metadata.hostname in self.clients: return self.collections[self.clients[metadata.hostname]] sclasses = set() relevant = list() for source in self.sources.entries: if source.applies(metadata): relevant.append(source) sclasses.update([source.__class__]) if len(sclasses) > 1: self.logger.warning("Packages: Multiple source types found for " "%s: %s" % ",".join([s.__name__ for s in sclasses])) cclass = Collection elif len(sclasses) == 0: self.logger.error("Packages: No sources found for %s" % metadata.hostname) cclass = Collection else: cclass = get_collection_class(sclasses.pop().__name__.replace( "Source", "")) if self.debug_flag: self.logger.error("Packages: Using %s for Collection of sources " "for %s" % (cclass.__name__, metadata.hostname)) 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 return collection
def __init__(self, metadata, sources, basepath, debug=False): Collection.__init__(self, metadata, sources, basepath, debug=debug) self.keypath = os.path.join(self.basepath, "keys") if self.use_yum: self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.configdir = os.path.join(self.basepath, "yum") if not os.path.exists(self.configdir): os.mkdir(self.configdir) self.cfgfile = os.path.join(self.configdir, "%s-yum.conf" % self.cachekey) self.write_config() if has_pulp and self.has_pulp_sources: _setup_pulp(self.setup) self._helper = None
def is_package(self, package): if not self.use_yum: return Collection.is_package(self, package) elif isinstance(package, tuple): if package[1] is None and package[2] == (None, None, None): package = package[0] else: return None else: # this should really never get called; it's just provided # for API completeness return self.call_helper("is_package", package)
def is_package(self, package): if not self.use_yum: return Collection.is_package(self, package) elif isinstance(package, tuple): if package[1] is None and package[2] == (None, None, None): package = package[0] else: return None else: # this should really never get called; it's just provided # for API completeness return self.call_helper("is_package", package)
def __init__(self, metadata, sources, basepath): Collection.__init__(self, metadata, sources, basepath) self.keypath = os.path.join(self.basepath, "keys") if len(sources): config = sources[0].config self.use_yum = has_yum try: self.use_yum &= config.getboolean("yum", "use_yum_libraries") except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): self.use_yum = False else: self.use_yum = False if self.use_yum: self._yb = None self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.configdir = os.path.join(self.basepath, "yum") if not os.path.exists(self.configdir): os.mkdir(self.configdir) self.cfgfile = os.path.join(self.configdir, "%s-yum.conf" % self.cachekey) if self.config.has_option("yum", "metadata_expire"): cache_expire = self.config.getint("yum", "metadata_expire") else: cache_expire = 21600 self.pkgs_cache = Cache(expiration=cache_expire) self.deps_cache = Cache(expiration=cache_expire) self.vpkgs_cache = Cache(expiration=cache_expire) self.group_cache = Cache(expiration=cache_expire) self.pkgset_cache = Cache(expiration=cache_expire) if has_pulp: _setup_pulp(self.config)
def __init__(self, metadata, sources, basepath, debug=False): Collection.__init__(self, metadata, sources, basepath, debug=debug) self.keypath = os.path.join(self.basepath, "keys") if len(sources): self.config = sources[0].config else: self.config = PackageConfig('Packages') if self.use_yum: self.cachefile = os.path.join(self.cachepath, "cache-%s" % self.cachekey) if not os.path.exists(self.cachefile): os.mkdir(self.cachefile) self.configdir = os.path.join(self.basepath, "yum") if not os.path.exists(self.configdir): os.mkdir(self.configdir) self.cfgfile = os.path.join(self.configdir, "%s-yum.conf" % self.cachekey) self.write_config() if has_pulp and self.has_pulp_sources: _setup_pulp(self.config)
def setup_data(self, force_update=False): if not self.use_yum: return Collection.setup_data(self, force_update) 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") os.unlink(self.cfgfile) self.write_config() if force_update: self.call_helper("clean")
def setup_data(self, force_update=False): if not self.use_yum: return Collection.setup_data(self, force_update) 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") os.unlink(self.cfgfile) self.write_config() if force_update: self.call_helper("clean")
def is_package(self, package): if not self.use_yum: return Collection.is_package(self, package) if isinstance(package, tuple): if package[1] is None and package[2] == (None, None, None): package = package[0] else: return None try: return self.pkgs_cache[package] except KeyError: pass self.pkgs_cache[package] = bool(self.get_package_object(package, silent=True)) return self.pkgs_cache[package]
def complete(self, packagelist): if not self.use_yum: return Collection.complete(self, packagelist) packages = set() unknown = set(packagelist) if unknown: result = self.call_helper("complete", dict(packages=list(unknown), groups=list(self.get_relevant_groups()))) if result and "packages" in result and "unknown" in result: # we stringify every package because it gets returned # in unicode; set.update() doesn't work if some # elements are unicode and other are strings. (I.e., # u'foo' and 'foo' get treated as unique elements.) packages.update([str(p) for p in result["packages"]]) unknown = set([str(p) for p in result["unknown"]]) self.filter_unknown(unknown) return packages, unknown
def get_deps(self, package): if not self.use_yum: return Collection.get_deps(self, package) try: return self.deps_cache[package] except KeyError: pass pkg = self.get_package_object(package) deps = [] if pkg: deps = set(pkg.requires) # filter out things the package itself provides deps.difference_update([dep for dep in deps if pkg.checkPrco('provides', dep)]) else: self.logger.error("Packages: No package available: %s" % self.get_package_name(package)) self.deps_cache[package] = deps return self.deps_cache[package]
def complete(self, packagelist): if not self.use_yum: 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() 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 # (unknown). turn those into a set of tuples and a set of # strings, respectively. unknown = set([str(u) for u in result["unknown"]]) packages = set([tuple(p) for p in result["packages"]]) self.filter_unknown(unknown) return packages, unknown else: return set(), set()
def packages_from_entry(self, entry): """ When using the Python yum libraries, convert a Package entry to a list of package tuples. See :ref:`yum-pkg-objects` and :ref:`pkg-objects` for more information on this process. :param entry: The Package entry to convert :type entry: lxml.etree._Element :returns: list of tuples """ if not self.use_yum: return Collection.packages_from_entry(self, entry) rv = set() name = entry.get("name") for inst in entry.getchildren(): if inst.tag != "Instance": continue rv.add(self._element_to_pkg(inst, name)) if not rv: rv.add(self._element_to_pkg(entry, name)) return list(rv)
def complete(self, packagelist): """ Build a complete list of all packages and their dependencies. When using the Python yum libraries, this defers to the :ref:`bcfg2-yum-helper`; when using the builtin yum parser, this defers to :func:`Bcfg2.Server.Plugins.Packages.Collection.Collection.complete`. :param packagelist: Set of initial packages computed from the specification. :type packagelist: set of strings, but see :ref:`pkg-objects` :returns: tuple of sets - The first element contains a set of strings (but see :ref:`pkg-objects`) describing the complete package list, and the second element is a set of symbols whose dependencies could not be resolved. """ if not self.use_yum: 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() 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 # (unknown). turn those into a set of tuples and a set of # strings, respectively. unknown = set([str(u) for u in result['unknown']]) packages = set([tuple(p) for p in result['packages']]) self.filter_unknown(unknown) return packages, unknown else: return set(), set()
def get_groups(self, grouplist): """ If using the yum libraries, given a list of package group names, return a dict of ``<group name>: <list of packages>``. This is much faster than implementing :func:`Bcfg2.Server.Plugins.Packages.Collection.Collection.get_group`, since we have to make a call to the bcfg2 Yum helper, and each time we do that we make another call to yum, which means we set up yum metadata from the cache (hopefully) each time. So resolving ten groups once is much faster than resolving one group ten times. If you are using the builtin yum parser, this raises a warning and returns an empty dict. :param grouplist: The list of groups to query :type grouplist: list of strings - group names :returns: dict of ``<group name>: <list of packages>`` In this implementation the packages may be strings or tuples. See :ref:`yum-pkg-objects` for more information. """ if not grouplist: return dict() gdicts = [] for group, ptype in grouplist: if group.startswith("@"): group = group[1:] if not ptype: ptype = "default" gdicts.append(dict(group=group, type=ptype)) 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 packages_to_entry(self, pkglist, entry): """ When using the Python yum libraries, convert a list of package tuples to a Package entry. See :ref:`yum-pkg-objects` and :ref:`pkg-objects` for more information on this process. If pkglist contains only one package, then its data is converted to a single ``BoundPackage`` entry that is added as a subelement of ``entry``. If pkglist contains more than one package, then a parent ``BoundPackage`` entry is created and child ``Instance`` entries are added to it. :param pkglist: A list of package tuples to convert to an XML Package entry :type pkglist: list of tuples :param entry: The base XML entry to add Package entries to. This is modified in place. :type entry: lxml.etree._Element :returns: None """ if not self.use_yum: return Collection.packages_to_entry(self, pkglist, entry) packages = dict() for pkg in pkglist: try: packages[pkg[0]].append(pkg) except KeyError: packages[pkg[0]] = [pkg] for name, instances in packages.items(): pkgattrs = dict(type=self.ptype, origin="Packages", name=name) if len(instances) > 1: pkg_el = lxml.etree.SubElement(entry, "BoundPackage", **pkgattrs) for inst in instances: lxml.etree.SubElement(pkg_el, "Instance", self._get_entry_attrs(inst)) else: attrs = self._get_entry_attrs(instances[0]) attrs.update(pkgattrs) lxml.etree.SubElement(entry, "BoundPackage", **attrs)
def complete(self, packagelist): """ Build a complete list of all packages and their dependencies. When using the Python yum libraries, this defers to the :ref:`bcfg2-yum-helper`; when using the builtin yum parser, this defers to :func:`Bcfg2.Server.Plugins.Packages.Collection.Collection.complete`. :param packagelist: Set of initial packages computed from the specification. :type packagelist: set of strings, but see :ref:`pkg-objects` :returns: tuple of sets - The first element contains a set of strings (but see :ref:`pkg-objects`) describing the complete package list, and the second element is a set of symbols whose dependencies could not be resolved. """ if not self.use_yum: 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() 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 # (unknown). turn those into a set of tuples and a set of # strings, respectively. unknown = set([str(u) for u in result['unknown']]) packages = set([tuple(p) for p in result['packages']]) self.filter_unknown(unknown) return packages, unknown else: return set(), set()
def complete(self, packagelist): if not self.use_yum: return Collection.complete(self, packagelist) packages = set() unknown = set(packagelist) if unknown: result = \ self.call_helper("complete", dict(packages=list(unknown), groups=list(self.get_relevant_groups()))) if result and "packages" in result and "unknown" in result: # we stringify every package because it gets returned # in unicode; set.update() doesn't work if some # elements are unicode and other are strings. (I.e., # u'foo' and 'foo' get treated as unique elements.) packages.update([str(p) for p in result['packages']]) unknown = set([str(p) for p in result['unknown']]) self.filter_unknown(unknown) return packages, unknown
def complete(self, packagelist): if not self.use_yum: return Collection.complete(self, packagelist) cachekey = cPickle.dumps(sorted(packagelist)) try: packages = self.pkgset_cache[cachekey] except KeyError: packages = set() pkgs = set(packagelist).difference(packages) requires = set() satisfied = set() unknown = set() final_pass = False while requires or pkgs: # infinite loop protection start_reqs = len(requires) while pkgs: package = pkgs.pop() if package in packages: continue if not self.is_package(package): # try this package out as a requirement requires.add((package, None, (None, None, None))) continue packages.add(package) reqs = set(self.get_deps(package)).difference(satisfied) if reqs: requires.update(reqs) reqs_satisfied = set() for req in requires: if req in satisfied: reqs_satisfied.add(req) continue if req[1] is None and self.is_package(req[0]): if req[0] not in packages: pkgs.add(req[0]) reqs_satisfied.add(req) continue self.logger.debug("Packages: Handling requirement '%s'" % self.get_package_name(req)) providers = list(set(self.get_provides(req))) if len(providers) > 1: # hopefully one of the providing packages is already # included best = [p for p in providers if p in packages] if best: providers = best else: # pick a provider whose name matches the requirement best = [p for p in providers if p == req[0]] if len(best) == 1: providers = best elif not final_pass: # found no "best" package, so defer providers = None # else: found no "best" package, but it's the # final pass, so include them all if providers: self.logger.debug("Packages: Requirement '%s' satisfied " "by %s" % (self.get_package_name(req), ",".join([self.get_package_name(p) for p in providers]))) newpkgs = set(providers).difference(packages) if newpkgs: for package in newpkgs: if self.is_package(package): pkgs.add(package) else: unknown.add(package) reqs_satisfied.add(req) elif providers is not None: # nothing provided this requirement at all unknown.add(req) reqs_satisfied.add(req) # else, defer requires.difference_update(reqs_satisfied) # infinite loop protection if len(requires) == start_reqs and len(pkgs) == 0: final_pass = True if final_pass and requires: unknown.update(requires) requires = set() self.filter_unknown(unknown) unknown = [self.get_package_name(p) for p in unknown] # we do not cache unknown packages, since those are likely to # be fixed self.pkgset_cache[cachekey] = packages return packages, unknown