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