more to do to get CreateSliver working at ProtoGeni aggregates
[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 = Certificate(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         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
378         if (os.path.isfile(file)):
379             # use existing cert if it exists                     
380             return file
381         else:
382             try:
383                 # attempt to use gid as the cert.  
384                 gid = self._get_gid()
385                 self.logger.info("Writing certificate to %s"%file)
386                 gid.save_to_file(file) 
387             except:
388                 # generate self signed certificate
389                 k = Keypair(filename=key_file)
390                 cert = Certificate(subject=self.user)
391                 cert.set_pubkey(k)
392                 cert.set_issuer(k, self.user)
393                 cert.sign()
394                 self.logger.info("Writing self-signed certificate to %s"%file)
395                 cert.save_to_file(file)
396             
397             return file
398
399     def get_cached_gid(self, file):
400         """
401         Return a cached gid    
402         """
403         gid = None 
404         if (os.path.isfile(file)):
405             gid = GID(filename=file)
406         return gid
407
408     # xxx opts unused
409     def get_gid(self, opts, args):
410         """
411         Get the specify gid and save it to file
412         """
413         hrn = None
414         if args:
415             hrn = args[0]
416         gid = self._get_gid(hrn)
417         self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
418         return gid
419
420     def _get_gid(self, hrn=None):
421         """
422         git_gid helper. Retrive the gid from the registry and save it to file.
423         """
424         
425         if not hrn:
426             hrn = self.user
427  
428         gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
429         gid = self.get_cached_gid(gidfile)
430         if not gid:
431             user_cred = self.get_user_cred()
432             records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
433             if not records:
434                 raise RecordNotFound(args[0])
435             gid = GID(string=records[0]['gid'])
436             self.logger.info("Writing gid to %s"%gidfile)
437             gid.save_to_file(filename=gidfile)
438         return gid   
439                 
440      
441     def get_cached_credential(self, file):
442         """
443         Return a cached credential only if it hasn't expired.
444         """
445         if (os.path.isfile(file)):
446             credential = Credential(filename=file)
447             # make sure it isnt expired 
448             if not credential.get_expiration or \
449                datetime.datetime.today() < credential.get_expiration():
450                 return credential
451         return None 
452  
453     def get_user_cred(self):
454         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
455         return self.get_cred(file, 'user', self.user)
456
457     def get_auth_cred(self):
458         if not self.authority:
459             self.logger.critical("no authority specified. Use -a or set SF_AUTH")
460             sys.exit(-1)
461         file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
462         return self.get_cred(file, 'authority', self.authority)
463
464     def get_slice_cred(self, name):
465         file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
466         return self.get_cred(file, 'slice', name)
467  
468     def get_cred(self, file, type, hrn):
469         # attempt to load a cached credential 
470         cred = self.get_cached_credential(file)    
471         if not cred:
472             if type in ['user']:
473                 cert_string = self.cert.save_to_string(save_parents=True)
474                 user_name = self.user.replace(self.authority + ".", '')
475                 if user_name.count(".") > 0:
476                     user_name = user_name.replace(".", '_')
477                     self.user = self.authority + "." + user_name
478                 cred_str = self.registry.GetSelfCredential(cert_string, hrn, "user")
479             else:
480                 # bootstrap slice credential from user credential
481                 user_cred = self.get_user_cred().save_to_string(save_parents=True)
482                 cred_str = self.registry.GetCredential(user_cred, hrn, type)
483             
484             if not cred_str:
485                 self.logger.critical("Failed to get %s credential" % type)
486                 sys.exit(-1)
487                 
488             cred = Credential(string=cred_str)
489             cred.save_to_file(file, save_parents=True)
490             self.logger.info("Writing %s credential to %s" %(type, file))
491
492         return cred
493  
494     
495     def get_rspec_file(self, rspec):
496        if (os.path.isabs(rspec)):
497           file = rspec
498        else:
499           file = os.path.join(self.options.sfi_dir, rspec)
500        if (os.path.isfile(file)):
501           return file
502        else:
503           self.logger.critical("No such rspec file %s"%rspec)
504           sys.exit(1)
505     
506     def get_record_file(self, record):
507        if (os.path.isabs(record)):
508           file = record
509        else:
510           file = os.path.join(self.options.sfi_dir, record)
511        if (os.path.isfile(file)):
512           return file
513        else:
514           self.logger.critical("No such registry record file %s"%record)
515           sys.exit(1)
516     
517     def load_publickey_string(self, fn):
518        f = file(fn, "r")
519        key_string = f.read()
520     
521        # if the filename is a private key file, then extract the public key
522        if "PRIVATE KEY" in key_string:
523            outfn = tempfile.mktemp()
524            cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
525            os.system(cmd)
526            f = file(outfn, "r")
527            key_string = f.read()
528            os.remove(outfn)
529     
530        return key_string
531
532     # xxx opts undefined
533     def get_component_server_from_hrn(self, hrn):
534         # direct connection to the nodes component manager interface
535         user_cred = self.get_user_cred().save_to_string(save_parents=True)
536         records = self.registry.Resolve(hrn, user_cred)
537         records = filter_records('node', records)
538         if not records:
539             self.logger.warning("No such component:%r"% opts.component)
540         record = records[0]
541   
542         return self.get_server(record['hostname'], CM_PORT, self.key_file, self.cert_file)
543  
544     def get_server(self, host, port, keyfile, certfile):
545         """
546         Return an instance of an xmlrpc server connection    
547         """
548         # port is appended onto the domain, before the path. Should look like:
549         # http://domain:port/path
550         host_parts = host.split('/')
551         host_parts[0] = host_parts[0] + ":" + str(port)
552         url =  "http://%s" %  "/".join(host_parts)    
553         return xmlrpcprotocol.get_server(url, keyfile, certfile, self.options)
554
555     # xxx opts could be retrieved in self.options
556     def get_server_from_opts(self, opts):
557         """
558         Return instance of an xmlrpc connection to a slice manager, aggregate
559         or component server depending on the specified opts
560         """
561         server = self.slicemgr
562         # direct connection to an aggregate
563         if hasattr(opts, 'aggregate') and opts.aggregate:
564             server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
565         # direct connection to the nodes component manager interface
566         if hasattr(opts, 'component') and opts.component:
567             server = self.get_component_server_from_hrn(opts.component)    
568  
569         return server
570     #==========================================================================
571     # Following functions implement the commands
572     #
573     # Registry-related commands
574     #==========================================================================
575   
576     def dispatch(self, command, cmd_opts, cmd_args):
577         return getattr(self, command)(cmd_opts, cmd_args)
578  
579     # list entires in named authority registry
580     def list(self, opts, args):
581         if len(args)!= 1:
582             self.print_help()
583             sys.exit(1)
584         hrn = args[0]
585         user_cred = self.get_user_cred().save_to_string(save_parents=True)
586         try:
587             list = self.registry.List(hrn, user_cred)
588         except IndexError:
589             raise Exception, "Not enough parameters for the 'list' command"
590           
591         # filter on person, slice, site, node, etc.  
592         # THis really should be in the self.filter_records funct def comment...
593         list = filter_records(opts.type, list)
594         for record in list:
595             print "%s (%s)" % (record['hrn'], record['type'])     
596         if opts.file:
597             file = opts.file
598             if not file.startswith(os.sep):
599                 file = os.path.join(self.options.sfi_dir, file)
600             save_records_to_file(file, list)
601         return
602     
603     # show named registry record
604     def show(self, opts, args):
605         if len(args)!= 1:
606             self.print_help()
607             sys.exit(1)
608         hrn = args[0]
609         user_cred = self.get_user_cred().save_to_string(save_parents=True)
610         records = self.registry.Resolve(hrn, user_cred)
611         records = filter_records(opts.type, records)
612         if not records:
613             print "No record of type", opts.type
614         for record in records:
615             if record['type'] in ['user']:
616                 record = UserRecord(dict=record)
617             elif record['type'] in ['slice']:
618                 record = SliceRecord(dict=record)
619             elif record['type'] in ['node']:
620                 record = NodeRecord(dict=record)
621             elif record['type'] in ['authority', 'ma', 'sa']:
622                 record = AuthorityRecord(dict=record)
623             else:
624                 record = SfaRecord(dict=record)
625             if (opts.format == "text"): 
626                 record.dump()  
627             else:
628                 print record.save_to_string() 
629        
630         if opts.file:
631             file = opts.file
632             if not file.startswith(os.sep):
633                 file = os.path.join(self.options.sfi_dir, file)
634             save_records_to_file(file, records)
635         return
636     
637     def delegate(self, opts, args):
638
639         delegee_hrn = args[0]
640         if opts.delegate_user:
641             user_cred = self.get_user_cred()
642             cred = self.delegate_cred(user_cred, delegee_hrn)
643         elif opts.delegate_slice:
644             slice_cred = self.get_slice_cred(opts.delegate_slice)
645             cred = self.delegate_cred(slice_cred, delegee_hrn)
646         else:
647             self.logger.warning("Must specify either --user or --slice <hrn>")
648             return
649         delegated_cred = Credential(string=cred)
650         object_hrn = delegated_cred.get_gid_object().get_hrn()
651         if opts.delegate_user:
652             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
653                                   + get_leaf(object_hrn) + ".cred")
654         elif opts.delegate_slice:
655             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
656                                   + get_leaf(object_hrn) + ".cred")
657
658         delegated_cred.save_to_file(dest_fn, save_parents=True)
659
660         self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
661     
662     def delegate_cred(self, object_cred, hrn):
663         # the gid and hrn of the object we are delegating
664         if isinstance(object_cred, str):
665             object_cred = Credential(string=object_cred) 
666         object_gid = object_cred.get_gid_object()
667         object_hrn = object_gid.get_hrn()
668     
669         if not object_cred.get_privileges().get_all_delegate():
670             self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
671             return
672
673         # the delegating user's gid
674         caller_gid = self._get_gid(self.user)
675         caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
676   
677         # the gid of the user who will be delegated to
678         delegee_gid = self._get_gid(hrn)
679         delegee_hrn = delegee_gid.get_hrn()
680         delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
681         delegee_gid.save_to_file(filename=delegee_gidfile)
682         dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
683         return dcred.save_to_string(save_parents=True)
684      
685     # removed named registry record
686     #   - have to first retrieve the record to be removed
687     def remove(self, opts, args):
688         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
689         if len(args)!=1:
690             self.print_help()
691             sys.exit(1)
692         hrn = args[0]
693         type = opts.type 
694         if type in ['all']:
695             type = '*'
696         return self.registry.Remove(hrn, auth_cred, type)
697     
698     # add named registry record
699     def add(self, opts, args):
700         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
701         if len(args)!=1:
702             self.print_help()
703             sys.exit(1)
704         record_filepath = args[0]
705         rec_file = self.get_record_file(record_filepath)
706         record = load_record_from_file(rec_file).as_dict()
707         return self.registry.Register(record, auth_cred)
708     
709     # update named registry entry
710     def update(self, opts, args):
711         user_cred = self.get_user_cred()
712         if len(args)!=1:
713             self.print_help()
714             sys.exit(1)
715         rec_file = self.get_record_file(args[0])
716         record = load_record_from_file(rec_file)
717         if record['type'] == "user":
718             if record.get_name() == user_cred.get_gid_object().get_hrn():
719                 cred = user_cred.save_to_string(save_parents=True)
720             else:
721                 cred = self.get_auth_cred().save_to_string(save_parents=True)
722         elif record['type'] in ["slice"]:
723             try:
724                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
725             except xmlrpcprotocol.ServerException, e:
726                # XXX smbaker -- once we have better error return codes, update this
727                # to do something better than a string compare
728                if "Permission error" in e.args[0]:
729                    cred = self.get_auth_cred().save_to_string(save_parents=True)
730                else:
731                    raise
732         elif record.get_type() in ["authority"]:
733             cred = self.get_auth_cred().save_to_string(save_parents=True)
734         elif record.get_type() == 'node':
735             cred = self.get_auth_cred().save_to_string(save_parents=True)
736         else:
737             raise "unknown record type" + record.get_type()
738         record = record.as_dict()
739         return self.registry.Update(record, cred)
740   
741     def get_trusted_certs(self, opts, args):
742         """
743         return uhe trusted certs at this interface 
744         """ 
745         trusted_certs = self.registry.get_trusted_certs()
746         for trusted_cert in trusted_certs:
747             cert = Certificate(string=trusted_cert)
748             self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
749         return 
750
751     def aggregates(self, opts, args):
752         """
753         return a list of details about known aggregates
754         """
755         user_cred = self.get_user_cred().save_to_string(save_parents=True)
756         hrn = None
757         if args:
758             hrn = args[0]
759
760         result = self.registry.get_aggregates(user_cred, hrn)
761         display_list(result)
762         return 
763
764     def registries(self, opts, args):
765         """
766         return a list of details about known registries
767         """
768         user_cred = self.get_user_cred().save_to_string(save_parents=True)
769         hrn = None
770         if args:
771             hrn = args[0]
772         result = self.registry.get_registries(user_cred, hrn)
773         display_list(result)
774         return
775
776  
777     # ==================================================================
778     # Slice-related commands
779     # ==================================================================
780     
781
782     def version(self, opts, args):
783         if opts.version_local:
784             version=version_core()
785         else:
786             if opts.version_registry:
787                 server=self.registry
788             else:
789                 server = self.get_server_from_opts(opts)
790             version=server.GetVersion()
791         for (k,v) in version.iteritems():
792             print "%-20s: %s"%(k,v)
793
794     # list instantiated slices
795     def slices(self, opts, args):
796         """
797         list instantiated slices
798         """
799         user_cred = self.get_user_cred().save_to_string(save_parents=True)
800         creds = [user_cred]
801         if opts.delegate:
802             delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
803             creds.append(delegated_cred)  
804         server = self.get_server_from_opts(opts)
805         #results = server.ListSlices(creds, unique_call_id())
806         results = server.ListSlices(creds)
807         display_list(results)
808         return
809     
810     # show rspec for named slice
811     def resources(self, opts, args):
812         user_cred = self.get_user_cred().save_to_string(save_parents=True)
813         server = self.slicemgr
814         call_options = {}
815         server = self.get_server_from_opts(opts)
816         
817         if args:
818             cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
819             hrn = args[0]
820             call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
821         else:
822             cred = user_cred
823             hrn = None
824      
825         creds = [cred]
826         if opts.delegate:
827             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
828             creds.append(delegated_cred)
829         if opts.rspec_version:
830             call_options['rspec_version'] = opts.rspec_version 
831         result = server.ListResources(creds, call_options,unique_call_id())
832         format = opts.format
833         if opts.file is None:
834             display_rspec(result, format)
835         else:
836             file = opts.file
837             if not file.startswith(os.sep):
838                 file = os.path.join(self.options.sfi_dir, file)
839             save_rspec_to_file(result, file)
840         return
841     
842     # created named slice with given rspec
843     def create(self, opts, args):
844         slice_hrn = args[0]
845         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
846         user_cred = self.get_user_cred()
847         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
848         creds = [slice_cred]
849         if opts.delegate:
850             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
851             creds.append(delegated_cred)
852         rspec_file = self.get_rspec_file(args[1])
853         rspec = open(rspec_file).read()
854
855         # TODO: need to determine if this request is going to a ProtoGENI aggregate. If so
856         # we need to obtain the keys for all users in the slice  
857         # e.g. 
858         # users = [
859         #  { urn: urn:publicid:IDN+emulab.net+user+alice
860         #    keys: [<ssh key A>, <ssh key B>] 
861         #  }]
862         users = []
863         server = self.get_server_from_opts(opts)
864         result =  server.CreateSliver(slice_urn, creds, rspec, users, unique_call_id())
865         print result
866         return result
867
868     # get a ticket for the specified slice
869     def get_ticket(self, opts, args):
870         slice_hrn, rspec_path = args[0], args[1]
871         slice_urn = hrn_to_urn(slice_hrn, 'slice')
872         user_cred = self.get_user_cred()
873         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
874         creds = [slice_cred]
875         if opts.delegate:
876             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
877             creds.append(delegated_cred)
878         rspec_file = self.get_rspec_file(rspec_path) 
879         rspec = open(rspec_file).read()
880         server = self.get_server_from_opts(opts)
881         ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
882         file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
883         self.logger.info("writing ticket to %s"%file)
884         ticket = SfaTicket(string=ticket_string)
885         ticket.save_to_file(filename=file, save_parents=True)
886
887     def redeem_ticket(self, opts, args):
888         ticket_file = args[0]
889         
890         # get slice hrn from the ticket
891         # use this to get the right slice credential 
892         ticket = SfaTicket(filename=ticket_file)
893         ticket.decode()
894         slice_hrn = ticket.gidObject.get_hrn()
895         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
896         #slice_hrn = ticket.attributes['slivers'][0]['hrn']
897         user_cred = self.get_user_cred()
898         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
899         
900         # get a list of node hostnames from the RSpec 
901         tree = etree.parse(StringIO(ticket.rspec))
902         root = tree.getroot()
903         hostnames = root.xpath("./network/site/node/hostname/text()")
904         
905         # create an xmlrpc connection to the component manager at each of these
906         # components and gall redeem_ticket
907         connections = {}
908         for hostname in hostnames:
909             try:
910                 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
911                 server = self.get_server(hostname, CM_PORT, self.key_file, \
912                                          self.cert_file, self.options.debug)
913                 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
914                 self.logger.info("Success")
915             except socket.gaierror:
916                 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
917             except Exception, e:
918                 self.logger.log_exc(e.message)
919         return
920  
921     # delete named slice
922     def delete(self, opts, args):
923         slice_hrn = args[0]
924         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
925         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
926         creds = [slice_cred]
927         if opts.delegate:
928             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
929             creds.append(delegated_cred)
930         server = self.get_server_from_opts(opts)
931         return server.DeleteSliver(slice_urn, creds, unique_call_id())
932     
933     # start named slice
934     def start(self, opts, args):
935         slice_hrn = args[0]
936         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
937         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
938         creds = [slice_cred]
939         if opts.delegate:
940             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
941             creds.append(delegated_cred)
942         server = self.get_server_from_opts(opts)
943         return server.Start(slice_urn, creds)
944     
945     # stop named slice
946     def stop(self, opts, args):
947         slice_hrn = args[0]
948         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
949         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
950         creds = [slice_cred]
951         if opts.delegate:
952             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
953             creds.append(delegated_cred)
954         server = self.get_server_from_opts(opts)
955         return server.Stop(slice_urn, creds)
956     
957     # reset named slice
958     def reset(self, opts, args):
959         slice_hrn = args[0]
960         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
961         server = self.get_server_from_opts(opts)
962         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
963         creds = [slice_cred]
964         if opts.delegate:
965             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
966             creds.append(delegated_cred)
967         return server.reset_slice(creds, slice_urn)
968
969     def renew(self, opts, args):
970         slice_hrn = args[0]
971         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
972         server = self.get_server_from_opts(opts)
973         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
974         creds = [slice_cred]
975         if opts.delegate:
976             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
977             creds.append(delegated_cred)
978         time = args[1]
979         return server.RenewSliver(slice_urn, creds, time, unique_call_id())
980
981
982     def status(self, opts, args):
983         slice_hrn = args[0]
984         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
985         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
986         creds = [slice_cred]
987         if opts.delegate:
988             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
989             creds.append(delegated_cred)
990         server = self.get_server_from_opts(opts)
991         print server.SliverStatus(slice_urn, creds, unique_call_id())
992
993
994     def shutdown(self, opts, args):
995         slice_hrn = args[0]
996         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
997         slice_cred = self.get_slice_cred(slice_hrn).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         server = self.get_server_from_opts(opts)
1003         return server.Shutdown(slice_urn, creds)         
1004     
1005     def print_help (self):
1006         self.sfi_parser.print_help()
1007         self.cmd_parser.print_help()
1008
1009     #
1010     # Main: parse arguments and dispatch to command
1011     #
1012     def main(self):
1013         self.sfi_parser = self.create_parser()
1014         (options, args) = self.sfi_parser.parse_args()
1015         self.options = options
1016
1017         self.logger.setLevelFromOptVerbose(self.options.verbose)
1018         if options.hashrequest:
1019             self.hashrequest = True
1020  
1021         if len(args) <= 0:
1022             self.logger.critical("No command given. Use -h for help.")
1023             return -1
1024     
1025         command = args[0]
1026         self.cmd_parser = self.create_cmd_parser(command)
1027         (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
1028
1029         self.set_servers()
1030     
1031         self.logger.info("Command=%s" % command)
1032         if command in ("resources"):
1033             self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
1034         elif command in ("list", "show", "remove"):
1035             self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
1036         self.logger.debug('cmd_args %s',cmd_args)
1037
1038         try:
1039             self.dispatch(command, cmd_opts, cmd_args)
1040         except KeyError:
1041             self.logger.critical ("Unknown command %s"%command)
1042             sys.exit(1)
1043     
1044         return
1045     
1046 if __name__ == "__main__":
1047     Sfi().main()