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