added sipb-xen-remote-listvms to aggregate results from multiple vm servers
[invirt/packages/invirt-remote.git] / files / usr / sbin / sipb-xen-remote-listvms
1 #!/usr/bin/env python2.5
2
3 """
4 Collates the results of listvms from multiple VM servers.  Part of the xvm
5 suite.
6 """
7
8 from itertools import chain
9 from subprocess import CalledProcessError, PIPE, Popen
10 from sys import argv
11
12 ###
13
14 import compiler
15
16 class Unsafe_Source_Error(Exception):
17     def __init__(self,error,descr = None,node = None):
18         self.error = error
19         self.descr = descr
20         self.node = node
21         self.lineno = getattr(node,"lineno",None)
22         
23     def __repr__(self):
24         return "Line %d.  %s: %s" % (self.lineno, self.error, self.descr)
25     __str__ = __repr__    
26            
27 class SafeEval(object):
28     
29     def visit(self, node,**kw):
30         cls = node.__class__
31         meth = getattr(self,'visit'+cls.__name__,self.default)
32         return meth(node, **kw)
33             
34     def default(self, node, **kw):
35         for child in node.getChildNodes():
36             return self.visit(child, **kw)
37             
38     visitExpression = default
39     
40     def visitConst(self, node, **kw):
41         return node.value
42
43     def visitDict(self,node,**kw):
44         return dict([(self.visit(k),self.visit(v)) for k,v in node.items])
45         
46     def visitTuple(self,node, **kw):
47         return tuple(self.visit(i) for i in node.nodes)
48         
49     def visitList(self,node, **kw):
50         return [self.visit(i) for i in node.nodes]
51
52 class SafeEvalWithErrors(SafeEval):
53
54     def default(self, node, **kw):
55         raise Unsafe_Source_Error("Unsupported source construct",
56                                 node.__class__,node)
57             
58     def visitName(self,node, **kw):
59         if node.name == 'None': return None
60         raise Unsafe_Source_Error("Strings must be quoted", 
61                                  node.name, node)
62                                  
63     # Add more specific errors if desired
64             
65 def safe_eval(source, fail_on_error = True):
66     if source.strip() == '': return None
67     walker = fail_on_error and SafeEvalWithErrors() or SafeEval()
68     try:
69         ast = compiler.parse(source,"eval")
70     except SyntaxError, err:
71         raise
72     try:
73         return walker.visit(ast)
74     except Unsafe_Source_Error, err:
75         raise
76
77 ###
78
79 def run(cmd):
80   """
81   Run the given command (a list of program and argument strings) and return the
82   stdout as a string, raising a CalledProcessError if the program exited with a
83   non-zero status.
84   """
85   p = Popen(cmd, stdout=PIPE)
86   stdout = p.communicate()[0]
87   if p.returncode != 0: raise CalledProcessError(p.returncode, cmd)
88   return stdout
89
90 def main(argv):
91   # Query each of the server for their VMs.
92   # run('kinit -k host/sipb-vm-58.mit.edu'.split())
93   # TODO get `servers` from a real list of all the VM hosts (instead of
94   # hardcoding the list here)
95   servers = [ 'black-mesa.mit.edu', 'sx-blade-2.mit.edu' ]
96   # XXX
97   results = [ safe_eval(run(['remctl', server, 'remote', 'web', 'listvms'] + argv[1:]))
98               for server in servers ]
99   results = filter( lambda x: x is not None, results )
100
101   # Merge the results and print.
102   merged = {}
103   for result in results: merged.update(result)
104   print merged
105   print '.'
106
107 if __name__ == '__main__':
108   main(argv)
109
110 # vim:et:sw=2:ts=2