Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cloudbridge/cloud/base/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ def debug_mode(self):


class BaseCloudProvider(CloudProvider):
def __init__(self, config):
def __init__(self, config, middleware_manager=None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, better than sending in middleware as a list.

self._config = BaseConfiguration(config)
self._config_parser = ConfigParser()
self._config_parser.read(CloudBridgeConfigLocations)
self._middleware = SimpleMiddlewareManager()
self._middleware = middleware_manager or SimpleMiddlewareManager()
self.add_required_middleware()

@property
Expand Down
22 changes: 11 additions & 11 deletions cloudbridge/cloud/base/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class BaseCloudService(CloudService):
STANDARD_EVENT_PRIORITY = 2500

def __init__(self, provider):
self._service_event_pattern = "provider"
self._service_event_pattern = provider.PROVIDER_ID
self._provider = provider
# discover and register all middleware
provider.middleware.add(self)
Expand Down Expand Up @@ -78,7 +78,7 @@ def __init__(self, provider):
super(BaseVMFirewallService, self).__init__(provider)
self._service_event_pattern += ".security.vm_firewalls"

@dispatch(event="provider.security.vm_firewalls.find",
@dispatch(event="security.vm_firewalls.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, **kwargs):
obj_list = self
Expand Down Expand Up @@ -107,7 +107,7 @@ def __init__(self, provider):
def provider(self):
return self._provider

@dispatch(event="provider.security.vm_firewall_rules.get",
@dispatch(event="security.vm_firewall_rules.get",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def get(self, firewall, rule_id):
matches = [rule for rule in firewall.rules if rule.id == rule_id]
Expand All @@ -116,7 +116,7 @@ def get(self, firewall, rule_id):
else:
return None

@dispatch(event="provider.security.vm_firewall_rules.find",
@dispatch(event="security.vm_firewall_rules.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, firewall, **kwargs):
obj_list = firewall.rules
Expand Down Expand Up @@ -157,7 +157,7 @@ def __init__(self, provider):

# Generic find will be used for providers where we have not implemented
# provider-specific querying for find method
@dispatch(event="provider.storage.buckets.find",
@dispatch(event="storage.buckets.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, **kwargs):
obj_list = self
Expand Down Expand Up @@ -211,13 +211,13 @@ def __init__(self, provider):
super(BaseVMTypeService, self).__init__(provider)
self._service_event_pattern += ".compute.vm_types"

@dispatch(event="provider.compute.vm_types.get",
@dispatch(event="compute.vm_types.get",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def get(self, vm_type_id):
vm_type = (t for t in self if t.id == vm_type_id)
return next(vm_type, None)

@dispatch(event="provider.compute.vm_types.find",
@dispatch(event="compute.vm_types.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, **kwargs):
obj_list = self
Expand All @@ -233,7 +233,7 @@ def __init__(self, provider):
super(BaseRegionService, self).__init__(provider)
self._service_event_pattern += ".compute.regions"

@dispatch(event="provider.compute.regions.find",
@dispatch(event="compute.regions.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, **kwargs):
obj_list = self
Expand Down Expand Up @@ -272,7 +272,7 @@ def get_or_create_default(self):
return self.provider.networking.networks.create(
BaseNetwork.CB_DEFAULT_NETWORK_LABEL, '10.0.0.0/16')

@dispatch(event="provider.networking.networks.find",
@dispatch(event="networking.networks.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, **kwargs):
obj_list = self
Expand All @@ -296,7 +296,7 @@ def __init__(self, provider):
super(BaseSubnetService, self).__init__(provider)
self._service_event_pattern += ".networking.subnets"

@dispatch(event="provider.networking.subnets.find",
@dispatch(event="networking.subnets.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, network=None, **kwargs):
if not network:
Expand Down Expand Up @@ -360,7 +360,7 @@ def __init__(self, provider):
def provider(self):
return self._provider

@dispatch(event="provider.networking.floating_ips.find",
@dispatch(event="networking.floating_ips.find",
priority=BaseCloudService.STANDARD_EVENT_PRIORITY)
def find(self, gateway, **kwargs):
obj_list = gateway.floating_ips
Expand Down
200 changes: 199 additions & 1 deletion cloudbridge/cloud/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import pkgutil
from collections import defaultdict

from pyeventsystem.events import SimpleEventDispatcher
from pyeventsystem.interfaces import Middleware
from pyeventsystem.middleware import AutoDiscoveredMiddleware
from pyeventsystem.middleware import SimpleMiddlewareManager

from cloudbridge.cloud import providers
from cloudbridge.cloud.interfaces import CloudProvider
from cloudbridge.cloud.interfaces import TestMockHelperMixin
Expand All @@ -12,6 +17,192 @@
log = logging.getLogger(__name__)


# Todo: Move to pyeventsystem if we're keeping this logic
class ParentMiddlewareGenerator(SimpleMiddlewareManager):

def __init__(self, event_manager=None):
super(ParentMiddlewareGenerator, self).__init__(event_manager)
self.middleware_constructors = []

def add_middleware_class(self, middleware_class, *args, **kwargs):
self.middleware_constructors.append((middleware_class, args, kwargs))

def remove_middleware_class(self, middleware_class, *args, **kwargs):
self.middleware_constructors.remove((middleware_class, args, kwargs))

def get_directly_subscribed_handlers(self):
all_handlers = []
# Todo: Expose this better in pyeventsystem library
event_dict = self.events._SimpleEventDispatcher__events
for key in event_dict.keys():
for h in event_dict[key]:
all_handlers.append((h,
h.event_pattern,
h.priority,
h.callback))
middleware_handlers = []
for m in self.middleware_list:
obj = (m.obj_to_discover if isinstance(m, AutoDiscoveredMiddleware)
else m)
handlers = m.discover_handlers(obj)
for h in handlers:
middleware_handlers.append((h.event_pattern, h.priority,
h.callback))
direct_subs = [h for h, e, p, c in all_handlers
if (e, p, c) not in middleware_handlers]
return direct_subs

def generate_child_manager(self, namespace=None):
event_dispatcher = None
if namespace:
event_dispatcher = NamespacedEventDispatcher(namespace)
manager = ChildMiddlewareManager(self, event_dispatcher)
manager.inherit_handlers()
return manager


# TODO: Move to pyeventsystem if we're keeping
class NamespacedEventDispatcher(SimpleEventDispatcher):

def __init__(self, namespace):
super(NamespacedEventDispatcher, self).__init__()
self._namespace = namespace

@property
def namespace(self):
return self._namespace

def get_handlers_for_event(self, event):
event = ".".join((self._namespace, event))
return self.get_handlers_for_event_direct(event)

def _create_handler_cache(self, event):
event = ".".join((self._namespace, event))
return self._create_handler_cache_direct(event)

def _invalidate_cache(self, event_pattern):
event_pattern = ".".join((self._namespace, event_pattern))
return self._invalidate_cache_direct(event_pattern)

def dispatch(self, sender, event, *args, **kwargs):
event = ".".join((self._namespace, event))
return self.dispatch_direct(sender, event, *args, **kwargs)

def observe(self, event_pattern, priority, callback):
event_pattern = ".".join((self._namespace, event_pattern))
return self.observe_direct(event_pattern, priority, callback)

def intercept(self, event_pattern, priority, callback):
event_pattern = ".".join((self._namespace, event_pattern))
return self.intercept_direct(event_pattern, priority, callback)

def implement(self, event_pattern, priority, callback):
event_pattern = ".".join((self._namespace, event_pattern))
return self.implement_direct(event_pattern, priority, callback)

def observe_direct(self, event_pattern, priority, callback):
return super(NamespacedEventDispatcher, self).observe(event_pattern,
priority,
callback)

def intercept_direct(self, event_pattern, priority, callback):
return super(NamespacedEventDispatcher, self).intercept(event_pattern,
priority,
callback)

def implement_direct(self, event_pattern, priority, callback):
return super(NamespacedEventDispatcher, self).implement(event_pattern,
priority,
callback)

def dispatch_direct(self, sender, event, *args, **kwargs):
return super(NamespacedEventDispatcher, self).dispatch(sender, event,
*args, **kwargs)

def get_handlers_for_event_direct(self, event):
return super(NamespacedEventDispatcher, self).get_handlers_for_event(
event)

def _create_handler_cache_direct(self, event):
return super(NamespacedEventDispatcher, self)._create_handler_cache(
event)

def _invalidate_cache_direct(self, event_pattern):
return super(NamespacedEventDispatcher, self)._invalidate_cache(
event_pattern)


# TODO: Move to pyeventsystem if we're keeping
class ChildMiddlewareManager(SimpleMiddlewareManager):

def __init__(self, parent_manager, event_manager=None):
super(ChildMiddlewareManager, self).__init__(event_manager)
self._parent_manager = parent_manager
self._inherited = {}

def inherit_handlers(self):
self.remove_inherited_handlers()
middleware_list = []
for middleware in self._parent_manager.middleware_list:
added = self.add_direct(middleware)
middleware_list.append(added)
for constructor, args, kwargs in (self._parent_manager
.middleware_constructors):
m = constructor(*args, **kwargs)
added = self.add_direct(m)
middleware_list.append(added)
self._inherited['middleware_list'] = middleware_list
handler_list = []
for handler in self._parent_manager.get_directly_subscribed_handlers():
new_handler = handler.__class__(handler.event_pattern,
handler.priority,
handler.callback)
handler_list.append(new_handler)
self.events.subscribe(new_handler)
self._inherited['handler_list'] = handler_list

def remove_inherited_handlers(self):
for m in self._inherited.get("middleware_list", []):
self.remove(m)

for h in self._inherited.get("handler_list", []):
self.events.unsubscribe(h)

self._inherited = {}

@property
def parent_manager(self):
return self._parent_manager

def add_direct(self, middleware):
return super(ChildMiddlewareManager, self).add(middleware)

def add(self, middleware):
Copy link
Contributor

@nuwang nuwang Feb 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should provide dual semantics here - one for shared middleware objects, and another for independent middleware, just to keep it simple. I'm currently leaning in favour of the factory namespacing child event dispatchers, and forwarding them as necessary, so that we still maintain event isolation within a given provider. I think we probably just need to avoid a scenario where providers are unwittingly subscribing to events outside of the provider.

if isinstance(middleware, Middleware):
m = middleware
m.events = self.events
discovered_handlers = m.discover_handlers(m)
else:
m = AutoDiscoveredMiddleware(middleware)
m.events = self.events
discovered_handlers = m.discover_handlers(m.obj_to_discover)

# Rewrap handlers with namespaced event pattern if the event dispatcher
# is namespaced
if isinstance(self.events, NamespacedEventDispatcher):
for handler in discovered_handlers:
event_pattern = ".".join((self.events.namespace,
handler.event_pattern))
new_handler = handler.__class__(event_pattern,
handler.priority,
handler.callback)
discovered_handlers.remove(handler)
discovered_handlers.append(new_handler)
m.add_handlers(discovered_handlers)
self.middleware_list.append(m)
return m


class ProviderList(object):
AWS = 'aws'
AZURE = 'azure'
Expand All @@ -27,9 +218,14 @@ class CloudProviderFactory(object):
"""

def __init__(self):
self._middleware = ParentMiddlewareGenerator()
self.provider_list = defaultdict(dict)
log.debug("Providers List: %s", self.provider_list)

@property
def middleware(self):
return self._middleware

def register_provider_class(self, cls):
"""
Registers a provider class with the factory. The class must
Expand Down Expand Up @@ -136,7 +332,9 @@ def create_provider(self, name, config):
'A provider with name {0} could not be'
' found'.format(name))
log.debug("Created '%s' provider", name)
return provider_class(config)
namespaced_middleware = self.middleware.generate_child_manager(name)
return provider_class(config,
namespaced_middleware)

def get_provider_class(self, name):
"""
Expand Down
4 changes: 2 additions & 2 deletions cloudbridge/cloud/providers/aws/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class AWSCloudProvider(BaseCloudProvider):
PROVIDER_ID = 'aws'
AWS_INSTANCE_DATA_DEFAULT_URL = "http://cloudve.org/cb-aws-vmtypes.json"

def __init__(self, config):
super(AWSCloudProvider, self).__init__(config)
def __init__(self, config, middleware_manager=None):
super(AWSCloudProvider, self).__init__(config, middleware_manager)

# Initialize cloud connection fields
# These are passed as-is to Boto
Expand Down
Loading