summaryrefslogtreecommitdiffstats
path: root/build/scripts-2.7/bcfg2-info
diff options
context:
space:
mode:
Diffstat (limited to 'build/scripts-2.7/bcfg2-info')
-rwxr-xr-xbuild/scripts-2.7/bcfg2-info468
1 files changed, 468 insertions, 0 deletions
diff --git a/build/scripts-2.7/bcfg2-info b/build/scripts-2.7/bcfg2-info
new file mode 100755
index 000000000..9721122f3
--- /dev/null
+++ b/build/scripts-2.7/bcfg2-info
@@ -0,0 +1,468 @@
+#!/usr/bin/python
+
+"""This tool loads the Bcfg2 core into an interactive debugger."""
+__revision__ = '$Revision$'
+
+from code import InteractiveConsole
+import cmd
+import errno
+import getopt
+import logging
+import lxml.etree
+import os
+import sys
+import tempfile
+
+try:
+ import profile
+ import pstats
+ have_profile = True
+except:
+ have_profile = False
+
+import Bcfg2.Logger
+import Bcfg2.Options
+import Bcfg2.Server.Core
+import Bcfg2.Server.Plugins.Metadata
+import Bcfg2.Server.Plugin
+
+logger = logging.getLogger('bcfg2-info')
+
+class dummyError(Exception):
+ pass
+
+class FileNotBuilt(Exception):
+ """Thrown when File entry contains no content."""
+ def __init__(self, value):
+ Exception.__init__(self)
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+def printTabular(rows):
+ """Print data in tabular format."""
+ cmax = tuple([max([len(str(row[index])) for row in rows]) + 1 \
+ for index in range(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)
+
+def displayTrace(trace, num=80, sort=('time', 'calls')):
+ stats = pstats.Stats(trace)
+ stats.sort_stats('cumulative', 'calls', 'time')
+ stats.print_stats(200)
+
+def write_config_file(outputdir, cfg):
+ """Store file content of an <ConfigFile name='/path/to/file' ...>...</ConfigFile> entry
+ in the appropriate directory under the output directory.
+ """
+ name = cfg.get('name')
+
+ # directory creation
+ try:
+ os.makedirs(os.path.dirname(outputdir + name))
+ except OSError, err:
+ if err.errno != errno.EEXIST:
+ raise
+ except:
+ raise
+
+ # write config file
+ config_file = open(outputdir + name, "w")
+ try:
+ config_file.write(cfg.text)
+ except: # plugin throw an exception and therefore there is no content => None
+ raise FileNotBuilt(name)
+ config_file.close()
+
+class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
+
+ def __init__(self, repo, plgs, passwd, encoding, event_debug):
+ cmd.Cmd.__init__(self)
+ try:
+ Bcfg2.Server.Core.Core.__init__(self, repo, plgs, passwd,
+ 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
+ self.fam.handle_events_in_interval(4)
+
+ def do_loop(self):
+ self.cont = True
+ while self.cont:
+ try:
+ self.cmdloop('Welcome to bcfg2-info\n'
+ 'Type "help" for more information')
+ except SystemExit, val:
+ raise
+ except Bcfg2.Server.Plugin.PluginExecutionError:
+ continue
+ except KeyboardInterrupt:
+ print("Ctrl-C pressed exiting...")
+ self.do_exit([])
+ except dummyError:
+ continue
+ except:
+ logger.error("command failure", exc_info=1)
+
+ def do_debug(self, args):
+ try:
+ opts, _ = getopt.getopt(args.split(), 'nf:')
+ except:
+ print "Usage: debug [-n] [-f <command list>]"
+ return
+ self.cont = False
+ scriptmode = False
+ interactive = True
+ for opt in opts:
+ if opt[0] == '-f':
+ scriptmode = True
+ spath = opt[1]
+ elif opt[0] == '-n':
+ interactive = False
+ sh = InteractiveConsole(locals())
+ if scriptmode:
+ for command in [c.strip() for c in open(spath).readlines()]:
+ if command:
+ sh.push(command)
+ if interactive:
+ print("dropping to python interpreter; press ^D to resume")
+ try:
+ import IPython
+ shell = IPython.Shell.IPShell(argv=[], user_ns=locals())
+ shell.mainloop()
+ except ImportError:
+ sh.interact()
+
+ def do_quit(self, _):
+ """
+ Exit program.
+ Usage: [quit|exit]
+ """
+ for plugin in self.plugins.values():
+ plugin.shutdown()
+ os._exit(0)
+
+ do_EOF = do_quit
+ do_exit = do_quit
+
+ def do_help(self, _):
+ """Print out usage info."""
+ print 'Commands:'
+ print 'build <hostname> <filename> - build config for hostname, writing to filename'
+ print 'builddir <hostname> <dirname> - build config for hostname, writing separate files to dirname'
+ print 'buildall <directory> - build configs for all clients in directory'
+ print 'buildfile <filename> <hostname> - 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 list of available commands'
+ print 'mappings <type*> <name*> - print generator mappings for optional type and name'
+ print 'profile <command> <args> - profile a single bcfg2-info command'
+ print 'quit - Exit the bcfg2-info command line'
+ print 'showentries <hostname> <type> - show abstract configuration entries for a given host'
+ print 'showclient <client1> <client2> - 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.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()
+ path_force = False
+ if '-f' in args:
+ alist.remove('-f')
+ path_force = True
+ if len(alist) == 2:
+ client, ofile = alist
+ if not ofile.startswith('/tmp') and not path_force:
+ print("Refusing to write files outside of /tmp without -f option")
+ return
+ output = open(ofile, 'w')
+ data = lxml.etree.tostring(self.BuildConfiguration(client),
+ encoding='UTF-8', xml_declaration=True,
+ pretty_print=True)
+ output.write(data)
+ output.close()
+ else:
+ print('Usage: build [-f] <hostname> <output file>')
+
+ def help_builddir(self):
+ """Display help for builddir command."""
+ print('Usage: builddir [-f] <hostname> <output dir>')
+ print('')
+ print('Generates a config for client <hostname> and writes the')
+ print('individual configuration files out separately in a tree')
+ print('under <output dir>. The <output dir> directory must be')
+ print('rooted under /tmp unless the -f argument is provided, in')
+ print('which case it can be located anywhere.')
+ print('')
+ print('NOTE: Currently only handles ConfigFile entries and writes')
+ print('all content with the default owner and permissions. These')
+ print('could be much more permissive than would be created by the')
+ print('bcfg2 client itself.')
+
+ def do_builddir(self, args):
+ """Build client configuration as separate files within a dir."""
+ alist = args.split()
+ path_force = False
+ if '-f' in args:
+ alist.remove('-f')
+ path_force = True
+ if len(alist) == 2:
+ client, odir = alist
+ if not odir.startswith('/tmp') and not path_force:
+ print("Refusing to write files outside of /tmp without -f option")
+ return
+ client_config = self.BuildConfiguration(client)
+ if client_config.tag == 'error':
+ print("Building client configuration failed.")
+ return
+
+ # handle <Path type='file'> entries
+ for configfile in [cfile for cfile in client_config.findall(".//Path[@type = 'file']")]:
+ try:
+ write_config_file(odir, configfile)
+ except FileNotBuilt, ex:
+ print("Warning: No file content generated for ConfigFile %s!" % ex)
+ pass
+ except Exception, ex:
+ print("unknown error, I give up: %s" %ex)
+ return
+
+ print("Config for %s written to %s" % (client, odir))
+
+ else:
+ print('Error: Incorrect number of parameters')
+ self.help_builddir()
+
+ def do_buildall(self, args):
+ if len(args.split()) != 1:
+ print("Usage: buildall <directory>")
+ 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('Path', type='file', name=fname)
+ metadata = self.build_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 = list(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 = list(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 [1, 2]:
+ print("Usage: showentries <hostname> <type>")
+ return
+ client = args.split()[0]
+ try:
+ meta = self.build_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 = list(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 do_showclient(self, args):
+ """Print host metadata."""
+ data = [('Client', 'Profile', "Groups", "Bundles")]
+ if not len(args):
+ print("Usage:\nshowclient <client> ... <clientN>")
+ return
+ for client in args.split():
+ try:
+ client_meta = self.build_metadata(client)
+ except:
+ print("Client %s not defined" % client)
+ continue
+ print "Hostname:\t", client_meta.hostname
+ print "Profile:\t", client_meta.profile
+ print "Groups:\t\t", list(client_meta.groups)[0]
+ for grp in list(client_meta.groups)[1:]:
+ print '\t\t%s' % grp
+ if client_meta.bundles:
+ print "Bundles:\t", list(client_meta.bundles)[0]
+ for bnd in list(client_meta.bundles)[1:]:
+ print '\t\t%s' % bnd
+ if client_meta.connectors:
+ print "Connector data"
+ print "=" * 80
+ for conn in client_meta.connectors:
+ if getattr(client_meta, conn):
+ print "%s:\t" % (conn), getattr(client_meta, conn)
+ print "=" * 80
+
+ 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 = list(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.build_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('Path') 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:
+ sys.stderr.write("Entry %s failed" % cfile.get('name'))
+ continue
+ print(cand[0].name)
+
+ def do_profile(self, arg):
+ if not have_profile:
+ print("Profiling functionality not available")
+ return
+ tracefname = tempfile.mktemp()
+ p = profile.Profile()
+ p.runcall(self.onecmd, arg)
+ displayTrace(p)
+
+ def Run(self, args):
+ if args:
+ self.onecmd(" ".join(args))
+ os._exit(0)
+ else:
+ self.do_loop()
+
+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,
+ 'plugins': Bcfg2.Options.SERVER_PLUGINS,
+ 'password': Bcfg2.Options.SERVER_PASSWORD,
+ 'event debug': Bcfg2.Options.DEBUG,
+ 'profile': Bcfg2.Options.CORE_PROFILE,
+ 'encoding': Bcfg2.Options.ENCODING})
+ setup = Bcfg2.Options.OptionParser(optinfo)
+ setup.parse(sys.argv[1:])
+ if setup['profile'] and have_profile:
+ prof = profile.Profile()
+ loop = prof.runcall(infoCore, setup['repo'], setup['plugins'],
+ setup['password'], setup['encoding'],
+ setup['event debug'])
+ displayTrace(prof)
+ else:
+ if setup['profile']:
+ print("Profiling functionality not available")
+ loop = infoCore(setup['repo'], setup['plugins'], setup['password'],
+ setup['encoding'], setup['event debug'])
+
+ loop.Run(setup['args'])