added -a --aggregate for resouces and create calls
[sfa.git] / sfa / client / sfi.py
1 #! /usr/bin/env python
2
3 # sfi -- slice-based facility interface
4
5 import sys
6 import os, os.path
7 import tempfile
8 import traceback
9 from types import StringTypes, ListType
10 from optparse import OptionParser
11
12 from sfa.trust.certificate import Keypair, Certificate
13 from sfa.trust.credential import Credential
14
15 from sfa.util.geniclient import GeniClient
16 from sfa.util.record import *
17 from sfa.util.rspec import Rspec
18 from sfa.util.xmlrpcprotocol import ServerException
19 from sfa.util.config import Config
20
21 class Sfi:
22     
23     slicemgr = None
24     registry = None
25     user = None
26     authority = None
27     options = None
28     
29     #
30     # Establish Connection to SliceMgr and Registry Servers
31     #
32     def set_servers(self):
33        config_file = self.options.sfi_dir + os.sep + "sfi_config"
34        try:
35           config = Config (config_file)
36        except:
37           print "Failed to read configuration file",config_file
38           print "Make sure to remove the export clauses and to add quotes"
39           if not self.options.verbose:
40              print "Re-run with -v for more details"
41           else:
42              traceback.print_exc()
43           sys.exit(1)
44     
45        errors=0
46        # Set SliceMgr URL
47        if (self.options.sm is not None):
48           sm_url = self.options.sm
49        elif hasattr(config,"SFI_SM"):
50           sm_url = config.SFI_SM
51        else:
52           print "You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s"%config_file
53           errors +=1 
54     
55        # Set Registry URL
56        if (self.options.registry is not None):
57           reg_url = self.options.registry
58        elif hasattr(config,"SFI_REGISTRY"):
59           reg_url = config.SFI_REGISTRY
60        else:
61           print "You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s"%config_file
62           errors +=1 
63     
64        # Set user HRN
65        if (self.options.user is not None):
66           self.user = self.options.user
67        elif hasattr(config,"SFI_USER"):
68           self.user = config.SFI_USER
69        else:
70           print "You need to set e.g. SFI_USER='plc.princeton.username' in %s"%config_file
71           errors +=1 
72     
73        # Set authority HRN
74        if (self.options.auth is not None):
75           self.authority = self.options.auth
76        elif hasattr(config,"SFI_AUTH"):
77           self.authority = config.SFI_AUTH
78        else:
79           print "You need to set e.g. SFI_AUTH='plc.princeton' in %s"%config_file
80           errors +=1 
81     
82        if errors:
83           sys.exit(1)
84     
85        if self.options.verbose :
86           print "Contacting Slice Manager at:", sm_url
87           print "Contacting Registry at:", reg_url
88     
89        # Get key and certificate
90        key_file = self.get_key_file()
91        cert_file = self.get_cert_file(key_file)
92        self.key_file = key_file
93        self.cert_file = cert_file 
94        # Establish connection to server(s)
95        self.slicemgr = GeniClient(sm_url, key_file, cert_file, self.options.protocol)
96        self.registry = GeniClient(reg_url, key_file, cert_file, self.options.protocol)
97        return
98     
99     #
100     # Get various credential and spec files
101     #
102     # Establishes limiting conventions
103     #   - conflates MAs and SAs
104     #   - assumes last token in slice name is unique
105     #
106     # Bootstraps credentials
107     #   - bootstrap user credential from self-signed certificate
108     #   - bootstrap authority credential from user credential
109     #   - bootstrap slice credential from user credential
110     #
111     
112     def get_leaf(self,name):
113        parts = name.split(".")
114        return parts[-1]
115     
116     def get_key_file(self):
117        file = os.path.join(self.options.sfi_dir, self.get_leaf(self.user) + ".pkey")
118        if (os.path.isfile(file)):
119           return file
120        else:
121           print "Key file", file, "does not exist"
122           sys.exit(-1)
123        return
124     
125     def get_cert_file(self,key_file):
126     
127        file = os.path.join(self.options.sfi_dir, self.get_leaf(self.user) + ".cert")
128        if (os.path.isfile(file)):
129           return file
130        else:
131           k = Keypair(filename = key_file)
132           cert = Certificate(subject=self.user)
133           cert.set_pubkey(k)
134           cert.set_issuer(k, self.user)
135           cert.sign()
136           if self.options.verbose :
137              print "Writing self-signed certificate to", file
138           cert.save_to_file(file)
139           return file
140     
141     def get_user_cred(self):
142        file = os.path.join(self.options.sfi_dir, self.get_leaf(self.user) + ".cred")
143        if (os.path.isfile(file)):
144           user_cred = Credential(filename=file)
145           return user_cred
146        else:
147           # bootstrap user credential
148           user_cred = self.registry.get_credential(None, "user", self.user)
149           if user_cred:
150              user_cred.save_to_file(file, save_parents=True)
151              if self.options.verbose:
152                 print "Writing user credential to", file
153              return user_cred
154           else:
155              print "Failed to get user credential"
156              sys.exit(-1)
157     
158     def get_auth_cred(self):
159     
160        if not self.authority:
161           print "no authority specified. Use -a or set SF_AUTH"
162           sys.exit(-1)
163     
164        file = os.path.join(self.options.sfi_dir, self.get_leaf("authority") +".cred")
165        if (os.path.isfile(file)):
166           auth_cred = Credential(filename=file)
167           return auth_cred
168        else:
169           # bootstrap authority credential from user credential
170           user_cred = self.get_user_cred()
171           auth_cred = self.registry.get_credential(user_cred, "authority", self.authority)
172           if auth_cred:
173              auth_cred.save_to_file(file, save_parents=True)
174              if self.options.verbose:
175                 print "Writing authority credential to", file
176              return auth_cred
177           else:
178              print "Failed to get authority credential"
179              sys.exit(-1)
180     
181     def get_slice_cred(self,name):
182        file = os.path.join(self.options.sfi_dir, "slice_" + self.get_leaf(name) + ".cred")
183        if (os.path.isfile(file)):
184           slice_cred = Credential(filename=file)
185           return slice_cred
186        else:
187           # bootstrap slice credential from user credential
188           user_cred = self.get_user_cred()
189           slice_cred = self.registry.get_credential(user_cred, "slice", name)
190           if slice_cred:
191              slice_cred.save_to_file(file, save_parents=True)
192              if self.options.verbose:
193                 print "Writing slice credential to", file
194              return slice_cred
195           else:
196              print "Failed to get slice credential"
197              sys.exit(-1)
198     
199     def delegate_cred(self,cred, hrn, type = 'authority'):
200         # the gid and hrn of the object we are delegating
201         object_gid = cred.get_gid_object()
202         object_hrn = object_gid.get_hrn()
203         cred.set_delegate(True)
204         if not cred.get_delegate():
205             raise Exception, "Error: Object credential %(object_hrn)s does not have delegate bit set" % locals()
206            
207     
208         records = self.registry.resolve(cred, hrn)
209         records = self.filter_records(type, records)
210         
211         if not records:
212             raise Exception, "Error: Didn't find a %(type)s record for %(hrn)s" % locals()
213     
214         # the gid of the user who will be delegated too
215         delegee_gid = records[0].get_gid_object()
216         delegee_hrn = delegee_gid.get_hrn()
217         
218         # the key and hrn of the user who will be delegating
219         user_key = Keypair(filename = self.get_key_file())
220         user_hrn = cred.get_gid_caller().get_hrn()
221     
222         dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
223         dcred.set_gid_caller(delegee_gid)
224         dcred.set_gid_object(object_gid)
225         dcred.set_privileges(cred.get_privileges())
226         dcred.set_delegate(True)
227         dcred.set_pubkey(object_gid.get_pubkey())
228         dcred.set_issuer(user_key, user_hrn)
229         dcred.set_parent(cred)
230         dcred.encode()
231         dcred.sign()
232     
233         return dcred
234     
235     def get_rspec_file(self,rspec):
236        if (os.path.isabs(rspec)):
237           file = rspec
238        else:
239           file = os.path.join(self.options.sfi_dir, rspec)
240        if (os.path.isfile(file)):
241           return file
242        else:
243           print "No such rspec file", rspec
244           sys.exit(1)
245     
246     def get_record_file(self,record):
247        if (os.path.isabs(record)):
248           file = record
249        else:
250           file = os.path.join(self.options.sfi_dir, record)
251        if (os.path.isfile(file)):
252           return file
253        else:
254           print "No such registry record file", record
255           sys.exit(1)
256     
257     def load_publickey_string(self,fn):
258        f = file(fn,"r")
259        key_string = f.read()
260     
261        # if the filename is a private key file, then extract the public key
262        if "PRIVATE KEY" in key_string:
263            outfn = tempfile.mktemp()
264            cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
265            os.system(cmd)
266            f = file(outfn, "r")
267            key_string = f.read()
268            os.remove(outfn)
269     
270        return key_string
271     #
272     # Generate sub-command parser
273     #
274     def create_cmd_parser(self,command, additional_cmdargs = None):
275        cmdargs = {"list": "name",
276                   "show": "name",
277                   "remove": "name",
278                   "add": "record",
279                   "update": "record",
280                   "aggregates": "[name]",
281                   "registries": "[name]",   
282                   "slices": "",
283                   "resources": "[name]",
284                   "create": "name rspec",
285                   "delete": "name",
286                   "reset": "name",
287                   "start": "name",
288                   "stop": "name",
289                   "delegate": "name"
290                  }
291     
292        if additional_cmdargs:
293           cmdargs.update(additional_cmdargs)
294     
295        if command not in cmdargs:
296           print "Invalid command\n"
297           print "Commands: ",
298           for key in cmdargs.keys():
299               print key+",",
300           print ""
301           sys.exit(2)
302     
303        parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
304           % (command, cmdargs[command]))
305
306        if command in ("resources"):
307            parser.add_option("-f", "--format", dest="format",type="choice",
308                              help="display format ([xml]|dns|ip)",default="xml",
309                              choices=("xml","dns","ip"))
310            parser.add_option("-a", "--aggregate", dest="aggregate",
311                              default=None, help="aggregate hrn")  
312     
313        if command in ("create"):
314            parser.add_option("-a", "--aggregate", dest="aggregate",default=None,
315                              help="aggregate hrn") 
316  
317        if command in ("list", "show", "remove"):
318           parser.add_option("-t", "--type", dest="type",type="choice",
319                             help="type filter ([all]|user|slice|sa|ma|node|aggregate)",
320                             choices=("all","user","slice","sa","ma","node","aggregate"),
321                             default="all")
322
323        if command in ("resources", "show", "list"):
324           parser.add_option("-o", "--output", dest="file",
325                             help="output XML to file", metavar="FILE", default=None)
326
327        if command in ("show", "list"):
328            parser.add_option("-f", "--format", dest="format", type="choice", 
329                              help="display format ([text]|xml)",default="text", 
330                              choices=("text","xml")) 
331
332        if command in ("delegate"):
333           parser.add_option("-u", "--user",
334                             action="store_true", dest="delegate_user", default=False,
335                             help="delegate user credential")
336           parser.add_option("-s", "--slice", dest="delegate_slice",
337                             help="delegate slice credential", metavar="HRN", default=None)
338        return parser
339     
340     def create_parser(self):
341
342        # Generate command line parser
343        parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
344                              description="Commands: list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
345        parser.add_option("-r", "--registry", dest="registry",
346                          help="root registry", metavar="URL", default=None)
347        parser.add_option("-s", "--slicemgr", dest="sm",
348                          help="slice manager", metavar="URL", default=None)
349        default_sfi_dir=os.path.expanduser("~/.sfi/")
350        parser.add_option("-d", "--dir", dest="sfi_dir",
351                          help="config & working directory - default is " + default_sfi_dir,
352                          metavar="PATH", default = default_sfi_dir)
353        parser.add_option("-u", "--user", dest="user",
354                          help="user name", metavar="HRN", default=None)
355        parser.add_option("-a", "--auth", dest="auth",
356                          help="authority name", metavar="HRN", default=None)
357        parser.add_option("-v", "--verbose",
358                          action="store_true", dest="verbose", default=False,
359                          help="verbose mode")
360        parser.add_option("-p", "--protocol",
361                          dest="protocol", default="xmlrpc",
362                          help="RPC protocol (xmlrpc or soap)")
363        parser.disable_interspersed_args()
364     
365        return parser
366     
367     def dispatch(self,command, cmd_opts, cmd_args):
368        getattr(self,command)(cmd_opts, cmd_args)
369     
370     #
371     # Following functions implement the commands
372     #
373     # Registry-related commands
374     #
375     
376     # list entires in named authority registry
377     def list(self,opts, args):
378        user_cred = self.get_user_cred()
379        try:
380           list = self.registry.list(user_cred, args[0])
381        except IndexError:
382           raise Exception, "Not enough parameters for the 'list' command"
383           
384        # filter on person, slice, site, node, etc.  
385        # THis really should be in the self.filter_records funct def comment...
386        list = self.filter_records(opts.type, list)
387        for record in list:
388            print "%s (%s)" % (record['hrn'], record['type'])     
389        if opts.file:
390            self.save_records_to_file(opts.file, list)
391        return
392     
393     # show named registry record
394     def show(self,opts, args):
395        user_cred = self.get_user_cred()
396        records = self.registry.resolve(user_cred, args[0])
397        records = self.filter_records(opts.type, records)
398        if not records:
399           print "No record of type", opts.type
400        for record in records:
401            if record['type'] in ['user']:
402                record = UserRecord(dict = record)
403            elif record['type'] in ['slice']:
404                record = SliceRecord(dict = record)
405            elif record['type'] in ['node']:
406                record = NodeRecord(dict = record)
407            elif record['type'] in ['authority', 'ma', 'sa']:
408                record = AuthorityRecord(dict = record)
409            else:
410                record = GeniRecord(dict = record)
411            if (opts.format=="text"): 
412                record.dump()  
413            else: 
414                print record.save_to_string() 
415        
416        if opts.file:
417            self.save_records_to_file(opts.file, records)
418        return
419     
420     def delegate(self,opts, args):
421        user_cred = self.get_user_cred()
422        if opts.delegate_user:
423            object_cred = user_cred
424        elif opts.delegate_slice:
425            object_cred = self.get_slice_cred(opts.delegate_slice)
426        else:
427            print "Must specify either --user or --slice <hrn>"
428            return
429     
430        # the gid and hrn of the object we are delegating
431        object_gid = object_cred.get_gid_object()
432        object_hrn = object_gid.get_hrn()
433     
434        if not object_cred.get_delegate():
435            print "Error: Object credential", object_hrn, "does not have delegate bit set"
436            return
437     
438        records = self.registry.resolve(user_cred, args[0])
439        records = self.filter_records("user", records)
440     
441        if not records:
442            print "Error: Didn't find a user record for", args[0]
443            return
444     
445        # the gid of the user who will be delegated too
446        delegee_gid = records[0].get_gid_object()
447        delegee_hrn = delegee_gid.get_hrn()
448     
449        # the key and hrn of the user who will be delegating
450        user_key = Keypair(filename = self.get_key_file())
451        user_hrn = user_cred.get_gid_caller().get_hrn()
452     
453        dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
454        dcred.set_gid_caller(delegee_gid)
455        dcred.set_gid_object(object_gid)
456        dcred.set_privileges(object_cred.get_privileges())
457        dcred.set_delegate(True)
458        dcred.set_pubkey(object_gid.get_pubkey())
459        dcred.set_issuer(user_key, user_hrn)
460        dcred.set_parent(object_cred)
461        dcred.encode()
462        dcred.sign()
463     
464        if opts.delegate_user:
465            dest_fn = os.path.join(self.options.sfi_dir, self.get_leaf(delegee_hrn) + "_" 
466                                   + self.get_leaf(object_hrn) + ".cred")
467        elif opts.delegate_slice:
468            dest_fn = os.path_join(self.options.sfi_dir, self.get_leaf(delegee_hrn) + "_slice_" 
469                                   + self.get_leaf(object_hrn) + ".cred")
470     
471        dcred.save_to_file(dest_fn, save_parents = True)
472     
473        print "delegated credential for", object_hrn, "to", delegee_hrn, "and wrote to", dest_fn
474     
475     # removed named registry record
476     #   - have to first retrieve the record to be removed
477     def remove(self,opts, args):
478        auth_cred = self.get_auth_cred()
479        type = opts.type 
480        if type in ['all']:
481            type = '*'                   
482        return self.registry.remove(auth_cred, type, args[0])
483     
484     # add named registry record
485     def add(self,opts, args):
486        auth_cred = self.get_auth_cred()
487        rec_file = self.get_record_file(args[0])
488        record = self.load_record_from_file(rec_file)
489     
490        return self.registry.register(auth_cred, record)
491     
492     # update named registry entry
493     def update(self,opts, args):
494        user_cred = self.get_user_cred()
495        rec_file = self.get_record_file(args[0])
496        record = self.load_record_from_file(rec_file)
497        if record.get_type() == "user":
498            if record.get_name() == user_cred.get_gid_object().get_hrn():
499               cred = user_cred
500            else:
501               cred = self.get_auth_cred()
502        elif record.get_type() in ["slice"]:
503            try:
504                cred = self.get_slice_cred(record.get_name())
505            except ServerException, e:
506                # XXX smbaker -- once we have better error return codes, update this
507                # to do something better than a string compare
508                if "Permission error" in e.args[0]:
509                    cred = self.get_auth_cred()
510                else:
511                    raise
512        elif record.get_type() in ["authority"]:
513            cred = self.get_auth_cred()
514        elif record.get_type() == 'node':
515             cred = self.get_auth_cred()
516        else:
517            raise "unknown record type" + record.get_type()
518        return self.registry.update(cred, record)
519    
520     
521     def aggregates(self, opts, args):
522         user_cred = self.get_user_cred()
523         hrn = None
524         if args: 
525             hrn = args[0]
526         
527         result = self.registry.get_aggregates(user_cred, hrn)
528         self.display_list(result)
529         return 
530
531     def registries(self, opts, args):
532         user_cred = self.get_user_cred()
533         hrn = None
534         if args:
535             hrn = args[0]
536         
537         result = self.registry.get_registries(user_cred, hrn)
538         self.display_list(result)
539         return
540  
541     #
542     # Slice-related commands
543     #
544     
545     # list available nodes -- use 'resources' w/ no argument instead
546
547     # list instantiated slices
548     def slices(self,opts, args):
549        user_cred = self.get_user_cred()
550        results = self.slicemgr.get_slices(user_cred)
551        self.display_list(results)
552        return
553     
554     # show rspec for named slice
555     def resources(self,opts, args):
556        user_cred = self.get_user_cred()
557        server = self.slicemgr
558        if opts.aggregate:
559             aggregates = self.registry.get_aggregates(user_cred, opts.aggregate)
560             if not aggregates:
561                 raise Exception, "No such aggregate %s" % opts.aggregate
562             aggregate = aggregates[0]
563             url = "http://%s:%s" % (aggregate['addr'], aggregate['port'])     
564             server = GeniClient(url, self.key_file, self.cert_file, self.options.protocol)
565        if args:
566             slice_cred = self.get_slice_cred(args[0])
567             result = server.get_resources(slice_cred, args[0])
568        else:
569             result = server.get_resources(user_cred)
570        format = opts.format
571        
572        self.display_rspec(result, format)
573        if (opts.file is not None):
574           self.save_rspec_to_file(result, opts.file)
575        return
576     
577     # created named slice with given rspec
578     def create(self,opts, args):
579        slice_hrn = args[0]
580        user_cred = self.get_user_cred()
581        slice_cred = self.get_slice_cred(slice_hrn)
582        rspec_file = self.get_rspec_file(args[1])
583        rspec=open(rspec_file).read()
584        server = self.slicemgr
585        if opts.aggregate:
586            aggregates = self.registry.get_aggregates(user_cred, opts.aggregate)
587            if not aggregates:
588                raise Exception, "No such aggregate %s" % opts.aggregate
589            aggregate = aggregates[0]
590            url = "http://%s:%s" % (aggregate['addr'], aggregate['port'])
591            server = GeniClient(url, self.key_file, self.cert_file, self.options.protocol)
592        return server.create_slice(slice_cred, slice_hrn, rspec)
593     
594     # delete named slice
595     def delete(self,opts, args):
596        slice_hrn = args[0]
597        slice_cred = self.get_slice_cred(slice_hrn)
598        
599        return self.slicemgr.delete_slice(slice_cred, slice_hrn)
600     
601     # start named slice
602     def start(self,opts, args):
603        slice_hrn = args[0]
604        slice_cred = self.get_slice_cred(args[0])
605        return self.slicemgr.start_slice(slice_cred, slice_hrn)
606     
607     # stop named slice
608     def stop(self,opts, args):
609        slice_hrn = args[0]
610        slice_cred = self.get_slice_cred(args[0])
611        return self.slicemgr.stop_slice(slice_cred, slice_hrn)
612     
613     # reset named slice
614     def reset(self,opts, args):
615        slice_hrn = args[0]
616        slice_cred = self.get_slice_cred(args[0])
617        return self.slicemgr.reset_slice(slice_cred, slice_hrn)
618     
619     #
620     #
621     # Display, Save, and Filter RSpecs and Records
622     #   - to be replace by EMF-generated routines
623     #
624     #
625     
626     def display_rspec(self,rspec, format = 'rspec'):
627         if format in ['dns']:
628             spec = Rspec()
629             spec.parseString(rspec)
630             hostnames = []
631             nodespecs = spec.getDictsByTagName('NodeSpec')
632             for nodespec in nodespecs:
633                 if nodespec.has_key('name') and nodespec['name']:
634                     if isinstance(nodespec['name'], ListType):
635                         hostnames.extend(nodespec['name'])
636                     elif isinstance(nodespec['name'], StringTypes):
637                         hostnames.append(nodespec['name'])
638             result = hostnames
639         elif format in ['ip']:
640             spec = Rspec()
641             spec.parseString(rspec)
642             ips = []
643             ifspecs = spec.getDictsByTagName('IfSpec')
644             for ifspec in ifspecs:
645                 if ifspec.has_key('addr') and ifspec['addr']:
646                     ips.append(ifspec['addr'])
647             result = ips 
648         else:     
649             result = rspec
650     
651         print result
652         return
653     
654     def display_list(self,results):
655         for result in results:
656             print result
657     
658     def save_rspec_to_file(self,rspec, filename):
659        if not filename.startswith(os.sep):
660            filename = self.options.sfi_dir + filename
661        if not filename.endswith(".rspec"):
662            filename = filename + ".rspec"
663     
664        f = open(filename, 'w')
665        f.write(rspec)
666        f.close()
667        return
668     
669     def display_records(self,recordList, dump = False):
670        ''' Print all fields in the record'''
671        for record in recordList:
672           self.display_record(record, dump)
673     
674     def display_record(self,record, dump = False):
675        if dump:
676            record.dump()
677        else:
678            info = record.getdict()
679            print "%s (%s)" % (info['hrn'], info['type'])
680        return
681     
682     def filter_records(self,type, records):
683        filtered_records = []
684        for record in records:
685            if (record.get_type() == type) or (type == "all"):
686                filtered_records.append(record)
687        return filtered_records
688     
689     def save_records_to_file(self,filename, recordList):
690        index = 0
691        for record in recordList:
692            if index>0:
693                self.save_record_to_file(filename + "." + str(index), record)
694            else:
695                self.save_record_to_file(filename, record)
696            index = index + 1
697     
698     def save_record_to_file(self,filename, record):
699        if not filename.startswith(os.sep):
700            filename = self.options.sfi_dir + filename
701        str = record.save_to_string()
702        file(filename, "w").write(str)
703        return
704     
705     def load_record_from_file(self,filename):
706        str = file(filename, "r").read()
707        record = GeniRecord(string=str)
708        return record
709     
710     #
711     # Main: parse arguments and dispatch to command
712     #
713     def main(self):
714     
715        parser = self.create_parser()
716        (options, args) = parser.parse_args()
717        self.options = options
718     
719        if len(args) <= 0:
720             print "No command given. Use -h for help."
721             return -1
722     
723        command = args[0]
724        (cmd_opts, cmd_args) = self.create_cmd_parser(command).parse_args(args[1:])
725        if self.options.verbose :
726           print "Registry %s, sm %s, dir %s, user %s, auth %s" % (options.registry,
727                                                                    options.sm,
728                                                                    options.sfi_dir,
729                                                                    options.user,
730                                                                    options.auth)
731           print "Command %s" %command
732           if command in ("resources"):
733              print "resources cmd_opts %s" %cmd_opts.format
734           elif command in ("list","show","remove"):
735              print "cmd_opts.type %s" %cmd_opts.type
736           print "cmd_args %s" %cmd_args
737     
738        self.set_servers()
739     
740        try:
741           self.dispatch(command, cmd_opts, cmd_args)
742        except KeyError:
743           raise 
744           print "Command not found:", command
745           sys.exit(1)
746     
747        return
748     
749 if __name__=="__main__":
750    Sfi().main()