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