Nuwan Goonasekera 7 лет назад
Родитель
Сommit
2e51da9acc

+ 48 - 0
cloudbridge/cloud/base/middleware.py

@@ -0,0 +1,48 @@
+import logging
+from abc import abstractmethod
+
+
+from ..interfaces.middleware import Middleware
+from ..interfaces.middleware import MiddlewareManager
+
+log = logging.getLogger(__name__)
+
+
+class SimpleMiddlewareManager(MiddlewareManager):
+
+    def __init__(self, event_manager):
+        self.events = event_manager
+        self.middleware = []
+
+    def add(self, middleware):
+        self.middleware.append(middleware)
+        middleware.install(self.events)
+
+    def remove(self, middleware):
+        middleware.uninstall()
+        self.middleware.remove(middleware)
+
+
+class BaseMiddleware(Middleware):
+
+    def install(self, event_manager):
+        self.event_handlers = []
+        self.events = event_manager
+        self.setup()
+
+    @abstractmethod
+    def setup(self):
+        pass
+
+    def add_observer(self, event_pattern, priority, callback):
+        handler = self.events.observe(event_pattern, priority, callback)
+        self.event_handlers.append(handler)
+
+    def add_interceptor(self, event_pattern, priority, callback):
+        handler = self.events.intercept(event_pattern, priority, callback)
+        self.event_handlers.append(handler)
+
+    def uninstall(self):
+        for handler in self.event_handlers:
+            handler.unsubscribe()
+        self.events = None

+ 6 - 0
cloudbridge/cloud/base/provider.py

@@ -11,6 +11,7 @@ except ImportError:  # Python 2
 import six
 import six
 
 
 from ..base.events import SimpleEventDispatcher
 from ..base.events import SimpleEventDispatcher
+from ..base.middleware import SimpleMiddlewareManager
 from ..interfaces import CloudProvider
 from ..interfaces import CloudProvider
 from ..interfaces.exceptions import ProviderConnectionException
 from ..interfaces.exceptions import ProviderConnectionException
 from ..interfaces.resources import Configuration
 from ..interfaces.resources import Configuration
@@ -86,6 +87,7 @@ class BaseCloudProvider(CloudProvider):
         self._config_parser = ConfigParser()
         self._config_parser = ConfigParser()
         self._config_parser.read(CloudBridgeConfigLocations)
         self._config_parser.read(CloudBridgeConfigLocations)
         self._events = SimpleEventDispatcher()
         self._events = SimpleEventDispatcher()
+        self._middleware = SimpleMiddlewareManager(self._events)
 
 
     @property
     @property
     def config(self):
     def config(self):
@@ -99,6 +101,10 @@ class BaseCloudProvider(CloudProvider):
     def events(self):
     def events(self):
         return self._events
         return self._events
 
 
+    @property
+    def middleware(self):
+        return self._middleware
+
     def authenticate(self):
     def authenticate(self):
         """
         """
         A basic implementation which simply runs a low impact command to
         A basic implementation which simply runs a low impact command to

+ 61 - 0
cloudbridge/cloud/interfaces/middleware.py

@@ -0,0 +1,61 @@
+from abc import ABCMeta, abstractmethod
+
+
+class Middleware(object):
+    """
+    Provides a mechanism for grouping related event handlers together, to
+    provide logically cohesive middleware. The middleware class allows event
+    handlers to subscribe to events through the install method, and unsubscribe
+    through the uninstall method. This allows event handlers to be added and
+    removed as a group. The event handler implementations will also typically
+    live inside the middleware class. For example, LoggingMiddleware may
+    register multiple event handlers to log data before and after calls.
+    ResourceTrackingMiddleware may track all objects that are created or
+    deleted.
+    """
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def install(self, provider):
+        """
+        Use this method to subscribe all event handlers that are part of this
+        middleware. The install method will be called when the middleware is
+        first added to a MiddleWareManager.
+
+        :type provider: :class:`.Provider`
+        :param provider: The provider that this middleware belongs to
+        """
+        pass
+
+    @abstractmethod
+    def uninstall(self, provider):
+        """
+        Use this method to unsubscribe all event handlers for this middleware.
+        """
+        pass
+
+
+class MiddlewareManager(object):
+    """
+    Provides a mechanism for tracking a list of installed middleware
+    """
+
+    __metaclass__ = ABCMeta
+
+    @abstractmethod
+    def add(self, middleware):
+        """
+        Use this method to add middleware to this middleware manager.
+
+        :type middleware: :class:`.Middleware`
+        :param middleware: The middleware implementation
+        """
+        pass
+
+    @abstractmethod
+    def remove(self, middleware):
+        """
+        Use this method to remove this middleware from the middleware manager.
+        """
+        pass

+ 134 - 0
test/test_middleware_system.py

@@ -0,0 +1,134 @@
+import unittest
+
+from cloudbridge.cloud.base.events import SimpleEventDispatcher
+from cloudbridge.cloud.base.middleware import BaseMiddleware
+from cloudbridge.cloud.base.middleware import SimpleMiddlewareManager
+from cloudbridge.cloud.interfaces.middleware import Middleware
+
+
+class MiddlewareSystemTestCase(unittest.TestCase):
+
+    def test_basic_middleware(self):
+
+        class DummyMiddleWare(Middleware):
+
+            def __init__(self):
+                self.invocation_order = ""
+
+            def install(self, event_manager):
+                self.event_manager = event_manager
+                self.invocation_order += "install_"
+
+            def uninstall(self):
+                self.invocation_order += "uninstall"
+
+        dispatcher = SimpleEventDispatcher()
+        manager = SimpleMiddlewareManager(dispatcher)
+        middleware = DummyMiddleWare()
+        manager.add(middleware)
+
+        self.assertEqual(middleware.invocation_order, "install_",
+                         "install should be called when adding new middleware")
+
+        manager.remove(middleware)
+        self.assertEqual(middleware.invocation_order, "install_uninstall",
+                         "uninstall should be called when removing middleware")
+
+    def test_base_middleware(self):
+        EVENT_NAME = "some.event.occurred"
+
+        class DummyMiddleWare(BaseMiddleware):
+
+            def __init__(self):
+                self.invocation_order = ""
+
+            def setup(self):
+                self.add_observer(event_pattern="some.event.*", priority=1000,
+                                  callback=self.my_callback_obs)
+                self.add_interceptor(event_pattern="some.*", priority=900,
+                                     callback=self.my_callback_intcpt)
+
+            def my_callback_obs(self, **kwargs):
+                self.invocation_order += "observe"
+
+            def my_callback_intcpt(self, **kwargs):
+                self.invocation_order += "intercept_"
+                return kwargs.get('next_handler').invoke(**kwargs)
+
+        dispatcher = SimpleEventDispatcher()
+        manager = SimpleMiddlewareManager(dispatcher)
+        middleware = DummyMiddleWare()
+        manager.add(middleware)
+        dispatcher.emit(self, EVENT_NAME)
+
+        self.assertEqual(middleware.invocation_order, "intercept_observe")
+        self.assertListEqual(
+            [middleware.my_callback_intcpt, middleware.my_callback_obs],
+            [handler.callback for handler
+             in dispatcher.get_handlers_for_event(EVENT_NAME)])
+
+        manager.remove(middleware)
+
+        self.assertListEqual([], dispatcher.get_handlers_for_event(EVENT_NAME))
+
+    def test_multiple_middleware(self):
+        EVENT_NAME = "some.really.interesting.event.occurred"
+
+        class DummyMiddleWare1(BaseMiddleware):
+
+            def __init__(self):
+                self.invocation_order = ""
+
+            def setup(self):
+                self.add_observer(event_pattern="some.really.*", priority=1000,
+                                  callback=self.my_callback_obs1)
+                self.add_interceptor(event_pattern="some.*", priority=900,
+                                     callback=self.my_callback_intcpt2)
+
+            def my_callback_obs1(self, **kwargs):
+                self.invocation_order += "observe"
+
+            def my_callback_intcpt2(self, **kwargs):
+                self.invocation_order += "intercept_"
+                return kwargs.get('next_handler').invoke(**kwargs)
+
+        class DummyMiddleWare2(BaseMiddleware):
+
+            def __init__(self):
+                self.invocation_order = ""
+
+            def setup(self):
+                self.add_observer(event_pattern="some.really.*", priority=1050,
+                                  callback=self.my_callback_obs1)
+                self.add_interceptor(event_pattern="*", priority=950,
+                                     callback=self.my_callback_intcpt2)
+
+            def my_callback_obs1(self, **kwargs):
+                self.invocation_order += "observe"
+
+            def my_callback_intcpt2(self, **kwargs):
+                self.invocation_order += "intercept_"
+                return kwargs.get('next_handler').invoke(**kwargs)
+
+        dispatcher = SimpleEventDispatcher()
+        manager = SimpleMiddlewareManager(dispatcher)
+        middleware1 = DummyMiddleWare1()
+        middleware2 = DummyMiddleWare2()
+        manager.add(middleware1)
+        manager.add(middleware2)
+        dispatcher.emit(self, EVENT_NAME)
+
+        # Callbacks in both middleware classes should be registered
+        self.assertListEqual(
+            [middleware1.my_callback_intcpt2, middleware2.my_callback_intcpt2,
+             middleware1.my_callback_obs1, middleware2.my_callback_obs1],
+            [handler.callback for handler
+             in dispatcher.get_handlers_for_event(EVENT_NAME)])
+
+        manager.remove(middleware1)
+
+        # Only middleware2 callbacks should be registered
+        self.assertListEqual(
+            [middleware2.my_callback_intcpt2, middleware2.my_callback_obs1],
+            [handler.callback for handler in
+             dispatcher.get_handlers_for_event(EVENT_NAME)])