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