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