def __contains__(self, other): other = int(other) if other in self.ints: return True return any((end is None and other >= start) or ( end is not None and other >= start and other <= end) for start, end in self.ranges)
def VerifyPackage(self, entry, modlist): """Verify Package status for entry.""" desired_version = entry.get('version') if desired_version == 'any': desired_version = self.installed.get(entry.get('name'), desired_version) if not self.cmd.run(["/usr/bin/pkginfo", "-q", "-v", desired_version, entry.get('name')]): if entry.get('name') in self.installed: self.logger.debug("Package %s version incorrect: " "have %s want %s" % (entry.get('name'), self.installed[entry.get('name')], desired_version)) else: self.logger.debug("Package %s not installed" % entry.get("name")) else: if Bcfg2.Options.setup.quick or \ entry.attrib.get('verify', 'true') == 'false': return True rv = self.cmd.run("/usr/sbin/pkgchk -n %s" % entry.get('name')) if rv.success: return True else: output = [line for line in rv.stdout.splitlines() if line[:5] == 'ERROR'] if any(name for name in output if name.split()[-1] not in modlist): self.logger.debug("Package %s content verification failed" % entry.get('name')) else: return True return False
def Install(self): """Install all entries.""" self.DispatchInstallCalls(self.whitelist) mods = self.modified mbundles = [struct for struct in self.config.findall('Bundle') if any(True for mod in mods if mod in struct)] if self.modified: # Handle Bundle interdeps if mbundles: self.logger.info("The Following Bundles have been modified:") self.logger.info([mbun.get('name') for mbun in mbundles]) tbm = [(t, b) for t in self.tools for b in mbundles] for tool, bundle in tbm: try: self.states.update(tool.Inventory(structures=[bundle])) except: # pylint: disable=W0702 self.logger.error("%s.Inventory() call failed:" % tool.name, exc_info=1) clobbered = [entry for bundle in mbundles for entry in bundle if (not self.states[entry] and entry not in self.blacklist)] if clobbered: self.logger.debug("Found clobbered entries:") self.logger.debug(["%s:%s" % (entry.tag, entry.get('name')) for entry in clobbered]) if not Bcfg2.Options.setup.interactive: self.DispatchInstallCalls(clobbered) 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 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" for tool in self.tools: try: self.states.update(getattr(tool, func)(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) for indep in self.config.findall('.//Independent'): for tool in self.tools: try: self.states.update(tool.BundleNotUpdated(indep)) except: # pylint: disable=W0702 self.logger.error("%s.BundleNotUpdated(%s:%s) call failed:" % (tool.name, indep.tag, indep.get("name")), exc_info=1)
def Run(self): if 'Cfg' in self.core.plugins: for entryset in self.core.plugins['Cfg'].entries.values(): for entry in entryset.entries.values(): if (self.HandlesFile(entry.name) and any( isinstance(entry, t) for t in self.templates)): self.check_template(entryset, entry)
def __contains__(self, other): other = int(other) if other in self.ints: return True return any((end is None and other >= start) or (end is not None and other >= start and other <= end) for start, end in self.ranges)
def includes(self, other): """ return True if other is included in this range """ iother = int(other) if iother in self.sparse: return True return any(iother in range(start, end + 1) for start, end in self.ranges)
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 HandleEvent(self, event=None): """Local event handler that does skn regen on pubkey change.""" # skip events we don't care about action = event.code2str() if action == "endExist" or event.filename == self.data: return for entry in list(self.entries.values()): if entry.specific.match(event.filename): entry.handle_event(event) if any(event.filename.startswith(kp) for kp in self.keypatterns if kp.endswith(".pub")): self.debug_log("New public key %s; invalidating " "ssh_known_hosts cache" % event.filename) self.skn = False return if event.filename == "info.xml": for entry in list(self.entries.values()): entry.handle_event(event) return if event.filename.endswith(".static"): self.logger.info("Static key %s %s; invalidating ssh_known_hosts " "cache" % (event.filename, action)) if action == "deleted" and event.filename in self.static: del self.static[event.filename] self.skn = False else: self.static[event.filename] = Bcfg2.Server.Plugin.FileBacked(os.path.join(self.data, event.filename)) self.static[event.filename].HandleEvent(event) self.skn = False return self.logger.warn("SSHbase: Got unknown event %s %s" % (event.filename, action))
def Run(self): if 'Cfg' in self.core.plugins: for entryset in self.core.plugins['Cfg'].entries.values(): for entry in entryset.entries.values(): if (self.HandlesFile(entry.name) and any(isinstance(entry, t) for t in self.templates)): self.check_template(entryset, entry)
def GenerateStats(self): """Generate XML summary of execution statistics.""" feedback = XML.Element("upload-statistics") stats = XML.SubElement(feedback, 'Statistics', total=str(len(self.states)), version='2.0', revision=self.config.get('revision', '-1')) good_entries = [key for key, val in list(self.states.items()) if val] good = len(good_entries) stats.set('good', str(good)) if any(not val for val in list(self.states.values())): stats.set('state', 'dirty') else: stats.set('state', 'clean') # List bad elements of the configuration for (data, ename) in [(self.modified, 'Modified'), (self.extra, "Extra"), (good_entries, "Good"), ([entry for entry in self.states if not self.states[entry]], "Bad")]: container = XML.SubElement(stats, ename) for item in data: item.set('qtext', '') container.append(item) item.text = None timeinfo = XML.Element("OpStamps") feedback.append(stats) for (event, timestamp) in list(self.times.items()): timeinfo.set(event, str(timestamp)) stats.append(timeinfo) return feedback
def check_missing_files(self): """ check that all files on the filesystem are known to Cfg """ cfg = self.core.plugins['Cfg'] # first, collect ignore patterns from handlers ignore = set() for hdlr in handlers(): ignore.update(hdlr.__ignore__) # next, get a list of all non-ignored files on the filesystem all_files = set() for root, _, files in os.walk(cfg.data, followlinks=True): for fname in files: fpath = os.path.join(root, fname) # check against the handler ignore patterns and the # global FAM ignore list if (not any(fname.endswith("." + i) for i in ignore) and not any( fnmatch(fpath, p) for p in self.config['ignore']) and not any( fnmatch(c, p) for p in self.config['ignore'] for c in self._list_path_components(fpath))): all_files.add(fpath) # next, get a list of all files known to Cfg cfg_files = set() for root, eset in cfg.entries.items(): cfg_files.update( os.path.join(cfg.data, root.lstrip("/"), fname) for fname in eset.entries.keys()) # finally, compare the two unknown_files = all_files - cfg_files extra_files = cfg_files - all_files if unknown_files: self.LintError( "unknown-cfg-files", "Files on the filesystem could not be understood by Cfg: %s" % "; ".join(unknown_files)) if extra_files: self.LintError( "extra-cfg-files", "Cfg has entries for files that do not exist on the " "filesystem: %s\nThis is probably a bug." % "; ".join(extra_files))
def check_missing_files(self): """ check that all files on the filesystem are known to Cfg """ cfg = self.core.plugins['Cfg'] # first, collect ignore patterns from handlers ignore = set() for hdlr in handlers(): ignore.update(hdlr.__ignore__) # next, get a list of all non-ignored files on the filesystem all_files = set() for root, _, files in os.walk(cfg.data, followlinks=True): for fname in files: fpath = os.path.join(root, fname) # check against the handler ignore patterns and the # global FAM ignore list if (not any(fname.endswith("." + i) for i in ignore) and not any(fnmatch(fpath, p) for p in self.config['ignore']) and not any(fnmatch(c, p) for p in self.config['ignore'] for c in self._list_path_components(fpath))): all_files.add(fpath) # next, get a list of all files known to Cfg cfg_files = set() for root, eset in cfg.entries.items(): cfg_files.update(os.path.join(cfg.data, root.lstrip("/"), fname) for fname in eset.entries.keys()) # finally, compare the two unknown_files = all_files - cfg_files extra_files = cfg_files - all_files if unknown_files: self.LintError( "unknown-cfg-files", "Files on the filesystem could not be understood by Cfg: %s" % "; ".join(unknown_files)) if extra_files: self.LintError( "extra-cfg-files", "Cfg has entries for files that do not exist on the " "filesystem: %s\nThis is probably a bug." % "; ".join(extra_files))
def is_package(self, package): """ Return True if a package is a package, False otherwise. The base implementation returns True if any Source object's :func:`Bcfg2.Server.Plugins.Packages.Source.Source.is_package` returns True. :param package: The name of the package, but see :ref:`pkg-objects` :type package: string :returns: bool """ return any(source.is_package(self.metadata, package) for source in self)
def is_package(self, package): """ Return True if a package is a package, False otherwise. The base implementation returns True if any Source object's :func:`Bcfg2.Server.Plugins.Packages.Source.Source.is_package` returns True. :param package: The name of the package, but see :ref:`pkg-objects` :type package: string :returns: bool """ return any( source.is_package(self.metadata, package) for source in self)
def is_virtual_package(self, package): """ Return True if a name is a virtual package (i.e., is a symbol provided by a real package), False otherwise. The base implementation returns True if any Source object's :func:`Bcfg2.Server.Plugins.Packages.Source.Source.is_virtual_package` returns True. :param package: The name of the symbol, but see :ref:`pkg-objects` :type package: string :returns: bool """ return any(source.is_virtual_package(self.metadata, package) for source in self)
def magic_groups_match(self): """ Returns True if the client's :ref:`server-plugins-generators-packages-magic-groups` match the magic groups for any of the sources contained in this Collection. The base implementation returns True if any source :func:`Bcfg2.Server.Plugins.Packages.Source.Source.magic_groups_match` returns True. :returns: bool """ return any(s.magic_groups_match(self.metadata) for s in self)
def GenerateStats(self): """Generate XML summary of execution statistics.""" states = {} for (item, val) in list(self.states.items()): if (not Bcfg2.Options.setup.only_important or item.get('important', 'false').lower() == 'true'): states[item] = val feedback = XML.Element("upload-statistics") stats = XML.SubElement(feedback, 'Statistics', total=str(len(states)), version='2.0', revision=self.config.get('revision', '-1')) flags = XML.SubElement(stats, "Flags") XML.SubElement(flags, "Flag", name="dry_run", value=str(Bcfg2.Options.setup.dry_run)) XML.SubElement(flags, "Flag", name="only_important", value=str(Bcfg2.Options.setup.only_important)) good_entries = [key for key, val in list(states.items()) if val] good = len(good_entries) stats.set('good', str(good)) if any(not val for val in list(states.values())): stats.set('state', 'dirty') else: stats.set('state', 'clean') # List bad elements of the configuration for (data, ename) in [ (self.modified, 'Modified'), (self.extra, "Extra"), (good_entries, "Good"), ([entry for entry in states if not states[entry]], "Bad") ]: container = XML.SubElement(stats, ename) for item in data: new_item = copy.deepcopy(item) new_item.set('qtext', '') container.append(new_item) new_item.text = None timeinfo = XML.Element("OpStamps") feedback.append(stats) for (event, timestamp) in list(self.times.items()): timeinfo.set(event, str(timestamp)) stats.append(timeinfo) return feedback
def is_virtual_package(self, package): """ Return True if a name is a virtual package (i.e., is a symbol provided by a real package), False otherwise. The base implementation returns True if any Source object's :func:`Bcfg2.Server.Plugins.Packages.Source.Source.is_virtual_package` returns True. :param package: The name of the symbol, but see :ref:`pkg-objects` :type package: string :returns: bool """ return any( source.is_virtual_package(self.metadata, package) for source in self)
def HandleEvent(self, event=None): """Local event handler that does skn regen on pubkey change.""" # skip events we don't care about action = event.code2str() if action == "endExist" or event.filename == self.data: return for entry in list(self.entries.values()): if event.filename.endswith(".crypt"): fname = event.filename[0:-6] else: fname = event.filename if entry.specific.match(fname): entry.handle_event(event) if any(event.filename.startswith(kp) for kp in self.keypatterns if kp.endswith(".pub")): self.debug_log("New public key %s; invalidating " "ssh_known_hosts cache" % event.filename) self.skn = False if self.core.metadata_cache_mode in ['cautious', 'aggressive']: self.core.metadata_cache.expire() return if event.filename == 'info.xml': for entry in list(self.entries.values()): entry.handle_event(event) return if event.filename.endswith('.static'): self.logger.info("Static key %s %s; invalidating ssh_known_hosts " "cache" % (event.filename, action)) if action == "deleted" and event.filename in self.static: del self.static[event.filename] self.skn = False else: self.static[event.filename] = Bcfg2.Server.Plugin.FileBacked( os.path.join(self.data, event.filename)) self.static[event.filename].HandleEvent(event) self.skn = False return self.logger.warn("SSHbase: Got unknown event %s %s" % (event.filename, action))
def GenerateStats(self): """Generate XML summary of execution statistics.""" states = {} for (item, val) in list(self.states.items()): if (not Bcfg2.Options.setup.only_important or item.get('important', 'false').lower() == 'true'): states[item] = val feedback = XML.Element("upload-statistics") stats = XML.SubElement(feedback, 'Statistics', total=str(len(states)), version='2.0', revision=self.config.get('revision', '-1')) flags = XML.SubElement(stats, "Flags") XML.SubElement(flags, "Flag", name="dry_run", value=str(Bcfg2.Options.setup.dry_run)) XML.SubElement(flags, "Flag", name="only_important", value=str(Bcfg2.Options.setup.only_important)) good_entries = [key for key, val in list(states.items()) if val] good = len(good_entries) stats.set('good', str(good)) if any(not val for val in list(states.values())): stats.set('state', 'dirty') else: stats.set('state', 'clean') # List bad elements of the configuration for (data, ename) in [(self.modified, 'Modified'), (self.extra, "Extra"), (good_entries, "Good"), ([entry for entry in states if not states[entry]], "Bad")]: container = XML.SubElement(stats, ename) for item in data: new_item = copy.deepcopy(item) new_item.set('qtext', '') container.append(new_item) new_item.text = None timeinfo = XML.Element("OpStamps") feedback.append(stats) for (event, timestamp) in list(self.times.items()): timeinfo.set(event, str(timestamp)) stats.append(timeinfo) return feedback
def ignore(cls, event, basename=None): # pylint: disable=W0613 """ Return True if this handler ignores the file described by ``event``. See :attr:`Bcfg2.Server.Plugins.Cfg.CfgBaseFileMatcher.__ignore__` for more information on how ignoring files works. :param event: The FAM event to check :type event: Bcfg2.Server.FileMonitor.Event :param basename: The base filename to use if :attr:`Bcfg2.Server.Plugins.Cfg.CfgBaseFileMatcher.__basenames__` is not defined (i.e., the name of the directory that contains the files the regex will be applied to) :type basename: string :returns: bool - True if this handler handles the file listed in the event, False otherwise. """ return any(event.filename.endswith("." + e) for e in cls.__ignore__)
def ignore(cls, event, basename=None): # pylint: disable=W0613 """ Return True if this handler ignores the file described by ``event``. See :attr:`CfgBaseFileMatcher.__ignore__` for more information on how ignoring files works. :param event: The FAM event to check :type event: Bcfg2.Server.FileMonitor.Event :param basename: The base filename to use if :attr:`CfgBaseFileMatcher.__basenames__` is not defined (i.e., the name of the directory that contains the files the regex will be applied to) :type basename: string :returns: bool - True if this handler handles the file listed in the event, False otherwise. """ return any(event.filename.endswith("." + e) for e in cls.__ignore__)
def run(self, setup): parser = Bcfg2.Options.get_parser() data = [('Description', 'Value')] for option in parser.option_list: if hasattr(setup, option.dest): value = getattr(setup, option.dest) if any(issubclass(a.__class__, Bcfg2.Options.ComponentAction) for a in option.actions.values()): if not setup.raw: try: if option.action.islist: value = [v.__name__ for v in value] else: value = value.__name__ except AttributeError: # just use the value as-is pass if setup.raw: value = repr(value) data.append((getattr(option, "help", option.dest), value)) print_tabular(data)
def check_missing_files(self): """ check that all files on the filesystem are known to Cfg """ cfg = self.core.plugins['Cfg'] # first, collect ignore patterns from handlers ignore = [] for hdlr in handlers(): ignore.extend(hdlr.__ignore__) # next, get a list of all non-ignored files on the filesystem all_files = set() for root, _, files in os.walk(cfg.data): all_files.update(os.path.join(root, fname) for fname in files if not any(fname.endswith("." + i) for i in ignore)) # next, get a list of all files known to Cfg cfg_files = set() for root, eset in cfg.entries.items(): cfg_files.update(os.path.join(cfg.data, root.lstrip("/"), fname) for fname in eset.entries.keys()) # finally, compare the two unknown_files = all_files - cfg_files extra_files = cfg_files - all_files if unknown_files: self.LintError( "unknown-cfg-files", "Files on the filesystem could not be understood by Cfg: %s" % "; ".join(unknown_files)) if extra_files: self.LintError( "extra-cfg-files", "Cfg has entries for files that do not exist on the " "filesystem: %s\nThis is probably a bug." % "; ".join(extra_files))
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 _group_allowed(self, group): """ Determine if the named group can be set as a probe group by checking the regexes listed in the [probes] groups_allowed setting """ return any( r.match(group) for r in Bcfg2.Options.setup.probes_allowed_groups)
def skip_path(path): return any(fnmatch(path, p) or fnmatch(os.path.basename(path), p) for p in Bcfg2.Options.setup.ignore_files)
def Decide(self): # pylint: disable=R0912 """Set self.whitelist based on user interaction.""" iprompt = "Install %s: %s? (y/N): " rprompt = "Remove %s: %s? (y/N): " if Bcfg2.Options.setup.remove: if Bcfg2.Options.setup.remove == 'all': self.removal = self.extra elif Bcfg2.Options.setup.remove == 'services': self.removal = [entry for entry in self.extra if entry.tag == 'Service'] elif Bcfg2.Options.setup.remove == 'packages': self.removal = [entry for entry in self.extra if entry.tag == 'Package'] elif Bcfg2.Options.setup.remove == 'users': self.removal = [entry for entry in self.extra if entry.tag in ['POSIXUser', 'POSIXGroup']] candidates = [entry for entry in self.states if not self.states[entry]] if Bcfg2.Options.setup.dry_run: if self.whitelist: self.logger.info("In dryrun mode: " "suppressing entry installation for:") self.logger.info(["%s:%s" % (entry.tag, entry.get('name')) for entry in self.whitelist]) self.whitelist = [] if self.removal: self.logger.info("In dryrun mode: " "suppressing entry removal for:") self.logger.info(["%s:%s" % (entry.tag, entry.get('name')) for entry in self.removal]) self.removal = [] # Here is where most of the work goes # first perform bundle filtering all_bundle_names = [b.get('name') for b in self.config.findall('./Bundle')] bundles = self.config.getchildren() if Bcfg2.Options.setup.only_bundles: # warn if non-existent bundle given for bundle in Bcfg2.Options.setup.only_bundles: if bundle not in all_bundle_names: self.logger.info("Warning: Bundle %s not found" % bundle) bundles = [b for b in bundles if b.get('name') in Bcfg2.Options.setup.only_bundles] if Bcfg2.Options.setup.except_bundles: # warn if non-existent bundle given if not Bcfg2.Options.setup.bundle_quick: for bundle in Bcfg2.Options.setup.except_bundles: if bundle not in all_bundle_names: self.logger.info("Warning: Bundle %s not found" % bundle) bundles = [ b for b in bundles if b.get('name') not in Bcfg2.Options.setup.except_bundles] self.whitelist = [e for e in self.whitelist if any(e in b for b in bundles)] # first process prereq actions for bundle in bundles[:]: if bundle.tag == 'Bundle': bmodified = any((item in self.whitelist or item in self.modified) for item in bundle) else: bmodified = False actions = [a for a in bundle.findall('./Action') if (a.get('timing') in ['pre', 'both'] and (bmodified or a.get('when') == 'always'))] # now we process all "pre" and "both" actions that are either # always or the bundle has been modified if Bcfg2.Options.setup.interactive: self.promptFilter(iprompt, actions) self.DispatchInstallCalls(actions) if bundle.tag != 'Bundle': continue # need to test to fail entries in whitelist if not all(self.states[a] for a in actions): # then display bundles forced off with entries self.logger.info("%s %s failed prerequisite action" % (bundle.tag, bundle.get('name'))) bundles.remove(bundle) b_to_remv = [ent for ent in self.whitelist if ent in bundle] if b_to_remv: self.logger.info("Not installing entries from %s %s" % (bundle.tag, bundle.get('name'))) self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in b_to_remv]) for ent in b_to_remv: self.whitelist.remove(ent) self.logger.debug("Installing entries in the following bundle(s):") self.logger.debug(" %s" % ", ".join(b.get("name") for b in bundles if b.get("name"))) if Bcfg2.Options.setup.interactive: self.whitelist = self.promptFilter(iprompt, self.whitelist) self.removal = self.promptFilter(rprompt, self.removal) for entry in candidates: if entry not in self.whitelist: self.blacklist.append(entry)
def passes_black_list(entry, blacklist): """ Return True if (<entry tag>, <entry name>) is not in the given blacklist. """ return not any(matches_entry(be, (entry.tag, entry.get('name'))) for be in blacklist)
def matches_white_list(entry, whitelist): """ Return True if (<entry tag>, <entry name>) is in the given whitelist. """ return any(matches_entry(we, (entry.tag, entry.get('name'))) for we in whitelist)
def has_pulp_sources(self): """ True if there are any Pulp sources to handle, False otherwise """ return any(s.pulp_id for s in self)
def skip_path(path): return any( fnmatch(path, p) or fnmatch(os.path.basename(path), p) for p in Bcfg2.Options.setup.ignore_files)
def _group_allowed(self, group): """ Determine if the named group can be set as a probe group by checking the regexes listed in the [probes] groups_allowed setting """ return any(r.match(group) for r in Bcfg2.Options.setup.probes_allowed_groups)