#!/usr/bin/env 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 ... 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 ]"
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 - build config for hostname, writing to filename'
print 'builddir - build config for hostname, writing separate files to dirname'
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 list of available commands'
print 'mappings - print generator mappings for optional type and name'
print 'profile - profile a single bcfg2-info command'
print 'quit - Exit the bcfg2-info command line'
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.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]