Jelajahi Sumber

Implemented service-level event methods & Better docs

almahmoud 7 tahun lalu
induk
melakukan
3c884ab0de

+ 1 - 1
cloudbridge/cloud/base/provider.py

@@ -123,7 +123,7 @@ class EventDispatcher(object):
             self.__events[event_name] = list()
         self.__events[event_name].append((priority, handler))
 
-    def interceptable_call(self, event_name, priority, callback, **kwargs):
+    def call(self, event_name, priority, callback, **kwargs):
         handler = EventHandler(HandlerType.SUBSCRIPTION, callback)
         if not self.__events.get(event_name):
             self.__events[event_name] = list()

+ 50 - 58
cloudbridge/cloud/base/services.py

@@ -38,11 +38,34 @@ class BaseCloudService(CloudService):
 
     def __init__(self, provider):
         self._provider = provider
+        self._service_event_name = "provider"
 
     @property
     def provider(self):
         return self._provider
 
+    def _generate_event_name(self, func_name):
+        return ".".join((self.service_name, func_name))
+
+    def subscribe_event(self, func_name, priority, callback,
+                        result_callback=False):
+        event_name = self._generate_event_name(func_name)
+        self.provider.events.subscribe(event_name, priority, callback,
+                                       result_callback)
+
+    def check_initialized(self, func_name):
+        event_name = self._generate_event_name(func_name)
+        return self.provider.events.check_initialized(event_name)
+
+    def mark_initialized(self, func_name):
+        event_name = self._generate_event_name(func_name)
+        self.provider.events.mark_initialized(event_name)
+
+    def call_event(self, func_name, priority, callback, **kwargs):
+        event_name = self._generate_event_name(func_name)
+        return self.provider.events.call(event_name, priority, callback,
+                                         **kwargs)
+
 
 class BaseSecurityService(SecurityService, BaseCloudService):
 
@@ -109,12 +132,7 @@ class BaseBucketService(
         super(BaseBucketService, self).__init__(provider)
         self._service_event_name = "provider.storage.buckets"
 
-    def get_event_name(self, func_name):
-        return ".".join((self._service_event_name, func_name))
-
     def _init_get(self):
-        event_name = self.get_event_name("get")
-
         def _get_pre_log(bucket_id):
             log.debug("Getting {} bucket with the id: {}".format(
                 self.provider.name, bucket_id))
@@ -122,28 +140,21 @@ class BaseBucketService(
         def _get_post_log(callback_result, bucket_id):
             log.debug("Returned bucket object: {}".format(callback_result))
 
-        self.provider.events.subscribe(event_name, 20000, _get_pre_log)
-        self.provider.events.subscribe(event_name, 30000,
-                                       _get_post_log,
-                                       result_callback=True)
-        self.provider.events.mark_initialized(event_name)
+        self.subscribe_event("get", 2000, _get_pre_log)
+        self.subscribe_event("get", 3000, _get_post_log, result_callback=True)
+        self.mark_initialized("get")
 
     def get(self, bucket_id):
         """
         Returns a bucket given its ID. Returns ``None`` if the bucket
         does not exist.
         """
-        event_name = self.get_event_name("get")
-        if not self.provider.events.check_initialized(event_name):
+        if not self.check_initialized("get"):
             self._init_get()
-        return self.provider.events.interceptable_call(event_name,
-                                                       priority=25000,
-                                                       callback=self._get,
-                                                       bucket_id=bucket_id)
+        return self.call_event("get", priority=2500, callback=self._get,
+                               bucket_id=bucket_id)
 
     def _init_find(self):
-        event_name = self.get_event_name("find")
-
         def _find_pre_log(**kwargs):
             log.debug("Finding {} buckets with the following arguments: {}"
                       .format(self.provider.name, kwargs))
@@ -151,12 +162,10 @@ class BaseBucketService(
         def _find_post_log(callback_result, **kwargs):
             log.debug("Returned bucket objects: {}".format(callback_result))
 
-        self.provider.events.subscribe(event_name, 20000,
-                                       _find_pre_log)
-        self.provider.events.subscribe(event_name, 30000,
-                                       _find_post_log,
-                                       result_callback=True)
-        self.provider.events.mark_initialized(event_name)
+        self.subscribe_event("find", 2000, _find_pre_log)
+        self.subscribe_event("find", 3000, _find_post_log,
+                             result_callback=True)
+        self.mark_initialized("find")
 
     # Generic find will be used for providers where we have not implemented
     # provider-specific querying for find method
@@ -178,16 +187,12 @@ class BaseBucketService(
         """
         Returns a list of buckets filtered by the given keyword arguments.
         """
-        event_name = self.get_event_name("find")
-        if not self.provider.events.check_initialized(event_name):
+        if not self.check_initialized("find"):
             self._init_find()
-        return self.provider.events.interceptable_call(event_name,
-                                                       priority=25000,
-                                                       callback=self._find,
-                                                       **kwargs)
+        return self.call_event("find", priority=2500, callback=self._find,
+                               **kwargs)
 
     def _init_list(self):
-        event_name = self.get_event_name("list")
 
         def _list_pre_log(limit, marker):
             message = "Listing {} buckets".format(self.provider.name)
@@ -200,28 +205,21 @@ class BaseBucketService(
         def _list_post_log(callback_result, limit, marker):
             log.debug("Returned bucket objects: {}".format(callback_result))
 
-        self.provider.events.subscribe(event_name, 20000,
-                                       _list_pre_log)
-        self.provider.events.subscribe(event_name, 30000,
-                                       _list_post_log,
-                                       result_callback=True)
-        self.provider.events.mark_initialized(event_name)
+        self.subscribe_event("list", 2000, _list_pre_log)
+        self.subscribe_event("list", 3000, _list_post_log,
+                             result_callback=True)
+        self.mark_initialized("list")
 
     def list(self, limit=None, marker=None):
         """
         List all buckets.
         """
-        event_name = self.get_event_name("list")
-        if not self.provider.events.check_initialized(event_name):
+        if not self.check_initialized("list"):
             self._init_list()
-        return self.provider.events.interceptable_call(event_name,
-                                                       priority=25000,
-                                                       callback=self._list,
-                                                       limit=limit,
-                                                       marker=marker)
+        return self.call_event("list", priority=2500, callback=self._list,
+                               limit=limit, marker=marker)
 
     def _init_create(self):
-        event_name = self.get_event_name("create")
 
         def _create_pre_log(name, location):
             message = "Creating {} bucket with name '{}'".format(
@@ -233,27 +231,21 @@ class BaseBucketService(
         def _create_post_log(callback_result, name, location):
             log.debug("Returned bucket object: {}".format(callback_result))
 
-        self.provider.events.subscribe(event_name, 20000,
-                                       _create_pre_log)
-        self.provider.events.subscribe(event_name, 30000,
-                                       _create_post_log,
-                                       result_callback=True)
-        self.provider.events.mark_initialized(event_name)
+        self.subscribe_event("create", 2000, _create_pre_log)
+        self.subscribe_event("create", 3000, _create_post_log,
+                             result_callback=True)
+        self.mark_initialized("create")
 
     def create(self, name, location=None):
         """
         Create a new bucket.
         """
-        event_name = self.get_event_name("create")
-        if not self.provider.events.check_initialized(event_name):
+        if not self.check_initialized("create"):
             self._init_create()
         BaseBucket.assert_valid_resource_name(name)
         location = location or self.provider.region_name
-        return self.provider.events.interceptable_call(event_name,
-                                                       priority=25000,
-                                                       callback=self._create,
-                                                       name=name,
-                                                       location=location)
+        return self.call_event("create", priority=2500, callback=self._create,
+                               name=name, location=location)
 
 
 class BaseComputeService(ComputeService, BaseCloudService):

+ 140 - 28
docs/topics/event_system.rst

@@ -50,11 +50,11 @@ Below are the default priorities used across events:
 +------------------------+----------+
 | Handler                | Priority |
 +------------------------+----------+
-| Pre-Logger             | 20000    |
+| Pre-Logger             | 2000     |
 +------------------------+----------+
-| Main Function Call     | 25000    |
+| Main Function Call     | 2500     |
 +------------------------+----------+
-| Post-Logger            | 30000    |
+| Post-Logger            | 3000     |
 +------------------------+----------+
 
 The Pre- and Post- loggers represent universal loggers respectively keeping
@@ -66,41 +66,153 @@ Example
 -------
 
 Below is an example of the way in which the Event System works for a simple
-getter.
+getter, from the CloudBridge developer perspective as well as the final user
+perspective.
 
 .. code-block:: python
 
-    def _get(object_id):
-        # get the object
-        return obj
+    ## Provider Specific code
+    class MyFirstProviderService(BaseService):
+
+        def __init__(self, provider):
+            super(MyFirstProviderService, self).__init__(provider)
+
+        def _get(self, obj_id):
+            # do the getting
+            resource = ...
+            return MyFirstProviderResource(resource)
+
+    class MySecondProviderService(BaseService):
+
+        def __init__(self, provider):
+            super(MySecondProviderService, self).__init__(provider)
+
+        def _get(self, obj_id):
+            # do the getting
+            resource = ...
+            return MySecondProviderResource(resource)
+
+    ## Base code
+    class BaseService(ProviderService):
+        def __init__(self, provider):
+            super(Service, self).__init__(provider)
+            self._service_event_name = "provider.service"
+
+        def _init_get(self):
+
+            def _get_pre_log(obj_id):
+                log.debug("Getting {} object with the id: {}".format(
+                    self.provider.name, bucket_id))
+
+            def _get_post_log(callback_result, obj_id):
+                log.debug("Returned object: {}".format(callback_result))
+
+            self.subscribe_event("get", 2000, _get_pre_log)
+            self.subscribe_event("get", 3000, _get_post_log,
+                                 result_callback=True)
+
+            self.mark_initialized("get")
+
+        # Public get function
+        def get(self, obj_id):
+            """
+            Returns an object given its ID. Returns ``None`` if the object
+            does not exist.
+            """
+            if not self.check_initialized("get"):
+                self._init_get()
+            return self.call_event("get", priority=2500,
+                                   main_call=self._get,
+                                   obj_id=obj_id)
+
+Thus, adding a new provider only requires adding the Service class with a
+protected class accepting the same parameters, and the logging and public
+method signature will remain the same, as the code will not be re-written
+for each provider.
+Additionally, if a developer needs to add additional logging for a
+particular service, beyond the default logging for all services, they can do
+so in the event initialisation function, and it will be applied to all
+providers. For example:
 
-    def _pre_log(object_id):
-        print("I am calling 'get' with object_id: {}".format(object_id))
+.. code-block:: python
+
+    ## Base code
+    class BaseService(ProviderService):
+        def __init__(self, provider):
+            super(Service, self).__init__(provider)
+            self._service_event_name = "provider.service"
+
+        def _init_get(self):
+
+            def _get_pre_log(obj_id):
+                log.debug("Getting {} object with the id: {}".format(
+                    self.provider.name, bucket_id))
+
+            def _get_post_log(callback_result, obj_id):
+                log.debug("Returned object: {}".format(callback_result))
+
+            def _special_none_log(callback_result, obj_id):
+                if not callback_result:
+                    log.debug("There is no object with id '{}'".format(obj_id))
+
+            self.subscribe_event("get", 2000, _get_pre_log)
+            self.subscribe_event("get", 3000, _get_post_log,
+                                 result_callback=True)
+            self.subscribe_event("get", 2750, _special_none_log,
+                                 result_callback=True)
+
+            self.mark_initialized("get")
+
+       # Public get function
+        def get(self, obj_id):
+            """
+            Returns an object given its ID. Returns ``None`` if the object
+            does not exist.
+            """
+            if not self.check_initialized("get"):
+                self._init_get()
+            return self.call_event("get", priority=2500,
+                                   main_call=self._get,
+                                   obj_id=obj_id)
 
-    def _post_log(callback_result, object_id):
-        print("Returned object {} for a 'get' request with object_id: {}"
-        .format(callback_result, object_id))
 
-    event_name = "service.get"
+From a user's perspective, the Event System is invisible unless the user
+wishes to extend the chain of handlers with their own code. Continuing with
+the service example from above:
 
-    provider.events.subscribe(event_name, priority=20000, callback=_pre_log)
-    provider.events.subscribe(event_name, priority=30000, callback=_post_log,
-                              result_callback=True)
+.. code-block:: python
+
+    from cloudbridge.cloud.factory import CloudProviderFactory, ProviderList
+
+    provider = CloudProviderFactory().create_provider(ProviderList.FIRST, {})
+    id = 'thisIsAnID'
+    obj = provider.service.get(id)
 
-    # Public get function
-    def get(object_id):
-        return provider.events.interceptable_call(event_name, priority=25000,
-                                                  callback=_get,
-                                                  object_id=object_id)
+However, if they wish to add their own logging interface, for example, they
+can do so without modifying CloudBridge code:
 
-In the above example, calling the public `get` function will be the
-equivalent of calling the below function:
 
 .. code-block:: python
 
-    def get(object_id):
-        _pre_log(object_id)
-        result = _get(object_id)
-        _post_log(callback_result=result, object_id)
-        return result
+    from cloudbridge.cloud.factory import CloudProviderFactory, ProviderList
+
+    provider = CloudProviderFactory().create_provider(ProviderList.FIRST, {})
+
+    ## I don't want to setup a logger, just want to print some messages for
+    ## debugging
+    def print_id(obj_id):
+        print(obj_id)
+
+    provider.service.subscribe_event("get", priority=2250, callback=print_id)
+
+    id1 = 'thisIsAnID'
+    id2 = 'thisIsAnID2'
+
+    ## The subscribed print function will get called every time the get
+    ## method is invoked
+    obj1 = provider.service.get(id1)
+    ## thisIsAnID
+    obj2 = provider.service.get(id2)
+    ## thisIsAnID2
+