Merge branch 'master' of ssh://git.onelab.eu/git/sfa
[sfa.git] / sfa / client / sfi.py
index 83a66f9..29149bd 100755 (executable)
@@ -2,10 +2,13 @@
 
 # sfi -- slice-based facility interface
 
+# xxx NOTE this will soon be reviewed to take advantage of sfaclientlib
+
 import sys
 sys.path.append('.')
+
 import os, os.path
-import tempfile
+#import tempfile
 import socket
 import datetime
 import codecs
@@ -13,21 +16,27 @@ import pickle
 from lxml import etree
 from StringIO import StringIO
 from optparse import OptionParser
-from sfa.client.client_helper import pg_users_arg, sfa_users_arg
-from sfa.util.sfalogging import sfi_logger
+
 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.rspecs.rspec import RSpec
-from sfa.rspecs.rspec_converter import RSpecConverter
+from sfa.trust.sfaticket import SfaTicket
+
+from sfa.util.sfalogging import sfi_logger
 from sfa.util.xrn import 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
+
+from sfa.storage.record import SfaRecord, UserRecord, SliceRecord, NodeRecord, AuthorityRecord
+
+from sfa.rspecs.rspec import RSpec
+from sfa.rspecs.rspec_converter import RSpecConverter
 from sfa.rspecs.version_manager import VersionManager
+from sfa.client.return_value import ReturnValue
+
+import sfa.client.sfaprotocol as sfaprotocol
+from sfa.client.client_helper import pg_users_arg, sfa_users_arg
 
 AGGREGATE_PORT=12346
 CM_PORT=12346
@@ -158,6 +167,13 @@ class Sfi:
     
     required_options=['verbose',  'debug',  'registry',  'sm',  'auth',  'user']
 
+    @staticmethod
+    def default_sfi_dir ():
+        if os.path.isfile("./sfi_config"): 
+            return os.getcwd()
+        else:
+            return os.path.expanduser("~/.sfi/")
+
     # dummy to meet Sfi's expectations for its 'options' field
     # i.e. s/t we can do setattr on
     class DummyOptions:
@@ -167,10 +183,7 @@ class Sfi:
         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/")
-        # xxx oops, this is dangerous, sounds like ww sometimes have discrepency
-        # would be safer to remove self.sfi_dir altogether
-        self.sfi_dir = options.sfi_dir
+        if not hasattr(options,'sfi_dir'): options.sfi_dir=Sfi.default_sfi_dir()
         self.options = options
         self.slicemgr = None
         self.registry = None
@@ -180,14 +193,12 @@ class Sfi:
         self.logger = sfi_logger
         self.logger.enable_console()
    
-    def create_cmd_parser(self, command, additional_cmdargs=None):
+    def create_cmd_parser(self, command):
         cmdargs = {"list": "authority",
                   "show": "name",
                   "remove": "name",
                   "add": "record",
                   "update": "record",
-                  "aggregates": "[name]",
-                  "registries": "[name]",
                   "create_gid": "[name]",
                   "get_gid": [],  
                   "get_trusted_certs": "cred",
@@ -207,9 +218,6 @@ class Sfi:
                   "version": "",  
                  }
 
-        if additional_cmdargs:
-            cmdargs.update(additional_cmdargs)
-
         if command not in cmdargs:
             msg="Invalid command\n"
             msg+="Commands: "
@@ -232,9 +240,9 @@ class Sfi:
             parser.add_option("-d", "--delegate", dest="delegate", default=None, 
                              action="store_true",
                              help="Include a credential delegated to the user's root"+\
-                                  "authority in set of credentials for this call")  
-        
-        # registy filter option    
+                                  "authority in set of credentials for this call")
+
+        # registy filter option
         if command in ("list", "show", "remove"):
             parser.add_option("-t", "--type", dest="type", type="choice",
                             help="type filter ([all]|user|slice|authority|node|aggregate)",
@@ -304,10 +312,9 @@ class Sfi:
                          help="root registry", metavar="URL", default=None)
         parser.add_option("-s", "--slicemgr", dest="sm",
                          help="slice manager", metavar="URL", default=None)
-        default_sfi_dir = os.path.expanduser("~/.sfi/")
         parser.add_option("-d", "--dir", dest="sfi_dir",
-                         help="config & working directory - default is " + default_sfi_dir,
-                         metavar="PATH", default=default_sfi_dir)
+                         help="config & working directory - default is " + Sfi.default_sfi_dir(),
+                         metavar="PATH", default=Sfi.default_sfi_dir())
         parser.add_option("-u", "--user", dest="user",
                          help="user name", metavar="HRN", default=None)
         parser.add_option("-a", "--auth", dest="auth",
@@ -393,14 +400,13 @@ class Sfi:
        # Get key and certificate
        key_file = self.get_key_file()
        cert_file = self.get_cert_file(key_file)
-       self.key = Keypair(filename=key_file) 
        self.key_file = key_file
        self.cert_file = cert_file
        self.cert = GID(filename=cert_file)
        self.logger.info("Contacting Registry at: %s"%self.reg_url)
-       self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)  
+       self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)  
        self.logger.info("Contacting Slice Manager at: %s"%self.sm_url)
-       self.slicemgr = xmlrpcprotocol.get_server(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
+       self.slicemgr = sfaprotocol.server_proxy(self.sm_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
        return
 
     def get_cached_server_version(self, server):
@@ -417,19 +423,19 @@ class Sfi:
 
         if cache:
             version = cache.get(cache_key)
-            
+
         if not version: 
-            version = server.GetVersion()
+            result = server.GetVersion()
+            version= ReturnValue.get_value(result)
             # cache version for 24 hours
             cache.add(cache_key, version, ttl= 60*60*24)
             self.logger.info("Updating cache file %s" % cache_file)
             cache.save_to_file(cache_file)
 
-
         return version   
         
 
-    def server_supports_call_id_arg(self, server):
+    def server_supports_options_arg(self, server):
         """
         Returns true if server support the optional call_id arg, false otherwise. 
         """
@@ -441,11 +447,11 @@ class Sfi:
             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:
+            if int(major) >= 1:
+                if int(minor) >= 2:
                     return True
         return False                
-             
+        
     #
     # Get various credential and spec files
     #
@@ -492,8 +498,9 @@ class Sfi:
         try:
             self.logger.info("Getting Registry issued cert")
             self.read_config()
-            # *hack.  need to set registyr before _get_gid() is called 
-            self.registry = xmlrpcprotocol.get_server(self.reg_url, key_file, cert_file, timeout=self.options.timeout, verbose=self.options.debug)
+            # *hack.  need to set registry before _get_gid() is called 
+            self.registry = sfaprotocol.server_proxy(self.reg_url, key_file, cert_file, 
+                                                     timeout=self.options.timeout, verbose=self.options.debug)
             gid = self._get_gid(type='user')
             self.registry = None 
             self.logger.info("Writing certificate to %s"%cert_file)
@@ -521,7 +528,7 @@ class Sfi:
         if args:
             hrn = args[0]
         gid = self._get_gid(hrn)
-        self.logger.debug("Sfi.get_gid-> %s",gid.save_to_string(save_parents=True))
+        self.logger.debug("Sfi.get_gid-> %s" % gid.save_to_string(save_parents=True))
         return gid
 
     def _get_gid(self, hrn=None, type=None):
@@ -630,23 +637,8 @@ class Sfi:
           self.logger.critical("No such registry record file %s"%record)
           sys.exit(1)
     
-    def load_publickey_string(self, fn):
-       f = file(fn, "r")
-       key_string = f.read()
-    
-       # if the filename is a private key file, then extract the public key
-       if "PRIVATE KEY" in key_string:
-           outfn = tempfile.mktemp()
-           cmd = "openssl rsa -in " + fn + " -pubout -outform PEM -out " + outfn
-           os.system(cmd)
-           f = file(outfn, "r")
-           key_string = f.read()
-           os.remove(outfn)
-    
-       return key_string
-
     # xxx opts undefined
-    def get_component_server_from_hrn(self, hrn):
+    def get_component_proxy_from_hrn(self, hrn):
         # direct connection to the nodes component manager interface
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
         records = self.registry.Resolve(hrn, user_cred)
@@ -655,9 +647,9 @@ class Sfi:
             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)
+        return self.server_proxy(record['hostname'], CM_PORT, self.key_file, self.cert_file)
  
-    def get_server(self, host, port, keyfile, certfile):
+    def server_proxy(self, host, port, keyfile, certfile):
         """
         Return an instance of an xmlrpc server connection    
         """
@@ -666,10 +658,11 @@ class Sfi:
         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, timeout=self.options.timeout, verbose=self.options.debug)
+        return sfaprotocol.server_proxy(url, keyfile, certfile, timeout=self.options.timeout, 
+                                        verbose=self.options.debug)
 
     # xxx opts could be retrieved in self.options
-    def get_server_from_opts(self, opts):
+    def server_proxy_from_opts(self, opts):
         """
         Return instance of an xmlrpc connection to a slice manager, aggregate
         or component server depending on the specified opts
@@ -677,10 +670,10 @@ class Sfi:
         server = self.slicemgr
         # direct connection to an aggregate
         if hasattr(opts, 'aggregate') and opts.aggregate:
-            server = self.get_server(opts.aggregate, opts.port, self.key_file, self.cert_file)
+            server = self.server_proxy(opts.aggregate, opts.port, self.key_file, self.cert_file)
         # direct connection to the nodes component manager interface
         if hasattr(opts, 'component') and opts.component:
-            server = self.get_component_server_from_hrn(opts.component)    
+            server = self.get_component_proxy_from_hrn(opts.component)    
  
         return server
     #==========================================================================
@@ -689,9 +682,6 @@ class Sfi:
     # Registry-related commands
     #==========================================================================
   
-    def dispatch(self, command, cmd_opts, cmd_args):
-        return getattr(self, command)(cmd_opts, cmd_args)
-
     def create_gid(self, opts, args):
         if len(args) < 1:
             self.print_help()
@@ -702,7 +692,7 @@ class Sfi:
         if opts.file:
             filename = opts.file
         else:
-            filename = os.sep.join([self.sfi_dir, '%s.gid' % target_hrn])
+            filename = os.sep.join([self.options.sfi_dir, '%s.gid' % target_hrn])
         self.logger.info("writing %s gid to %s" % (target_hrn, filename))
         GID(string=gid).save_to_file(filename)
          
@@ -738,7 +728,7 @@ class Sfi:
         records = self.registry.Resolve(hrn, user_cred)
         records = filter_records(opts.type, records)
         if not records:
-            print "No record of type", opts.type
+            self.logger.error("No record of type %s"% opts.type)
         for record in records:
             if record['type'] in ['user']:
                 record = UserRecord(dict=record)
@@ -846,7 +836,7 @@ class Sfi:
         elif record['type'] in ["slice"]:
             try:
                 cred = self.get_slice_cred(record.get_name()).save_to_string(save_parents=True)
-            except xmlrpcprotocol.ServerException, e:
+            except sfaprotocol.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]:
@@ -874,32 +864,6 @@ class Sfi:
             self.logger.debug('Sfi.get_trusted_certs -> %r'%cert.get_subject())
         return 
 
-    def aggregates(self, opts, args):
-        """
-        return a list of details about known aggregates
-        """
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        hrn = None
-        if args:
-            hrn = args[0]
-
-        result = self.registry.get_aggregates(user_cred, hrn)
-        display_list(result)
-        return 
-
-    def registries(self, opts, args):
-        """
-        return a list of details about known registries
-        """
-        user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        hrn = None
-        if args:
-            hrn = args[0]
-        result = self.registry.get_registries(user_cred, hrn)
-        display_list(result)
-        return
-
     # ==================================================================
     # Slice-related commands
     # ==================================================================
@@ -911,8 +875,9 @@ class Sfi:
             if opts.version_registry:
                 server=self.registry
             else:
-                server = self.get_server_from_opts(opts)
-            version=server.GetVersion()
+                server = self.server_proxy_from_opts(opts)
+            result = server.GetVersion()
+            version = ReturnValue.get_value(result)
         for (k,v) in version.iteritems():
             print "%-20s: %s"%(k,v)
         if opts.file:
@@ -928,26 +893,32 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(user_cred, get_authority(self.authority))
             creds.append(delegated_cred)  
-        server = self.get_server_from_opts(opts)
-        #results = server.ListSlices(creds, unique_call_id())
-        results = server.ListSlices(creds)
-        display_list(results)
+        server = self.server_proxy_from_opts(opts)
+        call_args = [creds]
+        if self.server_supports_options_arg(server):
+            options = {'call_id': unique_call_id()}
+            call_args.append(options)
+        result = server.ListSlices(*call_args)
+        value = ReturnValue.get_value(result)
+        display_list(value)
         return
     
     # show rspec for named slice
     def resources(self, opts, args):
         user_cred = self.get_user_cred().save_to_string(save_parents=True)
-        server = self.slicemgr
-        call_options = {}
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
+   
+        options = {'call_id': unique_call_id()}
+        #panos add info options
+        if opts.info:
+            options['info'] = opts.info
         
         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')}
+            options['geni_slice_urn'] = hrn_to_urn(hrn, 'slice')
         else:
             cred = user_cred
-            hrn = None
      
         creds = [cred]
         if opts.delegate:
@@ -958,37 +929,41 @@ class Sfi:
             server_version = self.get_cached_server_version(server)
             if 'sfa' in server_version:
                 # just request the version the client wants 
-                call_options['rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
+                options['geni_rspec_version'] = version_manager.get_version(opts.rspec_version).to_dict()
             else:
                 # this must be a protogeni aggregate. We should request a v2 ad rspec
                 # regardless of what the client user requested 
-                call_options['rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()     
-        #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())
+                options['geni_rspec_version'] = version_manager.get_version('ProtoGENI 2').to_dict()     
+        else:
+            options['geni_rspec_version'] = {'type': 'geni', 'version': '3.0'}
+        call_args = [creds, options]
         result = server.ListResources(*call_args)
+        value = ReturnValue.get_value(result)
         if opts.file is None:
-            display_rspec(result, opts.format)
+            display_rspec(value, opts.format)
         else:
-            save_rspec_to_file(result, opts.file)
+            save_rspec_to_file(value, opts.file)
         return
 
     # created named slice with given rspec
     def create(self, opts, args):
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         server_version = self.get_cached_server_version(server)
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice')
         user_cred = self.get_user_cred()
         slice_cred = self.get_slice_cred(slice_hrn).save_to_string(save_parents=True)
-        # delegate the cred to the callers root authority
-        delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority)+'.slicemanager')
-        #delegated_cred = self.delegate_cred(slice_cred, get_authority(slice_hrn))
-        #creds.append(delegated_cred)
+        delegated_cred = None
+        if server_version.get('interface') == 'slicemgr':
+            # delegate our cred to the slice manager
+            # do not delegate cred to slicemgr...not working at the moment
+            pass
+            #if server_version.get('hrn'):
+            #    delegated_cred = self.delegate_cred(slice_cred, server_version['hrn'])
+            #elif server_version.get('urn'):
+            #    delegated_cred = self.delegate_cred(slice_cred, urn_to_hrn(server_version['urn']))
+                 
         rspec_file = self.get_rspec_file(args[1])
         rspec = open(rspec_file).read()
 
@@ -1013,17 +988,20 @@ class Sfi:
                 creds = [slice_cred]
             else:
                 users = sfa_users_arg(user_records, slice_record)
-                creds = [slice_cred, delegated_cred]
+                creds = [slice_cred]
+                if delegated_cred:
+                    creds.append(delegated_cred)
         call_args = [slice_urn, creds, rspec, users]
-        if self.server_supports_call_id_arg(server):
-            call_args.append(unique_call_id())
-           
+        if self.server_supports_options_arg(server):
+            options = {'call_id': unique_call_id()}
+            call_args.append(options)
         result = server.CreateSliver(*call_args)
+        value = ReturnValue.get_value(result)
         if opts.file is None:
-            print result
+            print value
         else:
-            save_rspec_to_file (result, opts.file)
-        return result
+            save_rspec_to_file (value, opts.file)
+        return value
 
     # get a ticket for the specified slice
     def get_ticket(self, opts, args):
@@ -1037,7 +1015,7 @@ class Sfi:
             creds.append(delegated_cred)
         rspec_file = self.get_rspec_file(rspec_path) 
         rspec = open(rspec_file).read()
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         ticket_string = server.GetTicket(slice_urn, creds, rspec, [])
         file = os.path.join(self.options.sfi_dir, get_leaf(slice_hrn) + ".ticket")
         self.logger.info("writing ticket to %s"%file)
@@ -1068,7 +1046,7 @@ class Sfi:
         for hostname in hostnames:
             try:
                 self.logger.info("Calling redeem_ticket at %(hostname)s " % locals())
-                server = self.get_server(hostname, CM_PORT, self.key_file, \
+                server = self.server_proxy(hostname, CM_PORT, self.key_file, \
                                          self.cert_file, self.options.debug)
                 server.RedeemTicket(ticket.save_to_string(save_parents=True), slice_cred)
                 self.logger.info("Success")
@@ -1087,11 +1065,11 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.get_server_from_opts(opts)
-
+        server = self.server_proxy_from_opts(opts)
         call_args = [slice_urn, creds]
-        if self.server_supports_call_id_arg(server):
-            call_args.append(unique_call_id())
+        if self.server_supports_options_arg(server):
+            options = {'call_id': unique_call_id()}
+            call_args.append(options)
         return server.DeleteSliver(*call_args) 
   
     # start named slice
@@ -1103,7 +1081,7 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         return server.Start(slice_urn, creds)
     
     # stop named slice
@@ -1115,14 +1093,14 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         return server.Stop(slice_urn, creds)
     
     # reset named slice
     def reset(self, opts, args):
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
         creds = [slice_cred]
         if opts.delegate:
@@ -1133,7 +1111,7 @@ class Sfi:
     def renew(self, opts, args):
         slice_hrn = args[0]
         slice_urn = hrn_to_urn(slice_hrn, 'slice') 
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         slice_cred = self.get_slice_cred(args[0]).save_to_string(save_parents=True)
         creds = [slice_cred]
         if opts.delegate:
@@ -1142,9 +1120,12 @@ class Sfi:
         time = args[1]
         
         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)
+        if self.server_supports_options_arg(server):
+            options = {'call_id': unique_call_id()}
+            call_args.append(options)
+        result =  server.RenewSliver(*call_args)
+        value = ReturnValue.get_value(result)
+        return value
 
 
     def status(self, opts, args):
@@ -1155,14 +1136,16 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         call_args = [slice_urn, creds]
-        if self.server_supports_call_id_arg(server):
-            call_args.append(unique_call_id())
+        if self.server_supports_options_arg(server):
+            options = {'call_id': unique_call_id()}
+            call_args.append(options)
         result = server.SliverStatus(*call_args)
-        print result
+        value = ReturnValue.get_value(result)
+        print value
         if opts.file:
-            save_variable_to_file(result, opts.file, opts.fileformat)
+            save_variable_to_file(value, opts.file, opts.fileformat)
 
 
     def shutdown(self, opts, args):
@@ -1173,7 +1156,7 @@ class Sfi:
         if opts.delegate:
             delegated_cred = self.delegate_cred(slice_cred, get_authority(self.authority))
             creds.append(delegated_cred)
-        server = self.get_server_from_opts(opts)
+        server = self.server_proxy_from_opts(opts)
         return server.Shutdown(slice_urn, creds)         
     
     def print_help (self):
@@ -1183,6 +1166,9 @@ class Sfi:
     #
     # Main: parse arguments and dispatch to command
     #
+    def dispatch(self, command, cmd_opts, cmd_args):
+        return getattr(self, command)(cmd_opts, cmd_args)
+
     def main(self):
         self.sfi_parser = self.create_parser()
         (options, args) = self.sfi_parser.parse_args()