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