Example #1
0
    def __init__(self, name):
        """
        :param name: The filename of this properties file.

        .. automethod:: _write
        """
        self.name = name
        self.setup = get_option_parser()
Example #2
0
    def __init__(self, name):
        """
        :param name: The filename of this properties file.

        .. automethod:: _write
        """
        self.name = name
        self.setup = get_option_parser()
Example #3
0
    def __init__(self, fname):
        CfgCreator.__init__(self, fname)
        StructFile.__init__(self, fname)

        pubkey_path = os.path.dirname(self.name) + ".pub"
        pubkey_name = os.path.join(pubkey_path, os.path.basename(pubkey_path))
        self.pubkey_creator = CfgPublicKeyCreator(pubkey_name)
        self.setup = get_option_parser()
        self.cmd = Executor()
Example #4
0
    def __init__(self, fname):
        CfgCreator.__init__(self, fname)
        StructFile.__init__(self, fname)

        pubkey_path = os.path.dirname(self.name) + ".pub"
        pubkey_name = os.path.join(pubkey_path, os.path.basename(pubkey_path))
        self.pubkey_creator = CfgPublicKeyCreator(pubkey_name)
        self.setup = get_option_parser()
        self.cmd = Executor()
Example #5
0
    def __init__(self, filename, cachepath, packages):
        """
        :param filename: The full path to ``sources.xml``
        :type filename: string
        :param cachepath: The full path to the directory where
                          :class:`Bcfg2.Server.Plugins.Packages.Source.Source`
                          data will be cached
        :type cachepath: string
        :param packages: The Packages plugin object ``sources.xml`` is
                         being parsed on behalf of (i.e., the calling
                         object)
        :type packages: Bcfg2.Server.Plugins.Packages.Packages

        :raises: :class:`Bcfg2.Server.Plugin.exceptions.PluginInitError` -
                 If ``sources.xml`` cannot be read
        """
        Bcfg2.Server.Plugin.Debuggable.__init__(self)
        Bcfg2.Server.Plugin.StructFile.__init__(self,
                                                filename,
                                                should_monitor=True)

        #: The full path to the directory where
        #: :class:`Bcfg2.Server.Plugins.Packages.Source.Source` data
        #: will be cached
        self.cachepath = cachepath

        if not os.path.exists(self.cachepath):
            # create cache directory if needed
            try:
                os.makedirs(self.cachepath)
            except OSError:
                err = sys.exc_info()[1]
                self.logger.error("Could not create Packages cache at %s: %s" %
                                  (self.cachepath, err))
        #: The Bcfg2 options dict
        self.setup = get_option_parser()

        #: The :class:`Bcfg2.Server.Plugins.Packages.Packages` that
        #: instantiated this ``PackagesSources`` object
        self.pkg_obj = packages

        #: The set of all XML files that have been successfully
        #: parsed.  This is used by :attr:`loaded` to determine if the
        #: sources have been fully parsed and the
        #: :class:`Bcfg2.Server.Plugins.Packages.Packages` plugin
        #: should be told to reload its data.
        self.parsed = set()
Example #6
0
    def __init__(self, filename, cachepath, packages):
        """
        :param filename: The full path to ``sources.xml``
        :type filename: string
        :param cachepath: The full path to the directory where
                          :class:`Bcfg2.Server.Plugins.Packages.Source.Source`
                          data will be cached
        :type cachepath: string
        :param packages: The Packages plugin object ``sources.xml`` is
                         being parsed on behalf of (i.e., the calling
                         object)
        :type packages: Bcfg2.Server.Plugins.Packages.Packages

        :raises: :class:`Bcfg2.Server.Plugin.exceptions.PluginInitError` -
                 If ``sources.xml`` cannot be read
        """
        Bcfg2.Server.Plugin.Debuggable.__init__(self)
        Bcfg2.Server.Plugin.StructFile.__init__(self, filename,
                                                should_monitor=True)

        #: The full path to the directory where
        #: :class:`Bcfg2.Server.Plugins.Packages.Source.Source` data
        #: will be cached
        self.cachepath = cachepath

        if not os.path.exists(self.cachepath):
            # create cache directory if needed
            try:
                os.makedirs(self.cachepath)
            except OSError:
                err = sys.exc_info()[1]
                self.logger.error("Could not create Packages cache at %s: %s" %
                                  (self.cachepath, err))
        #: The Bcfg2 options dict
        self.setup = get_option_parser()

        #: The :class:`Bcfg2.Server.Plugins.Packages.Packages` that
        #: instantiated this ``PackagesSources`` object
        self.pkg_obj = packages

        #: The set of all XML files that have been successfully
        #: parsed.  This is used by :attr:`loaded` to determine if the
        #: sources have been fully parsed and the
        #: :class:`Bcfg2.Server.Plugins.Packages.Packages` plugin
        #: should be told to reload its data.
        self.parsed = set()
Example #7
0
    def __init__(self, metadata, sources, cachepath, basepath, debug=False):
        """
        :param metadata: The client metadata for this collection
        :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
        :param sources: A list of all sources known to the server that
                        will be used to generate the list of sources
                        that apply to this client
        :type sources: list of
                       :class:`Bcfg2.Server.Plugins.Packages.Source.Source`
                       objects
        :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 debug: Enable debugging output
        :type debug: bool

        .. -----
        .. autoattribute:: __package_groups__
        """
        Bcfg2.Server.Plugin.Debuggable.__init__(self)
        list.__init__(self, sources)
        self.debug_flag = debug
        self.metadata = metadata
        self.basepath = basepath
        self.cachepath = cachepath
        self.virt_pkgs = dict()
        self.fam = get_fam()
        self.setup = get_option_parser()

        try:
            self.ptype = sources[0].ptype
        except IndexError:
            self.ptype = "unknown"
Example #8
0
    def __init__(self, metadata, sources, cachepath, basepath, debug=False):
        """
        :param metadata: The client metadata for this collection
        :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata
        :param sources: A list of all sources known to the server that
                        will be used to generate the list of sources
                        that apply to this client
        :type sources: list of
                       :class:`Bcfg2.Server.Plugins.Packages.Source.Source`
                       objects
        :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 debug: Enable debugging output
        :type debug: bool

        .. -----
        .. autoattribute:: __package_groups__
        """
        Bcfg2.Server.Plugin.Debuggable.__init__(self)
        list.__init__(self, sources)
        self.debug_flag = debug
        self.metadata = metadata
        self.basepath = basepath
        self.cachepath = cachepath
        self.virt_pkgs = dict()
        self.fam = get_fam()
        self.setup = get_option_parser()

        try:
            self.ptype = sources[0].ptype
        except IndexError:
            self.ptype = "unknown"
Example #9
0
File: Yum.py Project: shellox/bcfg2
def _setup_pulp():
    """ Connect to a Pulp server and pass authentication credentials.
    This only needs to be called once, but multiple calls won't hurt
    anything.

    :returns: :class:`pulp.client.api.server.PulpServer`
    """
    global PULPSERVER, PULPCONFIG
    if not HAS_PULP:
        msg = "Packages: Cannot create Pulp collection: Pulp libraries " + "not found"
        LOGGER.error(msg)
        raise Bcfg2.Server.Plugin.PluginInitError(msg)

    if PULPSERVER is None:
        setup = get_option_parser()
        try:
            username = setup.cfp.get("packages:pulp", "username")
            password = setup.cfp.get("packages:pulp", "password")
        except ConfigParser.NoSectionError:
            msg = "Packages: No [pulp] section found in bcfg2.conf"
            LOGGER.error(msg)
            raise Bcfg2.Server.Plugin.PluginInitError(msg)
        except ConfigParser.NoOptionError:
            msg = "Packages: Required option not found in bcfg2.conf: %s" % sys.exc_info()[1]
            LOGGER.error(msg)
            raise Bcfg2.Server.Plugin.PluginInitError(msg)

        PULPCONFIG = ConsumerConfig()
        serveropts = PULPCONFIG.server

        PULPSERVER = server.PulpServer(
            serveropts["host"], int(serveropts["port"]), serveropts["scheme"], serveropts["path"]
        )
        PULPSERVER.set_basic_auth_credentials(username, password)
        server.set_active_server(PULPSERVER)
    return PULPSERVER
Example #10
0
    def __init__(self, config, times):
        self.setup = get_option_parser()
        self.config = config
        self.times = times
        self.dryrun = self.setup['dryrun']
        self.times['initialization'] = time.time()
        self.tools = []

        #: A dict of the state of each entry.  Keys are the entries.
        #: Values are boolean: True means that the entry is good,
        #: False means that the entry is bad.
        self.states = {}
        self.whitelist = []
        self.blacklist = []
        self.removal = []
        self.logger = logging.getLogger(__name__)
        drivers = self.setup['drivers']
        for driver in drivers[:]:
            if (driver not in Bcfg2.Client.Tools.__all__ and
                isinstance(driver, str)):
                self.logger.error("Tool driver %s is not available" % driver)
                drivers.remove(driver)

        tclass = {}
        for tool in drivers:
            if not isinstance(tool, str):
                tclass[time.time()] = tool
            tool_class = "Bcfg2.Client.Tools.%s" % tool
            try:
                tclass[tool] = getattr(__import__(tool_class, globals(),
                                                  locals(), ['*']),
                                       tool)
            except ImportError:
                continue
            except:
                self.logger.error("Tool %s unexpectedly failed to load" % tool,
                                  exc_info=1)

        for tool in list(tclass.values()):
            try:
                self.tools.append(tool(config))
            except Bcfg2.Client.Tools.ToolInstantiationError:
                continue
            except:
                self.logger.error("Failed to instantiate tool %s" % tool,
                                  exc_info=1)

        for tool in self.tools[:]:
            for conflict in getattr(tool, 'conflicts', []):
                for item in self.tools:
                    if item.name == conflict:
                        self.tools.remove(item)

        self.logger.info("Loaded tool drivers:")
        self.logger.info([tool.name for tool in self.tools])

        deprecated = [tool.name for tool in self.tools if tool.deprecated]
        if deprecated:
            self.logger.warning("Loaded deprecated tool drivers:")
            self.logger.warning(deprecated)
        experimental = [tool.name for tool in self.tools if tool.experimental]
        if experimental:
            self.logger.warning("Loaded experimental tool drivers:")
            self.logger.warning(experimental)

        # find entries not handled by any tools
        self.unhandled = [entry for struct in config
                          for entry in struct
                          if entry not in self.handled]

        if self.unhandled:
            self.logger.error("The following entries are not handled by any "
                              "tool:")
            for entry in self.unhandled:
                self.logger.error("%s:%s:%s" % (entry.tag, entry.get('type'),
                                                entry.get('name')))

        self.find_dups(config)

        pkgs = [(entry.get('name'), entry.get('origin'))
                for struct in config
                for entry in struct
                if entry.tag == 'Package']
        if pkgs:
            self.logger.debug("The following packages are specified in bcfg2:")
            self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] is None])
            self.logger.debug("The following packages are prereqs added by "
                              "Packages:")
            self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] == 'Packages'])
Example #11
0
    def __init__(self, basepath, xsource):  # pylint: disable=R0912
        """
        :param basepath: The base filesystem path under which cache
                         data for this source should be stored
        :type basepath: string
        :param xsource: The XML tag that describes this source
        :type source: lxml.etree._Element
        :raises: :class:`Bcfg2.Server.Plugins.Packages.Source.SourceInitError`
        """
        Bcfg2.Server.Plugin.Debuggable.__init__(self)

        #: The base filesystem path under which cache data for this
        #: source should be stored
        self.basepath = basepath

        #: The XML tag that describes this source
        self.xsource = xsource

        #: A Bcfg2 options dict
        self.setup = get_option_parser()

        #: A set of package names that are deemed "essential" by this
        #: source
        self.essentialpkgs = set()

        #: A list of the text of all 'Component' attributes of this
        #: source from XML
        self.components = [item.text for item in xsource.findall('Component')]

        #: A list of the arches supported by this source
        self.arches = [item.text for item in xsource.findall('Arch')]

        #: A list of the the names of packages that are blacklisted
        #: from this source
        self.blacklist = [item.text for item in xsource.findall('Blacklist')]

        #: A list of the the names of packages that are whitelisted in
        #: this source
        self.whitelist = [item.text for item in xsource.findall('Whitelist')]

        #: 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
        #: repository configuration on the Bcfg2 server)
        self.server_options = dict()

        #: A dict of repository options that will be included in the
        #: configuration generated for the client (if that is
        #: supported by the backend)
        self.client_options = dict()
        opts = xsource.findall("Options")
        for el in opts:
            repoopts = dict([(k, v)
                             for k, v in el.attrib.items()
                             if k != "clientonly" and k != "serveronly"])
            if el.get("clientonly", "false").lower() == "false":
                self.server_options.update(repoopts)
            if el.get("serveronly", "false").lower() == "false":
                self.client_options.update(repoopts)

        #: A list of URLs to GPG keys that apply to this source
        self.gpgkeys = [el.text for el in xsource.findall("GPGKey")]

        #: Whether or not to include essential packages from this source
        self.essential = xsource.get('essential', 'true').lower() == 'true'

        #: Whether or not to include recommended packages from this source
        self.recommended = xsource.get('recommended',
                                       'false').lower() == 'true'

        #: The "rawurl" attribute from :attr:`xsource`, if applicable.
        #: A trailing slash is automatically appended to this if there
        #: wasn't one already present.
        self.rawurl = xsource.get('rawurl', '')
        if self.rawurl and not self.rawurl.endswith("/"):
            self.rawurl += "/"

        #: The "url" attribute from :attr:`xsource`, if applicable.  A
        #: trailing slash is automatically appended to this if there
        #: wasn't one already present.
        self.url = xsource.get('url', '')
        if self.url and not self.url.endswith("/"):
            self.url += "/"

        #: The "version" attribute from :attr:`xsource`
        self.version = xsource.get('version', '')

        #: A list of predicates that are used to determine if this
        #: source applies to a given
        #: :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata`
        #: object.
        self.conditions = []
        #: Formerly, :ref:`server-plugins-generators-packages` only
        #: supported applying package sources to groups; that is, they
        #: could not be assigned by more complicated logic like
        #: per-client repositories and group or client negation.  This
        #: attribute attempts to provide for some limited backwards
        #: compat with older code that relies on this.
        self.groups = []
        for el in xsource.iterancestors():
            if el.tag == "Group":
                if el.get("negate", "false").lower() == "true":
                    self.conditions.append(lambda m, el=el:
                                           el.get("name") not in m.groups)
                else:
                    self.groups.append(el.get("name"))
                    self.conditions.append(lambda m, el=el:
                                           el.get("name") in m.groups)
            elif el.tag == "Client":
                if el.get("negate", "false").lower() == "true":
                    self.conditions.append(lambda m, el=el:
                                           el.get("name") != m.hostname)
                else:
                    self.conditions.append(lambda m, el=el:
                                           el.get("name") == m.hostname)

        #: A set of all package names in this source.  This will not
        #: necessarily be populated, particularly by backends that
        #: reimplement large portions of
        #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`
        self.pkgnames = set()

        #: A dict of ``<package name>`` -> ``<list of dependencies>``.
        #: This will not necessarily be populated, particularly by
        #: backends that reimplement large portions of
        #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`
        self.deps = dict()

        #: A dict of ``<package name>`` -> ``<list of provided
        #: symbols>``.  This will not necessarily be populated,
        #: particularly by backends that reimplement large portions of
        #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`
        self.provides = dict()

        #: The file (or directory) used for this source's cache data
        self.cachefile = os.path.join(self.basepath,
                                      "cache-%s" % self.cachekey)
        if not self.rawurl:
            baseurl = self.url + "%(version)s/%(component)s/%(arch)s/"
        else:
            baseurl = self.rawurl

        #: A list of dicts, each of which describes the URL to one
        #: repository contained in this source.  Each dict contains
        #: the following keys:
        #:
        #: * ``version``: The version of the repo (``None`` for
        #:   ``rawurl`` repos)
        #: * ``component``: The component use to form this URL
        #:   (``None`` for ``rawurl`` repos)
        #: * ``arch``: The architecture of this repo
        #: * ``baseurl``: Either the ``rawurl`` attribute, or the
        #:   format string built from the ``url`` attribute
        #: * ``url``: The actual URL to the repository
        self.url_map = []
        for arch in self.arches:
            if self.url:
                usettings = [dict(version=self.version, component=comp,
                                  arch=arch)
                             for comp in self.components]
            else:  # rawurl given
                usettings = [dict(version=self.version, component=None,
                                  arch=arch)]

            for setting in usettings:
                if not self.rawurl:
                    setting['baseurl'] = self.url
                else:
                    setting['baseurl'] = self.rawurl
                setting['url'] = baseurl % setting
            self.url_map.extend(usettings)
Example #12
0
    def __init__(self, config, times):
        self.setup = get_option_parser()
        self.config = config
        self.times = times
        self.dryrun = self.setup['dryrun']
        self.times['initialization'] = time.time()
        self.tools = []

        #: A dict of the state of each entry.  Keys are the entries.
        #: Values are boolean: True means that the entry is good,
        #: False means that the entry is bad.
        self.states = {}
        self.whitelist = []
        self.blacklist = []
        self.removal = []
        self.logger = logging.getLogger(__name__)
        drivers = self.setup['drivers']
        for driver in drivers[:]:
            if (driver not in Bcfg2.Client.Tools.__all__
                    and isinstance(driver, str)):
                self.logger.error("Tool driver %s is not available" % driver)
                drivers.remove(driver)

        tclass = {}
        for tool in drivers:
            if not isinstance(tool, str):
                tclass[time.time()] = tool
            tool_class = "Bcfg2.Client.Tools.%s" % tool
            try:
                tclass[tool] = getattr(
                    __import__(tool_class, globals(), locals(), ['*']), tool)
            except ImportError:
                continue
            except:
                self.logger.error("Tool %s unexpectedly failed to load" % tool,
                                  exc_info=1)

        for tool in list(tclass.values()):
            try:
                self.tools.append(tool(config))
            except Bcfg2.Client.Tools.ToolInstantiationError:
                continue
            except:
                self.logger.error("Failed to instantiate tool %s" % tool,
                                  exc_info=1)

        for tool in self.tools[:]:
            for conflict in getattr(tool, 'conflicts', []):
                for item in self.tools:
                    if item.name == conflict:
                        self.tools.remove(item)

        self.logger.info("Loaded tool drivers:")
        self.logger.info([tool.name for tool in self.tools])

        deprecated = [tool.name for tool in self.tools if tool.deprecated]
        if deprecated:
            self.logger.warning("Loaded deprecated tool drivers:")
            self.logger.warning(deprecated)
        experimental = [tool.name for tool in self.tools if tool.experimental]
        if experimental:
            self.logger.warning("Loaded experimental tool drivers:")
            self.logger.warning(experimental)

        # find entries not handled by any tools
        self.unhandled = [
            entry for struct in config for entry in struct
            if entry not in self.handled
        ]

        if self.unhandled:
            self.logger.error("The following entries are not handled by any "
                              "tool:")
            for entry in self.unhandled:
                self.logger.error(
                    "%s:%s:%s" %
                    (entry.tag, entry.get('type'), entry.get('name')))

        self.find_dups(config)

        pkgs = [(entry.get('name'), entry.get('origin')) for struct in config
                for entry in struct if entry.tag == 'Package']
        if pkgs:
            self.logger.debug("The following packages are specified in bcfg2:")
            self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] is None])
            self.logger.debug("The following packages are prereqs added by "
                              "Packages:")
            self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] == 'Packages'])
Example #13
0
    def __init__(self, basepath, xsource):  # pylint: disable=R0912
        """
        :param basepath: The base filesystem path under which cache
                         data for this source should be stored
        :type basepath: string
        :param xsource: The XML tag that describes this source
        :type source: lxml.etree._Element
        :raises: :class:`Bcfg2.Server.Plugins.Packages.Source.SourceInitError`
        """
        Bcfg2.Server.Plugin.Debuggable.__init__(self)

        #: The base filesystem path under which cache data for this
        #: source should be stored
        self.basepath = basepath

        #: The XML tag that describes this source
        self.xsource = xsource

        #: A Bcfg2 options dict
        self.setup = get_option_parser()

        #: A set of package names that are deemed "essential" by this
        #: source
        self.essentialpkgs = set()

        #: A list of the text of all 'Component' attributes of this
        #: source from XML
        self.components = [item.text for item in xsource.findall('Component')]

        #: A list of the arches supported by this source
        self.arches = [item.text for item in xsource.findall('Arch')]

        #: A list of the the names of packages that are blacklisted
        #: from this source
        self.blacklist = [item.text for item in xsource.findall('Blacklist')]

        #: A list of the the names of packages that are whitelisted in
        #: 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
        #: repository configuration on the Bcfg2 server)
        self.server_options = dict()

        #: A dict of repository options that will be included in the
        #: configuration generated for the client (if that is
        #: supported by the backend)
        self.client_options = dict()
        opts = xsource.findall("Options")
        for el in opts:
            repoopts = dict([(k, v) for k, v in el.attrib.items()
                             if k != "clientonly" and k != "serveronly"])
            if el.get("clientonly", "false").lower() == "false":
                self.server_options.update(repoopts)
            if el.get("serveronly", "false").lower() == "false":
                self.client_options.update(repoopts)

        #: A list of URLs to GPG keys that apply to this source
        self.gpgkeys = [el.text for el in xsource.findall("GPGKey")]

        #: Whether or not to include essential packages from this source
        self.essential = xsource.get('essential', 'true').lower() == 'true'

        #: Whether or not to include recommended packages from this source
        self.recommended = xsource.get('recommended',
                                       'false').lower() == 'true'

        #: The "rawurl" attribute from :attr:`xsource`, if applicable.
        #: A trailing slash is automatically appended to this if there
        #: wasn't one already present.
        self.rawurl = xsource.get('rawurl', '')
        if self.rawurl and not self.rawurl.endswith("/"):
            self.rawurl += "/"

        #: The "url" attribute from :attr:`xsource`, if applicable.  A
        #: trailing slash is automatically appended to this if there
        #: wasn't one already present.
        self.url = xsource.get('url', '')
        if self.url and not self.url.endswith("/"):
            self.url += "/"

        #: The "version" attribute from :attr:`xsource`
        self.version = xsource.get('version', '')

        #: A list of predicates that are used to determine if this
        #: source applies to a given
        #: :class:`Bcfg2.Server.Plugins.Metadata.ClientMetadata`
        #: object.
        self.conditions = []
        #: Formerly, :ref:`server-plugins-generators-packages` only
        #: supported applying package sources to groups; that is, they
        #: could not be assigned by more complicated logic like
        #: per-client repositories and group or client negation.  This
        #: attribute attempts to provide for some limited backwards
        #: compat with older code that relies on this.
        self.groups = []
        for el in xsource.iterancestors():
            if el.tag == "Group":
                if el.get("negate", "false").lower() == "true":
                    self.conditions.append(
                        lambda m, el=el: el.get("name") not in m.groups)
                else:
                    self.groups.append(el.get("name"))
                    self.conditions.append(
                        lambda m, el=el: el.get("name") in m.groups)
            elif el.tag == "Client":
                if el.get("negate", "false").lower() == "true":
                    self.conditions.append(
                        lambda m, el=el: el.get("name") != m.hostname)
                else:
                    self.conditions.append(
                        lambda m, el=el: el.get("name") == m.hostname)

        #: A set of all package names in this source.  This will not
        #: necessarily be populated, particularly by backends that
        #: reimplement large portions of
        #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`
        self.pkgnames = set()

        #: A dict of ``<package name>`` -> ``<list of dependencies>``.
        #: This will not necessarily be populated, particularly by
        #: backends that reimplement large portions of
        #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`
        self.deps = dict()

        #: A dict of ``<package name>`` -> ``<list of provided
        #: symbols>``.  This will not necessarily be populated,
        #: particularly by backends that reimplement large portions of
        #: :class:`Bcfg2.Server.Plugins.Packages.Collection.Collection`
        self.provides = dict()

        #: The file (or directory) used for this source's cache data
        self.cachefile = os.path.join(self.basepath,
                                      "cache-%s" % self.cachekey)
        if not self.rawurl:
            baseurl = self.url + "%(version)s/%(component)s/%(arch)s/"
        else:
            baseurl = self.rawurl

        #: A list of dicts, each of which describes the URL to one
        #: repository contained in this source.  Each dict contains
        #: the following keys:
        #:
        #: * ``version``: The version of the repo (``None`` for
        #:   ``rawurl`` repos)
        #: * ``component``: The component use to form this URL
        #:   (``None`` for ``rawurl`` repos)
        #: * ``arch``: The architecture of this repo
        #: * ``baseurl``: Either the ``rawurl`` attribute, or the
        #:   format string built from the ``url`` attribute
        #: * ``url``: The actual URL to the repository
        self.url_map = []
        for arch in self.arches:
            if self.url:
                usettings = [
                    dict(version=self.version, component=comp, arch=arch)
                    for comp in self.components
                ]
            else:  # rawurl given
                usettings = [
                    dict(version=self.version, component=None, arch=arch)
                ]

            for setting in usettings:
                if not self.rawurl:
                    setting['baseurl'] = self.url
                else:
                    setting['baseurl'] = self.rawurl
                setting['url'] = baseurl % setting
            self.url_map.extend(usettings)