Ticket #23: rename TestbedInstance -> TestbedController
[nepi.git] / src / nepi / core / testbed_impl.py
index 0f35a1e..fc8cf06 100644 (file)
@@ -6,9 +6,9 @@ from nepi.core.metadata import Metadata
 from nepi.util import validation
 from nepi.util.constants import AF_INET, AF_INET6, STATUS_UNDETERMINED, TIME_NOW
 
-class TestbedInstance(execute.TestbedInstance):
+class TestbedController(execute.TestbedController):
     def __init__(self, testbed_id, testbed_version):
-        super(TestbedInstance, self).__init__(testbed_id, testbed_version)
+        super(TestbedController, self).__init__(testbed_id, testbed_version)
         self._started = False
         # testbed attributes for validation
         self._attributes = None
@@ -45,14 +45,14 @@ class TestbedInstance(execute.TestbedInstance):
     def elements(self):
         return self._elements
 
-    def configure(self, name, value):
+    def defer_configure(self, name, value):
         if not self._attributes.has_attribute(name):
             raise RuntimeError("Invalid attribute %s for testbed" % name)
         # Validation
         self._attributes.set_attribute_value(name, value)
         self._configure[name] = value
 
-    def create(self, guid, factory_id):
+    def defer_create(self, guid, factory_id):
         if factory_id not in self._factories:
             raise RuntimeError("Invalid element type %s for testbed version %s" %
                     (factory_id, self._testbed_version))
@@ -61,7 +61,7 @@ class TestbedInstance(execute.TestbedInstance):
                     guid)
         self._create[guid] = factory_id
 
-    def create_set(self, guid, name, value):
+    def defer_create_set(self, guid, name, value):
         if not guid in self._create:
             raise RuntimeError("Element guid %d doesn't exist" % guid)
         factory_id = self._create[guid]
@@ -74,7 +74,7 @@ class TestbedInstance(execute.TestbedInstance):
             self._create_set[guid] = dict()
         self._create_set[guid][name] = value
 
-    def factory_set(self, guid, name, value):
+    def defer_factory_set(self, guid, name, value):
         if not guid in self._create:
             raise RuntimeError("Element guid %d doesn't exist" % guid)
         factory_id = self._create[guid]
@@ -87,7 +87,7 @@ class TestbedInstance(execute.TestbedInstance):
             self._factory_set[guid] = dict()
         self._factory_set[guid][name] = value
 
-    def connect(self, guid1, connector_type_name1, guid2, 
+    def defer_connect(self, guid1, connector_type_name1, guid2, 
             connector_type_name2):
         factory_id1 = self._create[guid1]
         factory_id2 = self._create[guid2]
@@ -109,7 +109,7 @@ class TestbedInstance(execute.TestbedInstance):
         self._connect[guid2][connector_type_name2][guid1] = \
                 connector_type_name1
 
-    def cross_connect(self, guid, connector_type_name, cross_guid, 
+    def defer_cross_connect(self, guid, connector_type_name, cross_guid, 
             cross_testbed_id, cross_factory_id, cross_connector_type_name):
         factory_id = self._create[guid]
         count = self._get_connection_count(guid, connector_type_name)
@@ -125,7 +125,7 @@ class TestbedInstance(execute.TestbedInstance):
                 (cross_guid, cross_testbed_id, cross_factory_id, 
                         cross_connector_type_name)
 
-    def add_trace(self, guid, trace_id):
+    def defer_add_trace(self, guid, trace_id):
         if not guid in self._create:
             raise RuntimeError("Element guid %d doesn't exist" % guid)
         factory_id = self._create[guid]
@@ -137,7 +137,7 @@ class TestbedInstance(execute.TestbedInstance):
             self._add_trace[guid] = list()
         self._add_trace[guid].append(trace_id)
 
-    def add_address(self, guid, family, address, netprefix, broadcast):
+    def defer_add_address(self, guid, address, netprefix, broadcast):
         if not guid in self._create:
             raise RuntimeError("Element guid %d doesn't exist" % guid)
         factory_id = self._create[guid]
@@ -145,17 +145,17 @@ class TestbedInstance(execute.TestbedInstance):
         if not factory.allow_addresses:
             raise RuntimeError("Element type '%s' doesn't support addresses" %
                     factory_id)
-        max_addresses = factory.get_attribute_value("MaxAddresses")
+            max_addresses = 1 # TODO: MAKE THIS PARAMETRIZABLE
         if guid in self._add_address:
             count_addresses = len(self._add_address[guid])
             if max_addresses == count_addresses:
                 raise RuntimeError("Element guid %d of type '%s' can't accept \
-                        more addresses" % (guid, family_id))
+                        more addresses" % (guid, factory_id))
         else:
             self._add_address[guid] = list()
-        self._add_address[guid].append((family, address, netprefix, broadcast))
+        self._add_address[guid].append((address, netprefix, broadcast))
 
-    def add_route(self, guid, destination, netprefix, nexthop):
+    def defer_add_route(self, guid, destination, netprefix, nexthop):
         if not guid in self._create:
             raise RuntimeError("Element guid %d doesn't exist" % guid)
         factory_id = self._create[guid]
@@ -167,8 +167,7 @@ class TestbedInstance(execute.TestbedInstance):
             self._add_route[guid] = list()
         self._add_route[guid].append((destination, netprefix, nexthop)) 
 
-    def do_setup(self):
-        raise NotImplementedError
+    #do_setup(self): NotImplementedError
 
     def do_create(self):
         guids = dict()
@@ -178,17 +177,14 @@ class TestbedInstance(execute.TestbedInstance):
                guids[factory_id] = list()
             guids[factory_id].append(guid)
         # create elements following the factory_id order
-        for factory_id in self._metadata.factories_order:
+        for factory_id in self._metadata.create_order:
             # omit the factories that have no element to create
             if factory_id not in guids:
                 continue
             factory = self._factories[factory_id]
             for guid in guids[factory_id]:
+                factory.create_function(self, guid)
                 parameters = self._get_parameters(guid)
-                factory_parameters = dict() if guid not in \
-                        self._factory_set else self._factory_set[guid]
-                factory.create_function(self, guid, parameters, 
-                        factory_parameters)
                 for name, value in parameters.iteritems():
                     self.set(TIME_NOW, guid, name, value)
 
@@ -212,7 +208,22 @@ class TestbedInstance(execute.TestbedInstance):
                         code_to_connect(self, element1, element2)
 
     def do_configure(self):
-        raise NotImplementedError
+        guids = dict()
+        # order guids (elements) according to factory_id
+        for guid, factory_id in self._create.iteritems():
+            if not factory_id in guids:
+               guids[factory_id] = list()
+            guids[factory_id].append(guid)
+        # configure elements following the factory_id order
+        for factory_id in self._metadata.configure_order:
+            # omit the factories that have no element to create
+            if factory_id not in guids:
+                continue
+            factory = self._factories[factory_id]
+            if not factory.configure_function:
+                continue
+            for guid in guids[factory_id]:
+                factory.configure_function(self, guid)
 
     def do_cross_connect(self):
         for guid, cross_connections in self._cross_connect.iteritems():
@@ -247,30 +258,98 @@ class TestbedInstance(execute.TestbedInstance):
             self._set[guid][time] = dict()
         self._set[guid][time][name] = value
 
-    def get(self, time, guid, name):
-        raise NotImplementedError
+    def box_get(self, time, guid, name):
+        """
+        Helper for subclasses, gets an attribute from box definitions
+        if available. Throws KeyError if the GUID wasn't created
+        through the defer_create interface, and AttributeError if the
+        attribute isn't available (doesn't exist or is design-only)
+        """
+        if not guid in self._create:
+            raise KeyError, "Element guid %d doesn't exist" % guid
+        factory_id = self._create[guid]
+        factory = self._factories[factory_id]
+        if not factory.box_attributes.has_attribute(name):
+            raise AttributeError, "Invalid attribute %s for element type %s" % (name, factory_id)
+        if self._started and factory.is_attribute_design_only(name):
+            raise AttributeError, "Attribute %s can only be queried during experiment design" % name
+        return factory.box_attributes.get_attribute_value(name)
+
+    #get: NotImplementedError
+
+    def box_get_route(self, guid, index, attribute):
+        """
+        Helper implementation for get_route, returns information
+        given to defer_add_route.
+        
+        Raises AttributeError if an invalid attribute is requested
+            or if the indexed routing rule does not exist.
+        
+        Raises KeyError if the GUID has not been seen by
+            defer_add_route
+        """
+        ATTRIBUTES = ['Destination', 'NetPrefix', 'NextHop']
+        
+        if attribute not in ATTRIBUTES:
+            raise AttributeError, "Attribute %r invalid for addresses of %r" % (attribute, guid)
+        
+        attribute_index = ATTRIBUTES.index(attribute)
+        
+        routes = self._add_route.get(guid)
+        if not routes:
+            raise KeyError, "GUID %r not found in %s" % (guid, self._testbed_id)
+        
+        if not (0 <= index < len(addresses)):
+            raise AttributeError, "GUID %r at %s does not have a routing entry #%s" % (
+                guid, self._testbed_id, index)
+        
+        return routes[index][attribute_index]
+
+    def box_get_address(self, guid, index, attribute='Address'):
+        """
+        Helper implementation for get_address, returns information
+        given to defer_add_address
+        
+        Raises AttributeError if an invalid attribute is requested
+            or if the indexed routing rule does not exist.
+        
+        Raises KeyError if the GUID has not been seen by
+            defer_add_address
+        """
+        ATTRIBUTES = ['Address', 'NetPrefix', 'Broadcast']
+        
+        if attribute not in ATTRIBUTES:
+            raise AttributeError, "Attribute %r invalid for addresses of %r" % (attribute, guid)
+        
+        attribute_index = ATTRIBUTES.index(attribute)
+        
+        addresses = self._add_address.get(guid)
+        if not addresses:
+            raise KeyError, "GUID %r not found in %s" % (guid, self._testbed_id)
+        
+        if not (0 <= index < len(addresses)):
+            raise AttributeError, "GUID %r at %s does not have an address #%s" % (
+                guid, self._testbed_id, index)
+        
+        return addresses[index][attribute_index]
+
 
     def start(self, time = TIME_NOW):
         for guid, factory_id in self._create.iteritems():
             factory = self._factories[factory_id]
             start_function = factory.start_function
             if start_function:
-                traces = self._traces(guid)
-                parameters = self._parameters(guid)
-                start_function(self, guid, parameters, traces)
+                start_function(self, guid)
         self._started = True
 
-    def action(self, time, guid, action):
-        raise NotImplementedError
+    #action: NotImplementedError
 
     def stop(self, time = TIME_NOW):
         for guid, factory_id in self._create.iteritems():
             factory = self._factories[factory_id]
             stop_function = factory.stop_function
             if stop_function:
-                traces = self._traces(guid)
-                parameters = self._parameters(guid)
-                stop_function(self, guid, parameters, traces)
+                stop_function(self, guid)
 
     def status(self, guid):
         if not guid in self._create:
@@ -279,14 +358,28 @@ class TestbedInstance(execute.TestbedInstance):
         factory = self._factories[factory_id]
         status_function = factory.status_function
         if status_function:
-            return status_function(self, guid, parameters, traces)
+            return status_function(self, guid)
         return STATUS_UNDETERMINED
 
-    def trace(self, guid, trace_id):
+    def trace(self, guid, trace_id, attribute='value'):
+        if attribute == 'value':
+            fd = open("%s" % self.trace_filename(guid, trace_id), "r")
+            content = fd.read()
+            fd.close()
+        elif attribute == 'path':
+            content = self.trace_filename(guid, trace_id)
+        else:
+            content = None
+        return content
+
+    def trace_filename(self, guid, trace_id):
+        """
+        Return a trace's file path, for TestbedController's default 
+        implementation of trace()
+        """
         raise NotImplementedError
 
-    def shutdown(self):
-        raise NotImplementedError
+    #shutdown: NotImplementedError
 
     def get_connected(self, guid, connector_type_name, 
             other_connector_type_name):