Added attribute MaxAddresses for interface factories to design
[nepi.git] / src / nepi / core / testbed_impl.py
index b34b8fe..291a24b 100644 (file)
@@ -4,22 +4,18 @@
 from nepi.core import execute
 from nepi.core.metadata import Metadata
 from nepi.util import validation
-from nepi.util.constants import STATUS_UNDETERMINED, TIME_NOW, \
-    TESTBED_STATUS_ZERO, \
-    TESTBED_STATUS_SETUP, \
-    TESTBED_STATUS_CREATED, \
-    TESTBED_STATUS_CONNECTED, \
-    TESTBED_STATUS_CROSS_CONNECTED, \
-    TESTBED_STATUS_CONFIGURED, \
-    TESTBED_STATUS_STARTED, \
-    TESTBED_STATUS_STOPPED
+from nepi.util.constants import TIME_NOW, \
+        ApplicationStatus as AS, \
+        TestbedStatus as TS, \
+        CONNECTION_DELAY
 
 import collections
+import copy
 
 class TestbedController(execute.TestbedController):
     def __init__(self, testbed_id, testbed_version):
         super(TestbedController, self).__init__(testbed_id, testbed_version)
-        self._status = TESTBED_STATUS_ZERO
+        self._status = TS.STATUS_ZERO
         # testbed attributes for validation
         self._attributes = None
         # element factories for validation
@@ -175,7 +171,7 @@ class TestbedController(execute.TestbedController):
             self._add_address[guid] = list()
         self._add_address[guid].append((address, netprefix, broadcast))
 
-    def defer_add_route(self, guid, destination, netprefix, nexthop):
+    def defer_add_route(self, guid, destination, netprefix, nexthop, metric = 0):
         if not guid in self._create:
             raise RuntimeError("Element guid %d doesn't exist" % guid)
         factory = self._get_factory(guid)
@@ -184,12 +180,12 @@ class TestbedController(execute.TestbedController):
                     factory.factory_id)
         if not guid in self._add_route:
             self._add_route[guid] = list()
-        self._add_route[guid].append((destination, netprefix, nexthop)) 
+        self._add_route[guid].append((destination, netprefix, nexthop, metric)) 
 
     def do_setup(self):
         self._root_directory = self._attributes.\
             get_attribute_value("rootDirectory")
-        self._status = TESTBED_STATUS_SETUP
+        self._status = TS.STATUS_SETUP
 
     def do_create(self):
         def set_params(self, guid):
@@ -201,39 +197,50 @@ class TestbedController(execute.TestbedController):
             'create_function',
             self._metadata.create_order,
             postaction = set_params )
-        self._status = TESTBED_STATUS_CREATED
+        self._status = TS.STATUS_CREATED
 
     def _do_connect(self, init = True):
-        for guid1, connections in self._connect.iteritems():
-            factory1 = self._get_factory(guid1)
-            for connector_type_name1, connections2 in connections.iteritems():
-                connector_type1 = factory1.connector_type(connector_type_name1)
-                for guid2, connector_type_name2 in connections2.iteritems():
-                    factory_id2 = self._create[guid2]
-                    # Connections are executed in a "From -> To" direction only
-                    # This explicitly ignores the "To -> From" (mirror) 
-                    # connections of every connection pair.
-                    if init:
-                        connect_code = connector_type1.connect_to_init_code(
-                                self._testbed_id, factory_id2, 
-                                connector_type_name2,
-                                False)
-                    else:
-                        connect_code = connector_type1.connect_to_compl_code(
-                                self._testbed_id, factory_id2, 
-                                connector_type_name2,
-                                False)
-                    if connect_code:
-                        connect_code(self, guid1, guid2)
+        unconnected = copy.deepcopy(self._connect)
+        
+        while unconnected:
+            for guid1, connections in unconnected.items():
+                factory1 = self._get_factory(guid1)
+                for connector_type_name1, connections2 in connections.items():
+                    connector_type1 = factory1.connector_type(connector_type_name1)
+                    for guid2, connector_type_name2 in connections2.items():
+                        factory_id2 = self._create[guid2]
+                        # Connections are executed in a "From -> To" direction only
+                        # This explicitly ignores the "To -> From" (mirror) 
+                        # connections of every connection pair.
+                        if init:
+                            connect_code = connector_type1.connect_to_init_code(
+                                    self._testbed_id, factory_id2, 
+                                    connector_type_name2,
+                                    False)
+                        else:
+                            connect_code = connector_type1.connect_to_compl_code(
+                                    self._testbed_id, factory_id2, 
+                                    connector_type_name2,
+                                    False)
+                        delay = None
+                        if connect_code:
+                            delay = connect_code(self, guid1, guid2)
+
+                        if delay is not CONNECTION_DELAY:
+                            del unconnected[guid1][connector_type_name1][guid2]
+                    if not unconnected[guid1][connector_type_name1]:
+                        del unconnected[guid1][connector_type_name1]
+                if not unconnected[guid1]:
+                    del unconnected[guid1]
 
     def do_connect_init(self):
         self._do_connect()
 
     def do_connect_compl(self):
         self._do_connect(init = False)
-        self._status = TESTBED_STATUS_CONNECTED
+        self._status = TS.STATUS_CONNECTED
 
-    def _do_in_factory_order(self, action, order, postaction = None):
+    def _do_in_factory_order(self, action, order, postaction = None, poststep = None):
         guids = collections.defaultdict(list)
         # order guids (elements) according to factory_id
         for guid, factory_id in self._create.iteritems():
@@ -250,17 +257,34 @@ class TestbedController(execute.TestbedController):
                 getattr(factory, action)(self, guid)
                 if postaction:
                     postaction(self, guid)
+            if poststep:
+                for guid in guids[factory_id]:
+                    poststep(self, guid)
+
+    @staticmethod
+    def do_poststep_preconfigure(self, guid):
+        # dummy hook for implementations interested in
+        # two-phase configuration
+        pass
 
     def do_preconfigure(self):
         self._do_in_factory_order(
             'preconfigure_function',
-            self._metadata.preconfigure_order )
+            self._metadata.preconfigure_order,
+            poststep = self.do_poststep_preconfigure )
+
+    @staticmethod
+    def do_poststep_configure(self, guid):
+        # dummy hook for implementations interested in
+        # two-phase configuration
+        pass
 
     def do_configure(self):
         self._do_in_factory_order(
             'configure_function',
-            self._metadata.configure_order )
-        self._status = TESTBED_STATUS_CONFIGURED
+            self._metadata.configure_order,
+            poststep = self.do_poststep_configure )
+        self._status = TS.STATUS_CONFIGURED
 
     def do_prestart(self):
         self._do_in_factory_order(
@@ -294,7 +318,7 @@ class TestbedController(execute.TestbedController):
 
     def do_cross_connect_compl(self, cross_data):
         self._do_cross_connect(cross_data, init = False)
-        self._status = TESTBED_STATUS_CROSS_CONNECTED
+        self._status = TS.STATUS_CROSS_CONNECTED
 
     def set(self, guid, name, value, time = TIME_NOW):
         if not guid in self._create:
@@ -303,7 +327,7 @@ class TestbedController(execute.TestbedController):
         if not factory.box_attributes.has_attribute(name):
             raise AttributeError("Invalid attribute %s for element type %s" %
                     (name, factory.factory_id))
-        if self._status > TESTBED_STATUS_STARTED and \
+        if self._status > TS.STATUS_STARTED and \
                 factory.box_attributes.is_attribute_design_only(name):
             raise AttributeError("Attribute %s can only be modified during experiment design" % name)
         if not factory.box_attributes.is_attribute_value_valid(name, value):
@@ -392,20 +416,20 @@ class TestbedController(execute.TestbedController):
         
         return addresses[index][attribute_index]
 
-    def get_tags(self, guid):
-        factory = self._get_factory(guid)
-        return factory.tags
-
     def get_attribute_list(self, guid):
         factory = self._get_factory(guid)
         attribute_list = list()
         return factory.box_attributes.attributes_list
 
+    def get_factory_id(self, guid):
+        factory = self._get_factory(guid)
+        return factory.factory_id
+
     def start(self, time = TIME_NOW):
         self._do_in_factory_order(
             'start_function',
             self._metadata.start_order )
-        self._status = TESTBED_STATUS_STARTED
+        self._status = TS.STATUS_STARTED
 
     #action: NotImplementedError
 
@@ -413,7 +437,7 @@ class TestbedController(execute.TestbedController):
         self._do_in_factory_order(
             'stop_function',
             reversed(self._metadata.start_order) )
-        self._status = TESTBED_STATUS_STOPPED
+        self._status = TS.STATUS_STOPPED
 
     def status(self, guid = None):
         if not guid:
@@ -424,20 +448,34 @@ class TestbedController(execute.TestbedController):
         status_function = factory.status_function
         if status_function:
             return status_function(self, guid)
-        return STATUS_UNDETERMINED
+        return AS.STATUS_UNDETERMINED
 
     def trace(self, guid, trace_id, attribute='value'):
         if attribute == 'value':
-            fd = open("%s" % self.trace_filename(guid, trace_id), "r")
+            fd = open("%s" % self.trace_filepath(guid, trace_id), "r")
             content = fd.read()
             fd.close()
         elif attribute == 'path':
-            content = self.trace_filename(guid, trace_id)
+            content = self.trace_filepath(guid, trace_id)
         else:
             content = None
         return content
 
-    def trace_filename(self, guid, trace_id):
+    def traces_info(self):
+        traces_info = dict()
+        host = self._attributes.get_attribute_value("deployment_host")
+        user = self._attributes.get_attribute_value("deployment_user")
+        for guid, trace_list in self._add_trace.iteritems(): 
+            traces_info[guid] = dict()
+            for trace_id in trace_list:
+                traces_info[guid][trace_id] = dict()
+                filepath = self.trace(guid, trace_id, attribute = "path")
+                traces_info[guid][trace_id]["host"] = host
+                traces_info[guid][trace_id]["user"] = user
+                traces_info[guid][trace_id]["filepath"] = filepath
+        return traces_info
+
+    def trace_filepath(self, guid, trace_id):
         """
         Return a trace's file path, for TestbedController's default 
         implementation of trace()