source: fedd/fedd_client.py @ f3d72f7

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

Hacks to get default certs to work for terminate and the info requests.

  • Property mode set to 100755
File size: 20.9 KB
Line 
1#!/usr/local/bin/python
2
3import sys
4import os
5import pwd
6
7from federation import fedid, service_error
8from federation.util import fedd_ssl_context, pack_id, unpack_id
9from federation.remote_service import service_caller
10
11from optparse import OptionParser, OptionValueError
12
13
14class IDFormatException(RuntimeError): pass
15
16class access_method:
17    """Encapsulates an access method generically."""
18    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
19    default_type = type_ssh
20    def __init__(self, buf=None, type=None, file=None):
21        self.buf = buf
22
23        if type != None: self.type = type
24        else: self.type = access_method.default_type
25
26        if file != None:
27            self.readfile(file)
28   
29    def readfile(self, file, type=None):
30        f = open(file, "r")
31        self.buf = f.read();
32        f.close()
33        if type == None:
34            if self.type == None:
35                self.type = access_method.default_type
36        else:
37            self.type = type;
38   
39class node_desc:
40    def __init__(self, image, hardware, count=1):
41        if getattr(image, "__iter__", None) == None:
42            if image == None: self.image = [ ]
43            else: self.image = [ image ]
44        else:
45            self.image = image
46
47        if getattr(hardware, "__iter__", None) == None: 
48            if hardware == None: self.hardware = [ ]
49            else: self.hardware = [ hardware ]
50        else:
51            self.hardware = hardware
52        if count != None: self.count = int(count)
53        else: self.count = 1
54
55class fedd_client_opts(OptionParser):
56    """Encapsulate option processing in this class, rather than in main"""
57    def __init__(self):
58        OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
59                version="0.1")
60
61        self.add_option("-c","--cert", action="store", dest="cert",
62                type="string", help="my certificate file")
63        self.add_option("-d", "--debug", action="count", dest="debug", 
64                default=0, help="Set debug.  Repeat for more information")
65        self.add_option("-s", "--serializeOnly", action="store_true", 
66                dest="serialize_only", default=False,
67                help="Print the SOAP request that would be sent and exit")
68        self.add_option("-T","--trusted", action="store", dest="trusted",
69                type="string", help="Trusted certificates (required)")
70        self.add_option("-u", "--url", action="store", dest="url",
71                type="string",default="https://localhost:23235", 
72                help="URL to connect to (default %default)")
73        self.add_option("-x","--transport", action="store", type="choice",
74                choices=("xmlrpc", "soap"), default="soap",
75                help="Transport for request (xmlrpc|soap) (Default: %default)")
76        self.add_option("--trace", action="store_const", dest="tracefile", 
77                const=sys.stderr, help="Print SOAP exchange to stderr")
78
79class fedd_create_opts(fedd_client_opts):
80    def __init__(self, access_keys, add_key_callback=None, 
81            add_cert_callback=None):
82        fedd_client_opts.__init__(self)
83        self.add_option("-e", "--experiment_cert", dest="out_certfile",
84                type="string", help="output certificate file")
85        self.add_option("-E", "--experiment_name", dest="exp_name",
86                type="string", help="output certificate file")
87        self.add_option("-F","--useFedid", action="store_true",
88                dest="use_fedid", default=False,
89                help="Use a fedid derived from my certificate as user identity")
90        self.add_option("-f", "--file", dest="file", 
91                help="experiment description file")
92        self.add_option("-p", "--project", action="store", dest="project", 
93                type="string",
94                help="Project to export from master")
95        if add_key_callback:
96            self.add_option("-k", "--sshKey", action="callback", type="string", 
97                    callback=add_key_callback, callback_args=(access_keys,),
98                    help="ssh key for access (can be supplied more than once")
99        if add_cert_callback:
100            self.add_option("-K", "--x509Key", action="callback",
101                    type="string", callback=add_cert_callback,
102                    callback_args=(access_keys,),
103                    help="X509 certificate for access " + \
104                        "(can be supplied more than once")
105        self.add_option("-m", "--master", dest="master",
106                help="Master testbed in the federation")
107        self.add_option("-U", "--username", action="store", dest="user",
108                type="string", help="Use this username instead of the uid")
109
110class fedd_split_opts(fedd_create_opts):
111    def __init__(self, access_keys, add_key_callback=None, 
112            add_cert_callback=None):
113        fedd_create_opts.__init__(self, access_keys, add_key_callback,
114                add_cert_callback)
115        self.add_option('-t','--fedkit', action='store_true', dest='fedkit',
116                default=False,
117                help="get output suitable for federation kit install")
118
119
120class fedd_access_opts(fedd_create_opts):
121    def __init__(self, access_keys, node_descs, add_key_callback=None, 
122            add_cert_callback=None, add_node_callback=None):
123        fedd_create_opts.__init__(self, access_keys, add_key_callback,
124                add_cert_callback)
125        self.add_option("-a","--anonymous", action="store_true",
126                dest="anonymous", default=False,
127                help="Do not include a user in the request")
128        self.add_option("-l","--label", action="store", dest="label",
129                type="string", help="Label for output")
130        if add_node_callback:
131            self.add_option("-n", "--node", action="callback", type="string", 
132                    callback=add_node_callback, callback_args=(node_descs,),
133                    help="Node description: image:hardware[:count]")
134        self.add_option("-t", "--testbed", action="store", dest="testbed",
135                type="string",
136                help="Testbed identifier (URI) to contact (required)")
137
138class fedd_exp_data_opts(fedd_client_opts):
139    def __init__(self):
140        fedd_client_opts.__init__(self)
141        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
142                type="string", help="output certificate file")
143        self.add_option("-E", "--experiment_name", dest="exp_name",
144                type="string", help="output certificate file")
145
146def exit_with_fault(dict, out=sys.stderr):
147    """ Print an error message and exit.
148
149    The dictionary contains the FeddFaultBody elements."""
150    codestr = ""
151
152    if dict.has_key('errstr'):
153        codestr = "Error: %s" % dict['errstr']
154
155    if dict.has_key('code'):
156        if len(codestr) > 0 : 
157            codestr += " (%d)" % dict['code']
158        else:
159            codestr = "Error Code: %d" % dict['code']
160
161    print>>out, codestr
162    print>>out, "Description: %s" % dict['desc']
163    sys.exit(dict.get('code', 20))
164# Base class that will do a the SOAP/XMLRPC exchange for a request.
165class fedd_rpc:
166    class RPCException:
167        def __init__(self, fb):
168            self.desc = fb.get('desc', None)
169            self.code = fb.get('code', -1)
170            self.errstr = fb.get('errstr', None)
171
172    def __init__(self, pre): 
173        """
174        Specialize the class for the pre method
175        """
176        self.RequestBody="%sRequestBody" % pre
177        self.ResponseBody="%sResponseBody" % pre
178        self.method = pre
179
180        self.caller = service_caller(self.method)
181        self.RPCException = fedd_rpc.RPCException
182
183
184    def add_ssh_key(self, option, opt_str, value, parser, access_keys):
185        try:
186            access_keys.append(access_method(file=value,
187                type=access_method.type_ssh))
188        except IOError, (errno, strerror):
189            raise OptionValueError("Cannot generate sshPubkey from %s: "\
190                    "%s (%d)" % (value,strerror,errno))
191
192    def add_x509_cert(self, option, opt_str, value, parser, access_keys):
193        try:
194            access_keys.append(access_method(file=value,
195                type=access_method.type_x509))
196        except IOError, (errno, strerror):
197            raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
198                    (value,strerror,errno))
199    def add_node_desc(self, option, opt_str, value, parser, node_descs):
200        def none_if_zero(x):
201            if len(x) > 0: return x
202            else: return None
203
204        params = map(none_if_zero, value.split(":"));
205       
206        if len(params) < 4 and len(params) > 1:
207            node_descs.append(node_desc(*params))
208        else:
209            raise OptionValueError("Bad node description: %s" % value)
210
211    def get_user_info(self, access_keys):
212        pw = pwd.getpwuid(os.getuid());
213        try_cert=None
214        user = None
215
216        if pw != None:
217            user = pw[0]
218            try_cert = "%s/.ssl/emulab.pem" % pw[5];
219            if not os.access(try_cert, os.R_OK):
220                try_cert = None
221            if len(access_keys) == 0:
222                for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub", 
223                        "%s/.ssh/identity.pub"]:
224                    try_key = k % pw[5];
225                    if os.access(try_key, os.R_OK):
226                        access_keys.append(access_method(file=try_key,
227                            type=access_method.type_ssh))
228                        break
229        return (user, try_cert)
230
231    def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None,
232            serialize_only=False):
233        """
234        The work of sending and parsing the RPC as either XMLRPC or SOAP
235        """
236
237        context = None
238        while context == None:
239            try:
240                context = fedd_ssl_context(cert, trusted)
241            except Exception, e:
242                # Yes, doing this on message type is not ideal.  The string
243                # comes from OpenSSL, so check there is this stops working.
244                if str(e) == "bad decrypt": 
245                    print >>sys.stderr, "Bad Passphrase given."
246                else: raise
247
248        if transport == "soap":
249            if serialize_only:
250                print self.caller.serialize_soap(req_dict) 
251                sys.exit(0)
252            else:
253                try:
254                    resp = self.caller.call_soap_service(url, req_dict, 
255                            context=context, tracefile=tracefile)
256                except service_error, e:
257                    raise self.RPCException( {\
258                            'code': e.code, 
259                            'desc': e.desc, 
260                            'errstr': e.code_string()\
261                        })
262        elif transport == "xmlrpc":
263            if serialize_only:
264                ser = dumps((req_dict,))
265                print ser
266                sys.exit(0)
267            else:
268                try:
269                    resp = self.caller.call_xmlrpc_service(url, req_dict, 
270                            context=context, tracefile=tracefile)
271                except service_error, e:
272                    raise self.RPCException( {\
273                            'code': e.code, 
274                            'desc': e.desc, 
275                            'errstr': e.code_string()\
276                        })
277
278        else:
279            raise RuntimeError("Unknown RPC transport: %s" % transport)
280
281        if resp.has_key(self.ResponseBody):
282            return resp[self.ResponseBody]
283        else:
284            raise RuntimeError("No body in response??")
285
286# Querying experiment data follows the same control flow regardless of the
287# specific data retrieved.  This class encapsulates that control flow.
288class exp_data(fedd_rpc):
289    def __init__(self, op): 
290        """
291        Specialize the class for the type of data requested (op)
292        """
293
294        fedd_rpc.__init__(self, op)
295        if op =='Vtopo':
296            self.key="vtopo"
297            self.xml='experiment'
298        elif op == 'Vis':
299            self.key="vis"
300            self.xml='vis'
301        elif op == 'Info': pass
302        else:
303            raise TypeError("Bad op: %s" % op)
304
305    def print_xml(self, d, out=sys.stdout):
306        """
307        Print the retrieved data is a simple xml representation of the dict.
308        """
309        str = "<%s>\n" % self.xml
310        for t in ('node', 'lan'):
311            if d.has_key(t): 
312                for x in d[t]:
313                    str += "<%s>" % t
314                    for k in x.keys():
315                        str += "<%s>%s</%s>" % (k, x[k],k)
316                    str += "</%s>\n" % t
317        str+= "</%s>" % self.xml
318        print >>out, str
319
320    def __call__(self):
321        """
322        The control flow.  Compose the request and print the response.
323        """
324        # Process the options using the customized option parser defined above
325        parser = fedd_exp_data_opts()
326
327        (opts, args) = parser.parse_args()
328
329        if opts.trusted:
330            if ( not os.access(opts.trusted, os.R_OK) ) :
331                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
332
333        if opts.debug > 0: opts.tracefile=sys.stderr
334
335        (user, cert) = self.get_user_info([])
336
337        if opts.cert != None: cert = opts.cert
338
339        if cert == None:
340            sys.exit("No certificate given (--cert) or found")
341
342        if os.access(cert, os.R_OK):
343            fid = fedid(file=cert)
344        else:
345            sys.exit("Cannot read certificate (%s)" % cert)
346
347        if opts.exp_name and opts.exp_certfile:
348            sys.exit("Only one of --experiment_cert and " +\
349                    "--experiment_name permitted");
350
351        if opts.exp_certfile:
352            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
353
354        if opts.exp_name:
355            exp_id = { 'localname' : opts.exp_name }
356
357        req = { 'experiment': exp_id }
358
359        try:
360            resp_dict = self.do_rpc(req,
361                    opts.url, opts.transport, cert, opts.trusted, 
362                    serialize_only=opts.serialize_only,
363                    tracefile=opts.tracefile)
364        except self.RPCException, e:
365            exit_with_fault(\
366                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
367        except RuntimeError, e:
368            print e
369            sys.exit("Error processing RPC: %s" % e)
370
371        if getattr(self, 'key', None):
372            try:
373                if resp_dict.has_key(self.key):
374                    self.print_xml(resp_dict[self.key])
375            except RuntimeError, e:
376                sys.exit("Bad response. %s" % e.message)
377        else:
378            print resp_dict
379
380class terminate(fedd_rpc):
381    def __init__(self): 
382        """
383        Termination request
384        """
385
386        fedd_rpc.__init__(self, "Terminate")
387
388    def __call__(self):
389        """
390        The control flow.  Compose the request and print the response.
391        """
392        # Process the options using the customized option parser defined above
393        parser = fedd_exp_data_opts()
394
395        (opts, args) = parser.parse_args()
396
397        (user, cert) = self.get_user_info([])
398        if opts.trusted:
399            if ( not os.access(opts.trusted, os.R_OK) ) :
400                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
401
402        if opts.debug > 0: opts.tracefile=sys.stderr
403
404        if opts.cert != None: cert = opts.cert
405
406        if cert == None:
407            sys.exit("No certificate given (--cert) or found")
408
409        if os.access(cert, os.R_OK):
410            fid = fedid(file=cert)
411        else:
412            sys.exit("Cannot read certificate (%s)" % cert)
413
414        if opts.exp_name and opts.exp_certfile:
415            sys.exit("Only one of --experiment_cert and " +\
416                    "--experiment_name permitted");
417
418        if opts.exp_certfile:
419            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
420
421        if opts.exp_name:
422            exp_id = { 'localname' : opts.exp_name }
423
424        req = { 'experiment': exp_id }
425
426        try:
427            resp_dict = self.do_rpc(req,
428                    opts.url, opts.transport, cert, opts.trusted, 
429                    serialize_only=opts.serialize_only,
430                    tracefile=opts.tracefile)
431        except self.RPCException, e:
432            exit_with_fault(\
433                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
434        except RuntimeError, e:
435            print e
436            sys.exit("Error processing RPC: %s" % e)
437
438        eid = resp_dict.get('experimentID', None)
439        if eid:
440            for id in eid:
441                for k in id.keys():
442                    if k == 'fedid': print "%s: %s" % (k,fedid(bits=id[k]))
443                    else: print "%s: %s" % (k, id[k])
444
445class create(fedd_rpc):
446    def __init__(self): 
447        fedd_rpc.__init__(self, "Create")
448    def __call__(self):
449        access_keys = []
450        # Process the options using the customized option parser defined above
451        parser = fedd_create_opts(access_keys, self.add_ssh_key,
452                self.add_x509_cert)
453
454        (opts, args) = parser.parse_args()
455
456        if opts.trusted:
457            if ( not os.access(opts.trusted, os.R_OK) ) :
458                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
459
460        if not opts.project :
461            parser.error('--project is required')
462
463        if opts.debug > 0: opts.tracefile=sys.stderr
464
465        (user, cert) = self.get_user_info(access_keys)
466
467        if opts.user: user = opts.user
468
469        if opts.cert != None: cert = opts.cert
470
471        if cert == None:
472            sys.exit("No certificate given (--cert) or found")
473
474        if os.access(cert, os.R_OK):
475            fid = fedid(file=cert)
476            if opts.use_fedid == True:
477                user = fid
478        else:
479            sys.exit("Cannot read certificate (%s)" % cert)
480
481        if opts.file:
482            exp_desc = ""
483            try:
484                f = open(opts.file, 'r')
485                for line in f:
486                    exp_desc += line
487                f.close()
488            except IOError:
489                sys.exit("Cannot read description file (%s)" %opts.file)
490        else:
491            sys.exit("Must specify an experiment description (--file)")
492
493        if not opts.master:
494            sys.exit("Must specify a master testbed (--master)")
495
496        out_certfile = opts.out_certfile
497
498        msg = {
499                'experimentdescription': { 'ns2description': exp_desc },
500                'master': opts.master,
501                'exportProject': { 'localname': opts.project },
502                'user' : [ {\
503                        'userID': pack_id(user), \
504                        'access': [ { a.type: a.buf } for a in access_keys]\
505                        } ]
506                }
507
508        if opts.exp_name:
509            msg['experimentID'] = { 'localname': opts.exp_name }
510
511        if opts.debug > 1: print >>sys.stderr, msg
512
513        try:
514            resp_dict = self.do_rpc(msg, 
515                    opts.url, opts.transport, cert, opts.trusted, 
516                    serialize_only=opts.serialize_only,
517                    tracefile=opts.tracefile)
518        except self.RPCException, e:
519            exit_with_fault(\
520                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
521        except RuntimeError, e:
522            sys.exit("Error processing RPC: %s" % e)
523
524        if opts.debug > 1: print >>sys.stderr, resp_dict
525
526        ea = resp_dict.get('experimentAccess', None)
527        if out_certfile and ea and ea.has_key('X509'):
528            try:
529                f = open(out_certfile, "w")
530                print >>f, ea['X509']
531                f.close()
532            except IOError:
533                sys.exit('Could not write to %s' %  out_certfile)
534        eid = resp_dict.get('experimentID', None)
535        if eid:
536            for id in eid:
537                for k in id.keys():
538                    print "%s: %s" % (k, id[k])
539
540class split(fedd_rpc):
541    def __init__(self): 
542        fedd_rpc.__init__(self, "Ns2Split")
543    def __call__(self):
544        access_keys = []
545        # Process the options using the customized option parser defined above
546        parser = fedd_split_opts(access_keys, self.add_ssh_key,
547                self.add_x509_cert)
548
549        (opts, args) = parser.parse_args()
550
551        if opts.trusted:
552            if ( not os.access(opts.trusted, os.R_OK) ) :
553                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
554
555        if opts.debug > 0: opts.tracefile=sys.stderr
556
557        if opts.cert != None: cert = opts.cert
558        else: cert = None
559
560        if cert == None:
561            sys.exit("No certificate given (--cert) or found")
562
563        if os.access(cert, os.R_OK):
564            fid = fedid(file=cert)
565            if opts.use_fedid == True:
566                user = fid
567        else:
568            sys.exit("Cannot read certificate (%s)" % cert)
569
570        if opts.file:
571            exp_desc = ""
572            try:
573                f = open(opts.file, 'r')
574                for line in f:
575                    exp_desc += line
576                f.close()
577            except IOError:
578                sys.exit("Cannot read description file (%s)" %opts.file)
579        else:
580            sys.exit("Must specify an experiment description (--file)")
581
582        if not opts.master:
583            sys.exit("Must specify a master testbed (--master)")
584
585        out_certfile = opts.out_certfile
586
587        msg = {
588                'description': { 'ns2description': exp_desc },
589                'master': opts.master,
590                'include_fedkit': opts.fedkit,
591                }
592
593        if opts.debug > 1: print >>sys.stderr, msg
594
595        try:
596            resp_dict = self.do_rpc(msg, 
597                    opts.url, opts.transport, cert, opts.trusted, 
598                    serialize_only=opts.serialize_only,
599                    tracefile=opts.tracefile)
600        except self.RPCException, e:
601            exit_with_fault(\
602                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
603        except RuntimeError, e:
604            sys.exit("Error processing RPC: %s" % e)
605
606        if opts.debug > 1: print >>sys.stderr, resp_dict
607
608        out = resp_dict.get('output', None)
609
610        for line in out.splitlines():
611            print "%s" % line
612
613class access(fedd_rpc):
614    def __init__(self):
615        fedd_rpc.__init__(self, "RequestAccess")
616
617    def print_response_as_testbed(self, resp, label, out=sys.stdout):
618        """Print the response as input to the splitter script"""
619
620        e = resp['emulab']
621        p = e['project']
622        fields = { 
623                "Boss": e['boss'],
624                "OpsNode": e['ops'],
625                "Domain": e['domain'],
626                "FileServer": e['fileServer'],
627                "EventServer": e['eventServer'],
628                "Project": unpack_id(p['name'])
629                }
630        if (label != None): print >> out, "[%s]" % label
631
632        for l, v in fields.iteritems():
633            print >>out, "%s: %s" % (l, v)
634
635        for u in p['user']:
636            print >>out, "User: %s" % unpack_id(u['userID'])
637
638        for a in e['fedAttr']:
639            print >>out, "%s: %s" % (a['attribute'], a['value'])
640
641    def __call__(self):
642        access_keys = []
643        node_descs = []
644        proj = None
645
646        # Process the options using the customized option parser defined above
647        parser = fedd_access_opts(access_keys, node_descs, self.add_ssh_key,
648                self.add_x509_cert, self.add_node_desc)
649
650        (opts, args) = parser.parse_args()
651
652        if opts.testbed == None:
653            parser.error("--testbed is required")
654
655        if opts.trusted:
656            if ( not os.access(opts.trusted, os.R_OK) ) :
657                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
658
659        if opts.debug > 0: opts.tracefile=sys.stderr
660
661        (user, cert) = self.get_user_info(access_keys)
662
663        if opts.user: user = opts.user
664
665        if opts.cert != None: cert = opts.cert
666
667        if cert == None:
668            sys.exit("No certificate given (--cert) or found")
669
670        if os.access(cert, os.R_OK):
671            fid = fedid(file=cert)
672            if opts.use_fedid == True:
673                user = fid
674        else:
675            sys.exit("Cannot read certificate (%s)" % cert)
676
677        msg = {
678                'allocID': pack_id('test alloc'),
679                'destinationTestbed': pack_id(opts.testbed),
680                'serviceAccess' : [ { a.type: a.buf } for a in access_keys ],
681                'createAccess' : [ { a.type: a.buf } for a in access_keys ],
682                }
683
684        if len(node_descs) > 0:
685            msg['resources'] = { 
686                    'node': [ 
687                        { 
688                            'image':  n.image ,
689                            'hardware':  n.hardware,
690                            'count': n.count,
691                        } for n in node_descs],
692                    }
693
694        if opts.project != None:
695            if not opts.anonymous and user != None:
696                msg['project'] = {
697                        'name': pack_id(opts.project),
698                        'user': [ { 'userID': pack_id(user) } ],
699                        }
700            else:
701                msg['project'] = { 'name': pack_id(opts.project) }
702        else:
703            if not opts.anonymous and user != None:
704                msg['user'] = [ { 'userID': pack_id(user) } ]
705            else:
706                msg['user'] = [];
707
708        if opts.debug > 1: print >>sys.stderr, msg
709
710        try:
711            resp_dict = self.do_rpc(msg, 
712                    opts.url, opts.transport, cert, opts.trusted, 
713                    serialize_only=opts.serialize_only,
714                    tracefile=opts.tracefile)
715        except self.RPCException, e:
716            exit_with_fault(\
717                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
718        except RuntimeError, e:
719            sys.exit("Error processing RPC: %s" % e.message)
720
721        if opts.debug > 1: print >>sys.stderr, resp_dict
722        self.print_response_as_testbed(resp_dict, opts.label)
723
724cmds = {\
725        'create': create(),\
726        'split': split(),\
727        'access': access(),\
728        'vtopo': exp_data('Vtopo'),\
729        'vis': exp_data('Vis'),\
730        'info': exp_data('Info'),\
731        'terminate': terminate(),\
732    }
733
734operation = cmds.get(sys.argv[1], None)
735if operation:
736    del sys.argv[1]
737    operation()
738else:
739    sys.exit("Bad command: %s.  Valid ones are: %s" % \
740            (sys.argv[1], ", ".join(cmds.keys())))
741
Note: See TracBrowser for help on using the repository browser.