Ignore:
Timestamp:
Sep 9, 2008 2:07:18 PM (16 years ago)
Author:
Ted Faber <faber@…>
Branches:
axis_example, compt_changes, info-ops, master, version-1.30, version-2.00, version-3.01, version-3.02
Children:
45ebc4d
Parents:
4fc2250
Message:

get topo and vis data, persistent state

File:
1 edited

Legend:

Unmodified
Added
Removed
  • fedd/fedd_create.py

    r4fc2250 r987aaa1  
    2020
    2121from fedd_util import fedid, fedd_ssl_context, pack_soap, unpack_soap, \
    22         pack_id, unpack_id
     22        pack_id, unpack_id, encapsulate_binaries, decapsulate_binaries
    2323
    2424from optparse import OptionParser, OptionValueError
     
    7676                version="0.1")
    7777
    78         self.set_defaults(url="https://localhost:23235", anonymous=False,
    79                 serialize_only=False, transport="soap", use_fedid=False,
    80                 debug=0, file=None, master=None)
    81 
    8278        self.add_option("-c","--cert", action="store", dest="cert",
    8379                type="string", help="my certificate file")
    8480        self.add_option("-d", "--debug", action="count", dest="debug",
    85                 help="Set debug.  Repeat for more information")
    86         self.add_option("-f", "--file", dest="file",
    87                 help="experiment description file")
    88         self.add_option("-F","--useFedid", action="store_true",
    89                 dest="use_fedid",
    90                 help="Use a fedid derived from my certificate as user identity")
    91         self.add_option("-k", "--sshKey", action="callback", type="string",
    92                 callback=add_ssh_key, callback_args=(access_keys,),
    93                 help="ssh key for access (can be supplied more than once")
    94         self.add_option("-K", "--x509Key", action="callback", type="string",
    95                 callback=add_x509_cert, callback_args=(access_keys,),
    96                 help="X509 certificate for access " + \
    97                         "(can be supplied more than once")
    98         self.add_option("-m", "--master", dest="master",
    99                 help="Master testbed in the federation")
     81                default=0, help="Set debug.  Repeat for more information")
    10082        self.add_option("-s", "--serializeOnly", action="store_true",
    101                 dest="serialize_only",
     83                dest="serialize_only", default=False,
    10284                help="Print the SOAP request that would be sent and exit")
    10385        self.add_option("-T","--trusted", action="store", dest="trusted",
    10486                type="string", help="Trusted certificates (required)")
    10587        self.add_option("-u", "--url", action="store", dest="url",
    106                 type="string",
     88                type="string",default="https://localhost:23235", 
    10789                help="URL to connect to (default %default)")
    108         self.add_option("-U", "--username", action="store", dest="user",
    109                 type="string", help="Use this username instead of the uid")
    11090        self.add_option("-x","--transport", action="store", type="choice",
    111                 choices=("xmlrpc", "soap"),
     91                choices=("xmlrpc", "soap"), default="soap",
    11292                help="Transport for request (xmlrpc|soap) (Default: %default)")
    11393        self.add_option("--trace", action="store_const", dest="tracefile",
    11494                const=sys.stderr, help="Print SOAP exchange to stderr")
    11595
     96class fedd_create_opts(fedd_client_opts):
     97    def __init__(self, access_keys, add_key_callback=None,
     98            add_cert_callback=None):
     99        fedd_client_opts.__init__(self)
     100        self.add_option("-e", "--experiment_cert", dest="out_certfile",
     101                type="string", help="output certificate file")
     102        self.add_option("-F","--useFedid", action="store_true",
     103                dest="use_fedid", default=False,
     104                help="Use a fedid derived from my certificate as user identity")
     105        self.add_option("-f", "--file", dest="file",
     106                help="experiment description file")
     107        if add_key_callback:
     108            self.add_option("-k", "--sshKey", action="callback", type="string",
     109                    callback=add_key_callback, callback_args=(access_keys,),
     110                    help="ssh key for access (can be supplied more than once")
     111        if add_cert_callback:
     112            self.add_option("-K", "--x509Key", action="callback",
     113                    type="string", callback=add_cert_callback,
     114                    callback_args=(access_keys,),
     115                    help="X509 certificate for access " + \
     116                        "(can be supplied more than once")
     117        self.add_option("-m", "--master", dest="master",
     118                help="Master testbed in the federation")
     119        self.add_option("-U", "--username", action="store", dest="user",
     120                type="string", help="Use this username instead of the uid")
     121
    116122def exit_with_fault(dict, out=sys.stderr):
    117123    """ Print an error message and exit.
     
    133139    sys.exit(dict.get('code', 20))
    134140
    135 def add_ssh_key(option, opt_str, value, parser, access_keys):
    136     try:
    137         access_keys.append(access_method(file=value,
    138             type=access_method.type_ssh))
    139     except IOError, (errno, strerror):
    140         raise OptionValueError("Cannot generate sshPubkey from %s: %s (%d)" %
    141                 (value,strerror,errno))
    142 
    143 def add_x509_cert(option, opt_str, value, parser, access_keys):
    144     try:
    145         access_keys.append(access_method(file=value,
    146             type=access_method.type_x509))
    147     except IOError, (errno, strerror):
    148         raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
    149                 (value,strerror,errno))
    150 
    151 def get_user_info(access_keys):
    152     pw = pwd.getpwuid(os.getuid());
    153     try_cert=None
    154     user = None
    155 
    156     if pw != None:
    157         user = pw[0]
    158         try_cert = "%s/.ssl/emulab.pem" % pw[5];
    159         if not os.access(try_cert, os.R_OK):
    160             try_cert = None
    161         if len(access_keys) == 0:
    162             for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub",
    163                     "%s/.ssh/identity.pub"]:
    164                 try_key = k % pw[5];
    165                 if os.access(try_key, os.R_OK):
    166                     access_keys.append(access_method(file=try_key,
    167                         type=access_method.type_ssh))
    168                     break
    169     return (user, try_cert)
    170 
    171 access_keys = []
    172 
    173 # Process the options using the customized option parser defined above
    174 parser = fedd_client_opts()
    175 
    176 (opts, args) = parser.parse_args()
    177 
    178 if opts.trusted != None:
    179     if ( not os.access(opts.trusted, os.R_OK) ) :
    180         sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
     141class fedd_exp_data_opts(fedd_client_opts):
     142    def __init__(self):
     143        fedd_client_opts.__init__(self)
     144        self.add_option("-e", "--experiment_cert", dest="exp_certfile",
     145                type="string", help="output certificate file")
     146
     147# Querying experiment data follows the same control flow regardless of the
     148# specific data retrieved.  This class encapsulates that control flow.
     149class exp_data:
     150    def __init__(self, op):
     151        """
     152        Specialize the class for the type of data requested (op)
     153        """
     154        if op =='vtopo':
     155            self.RequestMessage = VtopoRequestMessage;
     156            self.ResponseMessage = VtopoResponseMessage;
     157            self.RequestBody="VtopoRequestBody"
     158            self.ResponseBody="VtopoResponseBody"
     159            self.method = "Vtopo"
     160            self.key="vtopo"
     161            self.xml='experiment'
     162        elif op == 'vis':
     163            self.RequestMessage = VisRequestMessage;
     164            self.ResponseMessage = VisResponseMessage;
     165            self.RequestBody="VisRequestBody"
     166            self.ResponseBody="VisResponseBody"
     167            self.method = "Vis"
     168            self.key="vis"
     169            self.xml='vis'
     170        else:
     171            raise TypeError("Bad op: %s" % op)
     172
     173    def print_xml(self, d, out=sys.stdout):
     174        """
     175        Print the retrieved data is a simple xml representation of the dict.
     176        """
     177        str = "<%s>\n" % self.xml
     178        for t in ('node', 'lan'):
     179            if d.has_key(t):
     180                for x in d[t]:
     181                    str += "<%s>" % t
     182                    for k in x.keys():
     183                        str += "<%s>%s</%s>" % (k, x[k],k)
     184                    str += "</%s>\n" % t
     185        str+= "</%s>" % self.xml
     186        print >>out, str
     187
     188    def __call__(self):
     189        """
     190        The control flow.  Compose the request and print the response.
     191        """
     192        # Process the options using the customized option parser defined above
     193        parser = fedd_exp_data_opts()
     194
     195        (opts, args) = parser.parse_args()
     196
     197        if opts.trusted != None:
     198            if ( not os.access(opts.trusted, os.R_OK) ) :
     199                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
     200        else:
     201            parser.error("--trusted is required")
     202
     203        if opts.debug > 0: opts.tracefile=sys.stderr
     204
     205        if opts.cert != None: cert = opts.cert
     206
     207        if cert == None:
     208            sys.exit("No certificate given (--cert) or found")
     209
     210        if os.access(cert, os.R_OK):
     211            fid = fedid(file=cert)
     212        else:
     213            sys.exit("Cannot read certificate (%s)" % cert)
     214
     215        if opts.exp_certfile:
     216            exp_fedid = fedid(file=opts.exp_certfile)
     217        else:
     218            sys.exit("Experiment certfile required")
     219
     220        context = None
     221        while context == None:
     222            try:
     223                context = fedd_ssl_context(cert, opts.trusted)
     224            except SSL.SSLError, e:
     225                # Yes, doing this on message type is not ideal.  The string
     226                # comes from OpenSSL, so check there is this stops working.
     227                if str(e) == "bad decrypt":
     228                    print >>sys.stderr, "Bad Passphrase given."
     229                else: raise
     230
     231        msg = { 'experiment': { 'fedid': exp_fedid } }
     232
     233        if opts.debug > 1: print >>sys.stderr, msg
     234
     235        if opts.transport == "soap":
     236            loc = feddServiceLocator();
     237            port = loc.getfeddPortType(opts.url,
     238                    transport=M2Crypto.httpslib.HTTPSConnection,
     239                    transdict={ 'ssl_context' : context },
     240                    tracefile=opts.tracefile)
     241
     242            req = self.RequestMessage()
     243
     244            set_req = getattr(req, "set_element_%s" % self.RequestBody, None)
     245
     246            set_req(pack_soap(req, self.RequestBody, msg))
     247
     248            if opts.serialize_only:
     249                sw = SoapWriter()
     250                sw.serialize(req)
     251                print str(sw)
     252                sys.exit(0)
     253
     254            try:
     255                method_call = getattr(port, self.method, None)
     256                resp = method_call(req)
     257            except ZSI.ParseException, e:
     258                sys.exit("Malformed response (XMLPRC?): %s" % e)
     259            except ZSI.FaultException, e:
     260                resp = e.fault.detail[0]
     261
     262            if resp:
     263                resp_call = getattr(resp, "get_element_%s" %self.ResponseBody,
     264                        None)
     265                if resp_call:
     266                    resp_body = resp_call()
     267                    if ( resp_body != None):
     268                        try:
     269                            resp_dict = unpack_soap(resp_body)
     270                            if opts.debug > 1: print >>sys.stderr, resp_dict
     271                            if resp_dict.has_key(self.key):
     272                                self.print_xml(resp_dict[self.key])
     273                        except RuntimeError, e:
     274                            sys.exit("Bad response. %s" % e.message)
     275                elif 'get_element_FeddFaultBody' in dir(resp):
     276                    resp_body = resp.get_element_FeddFaultBody()
     277                    if resp_body != None:
     278                        exit_with_fault(unpack_soap(resp_body))
     279                else: sys.exit("No body in response!?")
     280            else: sys.exit("No response?!?")
     281        elif opts.transport == "xmlrpc":
     282            if opts.serialize_only:
     283                ser = dumps((msg,))
     284                print ser
     285                sys.exit(0)
     286
     287            transport = SSL_Transport(context)
     288            port = ServerProxy(opts.url, transport=transport)
     289
     290            try:
     291                method_call = getattr(port, self.method, None)
     292                resp = method_call(
     293                        encapsulate_binaries({ self.RequestBody: msg},\
     294                            ('fedid',)))
     295            except Error, e:
     296                resp = { 'FeddFaultBody': \
     297                        { 'errstr' : e.faultCode, 'desc' : e.faultString } }
     298            if resp:
     299                if resp.has_key(self.ResponseBody):
     300                    try:
     301                        resp_dict = resp[self.ResponseBody]
     302                        if opts.debug > 1: print >>sys.stderr, resp_dict
     303                        if resp_dict.has_key(self.key):
     304                            self.print_xml(resp_dict[self.key])
     305                    except RuntimeError, e:
     306                        sys.exit("Bad response. %s" % e.messgae)
     307                elif resp.has_key('FeddFaultBody'):
     308                    exit_with_fault(resp['FeddFaultBody'])
     309                else: sys.exit("No body in response!?")
     310            else: sys.exit("No response?!?")
     311
     312class create:
     313    def __init__(self): pass
     314
     315    def add_ssh_key(self, option, opt_str, value, parser, access_keys):
     316        try:
     317            access_keys.append(access_method(file=value,
     318                type=access_method.type_ssh))
     319        except IOError, (errno, strerror):
     320            raise OptionValueError("Cannot generate sshPubkey from %s: "\
     321                    "%s (%d)" % (value,strerror,errno))
     322
     323    def add_x509_cert(self, option, opt_str, value, parser, access_keys):
     324        try:
     325            access_keys.append(access_method(file=value,
     326                type=access_method.type_x509))
     327        except IOError, (errno, strerror):
     328            raise OptionValueError("Cannot read x509 cert from %s: %s (%d)" %
     329                    (value,strerror,errno))
     330
     331    def get_user_info(self, access_keys):
     332        pw = pwd.getpwuid(os.getuid());
     333        try_cert=None
     334        user = None
     335
     336        if pw != None:
     337            user = pw[0]
     338            try_cert = "%s/.ssl/emulab.pem" % pw[5];
     339            if not os.access(try_cert, os.R_OK):
     340                try_cert = None
     341            if len(access_keys) == 0:
     342                for k in ["%s/.ssh/id_rsa.pub", "%s/.ssh/id_dsa.pub",
     343                        "%s/.ssh/identity.pub"]:
     344                    try_key = k % pw[5];
     345                    if os.access(try_key, os.R_OK):
     346                        access_keys.append(access_method(file=try_key,
     347                            type=access_method.type_ssh))
     348                        break
     349        return (user, try_cert)
     350
     351    def __call__(self):
     352        access_keys = []
     353        # Process the options using the customized option parser defined above
     354        parser = fedd_create_opts(access_keys, self.add_ssh_key,
     355                self.add_x509_cert)
     356
     357        (opts, args) = parser.parse_args()
     358
     359        if opts.trusted != None:
     360            if ( not os.access(opts.trusted, os.R_OK) ) :
     361                sys.exit("Cannot read trusted certificates (%s)" % opts.trusted)
     362        else:
     363            parser.error("--trusted is required")
     364
     365        if opts.debug > 0: opts.tracefile=sys.stderr
     366
     367        (user, cert) = self.get_user_info(access_keys)
     368
     369        if opts.user: user = opts.user
     370
     371        if opts.cert != None: cert = opts.cert
     372
     373        if cert == None:
     374            sys.exit("No certificate given (--cert) or found")
     375
     376        if os.access(cert, os.R_OK):
     377            fid = fedid(file=cert)
     378            if opts.use_fedid == True:
     379                user = fid
     380        else:
     381            sys.exit("Cannot read certificate (%s)" % cert)
     382
     383        if opts.file:
     384            exp_desc = ""
     385            try:
     386                f = open(opts.file, 'r')
     387                for line in f:
     388                    exp_desc += line
     389                f.close()
     390            except IOError:
     391                sys.exit("Cannot read description file (%s)" %opts.file)
     392        else:
     393            sys.exit("Must specify an experiment description (--file)")
     394
     395        if not opts.master:
     396            sys.exit("Must specify a master testbed (--master)")
     397
     398        out_certfile = opts.out_certfile
     399
     400        context = None
     401        while context == None:
     402            try:
     403                context = fedd_ssl_context(cert, opts.trusted)
     404            except SSL.SSLError, e:
     405                # Yes, doing this on message type is not ideal.  The string comes
     406                # from OpenSSL, so check there is this stops working.
     407                if str(e) == "bad decrypt":
     408                    print >>sys.stderr, "Bad Passphrase given."
     409                else: raise
     410
     411        msg = {
     412                'experimentdescription': exp_desc,
     413                'master': opts.master,
     414                'user' : [ {\
     415                        'userID': pack_id(user), \
     416                        'access': [ { a.type: a.buf } for a in access_keys]\
     417                        } ]
     418                }
     419
     420        if opts.debug > 1: print >>sys.stderr, msg
     421
     422        if opts.transport == "soap":
     423            loc = feddServiceLocator();
     424            port = loc.getfeddPortType(opts.url,
     425                    transport=M2Crypto.httpslib.HTTPSConnection,
     426                    transdict={ 'ssl_context' : context },
     427                    tracefile=opts.tracefile)
     428
     429            req = CreateRequestMessage()
     430
     431            req.set_element_CreateRequestBody(
     432                    pack_soap(req, "CreateRequestBody", msg))
     433
     434            if opts.serialize_only:
     435                sw = SoapWriter()
     436                sw.serialize(req)
     437                print str(sw)
     438                sys.exit(0)
     439
     440            try:
     441                resp = port.Create(req)
     442            except ZSI.ParseException, e:
     443                sys.exit("Malformed response (XMLPRC?): %s" % e)
     444            except ZSI.FaultException, e:
     445                resp = e.fault.detail[0]
     446
     447            if resp:
     448                if 'get_element_CreateResponseBody' in dir(resp):
     449                    resp_body = resp.get_element_CreateResponseBody()
     450                    if ( resp_body != None):
     451                        try:
     452                            resp_dict = unpack_soap(resp_body)
     453                            if opts.debug > 1: print >>sys.stderr, resp_dict
     454                            ea = resp_dict.get('experimentAccess', None)
     455                            if out_certfile and ea and ea.has_key('X509'):
     456                                try:
     457                                    f = open(out_certfile, "w")
     458                                    print >>f, ea['X509']
     459                                    f.close()
     460                                except IOError:
     461                                    sys.exit('Could not write to %s' % \
     462                                            out_certfile)
     463                        except RuntimeError, e:
     464                            sys.exit("Bad response. %s" % e.message)
     465                elif 'get_element_FeddFaultBody' in dir(resp):
     466                    resp_body = resp.get_element_FeddFaultBody()
     467                    if resp_body != None:
     468                        exit_with_fault(unpack_soap(resp_body))
     469                else: sys.exit("No body in response!?")
     470            else: sys.exit("No response?!?")
     471        elif opts.transport == "xmlrpc":
     472            if opts.serialize_only:
     473                ser = dumps((msg,))
     474                print ser
     475                sys.exit(0)
     476
     477            transport = SSL_Transport(context)
     478            port = ServerProxy(opts.url, transport=transport)
     479
     480            try:
     481                resp = port.Create({ 'CreateRequestBody': msg})
     482            except Error, e:
     483                resp = { 'FeddFaultBody': \
     484                        { 'errstr' : e.faultCode, 'desc' : e.faultString } }
     485            if resp:
     486                if resp.has_key('CreateResponseBody'):
     487                    try:
     488                        resp_dict = resp['CreateResponseBody']
     489                        decapsulate_binaries(resp_dict, ('fedid',))
     490                        if opts.debug > 1: print >>sys.stderr, resp_dict
     491                        ea = resp_dict.get('experimentAccess', None)
     492                        if out_certfile and ea and ea.has_key('X509'):
     493                            try:
     494                                f = open(out_certfile, "w")
     495                                print >>f, ea['X509']
     496                                f.close()
     497                            except IOError:
     498                                sys.exit('Could not write to %s' % out_certfile)
     499                    except RuntimeError, e:
     500                        sys.exit("Bad response. %s" % e.messgae)
     501                elif resp.has_key('FeddFaultBody'):
     502                    exit_with_fault(resp['FeddFaultBody'])
     503                else: sys.exit("No body in response!?")
     504
     505            else: sys.exit("No response?!?")
     506
     507valid_cmds = ['create']
     508
     509f = None
     510if sys.argv[1] == 'create':
     511    del sys.argv[1]
     512    f = create()
     513elif sys.argv[1] == 'vtopo':
     514    del sys.argv[1]
     515    f = exp_data('vtopo')
     516elif sys.argv[1] == 'vis':
     517    del sys.argv[1]
     518    f = exp_data('vis')
    181519else:
    182     parser.error("--trusted is required")
    183 
    184 if opts.debug > 0: opts.tracefile=sys.stderr
    185 
    186 (user, cert) = get_user_info(access_keys)
    187 
    188 if opts.user: user = opts.user
    189 
    190 if opts.cert != None: cert = opts.cert
    191 
    192 if cert == None:
    193     sys.exit("No certificate given (--cert) or found")
    194 
    195 if os.access(cert, os.R_OK):
    196     fid = fedid(file=cert)
    197     if opts.use_fedid == True:
    198         user = fid
    199 else:
    200     sys.exit("Cannot read certificate (%s)" % cert)
    201 
    202 if opts.file:
    203     exp_desc = ""
    204     try:
    205         f = open(opts.file, 'r')
    206         for line in f:
    207             exp_desc += line
    208         f.close()
    209     except IOError:
    210         sys.exit("Cannot read description file (%s)" %opts.file)
    211 else:
    212     sys.exit("Must specify an experiment description (--file)")
    213 
    214 if not opts.master:
    215     sys.exit("Must specify a master testbed (--master)")
    216 
    217 
    218 context = None
    219 while context == None:
    220     try:
    221         context = fedd_ssl_context(cert, opts.trusted)
    222     except SSL.SSLError, e:
    223         # Yes, doing this on message type is not ideal.  The string comes from
    224         # OpenSSL, so check there is this stops working.
    225         if str(e) == "bad decrypt":
    226             print >>sys.stderr, "Bad Passphrase given."
    227         else: raise
    228 
    229 msg = {
    230         'experimentdescription': exp_desc,
    231         'master': opts.master,
    232         'user' : [ {\
    233                 'userID': pack_id(user), \
    234                 'access': [ { a.type: a.buf } for a in access_keys]\
    235                 } ]
    236         }
    237 
    238 if opts.debug > 1: print >>sys.stderr, msg
    239 
    240 if opts.transport == "soap":
    241     loc = feddServiceLocator();
    242     port = loc.getfeddPortType(opts.url,
    243             transport=M2Crypto.httpslib.HTTPSConnection,
    244             transdict={ 'ssl_context' : context },
    245             tracefile=opts.tracefile)
    246 
    247     req = CreateRequestMessage()
    248 
    249     req.set_element_CreateRequestBody(
    250             pack_soap(req, "CreateRequestBody", msg))
    251 
    252     if opts.serialize_only:
    253         sw = SoapWriter()
    254         sw.serialize(req)
    255         print str(sw)
    256         sys.exit(0)
    257 
    258     try:
    259         resp = port.Create(req)
    260     except ZSI.ParseException, e:
    261         sys.exit("Malformed response (XMLPRC?): %s" % e)
    262     except ZSI.FaultException, e:
    263         resp = e.fault.detail[0]
    264 
    265     if resp:
    266         if 'get_element_CreateResponseBody' in dir(resp):
    267             resp_body = resp.get_element_CreateResponseBody()
    268             if ( resp_body != None):
    269                 try:
    270                     resp_dict = unpack_soap(resp_body)
    271                     if opts.debug > 1: print >>sys.stderr, resp_dict
    272                 except RuntimeError, e:
    273                     sys.exit("Bad response. %s" % e.message)
    274         elif 'get_element_FeddFaultBody' in dir(resp):
    275             resp_body = resp.get_element_FeddFaultBody()
    276             if resp_body != None:
    277                 exit_with_fault(unpack_soap(resp_body))
    278         else: sys.exit("No body in response!?")
    279     else: sys.exit("No response?!?")
    280 elif opts.transport == "xmlrpc":
    281     if opts.serialize_only:
    282         ser = dumps((msg,))
    283         print ser
    284         sys.exit(0)
    285 
    286     transport = SSL_Transport(context)
    287     port = ServerProxy(opts.url, transport=transport)
    288 
    289     try:
    290         resp = port.Create({ 'CreateRequestBody': msg})
    291     except Error, e:
    292         resp = { 'FeddFaultBody': \
    293                 { 'errstr' : e.faultCode, 'desc' : e.faultString } }
    294     if resp:
    295         if resp.has_key('CreateResponseBody'):
    296             try:
    297                 resp_dict = resp['CreateResponseBody']
    298                 if opts.debug > 1: print >>sys.stderr, resp_dict
    299             except RuntimeError, e:
    300                 sys.exit("Bad response. %s" % e.messgae)
    301         elif resp.has_key('FeddFaultBody'):
    302             exit_with_fault(resp['FeddFaultBody'])
    303         else: sys.exit("No body in response!?")
    304 
    305     else: sys.exit("No response?!?")
     520    sys.exit("Bad command: %s.  Valid ones are: %s" % \
     521            (sys.argv[1], ", ".join(valid_cmds)))
     522
     523if f: f()
     524else: sys.exit("Null function?!?")
Note: See TracChangeset for help on using the changeset viewer.