cache server version. Only pass call id to interfaces that support it
[sfa.git] / sfa / client / sfi.py
index 9d55258..4e77b24 100755 (executable)
@@ -10,12 +10,11 @@ import traceback
 import socket
 import random
 import datetime
+import zlib
 from lxml import etree
 from StringIO import StringIO
 from types import StringTypes, ListType
 from optparse import OptionParser
-import zlib
-
 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
@@ -26,6 +25,7 @@ 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
 from sfa.util.version import version_core
+from sfa.util.cache import Cache
 
 AGGREGATE_PORT=12346
 CM_PORT=12346
@@ -134,6 +134,7 @@ class Sfi:
         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.sfi_dir = options.sfi_dir
         self.options = options
         self.slicemgr = None
         self.registry = None
@@ -204,11 +205,15 @@ class Sfi:
                             default="all")
         # display formats
         if command in ("resources"):
-            parser.add_option("-r", "--rspec-version", dest="rspec_version", default="sfa 1",
+            parser.add_option("-r", "--rspec-version", dest="rspec_version", default="SFA 1",
                               help="schema type and version of resulting RSpec")
             parser.add_option("-f", "--format", dest="format", type="choice",
                              help="display format ([xml]|dns|ip)", default="xml",
                              choices=("xml", "dns", "ip"))
+            #panos: a new option to define the type of information about resources a user is interested in
+           parser.add_option("-i", "--info", dest="info",
+                                help="optional component information", default=None)
+
 
         if command in ("resources", "show", "list"):
            parser.add_option("-o", "--output", dest="file",
@@ -272,7 +277,7 @@ class Sfi:
 
         return parser
         
+
     def read_config(self):
        config_file = self.options.sfi_dir + os.sep + "sfi_config"
        try:
@@ -348,7 +353,48 @@ class Sfi:
        self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, self.options)
 
        return
-    
+
+    def get_cached_server_version(self, server):
+        # check local cache first
+        cache = None
+        version = None 
+        cache_file = self.sfi_dir + os.path.sep + 'sfi_cache.dat'
+        cache_key = server.url + "-version"
+        try:
+            cache = Cache(cache_file)
+        except IOError:
+            cache = Cache()
+            self.logger.info("Local cache not found at: %s" % cache_file)
+
+        if cache:
+            version = cache.get(cache_key)
+            
+        if not version: 
+            version = server.GetVersion()
+            # cache version for 24 hours
+            cache.add(cache_key, version, ttl= 60*60*24)
+
+
+        return version   
+        
+
+    def server_supports_call_id_arg(self, server):
+        """
+        Returns true if server support the optional call_id arg, false otherwise. 
+        """
+        server_version = self.get_cached_server_version(server)
+        if 'sfa' in server_version:
+            code_tag = server_version['code_tag']
+            code_tag_parts = code_tag.split("-")
+            
+            version_parts = code_tag_parts[0].split(".")
+            major, minor = version_parts[0], version_parts[1]
+            rev = code_tag_parts[1]
+            if int(major) > 1:
+                if int(minor) > 0 or int(rev) > 20:
+                    return True
+        return False                
+             
     #
     # Get various credential and spec files
     #
@@ -791,7 +837,6 @@ class Sfi:
     # ==================================================================
     # Slice-related commands
     # ==================================================================
-    
 
     def version(self, opts, args):
         if opts.version_local:
@@ -831,7 +876,7 @@ class Sfi:
         if args:
             cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
             hrn = args[0]
-            call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
+           call_options = {'geni_slice_urn': hrn_to_urn(hrn, 'slice')}
         else:
             cred = user_cred
             hrn = None
@@ -842,7 +887,14 @@ class Sfi:
             creds.append(delegated_cred)
         if opts.rspec_version:
             call_options['rspec_version'] = opts.rspec_version 
-        result = server.ListResources(creds, call_options,unique_call_id())
+        #panos add info options
+        if opts.info:
+            call_options['info'] = opts.info 
+
+        call_args = [creds, call_options]
+        if self.server_supports_call_id_arg(server):
+            call_args.append(unique_call_id())
+        result = server.ListResources(*call_args)
         format = opts.format
         if opts.file is None:
             display_rspec(result, format)
@@ -875,8 +927,9 @@ class Sfi:
         version = server.GetVersion()
         if 'sfa' not in version:
             # need to pass along user keys if this request is going to a ProtoGENI aggregate 
-            # ProtoGeni Aggregaes will only install the keys of the user that is issuing the
-            # request.  all slice keys
+            # ProtoGeni Aggregates will only install the keys of the user that is issuing the
+            # request. So we will only pass in one user that contains the keys for all
+            # users of the slice 
             user = {'urn': user_cred.get_gid_caller().get_urn(),
                     'keys': []}
             slice_record = self.registry.Resolve(slice_urn, creds)
@@ -887,8 +940,13 @@ class Sfi:
                 for user_record in user_records:
                     if 'keys' in user_record:
                         user['keys'].extend(user_record['keys'])
-            users.append(user)             
-        result =  server.CreateSliver(slice_urn, creds, rspec, users, unique_call_id())
+            users.append(user)
+
+        call_args = [slice_urn, creds, rspec, users]
+        if self.server_supports_call_id_arg(server):
+            call_args.append(unique_call_id())
+             
+        result =  server.CreateSliver(*call_args)
         print result
         return result
 
@@ -955,8 +1013,12 @@ class Sfi:
             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, unique_call_id())
-    
+
+        call_args = [slice_urn, creds]
+        if self.server_supports_call_id_arg(server):
+            call_args.append(unique_call_id())
+        return server.DeleteSliver(*call_args) 
+  
     # start named slice
     def start(self, opts, args):
         slice_hrn = args[0]
@@ -1003,7 +1065,11 @@ class Sfi:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
         time = args[1]
-        return server.RenewSliver(slice_urn, creds, time, unique_call_id())
+        
+        call_args = [slice_urn, creds, time]
+        if self.server_supports_call_id_arg(server):
+            call_args.append(unique_call_id())
+        return server.RenewSliver(*call_args)
 
 
     def status(self, opts, args):
@@ -1015,7 +1081,10 @@ class Sfi:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
         server = self.get_server_from_opts(opts)
-        print server.SliverStatus(slice_urn, creds, unique_call_id())
+        call_args = [slice_urn, creds]
+        if self.server_supports_call_id_arg(server):
+            call_args.append(unique_call_id())
+        print server.SliverStatus(*call_args)
 
 
     def shutdown(self, opts, args):