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