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