From 432f448983ff27452d82d62314d91c942f31bce5 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Mon, 25 Mar 2013 11:19:11 -0400 Subject: Packages: properly implemented deepcopy() for PackagesSources objects --- src/lib/Bcfg2/Server/Plugin/helpers.py | 33 ++++++++++++++++++++++ .../Server/Plugins/Packages/PackagesSources.py | 12 ++++++-- src/lib/Bcfg2/Server/Plugins/Packages/Source.py | 7 ++++- src/lib/Bcfg2/Server/Plugins/Packages/__init__.py | 11 +++++++- 4 files changed, 59 insertions(+), 4 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 0b81077a3..93f690e18 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -1601,3 +1601,36 @@ class GroupSpool(Plugin, Generator): return reqid = self.core.fam.AddMonitor(name, self) self.handles[reqid] = relative + + +class DeepcopyMixin(object): + """ Mixin that adds a __deepcopy__() function that tries to do the + right thing by: + + #. Creating a new object of the same type by calling the + constructor with the arguments returned by + :func:`Bcfg2.Server.Plugin.helpers.DeepcopyMixin._deepcopy_constructor_args`; + #. Individually copying each attribute of the original object that + a) is not magical (``__whatever__``); b) is not callable; c) is + not a property; and d) is not in + :attr:`Bcfg2.Server.Plugin.helpers.DeepcopyMixin._deepcopy_exclude`. + """ + + #: Exclude the named attributes from the copy. + _deepcopy_exclude = ["fam", "logger", "setup"] + + def _deepcopy_constructor_args(self): + """ Get a tuple of arguments that will be passed to the + constructor of this class to create a base object before the + individual attributes are deepcopied. """ + raise NotImplementedError + + def __deepcopy__(self, memo): + rv = self.__class__(*self._deepcopy_constructor_args()) + for attr in dir(self): + if (not callable(getattr(self, attr)) and + (not attr.startswith("__") or not attr.endswith("__")) and + attr not in self._deepcopy_exclude and + not isinstance(getattr(self.__class__, attr, None), property)): + setattr(rv, attr, copy.deepcopy(getattr(self, attr), memo)) + return rv diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py index 2735e389a..3069e4068 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/PackagesSources.py @@ -8,13 +8,17 @@ from Bcfg2.Server.Plugins.Packages.Source import SourceInitError class PackagesSources(Bcfg2.Server.Plugin.StructFile, - Bcfg2.Server.Plugin.Debuggable): + Bcfg2.Server.Plugin.Debuggable, + Bcfg2.Server.Plugin.DeepcopyMixin): """ PackagesSources handles parsing of the :mod:`Bcfg2.Server.Plugins.Packages` ``sources.xml`` file, and the creation of the appropriate :class:`Bcfg2.Server.Plugins.Packages.Source.Source` object for each ``Source`` tag. """ + _deepcopy_exclude = Bcfg2.Server.Plugin.DeepcopyMixin._deepcopy_exclude + \ + ['pkg_obj'] + __identifier__ = None def __init__(self, filename, cachepath, fam, packages, setup): @@ -39,6 +43,7 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile, If ``sources.xml`` cannot be read """ Bcfg2.Server.Plugin.Debuggable.__init__(self) + Bcfg2.Server.Plugin.DeepcopyMixin.__init__(self) try: Bcfg2.Server.Plugin.StructFile.__init__(self, filename, fam=fam, should_monitor=True) @@ -129,7 +134,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. @@ -176,3 +181,6 @@ class PackagesSources(Bcfg2.Server.Plugin.StructFile, def __len__(self): return len(self.entries) + + def _deepcopy_constructor_args(self): + return (self.name, self.cachepath, None, None, dict(self.setup)) diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py index 985405e65..40c1f6676 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/Source.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/Source.py @@ -92,7 +92,8 @@ class SourceInitError(Exception): REPO_RE = re.compile(r'(?:pulp/repos/|/RPMS\.|/)([^/]+)/?$') -class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 +class Source(Bcfg2.Server.Plugin.Debuggable, # pylint: disable=R0902 + Bcfg2.Server.Plugin.DeepcopyMixin): """ ``Source`` objects represent a single tag in ``sources.xml``. Note that a single Source tag can itself describe multiple repositories (if it uses the "url" attribute @@ -128,6 +129,7 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 :raises: :class:`Bcfg2.Server.Plugins.Packages.Source.SourceInitError` """ Bcfg2.Server.Plugin.Debuggable.__init__(self) + Bcfg2.Server.Plugin.DeepcopyMixin.__init__(self) #: The base filesystem path under which cache data for this #: source should be stored @@ -738,3 +740,6 @@ class Source(Bcfg2.Server.Plugin.Debuggable): # pylint: disable=R0902 if group in metadata.groups: return True return False + + def _deepcopy_constructor_args(self): + return (self.basepath, self.xsource, dict(self.setup)) diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py index 308a0efc4..cc78497f1 100644 --- a/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Packages/__init__.py @@ -185,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: @@ -538,7 +546,8 @@ class Packages(Bcfg2.Server.Plugin.Plugin, """ collection = self.get_collection(metadata) return dict(sources=collection.get_additional_data(), - allsources=copy.deepcopy(self.sources)) + allsources=copy.deepcopy(self.sources), + get_config=self.get_config) def end_client_run(self, metadata): """ Hook to clear the cache for this client in -- cgit v1.2.3-1-g7c22