DeleteSliver supports call_id
[sfa.git] / sfa / client / sfi.py
index e3f65c9..5974a03 100755 (executable)
@@ -18,14 +18,14 @@ import zlib
 
 from sfa.util.sfalogging import sfa_logger,sfa_logger_goes_to_console
 from sfa.trust.certificate import Keypair, Certificate
 
 from sfa.util.sfalogging import sfa_logger,sfa_logger_goes_to_console
 from sfa.trust.certificate import Keypair, Certificate
+from sfa.trust.gid import GID
 from sfa.trust.credential import Credential
 from sfa.util.sfaticket import SfaTicket
 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
 from sfa.trust.credential import Credential
 from sfa.util.sfaticket import SfaTicket
 from sfa.util.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
-from sfa.util.namespace import get_leaf, get_authority, hrn_to_urn
-from sfa.util.xmlrpcprotocol import ServerException
+from sfa.util.xrn import Xrn, get_leaf, get_authority, hrn_to_urn
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
 from sfa.util.config import Config
 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
 from sfa.util.config import Config
-
+from sfa.util.version import version_core
 
 AGGREGATE_PORT=12346
 CM_PORT=12346
 
 AGGREGATE_PORT=12346
 CM_PORT=12346
@@ -117,21 +117,34 @@ def load_record_from_file(filename):
     return record
 
 
     return record
 
 
+import uuid
+def unique_call_id(): return uuid.uuid4().urn
 
 class Sfi:
 
 class Sfi:
+    
+    required_options=['verbose',  'debug',  'registry',  'sm',  'auth',  'user']
+
+    # dummy to meet Sfi's expectations for its 'options' field
+    # i.e. s/t we can do setattr on
+    class DummyOptions:
+        pass
 
 
-    def __init__ (self):
+    def __init__ (self,options=None):
+        if options is None: options=Sfi.DummyOptions()
+        for opt in Sfi.required_options:
+            if not hasattr(options,opt): setattr(options,opt,None)
+        if not hasattr(options,'sfi_dir'): options.sfi_dir=os.path.expanduser("~/.sfi/")
+        self.options = options
         self.slicemgr = None
         self.registry = None
         self.user = None
         self.authority = None
         self.slicemgr = None
         self.registry = None
         self.user = None
         self.authority = None
-        self.options = None
         self.hashrequest = False
         sfa_logger_goes_to_console()
         self.logger=sfa_logger()
    
     def create_cmd_parser(self, command, additional_cmdargs=None):
         self.hashrequest = False
         sfa_logger_goes_to_console()
         self.logger=sfa_logger()
    
     def create_cmd_parser(self, command, additional_cmdargs=None):
-        cmdargs = {"list": "name",
+        cmdargs = {"list": "authority",
                   "show": "name",
                   "remove": "name",
                   "add": "record",
                   "show": "name",
                   "remove": "name",
                   "add": "record",
@@ -211,6 +224,14 @@ class Sfi:
            parser.add_option("-s", "--slice", dest="delegate_slice",
                             help="delegate slice credential", metavar="HRN", default=None)
         
            parser.add_option("-s", "--slice", dest="delegate_slice",
                             help="delegate slice credential", metavar="HRN", default=None)
         
+        if command in ("version"):
+            parser.add_option("-R","--registry-version",
+                              action="store_true", dest="version_registry", default=False,
+                              help="probe registry version instead of slicemgr")
+            parser.add_option("-l","--local",
+                              action="store_true", dest="version_local", default=False,
+                              help="display version of the local client")
+
         return parser
 
         
         return parser
 
         
@@ -246,10 +267,7 @@ class Sfi:
         return parser
         
  
         return parser
         
  
-    #
-    # Establish Connection to SliceMgr and Registry Servers
-    #
-    def set_servers(self):
+    def read_config(self):
        config_file = self.options.sfi_dir + os.sep + "sfi_config"
        try:
           config = Config (config_file)
        config_file = self.options.sfi_dir + os.sep + "sfi_config"
        try:
           config = Config (config_file)
@@ -265,18 +283,18 @@ class Sfi:
        errors = 0
        # Set SliceMgr URL
        if (self.options.sm is not None):
        errors = 0
        # Set SliceMgr URL
        if (self.options.sm is not None):
-          sm_url = self.options.sm
+          self.sm_url = self.options.sm
        elif hasattr(config, "SFI_SM"):
        elif hasattr(config, "SFI_SM"):
-          sm_url = config.SFI_SM
+          self.sm_url = config.SFI_SM
        else:
           self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
           errors += 1 
     
        # Set Registry URL
        if (self.options.registry is not None):
        else:
           self.logger.error("You need to set e.g. SFI_SM='http://your.slicemanager.url:12347/' in %s" % config_file)
           errors += 1 
     
        # Set Registry URL
        if (self.options.registry is not None):
-          reg_url = self.options.registry
+          self.reg_url = self.options.registry
        elif hasattr(config, "SFI_REGISTRY"):
        elif hasattr(config, "SFI_REGISTRY"):
-          reg_url = config.SFI_REGISTRY
+          self.reg_url = config.SFI_REGISTRY
        else:
           self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
           errors += 1 
        else:
           self.logger.errors("You need to set e.g. SFI_REGISTRY='http://your.registry.url:12345/' in %s" % config_file)
           errors += 1 
@@ -302,8 +320,14 @@ class Sfi:
     
        if errors:
           sys.exit(1)
     
        if errors:
           sys.exit(1)
-    
-    
+
+
+    #
+    # Establish Connection to SliceMgr and Registry Servers
+    #
+    def set_servers(self):
+
+       self.read_config() 
        # Get key and certificate
        key_file = self.get_key_file()
        cert_file = self.get_cert_file(key_file)
        # Get key and certificate
        key_file = self.get_key_file()
        cert_file = self.get_cert_file(key_file)
@@ -312,10 +336,10 @@ class Sfi:
        self.cert_file = cert_file
        self.cert = Certificate(filename=cert_file) 
        # Establish connection to server(s)
        self.cert_file = cert_file
        self.cert = Certificate(filename=cert_file) 
        # Establish connection to server(s)
-       self.logger.info("Contacting Registry at: %s"%reg_url)
-       self.registry = xmlrpcprotocol.get_server(reg_url, key_file, cert_file, self.options)  
-       self.logger.info("Contacting Slice Manager at: %s"%sm_url)
-       self.slicemgr = xmlrpcprotocol.get_server(sm_url, key_file, cert_file, self.options)
+       self.logger.info("Contacting Registry at: %s"%self.reg_url)
+       self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, self.options)  
+       self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
+       self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, self.options)
 
        return
     
 
        return
     
@@ -335,7 +359,6 @@ class Sfi:
     
     def get_key_file(self):
        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
     
     def get_key_file(self):
        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".pkey")
-       #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".pkey")
        if (os.path.isfile(file)):
           return file
        else:
        if (os.path.isfile(file)):
           return file
        else:
@@ -345,19 +368,27 @@ class Sfi:
     
     def get_cert_file(self, key_file):
     
     
     def get_cert_file(self, key_file):
     
-       #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".cert")
-       file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
-       if (os.path.isfile(file)):
-          return file
-       else:
-          k = Keypair(filename=key_file)
-          cert = Certificate(subject=self.user)
-          cert.set_pubkey(k)
-          cert.set_issuer(k, self.user)
-          cert.sign()
-          self.logger.info("Writing self-signed certificate to %s"%file)
-          cert.save_to_file(file)
-          return file
+        file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cert")
+        if (os.path.isfile(file)):
+            # use existing cert if it exists                     
+            return file
+        else:
+            try:
+                # attempt to use gid as the cert.  
+                gid = self._get_gid()
+                self.logger.info("Writing certificate to %s"%file)
+                gid.save_to_file(file) 
+            except:
+                # generate self signed certificate
+                k = Keypair(filename=key_file)
+                cert = Certificate(subject=self.user)
+                cert.set_pubkey(k)
+                cert.set_issuer(k, self.user)
+                cert.sign()
+                self.logger.info("Writing self-signed certificate to %s"%file)
+                cert.save_to_file(file)
+            
+            return file
 
     def get_cached_gid(self, file):
         """
 
     def get_cached_gid(self, file):
         """
@@ -368,7 +399,11 @@ class Sfi:
             gid = GID(filename=file)
         return gid
 
             gid = GID(filename=file)
         return gid
 
+    # xxx opts unused
     def get_gid(self, opts, args):
     def get_gid(self, opts, args):
+        """
+        Get the specify gid and save it to file
+        """
         hrn = None
         if args:
             hrn = args[0]
         hrn = None
         if args:
             hrn = args[0]
@@ -377,6 +412,10 @@ class Sfi:
         return gid
 
     def _get_gid(self, hrn=None):
         return gid
 
     def _get_gid(self, hrn=None):
+        """
+        git_gid helper. Retrive the gid from the registry and save it to file.
+        """
+        
         if not hrn:
             hrn = self.user
  
         if not hrn:
             hrn = self.user
  
@@ -400,13 +439,12 @@ class Sfi:
         if (os.path.isfile(file)):
             credential = Credential(filename=file)
             # make sure it isnt expired 
         if (os.path.isfile(file)):
             credential = Credential(filename=file)
             # make sure it isnt expired 
-            if not credential.get_lifetime or \
-               datetime.datetime.today() < credential.get_lifetime():
+            if not credential.get_expiration or \
+               datetime.datetime.today() < credential.get_expiration():
                 return credential
         return None 
  
     def get_user_cred(self):
                 return credential
         return None 
  
     def get_user_cred(self):
-        #file = os.path.join(self.options.sfi_dir, get_leaf(self.user) + ".cred")
         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
         return self.get_cred(file, 'user', self.user)
 
         file = os.path.join(self.options.sfi_dir, self.user.replace(self.authority + '.', '') + ".cred")
         return self.get_cred(file, 'user', self.user)
 
@@ -414,7 +452,7 @@ class Sfi:
         if not self.authority:
             self.logger.critical("no authority specified. Use -a or set SF_AUTH")
             sys.exit(-1)
         if not self.authority:
             self.logger.critical("no authority specified. Use -a or set SF_AUTH")
             sys.exit(-1)
-        file = os.path.join(self.options.sfi_dir, get_leaf("authority") + ".cred")
+        file = os.path.join(self.options.sfi_dir, self.authority + ".cred")
         return self.get_cred(file, 'authority', self.authority)
 
     def get_slice_cred(self, name):
         return self.get_cred(file, 'authority', self.authority)
 
     def get_slice_cred(self, name):
@@ -485,6 +523,7 @@ class Sfi:
     
        return key_string
 
     
        return key_string
 
+    # xxx opts undefined
     def get_component_server_from_hrn(self, hrn):
         # direct connection to the nodes component manager interface
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
     def get_component_server_from_hrn(self, hrn):
         # direct connection to the nodes component manager interface
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
@@ -494,16 +533,20 @@ class Sfi:
             self.logger.warning("No such component:%r"% opts.component)
         record = records[0]
   
             self.logger.warning("No such component:%r"% opts.component)
         record = records[0]
   
-        return self.get_server(record['hostname'], CM_PORT, self.key_file, \
-                               self.cert_file, self.options)
+        return self.get_server(record['hostname'], CM_PORT, self.key_file, self.cert_file)
  
     def get_server(self, host, port, keyfile, certfile):
         """
  
     def get_server(self, host, port, keyfile, certfile):
         """
-        Return an instnace of an xmlrpc server connection    
+        Return an instance of an xmlrpc server connection    
         """
         """
-        url = "http://%s:%s" % (host, port)
+        # port is appended onto the domain, before the path. Should look like:
+        # http://domain:port/path
+        host_parts = host.split('/')
+        host_parts[0] = host_parts[0] + ":" + str(port)
+        url =  "http://%s" %  "/".join(host_parts)    
         return xmlrpcprotocol.get_server(url, keyfile, certfile, self.options)
 
         return xmlrpcprotocol.get_server(url, keyfile, certfile, self.options)
 
+    # xxx opts could be retrieved in self.options
     def get_server_from_opts(self, opts):
         """
         Return instance of an xmlrpc connection to a slice manager, aggregate
     def get_server_from_opts(self, opts):
         """
         Return instance of an xmlrpc connection to a slice manager, aggregate
@@ -529,8 +572,11 @@ class Sfi:
  
     # list entires in named authority registry
     def list(self, opts, args):
  
     # list entires in named authority registry
     def list(self, opts, args):
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
+        if len(args)!= 1:
+            self.print_help()
+            sys.exit(1)
         hrn = args[0]
         hrn = args[0]
+        user_cred = self.get_user_cred().save_to_string(save_parents=True)
         try:
             list = self.registry.List(hrn, user_cred)
         except IndexError:
         try:
             list = self.registry.List(hrn, user_cred)
         except IndexError:
@@ -550,8 +596,11 @@ class Sfi:
     
     # show named registry record
     def show(self, opts, args):
     
     # show named registry record
     def show(self, opts, args):
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
+        if len(args)!= 1:
+            self.print_help()
+            sys.exit(1)
         hrn = args[0]
         hrn = args[0]
+        user_cred = self.get_user_cred().save_to_string(save_parents=True)
         records = self.registry.Resolve(hrn, user_cred)
         records = filter_records(opts.type, records)
         if not records:
         records = self.registry.Resolve(hrn, user_cred)
         records = filter_records(opts.type, records)
         if not records:
@@ -631,6 +680,9 @@ class Sfi:
     #   - have to first retrieve the record to be removed
     def remove(self, opts, args):
         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
     #   - have to first retrieve the record to be removed
     def remove(self, opts, args):
         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
+        if len(args)!=1:
+            self.print_help()
+            sys.exit(1)
         hrn = args[0]
         type = opts.type 
         if type in ['all']:
         hrn = args[0]
         type = opts.type 
         if type in ['all']:
@@ -640,6 +692,9 @@ class Sfi:
     # add named registry record
     def add(self, opts, args):
         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
     # add named registry record
     def add(self, opts, args):
         auth_cred = self.get_auth_cred().save_to_string(save_parents=True)
+        if len(args)!=1:
+            self.print_help()
+            sys.exit(1)
         record_filepath = args[0]
         rec_file = self.get_record_file(record_filepath)
         record = load_record_from_file(rec_file).as_dict()
         record_filepath = args[0]
         rec_file = self.get_record_file(record_filepath)
         record = load_record_from_file(rec_file).as_dict()
@@ -648,6 +703,9 @@ class Sfi:
     # update named registry entry
     def update(self, opts, args):
         user_cred = self.get_user_cred()
     # update named registry entry
     def update(self, opts, args):
         user_cred = self.get_user_cred()
+        if len(args)!=1:
+            self.print_help()
+            sys.exit(1)
         rec_file = self.get_record_file(args[0])
         record = load_record_from_file(rec_file)
         if record['type'] == "user":
         rec_file = self.get_record_file(args[0])
         record = load_record_from_file(rec_file)
         if record['type'] == "user":
@@ -658,7 +716,7 @@ class Sfi:
         elif record['type'] in ["slice"]:
             try:
                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
         elif record['type'] in ["slice"]:
             try:
                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
-            except ServerException, e:
+            except xmlrpcprotocol.ServerException, e:
                # XXX smbaker -- once we have better error return codes, update this
                # to do something better than a string compare
                if "Permission error" in e.args[0]:
                # XXX smbaker -- once we have better error return codes, update this
                # to do something better than a string compare
                if "Permission error" in e.args[0]:
@@ -716,9 +774,16 @@ class Sfi:
     
 
     def version(self, opts, args):
     
 
     def version(self, opts, args):
-        server = self.get_server_from_opts(opts)
-        
-        print server.GetVersion()
+        if opts.version_local:
+            version=version_core()
+        else:
+            if opts.version_registry:
+                server=self.registry
+            else:
+                server = self.get_server_from_opts(opts)
+            version=server.GetVersion()
+        for (k,v) in version.iteritems():
+            print "%-20s: %s"%(k,v)
 
     # list instantiated slices
     def slices(self, opts, args):
 
     # list instantiated slices
     def slices(self, opts, args):
@@ -754,10 +819,11 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
             creds.append(delegated_cred) 
         if opts.delegate:
             delegated_cred = self.delegate_cred(cred, get_authority(self.authority))
             creds.append(delegated_cred) 
-        result = server.ListResources(creds, call_options)
+        result = server.ListResources(creds, call_options,unique_call_id())
         format = opts.format
         format = opts.format
-        display_rspec(result, format)
-        if (opts.file is not None):
+        if opts.file is None:
+            display_rspec(result, format)
+        else:
             file = opts.file
             if not file.startswith(os.sep):
                 file = os.path.join(self.options.sfi_dir, file)
             file = opts.file
             if not file.startswith(os.sep):
                 file = os.path.join(self.options.sfi_dir, file)
@@ -777,7 +843,7 @@ class Sfi:
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
         server = self.get_server_from_opts(opts)
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
         server = self.get_server_from_opts(opts)
-        result =  server.CreateSliver(slice_urn, creds, rspec, [])
+        result =  server.CreateSliver(slice_urn, creds, rspec, [], unique_call_id())
         print result
         return result
 
         print result
         return result
 
@@ -844,7 +910,7 @@ class Sfi:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
         server = self.get_server_from_opts(opts)
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
         server = self.get_server_from_opts(opts)
-        return server.DeleteSliver(slice_urn, creds)
+        return server.DeleteSliver(slice_urn, creds, unique_call_id())
     
     # start named slice
     def start(self, opts, args):
     
     # start named slice
     def start(self, opts, args):
@@ -918,13 +984,16 @@ class Sfi:
         server = self.get_server_from_opts(opts)
         return server.Shutdown(slice_urn, creds)         
     
         server = self.get_server_from_opts(opts)
         return server.Shutdown(slice_urn, creds)         
     
+    def print_help (self):
+        self.sfi_parser.print_help()
+        self.cmd_parser.print_help()
 
     #
     # Main: parse arguments and dispatch to command
     #
     def main(self):
 
     #
     # Main: parse arguments and dispatch to command
     #
     def main(self):
-        parser = self.create_parser()
-        (options, args) = parser.parse_args()
+        self.sfi_parser = self.create_parser()
+        (options, args) = self.sfi_parser.parse_args()
         self.options = options
 
         self.logger.setLevelFromOptVerbose(self.options.verbose)
         self.options = options
 
         self.logger.setLevelFromOptVerbose(self.options.verbose)
@@ -936,7 +1005,8 @@ class Sfi:
             return -1
     
         command = args[0]
             return -1
     
         command = args[0]
-        (cmd_opts, cmd_args) = self.create_cmd_parser(command).parse_args(args[1:])
+        self.cmd_parser = self.create_cmd_parser(command)
+        (cmd_opts, cmd_args) = self.cmd_parser.parse_args(args[1:])
 
         self.set_servers()
     
 
         self.set_servers()