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