diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/Bcfg2/Client/Tools/BundleDeps.py | 34 | ||||
-rw-r--r-- | src/lib/Bcfg2/Client/__init__.py | 59 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Bundler.py | 27 |
3 files changed, 104 insertions, 16 deletions
diff --git a/src/lib/Bcfg2/Client/Tools/BundleDeps.py b/src/lib/Bcfg2/Client/Tools/BundleDeps.py new file mode 100644 index 000000000..aaa090633 --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/BundleDeps.py @@ -0,0 +1,34 @@ +""" Bundle dependency support """ + +import Bcfg2.Client.Tools + + +class BundleDeps(Bcfg2.Client.Tools.Tool): + """Bundle dependency helper for Bcfg2. It handles Bundle tags inside the + bundles that references the required other bundles that should change the + modification status if the referenced bundles is modified.""" + + name = 'Bundle' + __handles__ = [('Bundle', None)] + __req__ = {'Bundle': ['name']} + + def InstallBundle(self, _): + """Simple no-op because we only need the BundleUpdated hook.""" + return dict() + + def VerifyBundle(self, entry, _): # pylint: disable=W0613 + """Simple no-op because we only need the BundleUpdated hook.""" + return True + + def BundleUpdated(self, entry): + """This handles the dependencies on this bundle. It searches all + Bundle tags in other bundles that references the current bundle name + and marks those tags as modified to trigger the modification hook on + the other bundles.""" + + bundle_name = entry.get('name') + for bundle in self.config.findall('./Bundle/Bundle'): + if bundle.get('name') == bundle_name and \ + bundle not in self.modified: + self.modified.append(bundle) + return dict() diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 5f4f15dcc..d834576c9 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -768,27 +768,27 @@ class Client(object): if not Bcfg2.Options.setup.interactive: self.DispatchInstallCalls(clobbered) - for bundle in self.config.findall('.//Bundle'): + all_bundles = self.config.findall('./Bundle') + mbundles.extend(self._get_all_modified_bundles(mbundles, all_bundles)) + + for bundle in all_bundles: if (Bcfg2.Options.setup.only_bundles and bundle.get('name') not in Bcfg2.Options.setup.only_bundles): # prune out unspecified bundles when running with -b continue if bundle in mbundles: - self.logger.debug("Bundle %s was modified" % - bundle.get('name')) - func = "BundleUpdated" - else: - self.logger.debug("Bundle %s was not modified" % - bundle.get('name')) - func = "BundleNotUpdated" + continue + + self.logger.debug("Bundle %s was not modified" % + bundle.get('name')) for tool in self.tools: try: - self.states.update(getattr(tool, func)(bundle)) + self.states.update(tool.BundleNotUpdated(bundle)) except: # pylint: disable=W0702 - self.logger.error("%s.%s(%s:%s) call failed:" % - (tool.name, func, bundle.tag, - bundle.get("name")), exc_info=1) + self.logger.error('%s.BundleNotUpdated(%s:%s) call failed:' + % (tool.name, bundle.tag, + bundle.get('name')), exc_info=1) for indep in self.config.findall('.//Independent'): for tool in self.tools: @@ -799,6 +799,41 @@ class Client(object): % (tool.name, indep.tag, indep.get("name")), exc_info=1) + def _get_all_modified_bundles(self, mbundles, all_bundles): + """This gets all modified bundles by calling BundleUpdated until no + new bundles get added to the modification list.""" + new_mbundles = mbundles + add_mbundles = [] + + while new_mbundles: + for bundle in self.config.findall('./Bundle'): + if (Bcfg2.Options.setup.only_bundles and + bundle.get('name') not in + Bcfg2.Options.setup.only_bundles): + # prune out unspecified bundles when running with -b + continue + if bundle not in new_mbundles: + continue + + self.logger.debug('Bundle %s was modified' % + bundle.get('name')) + for tool in self.tools: + try: + self.states.update(tool.BundleUpdated(bundle)) + except: # pylint: disable=W0702 + self.logger.error('%s.BundleUpdated(%s:%s) call ' + 'failed:' % (tool.name, bundle.tag, + bundle.get("name")), + exc_info=1) + + mods = self.modified + new_mbundles = [struct for struct in all_bundles + if any(True for mod in mods if mod in struct) + and struct not in mbundles + add_mbundles] + add_mbundles.extend(new_mbundles) + + return add_mbundles + def Remove(self): """Remove extra entries.""" for tool in self.tools: diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py index 41ee57b6d..4945bf85b 100644 --- a/src/lib/Bcfg2/Server/Plugins/Bundler.py +++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py @@ -8,6 +8,7 @@ import fnmatch import lxml.etree from Bcfg2.Server.Plugin import StructFile, Plugin, Structure, \ StructureValidator, XMLDirectoryBacked, Generator +from Bcfg2.version import Bcfg2VersionInfo from genshi.template import TemplateError @@ -116,17 +117,35 @@ class Bundler(Plugin, for el in child.getchildren(): data.append(el) data.remove(child) - elif child.get("name"): + else: + # no children -- wat + self.logger.warning("Bundler: Useless empty Bundle tag " + "in %s" % self.name) + data.remove(child) + + for child in data.findall('RequiredBundle'): + if child.get("name"): # dependent bundle -- add it to the list of # bundles for this client if child.get("name") not in bundles_added: bundles.append(child.get("name")) bundles_added.add(child.get("name")) + if child.get('modification', 'ignore') == 'inherit': + if metadata.version_info >= \ + Bcfg2VersionInfo('1.4.0pre2'): + lxml.etree.SubElement(data, 'Bundle', + name=child.get('name')) + else: + self.logger.warning( + 'Bundler: modification="inherit" is only ' + 'supported for clients starting 1.4.0pre2') data.remove(child) else: - # neither name or children -- wat - self.logger.warning("Bundler: Useless empty Bundle tag " - "in %s" % self.name) + # no name -- wat + self.logger.warning('Bundler: Missing required name in ' + 'RequiredBundle tag in %s' % + self.name) data.remove(child) + bundleset.append(data) return bundleset |