a framework for handling call ids
[sfa.git] / sfa / client / sfi.py
1 #! /usr/bin/env python
2
3 # sfi -- slice-based facility interface
4
5 import sys
6 sys.path.append('.')
7 import os, os.path
8 import tempfile
9 import traceback
10 import socket
11 import random
12 import datetime
13 from lxml import etree
14 from StringIO import StringIO
15 from types import StringTypes, ListType
16 from optparse import OptionParser
17 import zlib
18
19 from sfa.util.sfalogging import sfa_logger,sfa_logger_goes_to_console
20 from sfa.trust.certificate import Keypair, Certificate
21 from sfa.trust.gid import GID
22 from sfa.trust.credential import Credential
23 from sfa.util.sfaticket import SfaTicket
24 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
25 from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn
26 from sfa.util.xmlrpcprotocol import ServerException
27 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
28 from sfa.util.config import Config
29 from sfa.util.version import version_core
30
31 AGGREGATE_PORT=12346
32 CM_PORT=12346
33
34 # utility methods here
35 # display methods
36 def display_rspec(rspec, format='rspec'):
37     if format in ['dns']:
38         tree = etree.parse(StringIO(rspec))
39         root = tree.getroot()
40         result = root.xpath("./network/site/node/hostname/text()")
41     elif format in ['ip']:
42         # The IP address is not yet part of the new RSpec
43         # so this doesn't do anything yet.
44         tree = etree.parse(StringIO(rspec))
45         root = tree.getroot()
46         result = root.xpath("./network/site/node/ipv4/text()")
47     else:
48         result = rspec
49
50     print result
51     return
52
53 def display_list(results):
54     for result in results:
55         print result
56
57 def display_records(recordList, dump=False):
58     ''' Print all fields in the record'''
59     for record in recordList:
60         display_record(record, dump)
61
62 def display_record(record, dump=False):
63     if dump:
64         record.dump()
65     else:
66         info = record.getdict()
67         print "%s (%s)" % (info['hrn'], info['type'])
68     return
69
70
71 def filter_records(type, records):
72     filtered_records = []
73     for record in records:
74         if (record['type'] == type) or (type == "all"):
75             filtered_records.append(record)
76     return filtered_records
77
78
79 # save methods
80 def save_rspec_to_file(rspec, filename):
81     if not filename.endswith(".rspec"):
82         filename = filename + ".rspec"
83
84     f = open(filename, 'w')
85     f.write(rspec)
86     f.close()
87     return
88
89 def save_records_to_file(filename, recordList):
90     index = 0
91     for record in recordList:
92         if index > 0:
93             save_record_to_file(filename + "." + str(index), record)
94         else:
95             save_record_to_file(filename, record)
96         index = index + 1
97
98 def save_record_to_file(filename, record):
99     if record['type'] in ['user']:
100         record = UserRecord(dict=record)
101     elif record['type'] in ['slice']:
102         record = SliceRecord(dict=record)
103     elif record['type'] in ['node']:
104         record = NodeRecord(dict=record)
105     elif record['type'] in ['authority', 'ma', 'sa']:
106         record = AuthorityRecord(dict=record)
107     else:
108         record = SfaRecord(dict=record)
109     str = record.save_to_string()
110     file(filename, "w").write(str)
111     return
112
113
114 # load methods
115 def load_record_from_file(filename):
116     str = file(filename, "r").read()
117     record = SfaRecord(string=str)
118     return record
119
120
121 import uuid
122 def unique_call_id(): return uuid.uuid4().urn
123
124 class Sfi:
125     
126     required_options=['verbose',  'debug',  'registry',  'sm',  'auth',  'user']
127
128     # dummy to meet Sfi's expectations for its 'options' field
129     # i.e. s/t we can do setattr on
130     class DummyOptions:
131         pass
132
133     def __init__ (self,options=None):
134         if options is None: options=Sfi.DummyOptions()
135         for opt in Sfi.required_options:
136             if not hasattr(options,opt): setattr(options,opt,None)
137         if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
138         self.options = options
139         self.slicemgr = None
140         self.registry = None
141         self.user = None
142         self.authority = None
143         self.hashrequest = False
144         sfa_logger_goes_to_console()
145         self.logger=sfa_logger()
146    
147     def create_cmd_parser(self, command, additional_cmdargs=None):
148         cmdargs = {"list": "authority",
149                   "show": "name",
150                   "remove": "name",
151                   "add": "record",
152                   "update": "record",
153                   "aggregates": "[name]",
154                   "registries": "[name]",
155                   "get_gid": [],  
156                   "get_trusted_certs": "cred",
157                   "slices": "",
158                   "resources": "[name]",
159                   "create": "name rspec",
160                   "get_ticket": "name rspec",
161                   "redeem_ticket": "ticket",
162                   "delete": "name",
163                   "reset": "name",
164                   "start": "name",
165                   "stop": "name",
166                   "delegate": "name",
167                   "status": "name",
168                   "renew": "name",
169                   "shutdown": "name",
170                   "version": "",  
171                  }
172
173         if additional_cmdargs:
174             cmdargs.update(additional_cmdargs)
175
176         if command not in cmdargs:
177             msg="Invalid command\n"
178             msg+="Commands: "
179             msg += ','.join(cmdargs.keys())            
180             self.logger.critical(msg)
181             sys.exit(2)
182
183         parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
184                                      % (command, cmdargs[command]))
185
186         # user specifies remote aggregate/sm/component                          
187         if command in ("resources", "slices", "create", "delete", "start", "stop", 
188                        "restart", "shutdown",  "get_ticket", "renew", "status"):
189             parser.add_option("-a", "--aggregate", dest="aggregate",
190                              default=None, help="aggregate host")
191             parser.add_option("-p", "--port", dest="port",
192                              default=AGGREGATE_PORT, help="aggregate port")
193             parser.add_option("-c", "--component", dest="component", default=None,
194                              help="component hrn")
195             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
196                              action="store_true",
197                              help="Include a credential delegated to the user's root"+\
198                                   "authority in set of credentials for this call")  
199         
200         # registy filter option    
201         if command in ("list", "show", "remove"):
202             parser.add_option("-t", "--type", dest="type", type="choice",
203                             help="type filter ([all]|user|slice|authority|node|aggregate)",
204                             choices=("all", "user", "slice", "authority", "node", "aggregate"),
205                             default="all")
206         # display formats
207         if command in ("resources"):
208             parser.add_option("-f", "--format", dest="format", type="choice",
209                              help="display format ([xml]|dns|ip)", default="xml",
210                              choices=("xml", "dns", "ip"))
211
212         if command in ("resources", "show", "list"):
213            parser.add_option("-o", "--output", dest="file",
214                             help="output XML to file", metavar="FILE", default=None)
215         
216         if command in ("show", "list"):
217            parser.add_option("-f", "--format", dest="format", type="choice",
218                              help="display format ([text]|xml)", default="text",
219                              choices=("text", "xml"))
220
221         if command in ("delegate"):
222            parser.add_option("-u", "--user",
223                             action="store_true", dest="delegate_user", default=False,
224                             help="delegate user credential")
225            parser.add_option("-s", "--slice", dest="delegate_slice",
226                             help="delegate slice credential", metavar="HRN", default=None)
227         
228         if command in ("version"):
229             parser.add_option("-R","--registry-version",
230                               action="store_true", dest="version_registry", default=False,
231                               help="probe registry version instead of slicemgr")
232             parser.add_option("-l","--local",
233                               action="store_true", dest="version_local", default=False,
234                               help="display version of the local client")
235
236         return parser
237
238         
239     def create_parser(self):
240
241         # Generate command line parser
242         parser = OptionParser(usage="sfi [options] command [command_options] [command_args]",
243                              description="Commands: gid,list,show,remove,add,update,nodes,slices,resources,create,delete,start,stop,reset")
244         parser.add_option("-r", "--registry", dest="registry",
245                          help="root registry", metavar="URL", default=None)
246         parser.add_option("-s", "--slicemgr", dest="sm",
247                          help="slice manager", metavar="URL", default=None)
248         default_sfi_dir = os.path.expanduser("~/.sfi/")
249         parser.add_option("-d", "--dir", dest="sfi_dir",
250                          help="config & working directory - default is " + default_sfi_dir,
251                          metavar="PATH", default=default_sfi_dir)
252         parser.add_option("-u", "--user", dest="user",
253                          help="user name", metavar="HRN", default=None)
254         parser.add_option("-a", "--auth", dest="auth",
255                          help="authority name", metavar="HRN", default=None)
256         parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
257                          help="verbose mode - cumulative")
258         parser.add_option("-D", "--debug",
259                           action="store_true", dest="debug", default=False,
260                           help="Debug (xml-rpc) protocol messages")
261         parser.add_option("-p", "--protocol", dest="protocol", default="xmlrpc",
262                          help="RPC protocol (xmlrpc or soap)")
263         parser.add_option("-k", "--hashrequest",
264                          action="store_true", dest="hashrequest", default=False,
265                          help="Create a hash of the request that will be authenticated on the server")
266         parser.disable_interspersed_args()
267
268         return parser
269         
270  
271     def read_config(self):
272        config_file = self.options.sfi_dir + os.sep + "sfi_config"
273        try:
274           config = Config (config_file)
275        except:
276           self.logger.critical("Failed to read configuration file %s"%config_file)
277           self.logger.info("Make sure to remove the export clauses and to add quotes")
278           if self.options.verbose==0:
279               self.logger.info("Re-run with -v for more details")
280           else:
281               self.logger.log_exc("Could not read config file %s"%config_file)
282           sys.exit(1)
283     
284        errors = 0
285        # Set SliceMgr URL
286        if (self.options.sm is not None):
287           self.sm_url = self.options.sm
288        elif hasattr(config, "SFI_SM"):
289           self.sm_url = config.SFI_SM
290        else:
291           self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
292           errors += 1 
293     
294        # Set Registry URL
295        if (self.options.registry is not None):
296           self.reg_url = self.options.registry
297        elif hasattr(config, "SFI_REGISTRY"):
298           self.reg_url = config.SFI_REGISTRY
299        else:
300           self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
301           errors += 1 
302           
303
304        # Set user HRN
305        if (self.options.user is not None):
306           self.user = self.options.user
307        elif hasattr(config, "SFI_USER"):
308           self.user = config.SFI_USER
309        else:
310           self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
311           errors += 1 
312     
313        # Set authority HRN
314        if (self.options.auth is not None):
315           self.authority = self.options.auth
316        elif hasattr(config, "SFI_AUTH"):
317           self.authority = config.SFI_AUTH
318        else:
319           self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
320           errors += 1 
321     
322        if errors:
323           sys.exit(1)
324
325
326     #
327     # Establish Connection to SliceMgr and Registry Servers
328     #
329     def set_servers(self):
330
331        self.read_config() 
332        # Get key and certificate
333        key_file = self.get_key_file()
334        cert_file = self.get_cert_file(key_file)
335        self.key = Keypair(filename=key_file) 
336        self.key_file = key_file
337        self.cert_file = cert_file
338        self.cert = Certificate(filename=cert_file) 
339        # Establish connection to server(s)
340        self.logger.info("Contacting Registry at: %s"%self.reg_url)
341        self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, self.options)  
342        self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
343        self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, self.options)
344
345        return
346     
347     #
348     # Get various credential and spec files
349     #
350     # Establishes limiting conventions
351     #   - conflates MAs and SAs
352     #   - assumes last token in slice name is unique
353     #
354     # Bootstraps credentials
355     #   - bootstrap user credential from self-signed certificate
356     #   - bootstrap authority credential from user credential
357     #   - bootstrap slice credential from user credential
358     #
359     
360     
361     def get_key_file(self):
362        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
363        if (os.path.isfile(file)):
364           return file
365        else:
366           self.logger.error("Key file %s does not exist"%file)
367           sys.exit(-1)
368        return
369     
370     def get_cert_file(self, key_file):
371     
372         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
373         if (os.path.isfile(file)):
374             # use existing cert if it exists                     
375             return file
376         else:
377             try:
378                 # attempt to use gid as the cert.  
379                 gid = self._get_gid()
380                 self.logger.info("Writing certificate to %s"%file)
381                 gid.save_to_file(file) 
382             except:
383                 # generate self signed certificate
384                 k = Keypair(filename=key_file)
385                 cert = Certificate(subject=self.user)
386                 cert.set_pubkey(k)
387                 cert.set_issuer(k, self.user)
388                 cert.sign()
389                 self.logger.info("Writing self-signed certificate to %s"%file)
390                 cert.save_to_file(file)
391             
392             return file
393
394     def get_cached_gid(self, file):
395         """
396         Return a cached gid    
397         """
398         gid = None 
399         if (os.path.isfile(file)):
400             gid = GID(filename=file)
401         return gid
402
403     # xxx opts unused
404     def get_gid(self, opts, args):
405         """
406         Get the specify gid and save it to file
407         """
408         hrn = None
409         if args:
410             hrn = args[0]
411         gid = self._get_gid(hrn)
412         self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
413         return gid
414
415     def _get_gid(self, hrn=None):
416         """
417         git_gid helper. Retrive the gid from the registry and save it to file.
418         """
419         
420         if not hrn:
421             hrn = self.user
422  
423         gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
424         gid = self.get_cached_gid(gidfile)
425         if not gid:
426             user_cred = self.get_user_cred()
427             records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
428             if not records:
429                 raise RecordNotFound(args[0])
430             gid = GID(string=records[0]['gid'])
431             self.logger.info("Writing gid to %s"%gidfile)
432             gid.save_to_file(filename=gidfile)
433         return gid   
434                 
435      
436     def get_cached_credential(self, file):
437         """
438         Return a cached credential only if it hasn't expired.
439         """
440         if (os.path.isfile(file)):
441             credential = Credential(filename=file)
442             # make sure it isnt expired 
443             if not credential.get_expiration or \
444                datetime.datetime.today() < credential.get_expiration():
445                 return credential
446         return None 
447  
448     def get_user_cred(self):
449         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
450         return self.get_cred(file, 'user', self.user)
451
452     def get_auth_cred(self):
453         if not self.authority:
454             self.logger.critical("no authority specified. Use -a or set SF_AUTH")
455             sys.exit(-1)
456         file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
457         return self.get_cred(file, 'authority', self.authority)
458
459     def get_slice_cred(self, name):
460         file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
461         return self.get_cred(file, 'slice', name)
462  
463     def get_cred(self, file, type, hrn):
464         # attempt to load a cached credential 
465         cred = self.get_cached_credential(file)    
466         if not cred:
467             if type in ['user']:
468                 cert_string = self.cert.save_to_string(save_parents=True)
469                 user_name = self.user.replace(self.authority + ".", '')
470                 if user_name.count(".") > 0:
471                     user_name = user_name.replace(".", '_')
472                     self.user = self.authority + "." + user_name
473                 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
474             else:
475                 # bootstrap slice credential from user credential
476                 user_cred = self.get_user_cred().save_to_string(save_parents=True)
477                 cred_str = self.registry.GetCredential(user_cred, hrn, type)
478             
479             if not cred_str:
480                 self.logger.critical("Failed to get %s credential" % type)
481                 sys.exit(-1)
482                 
483             cred = Credential(string=cred_str)
484             cred.save_to_file(file, save_parents=True)
485             self.logger.info("Writing %s credential to %s" %(type, file))
486
487         return cred
488  
489     
490     def get_rspec_file(self, rspec):
491        if (os.path.isabs(rspec)):
492           file = rspec
493        else:
494           file = os.path.join(self.options.sfi_dir, rspec)
495        if (os.path.isfile(file)):
496           return file
497        else:
498           self.logger.critical("No such rspec file"%rspec)
499           sys.exit(1)
500     
501     def get_record_file(self, record):
502        if (os.path.isabs(record)):
503           file = record
504        else:
505           file = os.path.join(self.options.sfi_dir, record)
506        if (os.path.isfile(file)):
507           return file
508        else:
509           self.logger.critical("No such registry record file %s"%record)
510           sys.exit(1)
511     
512     def load_publickey_string(self, fn):
513        f = file(fn, "r")
514        key_string = f.read()
515     
516        # if the filename is a private key file, then extract the public key
517        if "PRIVATE KEY" in key_string:
518            outfn = tempfile.mktemp()
519            cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
520            os.system(cmd)
521            f = file(outfn, "r")
522            key_string = f.read()
523            os.remove(outfn)
524     
525        return key_string
526
527     # xxx opts undefined
528     def get_component_server_from_hrn(self, hrn):
529         # direct connection to the nodes component manager interface
530         user_cred = self.get_user_cred().save_to_string(save_parents=True)
531         records = self.registry.Resolve(hrn, user_cred)
532         records = filter_records('node', records)
533         if not records:
534             self.logger.warning("No such component:%r"% opts.component)
535         record = records[0]
536   
537         return self.get_server(record['hostname'], CM_PORT, self.key_file, self.cert_file)
538  
539     def get_server(self, host, port, keyfile, certfile):
540         """
541         Return an instance of an xmlrpc server connection    
542         """
543         # port is appended onto the domain, before the path. Should look like:
544         # http://domain:port/path
545         host_parts = host.split('/')
546         host_parts[0] = host_parts[0] + ":" + str(port)
547         url =  "http://%s" %  "/".join(host_parts)    
548         return xmlrpcprotocol.get_server(url, keyfile, certfile, self.options)
549
550     # xxx opts could be retrieved in self.options
551     def get_server_from_opts(self, opts):
552         """
553         Return instance of an xmlrpc connection to a slice manager, aggregate
554         or component server depending on the specified opts
555         """
556         server = self.slicemgr
557         # direct connection to an aggregate
558         if hasattr(opts, 'aggregate') and opts.aggregate:
559             server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
560         # direct connection to the nodes component manager interface
561         if hasattr(opts, 'component') and opts.component:
562             server = self.get_component_server_from_hrn(opts.component)    
563  
564         return server
565     #==========================================================================
566     # Following functions implement the commands
567     #
568     # Registry-related commands
569     #==========================================================================
570   
571     def dispatch(self, command, cmd_opts, cmd_args):
572         return getattr(self, command)(cmd_opts, cmd_args)
573  
574     # list entires in named authority registry
575     def list(self, opts, args):
576         if len(args)!= 1:
577             self.print_help()
578             sys.exit(1)
579         hrn = args[0]
580         user_cred = self.get_user_cred().save_to_string(save_parents=True)
581         try:
582             list = self.registry.List(hrn, user_cred)
583         except IndexError:
584             raise Exception, "Not enough parameters for the 'list' command"
585           
586         # filter on person, slice, site, node, etc.  
587         # THis really should be in the self.filter_records funct def comment...
588         list = filter_records(opts.type, list)
589         for record in list:
590             print "%s (%s)" % (record['hrn'], record['type'])     
591         if opts.file:
592             file = opts.file
593             if not file.startswith(os.sep):
594                 file = os.path.join(self.options.sfi_dir, file)
595             save_records_to_file(file, list)
596         return
597     
598     # show named registry record
599     def show(self, opts, args):
600         if len(args)!= 1:
601             self.print_help()
602             sys.exit(1)
603         hrn = args[0]
604         user_cred = self.get_user_cred().save_to_string(save_parents=True)
605         records = self.registry.Resolve(hrn, user_cred)
606         records = filter_records(opts.type, records)
607         if not records:
608             print "No record of type", opts.type
609         for record in records:
610             if record['type'] in ['user']:
611                 record = UserRecord(dict=record)
612             elif record['type'] in ['slice']:
613                 record = SliceRecord(dict=record)
614             elif record['type'] in ['node']:
615                 record = NodeRecord(dict=record)
616             elif record['type'] in ['authority', 'ma', 'sa']:
617                 record = AuthorityRecord(dict=record)
618             else:
619                 record = SfaRecord(dict=record)
620             if (opts.format == "text"): 
621                 record.dump()  
622             else:
623                 print record.save_to_string() 
624        
625         if opts.file:
626             file = opts.file
627             if not file.startswith(os.sep):
628                 file = os.path.join(self.options.sfi_dir, file)
629             save_records_to_file(file, records)
630         return
631     
632     def delegate(self, opts, args):
633
634         delegee_hrn = args[0]
635         if opts.delegate_user:
636             user_cred = self.get_user_cred()
637             cred = self.delegate_cred(user_cred, delegee_hrn)
638         elif opts.delegate_slice:
639             slice_cred = self.get_slice_cred(opts.delegate_slice)
640             cred = self.delegate_cred(slice_cred, delegee_hrn)
641         else:
642             self.logger.warning("Must specify either --user or --slice <hrn>")
643             return
644         delegated_cred = Credential(string=cred)
645         object_hrn = delegated_cred.get_gid_object().get_hrn()
646         if opts.delegate_user:
647             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
648                                   + get_leaf(object_hrn) + ".cred")
649         elif opts.delegate_slice:
650             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
651                                   + get_leaf(object_hrn) + ".cred")
652
653         delegated_cred.save_to_file(dest_fn, save_parents=True)
654
655         self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
656     
657     def delegate_cred(self, object_cred, hrn):
658         # the gid and hrn of the object we are delegating
659         if isinstance(object_cred, str):
660             object_cred = Credential(string=object_cred) 
661         object_gid = object_cred.get_gid_object()
662         object_hrn = object_gid.get_hrn()
663     
664         if not object_cred.get_privileges().get_all_delegate():
665             self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
666             return
667
668         # the delegating user's gid
669         caller_gid = self._get_gid(self.user)
670         caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
671   
672         # the gid of the user who will be delegated to
673         delegee_gid = self._get_gid(hrn)
674         delegee_hrn = delegee_gid.get_hrn()
675         delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
676         delegee_gid.save_to_file(filename=delegee_gidfile)
677         dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
678         return dcred.save_to_string(save_parents=True)
679      
680     # removed named registry record
681     #   - have to first retrieve the record to be removed
682     def remove(self, opts, args):
683         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
684         if len(args)!=1:
685             self.print_help()
686             sys.exit(1)
687         hrn = args[0]
688         type = opts.type 
689         if type in ['all']:
690             type = '*'
691         return self.registry.Remove(hrn, auth_cred, type)
692     
693     # add named registry record
694     def add(self, opts, args):
695         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
696         if len(args)!=1:
697             self.print_help()
698             sys.exit(1)
699         record_filepath = args[0]
700         rec_file = self.get_record_file(record_filepath)
701         record = load_record_from_file(rec_file).as_dict()
702         return self.registry.Register(record, auth_cred)
703     
704     # update named registry entry
705     def update(self, opts, args):
706         user_cred = self.get_user_cred()
707         if len(args)!=1:
708             self.print_help()
709             sys.exit(1)
710         rec_file = self.get_record_file(args[0])
711         record = load_record_from_file(rec_file)
712         if record['type'] == "user":
713             if record.get_name() == user_cred.get_gid_object().get_hrn():
714                 cred = user_cred.save_to_string(save_parents=True)
715             else:
716                 cred = self.get_auth_cred().save_to_string(save_parents=True)
717         elif record['type'] in ["slice"]:
718             try:
719                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
720             except ServerException, e:
721                # XXX smbaker -- once we have better error return codes, update this
722                # to do something better than a string compare
723                if "Permission error" in e.args[0]:
724                    cred = self.get_auth_cred().save_to_string(save_parents=True)
725                else:
726                    raise
727         elif record.get_type() in ["authority"]:
728             cred = self.get_auth_cred().save_to_string(save_parents=True)
729         elif record.get_type() == 'node':
730             cred = self.get_auth_cred().save_to_string(save_parents=True)
731         else:
732             raise "unknown record type" + record.get_type()
733         record = record.as_dict()
734         return self.registry.Update(record, cred)
735   
736     def get_trusted_certs(self, opts, args):
737         """
738         return uhe trusted certs at this interface 
739         """ 
740         trusted_certs = self.registry.get_trusted_certs()
741         for trusted_cert in trusted_certs:
742             cert = Certificate(string=trusted_cert)
743             self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
744         return 
745
746     def aggregates(self, opts, args):
747         """
748         return a list of details about known aggregates
749         """
750         user_cred = self.get_user_cred().save_to_string(save_parents=True)
751         hrn = None
752         if args:
753             hrn = args[0]
754
755         result = self.registry.get_aggregates(user_cred, hrn)
756         display_list(result)
757         return 
758
759     def registries(self, opts, args):
760         """
761         return a list of details about known registries
762         """
763         user_cred = self.get_user_cred().save_to_string(save_parents=True)
764         hrn = None
765         if args:
766             hrn = args[0]
767         result = self.registry.get_registries(user_cred, hrn)
768         display_list(result)
769         return
770
771  
772     # ==================================================================
773     # Slice-related commands
774     # ==================================================================
775     
776
777     def version(self, opts, args):
778         if opts.version_local:
779             version=version_core()
780         else:
781             if opts.version_registry:
782                 server=self.registry
783             else:
784                 server = self.get_server_from_opts(opts)
785             version=server.GetVersion()
786         for (k,v) in version.iteritems():
787             print "%-20s: %s"%(k,v)
788
789     # list instantiated slices
790     def slices(self, opts, args):
791         """
792         list instantiated slices
793         """
794         user_cred = self.get_user_cred().save_to_string(save_parents=True)
795         creds = [user_cred]
796         if opts.delegate:
797             delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
798             creds.append(delegated_cred)  
799         server = self.get_server_from_opts(opts)
800         results = server.ListSlices(creds)
801         display_list(results)
802         return
803     
804     # show rspec for named slice
805     def resources(self, opts, args):
806         user_cred = self.get_user_cred().save_to_string(save_parents=True)
807         server = self.slicemgr
808         call_options = {}
809         server = self.get_server_from_opts(opts)
810         
811         if args:
812             cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
813             hrn = args[0]
814             call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
815         else:
816             cred = user_cred
817             hrn = None
818      
819         creds = [cred]
820         if opts.delegate:
821             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
822             creds.append(delegated_cred) 
823         result = server.ListResources(creds, call_options,unique_call_id())
824         format = opts.format
825         if opts.file is None:
826             display_rspec(result, format)
827         else:
828             file = opts.file
829             if not file.startswith(os.sep):
830                 file = os.path.join(self.options.sfi_dir, file)
831             save_rspec_to_file(result, file)
832         return
833     
834     # created named slice with given rspec
835     def create(self, opts, args):
836         slice_hrn = args[0]
837         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
838         user_cred = self.get_user_cred()
839         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
840         creds = [slice_cred]
841         if opts.delegate:
842             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
843             creds.append(delegated_cred)
844         rspec_file = self.get_rspec_file(args[1])
845         rspec = open(rspec_file).read()
846         server = self.get_server_from_opts(opts)
847         result =  server.CreateSliver(slice_urn, creds, rspec, [])
848         print result
849         return result
850
851     # get a ticket for the specified slice
852     def get_ticket(self, opts, args):
853         slice_hrn, rspec_path = args[0], args[1]
854         slice_urn = hrn_to_urn(slice_hrn, 'slice')
855         user_cred = self.get_user_cred()
856         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
857         creds = [slice_cred]
858         if opts.delegate:
859             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
860             creds.append(delegated_cred)
861         rspec_file = self.get_rspec_file(rspec_path) 
862         rspec = open(rspec_file).read()
863         server = self.get_server_from_opts(opts)
864         ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
865         file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
866         self.logger.info("writing ticket to %s"%file)
867         ticket = SfaTicket(string=ticket_string)
868         ticket.save_to_file(filename=file, save_parents=True)
869
870     def redeem_ticket(self, opts, args):
871         ticket_file = args[0]
872         
873         # get slice hrn from the ticket
874         # use this to get the right slice credential 
875         ticket = SfaTicket(filename=ticket_file)
876         ticket.decode()
877         slice_hrn = ticket.gidObject.get_hrn()
878         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
879         #slice_hrn = ticket.attributes['slivers'][0]['hrn']
880         user_cred = self.get_user_cred()
881         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
882         
883         # get a list of node hostnames from the RSpec 
884         tree = etree.parse(StringIO(ticket.rspec))
885         root = tree.getroot()
886         hostnames = root.xpath("./network/site/node/hostname/text()")
887         
888         # create an xmlrpc connection to the component manager at each of these
889         # components and gall redeem_ticket
890         connections = {}
891         for hostname in hostnames:
892             try:
893                 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
894                 server = self.get_server(hostname, CM_PORT, self.key_file, \
895                                          self.cert_file, self.options.debug)
896                 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
897                 self.logger.info("Success")
898             except socket.gaierror:
899                 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
900             except Exception, e:
901                 self.logger.log_exc(e.message)
902         return
903  
904     # delete named slice
905     def delete(self, opts, args):
906         slice_hrn = args[0]
907         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
908         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
909         creds = [slice_cred]
910         if opts.delegate:
911             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
912             creds.append(delegated_cred)
913         server = self.get_server_from_opts(opts)
914         return server.DeleteSliver(slice_urn, creds)
915     
916     # start named slice
917     def start(self, opts, args):
918         slice_hrn = args[0]
919         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
920         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
921         creds = [slice_cred]
922         if opts.delegate:
923             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
924             creds.append(delegated_cred)
925         server = self.get_server_from_opts(opts)
926         return server.Start(slice_urn, creds)
927     
928     # stop named slice
929     def stop(self, opts, args):
930         slice_hrn = args[0]
931         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
932         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
933         creds = [slice_cred]
934         if opts.delegate:
935             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
936             creds.append(delegated_cred)
937         server = self.get_server_from_opts(opts)
938         return server.Stop(slice_urn, creds)
939     
940     # reset named slice
941     def reset(self, opts, args):
942         slice_hrn = args[0]
943         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
944         server = self.get_server_from_opts(opts)
945         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
946         creds = [slice_cred]
947         if opts.delegate:
948             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
949             creds.append(delegated_cred)
950         return server.reset_slice(creds, slice_urn)
951
952     def renew(self, opts, args):
953         slice_hrn = args[0]
954         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
955         server = self.get_server_from_opts(opts)
956         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
957         creds = [slice_cred]
958         if opts.delegate:
959             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
960             creds.append(delegated_cred)
961         time = args[1]
962         return server.RenewSliver(slice_urn, creds, time)
963
964
965     def status(self, opts, args):
966         slice_hrn = args[0]
967         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
968         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
969         creds = [slice_cred]
970         if opts.delegate:
971             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
972             creds.append(delegated_cred)
973         server = self.get_server_from_opts(opts)
974         print server.SliverStatus(slice_urn, creds)
975
976
977     def shutdown(self, opts, args):
978         slice_hrn = args[0]
979         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
980         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
981         creds = [slice_cred]
982         if opts.delegate:
983             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
984             creds.append(delegated_cred)
985         server = self.get_server_from_opts(opts)
986         return server.Shutdown(slice_urn, creds)         
987     
988     def print_help (self):
989         self.sfi_parser.print_help()
990         self.cmd_parser.print_help()
991
992     #
993     # Main: parse arguments and dispatch to command
994     #
995     def main(self):
996         self.sfi_parser = self.create_parser()
997         (options, args) = self.sfi_parser.parse_args()
998         self.options = options
999
1000         self.logger.setLevelFromOptVerbose(self.options.verbose)
1001         if options.hashrequest:
1002             self.hashrequest = True
1003  
1004         if len(args) <= 0:
1005             self.logger.critical("No command given. Use -h for help.")
1006             return -1
1007     
1008         command = args[0]
1009         self.cmd_parser = self.create_cmd_parser(command)
1010         (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1011
1012         self.set_servers()
1013     
1014         self.logger.info("Command=%s" % command)
1015         if command in ("resources"):
1016             self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1017         elif command in ("list", "show", "remove"):
1018             self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1019         self.logger.debug('cmd_args %s',cmd_args)
1020
1021         try:
1022             self.dispatch(command, cmd_opts, cmd_args)
1023         except KeyError:
1024             self.logger.critical ("Unknown command %s"%command)
1025             sys.exit(1)
1026     
1027         return
1028     
1029 if __name__ == "__main__":
1030     Sfi().main()