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