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