Index: fedd/Makefile/abac_client.py
===================================================================
--- fedd/Makefile/abac_client.py	(revision 88e7f2f76ed0f9ce0f3042e8d7ae0c319b72036b)
+++ fedd/Makefile/abac_client.py	(revision 88e7f2f76ed0f9ce0f3042e8d7ae0c319b72036b)
@@ -0,0 +1,753 @@
+#!/usr/local/bin/python
+
+import sys
+import os
+import pwd
+import tempfile
+import traceback
+import subprocess
+import re
+import xml.parsers.expat
+import time
+
+from federation import fedid, service_error
+from federation.util import fedd_ssl_context, pack_id, unpack_id
+from federation.abac_remote_service import abac_service_caller
+#from federation import topdl
+
+from optparse import OptionParser, OptionValueError
+
+class IDFormatException(RuntimeError): pass
+
+class access_method:
+    """Encapsulates an access method generically."""
+    (type_ssh, type_x509, type_pgp) = ('sshPubkey', 'X509', 'pgpPubkey')
+    default_type = type_ssh
+    def __init__(self, buf=None, type=None, file=None):
+	self.buf = buf
+
+	if type != None: self.type = type
+	else: self.type = access_method.default_type
+
+	if file != None:
+	    self.readfile(file)
+    
+    def readfile(self, file, type=None):
+	f = open(file, "r")
+	self.buf = f.read();
+	f.close()
+	if type == None:
+	    if self.type == None:
+		self.type = access_method.default_type
+	else:
+	    self.type = type;
+
+
+# Base class that will do a the SOAP/XMLRPC exchange for a request.
+class abac_rpc:
+    class RPCException:
+        def __init__(self, fb):
+            self.desc = fb.get('desc', None)
+            self.code = fb.get('code', -1)
+            self.errstr = fb.get('errstr', None)
+
+    def __init__(self, pre):
+        """
+        Specialize the class for the pre method
+        """
+        self.RequestBody="%sRequestBody" % pre
+        self.ResponseBody="%sResponseBody" % pre
+        self.method = pre
+
+        self.caller = abac_service_caller(self.method)
+        self.RPCException = abac_rpc.RPCException
+
+
+    def add_ssh_key(self, option, opt_str, value, parser, access_keys):
+        try:
+            access_keys.append(access_method(file=value,
+                type=access_method.type_ssh))
+        except IOError, (errno, strerror):
+            raise OptionValueError("Cannot generate sshPubkey from %s: "\
+                    "%s (%d)" % (value,strerror,errno))
+
+    def add_x509_cert(self, option, opt_str, value, parser, access_keys):
+        try:
+            access_keys.append(access_method(file=value,
+                type=access_method.type_x509))
+        except IOError, (errno, strerror):
+            raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
+                    (value,strerror,errno))
+
+    def add_node_desc(self, option, opt_str, value, parser, node_descs):
+        def none_if_zero(x):
+            if len(x) > 0: return x
+            else: return None
+
+        params = map(none_if_zero, value.split(":"));
+
+        if len(params) < 4 and len(params) > 1:
+            node_descs.append(node_desc(*params))
+        else:
+            raise OptionValueError("Bad node description: %s" % value)
+
+    def get_user_info(self, access_keys):
+        pw = pwd.getpwuid(os.getuid());
+        try_cert=None
+        user = None
+
+        if pw != None:
+            user = pw[0]
+            try_cert = "%s/.ssl/emulab.pem" % pw[5];
+            if not os.access(try_cert, os.R_OK):
+                try_cert = None
+            if len(access_keys) == 0:
+                for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub",
+                        "%s/.ssh/identity.pub"]:
+                    try_key = k % pw[5];
+                    if os.access(try_key, os.R_OK):
+                        access_keys.append(access_method(file=try_key,
+                            type=access_method.type_ssh))
+                        break
+        return (user, try_cert)
+
+    def do_rpc(self, req_dict, url, transport, cert, trusted, tracefile=None,
+            serialize_only=False):
+        """
+        The work of sending and parsing the RPC as either XMLRPC or SOAP
+        """
+
+        context = None
+        while context == None:
+            try:
+                context = fedd_ssl_context(cert, trusted)
+            except Exception, e:
+                # Yes, doing this on message type is not ideal.  The string
+                # comes from OpenSSL, so check there is this stops working.
+                if str(e) == "bad decrypt":
+                    print >>sys.stderr, "Bad Passphrase given."
+                else: raise
+
+        if transport == "soap":
+            if serialize_only:
+                print self.caller.serialize_soap(req_dict)
+                sys.exit(0)
+            else:
+                try:
+                    resp = self.caller.call_soap_service(url, req_dict,
+                            context=context, tracefile=tracefile)
+                except service_error, e:
+                    raise self.RPCException( {\
+                            'code': e.code,
+                            'desc': e.desc,
+                            'errstr': e.code_string()\
+                        })
+        elif transport == "xmlrpc":
+            if serialize_only:
+                ser = dumps((req_dict,))
+                print ser
+                sys.exit(0)
+            else:
+                try:
+                    resp = self.caller.call_xmlrpc_service(url, req_dict,
+                            context=context, tracefile=tracefile)
+                except service_error, e:
+                    raise self.RPCException( {\
+                            'code': e.code,
+                            'desc': e.desc,
+                            'errstr': e.code_string()\
+                        })
+
+        else:
+            raise RuntimeError("Unknown RPC transport: %s" % transport)
+
+        if resp.has_key(self.ResponseBody):
+            return resp[self.ResponseBody]
+        else:
+            raise  RuntimeError("No body in response??")
+
+class abac_client_opts(OptionParser):
+    """Encapsulate option processing in this class, rather than in main"""
+    def __init__(self):
+	OptionParser.__init__(self, usage="%prog [opts] (--help for details)",
+		version="0.1")
+
+	self.add_option("-c","--cert", action="store", dest="cert",
+		type="string", help="my certificate file")
+	self.add_option("-d", "--debug", action="count", dest="debug", 
+		default=0, help="Set debug.  Repeat for more information")
+	self.add_option("-s", "--serializeOnly", action="store_true", 
+		dest="serialize_only", default=False,
+		help="Print the SOAP request that would be sent and exit")
+	self.add_option("-T","--trusted", action="store", dest="trusted",
+		type="string", help="Trusted certificates (required)")
+	self.add_option("-u", "--url", action="store", dest="url",
+		type="string",default="https://localhost:23235",  
+		help="URL to connect to (default %default)")
+	self.add_option("-n", "--ident", action="store", dest="id",
+		type="string",default="Alice",  
+		help="URL to connect to (default %default)")
+	self.add_option("-F","--useFedid", action="store_true",
+		dest="use_fedid", default=False,
+		help="Use a fedid derived from my certificate as user identity")
+	self.add_option("-x","--transport", action="store", type="choice",
+		choices=("xmlrpc", "soap"), default="soap",
+		help="Transport for request (xmlrpc|soap) (Default: %default)")
+	self.add_option("--trace", action="store_const", dest="tracefile", 
+		const=sys.stderr, help="Print SOAP exchange to stderr")
+
+class abac_create_opts(abac_client_opts):
+    def __init__(self, access_keys, add_key_callback=None, 
+	    add_cert_callback=None):
+	abac_client_opts.__init__(self)
+	self.add_option("-f", "--file", dest="file", type="string",
+		help="negotiation context remote path name")
+	if add_key_callback:
+	    self.add_option("-k", "--ssh_key", action="callback",
+		    type="string", callback=add_key_callback,
+		    callback_args=(access_keys,),
+		    help="ssh key for access (can be supplied more than once")
+	if add_cert_callback:
+	    self.add_option("-K", "--x509Key", action="callback",
+		    type="string", callback=add_cert_callback,
+		    callback_args=(access_keys,),
+		    help="X509 certificate for access " + \
+			"(can be supplied more than once")
+ 	    self.add_option("-m", "--master", dest="master",
+		    help="Master testbed in the federation")
+	    self.add_option("-U", "--username", action="store", dest="user",
+		    type="string", 
+                    help="Use this username instead of the uid")
+
+class abac_access_opts(abac_create_opts):
+    def __init__(self, access_keys, node_descs, add_key_callback=None, 
+	    add_cert_callback=None, add_node_callback=None):
+	abac_create_opts.__init__(self, access_keys, add_key_callback,
+		add_cert_callback)
+	self.add_option("-a","--anonymous", action="store_true",
+		dest="anonymous", default=False,
+		help="Do not include a user in the request")
+	self.add_option("-l","--label", action="store", dest="label",
+		type="string", help="Label for output")
+	self.add_option("-i", "--context_id", action="store", dest="id",
+		type="string",
+		help="Negotiation context identifier (required)")
+	self.add_option("-g", "--goal", action="store", dest="goal",
+		type="string",
+		help="Trust target goal for negotiation (required)")
+	self.add_option("--srcURL", action="store", dest="srcURL",
+		type="string",default="https://localhost:23235",  
+		help="Originating URL address (Default: %default)")
+	self.add_option("--dstURL", action="store", dest="dstURL",
+		type="string",default="https://localhost:23235",  
+		help="Peer URL address (Default: %default)")
+	self.add_option("--srcContext", action="store", dest="srcContext",
+		type="string",default="Alice",  
+		help="Service negotiation context (Default: %default)")
+	self.add_option("--dstContext", action="store", dest="dstContext",
+		type="string",default="Bob",  
+		help="Client call back negotiation context (Default: %default)")
+
+	if add_node_callback:
+	    self.add_option("-n", "--node", action="callback", type="string", 
+		    callback=add_node_callback, callback_args=(node_descs,),
+		    help="Node description: image:hardware[:count]")
+	self.add_option("-t", "--testbed", action="store", dest="testbed",
+		type="string",
+		help="Testbed identifier (URI) to contact (required)")
+
+
+class abac_negotiate_opts(abac_create_opts):
+    def __init__(self, access_keys, node_descs, add_key_callback=None, 
+	    add_cert_callback=None, add_node_callback=None):
+	abac_create_opts.__init__(self, access_keys, add_key_callback,
+		add_cert_callback)
+	self.add_option("-a","--anonymous", action="store_true",
+		dest="anonymous", default=False,
+		help="Do not include a user in the request")
+	self.add_option("-l","--label", action="store", dest="label",
+		type="string", help="Label for output")
+	if add_node_callback:
+	    self.add_option("-n", "--node", action="callback", type="string", 
+		    callback=add_node_callback, callback_args=(node_descs,),
+		    help="Node description: image:hardware[:count]")
+	self.add_option("-t", "--testbed", action="store", dest="testbed",
+		type="string",
+		help="Testbed identifier (URI) to contact (required)")
+
+
+
+
+class exp_data_base(abac_rpc):
+    def __init__(self, op='Info'):
+        """
+        Init the various conversions
+        """
+
+        abac_rpc.__init__(self, op)
+        # List of things one could ask for and what formatting routine is
+        # called.
+        self.params = {
+                'vis': ('vis', self.print_xml('vis')),
+                'vtopo': ('vtopo', self.print_xml('vtopo')),
+                'federant': ('federant', self.print_xml),
+                'id': ('experimentID', self.print_id),
+                'status': ('experimentStatus', self.print_string),
+                'log': ('allocationLog', self.print_string),
+                'access': ('experimentAccess', self.print_string),
+            }
+
+    # Utility functions
+    def print_string(self, d, out=sys.stdout):
+        print >>out, d
+
+    def print_id(self, d, out=sys.stdout):
+        if d:
+            for id in d:
+                for k in id.keys():
+                    print >>out, "%s: %s" % (k, id[k])
+
+    class print_xml:
+        """
+        Print the retrieved data is a simple xml representation of the dict.
+        """
+        def __init__(self, top):
+            self.xml = top
+
+        def __call__(self, d, out=sys.stdout):
+            str = "<%s>\n" % self.xml
+            for t in ('node', 'lan'):
+                if d.has_key(t):
+                    for x in d[t]:
+                        str += "<%s>" % t
+                        for k in x.keys():
+                            str += "<%s>%s</%s>" % (k, x[k],k)
+                        str += "</%s>\n" % t
+            str+= "</%s>" % self.xml
+            print >>out, str
+
+
+
+class context_data(exp_data_base):
+    def __init__(self):
+        exp_data_base.__init__(self, 'MultiInfo')
+
+
+    def __call__(self):
+        """
+        The control flow.  Compose the request and print the response.
+        """
+        # Process the options using the customized option parser defined above
+        parser = abac_multi_exp_data_opts()
+
+        (opts, args) = parser.parse_args()
+
+        if opts.trusted:
+            if ( not os.access(opts.trusted, os.R_OK) ) :
+                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+        if opts.debug > 0: opts.tracefile=sys.stderr
+
+        (user, cert) = self.get_user_info([])
+
+        if opts.cert != None: cert = opts.cert
+
+        if cert == None:
+            sys.exit("No certificate given (--cert) or found")
+
+        if opts.file == None:
+            sys.exit("No file given (--file)")
+
+        if os.access(cert, os.R_OK):
+            fid = fedid(file=cert)
+        else:
+            sys.exit("Cannot read certificate (%s)" % cert)
+
+        req = { 'ContextIn': { 'contextFile': opts.file } }
+
+        try:
+            resp_dict = self.do_rpc(req,
+                    opts.url, opts.transport, cert, opts.trusted,
+                    serialize_only=opts.serialize_only,
+                    tracefile=opts.tracefile)
+        except self.RPCException, e:
+            exit_with_fault(\
+                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+        except RuntimeError, e:
+            sys.exit("Error processing RPC: %s" % e)
+
+        exps = resp_dict.get('info', [])
+        if exps:
+            print '---'
+            for exp in exps:
+                for d in opts.data:
+                    key, output = self.params[d]
+                    try:
+                        if exp.has_key(key):
+                            output(exp[key])
+                    except RuntimeError, e:
+                        sys.exit("Bad response. %s" % e.message)
+                print '---'
+
+
+    def __call__(self):
+        """
+        The control flow.  Compose the request and print the response.
+        """
+        # Process the options using the customized option parser defined above
+        parser = fedd_image_opts()
+
+        (opts, args) = parser.parse_args()
+
+        if opts.trusted:
+            if ( not os.access(opts.trusted, os.R_OK) ) :
+                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+        if opts.debug > 0: opts.tracefile=sys.stderr
+
+        (user, cert) = self.get_user_info([])
+
+        if opts.cert != None: cert = opts.cert
+
+        if cert == None:
+            sys.exit("No certificate given (--cert) or found")
+
+        if os.access(cert, os.R_OK):
+            fid = fedid(file=cert)
+        else:
+            sys.exit("Cannot read certificate (%s)" % cert)
+
+        if opts.exp_name and opts.exp_certfile:
+            sys.exit("Only one of --experiment_cert and " +\
+                    "--experiment_name permitted");
+
+        if opts.exp_certfile:
+            exp_id = { 'fedid': fedid(file=opts.exp_certfile) }
+
+        if opts.exp_name:
+            exp_id = { 'localname' : opts.exp_name }
+
+        if opts.format and opts.outfile:
+            fmt = opts.format
+            file = opts.outfile
+        elif not opts.format and opts.outfile:
+            fmt = opts.outfile[-3:]
+            if fmt not in ("png", "jpg", "dot", "svg"):
+                sys.exit("Unexpected file type and no format specified")
+            file = opts.outfile
+        elif opts.format and not opts.outfile:
+            fmt = opts.format
+            file = None
+        else:
+            fmt="dot"
+            file = None
+
+
+        req = { 'experiment': exp_id }
+
+        try:
+            resp_dict = self.do_rpc(req,
+                    opts.url, opts.transport, cert, opts.trusted,
+                    serialize_only=opts.serialize_only,
+                    tracefile=opts.tracefile)
+        except self.RPCException, e:
+            exit_with_fault(\
+                    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+        except RuntimeError, e:
+            sys.exit("Error processing RPC: %s" % e)
+
+
+        if resp_dict.has_key('vtopo'):
+            self.gen_image(resp_dict['vtopo'],
+                    len(resp_dict['vtopo'].get('node', [])),
+                    file, fmt, opts.neato, opts.labels, opts.pixels)
+        else:
+            sys.exit("Bad response. %s" % e.message)
+
+
+class create(abac_rpc):
+    def __init__(self): 
+	abac_rpc.__init__(self, "CreateContext")
+    def __call__(self):
+	access_keys = []
+	# Process the options using the customized option parser defined above
+	parser = abac_create_opts(access_keys, self.add_ssh_key,
+		self.add_x509_cert)
+
+	(opts, args) = parser.parse_args()
+
+	if opts.trusted:
+	    if ( not os.access(opts.trusted, os.R_OK) ) :
+		sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+	if opts.debug > 0: opts.tracefile=sys.stderr
+
+	(user, cert) = self.get_user_info(access_keys)
+
+	if opts.user: user = opts.user
+
+	if opts.cert != None: cert = opts.cert
+
+	if cert == None:
+	    sys.exit("No certificate given (--cert) or found")
+
+	if os.access(cert, os.R_OK):
+	    fid = fedid(file=cert)
+	    if opts.use_fedid == True:
+		user = fid
+	else:
+	    sys.exit("Cannot read certificate (%s)" % cert)
+
+#	if opts.file:
+#	    exp_desc = ""
+#	    try:
+#		f = open(opts.file, 'r')
+#		for line in f:
+#		    exp_desc += line
+#		f.close()
+#	    except IOError:
+#		sys.exit("Cannot read description file (%s)" %opts.file)
+#	else:
+#	    sys.exit("Must specify an experiment description (--file)")
+#
+#	if not opts.master:
+#	    sys.exit("Must specify a master testbed (--master)")
+#
+#	out_certfile = opts.out_certfile
+
+	print "New context using file: %s" % opts.file
+#	msg = { 'ContextIn': { 'contextFile': opts.file } }
+	msg = { 'contextFile': opts.file }
+
+	if opts.debug > 1: print >>sys.stderr, msg
+
+	try:
+	    resp_dict = self.do_rpc(msg, 
+		    opts.url, opts.transport, cert, opts.trusted, 
+		    serialize_only=opts.serialize_only,
+		    tracefile=opts.tracefile)
+	except self.RPCException, e:
+	    exit_with_fault(\
+		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+	except RuntimeError, e:
+	    sys.exit("Error processing RPC: %s" % e)
+
+	if opts.debug > 1: print >>sys.stderr, resp_dict
+
+#	ea = resp_dict.get('experimentAccess', None)
+	id = resp_dict.get('contextID', None)
+	print "New context loaded w/id = %s" % id
+
+class negotiate(abac_rpc):
+    def __init__(self): 
+	abac_rpc.__init__(self, "Negotiate")
+    def __call__(self):
+	access_keys = []
+	# Process the options using the customized option parser defined above
+	parser = abac_create_opts(access_keys, self.add_ssh_key,
+		self.add_x509_cert)
+
+	(opts, args) = parser.parse_args()
+
+	if opts.trusted:
+	    if ( not os.access(opts.trusted, os.R_OK) ) :
+		sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+	if not opts.project :
+	    parser.error('--project is required')
+
+	if opts.debug > 0: opts.tracefile=sys.stderr
+
+	(user, cert) = self.get_user_info(access_keys)
+
+	if opts.user: user = opts.user
+
+	if opts.cert != None: cert = opts.cert
+
+	if cert == None:
+	    sys.exit("No certificate given (--cert) or found")
+
+	if os.access(cert, os.R_OK):
+	    fid = fedid(file=cert)
+	    if opts.use_fedid == True:
+		user = fid
+	else:
+	    sys.exit("Cannot read certificate (%s)" % cert)
+
+	if opts.file:
+	    exp_desc = ""
+	    try:
+		f = open(opts.file, 'r')
+		for line in f:
+		    exp_desc += line
+		f.close()
+	    except IOError:
+		sys.exit("Cannot read description file (%s)" %opts.file)
+	else:
+	    sys.exit("Must specify an experiment description (--file)")
+
+	if not opts.master:
+	    sys.exit("Must specify a master testbed (--master)")
+
+	out_certfile = opts.out_certfile
+
+	msg = {
+		'experimentdescription': { 'ns2description': exp_desc },
+		'master': opts.master,
+		'exportProject': { 'localname': opts.project },
+		'user' : [ {\
+			'userID': pack_id(user), \
+			'access': [ { a.type: a.buf } for a in access_keys]\
+			} ]
+		}
+
+	if opts.exp_name:
+	    msg['experimentID'] = { 'localname': opts.exp_name }
+
+	if opts.debug > 1: print >>sys.stderr, msg
+
+	try:
+	    resp_dict = self.do_rpc(msg, 
+		    opts.url, opts.transport, cert, opts.trusted, 
+		    serialize_only=opts.serialize_only,
+		    tracefile=opts.tracefile)
+	except self.RPCException, e:
+	    exit_with_fault(\
+		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+	except RuntimeError, e:
+	    sys.exit("Error processing RPC: %s" % e)
+
+	if opts.debug > 1: print >>sys.stderr, resp_dict
+
+	ea = resp_dict.get('goal')
+	if out_certfile and ea and ea.has_key('X509'):
+	    try:
+		f = open(out_certfile, "w")
+		print >>f, ea['X509']
+		f.close()
+	    except IOError:
+		sys.exit('Could not write to %s' %  out_certfile)
+	eid = resp_dict.get('experimentID', None)
+	if eid:
+	    for id in eid:
+		for k in id.keys():
+		    print "%s: %s" % (k, id[k])
+	st = resp_dict.get('experimentStatus', None)
+	if st:
+	    print "status: %s" % st
+
+class access(abac_rpc):
+    def __init__(self): 
+	abac_rpc.__init__(self, "Access")
+    def __call__(self):
+	access_keys = []
+	# Process the options using the customized option parser defined above
+	parser = abac_access_opts(access_keys, self.add_ssh_key,
+		self.add_x509_cert)
+
+	(opts, args) = parser.parse_args()
+
+	if opts.trusted:
+	    if ( not os.access(opts.trusted, os.R_OK) ) :
+		sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
+
+	if opts.debug > 0: opts.tracefile=sys.stderr
+
+	(user, cert) = self.get_user_info(access_keys)
+
+#	if opts.user: user = opts.user
+
+	if opts.cert != None: cert = opts.cert
+
+	if cert == None:
+	    sys.exit("No certificate given (--cert) or found")
+
+	if os.access(cert, os.R_OK):
+	    fid = fedid(file=cert)
+	    if opts.use_fedid == True:
+		user = fid
+	else:
+	    sys.exit("Cannot read certificate (%s)" % cert)
+
+	if not opts.id:
+	    sys.exit("Must specify a negotiator id (--id)")
+
+	if not opts.goal:
+	    sys.exit("Must specify a goal (--goal)")
+
+#	out_certfile = opts.out_certfile
+
+	msg = {
+                'context': { 'contextID': opts.id },
+                'goal': opts.goal,
+                'peerURL': opts.dstURL,
+                'peerContext' : { 'contextID': opts.dstContext },
+                'selfURL': opts.srcURL
+ 	}
+
+	if opts.debug > 1: print >>sys.stderr, msg
+
+	try:
+	    resp_dict = self.do_rpc(msg, 
+		    opts.url, opts.transport, cert, opts.trusted, 
+		    serialize_only=opts.serialize_only,
+		    tracefile=opts.tracefile)
+	except self.RPCException, e:
+	    exit_with_fault(\
+		    {'desc': e.desc, 'errstr': e.errstr, 'code': e.code})
+	except RuntimeError, e:
+	    sys.exit("Error processing RPC: %s" % e)
+
+	if opts.debug > 1: print >>sys.stderr, resp_dict
+
+	result = resp_dict.get('result', None)
+	goal = resp_dict.get('goal', None)
+        print "%s: %s" % (goal, result)
+
+def exit_with_fault(dict, out=sys.stderr):
+    """ Print an error message and exit.
+
+    The dictionary contains the FeddFaultBody elements."""
+    codestr = ""
+
+    if dict.has_key('errstr'):
+	codestr = "Error: %s" % dict['errstr']
+
+    if dict.has_key('code'):
+	if len(codestr) > 0 : 
+	    codestr += " (%d)" % dict['code']
+	else:
+	    codestr = "Error Code: %d" % dict['code']
+    print>>out, codestr
+    print>>out, "Description: %s" % dict['desc']
+    traceback.print_stack()
+    sys.exit(dict.get('code', 20))
+
+
+cmds = {\
+        'create': create(),\
+        'access': access()\
+#        'negotiate': negotiate(),\
+#        'status': status()\
+    }
+
+operation = cmds.get(sys.argv[1], None)
+if operation:
+    del sys.argv[1]
+    operation()
+else:
+    if sys.argv[1] == '--help':
+        sys.exit(\
+'''Only context sensitive help is available.  For one of the commands:
+
+%s
+
+type
+  %s command --help
+
+to get help, e.g., %s create --help
+''' % (", ".join(cmds.keys()), sys.argv[0], sys.argv[0]))
+    else:
+        sys.exit("Bad command: %s.  Valid ones are: %s" % \
+                (sys.argv[1], ", ".join(cmds.keys())))
+
