diff options
Diffstat (limited to 'src/sbin/bcfg2-info')
-rwxr-xr-x | src/sbin/bcfg2-info | 255 |
1 files changed, 149 insertions, 106 deletions
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info index 656532155..617584d3d 100755 --- a/src/sbin/bcfg2-info +++ b/src/sbin/bcfg2-info @@ -1,17 +1,17 @@ #!/usr/bin/env python - """This tool loads the Bcfg2 core into an interactive debugger.""" -__revision__ = '$Revision$' -from code import InteractiveConsole +import os +import sys import cmd import errno import getopt +import fnmatch import logging -import lxml.etree -import os -import sys import tempfile +import lxml.etree +import traceback +from code import InteractiveConsole try: try: @@ -27,14 +27,20 @@ import Bcfg2.Logger import Bcfg2.Options import Bcfg2.Server.Core import Bcfg2.Server.Plugins.Metadata -import Bcfg2.Server.Plugins.SGenshi import Bcfg2.Server.Plugin +try: + import Bcfg2.Server.Plugins.SGenshi + has_genshi = True +except ImportError: + has_genshi = False + logger = logging.getLogger('bcfg2-info') USAGE = """Commands: build <hostname> <filename> - Build config for hostname, writing to filename builddir <hostname> <dirname> - Build config for hostname, writing separate files to dirname -buildall <directory> - Build configs for all clients in directory +buildall <directory> [<hostnames*>] - Build configs for all clients in directory +buildallfile <directory> <filename> [<hostnames*>] - Build config file for all clients in directory buildfile <filename> <hostname> - Build config file for hostname (not written to disk) buildbundle <bundle> <hostname> - Render a templated bundle for hostname (not written to disk) bundles - Print out group/bundle information @@ -51,8 +57,7 @@ profile <command> <args> - Profile a single bcfg2-info command quit - Exit the bcfg2-info command line showentries <hostname> <type> - Show abstract configuration entries for a given host showclient <client1> <client2> - Show metadata for given hosts -update - Process pending file events -version - Print version of this tool""" +update - Process pending file events""" BUILDDIR_USAGE = """Usage: builddir [-f] <hostname> <output dir> @@ -78,10 +83,12 @@ class mockLog(object): def debug(self, *args, **kwargs): pass + class dummyError(Exception): """This is just a dummy.""" pass + class FileNotBuilt(Exception): """Thrown when File entry contains no content.""" def __init__(self, value): @@ -90,6 +97,30 @@ class FileNotBuilt(Exception): def __str__(self): return repr(self.value) + +def getClientList(hostglobs): + """ given a host glob, get a list of clients that match it """ + # special cases to speed things up: + if '*' in hostglobs: + return list(self.metadata.clients.keys()) + has_wildcards = False + for glob in hostglobs: + # check if any wildcard characters are in the string + if set('*?[]') & set(glob): + has_wildcards = True + break + if not has_wildcards: + return hostglobs + + rv = set() + clist = set(self.metadata.clients.keys()) + for glob in hostglobs: + for client in clist: + if fnmatch.fnmatch(client, glob): + rv.update(client) + clist.difference_update(rv) + return list(rv) + def printTabular(rows): """Print data in tabular format.""" cmax = tuple([max([len(str(row[index])) for row in rows]) + 1 \ @@ -109,12 +140,12 @@ def displayTrace(trace, num=80, sort=('time', 'calls')): class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): """Main class for bcfg2-info.""" def __init__(self, repo, plgs, passwd, encoding, event_debug, - cfile='/etc/bcfg2.conf', filemonitor='default'): + filemonitor='default', setup=None): cmd.Cmd.__init__(self) try: Bcfg2.Server.Core.Core.__init__(self, repo, plgs, passwd, - encoding, cfile=cfile, - filemonitor=filemonitor) + encoding, filemonitor=filemonitor, + setup=setup) if event_debug: self.fam.debug = True except Bcfg2.Server.Core.CoreInitError: @@ -177,7 +208,11 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): else: raise ImportError except ImportError: - sh.interact() + try: + import bpython.cli + bpython.cli.main(locals_=locals()) + except ImportError: + sh.interact() def do_quit(self, _): """ @@ -199,10 +234,6 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): """Process pending filesystem events.""" self.fam.handle_events_in_interval(0.1) - def do_version(self, _): - """Print out code version.""" - print(__revision__) - def do_build(self, args): """Build client configuration.""" alist = args.split() @@ -260,50 +291,101 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): def do_buildall(self, args): alist = args.split() - flags = [] - for arg in alist: - if arg == '-f': - alist.remove('-f') - flags.append(arg) - if len(alist) != 1: - print("Usage: buildall [-f] <directory>") + if len(alist) < 1: + print("Usage: buildall <directory> [<hostnames*>]") return - if not os.path.exists(alist[0]): - try: - os.mkdir(alist[0]) - except OSError: - err = sys.exc_info()[1] - logger.error("Could not create %s: %s" % (alist[0], err)) - for client in self.metadata.clients: - self.do_build("%s %s %s/%s.xml" % (" ".join(flags), - client, args, client)) + + destdir = alist[0] + try: + os.mkdir(destdir) + except OSError: + err = sys.exc_info()[1] + if err.errno != 17: + print("Could not create %s: %s" % (destdir, err)) + if len(alist) > 1: + clients = getClientList(alist[1:]) + else: + clients = list(self.metadata.clients.keys()) + for client in clients: + self.do_build("%s %s" % (client, os.path.join(destdir, + client + ".xml"))) + + def do_buildallfile(self, args): + """Build a config file for all clients.""" + usage = 'Usage: buildallfile [--altsrc=<altsrc>] <directory> <filename> [<hostnames*>]' + try: + opts, args = getopt.gnu_getopt(args.split(), '', ['altsrc=']) + except: + print(usage) + return + altsrc = None + for opt in opts: + if opt[0] == '--altsrc': + altsrc = opt[1] + if len(args) < 2: + print(usage) + return + + destdir = args[0] + filename = args[1] + try: + os.mkdir(destdir) + except OSError: + err = sys.exc_info()[1] + if err.errno != 17: + print("Could not create %s: %s" % (destdir, err)) + if len(args) > 2: + clients = getClientList(args[1:]) + else: + clients = list(self.metadata.clients.keys()) + if altsrc: + args = "--altsrc %s -f %%s %%s %%s" % altsrc + else: + args = "-f %s %s %s" + for client in clients: + self.do_buildfile(args % (os.path.join(destdir, client), + filename, client)) def do_buildfile(self, args): """Build a config file for client.""" - usage = 'Usage: buildfile [--altsrc=<altsrc>] filename hostname' + usage = 'Usage: buildfile [-f <outfile>] [--altsrc=<altsrc>] filename hostname' try: - opts, alist = getopt.gnu_getopt(args.split(), '', ['altsrc=']) + opts, alist = getopt.gnu_getopt(args.split(), 'f:', ['altsrc=']) except: print(usage) return altsrc = None + outfile = None for opt in opts: if opt[0] == '--altsrc': altsrc = opt[1] - if len(alist) == 2: - fname, client = alist - entry = lxml.etree.Element('Path', type='file', name=fname) - if altsrc: - entry.set("altsrc", altsrc) - try: - metadata = self.build_metadata(client) - self.Bind(entry, metadata) - print(lxml.etree.tostring(entry, encoding="UTF-8", - xml_declaration=True)) - except: - print("Failed to build entry %s for host %s" % (fname, client)) - else: + elif opt[0] == '-f': + outfile = opt[1] + if len(alist) != 2: print(usage) + return + + fname, client = alist + entry = lxml.etree.Element('Path', type='file', name=fname) + if altsrc: + entry.set("altsrc", altsrc) + try: + metadata = self.build_metadata(client) + self.Bind(entry, metadata) + data = lxml.etree.tostring(entry, encoding="UTF-8", + xml_declaration=True) + if outfile: + open(outfile, 'w').write(data) + else: + print(data) + except IOError: + err = sys.exc_info()[1] + print("Could not write to %s: %s" % (outfile, err)) + print(data) + except Exception: + print("Failed to build entry %s for host %s: %s" % + (fname, client, traceback.format_exc().splitlines()[-1])) + raise def do_buildbundle(self, args): """Render a bundle for client.""" @@ -313,8 +395,9 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): metadata = self.build_metadata(client) if bname in self.plugins['Bundler'].entries: bundle = self.plugins['Bundler'].entries[bname] - if isinstance(bundle, - Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile): + if (has_genshi and + isinstance(bundle, + Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile)): stream = bundle.template.generate(metadata=metadata) print(stream.render("xml")) else: @@ -523,31 +606,8 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): print("Unable to build metadata for host %s" % args) return collection = self.plugins['Packages']._get_collection(metadata) - for source in collection.sources: - # get_urls() loads url_map as a side-effect - source.get_urls() - for url_map in source.url_map: - for arch in url_map['arches']: - # make sure client is in all the proper arch groups - if arch not in metadata.groups: - continue - reponame = source.get_repo_name(url_map) - print("Name: %s" % reponame) - print(" Type: %s" % source.ptype) - if url_map['url'] != '': - print(" URL: %s" % url_map['url']) - elif url_map['rawurl'] != '': - print(" RAWURL: %s" % url_map['rawurl']) - if source.gpgkeys: - print(" GPG Key(s): %s" % ", ".join(source.gpgkeys)) - else: - print(" GPG Key(s): None") - if len(source.blacklist): - print(" Blacklist: %s" % ", ".join(source.blacklist)) - if len(source.whitelist): - print(" Whitelist: %s" % ", ".join(source.whitelist)) - print("") - + print collection.sourcelist() + def do_profile(self, arg): """.""" if not have_profile: @@ -568,32 +628,16 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core): if __name__ == '__main__': Bcfg2.Logger.setup_logging('bcfg2-info', to_syslog=False) - optinfo = { - 'configfile': Bcfg2.Options.CFILE, - 'help': Bcfg2.Options.HELP, - 'event debug': Bcfg2.Options.DEBUG, - 'profile': Bcfg2.Options.CORE_PROFILE, - 'encoding': Bcfg2.Options.ENCODING, - # Server options - 'repo': Bcfg2.Options.SERVER_REPOSITORY, - 'plugins': Bcfg2.Options.SERVER_PLUGINS, - 'password': Bcfg2.Options.SERVER_PASSWORD, - 'mconnect': Bcfg2.Options.SERVER_MCONNECT, - 'filemonitor': Bcfg2.Options.SERVER_FILEMONITOR, - 'location': Bcfg2.Options.SERVER_LOCATION, - 'static': Bcfg2.Options.SERVER_STATIC, - 'key': Bcfg2.Options.SERVER_KEY, - 'cert': Bcfg2.Options.SERVER_CERT, - 'ca': Bcfg2.Options.SERVER_CA, - 'password': Bcfg2.Options.SERVER_PASSWORD, - 'protocol': Bcfg2.Options.SERVER_PROTOCOL, - # More options - 'logging': Bcfg2.Options.LOGGING_FILE_PATH, - 'interactive': Bcfg2.Options.INTERACTIVE, - } + optinfo = dict(profile=Bcfg2.Options.CORE_PROFILE, + mconnect=Bcfg2.Options.SERVER_MCONNECT, + interactive=Bcfg2.Options.INTERACTIVE) + optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) + optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) setup = Bcfg2.Options.OptionParser(optinfo) - setup.hm = "Usage:\n %s\n%s" % (setup.buildHelpMessage(), - USAGE) + setup.hm = "\n".join([" bcfg2-info [options] [command <command args>]", + "Options:", + setup.buildHelpMessage(), + USAGE]) setup.parse(sys.argv[1:]) if setup['args'] and setup['args'][0] == 'help': @@ -603,15 +647,14 @@ if __name__ == '__main__': prof = profile.Profile() loop = prof.runcall(infoCore, setup['repo'], setup['plugins'], setup['password'], setup['encoding'], - setup['event debug'], cfile=setup['configfile'], - filemonitor=setup['filemonitor']) + setup['debug'], setup['filemonitor'], + setup) displayTrace(prof) else: if setup['profile']: print("Profiling functionality not available.") loop = infoCore(setup['repo'], setup['plugins'], setup['password'], - setup['encoding'], setup['event debug'], - cfile=setup['configfile'], - filemonitor=setup['filemonitor']) + setup['encoding'], setup['debug'], + setup['filemonitor'], setup) loop.Run(setup['args']) |