def run_probes(self): """ run probes and upload probe data """ try: probes = XML.XML(str(self.proxy.GetProbes())) except (Proxy.ProxyError, Proxy.CertificateError, socket.gaierror, socket.error): err = sys.exc_info()[1] self.fatal_error("Failed to download probes from bcfg2: %s" % err) except XML.ParseError: err = sys.exc_info()[1] self.fatal_error("Server returned invalid probe requests: %s" % err) self.times['probe_download'] = time.time() # execute probes probedata = XML.Element("ProbeData") for probe in probes.findall(".//probe"): probedata.append(self.run_probe(probe)) if len(probes.findall(".//probe")) > 0: try: # upload probe responses self.proxy.RecvProbeData( XML.tostring(probedata, xml_declaration=False).decode('utf-8')) except Proxy.ProxyError: err = sys.exc_info()[1] self.fatal_error("Failed to upload probe data: %s" % err) self.times['probe_upload'] = time.time()
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 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 run_probe(self, probe): """Execute probe.""" name = probe.get('name') self.logger.info("Running probe %s" % name) ret = XML.Element("probe-data", name=name, source=probe.get('source')) try: scripthandle, scriptname = tempfile.mkstemp() if sys.hexversion >= 0x03000000: script = os.fdopen(scripthandle, 'w', encoding=Bcfg2.Options.setup.encoding) else: script = os.fdopen(scripthandle, 'w') try: script.write("#!%s\n" % (probe.attrib.get('interpreter', '/bin/sh'))) if sys.hexversion >= 0x03000000: script.write(probe.text) else: script.write(probe.text.encode('utf-8')) script.close() os.chmod(scriptname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH | stat.S_IWUSR) # 0755 rv = self.cmd.run(scriptname) if rv.stderr: self.logger.warning("Probe %s has error output: %s" % (name, rv.stderr)) if not rv.success: self._probe_failure(name, "Return value %s" % rv.retval) self.logger.info("Probe %s has result:" % name) self.logger.info(rv.stdout) if sys.hexversion >= 0x03000000: ret.text = rv.stdout else: ret.text = rv.stdout.decode('utf-8') finally: os.unlink(scriptname) except SystemExit: raise except: self._probe_failure(name, sys.exc_info()[1]) return ret
def parse_config(self, rawconfig): """ Parse the XML configuration received from the Bcfg2 server """ try: self.config = XML.XML(rawconfig) except XML.ParseError: syntax_error = sys.exc_info()[1] self.fatal_error("The configuration could not be parsed: %s" % syntax_error) self.load_tools() # find entries not handled by any tools self.unhandled = [ entry for struct in self.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'))) # find duplicates self.find_dups(self.config) pkgs = [(entry.get('name'), entry.get('origin')) for struct in self.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']) self.times['config_parse'] = time.time()
def run(self): """Perform client execution phase.""" # begin configuration self.times['start'] = time.time() self.logger.info("Starting Bcfg2 client run at %s" % self.times['start']) self.parse_config(self.get_config().decode('utf-8')) if self.config.tag == 'error': self.fatal_error("Server error: %s" % (self.config.text)) if Bcfg2.Options.setup.bundle_quick: newconfig = XML.XML('<Configuration/>') for bundle in self.config.getchildren(): name = bundle.get("name") if (name and (name in Bcfg2.Options.setup.only_bundles or name not in Bcfg2.Options.setup.except_bundles)): newconfig.append(bundle) self.config = newconfig if not Bcfg2.Options.setup.no_lock: # check lock here try: lockfile = open(Bcfg2.Options.setup.lockfile, 'w') if locked(lockfile.fileno()): self.fatal_error("Another instance of Bcfg2 is running. " "If you want to bypass the check, run " "with the -O/--no-lock option") except SystemExit: raise except: lockfile = None self.logger.error("Failed to open lockfile %s: %s" % (Bcfg2.Options.setup.lockfile, sys.exc_info()[1])) # execute the configuration self.Execute() if not Bcfg2.Options.setup.no_lock: # unlock here if lockfile: try: fcntl.lockf(lockfile.fileno(), fcntl.LOCK_UN) os.remove(Bcfg2.Options.setup.lockfile) except OSError: self.logger.error("Failed to unlock lockfile %s" % lockfile.name) if (not Bcfg2.Options.setup.file and not Bcfg2.Options.setup.bundle_quick): # upload statistics feedback = self.GenerateStats() try: self.proxy.RecvStats( XML.tostring(feedback, xml_declaration=False).decode('utf-8')) except Proxy.ProxyError: err = sys.exc_info()[1] self.logger.error("Failed to upload configuration statistics: " "%s" % err) raise SystemExit(2) self.logger.info("Finished Bcfg2 client run at %s" % time.time())
def Verifydirectory(self, entry, modlist): """Verify Path type='directory' entry.""" if entry.get('perms') == None or \ entry.get('owner') == None or \ entry.get('group') == None: self.logger.error("POSIX: Entry %s not completely specified. " "Try running bcfg2-lint." % (entry.get('name'))) return False while len(entry.get('perms', '')) < 4: entry.set('perms', '0' + entry.get('perms', '')) try: ondisk = os.stat(entry.get('name')) except OSError: entry.set('current_exists', 'false') self.logger.info("POSIX: %s %s does not exist" % (entry.tag, entry.get('name'))) return False try: owner = str(ondisk[stat.ST_UID]) group = str(ondisk[stat.ST_GID]) except (OSError, KeyError): self.logger.info("POSIX: User/Group resolution failed " "for path %s" % entry.get('name')) owner = 'root' group = '0' finfo = os.stat(entry.get('name')) perms = oct(finfo[stat.ST_MODE])[-4:] if entry.get('mtime', '-1') != '-1': mtime = str(finfo[stat.ST_MTIME]) else: mtime = '-1' pTrue = ((owner == str(normUid(entry))) and (group == str(normGid(entry))) and (perms == entry.get('perms')) and (mtime == entry.get('mtime', '-1'))) pruneTrue = True ex_ents = [] if entry.get('prune', 'false') == 'true' \ and (entry.tag == 'Path' and entry.get('type') == 'directory'): # check for any extra entries when prune='true' attribute is set try: entries = ['/'.join([entry.get('name'), ent]) \ for ent in os.listdir(entry.get('name'))] ex_ents = [e for e in entries if e not in modlist] if ex_ents: pruneTrue = False self.logger.info("POSIX: Directory %s contains " "extra entries:" % entry.get('name')) self.logger.info(ex_ents) nqtext = entry.get('qtext', '') + '\n' nqtext += "Directory %s contains extra entries: " % \ entry.get('name') nqtext += ":".join(ex_ents) entry.set('qtest', nqtext) [entry.append(XML.Element('Prune', path=x)) \ for x in ex_ents] except OSError: ex_ents = [] pruneTrue = True if not pTrue: if owner != str(normUid(entry)): entry.set('current_owner', owner) self.logger.debug("%s %s ownership wrong" % \ (entry.tag, entry.get('name'))) nqtext = entry.get('qtext', '') + '\n' nqtext += "%s owner wrong. is %s should be %s" % \ (entry.get('name'), owner, entry.get('owner')) entry.set('qtext', nqtext) if group != str(normGid(entry)): entry.set('current_group', group) self.logger.debug("%s %s group wrong" % \ (entry.tag, entry.get('name'))) nqtext = entry.get('qtext', '') + '\n' nqtext += "%s group is %s should be %s" % \ (entry.get('name'), group, entry.get('group')) entry.set('qtext', nqtext) if perms != entry.get('perms'): entry.set('current_perms', perms) self.logger.debug( "%s %s permissions are %s should be %s" % (entry.tag, entry.get('name'), perms, entry.get('perms'))) nqtext = entry.get('qtext', '') + '\n' nqtext += "%s %s perms are %s should be %s" % \ (entry.tag, entry.get('name'), perms, entry.get('perms')) entry.set('qtext', nqtext) if mtime != entry.get('mtime', '-1'): entry.set('current_mtime', mtime) self.logger.debug("%s %s mtime is %s should be %s" \ % (entry.tag, entry.get('name'), mtime, entry.get('mtime'))) nqtext = entry.get('qtext', '') + '\n' nqtext += "%s mtime is %s should be %s" % \ (entry.get('name'), mtime, entry.get('mtime')) entry.set('qtext', nqtext) if entry.get('type') != 'file': nnqtext = entry.get('qtext') nnqtext += '\nInstall %s %s: (y/N) ' % (entry.get('type'), entry.get('name')) entry.set('qtext', nnqtext) return pTrue and pruneTrue