From dab1d03d81c538966d03fb9318a4588a9e803b44 Mon Sep 17 00:00:00 2001 From: Sol Jerome Date: Sat, 24 Mar 2012 11:20:07 -0500 Subject: Allow to run directly from a git checkout (#1037) Signed-off-by: Sol Jerome --- src/lib/Client/Frame.py | 454 ------------------------------------------------ 1 file changed, 454 deletions(-) delete mode 100644 src/lib/Client/Frame.py (limited to 'src/lib/Client/Frame.py') diff --git a/src/lib/Client/Frame.py b/src/lib/Client/Frame.py deleted file mode 100644 index 67222f245..000000000 --- a/src/lib/Client/Frame.py +++ /dev/null @@ -1,454 +0,0 @@ -""" -Frame is the Client Framework that verifies and -installs entries, and generates statistics. -""" - -import logging -import sys -import time -import Bcfg2.Client.Tools - - -def cmpent(ent1, ent2): - """Sort entries.""" - if ent1.tag != ent2.tag: - return cmp(ent1.tag, ent2.tag) - else: - return cmp(ent1.get('name'), ent2.get('name')) - - -def promptFilter(prompt, entries): - """Filter a supplied list based on user input.""" - ret = [] - entries.sort(cmpent) - for entry in entries[:]: - if 'qtext' in entry.attrib: - iprompt = entry.get('qtext') - else: - iprompt = prompt % (entry.tag, entry.get('name')) - try: - # py3k compatibility - try: - ans = raw_input(iprompt.encode(sys.stdout.encoding, 'replace')) - except NameError: - ans = input(iprompt) - if ans in ['y', 'Y']: - ret.append(entry) - except EOFError: - # python 2.4.3 on CentOS doesn't like ^C for some reason - break - except: - print("Error while reading input") - continue - return ret - - -def matches_entry(entryspec, entry): - # both are (tag, name) - if entryspec == entry: - return True - else: - for i in [0, 1]: - if entryspec[i] == entry[i]: - continue - elif entryspec[i] == '*': - continue - elif '*' in entryspec[i]: - starpt = entryspec[i].index('*') - if entry[i].startswith(entryspec[i][:starpt]): - continue - return False - return True - - -def matches_white_list(entry, whitelist): - return True in [matches_entry(we, (entry.tag, entry.get('name'))) - for we in whitelist] - - -def passes_black_list(entry, blacklist): - return True not in [matches_entry(be, (entry.tag, entry.get('name'))) - for be in blacklist] - - -class Frame: - """Frame is the container for all Tool objects and state information.""" - def __init__(self, config, setup, times, drivers, dryrun): - self.config = config - self.times = times - self.dryrun = dryrun - self.times['initialization'] = time.time() - self.setup = setup - self.tools = [] - self.states = {} - self.whitelist = [] - self.blacklist = [] - self.removal = [] - self.logger = logging.getLogger("Bcfg2.Client.Frame") - for driver in drivers[:]: - if driver not in Bcfg2.Client.Tools.drivers 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(self.logger, setup, 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', []): - [self.tools.remove(item) for item in self.tools \ - if item.name == conflict] - - self.logger.info("Loaded tool drivers:") - self.logger.info([tool.name for tool in self.tools]) - - # find entries not handled by any tools - problems = [entry for struct in config for \ - entry in struct if entry not in self.handled] - - if problems: - self.logger.error("The following entries are not handled by any tool:") - self.logger.error(["%s:%s:%s" % (entry.tag, entry.get('type'), \ - entry.get('name')) for entry in problems]) - self.logger.error("") - entries = [(entry.tag, entry.get('name')) - for struct in config for entry in struct] - pkgs = [(entry.get('name'), entry.get('origin')) - for struct in config for entry in struct if entry.tag == 'Package'] - multi = [] - for entry in entries[:]: - if entries.count(entry) > 1: - multi.append(entry) - entries.remove(entry) - if multi: - self.logger.debug("The following entries are included multiple times:") - self.logger.debug(["%s:%s" % entry for entry in multi]) - self.logger.debug("") - if pkgs: - self.logger.debug("The following packages are specified in bcfg2:") - self.logger.debug([pkg[0] for pkg in pkgs if pkg[1] == 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']) - - def __getattr__(self, name): - if name in ['extra', 'handled', 'modified', '__important__']: - ret = [] - for tool in self.tools: - ret += getattr(tool, name) - return ret - elif name in self.__dict__: - return self.__dict__[name] - raise AttributeError(name) - - def InstallImportant(self): - """Install important entries - - We also process the decision mode stuff here because we want to prevent - non-whitelisted/blacklisted 'important' entries from being installed - prior to determining the decision mode on the client. - """ - # Need to process decision stuff early so that dryrun mode works with it - self.whitelist = [entry for entry in self.states \ - if not self.states[entry]] - if not self.setup['file']: - if self.setup['decision'] == 'whitelist': - dwl = self.setup['decision_list'] - w_to_rem = [e for e in self.whitelist \ - if not matches_white_list(e, dwl)] - if w_to_rem: - self.logger.info("In whitelist mode: suppressing installation of:") - self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in w_to_rem]) - self.whitelist = [x for x in self.whitelist \ - if x not in w_to_rem] - elif self.setup['decision'] == 'blacklist': - b_to_rem = [e for e in self.whitelist \ - if not passes_black_list(e, self.setup['decision_list'])] - if b_to_rem: - self.logger.info("In blacklist mode: suppressing installation of:") - self.logger.info(["%s:%s" % (e.tag, e.get('name')) for e in b_to_rem]) - self.whitelist = [x for x in self.whitelist if x not in b_to_rem] - - # take care of important entries first - if not self.dryrun and not self.setup['bundle']: - for cfile in [cfl for cfl in self.config.findall(".//Path") \ - if cfl.get('name') in self.__important__ and \ - cfl.get('type') == 'file']: - if cfile not in self.whitelist: - continue - tl = [t for t in self.tools if t.handlesEntry(cfile) \ - and t.canVerify(cfile)] - if tl: - if self.setup['interactive'] and not \ - promptFilter("Install %s: %s? (y/N):", [cfile]): - self.whitelist.remove(cfile) - continue - try: - self.states[cfile] = tl[0].InstallPath(cfile) - if self.states[cfile]: - tl[0].modified.append(cfile) - except: - self.logger.error("Unexpected tool failure", - exc_info=1) - cfile.set('qtext', '') - if tl[0].VerifyPath(cfile, []): - self.whitelist.remove(cfile) - - def Inventory(self): - """ - Verify all entries, - find extra entries, - and build up workqueues - - """ - # initialize all states - for struct in self.config.getchildren(): - for entry in struct.getchildren(): - self.states[entry] = False - for tool in self.tools: - try: - tool.Inventory(self.states) - except: - self.logger.error("%s.Inventory() call failed:" % tool.name, exc_info=1) - - def Decide(self): - """Set self.whitelist based on user interaction.""" - prompt = "Install %s: %s? (y/N): " - rprompt = "Remove %s: %s? (y/N): " - if self.setup['remove']: - if self.setup['remove'] == 'all': - self.removal = self.extra - elif self.setup['remove'] in ['services', 'Services']: - self.removal = [entry for entry in self.extra - if entry.tag == 'Service'] - elif self.setup['remove'] in ['packages', 'Packages']: - self.removal = [entry for entry in self.extra - if entry.tag == 'Package'] - - candidates = [entry for entry in self.states - if not self.states[entry]] - - if self.dryrun: - 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 = [] - return - # Here is where most of the work goes - # first perform bundle filtering - if self.setup['bundle']: - all_bundle_names = [b.get('name') for b in - self.config.findall('./Bundle')] - # warn if non-existent bundle given - for bundle in self.setup['bundle']: - if bundle not in all_bundle_names: - self.logger.info("Warning: Bundle %s not found" % bundle) - bundles = [b for b in self.config.findall('./Bundle') - if b.get('name') in self.setup['bundle']] - self.whitelist = [e for e in self.whitelist - if True in [e in b for b in bundles]] - elif self.setup['indep']: - bundles = [nb for nb in self.config.getchildren() - if nb.tag != 'Bundle'] - else: - bundles = self.config.getchildren() - - # first process prereq actions - for bundle in bundles[:]: - if bundle.tag != 'Bundle': - continue - bmodified = len([item for item in bundle if item in self.whitelist]) - actions = [a for a in bundle.findall('./Action') - if (a.get('timing') != 'post' and - (bmodified or a.get('when') == 'always'))] - # now we process all "always actions" - if self.setup['interactive']: - promptFilter(prompt, actions) - self.DispatchInstallCalls(actions) - - # need to test to fail entries in whitelist - if False in [self.states[a] for a in actions]: - # then display bundles forced off with entries - self.logger.info("Bundle %s failed prerequisite action" % - (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 Bundle %s" % - (bundle.get('name'))) - self.logger.info(["%s:%s" % (e.tag, e.get('name')) - for e in b_to_remv]) - [self.whitelist.remove(ent) for ent in b_to_remv] - - if self.setup['interactive']: - self.whitelist = promptFilter(prompt, self.whitelist) - self.removal = promptFilter(rprompt, self.removal) - - for entry in candidates: - if entry not in self.whitelist: - self.blacklist.append(entry) - - def DispatchInstallCalls(self, entries): - """Dispatch install calls to underlying tools.""" - for tool in self.tools: - handled = [entry for entry in entries if tool.canInstall(entry)] - if not handled: - continue - try: - tool.Install(handled, self.states) - except: - self.logger.error("%s.Install() call failed:" % tool.name, exc_info=1) - - def Install(self): - """Install all entries.""" - self.DispatchInstallCalls(self.whitelist) - mods = self.modified - mbundles = [struct for struct in self.config.findall('Bundle') if \ - [mod 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]) - self.logger.info("") - tbm = [(t, b) for t in self.tools for b in mbundles] - for tool, bundle in tbm: - try: - tool.Inventory(self.states, [bundle]) - except: - 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 self.setup['interactive']: - self.DispatchInstallCalls(clobbered) - - for bundle in self.config.findall('.//Bundle'): - if self.setup['bundle'] and \ - bundle.get('name') not in self.setup['bundle']: - # prune out unspecified bundles when running with -b - continue - for tool in self.tools: - try: - if bundle in mbundles: - tool.BundleUpdated(bundle, self.states) - else: - tool.BundleNotUpdated(bundle, self.states) - except: - self.logger.error("%s.BundleNotUpdated() call failed:" % \ - (tool.name), exc_info=1) - - def Remove(self): - """Remove extra entries.""" - for tool in self.tools: - extras = [entry for entry in self.removal if tool.handlesEntry(entry)] - if extras: - try: - tool.Remove(extras) - except: - self.logger.error("%s.Remove() failed" % tool.name, exc_info=1) - - def CondDisplayState(self, phase): - """Conditionally print tracing information.""" - self.logger.info('\nPhase: %s' % phase) - self.logger.info('Correct entries:\t%d' % list(self.states.values()).count(True)) - self.logger.info('Incorrect entries:\t%d' % list(self.states.values()).count(False)) - if phase == 'final' and list(self.states.values()).count(False): - self.logger.info(["%s:%s" % (entry.tag, entry.get('name')) for \ - entry in self.states if not self.states[entry]]) - self.logger.info('Total managed entries:\t%d' % len(list(self.states.values()))) - self.logger.info('Unmanaged entries:\t%d' % len(self.extra)) - if phase == 'final' and self.setup['extra']: - self.logger.info(["%s:%s" % (entry.tag, entry.get('name')) \ - for entry in self.extra]) - - self.logger.info("") - - if ((list(self.states.values()).count(False) == 0) and not self.extra): - self.logger.info('All entries correct.') - - def ReInventory(self): - """Recheck everything.""" - if not self.dryrun and self.setup['kevlar']: - self.logger.info("Rechecking system inventory") - self.Inventory() - - def Execute(self): - """Run all methods.""" - self.Inventory() - self.times['inventory'] = time.time() - self.CondDisplayState('initial') - self.InstallImportant() - self.Decide() - self.Install() - self.times['install'] = time.time() - self.Remove() - self.times['remove'] = time.time() - if self.modified: - self.ReInventory() - self.times['reinventory'] = time.time() - self.times['finished'] = time.time() - self.CondDisplayState('final') - - def GenerateStats(self): - """Generate XML summary of execution statistics.""" - feedback = Bcfg2.Client.XML.Element("upload-statistics") - stats = Bcfg2.Client.XML.SubElement(feedback, - 'Statistics', - total=str(len(self.states)), - client_version=__revision__, - version='2.0', - revision=self.config.get('revision', '-1')) - good = len([key for key, val in list(self.states.items()) if val]) - stats.set('good', str(good)) - if len([key for key, val in list(self.states.items()) if not val]) == 0: - stats.set('state', 'clean') - else: - stats.set('state', 'dirty') - - # List bad elements of the configuration - for (data, ename) in [(self.modified, 'Modified'), (self.extra, "Extra"), \ - ([entry for entry in self.states if not \ - self.states[entry]], "Bad")]: - container = Bcfg2.Client.XML.SubElement(stats, ename) - for item in data: - item.set('qtext', '') - container.append(item) - item.text = None - - timeinfo = Bcfg2.Client.XML.Element("OpStamps") - feedback.append(stats) - for (event, timestamp) in list(self.times.items()): - timeinfo.set(event, str(timestamp)) - stats.append(timeinfo) - return feedback -- cgit v1.2.3-1-g7c22