source: fedd/abac_client.py @ b501f63

version-3.02
Last change on this file since b501f63 was 20cda34, checked in by Jay Jacobs <Jay.Jacobs@…>, 15 years ago

Added two-party functionality and example

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