only use cached credentials if they havn't expired
[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
54 def display_records(recordList, dump=False):
55     ''' Print all fields in the record'''
56     for record in recordList:
57         display_record(record, dump)
58
59 def display_record(record, dump=False):
60     if dump:
61         record.dump()
62     else:
63         info = record.getdict()
64         print "%s (%s)" % (info['hrn'], info['type'])
65     return
66
67
68 def filter_records(type, records):
69     filtered_records = []
70     for record in records:
71         if (record['type'] == type) or (type == "all"):
72             filtered_records.append(record)
73     return filtered_records
74
75
76 # save methods
77 def save_rspec_to_file(rspec, filename):
78     if not filename.endswith(".rspec"):
79         filename = filename + ".rspec"
80
81     f = open(filename, 'w')
82     f.write(rspec)
83     f.close()
84     return
85
86 def save_records_to_file(filename, recordList):
87     index = 0
88     for record in recordList:
89         if index > 0:
90             save_record_to_file(filename + "." + str(index), record)
91         else:
92             save_record_to_file(filename, record)
93         index = index + 1
94
95 def save_record_to_file(filename, record):
96     if record['type'] in ['user']:
97         record = UserRecord(dict=record)
98     elif record['type'] in ['slice']:
99         record = SliceRecord(dict=record)
100     elif record['type'] in ['node']:
101         record = NodeRecord(dict=record)
102     elif record['type'] in ['authority', 'ma', 'sa']:
103         record = AuthorityRecord(dict=record)
104     else:
105         record = SfaRecord(dict=record)
106     str = record.save_to_string()
107     file(filename, "w").write(str)
108     return
109
110
111 # load methods
112 def load_record_from_file(filename):
113     str = file(filename, "r").read()
114     record = SfaRecord(string=str)
115     return record
116
117
118
119 class Sfi:
120
121     geni_am = None
122     slicemgr = None
123     registry = None
124     user = None
125     authority = None
126     options = None
127     hashrequest = False
128    
129     def create_cmd_parser(self, command, additional_cmdargs=None):
130         cmdargs = {"gid": "",
131                   "list": "name",
132                   "show": "name",
133                   "remove": "name",
134                   "add": "record",
135                   "update": "record",
136                   "aggregates": "[name]",
137                   "registries": "[name]",
138                   "slices": "",
139                   "resources": "[name]",
140                   "create": "name rspec",
141                   "get_trusted_certs": "cred",
142                   "get_ticket": "name rspec",
143                   "redeem_ticket": "ticket",
144                   "delete": "name",
145                   "reset": "name",
146                   "start": "name",
147                   "stop": "name",
148                   "delegate": "name",
149                   "GetVersion": "name",
150                   "ListResources": "name",
151                   "CreateSliver": "name",
152                   "get_geni_aggregates": "name",
153                   "DeleteSliver": "name",
154                   "SliverStatus": "name",
155                   "RenewSliver": "name",
156                   "Shutdown": "name"
157                  }
158
159         if additional_cmdargs:
160             cmdargs.update(additional_cmdargs)
161
162         if command not in cmdargs:
163             print "Invalid command\n"
164             print "Commands: ",
165             for key in cmdargs.keys():
166                 print key + ",",
167             print ""
168             sys.exit(2)
169
170         parser = OptionParser(usage="sfi [sfi_options] %s [options] %s" \
171                                      % (command, cmdargs[command]))
172
173         if command in ("resources"):
174             parser.add_option("-f", "--format", dest="format", type="choice",
175                              help="display format ([xml]|dns|ip)", default="xml",
176                              choices=("xml", "dns", "ip"))
177                                 
178         if command in ("resources", "slices", "create", "delete", "start", "stop", "get_ticket"):
179             parser.add_option("-a", "--aggregate", dest="aggregate",
180                              default=None, help="aggregate host")
181             parser.add_option("-p", "--port", dest="port",
182                              default=AGGREGATE_PORT, help="aggregate port")
183
184         if command in ("start", "stop", "reset", "delete", "slices"):
185             parser.add_option("-c", "--component", dest="component", default=None,
186                              help="component hrn")
187             
188         if command in ("list", "show", "remove"):
189             parser.add_option("-t", "--type", dest="type", type="choice",
190                             help="type filter ([all]|user|slice|sa|ma|node|aggregate)",
191                             choices=("all", "user", "slice", "sa", "ma", "node", "aggregate"),
192                             default="all")
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_lifefime():
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
401         user_cred = self.get_cached_credential(file)
402         if user_cred:
403             return user_cred
404         else:
405             # bootstrap user credential
406             cert_string = self.cert.save_to_string(save_parents=True)
407             user_name = self.user.replace(self.authority + ".", '')
408             if user_name.count(".") > 0:
409                 user_name = user_name.replace(".", '_')
410                 self.user = self.authority + "." + user_name
411
412             user_cred = self.registry.get_self_credential(cert_string, "user", self.user)
413             if user_cred:
414                cred = Credential(string=user_cred)
415                cred.save_to_file(file, save_parents=True)
416                if self.options.verbose:
417                     print "Writing user credential to", file
418                return cred
419             else:
420                print "Failed to get user credential"
421                sys.exit(-1)
422   
423     def get_auth_cred(self):
424         if not self.authority:
425             print "no authority specified. Use -a or set SF_AUTH"
426             sys.exit(-1)
427     
428         file = os.path.join(self.options.sfi_dir, get_leaf("authority") + ".cred")
429         auth_cred = self.get_cached_credential(file)
430         if auth_cred:
431             return auth_cred
432         else:
433             # bootstrap authority credential from user credential
434             user_cred = self.get_user_cred().save_to_string(save_parents=True)
435             auth_cred = self.registry.get_credential(user_cred, "authority", self.authority)
436             if auth_cred:
437                 cred = Credential(string=auth_cred)
438                 cred.save_to_file(file, save_parents=True)
439                 if self.options.verbose:
440                     print "Writing authority credential to", file
441                 return cred
442             else:
443                 print "Failed to get authority credential"
444                 sys.exit(-1)
445     
446     def get_slice_cred(self, name):
447         file = os.path.join(self.options.sfi_dir, "slice_" + get_leaf(name) + ".cred")
448         slice_cred = self.get_cached_credential(file)
449         if slice_cred:
450             return slice_cred
451         else:
452             # bootstrap slice credential from user credential
453             user_cred = self.get_user_cred().save_to_string(save_parents=True)
454             arg_list = [user_cred, "slice", name]
455             slice_cred_str = self.registry.get_credential(user_cred, "slice", name)
456             if slice_cred_str:
457                 slice_cred = Credential(string=slice_cred_str)
458                 slice_cred.save_to_file(file, save_parents=True)
459                 if self.options.verbose:
460                     print "Writing slice credential to", file
461                 return slice_cred
462             else:
463                 print "Failed to get slice credential"
464                 sys.exit(-1)
465     
466     def delegate_cred(self, cred, hrn, type='authority'):
467         # the gid and hrn of the object we are delegating
468         user_cred = Credential(string=cred)
469         object_gid = user_cred.get_gid_object()
470         object_hrn = object_gid.get_hrn()
471         #cred.set_delegate(True)
472         #if not cred.get_delegate():
473         #    raise Exception, "Error: Object credential %(object_hrn)s does not have delegate bit set" % locals()
474            
475     
476         records = self.registry.resolve(cred, hrn)
477         records = filter_records(type, records)
478         
479         if not records:
480             raise Exception, "Error: Didn't find a %(type)s record for %(hrn)s" % locals()
481     
482         # the gid of the user who will be delegated too
483         record = SfaRecord(dict=records[0])
484         delegee_gid = record.get_gid_object()
485         delegee_hrn = delegee_gid.get_hrn()
486         
487         # the key and hrn of the user who will be delegating
488         user_key = Keypair(filename=self.get_key_file())
489         user_hrn = user_cred.get_gid_caller().get_hrn()
490     
491         dcred = Credential(subject=object_hrn + " delegated to " + delegee_hrn)
492         dcred.set_gid_caller(delegee_gid)
493         dcred.set_gid_object(object_gid)
494         dcred.set_privileges(user_cred.get_privileges())
495         dcred.get_privileges().delegate_all_privileges(True)
496         
497
498         # Save the issuer's gid to a file
499         fname = self.options.sfi_dir + os.sep + "gid_%d" % random.randint(0, 999999999)
500         f = open(fname, "w")
501         f.write(user_cred.get_gid_caller().save_to_string())
502         f.close()
503         dcred.set_issuer_keys(self.get_key_file(), fname)
504         os.remove(fname)
505         
506         dcred.set_parent(user_cred)
507         dcred.encode()
508         dcred.sign()
509     
510         return dcred.save_to_string(save_parents=True)
511     
512     def get_rspec_file(self, rspec):
513        if (os.path.isabs(rspec)):
514           file = rspec
515        else:
516           file = os.path.join(self.options.sfi_dir, rspec)
517        if (os.path.isfile(file)):
518           return file
519        else:
520           print "No such rspec file", rspec
521           sys.exit(1)
522     
523     def get_record_file(self, record):
524        if (os.path.isabs(record)):
525           file = record
526        else:
527           file = os.path.join(self.options.sfi_dir, record)
528        if (os.path.isfile(file)):
529           return file
530        else:
531           print "No such registry record file", record
532           sys.exit(1)
533     
534     def load_publickey_string(self, fn):
535        f = file(fn, "r")
536        key_string = f.read()
537     
538        # if the filename is a private key file, then extract the public key
539        if "PRIVATE KEY" in key_string:
540            outfn = tempfile.mktemp()
541            cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
542            os.system(cmd)
543            f = file(outfn, "r")
544            key_string = f.read()
545            os.remove(outfn)
546     
547        return key_string
548
549     def get_component_server_from_hrn(self, hrn):
550         # direct connection to the nodes component manager interface
551         user_cred = self.get_user_cred().save_to_string(save_parents=True)
552         records = self.registry.resolve(user_cred, hrn)
553         records = filter_records('node', records)
554         if not records:
555             print "No such component:", opts.component
556         record = records[0]
557   
558         return self.get_server(record['hostname'], CM_PORT, self.key_file, \
559                                self.cert_file, self.options.debug)
560  
561     def get_server(self, host, port, keyfile, certfile, debug):
562         """
563         Return an instnace of an xmlrpc server connection    
564         """
565         url = "http://%s:%s" % (host, port)
566         return xmlrpcprotocol.get_server(url, keyfile, certfile, debug)
567  
568     #==========================================================================
569     # Following functions implement the commands
570     #
571     # Registry-related commands
572     #==========================================================================
573   
574     def dispatch(self, command, cmd_opts, cmd_args):
575         getattr(self, command)(cmd_opts, cmd_args)
576  
577     def gid(self, opts, args):
578         gid = self.get_gid()
579         print "GID: %s" % (gid.save_to_string(save_parents=True))
580         return   
581  
582     # list entires in named authority registry
583     def list(self, opts, args):
584         user_cred = self.get_user_cred().save_to_string(save_parents=True)
585         hrn = args[0]
586         try:
587             list = self.registry.list(user_cred, hrn)
588         except IndexError:
589             raise Exception, "Not enough parameters for the 'list' command"
590           
591         # filter on person, slice, site, node, etc.  
592         # THis really should be in the self.filter_records funct def comment...
593         list = filter_records(opts.type, list)
594         for record in list:
595             print "%s (%s)" % (record['hrn'], record['type'])     
596         if opts.file:
597             file = opts.file
598             if not file.startswith(os.sep):
599                 file = os.path.join(self.options.sfi_dir, file)
600             save_records_to_file(file, list)
601         return
602     
603     # show named registry record
604     def show(self, opts, args):
605         user_cred = self.get_user_cred().save_to_string(save_parents=True)
606         hrn = args[0]
607         records = self.registry.resolve(user_cred, hrn)
608         records = filter_records(opts.type, records)
609         if not records:
610             print "No record of type", opts.type
611         for record in records:
612             if record['type'] in ['user']:
613                 record = UserRecord(dict=record)
614             elif record['type'] in ['slice']:
615                 record = SliceRecord(dict=record)
616             elif record['type'] in ['node']:
617                 record = NodeRecord(dict=record)
618             elif record['type'] in ['authority', 'ma', 'sa']:
619                 record = AuthorityRecord(dict=record)
620             else:
621                 record = SfaRecord(dict=record)
622             if (opts.format == "text"): 
623                 record.dump()  
624             else:
625                 print record.save_to_string() 
626        
627         if opts.file:
628             file = opts.file
629             if not file.startswith(os.sep):
630                 file = os.path.join(self.options.sfi_dir, file)
631             save_records_to_file(file, records)
632         return
633     
634     def delegate(self, opts, args):
635        user_cred = self.get_user_cred()
636        if opts.delegate_user:
637            object_cred = user_cred
638        elif opts.delegate_slice:
639            object_cred = self.get_slice_cred(opts.delegate_slice)
640        else:
641            print "Must specify either --user or --slice <hrn>"
642            return
643     
644        # the gid and hrn of the object we are delegating
645        object_gid = object_cred.get_gid_object()
646        object_hrn = object_gid.get_hrn()
647     
648        if not object_cred.get_privileges().get_all_delegate():
649            print "Error: Object credential", object_hrn, "does not have delegate bit set"
650            return
651     
652        records = self.registry.resolve(user_cred.save_to_string(save_parents=True), args[0])
653        records = filter_records("user", records)
654     
655        if not records:
656            print "Error: Didn't find a user record for", args[0]
657            return
658     
659        # the gid of the user who will be delegated to
660        delegee_gid = GID(string=records[0]['gid'])
661        delegee_hrn = delegee_gid.get_hrn()
662    
663        # the key and hrn of the user who will be delegating
664        user_key = Keypair(filename=self.get_key_file())
665        user_hrn = user_cred.get_gid_caller().get_hrn()
666        subject_string = "%s delegated to %s" % (object_hrn, delegee_hrn)
667        dcred = Credential(subject=subject_string)
668        dcred.set_gid_caller(delegee_gid)
669        dcred.set_gid_object(object_gid)
670        privs = object_cred.get_privileges()
671        dcred.set_privileges(object_cred.get_privileges())
672        dcred.get_privileges().delegate_all_privileges(True)
673        dcred.set_pubkey(object_gid.get_pubkey())
674        dcred.set_issuer(user_key, user_hrn)
675        dcred.set_parent(object_cred)
676        dcred.encode()
677        dcred.sign()
678     
679        if opts.delegate_user:
680            dest_fn = os.path.join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_" 
681                                   + get_leaf(object_hrn) + ".cred")
682        elif opts.delegate_slice:
683            dest_fn = os.path_join(self.options.sfi_dir, get_leaf(delegee_hrn) + "_slice_" 
684                                   + get_leaf(object_hrn) + ".cred")
685     
686        dcred.save_to_file(dest_fn, save_parents=True)
687     
688        print "delegated credential for", object_hrn, "to", delegee_hrn, "and wrote to", dest_fn
689     
690     # removed named registry record
691     #   - have to first retrieve the record to be removed
692     def remove(self, opts, args):
693         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
694         hrn = args[0]
695         type = opts.type 
696         if type in ['all']:
697             type = '*'
698         return self.registry.remove(auth_cred, type, hrn)
699     
700     # add named registry record
701     def add(self, opts, args):
702         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
703         record_filepath = args[0]
704         rec_file = self.get_record_file(record_filepath)
705         record = load_record_from_file(rec_file).as_dict()
706         return self.registry.register(auth_cred, record)
707     
708     # update named registry entry
709     def update(self, opts, args):
710         user_cred = self.get_user_cred()
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 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(cred, record)
736   
737     def get_trusted_certs(self, opts, args):
738         """
739         return the 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             print 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 get_geni_aggregates(self, opts, args):
761         """
762         return a list of details about known aggregates
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
769         result = self.registry.get_geni_aggregates(user_cred, hrn)
770         display_list(result)
771         return 
772
773
774     def registries(self, opts, args):
775         """
776         return a list of details about known registries
777         """
778         user_cred = self.get_user_cred().save_to_string(save_parents=True)
779         hrn = None
780         if args:
781             hrn = args[0]
782         result = self.registry.get_registries(user_cred, hrn)
783         display_list(result)
784         return
785
786  
787     # ==================================================================
788     # Slice-related commands
789     # ==================================================================
790     
791
792     # list instantiated slices
793     def slices(self, opts, args):
794         """
795         list instantiated slices
796         """
797         user_cred = self.get_user_cred().save_to_string(save_parents=True)
798         server = self.slicemgr
799         if opts.aggregate:
800             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
801                                      self.cert_file, self.options.debug)
802         # direct connection to the nodes component manager interface
803         if opts.component:
804             server = self.get_component_server_from_hrn(opts.component)
805         results = server.get_slices(user_cred)
806         display_list(results)
807         return
808     
809     # show rspec for named slice
810     def resources(self, opts, args):
811         user_cred = self.get_user_cred().save_to_string(save_parents=True)
812         server = self.slicemgr
813         if opts.aggregate:
814             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
815                                      self.cert_file, self.options.debug)
816         if args:
817             cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
818             hrn = args[0]
819         else:
820             cred = user_cred
821             hrn = None
822
823         result = server.get_resources(cred, hrn)
824         format = opts.format
825        
826         display_rspec(result, format)
827         if (opts.file is not None):
828             file = opts.file
829             if not file.startswith(os.sep):
830                 file = os.path.join(self.options.sfi_dir, file)
831             save_rspec_to_file(result, file)
832         return
833     
834     # created named slice with given rspec
835     def create(self, opts, args):
836         slice_hrn = args[0]
837         user_cred = self.get_user_cred()
838         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
839         rspec_file = self.get_rspec_file(args[1])
840         rspec = open(rspec_file).read()
841         server = self.slicemgr
842
843         if opts.aggregate:
844             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
845                                      self.cert_file, self.options.debug)
846
847         return server.create_slice(slice_cred, slice_hrn, rspec)
848
849     # get a ticket for the specified slice
850     def get_ticket(self, opts, args):
851         slice_hrn, rspec_path = args[0], args[1]
852         user_cred = self.get_user_cred()
853         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
854         rspec_file = self.get_rspec_file(rspec_path) 
855         rspec = open(rspec_file).read()
856         server = self.slicemgr
857         if opts.aggregate:
858             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
859                                      self.cert_file, self.options.debug)
860         ticket_string = server.get_ticket(slice_cred, slice_hrn, rspec)
861         file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
862         print "writing ticket to ", file        
863         ticket = SfaTicket(string=ticket_string)
864         ticket.save_to_file(filename=file, save_parents=True)
865
866     def redeem_ticket(self, opts, args):
867         ticket_file = args[0]
868         
869         # get slice hrn from the ticket
870         # use this to get the right slice credential 
871         ticket = SfaTicket(filename=ticket_file)
872         ticket.decode()
873         slice_hrn = ticket.gidObject.get_hrn()
874         #slice_hrn = ticket.attributes['slivers'][0]['hrn']
875         user_cred = self.get_user_cred()
876         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
877         
878         # get a list of node hostnames from the RSpec 
879         tree = etree.parse(StringIO(ticket.rspec))
880         root = tree.getroot()
881         hostnames = root.xpath("./network/site/node/hostname/text()")
882         
883         # create an xmlrpc connection to the component manager at each of these
884         # components and gall redeem_ticket
885         connections = {}
886         for hostname in hostnames:
887             try:
888                 print "Calling redeem_ticket at %(hostname)s " % locals(),
889                 server = self.get_server(hostname, CM_PORT, self.key_file, \
890                                          self.cert_file, self.options.debug)
891                 server.redeem_ticket(slice_cred, ticket.save_to_string(save_parents=True))
892                 print "Success"
893             except socket.gaierror:
894                 print "Failed:",
895                 print "Componet Manager not accepting requests" 
896             except Exception, e:
897                 print "Failed:", e.message
898         return
899  
900     # delete named slice
901     def delete(self, opts, args):
902         slice_hrn = args[0]
903         server = self.slicemgr
904         if opts.aggregate:
905             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
906                                      self.cert_file, self.options.debug)
907         # direct connection to the nodes component manager interface
908         if opts.component:
909             server = self.get_component_server_from_hrn(opts.component)
910         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
911         return server.delete_slice(slice_cred, slice_hrn)
912     
913     # start named slice
914     def start(self, opts, args):
915         slice_hrn = args[0]
916         server = self.slicemgr
917         # direct connection to an aggregagte
918         if opts.aggregate:
919             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
920                                      self.cert_file, self.options.debug)
921         if opts.component:
922             server = self.get_component_server_from_hrn(opts.component)
923         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
924         return server.start_slice(slice_cred, slice_hrn)
925     
926     # stop named slice
927     def stop(self, opts, args):
928         slice_hrn = args[0]
929         server = self.slicemgr
930         # direct connection to an aggregate
931         if opts.aggregate:
932             server = self.get_server(opts.aggregate, opts.port, self.key_file, \
933                                      self.cert_file, self.options.debug)
934         # direct connection to the nodes component manager interface
935         if opts.component:
936             server = self.get_component_server_from_hrn(opts.component)
937         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
938         return server.stop_slice(slice_cred, slice_hrn)
939     
940     # reset named slice
941     def reset(self, opts, args):
942         slice_hrn = args[0]
943         server = self.slicemgr
944         # direct connection to the nodes component manager interface
945         if opts.component:
946             server = self.get_component_server_from_hrn(opts.component)
947         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
948         return server.reset_slice(slice_cred, slice_hrn)
949
950
951     # =====================================================================
952     # GENI AM related calls
953     # =====================================================================
954
955     def GetVersion(self, opts, args):
956         server = self.geni_am
957         print server.GetVersion()
958
959     def ListResources(self, opts, args):
960         user_cred = self.get_user_cred().save_to_string(save_parents=True)
961         server = self.geni_am
962         call_options = {'geni_compressed': True}
963         xrn = None
964         cred = user_cred
965         if args:
966             xrn = args[0]
967             cred = self.get_slice_cred(xrn).save_to_string(save_parents=True)
968
969         if xrn:
970             call_options['geni_slice_urn'] = xrn
971             
972         rspec = server.ListResources([cred], call_options)
973         rspec = zlib.decompress(rspec.decode('base64'))
974         print rspec
975         
976     def CreateSliver(self, opts, args):
977         slice_xrn = args[0]
978         slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
979         rspec_file = self.get_rspec_file(args[1])
980         rspec = open(rspec_file).read()
981         server = self.geni_am
982         return server.CreateSliver(slice_xrn, [slice_cred], rspec, [])
983     
984     def DeleteSliver(self, opts, args):
985         slice_xrn = args[0]
986         slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
987         server = self.geni_am
988         return server.DeleteSliver(slice_xrn, [slice_cred])    
989
990     def SliverStatus(self, opts, args):
991         slice_xrn = args[0]
992         slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
993         server = self.geni_am
994         print server.SliverStatus(slice_xrn, [slice_cred])
995     
996     def RenewSliver(self, opts, args):
997         slice_xrn = args[0]
998         slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
999         time = args[1]
1000         server = self.geni_am
1001         return server.RenewSliver(slice_xrn, [slice_cred], time)   
1002
1003     def Shutdown(self, opts, args):
1004         slice_xrn = args[0]
1005         slice_cred = self.get_slice_cred(slice_xrn).save_to_string(save_parents=True)
1006         server = self.geni_am
1007         return server.Shutdown(slice_xrn, [slice_cred])         
1008     
1009     #
1010     # Main: parse arguments and dispatch to command
1011     #
1012     def main(self):
1013         parser = self.create_parser()
1014         (options, args) = parser.parse_args()
1015         self.options = options
1016    
1017         if options.hashrequest:
1018             self.hashrequest = True
1019  
1020         if len(args) <= 0:
1021             print "No command given. Use -h for help."
1022             return - 1
1023     
1024         command = args[0]
1025         (cmd_opts, cmd_args) = self.create_cmd_parser(command).parse_args(args[1:])
1026         if self.options.verbose :
1027             print "Registry %s, sm %s, dir %s, user %s, auth %s" % (options.registry, options.sm,
1028                                                                    options.sfi_dir, options.user,
1029                                                                    options.auth)
1030             print "Command %s" % command
1031             if command in ("resources"):
1032                 print "resources cmd_opts %s" % cmd_opts.format
1033             elif command in ("list", "show", "remove"):
1034                 print "cmd_opts.type %s" % cmd_opts.type
1035             print "cmd_args %s" % cmd_args
1036     
1037         self.set_servers()
1038     
1039         try:
1040             self.dispatch(command, cmd_opts, cmd_args)
1041         except KeyError:
1042             raise 
1043             print "Command not found:", command
1044             sys.exit(1)
1045     
1046         return
1047     
1048 if __name__ == "__main__":
1049    Sfi().main()