enforce tags system
[nepi.git] / src / nepi / core / design.py
index 6347510..602667c 100644 (file)
@@ -6,43 +6,14 @@ Experiment design API
 """
 
 from nepi.core.attributes import AttributesMap, Attribute
-from nepi.core.connector import ConnectorTypeBase
 from nepi.core.metadata import Metadata
 from nepi.util import validation
 from nepi.util.guid import GuidGenerator
 from nepi.util.graphical_info import GraphicalInfo
 from nepi.util.parser._xml import XmlExperimentParser
+from nepi.util.tags import Taggable
 import sys
 
-class ConnectorType(ConnectorTypeBase):
-    def __init__(self, testbed_id, factory_id, name, help, max = -1, min = 0):
-        super(ConnectorType, self).__init__(testbed_id, factory_id, name, max, min)
-        
-        # help -- help text
-        self._help = help
-        
-        # allowed_connections -- keys in the dictionary correspond to the 
-        # connector_type_id for possible connections. The value indicates if
-        # the connection is allowed accros different testbed instances
-        self._allowed_connections = dict()
-
-    @property
-    def help(self):
-        return self._help
-
-    def add_allowed_connection(self, testbed_id, factory_id, name, can_cross):
-        type_id = self.make_connector_type_id(testbed_id, factory_id, name)
-        self._allowed_connections[type_id] = can_cross
-
-    def can_connect(self, connector_type_id, testbed_guid1, testbed_guid2):
-        for lookup_type_id in self._type_resolution_order(connector_type_id):
-            if lookup_type_id in self._allowed_connections:
-                can_cross = self._allowed_connections[lookup_type_id]
-                if can_cross or (testbed_guid1 == testbed_guid2):
-                    return True
-        else:
-            return False
-
 class Connector(object):
     """A Connector sepcifies the connection points in an Object"""
     def __init__(self, box, connector_type):
@@ -100,11 +71,12 @@ class Connector(object):
             return False
         if self.is_connected(connector):
             return False
-        connector_type_id = connector.connector_type.connector_type_id
+        (testbed_id, factory_id, name) = connector.connector_type.connector_type_id
         testbed_guid1 = self.box.testbed_guid
         testbed_guid2 = connector.box.testbed_guid
-        return self.connector_type.can_connect(connector_type_id, 
-                testbed_guid1, testbed_guid2)
+        must_cross = (testbed_guid1 != testbed_guid2)
+        return self.connector_type.can_connect(testbed_id, factory_id, name,
+                must_cross)
 
     def destroy(self):
         for connector in self.connections:
@@ -112,15 +84,15 @@ class Connector(object):
         self._box = self._connectors = None
 
 class Trace(AttributesMap):
-    def __init__(self, trace_id, help, enabled = False):
+    def __init__(self, name, help, enabled = False):
         super(Trace, self).__init__()
-        self._trace_id = trace_id
+        self._name = name
         self._help = help       
         self._enabled = enabled
     
     @property
-    def trace_id(self):
-        return self._trace_id
+    def name(self):
+        return self._name
 
     @property
     def help(self):
@@ -139,12 +111,6 @@ class Trace(AttributesMap):
 class Address(AttributesMap):
     def __init__(self):
         super(Address, self).__init__()
-        self.add_attribute(name = "AutoConfigure", 
-                help = "If set, this address will automatically be assigned", 
-                type = Attribute.BOOL,
-                value = False,
-                flags = Attribute.DesignOnly,
-                validation_function = validation.is_bool)
         self.add_attribute(name = "Address",
                 help = "Address number", 
                 type = Attribute.STRING,
@@ -168,7 +134,7 @@ class Route(AttributesMap):
         self.add_attribute(name = "Destination", 
                 help = "Network destintation",
                 type = Attribute.STRING, 
-                validation_function = validation.is_ip_address)
+                validation_function = validation.is_ref_address)
         self.add_attribute(name = "NetPrefix",
                 help = "Network destination prefix", 
                 type = Attribute.INTEGER, 
@@ -180,9 +146,15 @@ class Route(AttributesMap):
                 help = "Address for the next hop", 
                 type = Attribute.STRING,
                 flags = Attribute.HasNoDefaultValue,
-                validation_function = validation.is_ip_address)
+                validation_function = validation.is_ref_address)
+        self.add_attribute(name = "Metric",
+                help = "Routing metric", 
+                type = Attribute.INTEGER,
+                value = 0,
+                flags = Attribute.HasNoDefaultValue,
+                validation_function = validation.is_integer)
 
-class Box(AttributesMap):
+class Box(AttributesMap, Taggable):
     def __init__(self, guid, factory, testbed_guid, container = None):
         super(Box, self).__init__()
         # guid -- global unique identifier
@@ -195,8 +167,6 @@ class Box(AttributesMap):
         self._container = container
         # traces -- list of available traces for the box
         self._traces = dict()
-        # tags -- list of tags for the box
-        self._tags = list()
         # connectors -- list of available connectors for the box
         self._connectors = dict()
         # factory_attributes -- factory attributes for box construction
@@ -207,17 +177,17 @@ class Box(AttributesMap):
         for connector_type in factory.connector_types:
             connector = Connector(self, connector_type)
             self._connectors[connector_type.name] = connector
-        for trace in factory.traces:
-            tr = Trace(trace.trace_id, trace.help, trace.enabled)
-            self._traces[trace.trace_id] = tr
+        for (name, help, enabled) in factory.traces:
+            trace = Trace(name, help, enabled)
+            self._traces[name] = trace
         for tag_id in factory.tags:
-            self._tags.append(tag_id)
+            self.add_tag(tag_id)
         for attr in factory.box_attributes.attributes:
             self.add_attribute(attr.name, attr.help, attr.type, attr.value, 
                     attr.range, attr.allowed, attr.flags, 
                     attr.validation_function, attr.category)
         for attr in factory.attributes:
-            if attr.modified:
+            if attr.modified or attr.invisible:
                 self._factory_attributes[attr.name] = attr.value
 
     def __str__(self):
@@ -248,17 +218,13 @@ class Box(AttributesMap):
         return self._traces.values()
 
     @property
-    def trace_names(self):
+    def traces_list(self):
         return self._traces.keys()
 
     @property
     def factory_attributes(self):
         return self._factory_attributes
 
-    @property
-    def tags(self):
-        return self._tags
-
     def trace_help(self, trace_id):
         return self._traces[trace_id].help
 
@@ -282,211 +248,6 @@ class Box(AttributesMap):
             t.destroy()
         self._connectors = self._traces = self._factory_attributes = None
 
-class AddressableMixin(object):
-    def __init__(self, guid, factory, testbed_guid, container = None):
-        super(AddressableMixin, self).__init__(guid, factory, testbed_guid, 
-                container)
-        self._max_addresses = 1 # TODO: How to make this configurable!
-        self._addresses = list()
-
-    @property
-    def addresses(self):
-        return self._addresses
-
-    @property
-    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.")
-        address = Address()
-        self._addresses.append(address)
-        return address
-
-    def delete_address(self, address):
-        self._addresses.remove(address)
-        del address
-
-    def destroy(self):
-        super(UserAddressableMixin, self).destroy()
-        for address in list(self.addresses):
-            self.delete_address(address)
-        self._addresses = None
-
-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)
-        return route
-
-    def delete_route(self, route):
-        self._routes.remove(route)
-        del route
-
-    def destroy(self):
-        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):
-    _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 = 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._tags = 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):
-        return self._factory_id
-
-    @property
-    def allow_addresses(self):
-        return self._allow_addresses
-
-    @property
-    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
-
-    @property
-    def category(self):
-        return self._category
-
-    @property
-    def connector_types(self):
-        return self._connector_types
-
-    @property
-    def traces(self):
-        return self._traces
-
-    @property
-    def tags(self):
-        return self._tags
-    
-    @property
-    def box_attributes(self):
-        return self._box_attributes
-
-    def add_connector_type(self, connector_type):
-        self._connector_types.append(connector_type)
-
-    def add_trace(self, trace_id, help, enabled = False):
-        trace = Trace(trace_id, help, enabled)
-        self._traces.append(trace)
-
-    def add_tag(self, tag_id):
-        self._tags.append(tag_id)
-
-    def add_box_attribute(self, name, help, type, value = None, range = None,
-        allowed = None, flags = Attribute.NoFlags, validation_function = None,
-        category = None):
-        self._box_attributes.add_attribute(name, help, type, value, range,
-                allowed, flags, validation_function, category)
-
-    def create(self, guid, testbed_description):
-        return self._factory(guid, self, testbed_description.guid)
-
-    def destroy(self):
-        super(Factory, self).destroy()
-        self._connector_types = None
-
 class FactoriesProvider(object):
     def __init__(self, testbed_id, testbed_version):
         super(FactoriesProvider, self).__init__()
@@ -495,7 +256,7 @@ class FactoriesProvider(object):
         self._factories = dict()
 
         metadata = Metadata(testbed_id, testbed_version) 
-        for factory in metadata.build_design_factories():
+        for factory in metadata.build_factories():
             self.add_factory(factory)
 
     @property