#!/usr/bin/python -i '''This tool loads the Bcfg2 core into an interactive debugger''' __revision__ = '$Revision$' import copy, logging, lxml.etree, sys, time, cmd import Bcfg2.Logger, Bcfg2.Server.Core, os import Bcfg2.Server.Plugins.Metadata, Bcfg2.Server.Plugin import Bcfg2.Options logger = logging.getLogger('bcfg2-info') class dummyError(Exception): pass def printTabular(rows): '''print data in tabular format''' cmax = tuple([max([len(str(row[index])) for row in rows]) + 1 for index in xrange(len(rows[0]))]) fstring = (" %%-%ss |" * len(cmax)) % cmax fstring = ('|'.join([" %%-%ss "] * len(cmax))) % cmax print fstring % rows[0] print (sum(cmax) + (len(cmax) * 2) + (len(cmax) - 1)) * '=' for row in rows[1:]: print fstring % row class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): def __init__(self, repo, plgs, struct, gens, passwd, svn, encoding, event_debug): cmd.Cmd.__init__(self) try: Bcfg2.Server.Core.Core.__init__(self, repo, plgs, struct, gens, passwd, svn, encoding) if event_debug: self.fam.debug = True except Bcfg2.Server.Core.CoreInitError, msg: print "Core load failed because %s" % msg raise SystemExit(1) self.prompt = '> ' self.cont = True i = 0 while self.fam.Service() or i < 5: i += 1 def do_loop(self): self.cont = True while self.cont: try: self.cmdloop() except SystemExit, val: raise except Bcfg2.Server.Plugin.PluginExecutionError: continue except dummyError: continue except: logger.error("command failure", exc_info=1) def do_debug(self, _): self.cont = False print "dropping to python interpreter; run loop.do_loop() to resume" raise dummyError def do_quit(self, _): """Exit program. Usage: [quit|exit]""" raise SystemExit(0) do_EOF = do_quit do_exit = do_quit def do_help(self, _): '''print out usage info''' print 'Commands:' print 'build - build config for hostname, writing to filename' print 'buildall - build configs for all clients in directory' print 'buildfile - build config file for hostname (not written to disk)' print 'bundles - print out group/bundle information' print 'clients - print out client/profile information' print 'debug - shell out to native python interpreter' print 'event_debug - display filesystem events as they are processed' print 'generators - list current versions of generators' print 'groups - list groups' print 'help - print this text' print 'mappings - print generator mappings for optional type and name' print 'quit' print 'showentries - show abstract configuration entries for a given host' print 'showclient - show metadata for given hosts' print 'update - process pending file events' print 'version - print version of this tool' def do_update(self, _): '''Process pending fs events''' self.fam.Service() def do_version(self, _): '''print out code version''' print __revision__ def do_build(self, args): '''build client configuration''' if len(args.split()) == 2: client, ofile = args.split() output = open(ofile, 'w') data = lxml.etree.tostring(self.BuildConfiguration(client), encoding='UTF-8', xml_declaration=True) output.write(data) output.close() else: print 'Usage: build ' def do_buildall(self, args): if len(args.split()) != 1: print "Usage: buildall " return try: os.mkdir(args) except: pass for client in self.metadata.clients: self.do_build("%s %s/%s.xml" % (client, args, client)) def do_buildfile(self, args): '''build a config file for client''' if len(args.split()) == 2: fname, client = args.split() entry = lxml.etree.Element('ConfigFile', name=fname) metadata = self.metadata.get_metadata(client) self.Bind(entry, metadata) print lxml.etree.tostring(entry, encoding="UTF-8", xml_declaration=True) else: print 'Usage: buildfile filename hostname' def do_bundles(self, _): '''print out group/bundle info''' data = [('Group', 'Bundles')] groups = self.metadata.groups.keys() groups.sort() for group in groups: data.append((group, ','.join(self.metadata.groups[group][0]))) printTabular(data) def do_clients(self, _): '''print out client info''' data = [('Client', 'Profile')] clist = self.metadata.clients.keys() clist.sort() for client in clist: data.append((client, self.metadata.clients[client])) printTabular(data) def do_generators(self, _): '''print out generator info''' for generator in self.generators: print generator.__version__ def do_showentries(self, args): '''show abstract configuration entries for a given host''' arglen = len(args.split()) if arglen not in [2, 3]: print "Usage: showentries " return client = args.split()[0] try: meta = self.metadata.get_metadata(client) except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError: print "Unable to find metadata for host %s" % client return structures = self.GetStructures(meta) output = [('entrytype', 'name')] if arglen == 1: for item in structures: for child in item.getchildren(): output.append((child.tag, child.get('name'))) if arglen == 2: etype = args.split()[1] for item in structures: for child in item.getchildren(): if child.tag in [etype, "Bound%s" % etype]: output.append((child.tag, child.get('name'))) printTabular(output) def do_groups(self, _): '''print out group info''' data = [("Groups", "Profile", "Category", "Contains")] grouplist = self.metadata.groups.keys() grouplist.sort() for group in grouplist: if group in self.metadata.profiles: prof = 'yes' else: prof = 'no' if group in self.metadata.categories: cat = self.metadata.categories[group] else: cat = '' gdata = [grp for grp in self.metadata.groups[group][1]] if group in gdata: gdata.remove(group) data.append((group, prof, cat, ','.join(gdata))) printTabular(data) def complete_showclient(self, text, line, begidx, endidx): return [entry for entry in self.metadata.clients \ if entry.startswith(text)] def do_showclient(self, args): ''' print host metadata''' data = [('Client', 'Profile', "Groups", "Bundles")] if not len(args): print "Usage:\nshowclient ... " return for client in args.split(): if client not in self.metadata.clients: print "Client %s not defined" % client continue profile = self.metadata.clients[client] bds, gps, cgs = \ copy.deepcopy(self.metadata.groups[profile]) gps.remove(profile) numbundles = len(bds) numgroups = len(gps) num = max((numbundles, numgroups)) for i in range(0, num): if i == 0: c = client p = profile else: c = "" p = "" if i < numbundles: b = bds[i] else: b = "" if i < numgroups: g = gps[i] else: g = "" data.append((c, p, g, b)) if len(data) > 1: printTabular(data) def do_mappings(self, args): '''print out mapping info''' # dump all mappings unless type specified data = [('Plugin', 'Type', 'Name')] arglen = len(args.split()) for generator in self.generators: if arglen == 0: etypes = generator.Entries.keys() else: etypes = [args.split()[0]] if arglen == 2: interested = [(etype, [args.split()[1]]) \ for etype in etypes] else: interested = [(etype, generator.Entries[etype]) \ for etype in etypes \ if etype in generator.Entries] for etype, names in interested: for name in [name for name in names if name in \ generator.Entries.get(etype, {})]: data.append((generator.__name__, etype, name)) printTabular(data) def do_event_debug(self, args): self.fam.debug = True def do_cfgdebug(self, args): try: meta = self.metadata.get_metadata(args) except Bcfg2.Server.Plugins.Metadata.MetadataConsistencyError: print "Unable to find metadata for host %s" % client return structures = self.GetStructures(meta) for clist in [struct.findall('ConfigFile') for struct in structures]: for cfile in clist: if cfile.get('name') in self.plugins['Cfg'].Entries['ConfigFile']: cset = self.plugins['Cfg'].entries[cfile.get('name')] cand = cset.get_matching(meta) fields = ['all', 'group'] while len(cand) > 1 and fields: field = fields.pop(0) [cand.remove(c) for c in cand[:] if getattr(c.specific, field)] if len(cand) != 1: print >>sys.stderr, "Entry %s failed" % cfile.get('name') continue print cand[0].name if __name__ == '__main__': Bcfg2.Logger.setup_logging('bcfg2-info', to_syslog=False) optinfo = { 'configfile': Bcfg2.Options.CFILE, 'help': Bcfg2.Options.HELP, } optinfo.update({'repo': Bcfg2.Options.SERVER_REPOSITORY, 'svn': Bcfg2.Options.SERVER_SVN, 'plugins': Bcfg2.Options.SERVER_PLUGINS, 'structures': Bcfg2.Options.SERVER_STRUCTURES, 'generators': Bcfg2.Options.SERVER_GENERATORS, 'password': Bcfg2.Options.SERVER_PASSWORD, 'event debug': Bcfg2.Options.DEBUG, 'encoding': Bcfg2.Options.ENCODING}) setup = Bcfg2.Options.OptionParser(optinfo) setup.parse(sys.argv[1:]) if "-d" in sys.argv: loop = infoCore(setup['repo'], setup['plugins'], setup['structures'], setup['generators'], setup['password'], setup['svn'], setup['encoding'], True) else: loop = infoCore(setup['repo'], setup['plugins'], setup['structures'], setup['generators'], setup['password'], setup['svn'], setup['encoding'], False) if "args" in setup and setup['args']: loop.onecmd(" ".join(setup['args'])) raise SystemExit(0) else: loop.do_loop()