From 5e0cfb796f2e38bb43c8d3284e5a6238c7f70bad Mon Sep 17 00:00:00 2001 From: Claudio-Daniel Freire Date: Wed, 27 Apr 2011 15:11:12 +0200 Subject: [PATCH] Ticket #28: Refactor Box classes to use mixins, and provide read-only routes/addesses --- src/nepi/core/design.py | 109 ++++++++++++++++---- src/nepi/core/execute.py | 17 ++- src/nepi/core/metadata.py | 19 ++-- src/nepi/testbeds/planetlab/metadata_v01.py | 6 +- 4 files changed, 120 insertions(+), 31 deletions(-) diff --git a/src/nepi/core/design.py b/src/nepi/core/design.py index 3ef4a0d3..cd0c3841 100644 --- a/src/nepi/core/design.py +++ b/src/nepi/core/design.py @@ -289,9 +289,9 @@ class Box(AttributesMap): t.destroy() self._connectors = self._traces = self._factory_attributes = None -class AddressableBox(Box): +class AddressableMixin(object): def __init__(self, guid, factory, testbed_guid, container = None): - super(AddressableBox, self).__init__(guid, factory, testbed_guid, + super(AddressableMixin, self).__init__(guid, factory, testbed_guid, container) self._max_addresses = 1 # TODO: How to make this configurable! self._addresses = list() @@ -304,6 +304,11 @@ class AddressableBox(Box): def max_addresses(self): return self._max_addresses +class UserAddressableMixin(AddressableMixin): + def __init__(self, guid, factory, testbed_guid, container = None): + super(UserAddressableMixin, self).__init__(guid, factory, testbed_guid, + container) + def add_address(self): if len(self._addresses) == self.max_addresses: raise RuntimeError("Maximun number of addresses for this box reached.") @@ -316,20 +321,26 @@ class AddressableBox(Box): del address def destroy(self): - super(AddressableBox, self).destroy() - for address in self.addresses: + super(UserAddressableMixin, self).destroy() + for address in list(self.addresses): self.delete_address(address) self._addresses = None -class RoutingTableBox(Box): - def __init__(self, guid, factory, container = None): - super(RoutingTableBox, self).__init__(guid, factory, container) +class RoutableMixin(object): + def __init__(self, guid, factory, testbed_guid, container = None): + super(RoutableMixin, self).__init__(guid, factory, testbed_guid, + container) self._routes = list() @property def routes(self): return self._routes +class UserRoutableMixin(RoutableMixin): + def __init__(self, guid, factory, testbed_guid, container = None): + super(UserRoutableMixin, self).__init__(guid, factory, testbed_guid, + container) + def add_route(self): route = Route() self._routes.append(route) @@ -340,23 +351,80 @@ class RoutingTableBox(Box): del route def destroy(self): - super(RoutingTableBox, self).destroy() - for route in self.routes: + super(UserRoutableMixin, self).destroy() + for route in list(self.routes): self.delete_route(route) self._route = None +def MixIn(MyClass, MixIn): + # Mixins are installed BEFORE "Box" because + # Box inherits from non-cooperative classes, + # so the MRO chain gets broken when it gets + # to Box. + + # Install mixin + MyClass.__bases__ = (MixIn,) + MyClass.__bases__ + + # Add properties + # Somehow it doesn't work automatically + for name in dir(MixIn): + prop = getattr(MixIn,name,None) + if isinstance(prop, property): + setattr(MyClass, name, prop) + + # Update name + MyClass.__name__ = MyClass.__name__.replace( + 'Box', + MixIn.__name__.replace('MixIn','')+'Box', + 1) + class Factory(AttributesMap): - def __init__(self, factory_id, allow_addresses = False, - allow_routes = False, Help = None, category = None): + _box_class_cache = {} + + def __init__(self, factory_id, + allow_addresses = False, has_addresses = False, + allow_routes = False, has_routes = False, + Help = None, category = None): super(Factory, self).__init__() self._factory_id = factory_id - self._allow_addresses = (allow_addresses == True) - self._allow_routes = (allow_routes == True) + self._allow_addresses = bool(allow_addresses) + self._allow_routes = bool(allow_routes) + self._has_addresses = bool(allow_addresses) or self._allow_addresses + self._has_routes = bool(allow_routes) or self._allow_routes self._help = help self._category = category self._connector_types = list() self._traces = list() self._box_attributes = AttributesMap() + + if not self._has_addresses and not self._has_routes: + self._factory = Box + else: + addresses = 'w' if self._allow_addresses else ('r' if self._has_addresses else '-') + routes = 'w' if self._allow_routes else ('r' if self._has_routes else '-') + key = addresses+routes + + if key in self._box_class_cache: + self._factory = self._box_class_cache[key] + else: + # Create base class + class _factory(Box): + def __init__(self, guid, factory, testbed_guid, container = None): + super(_factory, self).__init__(guid, factory, testbed_guid, container) + + # Add mixins, one by one + if allow_addresses: + MixIn(_factory, UserAddressableMixin) + elif has_addresses: + MixIn(_factory, AddressableMixin) + + if allow_routes: + MixIn(_factory, UserRoutableMixin) + elif has_routes: + MixIn(_factory, RoutableMixin) + + # Put into cache + self._box_class_cache[key] = self._factory = _factory @property def factory_id(self): @@ -370,6 +438,14 @@ class Factory(AttributesMap): def allow_routes(self): return self._allow_routes + @property + def has_addresses(self): + return self._has_addresses + + @property + def has_routes(self): + return self._has_routes + @property def help(self): return self._help @@ -403,12 +479,7 @@ class Factory(AttributesMap): allowed, flags, validation_function) def create(self, guid, testbed_description): - if self._allow_addresses: - return AddressableBox(guid, self, testbed_description.guid) - elif self._allow_routes: - return RoutingTableBox(guid, self, testbed_description.guid) - else: - return Box(guid, self, testbed_description.guid) + return self._factory(guid, self, testbed_description.guid) def destroy(self): super(Factory, self).destroy() diff --git a/src/nepi/core/execute.py b/src/nepi/core/execute.py index 88707423..f977802b 100644 --- a/src/nepi/core/execute.py +++ b/src/nepi/core/execute.py @@ -99,11 +99,14 @@ class Factory(AttributesMap): def __init__(self, factory_id, create_function, start_function, stop_function, status_function, configure_function, preconfigure_function, - allow_addresses = False, allow_routes = False): + allow_addresses = False, has_addresses = False, + allow_routes = False, has_routes = False): super(Factory, self).__init__() self._factory_id = factory_id - self._allow_addresses = (allow_addresses == True) - self._allow_routes = (allow_routes == True) + self._allow_addresses = bool(allow_addresses) + self._allow_routes = bool(allow_routes) + self._has_addresses = bool(has_addresses) or self._allow_addresses + self._has_routes = bool(has_routes) or self._allow_routes self._create_function = create_function self._start_function = start_function self._stop_function = stop_function @@ -126,6 +129,14 @@ class Factory(AttributesMap): def allow_routes(self): return self._allow_routes + @property + def has_addresses(self): + return self._has_addresses + + @property + def has_routes(self): + return self._has_routes + @property def box_attributes(self): return self._box_attributes diff --git a/src/nepi/core/metadata.py b/src/nepi/core/metadata.py index 2645b8d5..93a31c71 100644 --- a/src/nepi/core/metadata.py +++ b/src/nepi/core/metadata.py @@ -86,6 +86,8 @@ class VersionedMetadataInfo(object): factory_id: dict({ "allow_addresses": whether the box allows adding IP addresses, "allow_routes": wether the box allows adding routes, + "has_addresses": whether the box allows obtaining IP addresses, + "has_routes": wether the box allows obtaining routes, "help": help text, "category": category the element belongs to, "create_function": function for element instantiation, @@ -190,11 +192,13 @@ class Metadata(object): for factory_id, info in self._metadata.factories_info.iteritems(): help = info["help"] category = info["category"] - allow_addresses = info["allow_addresses"] \ - if "allow_addresses" in info else False - allow_routes = info["allow_routes"] \ - if "allow_routes" in info else False - factory = Factory(factory_id, allow_addresses, allow_routes, + allow_addresses = info.get("allow_addresses", False) + allow_routes = info.get("allow_routes", False) + has_addresses = info.get("has_addresses", False) + has_routes = info.get("has_routes", False) + factory = Factory(factory_id, + allow_addresses, has_addresses, + allow_routes, has_routes, help, category) # standard attributes @@ -222,10 +226,13 @@ class Metadata(object): preconfigure_function = info.get("preconfigure_function") allow_addresses = info.get("allow_addresses", False) allow_routes = info.get("allow_routes", False) + has_addresses = info.get("has_addresses", False) + has_routes = info.get("has_routes", False) factory = Factory(factory_id, create_function, start_function, stop_function, status_function, configure_function, preconfigure_function, - allow_addresses, allow_routes) + allow_addresses, has_addresses, + allow_routes, has_routes) # standard attributes self._add_standard_attributes(factory, info, False, True, diff --git a/src/nepi/testbeds/planetlab/metadata_v01.py b/src/nepi/testbeds/planetlab/metadata_v01.py index b0b0c474..4fead345 100644 --- a/src/nepi/testbeds/planetlab/metadata_v01.py +++ b/src/nepi/testbeds/planetlab/metadata_v01.py @@ -173,7 +173,7 @@ def configure_nodeiface(testbed_instance, guid): # Cannot explicitly configure addresses if guid in testbed_instance._add_address: - del testbed_instance._add_address[guid] + raise ValueError, "Cannot explicitly set address of public PlanetLab interface" # Get siblings node_guid = testbed_instance.get_connected(guid, "node", "devs")[0] @@ -196,7 +196,7 @@ def configure_tuniface(testbed_instance, guid): addresses = testbed_instance._add_address[guid] for address in addresses: (address, netprefix, broadcast) = address - raise NotImplementedError, "C'mon... TUNs are hard..." + element.add_address(address, netprefix, broadcast) # Do some validations element.validate() @@ -637,7 +637,7 @@ factories_info = dict({ "connector_types": ["devs", "apps", "pipes"] }), NODEIFACE: dict({ - "allow_addresses": True, + "has_addresses": True, "help": "External network interface - they cannot be brought up or down, and they MUST be connected to the internet.", "category": "devices", "create_function": create_nodeiface, -- 2.47.0