Merge branch 'master' of ssh://git.planet-lab.org/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 import logging
19
20 from sfa.trust.certificate import Keypair, Certificate
21 from sfa.trust.credential import Credential
22 from sfa.util.sfaticket import SfaTicket
23 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
24 from sfa.util.namespace import get_leaf, get_authority, hrn_to_urn
25 from sfa.util.xmlrpcprotocol import ServerException
26 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
27 from sfa.util.config import Config
28 from sfa.util.sfalogging import console_logger
29
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     def __init__ (self):
125         self.slicemgr = None
126         self.registry = None
127         self.user = None
128         self.authority = None
129         self.options = None
130         self.hashrequest = False
131         self.logger=console_logger
132    
133     def create_cmd_parser(self, command, additional_cmdargs=None):
134         cmdargs = {"list": "name",
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",
235                          action="store_true", dest="verbose", default=False,
236                          help="verbose mode")
237         parser.add_option("-D", "--debug",
238                           action="store_true", dest="debug", default=False,
239                           help="Debug (xml-rpc) protocol messages")
240         parser.add_option("-p", "--protocol",
241                          dest="protocol", default="xmlrpc",
242                          help="RPC protocol (xmlrpc or soap)")
243         parser.add_option("-k", "--hashrequest",
244                          action="store_true", dest="hashrequest", default=False,
245                          help="Create a hash of the request that will be authenticated on the server")
246         parser.disable_interspersed_args()
247
248         return parser
249         
250  
251     #
252     # Establish Connection to SliceMgr and Registry Servers
253     #
254     def set_servers(self):
255        config_file = self.options.sfi_dir + os.sep + "sfi_config"
256        try:
257           config = Config (config_file)
258        except:
259           self.logger.critical("Failed to read configuration file %s"%config_file)
260           self.logger.info("Make sure to remove the export clauses and to add quotes")
261           if not self.options.verbose:
262               self.logger.info("Re-run with -v for more details")
263           else:
264               self.logger.log_exc("Could not read config file %s"%config_file)
265           sys.exit(1)
266     
267        errors = 0
268        # Set SliceMgr URL
269        if (self.options.sm is not None):
270           sm_url = self.options.sm
271        elif hasattr(config, "SFI_SM"):
272           sm_url = config.SFI_SM
273        else:
274           self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
275           errors += 1 
276     
277        # Set Registry URL
278        if (self.options.registry is not None):
279           reg_url = self.options.registry
280        elif hasattr(config, "SFI_REGISTRY"):
281           reg_url = config.SFI_REGISTRY
282        else:
283           self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
284           errors += 1 
285           
286
287        # Set user HRN
288        if (self.options.user is not None):
289           self.user = self.options.user
290        elif hasattr(config, "SFI_USER"):
291           self.user = config.SFI_USER
292        else:
293           self.logger.errors("You need to set e.g. SFI_USER='plc.princeton.username' in %s" % config_file)
294           errors += 1 
295     
296        # Set authority HRN
297        if (self.options.auth is not None):
298           self.authority = self.options.auth
299        elif hasattr(config, "SFI_AUTH"):
300           self.authority = config.SFI_AUTH
301        else:
302           self.logger.error("You need to set e.g. SFI_AUTH='plc.princeton' in %s" % config_file)
303           errors += 1 
304     
305        if errors:
306           sys.exit(1)
307     
308     
309        # Get key and certificate
310        key_file = self.get_key_file()
311        cert_file = self.get_cert_file(key_file)
312        self.key = Keypair(filename=key_file) 
313        self.key_file = key_file
314        self.cert_file = cert_file
315        self.cert = Certificate(filename=cert_file) 
316        # Establish connection to server(s)
317        self.logger.info("Contacting Registry at: %s"%reg_url)
318        self.registry = xmlrpcprotocol.get_server(reg_url, key_file, cert_file, self.options)  
319        self.logger.info("Contacting Slice Manager at: %s"%sm_url)
320        self.slicemgr = xmlrpcprotocol.get_server(sm_url, key_file, cert_file, self.options)
321
322        return
323     
324     #
325     # Get various credential and spec files
326     #
327     # Establishes limiting conventions
328     #   - conflates MAs and SAs
329     #   - assumes last token in slice name is unique
330     #
331     # Bootstraps credentials
332     #   - bootstrap user credential from self-signed certificate
333     #   - bootstrap authority credential from user credential
334     #   - bootstrap slice credential from user credential
335     #
336     
337     
338     def get_key_file(self):
339        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
340        #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".pkey")
341        if (os.path.isfile(file)):
342           return file
343        else:
344           self.logger.error("Key file %s does not exist"%file)
345           sys.exit(-1)
346        return
347     
348     def get_cert_file(self, key_file):
349     
350        #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".cert")
351        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
352        if (os.path.isfile(file)):
353           return file
354        else:
355           k = Keypair(filename=key_file)
356           cert = Certificate(subject=self.user)
357           cert.set_pubkey(k)
358           cert.set_issuer(k, self.user)
359           cert.sign()
360           self.logger.info("Writing self-signed certificate to %s"%file)
361           cert.save_to_file(file)
362           return file
363
364     def get_cached_gid(self, file):
365         """
366         Return a cached gid    
367         """
368         gid = None 
369         if (os.path.isfile(file)):
370             gid = GID(filename=file)
371         return gid
372
373     def get_gid(self, opts, args):
374         hrn = None
375         if args:
376             hrn = args[0]
377         gid = self._get_gid(hrn)
378         self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
379         return gid
380
381     def _get_gid(self, hrn=None):
382         if not hrn:
383             hrn = self.user
384  
385         gidfile = os.path.join(self.options.sfi_dir, hrn + ".gid")
386         gid = self.get_cached_gid(gidfile)
387         if not gid:
388             user_cred = self.get_user_cred()
389             records = self.registry.Resolve(hrn, user_cred.save_to_string(save_parents=True))
390             if not records:
391                 raise RecordNotFound(args[0])
392             gid = GID(string=records[0]['gid'])
393             self.logger.info("Writing gid to %s"%gidfile)
394             gid.save_to_file(filename=gidfile)
395         return gid   
396                 
397      
398     def get_cached_credential(self, file):
399         """
400         Return a cached credential only if it hasn't expired.
401         """
402         if (os.path.isfile(file)):
403             credential = Credential(filename=file)
404             # make sure it isnt expired 
405             if not credential.get_lifetime or \
406                datetime.datetime.today() < credential.get_lifetime():
407                 return credential
408         return None 
409  
410     def get_user_cred(self):
411         #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".cred")
412         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
413         return self.get_cred(file, 'user', self.user)
414
415     def get_auth_cred(self):
416         if not self.authority:
417             self.logger.critical("no authority specified. Use -a or set SF_AUTH")
418             sys.exit(-1)
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.get_self_credential(cert_string, "user", hrn)
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.get_credential(user_cred, type, hrn)
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         user_cred = self.get_user_cred().save_to_string(save_parents=True)
535         hrn = args[0]
536         try:
537             list = self.registry.List(hrn, user_cred)
538         except IndexError:
539             raise Exception, "Not enough parameters for the 'list' command"
540           
541         # filter on person, slice, site, node, etc.  
542         # THis really should be in the self.filter_records funct def comment...
543         list = filter_records(opts.type, list)
544         for record in list:
545             print "%s (%s)" % (record['hrn'], record['type'])     
546         if opts.file:
547             file = opts.file
548             if not file.startswith(os.sep):
549                 file = os.path.join(self.options.sfi_dir, file)
550             save_records_to_file(file, list)
551         return
552     
553     # show named registry record
554     def show(self, opts, args):
555         user_cred = self.get_user_cred().save_to_string(save_parents=True)
556         hrn = args[0]
557         records = self.registry.Resolve(hrn, user_cred)
558         records = filter_records(opts.type, records)
559         if not records:
560             print "No record of type", opts.type
561         for record in records:
562             if record['type'] in ['user']:
563                 record = UserRecord(dict=record)
564             elif record['type'] in ['slice']:
565                 record = SliceRecord(dict=record)
566             elif record['type'] in ['node']:
567                 record = NodeRecord(dict=record)
568             elif record['type'] in ['authority', 'ma', 'sa']:
569                 record = AuthorityRecord(dict=record)
570             else:
571                 record = SfaRecord(dict=record)
572             if (opts.format == "text"): 
573                 record.dump()  
574             else:
575                 print record.save_to_string() 
576        
577         if opts.file:
578             file = opts.file
579             if not file.startswith(os.sep):
580                 file = os.path.join(self.options.sfi_dir, file)
581             save_records_to_file(file, records)
582         return
583     
584     def delegate(self, opts, args):
585
586         delegee_hrn = args[0]
587         if opts.delegate_user:
588             user_cred = self.get_user_cred()
589             cred = self.delegate_cred(user_cred, delegee_hrn)
590         elif opts.delegate_slice:
591             slice_cred = self.get_slice_cred(opts.delegate_slice)
592             cred = self.delegate_cred(slice_cred, delegee_hrn)
593         else:
594             self.logger.warning("Must specify either --user or --slice <hrn>")
595             return
596         delegated_cred = Credential(string=cred)
597         object_hrn = delegated_cred.get_gid_object().get_hrn()
598         if opts.delegate_user:
599             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_"
600                                   + get_leaf(object_hrn) + ".cred")
601         elif opts.delegate_slice:
602             dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_"
603                                   + get_leaf(object_hrn) + ".cred")
604
605         delegated_cred.save_to_file(dest_fn, save_parents=True)
606
607         self.logger.info("delegated credential for %s to %s and wrote to %s"%(object_hrn, delegee_hrn,dest_fn))
608     
609     def delegate_cred(self, object_cred, hrn):
610         # the gid and hrn of the object we are delegating
611         if isinstance(object_cred, str):
612             object_cred = Credential(string=object_cred) 
613         object_gid = object_cred.get_gid_object()
614         object_hrn = object_gid.get_hrn()
615     
616         if not object_cred.get_privileges().get_all_delegate():
617             self.logger.error("Object credential %s does not have delegate bit set"%object_hrn)
618             return
619
620         # the delegating user's gid
621         caller_gid = self._get_gid(self.user)
622         caller_gidfile = os.path.join(self.options.sfi_dir, self.user + ".gid")
623   
624         # the gid of the user who will be delegated to
625         delegee_gid = self._get_gid(hrn)
626         delegee_hrn = delegee_gid.get_hrn()
627         delegee_gidfile = os.path.join(self.options.sfi_dir, delegee_hrn + ".gid")
628         delegee_gid.save_to_file(filename=delegee_gidfile)
629         dcred = object_cred.delegate(delegee_gidfile, self.get_key_file(), caller_gidfile)
630         return dcred.save_to_string(save_parents=True)
631      
632     # removed named registry record
633     #   - have to first retrieve the record to be removed
634     def remove(self, opts, args):
635         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
636         hrn = args[0]
637         type = opts.type 
638         if type in ['all']:
639             type = '*'
640         return self.registry.Remove(hrn, auth_cred, type)
641     
642     # add named registry record
643     def add(self, opts, args):
644         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
645         record_filepath = args[0]
646         rec_file = self.get_record_file(record_filepath)
647         record = load_record_from_file(rec_file).as_dict()
648         return self.registry.Register(record, auth_cred)
649     
650     # update named registry entry
651     def update(self, opts, args):
652         user_cred = self.get_user_cred()
653         rec_file = self.get_record_file(args[0])
654         record = load_record_from_file(rec_file)
655         if record['type'] == "user":
656             if record.get_name() == user_cred.get_gid_object().get_hrn():
657                 cred = user_cred.save_to_string(save_parents=True)
658             else:
659                 cred = self.get_auth_cred().save_to_string(save_parents=True)
660         elif record['type'] in ["slice"]:
661             try:
662                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
663             except ServerException, e:
664                # XXX smbaker -- once we have better error return codes, update this
665                # to do something better than a string compare
666                if "Permission error" in e.args[0]:
667                    cred = self.get_auth_cred().save_to_string(save_parents=True)
668                else:
669                    raise
670         elif record.get_type() in ["authority"]:
671             cred = self.get_auth_cred().save_to_string(save_parents=True)
672         elif record.get_type() == 'node':
673             cred = self.get_auth_cred().save_to_string(save_parents=True)
674         else:
675             raise "unknown record type" + record.get_type()
676         record = record.as_dict()
677         return self.registry.Update(record, cred)
678   
679     def get_trusted_certs(self, opts, args):
680         """
681         return uhe trusted certs at this interface 
682         """ 
683         trusted_certs = self.registry.get_trusted_certs()
684         for trusted_cert in trusted_certs:
685             cert = Certificate(string=trusted_cert)
686             self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
687         return 
688
689     def aggregates(self, opts, args):
690         """
691         return a list of details about known aggregates
692         """
693         user_cred = self.get_user_cred().save_to_string(save_parents=True)
694         hrn = None
695         if args:
696             hrn = args[0]
697
698         result = self.registry.get_aggregates(user_cred, hrn)
699         display_list(result)
700         return 
701
702     def registries(self, opts, args):
703         """
704         return a list of details about known registries
705         """
706         user_cred = self.get_user_cred().save_to_string(save_parents=True)
707         hrn = None
708         if args:
709             hrn = args[0]
710         result = self.registry.get_registries(user_cred, hrn)
711         display_list(result)
712         return
713
714  
715     # ==================================================================
716     # Slice-related commands
717     # ==================================================================
718     
719
720     def version(self, opts, args):
721         server = self.get_server_from_opts(opts)
722         
723         print server.GetVersion()
724
725     # list instantiated slices
726     def slices(self, opts, args):
727         """
728         list instantiated slices
729         """
730         user_cred = self.get_user_cred().save_to_string(save_parents=True)
731         creds = [user_cred]
732         if opts.delegate:
733             delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
734             creds.append(delegated_cred)  
735         server = self.get_server_from_opts(opts)
736         results = server.ListSlices(creds)
737         display_list(results)
738         return
739     
740     # show rspec for named slice
741     def resources(self, opts, args):
742         user_cred = self.get_user_cred().save_to_string(save_parents=True)
743         server = self.slicemgr
744         call_options = {}
745         server = self.get_server_from_opts(opts)
746         
747         if args:
748             cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
749             hrn = args[0]
750             call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
751         else:
752             cred = user_cred
753             hrn = None
754      
755         creds = [cred]
756         if opts.delegate:
757             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
758             creds.append(delegated_cred) 
759         result = server.ListResources(creds, call_options)
760         format = opts.format
761         display_rspec(result, format)
762         if (opts.file is not None):
763             file = opts.file
764             if not file.startswith(os.sep):
765                 file = os.path.join(self.options.sfi_dir, file)
766             save_rspec_to_file(result, file)
767         return
768     
769     # created named slice with given rspec
770     def create(self, opts, args):
771         slice_hrn = args[0]
772         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
773         user_cred = self.get_user_cred()
774         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
775         creds = [slice_cred]
776         if opts.delegate:
777             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
778             creds.append(delegated_cred)
779         rspec_file = self.get_rspec_file(args[1])
780         rspec = open(rspec_file).read()
781         server = self.get_server_from_opts(opts)
782         result =  server.CreateSliver(slice_urn, creds, rspec, [])
783         print result
784         return result
785
786     # get a ticket for the specified slice
787     def get_ticket(self, opts, args):
788         slice_hrn, rspec_path = args[0], args[1]
789         slice_urn = hrn_to_urn(slice_hrn, 'slice')
790         user_cred = self.get_user_cred()
791         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
792         creds = [slice_cred]
793         if opts.delegate:
794             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
795             creds.append(delegated_cred)
796         rspec_file = self.get_rspec_file(rspec_path) 
797         rspec = open(rspec_file).read()
798         server = self.get_server_from_opts(opts)
799         ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
800         file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
801         self.logger.info("writing ticket to %s"%file)
802         ticket = SfaTicket(string=ticket_string)
803         ticket.save_to_file(filename=file, save_parents=True)
804
805     def redeem_ticket(self, opts, args):
806         ticket_file = args[0]
807         
808         # get slice hrn from the ticket
809         # use this to get the right slice credential 
810         ticket = SfaTicket(filename=ticket_file)
811         ticket.decode()
812         slice_hrn = ticket.gidObject.get_hrn()
813         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
814         #slice_hrn = ticket.attributes['slivers'][0]['hrn']
815         user_cred = self.get_user_cred()
816         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
817         
818         # get a list of node hostnames from the RSpec 
819         tree = etree.parse(StringIO(ticket.rspec))
820         root = tree.getroot()
821         hostnames = root.xpath("./network/site/node/hostname/text()")
822         
823         # create an xmlrpc connection to the component manager at each of these
824         # components and gall redeem_ticket
825         connections = {}
826         for hostname in hostnames:
827             try:
828                 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
829                 server = self.get_server(hostname, CM_PORT, self.key_file, \
830                                          self.cert_file, self.options.debug)
831                 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
832                 self.logger.info("Success")
833             except socket.gaierror:
834                 self.logger.error("redeem_ticket failed: Component Manager not accepting requests")
835             except Exception, e:
836                 self.logger.log_exc(e.message)
837         return
838  
839     # delete named slice
840     def delete(self, opts, args):
841         slice_hrn = args[0]
842         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
843         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
844         creds = [slice_cred]
845         if opts.delegate:
846             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
847             creds.append(delegated_cred)
848         server = self.get_server_from_opts(opts)
849         return server.DeleteSliver(slice_urn, creds)
850     
851     # start named slice
852     def start(self, opts, args):
853         slice_hrn = args[0]
854         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
855         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
856         creds = [slice_cred]
857         if opts.delegate:
858             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
859             creds.append(delegated_cred)
860         server = self.get_server_from_opts(opts)
861         return server.Start(slice_urn, creds)
862     
863     # stop named slice
864     def stop(self, opts, args):
865         slice_hrn = args[0]
866         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
867         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
868         creds = [slice_cred]
869         if opts.delegate:
870             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
871             creds.append(delegated_cred)
872         server = self.get_server_from_opts(opts)
873         return server.Stop(slice_urn, creds)
874     
875     # reset named slice
876     def reset(self, opts, args):
877         slice_hrn = args[0]
878         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
879         server = self.get_server_from_opts(opts)
880         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
881         creds = [slice_cred]
882         if opts.delegate:
883             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
884             creds.append(delegated_cred)
885         return server.reset_slice(creds, slice_urn)
886
887     def renew(self, opts, args):
888         slice_hrn = args[0]
889         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
890         server = self.get_server_from_opts(opts)
891         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
892         creds = [slice_cred]
893         if opts.delegate:
894             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
895             creds.append(delegated_cred)
896         time = args[1]
897         return server.RenewSliver(slice_urn, creds, time)
898
899
900     def status(self, opts, args):
901         slice_hrn = args[0]
902         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
903         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
904         creds = [slice_cred]
905         if opts.delegate:
906             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
907             creds.append(delegated_cred)
908         server = self.get_server_from_opts(opts)
909         print server.SliverStatus(slice_urn, creds)
910
911
912     def shutdown(self, opts, args):
913         slice_hrn = args[0]
914         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
915         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
916         creds = [slice_cred]
917         if opts.delegate:
918             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
919             creds.append(delegated_cred)
920         server = self.get_server_from_opts(opts)
921         return server.Shutdown(slice_urn, creds)         
922     
923
924     #
925     # Main: parse arguments and dispatch to command
926     #
927     def main(self):
928         parser = self.create_parser()
929         (options, args) = parser.parse_args()
930         self.options = options
931
932         if self.options.verbose: self.logger.setLevel(logging.DEBUG)
933         if options.hashrequest:
934             self.hashrequest = True
935  
936         if len(args) <= 0:
937             self.logger.critical("No command given. Use -h for help.")
938             return -1
939     
940         command = args[0]
941         (cmd_opts, cmd_args) = self.create_cmd_parser(command).parse_args(args[1:])
942
943         self.set_servers()
944     
945         self.logger.info("Command %s" % command)
946         self.logger.info("dir %s, user %s, auth %s, reg %s, sm %s" % (
947                 self. options.sfi_dir, self.options.user,self.options.auth,
948                 self.options.registry, self.options.sm))
949         if command in ("resources"):
950             self.logger.debug("resources cmd_opts %s" % cmd_opts.format)
951         elif command in ("list", "show", "remove"):
952             self.logger.debug("cmd_opts.type %s" % cmd_opts.type)
953         self.logger.debug('cmd_args %s',cmd_args)
954
955         try:
956             self.dispatch(command, cmd_opts, cmd_args)
957         except KeyError:
958             self.logger.critical ("Unknown command %s"%command)
959             sys.exit(1)
960     
961         return
962     
963 if __name__ == "__main__":
964    Sfi().main()