#!/usr/bin/env python2.5

"""
Collates the results of listvms from multiple VM servers.  Part of the xvm
suite.
"""

from itertools import chain
from subprocess import CalledProcessError, PIPE, Popen
from sys import argv

###

import compiler

class Unsafe_Source_Error(Exception):
    def __init__(self,error,descr = None,node = None):
        self.error = error
        self.descr = descr
        self.node = node
        self.lineno = getattr(node,"lineno",None)
        
    def __repr__(self):
        return "Line %d.  %s: %s" % (self.lineno, self.error, self.descr)
    __str__ = __repr__    
           
class SafeEval(object):
    
    def visit(self, node,**kw):
        cls = node.__class__
        meth = getattr(self,'visit'+cls.__name__,self.default)
        return meth(node, **kw)
            
    def default(self, node, **kw):
        for child in node.getChildNodes():
            return self.visit(child, **kw)
            
    visitExpression = default
    
    def visitConst(self, node, **kw):
        return node.value

    def visitDict(self,node,**kw):
        return dict([(self.visit(k),self.visit(v)) for k,v in node.items])
        
    def visitTuple(self,node, **kw):
        return tuple(self.visit(i) for i in node.nodes)
        
    def visitList(self,node, **kw):
        return [self.visit(i) for i in node.nodes]

class SafeEvalWithErrors(SafeEval):

    def default(self, node, **kw):
        raise Unsafe_Source_Error("Unsupported source construct",
                                node.__class__,node)
            
    def visitName(self,node, **kw):
        if node.name == 'None': return None
        raise Unsafe_Source_Error("Strings must be quoted", 
                                 node.name, node)
                                 
    # Add more specific errors if desired
            
def safe_eval(source, fail_on_error = True):
    if source.strip() == '': return None
    walker = fail_on_error and SafeEvalWithErrors() or SafeEval()
    try:
        ast = compiler.parse(source,"eval")
    except SyntaxError, err:
        raise
    try:
        return walker.visit(ast)
    except Unsafe_Source_Error, err:
        raise

###

def run(cmd):
  """
  Run the given command (a list of program and argument strings) and return the
  stdout as a string, raising a CalledProcessError if the program exited with a
  non-zero status.
  """
  p = Popen(cmd, stdout=PIPE)
  stdout = p.communicate()[0]
  if p.returncode != 0: raise CalledProcessError(p.returncode, cmd)
  return stdout

def main(argv):
  # Query each of the server for their VMs.
  # run('kinit -k host/sipb-vm-58.mit.edu'.split())
  # TODO get `servers` from a real list of all the VM hosts (instead of
  # hardcoding the list here)
  servers = [ 'black-mesa.mit.edu', 'sx-blade-2.mit.edu' ]
  # XXX
  results = [ safe_eval(run(['remctl', server, 'remote', 'web', 'listvms'] + argv[1:]))
              for server in servers ]
  results = filter( lambda x: x is not None, results )

  # Merge the results and print.
  merged = {}
  for result in results: merged.update(result)
  print merged
  print '.'

if __name__ == '__main__':
  main(argv)

# vim:et:sw=2:ts=2
