diff options
Diffstat (limited to 'src/lib/Proxy.py')
-rw-r--r-- | src/lib/Proxy.py | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/lib/Proxy.py b/src/lib/Proxy.py new file mode 100644 index 000000000..5bcc78ad1 --- /dev/null +++ b/src/lib/Proxy.py @@ -0,0 +1,188 @@ +"""RPC client access to cobalt components. + +Classes: +ComponentProxy -- an RPC client proxy to Cobalt components + +Functions: +load_config -- read configuration files +""" + +__revision__ = '$Revision: $' + +from ConfigParser import SafeConfigParser, NoSectionError +import logging, socket, urlparse, time, Bcfg2.tlslite.errors +from Bcfg2.tlslite.integration.XMLRPCTransport import XMLRPCTransport +import xmlrpclib +from xmlrpclib import _Method + +__all__ = [ + "ComponentProxy", "ComponentLookupError", "RetryMethod", + "register_component", "find_configured_servers", +] + +local_components = dict() +known_servers = dict() + +def register_component (component): + local_components[component.name] = component + + +class ComponentError (Exception): + + """Component error baseclass""" + + +class ComponentLookupError (ComponentError): + + """Unable to locate an address for the given component.""" + + +class ComponentOperationError (ComponentError): + + """Component Failure during operation""" + + +class RetryMethod(_Method): + """Method with error handling and retries built in""" + log = logging.getLogger('xmlrpc') + def __call__(self, *args): + max_retries = 4 + for retry in range(max_retries): + try: + return _Method.__call__(self, *args) + except xmlrpclib.ProtocolError: + self.log.error("Server failure: Protocol Error") + raise xmlrpclib.Fault(20, "Server Failure") + except socket.error: + pass + except Bcfg2.tlslite.errors.TLSFingerprintError, err: + self.log.error("Server fingerprint did not match") + errmsg = err.message.split() + self.log.error("Got %s expected %s" % (errmsg[3], errmsg[4])) + raise SystemExit, 1 + except: + self.log.error("Unknown failure", exc_info=1) + break + time.sleep(0.5) + raise xmlrpclib.Fault(20, "Server Failure") + +# sorry jon +xmlrpclib._Method = RetryMethod + +def ComponentProxy (component_name, defer=False, user=None, password=None, + fingerprint=None): + + """Constructs proxies to components. + + Arguments: + component_name -- name of the component to connect to + + Additional arguments are passed to the ServerProxy constructor. + """ + + if defer: + return DeferredProxy(component_name) + + if component_name in local_components: + return LocalProxy(local_components[component_name]) + elif component_name in known_servers: + url = known_servers[component_name] + elif component_name != "service-location": + try: + slp = ComponentProxy("service-location") + except ComponentLookupError: + raise ComponentLookupError(component_name) + try: + url = slp.locate(component_name) + except: + raise ComponentLookupError(component_name) + if not url: + raise ComponentLookupError(component_name) + else: + raise ComponentLookupError(component_name) + # process url + if user and password: + method, path = urlparse.urlparse(url)[:2] + newurl = "%s://%s:%s@%s" % (method, user, password, path) + else: + newurl = url + return xmlrpclib.ServerProxy(newurl, allow_none=True, + transport=XMLRPCTransport(x509Fingerprint=fingerprint)) + +class LocalProxy (object): + + """Proxy-like filter for inter-component communication. + + Used to access other components stored in local memory, + without having to transport across tcp/http. + + Dispatches method calls through the component's _dispatch + method to keep the interface between this and ServerProxy + consistent. + """ + + def __init__ (self, component): + self._component = component + + def __getattr__ (self, attribute): + return LocalProxyMethod(self, attribute) + + +class LocalProxyMethod (object): + + def __init__ (self, proxy, func_name): + self._proxy = proxy + self._func_name = func_name + + def __call__ (self, *args): + return self._proxy._component._dispatch(self._func_name, args) + + +class DeferredProxy (object): + + """Defering proxy object. + + Gets a new proxy when it can't connect to a component. + """ + + def __init__ (self, component_name): + self._component_name = component_name + + def __getattr__ (self, attribute): + return DeferredProxyMethod(self, attribute) + + +class DeferredProxyMethod (object): + + def __init__ (self, proxy, func_name): + self._proxy = proxy + self._func_name = func_name + + def __call__ (self, *args): + proxy = ComponentProxy(self._proxy._component_name, defer=False) + func = getattr(proxy, self._func_name) + return func(*args) + + +def find_configured_servers (config_files=None): + """Read associated config files into the module. + + Arguments: + config_files -- a list of paths to config files. + """ + if not config_files: + config_files = ['/etc/bcfg2.conf'] + config = SafeConfigParser() + config.read(config_files) + try: + components = config.options("components") + except NoSectionError: + return [] + known_servers.clear() + known_servers.update(dict([ + (component, config.get("components", component)) + for component in components + ])) + return known_servers.copy() + +find_configured_servers() |