#!/usr/bin/python -i
'''This tool loads the Bcfg2 core into an interactive debugger'''
__revision__ = '$Revision: 2234 $'

import logging, lxml.etree, sys, time, Bcfg2.Logging, Bcfg2.Server.Core, Bcfg2.Server.Metadata, Bcfg2.Server.Plugin

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

def getInput():
    '''read commands from stdin'''
    try:
        return raw_input('> ').split(" ")
    except KeyboardInterrupt:
        return ['']
    except EOFError:
        return ['quit']
    
def doShowentries(cmd, core):
    '''show abstract configuration entries for a given host'''
    if len(cmd) not in [2, 3]:
        print "Usage: showentries <hostname> <type>"
        return
    try:
        meta = core.metadata.get_metadata(cmd[1])
    except Bcfg2.Server.Metadata.MetadataConsistencyError:
        print "Unable to find metadata for host %s" % cmd[1]
        return
    structures = core.GetStructures(meta)
    output = [('entrytype', 'name')]
    if len(cmd) == 2:
        for item in structures:
            for child in item.getchildren():
                output.append((child.tag, child.get('name')))
    if len(cmd) == 3:
        for item in structures:
            for child in item.getchildren():
                if child.tag == cmd[2]:
                    output.append((child.tag, child.get('name')))
    printTabular(output)

def doBuild(cmd, core):
    '''build client configuration'''
    if len(cmd) == 3:
        output = open(cmd[2], 'w')
        output.write(lxml.etree.tostring(core.BuildConfiguration(cmd[1])))
        output.close()
    else:
        print 'Usage: build <hostname> <output file>'

def doBuildFile(cmd, core):
    '''build a config file for client'''
    if len(cmd) == 3:
        entry = lxml.etree.Element('ConfigFile', name=cmd[1])
        metadata = core.metadata.get_metadata(cmd[2])
        core.Bind(entry, metadata)
        print lxml.etree.tostring(entry)
    else:
        print 'Usage: buildfile filename hostname'

def doBundles(_, core):
    '''print out group/bundle info'''
    data = [('Group', 'Bundles')]
    groups = core.metadata.groups.keys()
    groups.sort()
    for group in groups:
        data.append((group, ','.join(core.metadata.groups[group][0])))
    printTabular(data)

def doClients(_, core):
    '''print out client info'''
    data = [('Client', 'Profile')]
    clist = core.metadata.clients.keys()
    clist.sort()
    for client in clist:
        data.append((client, core.metadata.clients[client]))
    printTabular(data)

def doHelp(_, dummy):
    '''print out usage info'''
    print 'Commands:'
    print 'build <hostname> <filename> - build config for hostname, writing to filename'
    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 'generators - list current versions of generators'
    print 'groups - list groups'
    print 'help - print this text'
    print 'mappings <type*> <name*>- print generator mappings for optional type and name'
    print 'quit'
    print 'showentries <hostname> <type> - show abstract configuration entries for a given host'
    print 'update - process pending file events'
    print 'version - print version of this tool'

def doGenerators(_, core):
    '''print out generator info'''
    for generator in core.generators:
        print generator.__version__

def doGroups(_, core):
    '''print out group info'''
    data = [("Groups", "Profile", "Category", "Contains")]
    grouplist = core.metadata.groups.keys()
    grouplist.sort()
    for group in grouplist:
        if group in core.metadata.profiles:
            prof = 'yes'
        else:
            prof = 'no'
        if core.metadata.categories.has_key(group):
            cat = core.metadata.categories[group]
        else:
            cat = ''
        gdata = [grp for grp in core.metadata.groups[group][1]]
        gdata.remove(group)
        data.append((group, prof, cat, ','.join(gdata)))
    printTabular(data)

def doMappings(cmd, core):
    '''print out mapping info'''
    # dump all mappings unless type specified
    data = [('Plugin', 'Type', 'Name')]
    for generator in core.generators:
        if len(cmd) == 1:
            etypes = generator.Entries.keys()
        elif len(cmd) > 1:
            etypes = [cmd[1]]
        if len(cmd) == 3:
            interested = [(etype, [cmd[2]]) for etype in etypes]
        else:
            interested = [(etype, generator.Entries[etype].keys()) for etype in etypes
                          if generator.Entries.has_key(etype)]
        if [etype for (etype, names) in interested
            if generator.Entries.has_key(etype) and [name for name in names
                                                     if generator.Entries[etype].has_key(name)]]:
            for (etype, names) in interested:
                for name in names:
                    if generator.Entries.has_key(etype) and generator.Entries[etype].has_key(name):
                        data.append((generator.__name__, etype, name))
    printTabular(data)

def doQuit(cmd, core):
    '''exit program'''
    raise SystemExit, 0

def doUpdate(_, core):
    '''Process pending fs events'''
    core.fam.Service()

def doVersion(_, dummy):
    '''print out code version'''
    print __revision__
        
if __name__ == '__main__':
    Bcfg2.Logging.setup_logging('bcfg2-info', to_syslog=False)
    logger = logging.getLogger('bcfg2-info')
    dispatch = {'build': doBuild, 'buildfile': doBuildFile,  'bundles': doBundles,
                'clients': doClients, 'generators': doGenerators, 'groups': doGroups,
                'help': doHelp, 'mappings': doMappings, 'quit': doQuit,
                'showentries': doShowentries, 'update': doUpdate, 'version': doVersion}
    if '-c' in sys.argv:
        cfile = sys.argv[-1]
    else:
        cfile = '/etc/bcfg2.conf'
    try:
        bcore = Bcfg2.Server.Core.Core({}, cfile)
    except Bcfg2.Server.Core.CoreInitError, msg:
        print "Core load failed because %s" % msg
        raise SystemExit, 1
    for i in range(25):
        bcore.fam.Service()
        time.sleep(0.5)
    while True:
        ucmd = getInput()
        if ucmd[0] == 'debug':
            break
        else:
            if dispatch.has_key(ucmd[0]):
                try:
                    dispatch[ucmd[0]](ucmd, bcore)
                except SystemExit, code:
                    raise SystemExit, code
                except Bcfg2.Server.Plugin.PluginExecutionError:
                    continue
                except:
                    logger.error("command failure", exc_info=1)
            else:
                print "Unknown command: %s" % ucmd[0]
