attempt to use the user's gid before generating a self signed cert when establishing...
[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         url = "http://%s:%s" % (host, port)
518         return xmlrpcprotocol.get_server(url, keyfile, certfile, self.options)
519
520     def get_server_from_opts(self, opts):
521         """
522         Return instance of an xmlrpc connection to a slice manager, aggregate
523         or component server depending on the specified opts
524         """
525         server = self.slicemgr
526         # direct connection to an aggregate
527         if hasattr(opts, 'aggregate') and opts.aggregate:
528             server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
529         # direct connection to the nodes component manager interface
530         if hasattr(opts, 'component') and opts.component:
531             server = self.get_component_server_from_hrn(opts.component)    
532  
533         return server
534     #==========================================================================
535     # Following functions implement the commands
536     #
537     # Registry-related commands
538     #==========================================================================
539   
540     def dispatch(self, command, cmd_opts, cmd_args):
541         return getattr(self, command)(cmd_opts, cmd_args)
542  
543     # list entires in named authority registry
544     def list(self, opts, args):
545         if len(args)!= 1:
546             self.parser.print_help()
547             sys.exit(1)
548         hrn = args[0]
549         user_cred = self.get_user_cred().save_to_string(save_parents=True)
550         try:
551             list = self.registry.List(hrn, user_cred)
552         except IndexError:
553             raise Exception, "Not enough parameters for the 'list' command"
554           
555         # filter on person, slice, site, node, etc.  
556         # THis really should be in the self.filter_records funct def comment...
557         list = filter_records(opts.type, list)
558         for record in list:
559             print "%s (%s)" % (record['hrn'], record['type'])     
560         if opts.file:
561             file = opts.file
562             if not file.startswith(os.sep):
563                 file = os.path.join(self.options.sfi_dir, file)
564             save_records_to_file(file, list)
565         return
566     
567     # show named registry record
568     def show(self, opts, args):
569         if len(args)!= 1:
570             self.parser.print_help()
571             sys.exit(1)
572         hrn = args[0]
573         user_cred = self.get_user_cred().save_to_string(save_parents=True)
574         records = self.registry.Resolve(hrn, user_cred)
575         records = filter_records(opts.type, records)
576         if not records:
577             print "No record of type", opts.type
578         for record in records:
579             if record['type'] in ['user']:
580                 record = UserRecord(dict=record)
581             elif record['type'] in ['slice']:
582                 record = SliceRecord(dict=record)
583             elif record['type'] in ['node']:
584                 record = NodeRecord(dict=record)
585             elif record['type'] in ['authority', 'ma', 'sa']:
586                 record = AuthorityRecord(dict=record)
587             else:
588                 record = SfaRecord(dict=record)
589             if (opts.format == "text"): 
590                 record.dump()  
591             else:
592                 print record.save_to_string() 
593        
594         if opts.file:
595             file = opts.file
596             if not file.startswith(os.sep):
597                 file = os.path.join(self.options.sfi_dir, file)
598             save_records_to_file(file, records)
599         return
600     
601     def delegate(self, opts, args):
602
603         delegee_hrn = args[0]
604         if opts.delegate_user:
605             user_cred = self.get_user_cred()
606             cred = self.delegate_cred(user_cred, delegee_hrn)
607         elif opts.delegate_slice:
608             slice_cred = self.get_slice_cred(opts.delegate_slice)
609             cred = self.delegate_cred(slice_cred, delegee_hrn)
610         else:
611             self.logger.warning("Must specify either --user or --slice <hrn>")
612             return
613         delegated_cred = Credential(string=cred)
614         object_hrn = delegated_cred.get_gid_object().get_hrn()
615         if opts.delegate_user:
616             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
617                                   + get_leaf(object_hrn) + ".cred")
618         elif opts.delegate_slice:
619             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
620                                   + get_leaf(object_hrn) + ".cred")
621
622         delegated_cred.save_to_file(dest_fn, save_parents=True)
623
624         self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
625     
626     def delegate_cred(self, object_cred, hrn):
627         # the gid and hrn of the object we are delegating
628         if isinstance(object_cred, str):
629             object_cred = Credential(string=object_cred) 
630         object_gid = object_cred.get_gid_object()
631         object_hrn = object_gid.get_hrn()
632     
633         if not object_cred.get_privileges().get_all_delegate():
634             self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
635             return
636
637         # the delegating user's gid
638         caller_gid = self._get_gid(self.user)
639         caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
640   
641         # the gid of the user who will be delegated to
642         delegee_gid = self._get_gid(hrn)
643         delegee_hrn = delegee_gid.get_hrn()
644         delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
645         delegee_gid.save_to_file(filename=delegee_gidfile)
646         dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
647         return dcred.save_to_string(save_parents=True)
648      
649     # removed named registry record
650     #   - have to first retrieve the record to be removed
651     def remove(self, opts, args):
652         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
653         if len(args)!=1:
654             self.parser.print_help()
655             sys.exit(1)
656         hrn = args[0]
657         type = opts.type 
658         if type in ['all']:
659             type = '*'
660         return self.registry.Remove(hrn, auth_cred, type)
661     
662     # add named registry record
663     def add(self, opts, args):
664         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
665         if len(args)!=1:
666             self.parser.print_help()
667             sys.exit(1)
668         record_filepath = args[0]
669         rec_file = self.get_record_file(record_filepath)
670         record = load_record_from_file(rec_file).as_dict()
671         return self.registry.Register(record, auth_cred)
672     
673     # update named registry entry
674     def update(self, opts, args):
675         user_cred = self.get_user_cred()
676         if len(args)!=1:
677             self.parser.print_help()
678             sys.exit(1)
679         rec_file = self.get_record_file(args[0])
680         record = load_record_from_file(rec_file)
681         if record['type'] == "user":
682             if record.get_name() == user_cred.get_gid_object().get_hrn():
683                 cred = user_cred.save_to_string(save_parents=True)
684             else:
685                 cred = self.get_auth_cred().save_to_string(save_parents=True)
686         elif record['type'] in ["slice"]:
687             try:
688                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
689             except ServerException, e:
690                # XXX smbaker -- once we have better error return codes, update this
691                # to do something better than a string compare
692                if "Permission error" in e.args[0]:
693                    cred = self.get_auth_cred().save_to_string(save_parents=True)
694                else:
695                    raise
696         elif record.get_type() in ["authority"]:
697             cred = self.get_auth_cred().save_to_string(save_parents=True)
698         elif record.get_type() == 'node':
699             cred = self.get_auth_cred().save_to_string(save_parents=True)
700         else:
701             raise "unknown record type" + record.get_type()
702         record = record.as_dict()
703         return self.registry.Update(record, cred)
704   
705     def get_trusted_certs(self, opts, args):
706         """
707         return uhe trusted certs at this interface 
708         """ 
709         trusted_certs = self.registry.get_trusted_certs()
710         for trusted_cert in trusted_certs:
711             cert = Certificate(string=trusted_cert)
712             self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
713         return 
714
715     def aggregates(self, opts, args):
716         """
717         return a list of details about known aggregates
718         """
719         user_cred = self.get_user_cred().save_to_string(save_parents=True)
720         hrn = None
721         if args:
722             hrn = args[0]
723
724         result = self.registry.get_aggregates(user_cred, hrn)
725         display_list(result)
726         return 
727
728     def registries(self, opts, args):
729         """
730         return a list of details about known registries
731         """
732         user_cred = self.get_user_cred().save_to_string(save_parents=True)
733         hrn = None
734         if args:
735             hrn = args[0]
736         result = self.registry.get_registries(user_cred, hrn)
737         display_list(result)
738         return
739
740  
741     # ==================================================================
742     # Slice-related commands
743     # ==================================================================
744     
745
746     def version(self, opts, args):
747         server = self.get_server_from_opts(opts)
748         
749         print server.GetVersion()
750
751     # list instantiated slices
752     def slices(self, opts, args):
753         """
754         list instantiated slices
755         """
756         user_cred = self.get_user_cred().save_to_string(save_parents=True)
757         creds = [user_cred]
758         if opts.delegate:
759             delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
760             creds.append(delegated_cred)  
761         server = self.get_server_from_opts(opts)
762         results = server.ListSlices(creds)
763         display_list(results)
764         return
765     
766     # show rspec for named slice
767     def resources(self, opts, args):
768         user_cred = self.get_user_cred().save_to_string(save_parents=True)
769         server = self.slicemgr
770         call_options = {}
771         server = self.get_server_from_opts(opts)
772         
773         if args:
774             cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
775             hrn = args[0]
776             call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
777         else:
778             cred = user_cred
779             hrn = None
780      
781         creds = [cred]
782         if opts.delegate:
783             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
784             creds.append(delegated_cred) 
785         result = server.ListResources(creds, call_options)
786         format = opts.format
787         display_rspec(result, format)
788         if (opts.file is not None):
789             file = opts.file
790             if not file.startswith(os.sep):
791                 file = os.path.join(self.options.sfi_dir, file)
792             save_rspec_to_file(result, file)
793         return
794     
795     # created named slice with given rspec
796     def create(self, opts, args):
797         slice_hrn = args[0]
798         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
799         user_cred = self.get_user_cred()
800         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
801         creds = [slice_cred]
802         if opts.delegate:
803             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
804             creds.append(delegated_cred)
805         rspec_file = self.get_rspec_file(args[1])
806         rspec = open(rspec_file).read()
807         server = self.get_server_from_opts(opts)
808         result =  server.CreateSliver(slice_urn, creds, rspec, [])
809         print result
810         return result
811
812     # get a ticket for the specified slice
813     def get_ticket(self, opts, args):
814         slice_hrn, rspec_path = args[0], args[1]
815         slice_urn = hrn_to_urn(slice_hrn, 'slice')
816         user_cred = self.get_user_cred()
817         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
818         creds = [slice_cred]
819         if opts.delegate:
820             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
821             creds.append(delegated_cred)
822         rspec_file = self.get_rspec_file(rspec_path) 
823         rspec = open(rspec_file).read()
824         server = self.get_server_from_opts(opts)
825         ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
826         file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
827         self.logger.info("writing ticket to %s"%file)
828         ticket = SfaTicket(string=ticket_string)
829         ticket.save_to_file(filename=file, save_parents=True)
830
831     def redeem_ticket(self, opts, args):
832         ticket_file = args[0]
833         
834         # get slice hrn from the ticket
835         # use this to get the right slice credential 
836         ticket = SfaTicket(filename=ticket_file)
837         ticket.decode()
838         slice_hrn = ticket.gidObject.get_hrn()
839         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
840         #slice_hrn = ticket.attributes['slivers'][0]['hrn']
841         user_cred = self.get_user_cred()
842         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
843         
844         # get a list of node hostnames from the RSpec 
845         tree = etree.parse(StringIO(ticket.rspec))
846         root = tree.getroot()
847         hostnames = root.xpath("./network/site/node/hostname/text()")
848         
849         # create an xmlrpc connection to the component manager at each of these
850         # components and gall redeem_ticket
851         connections = {}
852         for hostname in hostnames:
853             try:
854                 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
855                 server = self.get_server(hostname, CM_PORT, self.key_file, \
856                                          self.cert_file, self.options.debug)
857                 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
858                 self.logger.info("Success")
859             except socket.gaierror:
860                 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
861             except Exception, e:
862                 self.logger.log_exc(e.message)
863         return
864  
865     # delete named slice
866     def delete(self, opts, args):
867         slice_hrn = args[0]
868         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
869         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
870         creds = [slice_cred]
871         if opts.delegate:
872             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
873             creds.append(delegated_cred)
874         server = self.get_server_from_opts(opts)
875         return server.DeleteSliver(slice_urn, creds)
876     
877     # start named slice
878     def start(self, opts, args):
879         slice_hrn = args[0]
880         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
881         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
882         creds = [slice_cred]
883         if opts.delegate:
884             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
885             creds.append(delegated_cred)
886         server = self.get_server_from_opts(opts)
887         return server.Start(slice_urn, creds)
888     
889     # stop named slice
890     def stop(self, opts, args):
891         slice_hrn = args[0]
892         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
893         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
894         creds = [slice_cred]
895         if opts.delegate:
896             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
897             creds.append(delegated_cred)
898         server = self.get_server_from_opts(opts)
899         return server.Stop(slice_urn, creds)
900     
901     # reset named slice
902     def reset(self, opts, args):
903         slice_hrn = args[0]
904         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
905         server = self.get_server_from_opts(opts)
906         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
907         creds = [slice_cred]
908         if opts.delegate:
909             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
910             creds.append(delegated_cred)
911         return server.reset_slice(creds, slice_urn)
912
913     def renew(self, opts, args):
914         slice_hrn = args[0]
915         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
916         server = self.get_server_from_opts(opts)
917         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
918         creds = [slice_cred]
919         if opts.delegate:
920             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
921             creds.append(delegated_cred)
922         time = args[1]
923         return server.RenewSliver(slice_urn, creds, time)
924
925
926     def status(self, opts, args):
927         slice_hrn = args[0]
928         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
929         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
930         creds = [slice_cred]
931         if opts.delegate:
932             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
933             creds.append(delegated_cred)
934         server = self.get_server_from_opts(opts)
935         print server.SliverStatus(slice_urn, creds)
936
937
938     def shutdown(self, opts, args):
939         slice_hrn = args[0]
940         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
941         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
942         creds = [slice_cred]
943         if opts.delegate:
944             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
945             creds.append(delegated_cred)
946         server = self.get_server_from_opts(opts)
947         return server.Shutdown(slice_urn, creds)         
948     
949
950     #
951     # Main: parse arguments and dispatch to command
952     #
953     def main(self):
954         parser = self.create_parser()
955         (options, args) = parser.parse_args()
956         self.options = options
957
958         self.logger.setLevelFromOptVerbose(self.options.verbose)
959         if options.hashrequest:
960             self.hashrequest = True
961  
962         if len(args) <= 0:
963             self.logger.critical("No command given. Use -h for help.")
964             return -1
965     
966         command = args[0]
967         self.parser = self.create_cmd_parser(command)
968         (cmd_opts, cmd_args) = self.parser.parse_args(args[1:])
969
970         self.set_servers()
971     
972         self.logger.info("Command=%s" % command)
973         if command in ("resources"):
974             self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
975         elif command in ("list", "show", "remove"):
976             self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
977         self.logger.debug('cmd_args %s',cmd_args)
978
979         try:
980             self.dispatch(command, cmd_opts, cmd_args)
981         except KeyError:
982             self.logger.critical ("Unknown command %s"%command)
983             sys.exit(1)
984     
985         return
986     
987 if __name__ == "__main__":
988     Sfi().main()