source: fedd/fedd_client.py @ 8f32dc0

axis_examplecompt_changesinfo-opsversion-1.30version-2.00version-3.01version-3.02
Last change on this file since 8f32dc0 was ca489e8, checked in by Ted Faber <faber@…>, 15 years ago

Add override fedids that can access all experiments. These are admins,
basically.

Also add a force parameter to terminate requests, to force the termination (and
really deletion of internal state) for experiments in odd states or with
internal misconfigurations.

  • Property mode set to 100755
File size: 40.8 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4import os
5import pwd
6import tempfile
7import subprocess
8import re
9import xml.parsers.expat
10import time
11
12from federation import fedid, service_error
13from federation.util import fedd_ssl_context, pack_id, unpack_id
14from federation.remote_service import service_caller
15
16from optparse import OptionParser, OptionValueError
17
18
19class IDFormatException(RuntimeError): pass
20
21class access_method:
22    """Encapsulates an access method generically."""
23    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
24    default_type = type_ssh
25    def __init__(self, buf=None, type=None, file=None):
26        self.buf = buf
27
28        if type != None: self.type = type
29        else: self.type = access_method.default_type
30
31        if file != None:
32            self.readfile(file)
33   
34    def readfile(self, file, type=None):
35        f = open(file, "r")
36        self.buf = f.read();
37        f.close()
38        if type == None:
39            if self.type == None:
40                self.type = access_method.default_type
41        else:
42            self.type = type;
43   
44class node_desc:
45    def __init__(self, image, hardware, count=1):
46        if getattr(image, "__iter__", None) == None:
47            if image == None: self.image = [ ]
48            else: self.image = [ image ]
49        else:
50            self.image = image
51
52        if getattr(hardware, "__iter__", None) == None: 
53            if hardware == None: self.hardware = [ ]
54            else: self.hardware = [ hardware ]
55        else:
56            self.hardware = hardware
57        if count != None: self.count = int(count)
58        else: self.count = 1
59
60class fedd_client_opts(OptionParser):
61    """Encapsulate option processing in this class, rather than in main"""
62    def __init__(self):
63        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
64                version="0.1")
65
66        self.add_option("-c","--cert", action="store", dest="cert",
67                type="string", help="my certificate file")
68        self.add_option("-d", "--debug", action="count", dest="debug", 
69                default=0, help="Set debug.  Repeat for more information")
70        self.add_option("-s", "--serializeOnly", action="store_true", 
71                dest="serialize_only", default=False,
72                help="Print the SOAP request that would be sent and exit")
73        self.add_option("-T","--trusted", action="store", dest="trusted",
74                type="string", help="Trusted certificates (required)")
75        self.add_option("-u", "--url", action="store", dest="url",
76                type="string",default="https://localhost:23235", 
77                help="URL to connect to (default %default)")
78        self.add_option("-x","--transport", action="store", type="choice",
79                choices=("xmlrpc", "soap"), default="soap",
80                help="Transport for request (xmlrpc|soap) (Default: %default)")
81        self.add_option("--trace", action="store_const", dest="tracefile", 
82                const=sys.stderr, help="Print SOAP exchange to stderr")
83
84class fedd_create_opts(fedd_client_opts):
85    def __init__(self, access_keys, add_key_callback=None, 
86            add_cert_callback=None):
87        fedd_client_opts.__init__(self)
88        self.add_option("-e", "--experiment_cert", dest="out_certfile",
89                type="string", help="output certificate file")
90        self.add_option("-E", "--experiment_name", dest="exp_name",
91                type="string", help="Suggested experiment name")
92        self.add_option("-F","--useFedid", action="store_true",
93                dest="use_fedid", default=False,
94                help="Use a fedid derived from my certificate as user identity")
95        self.add_option("-f", "--file", dest="file", 
96                help="experiment description file")
97        self.add_option("-p", "--project", action="store", dest="project", 
98                type="string",
99                help="Project to export from master")
100        if add_key_callback:
101            self.add_option("-k", "--ssh_key", action="callback",
102                    type="string", callback=add_key_callback,
103                    callback_args=(access_keys,),
104                    help="ssh key for access (can be supplied more than once")
105        if add_cert_callback:
106            self.add_option("-K", "--x509Key", action="callback",
107                    type="string", callback=add_cert_callback,
108                    callback_args=(access_keys,),
109                    help="X509 certificate for access " + \
110                        "(can be supplied more than once")
111        self.add_option("-m", "--master", dest="master",
112                help="Master testbed in the federation")
113        self.add_option("-U", "--username", action="store", dest="user",
114                type="string", help="Use this username instead of the uid")
115
116class fedd_split_opts(fedd_create_opts):
117    def __init__(self, access_keys, add_key_callback=None, 
118            add_cert_callback=None):
119        fedd_create_opts.__init__(self, access_keys, add_key_callback,
120                add_cert_callback)
121        self.add_option('-t','--fedkit', action='store_true', dest='fedkit',
122                default=False,
123                help="get output suitable for federation kit install")
124        self.add_option('-G','--gatewaykit', action='store_true',
125                dest='gatewaykit', default=False,
126                help="get output suitable for federation gateway kit install")
127
128
129class fedd_access_opts(fedd_create_opts):
130    def __init__(self, access_keys, node_descs, add_key_callback=None, 
131            add_cert_callback=None, add_node_callback=None):
132        fedd_create_opts.__init__(self, access_keys, add_key_callback,
133                add_cert_callback)
134        self.add_option("-a","--anonymous", action="store_true",
135                dest="anonymous", default=False,
136                help="Do not include a user in the request")
137        self.add_option("-l","--label", action="store", dest="label",
138                type="string", help="Label for output")
139        if add_node_callback:
140            self.add_option("-n", "--node", action="callback", type="string", 
141                    callback=add_node_callback, callback_args=(node_descs,),
142                    help="Node description: image:hardware[:count]")
143        self.add_option("-t", "--testbed", action="store", dest="testbed",
144                type="string",
145                help="Testbed identifier (URI) to contact (required)")
146
147class fedd_exp_data_opts(fedd_client_opts):
148    def __init__(self):
149        fedd_client_opts.__init__(self)
150        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
151                type="string", help="experiment certificate file")
152        self.add_option("-E", "--experiment_name", dest="exp_name",
153                type="string", help="human readable experiment name")
154        self.add_option("--data", dest="data", default=[],
155                action="append", type="choice",
156                choices=("id", "federant", "vtopo", "vis", "log", "status"),
157                help="data to extract")
158
159class fedd_terminate_opts(fedd_exp_data_opts):
160    def __init__(self):
161        fedd_exp_data_opts.__init__(self)
162        self.add_option("--force", dest="force",
163                action="store_true", default=False,
164                help="Force termination if experiment is in strange state")
165
166class fedd_multi_exp_data_opts(fedd_client_opts):
167    def __init__(self):
168        fedd_client_opts.__init__(self)
169        self.add_option("--data", dest="data", default=[],
170                action="append", type="choice",
171                choices=("id", "federant", "vtopo", "vis", "log", "status"),
172                help="data to extract")
173
174class fedd_spew_opts(fedd_client_opts):
175    def __init__(self):
176        fedd_client_opts.__init__(self)
177        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
178                type="string", help="experiment name certificate file")
179        self.add_option("-E", "--experiment_name", dest="exp_name",
180                type="string", help="human readable experiment name")
181        self.add_option("--logfile", dest="logfile", default=None,
182                help="File to write log to")
183        self.add_option('--update_time', dest='update', type='int', default=10,
184                help='how often to update the printed log')
185
186class fedd_image_opts(fedd_exp_data_opts):
187    def __init__(self):
188        fedd_exp_data_opts.__init__(self)
189        self.add_option("-o", "--output", dest="outfile", type="string",
190                help="output image file")
191        self.add_option("-F", "--format", dest="format", type="choice", 
192                choices=("jpg", "png", "dot", "svg"),
193                help="Output file format override")
194        self.add_option("-P", "--program", dest="neato", default=None,
195                type="string",
196                help="Program compatible with dot (from graphviz) used to " + \
197                        "render image")
198        self.add_option('-L', "--labels", dest='labels', action='store_true',
199                default=True, help='Label nodes and edges')
200        self.add_option('-A', "--no_labels", dest='labels',
201                default=True, action='store_false',
202                help='Label nodes and edges')
203        self.add_option('-j','--pixels', dest="pixels", default=None,
204                type="int",
205                help="Size of output in pixels (diagrams are square")
206
207class fedd_ns_image_opts(fedd_split_opts):
208    def __init__(self, access_keys, add_key_callback=None, 
209            add_cert_callback=None):
210        fedd_split_opts.__init__(self, access_keys, add_key_callback, 
211                add_cert_callback)
212        self.add_option("-o", "--output", dest="outfile", type="string",
213                help="output image file")
214        self.add_option("-Q", "--format", dest="format", type="choice", 
215                choices=("jpg", "png", "dot", "svg"),
216                help="Output file format override")
217        self.add_option("-P", "--program", dest="neato", default=None,
218                type="string",
219                help="Program compatible with dot (from graphviz) used to " + \
220                        "render image")
221        self.add_option('-L', "--labels", dest='labels', action='store_true',
222                default=True, help='Label nodes and edges')
223        self.add_option('-A', "--no_labels", dest='labels',
224                default=True, action='store_false',
225                help='Label nodes and edges')
226        self.add_option('-j','--pixels', dest="pixels", default=None,
227                type="int",
228                help="Size of output in pixels (diagrams are square")
229
230def exit_with_fault(dict, out=sys.stderr):
231    """ Print an error message and exit.
232
233    The dictionary contains the FeddFaultBody elements."""
234    codestr = ""
235
236    if dict.has_key('errstr'):
237        codestr = "Error: %s" % dict['errstr']
238
239    if dict.has_key('code'):
240        if len(codestr) > 0 : 
241            codestr += " (%d)" % dict['code']
242        else:
243            codestr = "Error Code: %d" % dict['code']
244
245    print>>out, codestr
246    print>>out, "Description: %s" % dict['desc']
247    sys.exit(dict.get('code', 20))
248# Base class that will do a the SOAP/XMLRPC exchange for a request.
249class fedd_rpc:
250    class RPCException:
251        def __init__(self, fb):
252            self.desc = fb.get('desc', None)
253            self.code = fb.get('code', -1)
254            self.errstr = fb.get('errstr', None)
255
256    def __init__(self, pre): 
257        """
258        Specialize the class for the pre method
259        """
260        self.RequestBody="%sRequestBody" % pre
261        self.ResponseBody="%sResponseBody" % pre
262        self.method = pre
263
264        self.caller = service_caller(self.method)
265        self.RPCException = fedd_rpc.RPCException
266
267
268    def add_ssh_key(self, option, opt_str, value, parser, access_keys):
269        try:
270            access_keys.append(access_method(file=value,
271                type=access_method.type_ssh))
272        except IOError, (errno, strerror):
273            raise OptionValueError("Cannot generate sshPubkey from %s: "\
274                    "%s (%d)" % (value,strerror,errno))
275
276    def add_x509_cert(self, option, opt_str, value, parser, access_keys):
277        try:
278            access_keys.append(access_method(file=value,
279                type=access_method.type_x509))
280        except IOError, (errno, strerror):
281            raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
282                    (value,strerror,errno))
283    def add_node_desc(self, option, opt_str, value, parser, node_descs):
284        def none_if_zero(x):
285            if len(x) > 0: return x
286            else: return None
287
288        params = map(none_if_zero, value.split(":"));
289       
290        if len(params) < 4 and len(params) > 1:
291            node_descs.append(node_desc(*params))
292        else:
293            raise OptionValueError("Bad node description: %s" % value)
294
295    def get_user_info(self, access_keys):
296        pw = pwd.getpwuid(os.getuid());
297        try_cert=None
298        user = None
299
300        if pw != None:
301            user = pw[0]
302            try_cert = "%s/.ssl/emulab.pem" % pw[5];
303            if not os.access(try_cert, os.R_OK):
304                try_cert = None
305            if len(access_keys) == 0:
306                for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub", 
307                        "%s/.ssh/identity.pub"]:
308                    try_key = k % pw[5];
309                    if os.access(try_key, os.R_OK):
310                        access_keys.append(access_method(file=try_key,
311                            type=access_method.type_ssh))
312                        break
313        return (user, try_cert)
314
315    def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None,
316            serialize_only=False):
317        """
318        The work of sending and parsing the RPC as either XMLRPC or SOAP
319        """
320
321        context = None
322        while context == None:
323            try:
324                context = fedd_ssl_context(cert, trusted)
325            except Exception, e:
326                # Yes, doing this on message type is not ideal.  The string
327                # comes from OpenSSL, so check there is this stops working.
328                if str(e) == "bad decrypt": 
329                    print >>sys.stderr, "Bad Passphrase given."
330                else: raise
331
332        if transport == "soap":
333            if serialize_only:
334                print self.caller.serialize_soap(req_dict) 
335                sys.exit(0)
336            else:
337                try:
338                    resp = self.caller.call_soap_service(url, req_dict, 
339                            context=context, tracefile=tracefile)
340                except service_error, e:
341                    raise self.RPCException( {\
342                            'code': e.code, 
343                            'desc': e.desc, 
344                            'errstr': e.code_string()\
345                        })
346        elif transport == "xmlrpc":
347            if serialize_only:
348                ser = dumps((req_dict,))
349                print ser
350                sys.exit(0)
351            else:
352                try:
353                    resp = self.caller.call_xmlrpc_service(url, req_dict, 
354                            context=context, tracefile=tracefile)
355                except service_error, e:
356                    raise self.RPCException( {\
357                            'code': e.code, 
358                            'desc': e.desc, 
359                            'errstr': e.code_string()\
360                        })
361
362        else:
363            raise RuntimeError("Unknown RPC transport: %s" % transport)
364
365        if resp.has_key(self.ResponseBody):
366            return resp[self.ResponseBody]
367        else:
368            raise RuntimeError("No body in response??")
369
370class exp_data_base(fedd_rpc):
371    def __init__(self, op='Info'):
372        """
373        Init the various conversions
374        """
375
376        fedd_rpc.__init__(self, op)
377        # List of things one could ask for and what formatting routine is
378        # called.
379        self.params = {
380                'vis': ('vis', self.print_xml('vis')),
381                'vtopo': ('vtopo', self.print_xml('vtopo')),
382                'federant': ('federant', self.print_xml),
383                'id': ('experimentID', self.print_id),
384                'status': ('experimentStatus', self.print_string),
385                'log': ('allocationLog', self.print_string),
386                'access': ('experimentAccess', self.print_string),
387            }
388
389    # Utility functions
390    def print_string(self, d, out=sys.stdout):
391        print >>out, d
392
393    def print_id(self, d, out=sys.stdout):
394        if d:
395            for id in d:
396                for k in id.keys():
397                    print >>out, "%s: %s" % (k, id[k])
398
399    class print_xml:
400        """
401        Print the retrieved data is a simple xml representation of the dict.
402        """
403        def __init__(self, top):
404            self.xml = top
405
406        def __call__(self, d, out=sys.stdout):
407            str = "<%s>\n" % self.xml
408            for t in ('node', 'lan'):
409                if d.has_key(t): 
410                    for x in d[t]:
411                        str += "<%s>" % t
412                        for k in x.keys():
413                            str += "<%s>%s</%s>" % (k, x[k],k)
414                        str += "</%s>\n" % t
415            str+= "</%s>" % self.xml
416            print >>out, str
417
418       
419
420# Querying experiment data follows the same control flow regardless of the
421# specific data retrieved.  This class encapsulates that control flow.
422class exp_data(exp_data_base):
423    def __init__(self): 
424        exp_data_base.__init__(self, 'Info')
425
426    def __call__(self):
427        """
428        The control flow.  Compose the request and print the response.
429        """
430        # Process the options using the customized option parser defined above
431        parser = fedd_exp_data_opts()
432
433        (opts, args) = parser.parse_args()
434
435        if opts.trusted:
436            if ( not os.access(opts.trusted, os.R_OK) ) :
437                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
438
439        if opts.debug > 0: opts.tracefile=sys.stderr
440
441        (user, cert) = self.get_user_info([])
442
443        if opts.cert != None: cert = opts.cert
444
445        if cert == None:
446            sys.exit("No certificate given (--cert) or found")
447
448        if os.access(cert, os.R_OK):
449            fid = fedid(file=cert)
450        else:
451            sys.exit("Cannot read certificate (%s)" % cert)
452
453        if opts.exp_name and opts.exp_certfile:
454            sys.exit("Only one of --experiment_cert and " +\
455                    "--experiment_name permitted");
456
457        if opts.exp_certfile:
458            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
459
460        if opts.exp_name:
461            exp_id = { 'localname' : opts.exp_name }
462
463        req = { 'experiment': exp_id }
464
465        try:
466            resp_dict = self.do_rpc(req,
467                    opts.url, opts.transport, cert, opts.trusted, 
468                    serialize_only=opts.serialize_only,
469                    tracefile=opts.tracefile)
470        except self.RPCException, e:
471            exit_with_fault(\
472                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
473        except RuntimeError, e:
474            print e
475            sys.exit("Error processing RPC: %s" % e)
476
477        for d in opts.data:
478            key, output = self.params[d]
479            try:
480                if resp_dict.has_key(key):
481                    output(resp_dict[key])
482            except RuntimeError, e:
483                sys.exit("Bad response. %s" % e.message)
484
485class vtopo(exp_data):
486    """
487    vtopo is just an info --data=vtopo request, so this adds that parameter to
488    the arguments and executes exp_info when called.
489    """
490    def __init__(self):
491        exp_data.__init__(self)
492    def __call__(self):
493        sys.argv.append('--data=vtopo')
494        exp_data.__call__(self)
495
496
497class vis(exp_data):
498    """
499    vis is just an info --data=vis request, so this adds that parameter to
500    the arguments and executes exp_info when called.
501    """
502    def __init__(self):
503        exp_data.__init__(self)
504    def __call__(self):
505        sys.argv.append('--data=vis')
506        exp_data.__call__(self)
507
508class status(exp_data):
509    """
510    status is just an info --data=status request, so this adds that parameter
511    to the arguments and executes exp_info when called.
512    """
513    def __init__(self):
514        exp_data.__init__(self)
515    def __call__(self):
516        sys.argv.append('--data=status')
517        exp_data.__call__(self)
518
519class multi_exp_data(exp_data_base):
520    def __init__(self): 
521        exp_data_base.__init__(self, 'MultiInfo')
522
523
524    def __call__(self):
525        """
526        The control flow.  Compose the request and print the response.
527        """
528        # Process the options using the customized option parser defined above
529        parser = fedd_multi_exp_data_opts()
530
531        (opts, args) = parser.parse_args()
532
533        if opts.trusted:
534            if ( not os.access(opts.trusted, os.R_OK) ) :
535                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
536
537        if opts.debug > 0: opts.tracefile=sys.stderr
538
539        (user, cert) = self.get_user_info([])
540
541        if opts.cert != None: cert = opts.cert
542
543        if cert == None:
544            sys.exit("No certificate given (--cert) or found")
545
546        if os.access(cert, os.R_OK):
547            fid = fedid(file=cert)
548        else:
549            sys.exit("Cannot read certificate (%s)" % cert)
550
551        req = { }
552
553        try:
554            resp_dict = self.do_rpc(req,
555                    opts.url, opts.transport, cert, opts.trusted, 
556                    serialize_only=opts.serialize_only,
557                    tracefile=opts.tracefile)
558        except self.RPCException, e:
559            exit_with_fault(\
560                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
561        except RuntimeError, e:
562            print e
563            sys.exit("Error processing RPC: %s" % e)
564
565        exps = resp_dict.get('info', [])
566        if exps:
567            print '---'
568            for exp in exps:
569                for d in opts.data:
570                    key, output = self.params[d]
571                    try:
572                        if exp.has_key(key):
573                            output(exp[key])
574                    except RuntimeError, e:
575                        sys.exit("Bad response. %s" % e.message)
576                print '---'
577
578
579class multi_status(exp_data_base):
580    def __init__(self): 
581        exp_data_base.__init__(self, 'MultiInfo')
582
583
584    def __call__(self):
585        """
586        The control flow.  Compose the request and print the response.
587        """
588        # Process the options using the customized option parser defined above
589        parser = fedd_client_opts()
590
591        (opts, args) = parser.parse_args()
592
593        if opts.trusted:
594            if ( not os.access(opts.trusted, os.R_OK) ) :
595                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
596
597        if opts.debug > 0: opts.tracefile=sys.stderr
598
599        (user, cert) = self.get_user_info([])
600
601        if opts.cert != None: cert = opts.cert
602
603        if cert == None:
604            sys.exit("No certificate given (--cert) or found")
605
606        if os.access(cert, os.R_OK):
607            fid = fedid(file=cert)
608        else:
609            sys.exit("Cannot read certificate (%s)" % cert)
610
611        req = { }
612
613        try:
614            resp_dict = self.do_rpc(req,
615                    opts.url, opts.transport, cert, opts.trusted, 
616                    serialize_only=opts.serialize_only,
617                    tracefile=opts.tracefile)
618        except self.RPCException, e:
619            exit_with_fault(\
620                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
621        except RuntimeError, e:
622            print e
623            sys.exit("Error processing RPC: %s" % e)
624
625        for exp in resp_dict.get('info', []):
626            out = []
627            for eid in exp.get('experimentID', []):
628                if eid.has_key('localname'):
629                    out.append(eid['localname'])
630                    break
631            else:
632                out.append("")
633            for eid in exp.get('experimentID', []):
634                if eid.has_key('fedid'):
635                    out.append("%s" % eid['fedid'])
636                    break
637            else:
638                out.append("")
639
640            out.append(exp.get('experimentStatus', ""))
641
642            for f in exp.get('federant', []):
643                if f.get('master', False):
644                    em = f.get('emulab', None)
645                    if em:
646                        project = em.get('project', None)
647                        if project:
648                            tb = project.get('testbed', None)
649                            if tb and tb.has_key('localname'):
650                                out.append(tb['localname'])
651                            else:
652                                out.append("")
653                            pn = project.get('name', None)
654                            if pn and pn.has_key('localname'):
655                                out.append(pn['localname'])
656                            else:
657                                out.append("")
658                        else:
659                            out.extend(("", ""))
660                    else:
661                        out.extend(("", ""))
662                    break
663            else:
664                out.extend(("",""))
665
666            print ":".join(out)
667
668class image(fedd_rpc):
669    def __init__(self, op='Vtopo'): 
670        """
671        Null constructor
672        """
673
674        fedd_rpc.__init__(self, 'Vtopo')
675
676    def gen_image(self, d, file, fmt, neato, labels, pix=None):
677
678        # Open up a temporary file for dot to turn into a visualization
679        try:
680            df, dotname = tempfile.mkstemp(prefix='fedd_client', suffix=".dot")
681            dotfile = os.fdopen(df, 'w')
682        except IOError:
683            raise service_error(service_error.internal,
684                    "Failed to open file in genviz")
685
686        if not neato:
687            for f in ['/usr/bin/neato', '/usr/local/bin/neato', 
688                    '/usr/bin/dot', '/usr/local/bin/dot']:
689                if os.access(f, os.X_OK):
690                    neato = f
691                    break
692            else:
693                sys.exit("Cannot find graph rendering program")
694
695        cmd = [neato, '-Gsplines=true']
696        if fmt != 'dot': cmd.append('-T%s' % fmt)
697        if file:
698            cmd.append('-o')
699            cmd.append(file)
700        cmd.append(dotname)
701
702        nodes = d.get('node',[])
703
704        if len(nodes) < 10: size = 5
705        elif len(nodes) < 50: size = 10
706        else: size = 18
707
708        if pix:
709            dpi = pix / size
710        else:
711            dpi = None
712
713
714        print >>dotfile, "graph G {"
715        if dpi:
716            print >>dotfile, '\tgraph [size="%i,%i", dpi="%i", ratio=fill];' \
717                    % (size, size, dpi)
718        else:
719            print >>dotfile, '\tgraph [size="%i,%i", ratio=fill];' \
720                    % (size, size)
721
722        if labels:
723            print >>dotfile, '\tnode [fontname=arial,fontsize=9,label="\N"];'
724            print >>dotfile, '\tedge [fontname=arial,fontsize=9];\n'
725        else:
726            print >>dotfile, '\tnode [label=""];'
727
728        for n in d.get('node', []):
729            print >>dotfile, '\t"%s" [shape=box,style=filled,\\' % n['vname']
730            print >>dotfile, '\t\tcolor=black,fillcolor="#60f8c0",regular=1]'
731
732
733        lans = { }
734        links = { }
735
736        # Collect lan members (we have to draw extra stuff for these)
737        for n in d.get('lan', []):
738            v = n['vname']
739            m = n['member']
740            i = n['ip']
741            if m.find(':') != -1:
742                m = m[0:m.find(':')]
743            if lans.has_key(v):
744                lans[v].append((m, i))
745            elif links.has_key(v):
746                links[v].append((m, i))
747                if len(links[v]) > 2:
748                    lans[v] = links[v]
749                    del links[v]
750            else:
751                links[v] = [(m, i)]
752
753        # Encode the lans and the links
754        for l in lans.keys():
755            print >>dotfile, '\t"%s" [shape=ellipse, style=filled,\\' % l
756            print >>dotfile,'\t\tcolor=black,fillcolor="#80c0f8",regular=1]'
757            for n in lans[l]:
758                if labels:
759                    print >>dotfile, '\t"%s" -- "%s" [headlabel="%s"]' % \
760                            (l, n[0], n[1])
761                else:
762                    print >>dotfile, '\t"%s" -- "%s"' % (l, n[0])
763
764        for k, l in links.items():
765            if len(l) == 2:
766                if labels:
767                    print >>dotfile, \
768                            ('\t"%s" -- "%s" [label="%s",taillabel="%s",' + \
769                            'headlabel="%s"]') % \
770                            (l[0][0], l[1][0], k, l[0][1], l[1][1])
771                else:
772                    print >>dotfile, '\t"%s" -- "%s" ' % (l[0][0], l[1][0])
773
774        print >>dotfile, "}"
775        dotfile.close()
776
777        # Redirect the drawing program stderr
778        dev_null = open("/dev/null", "w")
779        rv = subprocess.call(cmd, stderr=dev_null)
780        os.remove(dotname)
781        dev_null.close()
782        if rv != 0:
783            sys.exit("Error creating graph")
784
785
786
787    def __call__(self):
788        """
789        The control flow.  Compose the request and print the response.
790        """
791        # Process the options using the customized option parser defined above
792        parser = fedd_image_opts()
793
794        (opts, args) = parser.parse_args()
795
796        if opts.trusted:
797            if ( not os.access(opts.trusted, os.R_OK) ) :
798                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
799
800        if opts.debug > 0: opts.tracefile=sys.stderr
801
802        (user, cert) = self.get_user_info([])
803
804        if opts.cert != None: cert = opts.cert
805
806        if cert == None:
807            sys.exit("No certificate given (--cert) or found")
808
809        if os.access(cert, os.R_OK):
810            fid = fedid(file=cert)
811        else:
812            sys.exit("Cannot read certificate (%s)" % cert)
813
814        if opts.exp_name and opts.exp_certfile:
815            sys.exit("Only one of --experiment_cert and " +\
816                    "--experiment_name permitted");
817
818        if opts.exp_certfile:
819            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
820
821        if opts.exp_name:
822            exp_id = { 'localname' : opts.exp_name }
823
824        if opts.format and opts.outfile:
825            fmt = opts.format
826            file = opts.outfile
827        elif not opts.format and opts.outfile:
828            fmt = opts.outfile[-3:]
829            if fmt not in ("png", "jpg", "dot", "svg"):
830                sys.exit("Unexpected file type and no format specified")
831            file = opts.outfile
832        elif opts.format and not opts.outfile:
833            fmt = opts.format
834            file = None
835        else:
836            fmt="dot"
837            file = None
838
839
840        req = { 'experiment': exp_id }
841
842        try:
843            resp_dict = self.do_rpc(req,
844                    opts.url, opts.transport, cert, opts.trusted, 
845                    serialize_only=opts.serialize_only,
846                    tracefile=opts.tracefile)
847        except self.RPCException, e:
848            exit_with_fault(\
849                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
850        except RuntimeError, e:
851            print e
852            sys.exit("Error processing RPC: %s" % e)
853
854
855        if resp_dict.has_key('vtopo'):
856            self.gen_image(resp_dict['vtopo'], file, fmt, opts.neato,
857                    opts.labels, opts.pixels)
858        else:
859            sys.exit("Bad response. %s" % e.message)
860
861class ns_image(image):
862    def __init__(self, op='Ns2Split'): 
863        """
864        Null constructor
865        """
866
867        image.__init__(self, 'Ns2Split')
868
869    def generate_topo_dict(self, splitout):
870        class topo_parse:
871            """
872            Parse the topology XML and create the dats structure.  This class
873            is copied from federation.experiment_control.
874            """
875            def __init__(self):
876                # Typing of the subelements for data conversion
877                self.str_subelements = ('vname', 'vnode', 'ips', 'ip', 'member')
878                self.int_subelements = ( 'bandwidth',)
879                self.float_subelements = ( 'delay',)
880                # The final data structure
881                self.nodes = [ ]
882                self.lans =  [ ]
883                self.topo = { \
884                        'node': self.nodes,\
885                        'lan' : self.lans,\
886                    }
887                self.element = { }  # Current element being created
888                self.chars = ""     # Last text seen
889
890            def end_element(self, name):
891                # After each sub element the contents is added to the current
892                # element or to the appropriate list.
893                if name == 'node':
894                    self.nodes.append(self.element)
895                    self.element = { }
896                elif name == 'lan':
897                    self.lans.append(self.element)
898                    self.element = { }
899                elif name in self.str_subelements:
900                    self.element[name] = self.chars
901                    self.chars = ""
902                elif name in self.int_subelements:
903                    self.element[name] = int(self.chars)
904                    self.chars = ""
905                elif name in self.float_subelements:
906                    self.element[name] = float(self.chars)
907                    self.chars = ""
908
909            def found_chars(self, data):
910                self.chars += data.rstrip()
911
912
913        tp = topo_parse();
914        parser = xml.parsers.expat.ParserCreate()
915        parser.EndElementHandler = tp.end_element
916        parser.CharacterDataHandler = tp.found_chars
917
918        m = re.search('^#\s+Begin\s+Vtopo\s*$(.*)^#\s+End\s+Vtopo', splitout, 
919                re.MULTILINE | re.DOTALL)
920        if m:
921            str = m.group(1)
922        else:
923            sys.exit("Badly formatted split")
924
925        parser.Parse(str)
926
927        return tp.topo
928
929    def __call__(self):
930        """
931        The control flow.  Compose the request and print the response.
932        """
933        access_keys = []
934        # Process the options using the customized option parser defined above
935        parser = fedd_ns_image_opts(access_keys, self.add_ssh_key,
936                self.add_x509_cert)
937
938        (opts, args) = parser.parse_args()
939
940        if opts.trusted:
941            if ( not os.access(opts.trusted, os.R_OK) ) :
942                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
943
944        if opts.debug > 0: opts.tracefile=sys.stderr
945
946        (user, cert) = self.get_user_info([])
947
948        if opts.cert != None: cert = opts.cert
949
950        if cert == None:
951            sys.exit("No certificate given (--cert) or found")
952
953        if os.access(cert, os.R_OK):
954            fid = fedid(file=cert)
955        else:
956            sys.exit("Cannot read certificate (%s)" % cert)
957
958        if opts.file:
959            exp_desc = ""
960            try:
961                f = open(opts.file, 'r')
962                for line in f:
963                    exp_desc += line
964                f.close()
965            except IOError:
966                sys.exit("Cannot read description file (%s)" %opts.file)
967        else:
968            sys.exit("Must specify an experiment description (--file)")
969
970        if not opts.master:
971            opts.master="dummy"
972
973
974        req = {
975                'description': { 'ns2description': exp_desc },
976                'master': opts.master,
977                'include_fedkit': opts.fedkit,
978                'include_gatewaykit': opts.gatewaykit,
979                }
980
981
982        if opts.format and opts.outfile:
983            fmt = opts.format
984            file = opts.outfile
985        elif not opts.format and opts.outfile:
986            fmt = opts.outfile[-3:]
987            if fmt not in ("png", "jpg", "dot", "svg"):
988                sys.exit("Unexpected file type and no format specified")
989            file = opts.outfile
990        elif opts.format and not opts.outfile:
991            fmt = opts.format
992            file = None
993        else:
994            fmt="dot"
995            file = None
996
997
998        try:
999            resp_dict = self.do_rpc(req,
1000                    opts.url, opts.transport, cert, opts.trusted, 
1001                    serialize_only=opts.serialize_only,
1002                    tracefile=opts.tracefile)
1003        except self.RPCException, e:
1004            exit_with_fault(\
1005                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1006        except RuntimeError, e:
1007            print e
1008            sys.exit("Error processing RPC: %s" % e)
1009
1010
1011        if resp_dict.has_key('output'):
1012            if len(resp_dict['output']) < 1:
1013                sys.exit("Bad response: could not split")
1014            topo = self.generate_topo_dict(resp_dict['output'])
1015            self.gen_image(topo, file, fmt, opts.neato, opts.labels,
1016                    opts.pixels)
1017        else:
1018            sys.exit("Bad response. %s" % e.message)
1019
1020class terminate(fedd_rpc):
1021    def __init__(self): 
1022        """
1023        Termination request
1024        """
1025
1026        fedd_rpc.__init__(self, "Terminate")
1027
1028    def __call__(self):
1029        """
1030        The control flow.  Compose the request and print the response.
1031        """
1032        # Process the options using the customized option parser defined above
1033        parser = fedd_terminate_opts()
1034
1035        (opts, args) = parser.parse_args()
1036
1037        (user, cert) = self.get_user_info([])
1038        if opts.trusted:
1039            if ( not os.access(opts.trusted, os.R_OK) ) :
1040                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1041
1042        if opts.debug > 0: opts.tracefile=sys.stderr
1043
1044        if opts.cert != None: cert = opts.cert
1045
1046        if cert == None:
1047            sys.exit("No certificate given (--cert) or found")
1048
1049        if os.access(cert, os.R_OK):
1050            fid = fedid(file=cert)
1051        else:
1052            sys.exit("Cannot read certificate (%s)" % cert)
1053
1054        if opts.exp_name and opts.exp_certfile:
1055            sys.exit("Only one of --experiment_cert and " +\
1056                    "--experiment_name permitted");
1057
1058        exp_id = None
1059
1060        if opts.exp_certfile:
1061            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1062
1063        if opts.exp_name:
1064            exp_id = { 'localname' : opts.exp_name }
1065
1066        if not exp_id:
1067            sys.exit("Must give one of --experiment_cert and " +\
1068                    "--experiment_name");
1069
1070        req = { 'experiment': exp_id, 'force': opts.force }
1071
1072        try:
1073            resp_dict = self.do_rpc(req,
1074                    opts.url, opts.transport, cert, opts.trusted, 
1075                    serialize_only=opts.serialize_only,
1076                    tracefile=opts.tracefile)
1077        except self.RPCException, e:
1078            exit_with_fault(\
1079                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1080        except RuntimeError, e:
1081            print e
1082            sys.exit("Error processing RPC: %s" % e)
1083
1084        eid = resp_dict.get('experimentID', None)
1085        if eid:
1086            for id in eid:
1087                for k in id.keys():
1088                    if k == 'fedid': print "%s: %s" % (k,fedid(bits=id[k]))
1089                    else: print "%s: %s" % (k, id[k])
1090
1091class create(fedd_rpc):
1092    def __init__(self): 
1093        fedd_rpc.__init__(self, "Create")
1094    def __call__(self):
1095        access_keys = []
1096        # Process the options using the customized option parser defined above
1097        parser = fedd_create_opts(access_keys, self.add_ssh_key,
1098                self.add_x509_cert)
1099
1100        (opts, args) = parser.parse_args()
1101
1102        if opts.trusted:
1103            if ( not os.access(opts.trusted, os.R_OK) ) :
1104                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1105
1106        if not opts.project :
1107            parser.error('--project is required')
1108
1109        if opts.debug > 0: opts.tracefile=sys.stderr
1110
1111        (user, cert) = self.get_user_info(access_keys)
1112
1113        if opts.user: user = opts.user
1114
1115        if opts.cert != None: cert = opts.cert
1116
1117        if cert == None:
1118            sys.exit("No certificate given (--cert) or found")
1119
1120        if os.access(cert, os.R_OK):
1121            fid = fedid(file=cert)
1122            if opts.use_fedid == True:
1123                user = fid
1124        else:
1125            sys.exit("Cannot read certificate (%s)" % cert)
1126
1127        if opts.file:
1128            exp_desc = ""
1129            try:
1130                f = open(opts.file, 'r')
1131                for line in f:
1132                    exp_desc += line
1133                f.close()
1134            except IOError:
1135                sys.exit("Cannot read description file (%s)" %opts.file)
1136        else:
1137            sys.exit("Must specify an experiment description (--file)")
1138
1139        if not opts.master:
1140            sys.exit("Must specify a master testbed (--master)")
1141
1142        out_certfile = opts.out_certfile
1143
1144        msg = {
1145                'experimentdescription': { 'ns2description': exp_desc },
1146                'master': opts.master,
1147                'exportProject': { 'localname': opts.project },
1148                'user' : [ {\
1149                        'userID': pack_id(user), \
1150                        'access': [ { a.type: a.buf } for a in access_keys]\
1151                        } ]
1152                }
1153
1154        if opts.exp_name:
1155            msg['experimentID'] = { 'localname': opts.exp_name }
1156
1157        if opts.debug > 1: print >>sys.stderr, msg
1158
1159        try:
1160            resp_dict = self.do_rpc(msg, 
1161                    opts.url, opts.transport, cert, opts.trusted, 
1162                    serialize_only=opts.serialize_only,
1163                    tracefile=opts.tracefile)
1164        except self.RPCException, e:
1165            exit_with_fault(\
1166                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1167        except RuntimeError, e:
1168            sys.exit("Error processing RPC: %s" % e)
1169
1170        if opts.debug > 1: print >>sys.stderr, resp_dict
1171
1172        ea = resp_dict.get('experimentAccess', None)
1173        if out_certfile and ea and ea.has_key('X509'):
1174            try:
1175                f = open(out_certfile, "w")
1176                print >>f, ea['X509']
1177                f.close()
1178            except IOError:
1179                sys.exit('Could not write to %s' %  out_certfile)
1180        eid = resp_dict.get('experimentID', None)
1181        if eid:
1182            for id in eid:
1183                for k in id.keys():
1184                    print "%s: %s" % (k, id[k])
1185        st = resp_dict.get('experimentStatus', None)
1186        if st:
1187            print "status: %s" % st
1188
1189class split(fedd_rpc):
1190    def __init__(self): 
1191        fedd_rpc.__init__(self, "Ns2Split")
1192    def __call__(self):
1193        access_keys = []
1194        # Process the options using the customized option parser defined above
1195        parser = fedd_split_opts(access_keys, self.add_ssh_key,
1196                self.add_x509_cert)
1197
1198        (opts, args) = parser.parse_args()
1199
1200        if opts.trusted:
1201            if ( not os.access(opts.trusted, os.R_OK) ) :
1202                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1203
1204        if opts.debug > 0: opts.tracefile=sys.stderr
1205
1206        (user, cert) = self.get_user_info(access_keys)
1207
1208        if opts.cert != None: cert = opts.cert
1209
1210        if cert == None:
1211            sys.exit("No certificate given (--cert) or found")
1212
1213        if os.access(cert, os.R_OK):
1214            fid = fedid(file=cert)
1215            if opts.use_fedid == True:
1216                user = fid
1217        else:
1218            sys.exit("Cannot read certificate (%s)" % cert)
1219
1220        if opts.file:
1221            exp_desc = ""
1222            try:
1223                f = open(opts.file, 'r')
1224                for line in f:
1225                    exp_desc += line
1226                f.close()
1227            except IOError:
1228                sys.exit("Cannot read description file (%s)" %opts.file)
1229        else:
1230            sys.exit("Must specify an experiment description (--file)")
1231
1232        if not opts.master:
1233            sys.exit("Must specify a master testbed (--master)")
1234
1235        out_certfile = opts.out_certfile
1236
1237        msg = {
1238                'description': { 'ns2description': exp_desc },
1239                'master': opts.master,
1240                'include_fedkit': opts.fedkit,
1241                'include_gatewaykit': opts.gatewaykit,
1242                }
1243
1244        if opts.debug > 1: print >>sys.stderr, msg
1245
1246        try:
1247            resp_dict = self.do_rpc(msg, 
1248                    opts.url, opts.transport, cert, opts.trusted, 
1249                    serialize_only=opts.serialize_only,
1250                    tracefile=opts.tracefile)
1251        except self.RPCException, e:
1252            exit_with_fault(\
1253                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1254        except RuntimeError, e:
1255            sys.exit("Error processing RPC: %s" % e)
1256
1257        if opts.debug > 1: print >>sys.stderr, resp_dict
1258
1259        out = resp_dict.get('output', None)
1260
1261        for line in out.splitlines():
1262            print "%s" % line
1263
1264class access(fedd_rpc):
1265    def __init__(self):
1266        fedd_rpc.__init__(self, "RequestAccess")
1267
1268    def print_response_as_testbed(self, resp, label, out=sys.stdout):
1269        """Print the response as input to the splitter script"""
1270
1271        e = resp['emulab']
1272        p = e['project']
1273        fields = { 
1274                "Boss": e['boss'],
1275                "OpsNode": e['ops'],
1276                "Domain": e['domain'],
1277                "FileServer": e['fileServer'],
1278                "EventServer": e['eventServer'],
1279                "Project": unpack_id(p['name'])
1280                }
1281        if (label != None): print >> out, "[%s]" % label
1282
1283        for l, v in fields.iteritems():
1284            print >>out, "%s: %s" % (l, v)
1285
1286        for u in p['user']:
1287            print >>out, "User: %s" % unpack_id(u['userID'])
1288
1289        for a in e['fedAttr']:
1290            print >>out, "%s: %s" % (a['attribute'], a['value'])
1291
1292    def __call__(self):
1293        access_keys = []
1294        node_descs = []
1295        proj = None
1296
1297        # Process the options using the customized option parser defined above
1298        parser = fedd_access_opts(access_keys, node_descs, self.add_ssh_key,
1299                self.add_x509_cert, self.add_node_desc)
1300
1301        (opts, args) = parser.parse_args()
1302
1303        if opts.testbed == None:
1304            parser.error("--testbed is required")
1305
1306        if opts.trusted:
1307            if ( not os.access(opts.trusted, os.R_OK) ) :
1308                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1309
1310        if opts.debug > 0: opts.tracefile=sys.stderr
1311
1312        (user, cert) = self.get_user_info(access_keys)
1313
1314        if opts.user: user = opts.user
1315
1316        if opts.cert != None: cert = opts.cert
1317
1318        if cert == None:
1319            sys.exit("No certificate given (--cert) or found")
1320
1321        if os.access(cert, os.R_OK):
1322            fid = fedid(file=cert)
1323            if opts.use_fedid == True:
1324                user = fid
1325        else:
1326            sys.exit("Cannot read certificate (%s)" % cert)
1327
1328        msg = {
1329                'allocID': pack_id('test alloc'),
1330                'destinationTestbed': pack_id(opts.testbed),
1331                'serviceAccess' : [ { a.type: a.buf } for a in access_keys ],
1332                'createAccess' : [ { a.type: a.buf } for a in access_keys ],
1333                }
1334
1335        if len(node_descs) > 0:
1336            msg['resources'] = { 
1337                    'node': [ 
1338                        { 
1339                            'image':  n.image ,
1340                            'hardware':  n.hardware,
1341                            'count': n.count,
1342                        } for n in node_descs],
1343                    }
1344
1345        if opts.project != None:
1346            if not opts.anonymous and user != None:
1347                msg['project'] = {
1348                        'name': pack_id(opts.project),
1349                        'user': [ { 'userID': pack_id(user) } ],
1350                        }
1351            else:
1352                msg['project'] = { 'name': pack_id(opts.project) }
1353        else:
1354            if not opts.anonymous and user != None:
1355                msg['user'] = [ { 'userID': pack_id(user) } ]
1356            else:
1357                msg['user'] = [];
1358
1359        if opts.debug > 1: print >>sys.stderr, msg
1360
1361        try:
1362            resp_dict = self.do_rpc(msg, 
1363                    opts.url, opts.transport, cert, opts.trusted, 
1364                    serialize_only=opts.serialize_only,
1365                    tracefile=opts.tracefile)
1366        except self.RPCException, e:
1367            exit_with_fault(\
1368                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1369        except RuntimeError, e:
1370            sys.exit("Error processing RPC: %s" % e.message)
1371
1372        if opts.debug > 1: print >>sys.stderr, resp_dict
1373        self.print_response_as_testbed(resp_dict, opts.label)
1374
1375# Keep requesting experiment status and printing updates to the log until the
1376# experiment is done being created.
1377class spew_log(fedd_rpc):
1378    def __init__(self): 
1379        """
1380        Init the superclass
1381        """
1382
1383        fedd_rpc.__init__(self, 'Info')
1384
1385    def __call__(self):
1386        """
1387        The control flow.  Compose the request and print the response.
1388        """
1389        # Process the options using the customized option parser defined above
1390        parser = fedd_spew_opts()
1391
1392        (opts, args) = parser.parse_args()
1393
1394        if opts.trusted:
1395            if ( not os.access(opts.trusted, os.R_OK) ) :
1396                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
1397
1398        if opts.debug > 0: opts.tracefile=sys.stderr
1399
1400        (user, cert) = self.get_user_info([])
1401
1402        if opts.cert != None: cert = opts.cert
1403
1404        if cert == None:
1405            sys.exit("No certificate given (--cert) or found")
1406
1407        if os.access(cert, os.R_OK):
1408            fid = fedid(file=cert)
1409        else:
1410            sys.exit("Cannot read certificate (%s)" % cert)
1411
1412        if opts.exp_name and opts.exp_certfile:
1413            sys.exit("Only one of --experiment_cert and " +\
1414                    "--experiment_name permitted");
1415
1416        if opts.exp_certfile:
1417            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
1418
1419        if opts.exp_name:
1420            exp_id = { 'localname' : opts.exp_name }
1421
1422        if opts.logfile:
1423            try:
1424                out = open(opts.logfile, "w")
1425            except IOError,e:
1426                sys.exit("Cannot open logfile: %s" %e)
1427        else:
1428            out = sys.stdout
1429
1430        req = { 'experiment': exp_id }
1431
1432        status = "starting"
1433        log = ""
1434        log_offset = 0
1435        update = opts.update
1436        while status == 'starting':
1437            try:
1438                resp_dict = self.do_rpc(req,
1439                        opts.url, opts.transport, cert, opts.trusted, 
1440                        serialize_only=opts.serialize_only,
1441                        tracefile=opts.tracefile)
1442            except self.RPCException, e:
1443                exit_with_fault(\
1444                        {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
1445            except RuntimeError, e:
1446                sys.exit("Error processing RPC: %s" % e)
1447
1448            status = resp_dict.get('experimentStatus', None)
1449            log = resp_dict.get('allocationLog', None)
1450            if not status:
1451                sys.exit("No status in Info response??")
1452            if not log:
1453                sys.exit("No log in Info response??")
1454
1455            if len(log) > log_offset:
1456                print >>out, log[log_offset:],
1457                out.flush()
1458                log_offset = len(log)
1459            if status == 'starting': 
1460                time.sleep(update)
1461
1462        print >>out
1463        print >>out, status
1464        out.close()
1465
1466
1467cmds = {\
1468        'create': create(),\
1469        'split': split(),\
1470        'access': access(),\
1471        'vtopo': vtopo(),\
1472        'vis': vis(),\
1473        'info': exp_data(),\
1474        'multiinfo': multi_exp_data(),\
1475        'multistatus': multi_status(),\
1476        'image': image(),\
1477        'ns_image': ns_image(),\
1478        'status': status(),\
1479        'terminate': terminate(),\
1480        'spewlog': spew_log(),
1481    }
1482
1483operation = cmds.get(sys.argv[1], None)
1484if operation:
1485    del sys.argv[1]
1486    operation()
1487else:
1488    sys.exit("Bad command: %s.  Valid ones are: %s" % \
1489            (sys.argv[1], ", ".join(cmds.keys())))
1490
Note: See TracBrowser for help on using the repository browser.