From ee8a376da1107884bee1ea29248a70e4da8410c9 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Thu, 27 Oct 2011 15:00:25 +0200 Subject: [PATCH] split the various *Api classes into somethin more sensible move the core classes in server/ --- sfa/managers/aggregate_manager.py | 4 +- sfa/managers/aggregate_manager_eucalyptus.py | 6 +- sfa/managers/aggregate_manager_max.py | 2 +- sfa/managers/import_manager.py | 26 -- sfa/managers/managerwrapper.py | 49 +++ sfa/methods/Resolve.py | 2 - sfa/plc/{api.py => plcsfaapi.py} | 296 ++---------------- sfa/server/componentserver.py | 4 +- sfa/server/interface.py | 4 + sfa/server/modpython/SfaAggregateModPython.py | 4 +- sfa/server/modpython/SfaRegistryModPython.py | 4 +- sfa/server/modpython/SfaSliceMgrModPython.py | 4 +- sfa/server/sfa-clean-peer-records.py | 4 +- sfa/server/sfa-start.py | 8 +- sfa/server/sfa_component_setup.py | 4 +- sfa/server/sfaapi.py | 212 +++++++++++++ sfa/server/sfaserver.py | 2 - sfa/server/threadedserver.py | 4 +- sfa/{util/api.py => server/xmlrpcapi.py} | 123 ++------ sfa/util/xmlrpcprotocol.py | 3 +- 20 files changed, 331 insertions(+), 434 deletions(-) delete mode 100644 sfa/managers/import_manager.py create mode 100644 sfa/managers/managerwrapper.py rename sfa/plc/{api.py => plcsfaapi.py} (65%) create mode 100644 sfa/server/sfaapi.py rename sfa/{util/api.py => server/xmlrpcapi.py} (57%) diff --git a/sfa/managers/aggregate_manager.py b/sfa/managers/aggregate_manager.py index 46362ddc..2e56a490 100644 --- a/sfa/managers/aggregate_manager.py +++ b/sfa/managers/aggregate_manager.py @@ -15,7 +15,7 @@ from sfa.rspecs.version_manager import VersionManager from sfa.rspecs.rspec import RSpec import sfa.plc.peers as peers -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.plc.aggregate import Aggregate from sfa.plc.slices import Slices @@ -396,7 +396,7 @@ def main(): #rspec = ListResources(api, "plc.pl.sirius", None, 'pl_test_sirius') print rspec """ - api = SfaAPI() + api = PlcSfaApi() f = open(sys.argv[1]) xml = f.read() f.close() diff --git a/sfa/managers/aggregate_manager_eucalyptus.py b/sfa/managers/aggregate_manager_eucalyptus.py index 6c7c1f4c..24cc0db3 100644 --- a/sfa/managers/aggregate_manager_eucalyptus.py +++ b/sfa/managers/aggregate_manager_eucalyptus.py @@ -17,7 +17,7 @@ from sfa.util.faults import * from sfa.util.xrn import urn_to_hrn, Xrn from sfa.server.registry import Registries from sfa.trust.credential import Credential -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.plc.aggregate import Aggregate from sfa.plc.slices import * from sfa.util.plxrn import hrn_to_pl_slicename, slicename_to_hrn @@ -41,7 +41,7 @@ cloud = {} # EUCALYPTUS_RSPEC_SCHEMA='/etc/sfa/eucalyptus.rng' -api = SfaAPI() +api = PlcSfaApi() ## # Meta data of an instance. @@ -735,7 +735,7 @@ def main(): server_key_file = '/var/lib/sfa/authorities/server.key' server_cert_file = '/var/lib/sfa/authorities/server.cert' - api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file, interface='aggregate') + api = PlcSfaApi(key_file = server_key_file, cert_file = server_cert_file, interface='aggregate') print getKeysForSlice(api, 'gc.gc.test1') if __name__ == "__main__": diff --git a/sfa/managers/aggregate_manager_max.py b/sfa/managers/aggregate_manager_max.py index d7d37761..ff6ea73b 100644 --- a/sfa/managers/aggregate_manager_max.py +++ b/sfa/managers/aggregate_manager_max.py @@ -259,6 +259,6 @@ return the basic information needed in a dict. def fetch_context(slice_hrn, user_hrn, contexts): base_context = {'sfa':{'user':{'hrn':user_hrn}}} return base_context - api = SfaAPI() + api = PlcSfaApi() create_slice(api, "plc.maxpl.test000", None, rspec_xml, None) diff --git a/sfa/managers/import_manager.py b/sfa/managers/import_manager.py deleted file mode 100644 index f5f30c44..00000000 --- a/sfa/managers/import_manager.py +++ /dev/null @@ -1,26 +0,0 @@ -from sfa.util.sfalogging import logger - -def import_manager(kind, type): - """ - kind expected in ['registry', 'aggregate', 'slice', 'component'] - type is e.g. 'pl' or 'max' or whatever - """ - basepath = 'sfa.managers' - qualified = "%s.%s_manager_%s"%(basepath,kind,type) - generic = "%s.%s_manager"%(basepath,kind) - - message="import_manager for kind=%s and type=%s"%(kind,type) - try: - manager = __import__(qualified, fromlist=[basepath]) - logger.info ("%s: loaded %s"%(message,qualified)) - except: - try: - manager = __import__ (generic, fromlist=[basepath]) - if type != 'pl' : - logger.warn ("%s: using generic with type!='pl'"%(message)) - logger.info("%s: loaded %s"%(message,generic)) - except: - manager=None - logger.log_exc("%s: unable to import either %s or %s"%(message,qualified,generic)) - return manager - diff --git a/sfa/managers/managerwrapper.py b/sfa/managers/managerwrapper.py new file mode 100644 index 00000000..218cd4a4 --- /dev/null +++ b/sfa/managers/managerwrapper.py @@ -0,0 +1,49 @@ +from sfa.util.faults import SfaNotImplemented +from sfa.util.sfalogging import logger + +## locate the right manager +def import_manager(kind, type): + """ + kind expected in ['registry', 'aggregate', 'slice', 'component'] + type is e.g. 'pl' or 'max' or whatever + """ + basepath = 'sfa.managers' + qualified = "%s.%s_manager_%s"%(basepath,kind,type) + generic = "%s.%s_manager"%(basepath,kind) + + message="import_manager for kind=%s and type=%s"%(kind,type) + try: + manager = __import__(qualified, fromlist=[basepath]) + logger.info ("%s: loaded %s"%(message,qualified)) + except: + try: + manager = __import__ (generic, fromlist=[basepath]) + if type != 'pl' : + logger.warn ("%s: using generic with type!='pl'"%(message)) + logger.info("%s: loaded %s"%(message,generic)) + except: + manager=None + logger.log_exc("%s: unable to import either %s or %s"%(message,qualified,generic)) + return manager + +#################### +class ManagerWrapper: + """ + This class acts as a wrapper around an SFA interface manager module, but + can be used with any python module. The purpose of this class is raise a + SfaNotImplemented exception if someone attempts to use an attribute + (could be a callable) thats not available in the library by checking the + library using hasattr. This helps to communicate better errors messages + to the users and developers in the event that a specifiec operation + is not implemented by a libarary and will generally be more helpful than + the standard AttributeError + """ + def __init__(self, manager, interface): + self.manager = manager + self.interface = interface + + def __getattr__(self, method): + if not hasattr(self.manager, method): + raise SfaNotImplemented(method, self.interface) + return getattr(self.manager, method) + diff --git a/sfa/methods/Resolve.py b/sfa/methods/Resolve.py index 1e1e7a44..74972cc9 100644 --- a/sfa/methods/Resolve.py +++ b/sfa/methods/Resolve.py @@ -42,6 +42,4 @@ class Resolve(Method): # send the call to the right manager manager = self.api.get_interface_manager() return manager.resolve(self.api, xrns, type) - - diff --git a/sfa/plc/api.py b/sfa/plc/plcsfaapi.py similarity index 65% rename from sfa/plc/api.py rename to sfa/plc/plcsfaapi.py index 02d571ac..e1360537 100644 --- a/sfa/plc/api.py +++ b/sfa/plc/plcsfaapi.py @@ -1,27 +1,15 @@ -# -# SFA XML-RPC and SOAP interfaces -# - -import os -import tempfile -import datetime import xmlrpclib - -from sfa.util.faults import RecordNotFound, MissingSfaInfo -from sfa.util.api import BaseAPI -from sfa.util.config import Config +# +from sfa.util.faults import MissingSfaInfo from sfa.util.sfalogging import logger -import sfa.util.xmlrpcprotocol as xmlrpcprotocol +from sfa.util.table import SfaTable + from sfa.util.xrn import hrn_to_urn -from sfa.util.plxrn import hostname_to_hrn, hrn_to_pl_slicename, \ - hrn_to_pl_slicename, slicename_to_hrn, hrn_to_pl_login_base -from sfa.util.nodemanager import NodeManager - -from sfa.trust.auth import Auth -from sfa.trust.rights import determine_rights -from sfa.trust.credential import Credential -from sfa.trust.certificate import Certificate, Keypair -from sfa.trust.gid import GID +from sfa.util.plxrn import slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base + +from sfa.server.sfaapi import SfaApi + +#################### xxx should move into util/defaultdict try: from collections import defaultdict except: @@ -69,35 +57,19 @@ def list_to_dict(recs, key): keys = [rec[key] for rec in recs] return dict(zip(keys, recs)) -class SfaAPI(BaseAPI): - - # flat list of method names - import sfa.methods - methods = sfa.methods.all - - def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", - methods='sfa.methods', peer_cert = None, interface = None, - key_file = None, cert_file = None, cache = None): - BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, \ - peer_cert=peer_cert, interface=interface, key_file=key_file, \ - cert_file=cert_file, cache=cache) +class PlcSfaApi(SfaApi): + + def __init__ (self, encoding="utf-8", methods='sfa.methods', + config = "/etc/sfa/sfa_config.py", + peer_cert = None, interface = None, + key_file = None, cert_file = None, cache = None): + SfaApi.__init__(self, encoding=encoding, methods=methods, + config=config, + peer_cert=peer_cert, interface=interface, + key_file=key_file, + cert_file=cert_file, cache=cache) - self.encoding = encoding - from sfa.util.table import SfaTable self.SfaTable = SfaTable - # Better just be documenting the API - if config is None: - return - - # Load configuration - self.config = Config(config) - self.auth = Auth(peer_cert) - self.interface = interface - self.key_file = key_file - self.key = Keypair(filename=self.key_file) - self.cert_file = cert_file - self.cert = Certificate(filename=self.cert_file) - self.credential = None # Initialize the PLC shell only if SFA wraps a myPLC rspec_type = self.config.get_aggregate_type() if (rspec_type == 'pl' or rspec_type == 'vini' or \ @@ -105,10 +77,6 @@ class SfaAPI(BaseAPI): self.plshell = self.getPLCShell() self.plshell_version = "4.3" - self.hrn = self.config.SFA_INTERFACE_HRN - self.time_format = "%Y-%m-%d %H:%M:%S" - - def getPLCShell(self): self.plauth = {'Username': self.config.SFA_PLC_USER, 'AuthMethod': 'password', @@ -129,132 +97,6 @@ class SfaAPI(BaseAPI): shell = xmlrpclib.Server(url, verbose = 0, allow_none = True) return shell - def get_server(self, interface, cred, timeout=30): - """ - Returns a connection to the specified interface. Use the specified - credential to determine the caller and look for the caller's key/cert - in the registry hierarchy cache. - """ - from sfa.trust.hierarchy import Hierarchy - if not isinstance(cred, Credential): - cred_obj = Credential(string=cred) - else: - cred_obj = cred - caller_gid = cred_obj.get_gid_caller() - hierarchy = Hierarchy() - auth_info = hierarchy.get_auth_info(caller_gid.get_hrn()) - key_file = auth_info.get_privkey_filename() - cert_file = auth_info.get_gid_filename() - server = interface.get_server(key_file, cert_file, timeout) - return server - - - def getCredential(self): - """ - Return a valid credential for this interface. - """ - type = 'authority' - path = self.config.SFA_DATA_DIR - filename = ".".join([self.interface, self.hrn, type, "cred"]) - cred_filename = path + os.sep + filename - cred = None - if os.path.isfile(cred_filename): - cred = Credential(filename = cred_filename) - # make sure cred isnt expired - if not cred.get_expiration or \ - datetime.datetime.utcnow() < cred.get_expiration(): - return cred.save_to_string(save_parents=True) - - # get a new credential - if self.interface in ['registry']: - cred = self.__getCredentialRaw() - else: - cred = self.__getCredential() - cred.save_to_file(cred_filename, save_parents=True) - - return cred.save_to_string(save_parents=True) - - - def getDelegatedCredential(self, creds): - """ - Attempt to find a credential delegated to us in - the specified list of creds. - """ - from sfa.trust.hierarchy import Hierarchy - if creds and not isinstance(creds, list): - creds = [creds] - hierarchy = Hierarchy() - - delegated_cred = None - for cred in creds: - if hierarchy.auth_exists(Credential(string=cred).get_gid_caller().get_hrn()): - delegated_cred = cred - break - return delegated_cred - - def __getCredential(self): - """ - Get our credential from a remote registry - """ - from sfa.server.registry import Registries - registries = Registries() - registry = registries.get_server(self.hrn, self.key_file, self.cert_file) - cert_string=self.cert.save_to_string(save_parents=True) - # get self credential - self_cred = registry.GetSelfCredential(cert_string, self.hrn, 'authority') - # get credential - cred = registry.GetCredential(self_cred, self.hrn, 'authority') - return Credential(string=cred) - - def __getCredentialRaw(self): - """ - Get our current credential directly from the local registry. - """ - - hrn = self.hrn - auth_hrn = self.auth.get_authority(hrn) - - # is this a root or sub authority - if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN: - auth_hrn = hrn - auth_info = self.auth.get_auth_info(auth_hrn) - table = self.SfaTable() - records = table.findObjects({'hrn': hrn, 'type': 'authority+sa'}) - if not records: - raise RecordNotFound - record = records[0] - type = record['type'] - object_gid = record.get_gid_object() - new_cred = Credential(subject = object_gid.get_subject()) - new_cred.set_gid_caller(object_gid) - new_cred.set_gid_object(object_gid) - new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename()) - - r1 = determine_rights(type, hrn) - new_cred.set_privileges(r1) - new_cred.encode() - new_cred.sign() - - return new_cred - - - def loadCredential (self): - """ - Attempt to load credential from file if it exists. If it doesnt get - credential from registry. - """ - - # see if this file exists - # XX This is really the aggregate's credential. Using this is easier than getting - # the registry's credential from iteslf (ssl errors). - ma_cred_filename = self.config.SFA_DATA_DIR + os.sep + self.interface + self.hrn + ".ma.cred" - try: - self.credential = Credential(filename = ma_cred_filename) - except IOError: - self.credential = self.getCredentialFromRegistry() - - - ## # Convert SFA fields to PLC fields for use when registering up updating # registry record in the PLC database @@ -588,7 +430,6 @@ class SfaAPI(BaseAPI): # please remove once this is issue is cleanly fixed def normalize (value): from types import StringTypes - from sfa.util.sfalogging import logger if isinstance(value,StringTypes): return value elif isinstance(value,dict): newvalue=value['text'] @@ -636,100 +477,3 @@ class SfaAPI(BaseAPI): elif record.type == "authority": # xxx TODO pass - - - -class ComponentAPI(BaseAPI): - - def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", methods='sfa.methods', - peer_cert = None, interface = None, key_file = None, cert_file = None): - - BaseAPI.__init__(self, config=config, encoding=encoding, methods=methods, peer_cert=peer_cert, - interface=interface, key_file=key_file, cert_file=cert_file) - self.encoding = encoding - - # Better just be documenting the API - if config is None: - return - - self.nodemanager = NodeManager(self.config) - - def sliver_exists(self): - sliver_dict = self.nodemanager.GetXIDs() - ### xxx slicename is undefined - if slicename in sliver_dict.keys(): - return True - else: - return False - - def get_registry(self): - addr, port = self.config.SFA_REGISTRY_HOST, self.config.SFA_REGISTRY_PORT - url = "http://%(addr)s:%(port)s" % locals() - server = xmlrpcprotocol.get_server(url, self.key_file, self.cert_file) - return server - - def get_node_key(self): - # this call requires no authentication, - # so we can generate a random keypair here - subject="component" - (kfd, keyfile) = tempfile.mkstemp() - (cfd, certfile) = tempfile.mkstemp() - key = Keypair(create=True) - key.save_to_file(keyfile) - cert = Certificate(subject=subject) - cert.set_issuer(key=key, subject=subject) - cert.set_pubkey(key) - cert.sign() - cert.save_to_file(certfile) - registry = self.get_registry() - # the registry will scp the key onto the node - registry.get_key() - - def getCredential(self): - """ - Get our credential from a remote registry - """ - path = self.config.SFA_DATA_DIR - config_dir = self.config.config_path - cred_filename = path + os.sep + 'node.cred' - try: - credential = Credential(filename = cred_filename) - return credential.save_to_string(save_parents=True) - except IOError: - node_pkey_file = config_dir + os.sep + "node.key" - node_gid_file = config_dir + os.sep + "node.gid" - cert_filename = path + os.sep + 'server.cert' - if not os.path.exists(node_pkey_file) or \ - not os.path.exists(node_gid_file): - self.get_node_key() - - # get node's hrn - gid = GID(filename=node_gid_file) - hrn = gid.get_hrn() - # get credential from registry - cert_str = Certificate(filename=cert_filename).save_to_string(save_parents=True) - registry = self.get_registry() - cred = registry.GetSelfCredential(cert_str, hrn, 'node') - # xxx credfile is undefined - Credential(string=cred).save_to_file(credfile, save_parents=True) - - return cred - - def clean_key_cred(self): - """ - remove the existing keypair and cred and generate new ones - """ - files = ["server.key", "server.cert", "node.cred"] - for f in files: - # xxx KEYDIR is undefined, could be meant to be "/var/lib/sfa/" from sfa_component_setup.py - filepath = KEYDIR + os.sep + f - if os.path.isfile(filepath): - os.unlink(f) - - # install the new key pair - # GetCredential will take care of generating the new keypair - # and credential - self.get_node_key() - self.getCredential() - - diff --git a/sfa/server/componentserver.py b/sfa/server/componentserver.py index eb97c7dd..419ebf2d 100644 --- a/sfa/server/componentserver.py +++ b/sfa/server/componentserver.py @@ -12,7 +12,7 @@ import SimpleXMLRPCServer from sfa.util.sfalogging import logger from sfa.trust.certificate import Keypair, Certificate -from sfa.plc.api import ComponentAPI +from sfa.plc.api import PlcComponentApi from sfa.server.threadedserver import ThreadedServer @@ -37,7 +37,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): try: peer_cert = Certificate() peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate()) - self.api = ComponentAPI(peer_cert = peer_cert, + self.api = PlcComponentApi(peer_cert = peer_cert, interface = self.server.interface, key_file = self.server.key_file, cert_file = self.server.cert_file) diff --git a/sfa/server/interface.py b/sfa/server/interface.py index d2b96b7f..7d2beef6 100644 --- a/sfa/server/interface.py +++ b/sfa/server/interface.py @@ -70,6 +70,10 @@ class Interfaces(dict): # port is appended onto the domain, before the path. Should look like: # http://domain:port/path hrn, address, port = record['hrn'], record['addr'], record['port'] + # sometime this is called at a very early stage with no config loaded + # avoid to remember this instance in such a case + if not address or not port: + continue interface = Interface(hrn, address, port) self[hrn] = interface diff --git a/sfa/server/modpython/SfaAggregateModPython.py b/sfa/server/modpython/SfaAggregateModPython.py index deaf89f9..ee4dc1f9 100755 --- a/sfa/server/modpython/SfaAggregateModPython.py +++ b/sfa/server/modpython/SfaAggregateModPython.py @@ -12,10 +12,10 @@ import traceback import xmlrpclib from mod_python import apache -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.util.sfalogging import logger -api = SfaAPI(interface='aggregate') +api = PlcSfaApi(interface='aggregate') def handler(req): try: diff --git a/sfa/server/modpython/SfaRegistryModPython.py b/sfa/server/modpython/SfaRegistryModPython.py index 8879813a..5fb7c379 100755 --- a/sfa/server/modpython/SfaRegistryModPython.py +++ b/sfa/server/modpython/SfaRegistryModPython.py @@ -12,10 +12,10 @@ import traceback import xmlrpclib from mod_python import apache -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.util.sfalogging import logger -api = SfaAPI(interface='registry') +api = PlcSfaApi(interface='registry') def handler(req): try: diff --git a/sfa/server/modpython/SfaSliceMgrModPython.py b/sfa/server/modpython/SfaSliceMgrModPython.py index e0f2b923..93f5ecf9 100755 --- a/sfa/server/modpython/SfaSliceMgrModPython.py +++ b/sfa/server/modpython/SfaSliceMgrModPython.py @@ -12,10 +12,10 @@ import traceback import xmlrpclib from mod_python import apache -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.util.sfalogging import logger -api = SfaAPI(interface='slicemgr') +api = PlcSfaApi(interface='slicemgr') def handler(req): try: diff --git a/sfa/server/sfa-clean-peer-records.py b/sfa/server/sfa-clean-peer-records.py index f821f4ce..c7aea3c1 100644 --- a/sfa/server/sfa-clean-peer-records.py +++ b/sfa/server/sfa-clean-peer-records.py @@ -5,7 +5,7 @@ import os import traceback from sfa.util.table import SfaTable from sfa.util.prefixTree import prefixTree -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.util.config import Config from sfa.trust.certificate import Keypair from sfa.trust.hierarchy import Hierarchy @@ -31,7 +31,7 @@ def main(): authority = config.SFA_INTERFACE_HRN url = 'http://%s:%s/' %(config.SFA_REGISTRY_HOST, config.SFA_REGISTRY_PORT) registry = xmlrpcprotocol.get_server(url, key_file, cert_file) - sfa_api = SfaAPI(key_file = key_file, cert_file = cert_file, interface='registry') + sfa_api = PlcSfaApi(key_file = key_file, cert_file = cert_file, interface='registry') credential = sfa_api.getCredential() # get peer registries diff --git a/sfa/server/sfa-start.py b/sfa/server/sfa-start.py index 379c72d1..ed77c78a 100755 --- a/sfa/server/sfa-start.py +++ b/sfa/server/sfa-start.py @@ -42,13 +42,13 @@ from sfa.trust.certificate import Keypair, Certificate from sfa.trust.hierarchy import Hierarchy from sfa.trust.gid import GID from sfa.util.config import Config -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi from sfa.server.registry import Registries from sfa.server.aggregate import Aggregates from sfa.util.xrn import get_authority, hrn_to_urn from sfa.util.sfalogging import logger -from sfa.managers.import_manager import import_manager +from sfa.managers.managerwrapper import import_manager # after http://www.erlenstar.demon.co.uk/unix/faq_2.html def daemon(): @@ -167,7 +167,7 @@ def install_peer_certs(server_key_file, server_cert_file): # There should be a gid file in /etc/sfa/trusted_roots for every # peer registry found in in the registries.xml config file. If there # are any missing gids, request a new one from the peer registry. - api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file) + api = PlcSfaApi(key_file = server_key_file, cert_file = server_cert_file) registries = Registries() aggregates = Aggregates() interfaces = dict(registries.items() + aggregates.items()) @@ -224,7 +224,7 @@ def update_cert_records(gids): Make sure there is a record in the registry for the specified gids. Removes old records from the db. """ - # import SfaTable here so this module can be loaded by ComponentAPI + # import SfaTable here so this module can be loaded by PlcComponentApi from sfa.util.table import SfaTable from sfa.util.record import SfaRecord if not gids: diff --git a/sfa/server/sfa_component_setup.py b/sfa/server/sfa_component_setup.py index 6d74468f..e1e02811 100755 --- a/sfa/server/sfa_component_setup.py +++ b/sfa/server/sfa_component_setup.py @@ -221,8 +221,8 @@ def get_gids(registry=None, verbose=False): if verbose: print "Getting current slices on this node" # get a list of slices on this node - from sfa.plc.api import ComponentAPI - api = ComponentAPI() + from sfa.plc.api import PlcComponentApi + api = PlcComponentApi() xids_tuple = api.nodemanager.GetXIDs() slices = eval(xids_tuple[1]) slicenames = slices.keys() diff --git a/sfa/server/sfaapi.py b/sfa/server/sfaapi.py new file mode 100644 index 00000000..97741bf6 --- /dev/null +++ b/sfa/server/sfaapi.py @@ -0,0 +1,212 @@ +from sfa.util.faults import SfaAPIError +from sfa.util.config import Config +from sfa.util.cache import Cache + +from sfa.trust.auth import Auth +from sfa.trust.certificate import Keypair, Certificate + +# this is wrong all right, but temporary +from sfa.managers.managerwrapper import ManagerWrapper, import_manager + +from sfa.server.xmlrpcapi import XmlrpcApi + +#################### +class SfaApi (XmlrpcApi): + + """ + An SfaApi instance is a basic xmlrcp service + augmented with the local cryptographic material and hrn + It also has the notion of neighbour sfa services + as defined in /etc/sfa/{aggregates,registries}.xml + It has no a priori knowledge of the underlying testbed + """ + + def __init__ (self, encoding="utf-8", methods='sfa.methods', + config = "/etc/sfa/sfa_config.py", + peer_cert = None, interface = None, + key_file = None, cert_file = None, cache = None): + + XmlrpcApi.__init__ (self, encoding) + + # we may be just be documenting the API + if config is None: + return + # Load configuration + self.config = Config(config) + self.auth = Auth(peer_cert) + self.interface = interface + self.hrn = self.config.SFA_INTERFACE_HRN + self.key_file = key_file + self.key = Keypair(filename=self.key_file) + self.cert_file = cert_file + self.cert = Certificate(filename=self.cert_file) + self.cache = cache + if self.cache is None: + self.cache = Cache() + self.credential = None + + # load registries + from sfa.server.registry import Registries + self.registries = Registries() + + # load aggregates + from sfa.server.aggregate import Aggregates + self.aggregates = Aggregates() + + + def get_interface_manager(self, manager_base = 'sfa.managers'): + """ + Returns the appropriate manager module for this interface. + Modules are usually found in sfa/managers/ + """ + manager=None + if self.interface in ['registry']: + manager=import_manager ("registry", self.config.SFA_REGISTRY_TYPE) + elif self.interface in ['aggregate']: + manager=import_manager ("aggregate", self.config.SFA_AGGREGATE_TYPE) + elif self.interface in ['slicemgr', 'sm']: + manager=import_manager ("slice", self.config.SFA_SM_TYPE) + elif self.interface in ['component', 'cm']: + manager=import_manager ("component", self.config.SFA_CM_TYPE) + if not manager: + raise SfaAPIError("No manager for interface: %s" % self.interface) + + # this isnt necessary but will help to produce better error messages + # if someone tries to access an operation this manager doesn't implement + manager = ManagerWrapper(manager, self.interface) + + return manager + + def get_server(self, interface, cred, timeout=30): + """ + Returns a connection to the specified interface. Use the specified + credential to determine the caller and look for the caller's key/cert + in the registry hierarchy cache. + """ + from sfa.trust.hierarchy import Hierarchy + if not isinstance(cred, Credential): + cred_obj = Credential(string=cred) + else: + cred_obj = cred + caller_gid = cred_obj.get_gid_caller() + hierarchy = Hierarchy() + auth_info = hierarchy.get_auth_info(caller_gid.get_hrn()) + key_file = auth_info.get_privkey_filename() + cert_file = auth_info.get_gid_filename() + server = interface.get_server(key_file, cert_file, timeout) + return server + + + def getCredential(self): + """ + Return a valid credential for this interface. + """ + type = 'authority' + path = self.config.SFA_DATA_DIR + filename = ".".join([self.interface, self.hrn, type, "cred"]) + cred_filename = path + os.sep + filename + cred = None + if os.path.isfile(cred_filename): + cred = Credential(filename = cred_filename) + # make sure cred isnt expired + if not cred.get_expiration or \ + datetime.datetime.utcnow() < cred.get_expiration(): + return cred.save_to_string(save_parents=True) + + # get a new credential + if self.interface in ['registry']: + cred = self.__getCredentialRaw() + else: + cred = self.__getCredential() + cred.save_to_file(cred_filename, save_parents=True) + + return cred.save_to_string(save_parents=True) + + + def getDelegatedCredential(self, creds): + """ + Attempt to find a credential delegated to us in + the specified list of creds. + """ + from sfa.trust.hierarchy import Hierarchy + if creds and not isinstance(creds, list): + creds = [creds] + hierarchy = Hierarchy() + + delegated_cred = None + for cred in creds: + if hierarchy.auth_exists(Credential(string=cred).get_gid_caller().get_hrn()): + delegated_cred = cred + break + return delegated_cred + + def __getCredential(self): + """ + Get our credential from a remote registry + """ + from sfa.server.registry import Registries + registries = Registries() + registry = registries.get_server(self.hrn, self.key_file, self.cert_file) + cert_string=self.cert.save_to_string(save_parents=True) + # get self credential + self_cred = registry.GetSelfCredential(cert_string, self.hrn, 'authority') + # get credential + cred = registry.GetCredential(self_cred, self.hrn, 'authority') + return Credential(string=cred) + + def __getCredentialRaw(self): + """ + Get our current credential directly from the local registry. + """ + + hrn = self.hrn + auth_hrn = self.auth.get_authority(hrn) + + # is this a root or sub authority + if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN: + auth_hrn = hrn + auth_info = self.auth.get_auth_info(auth_hrn) + table = self.SfaTable() + records = table.findObjects({'hrn': hrn, 'type': 'authority+sa'}) + if not records: + raise RecordNotFound + record = records[0] + type = record['type'] + object_gid = record.get_gid_object() + new_cred = Credential(subject = object_gid.get_subject()) + new_cred.set_gid_caller(object_gid) + new_cred.set_gid_object(object_gid) + new_cred.set_issuer_keys(auth_info.get_privkey_filename(), auth_info.get_gid_filename()) + + r1 = determine_rights(type, hrn) + new_cred.set_privileges(r1) + new_cred.encode() + new_cred.sign() + + return new_cred + + def loadCredential (self): + """ + Attempt to load credential from file if it exists. If it doesnt get + credential from registry. + """ + + # see if this file exists + # XX This is really the aggregate's credential. Using this is easier than getting + # the registry's credential from iteslf (ssl errors). + ma_cred_filename = self.config.SFA_DATA_DIR + os.sep + self.interface + self.hrn + ".ma.cred" + try: + self.credential = Credential(filename = ma_cred_filename) + except IOError: + self.credential = self.getCredentialFromRegistry() + + def get_cached_server_version(self, server): + cache_key = server.url + "-version" + server_version = None + if self.cache: + server_version = self.cache.get(cache_key) + if not server_version: + server_version = server.GetVersion() + # cache version for 24 hours + self.cache.add(cache_key, server_version, ttl= 60*60*24) + return server_version diff --git a/sfa/server/sfaserver.py b/sfa/server/sfaserver.py index d6123ea0..f392b785 100644 --- a/sfa/server/sfaserver.py +++ b/sfa/server/sfaserver.py @@ -12,8 +12,6 @@ from sfa.server.threadedserver import ThreadedServer, SecureXMLRpcRequestHandler from sfa.util.sfalogging import logger from sfa.trust.certificate import Keypair, Certificate -#should be passed to threadedserver -#from sfa.plc.api import SfaAPI ## # Implements an HTTPS XML-RPC server. Generally it is expected that SFA diff --git a/sfa/server/threadedserver.py b/sfa/server/threadedserver.py index 03ba5b3c..6dafa0bb 100644 --- a/sfa/server/threadedserver.py +++ b/sfa/server/threadedserver.py @@ -22,7 +22,7 @@ from sfa.util.cache import Cache from sfa.trust.certificate import Certificate from sfa.trust.trustedroots import TrustedRoots #can we get rid of that ? -from sfa.plc.api import SfaAPI +from sfa.plc.api import PlcSfaApi ## # Verification callback for pyOpenSSL. We do our own checking of keys because @@ -95,7 +95,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): try: peer_cert = Certificate() peer_cert.load_from_pyopenssl_x509(self.connection.get_peer_certificate()) - self.api = SfaAPI(peer_cert = peer_cert, + self.api = PlcSfaApi(peer_cert = peer_cert, interface = self.server.interface, key_file = self.server.key_file, cert_file = self.server.cert_file, diff --git a/sfa/util/api.py b/sfa/server/xmlrpcapi.py similarity index 57% rename from sfa/util/api.py rename to sfa/server/xmlrpcapi.py index afc4e970..456cd42d 100644 --- a/sfa/util/api.py +++ b/sfa/server/xmlrpcapi.py @@ -5,16 +5,22 @@ import string import xmlrpclib -from sfa.util.faults import SfaNotImplemented, SfaAPIError, SfaInvalidAPIMethod, SfaFault -from sfa.util.config import Config -from sfa.util.sfalogging import logger -from sfa.trust.auth import Auth -from sfa.util.cache import Cache -from sfa.trust.certificate import Keypair, Certificate +# SOAP support is optional +try: + import SOAPpy + from SOAPpy.Parser import parseSOAPRPC + from SOAPpy.Types import faultType + from SOAPpy.NS import NS + from SOAPpy.SOAPBuilder import buildSOAP +except ImportError: + SOAPpy = None -# this is wrong all right, but temporary -from sfa.managers.import_manager import import_manager +#################### +#from sfa.util.faults import SfaNotImplemented, SfaAPIError, SfaInvalidAPIMethod, SfaFault +from sfa.util.faults import SfaInvalidAPIMethod, SfaAPIError, SfaFault +from sfa.util.sfalogging import logger +#################### # See "2.2 Characters" in the XML specification: # # #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] @@ -72,103 +78,24 @@ def xmlrpclib_dump(self, value, write): # You can't hide from me! xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump -# SOAP support is optional -try: - import SOAPpy - from SOAPpy.Parser import parseSOAPRPC - from SOAPpy.Types import faultType - from SOAPpy.NS import NS - from SOAPpy.SOAPBuilder import buildSOAP -except ImportError: - SOAPpy = None - - -class ManagerWrapper: +class XmlrpcApi: """ - This class acts as a wrapper around an SFA interface manager module, but - can be used with any python module. The purpose of this class is raise a - SfaNotImplemented exception if someone attempts to use an attribute - (could be a callable) thats not available in the library by checking the - library using hasattr. This helps to communicate better errors messages - to the users and developers in the event that a specifiec operation - is not implemented by a libarary and will generally be more helpful than - the standard AttributeError + The XmlrpcApi class implements a basic xmlrpc (or soap) service """ - def __init__(self, manager, interface): - self.manager = manager - self.interface = interface - - def __getattr__(self, method): - if not hasattr(self.manager, method): - raise SfaNotImplemented(method, self.interface) - return getattr(self.manager, method) - -class BaseAPI: protocol = None - def __init__(self, config = "/etc/sfa/sfa_config.py", encoding = "utf-8", - methods='sfa.methods', peer_cert = None, interface = None, - key_file = None, cert_file = None, cache = None): + def __init__ (self, encoding="utf-8", methods='sfa.methods'): self.encoding = encoding + self.source = None # flat list of method names self.methods_module = methods_module = __import__(methods, fromlist=[methods]) self.methods = methods_module.all - # Better just be documenting the API - if config is None: - return - # Load configuration - self.config = Config(config) - self.auth = Auth(peer_cert) - self.hrn = self.config.SFA_INTERFACE_HRN - self.interface = interface - self.key_file = key_file - self.key = Keypair(filename=self.key_file) - self.cert_file = cert_file - self.cert = Certificate(filename=self.cert_file) - self.cache = cache - if self.cache is None: - self.cache = Cache() - self.credential = None - self.source = None - self.time_format = "%Y-%m-%d %H:%M:%S" self.logger = logger - # load registries - from sfa.server.registry import Registries - self.registries = Registries() - - # load aggregates - from sfa.server.aggregate import Aggregates - self.aggregates = Aggregates() - - - def get_interface_manager(self, manager_base = 'sfa.managers'): - """ - Returns the appropriate manager module for this interface. - Modules are usually found in sfa/managers/ - """ - manager=None - if self.interface in ['registry']: - manager=import_manager ("registry", self.config.SFA_REGISTRY_TYPE) - elif self.interface in ['aggregate']: - manager=import_manager ("aggregate", self.config.SFA_AGGREGATE_TYPE) - elif self.interface in ['slicemgr', 'sm']: - manager=import_manager ("slice", self.config.SFA_SM_TYPE) - elif self.interface in ['component', 'cm']: - manager=import_manager ("component", self.config.SFA_CM_TYPE) - if not manager: - raise SfaAPIError("No manager for interface: %s" % self.interface) - - # this isnt necessary but will help to produce better error messages - # if someone tries to access an operation this manager doesn't implement - manager = ManagerWrapper(manager, self.interface) - - return manager - def callable(self, method): """ Return a new instance of the specified method. @@ -183,7 +110,7 @@ class BaseAPI: module = __import__(self.methods_module.__name__ + "." + method, globals(), locals(), [classname]) callablemethod = getattr(module, classname)(self) return getattr(module, classname)(self) - except ImportError, AttributeError: + except (ImportError, AttributeError): raise SfaInvalidAPIMethod, method def call(self, source, method, *args): @@ -226,7 +153,7 @@ class BaseAPI: except SfaFault, fault: result = fault except Exception, fault: - logger.log_exc("BaseAPI.handle has caught Exception") + self.logger.log_exc("XmlrpcApi.handle has caught Exception") result = SfaAPIError(fault) @@ -255,13 +182,3 @@ class BaseAPI: return response - def get_cached_server_version(self, server): - cache_key = server.url + "-version" - server_version = None - if self.cache: - server_version = self.cache.get(cache_key) - if not server_version: - server_version = server.GetVersion() - # cache version for 24 hours - self.cache.add(cache_key, server_version, ttl= 60*60*24) - return server_version diff --git a/sfa/util/xmlrpcprotocol.py b/sfa/util/xmlrpcprotocol.py index 25e7b76d..2263b286 100644 --- a/sfa/util/xmlrpcprotocol.py +++ b/sfa/util/xmlrpcprotocol.py @@ -1,9 +1,10 @@ # XMLRPC-specific code for SFA Client import xmlrpclib -#from sfa.util.httpsProtocol import HTTPS, HTTPSConnection from httplib import HTTPS, HTTPSConnection + from sfa.util.sfalogging import logger + ## # ServerException, ExceptionUnmarshaller # -- 2.43.0