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