allow multiple edges/interfaces per vnf in the DCNetwork graph
[osm/vim-emu.git] / src / emuvim / dcemulator / net.py
index da546ab..96fda6b 100755 (executable)
@@ -5,13 +5,13 @@ Distributed Cloud Emulator (dcemulator)
 import logging
 
 import site
+import time
 from subprocess import Popen
 import os
 
 from mininet.net import Dockernet
-from mininet.node import Controller, OVSSwitch, OVSKernelSwitch, Switch, Docker, Host, RemoteController
+from mininet.node import Controller, DefaultController, OVSSwitch, OVSKernelSwitch, Docker, RemoteController
 from mininet.cli import CLI
-from mininet.log import setLogLevel, info, debug
 from mininet.link import TCLink
 import networkx as nx
 from emuvim.dcemulator.monitoring import DCNetworkMonitor
@@ -27,7 +27,10 @@ class DCNetwork(Dockernet):
     This class is used by topology definition scripts.
     """
 
-    def __init__(self, dc_emulation_max_cpu=1.0, **kwargs):
+    def __init__(self, controller=RemoteController,
+                 dc_emulation_max_cpu=1.0,  # fraction of overall CPU time for emulation
+                 dc_emulation_max_mem=512,  # emulation max mem in MB
+                 **kwargs):
         """
         Create an extended version of a Dockernet network
         :param dc_emulation_max_cpu: max. CPU time used by containers in data centers
@@ -38,30 +41,34 @@ class DCNetwork(Dockernet):
 
         # call original Docker.__init__ and setup default controller
         Dockernet.__init__(
-            self, controller=RemoteController, switch=OVSKernelSwitch, **kwargs)
+            self, switch=OVSKernelSwitch, **kwargs)
 
-        # ass a remote controller to be able to use Ryu
-        self.addController('c0', controller=RemoteController)
+        # Ryu management
+        self.ryu_process = None
+        if controller == RemoteController:
+            # start Ryu controller
+            self.startRyu()
+
+        # add the specified controller
+        self.addController('c0', controller=controller)
 
         # graph of the complete DC network
-        self.DCNetwork_graph=nx.DiGraph()
+        self.DCNetwork_graph = nx.MultiDiGraph()
 
         # monitoring agent
         self.monitor_agent = DCNetworkMonitor(self)
 
-        # start Ryu controller
-        self.startRyu()
-
         # initialize resource model registrar
-        self.rm_registrar = ResourceModelRegistrar(dc_emulation_max_cpu)
+        self.rm_registrar = ResourceModelRegistrar(
+            dc_emulation_max_cpu, dc_emulation_max_mem)
 
-    def addDatacenter(self, label, metadata={}):
+    def addDatacenter(self, label, metadata={}, resource_log_path=None):
         """
         Create and add a logical cloud data center to the network.
         """
         if label in self.dcs:
             raise Exception("Data center label already exists: %s" % label)
-        dc = Datacenter(label, metadata=metadata)
+        dc = Datacenter(label, metadata=metadata, resource_log_path=resource_log_path)
         dc.net = self  # set reference to network
         self.dcs[label] = dc
         dc.create()  # finally create the data center in our Mininet instance
@@ -107,11 +114,27 @@ class DCNetwork(Dockernet):
 
         link = Dockernet.addLink(self, node1, node2, **params)
 
+        # try to give container interfaces a default id
+        node1_port_id = node1.ports[link.intf1]
+        if isinstance(node1, Docker):
+            if "id" in params["params1"]:
+                node1_port_id = params["params1"]["id"]
+
+        node2_port_id = node2.ports[link.intf2]
+        if isinstance(node2, Docker):
+            if "id" in params["params2"]:
+                node2_port_id = params["params2"]["id"]
+
         # add edge and assigned port number to graph in both directions between node1 and node2
-        self.DCNetwork_graph.add_edge(node1.name, node2.name, \
-                                      {'src_port': node1.ports[link.intf1], 'dst_port': node2.ports[link.intf2]})
-        self.DCNetwork_graph.add_edge(node2.name, node1.name, \
-                                       {'src_port': node2.ports[link.intf2], 'dst_port': node1.ports[link.intf1]})
+        # port_id: id given in descriptor (if available, otherwise same as port)
+        # port: portnumber assigned by Dockernet
+
+        self.DCNetwork_graph.add_edge(node1.name, node2.name,
+                                      attr_dict={'src_port_id': node1_port_id, 'src_port': node1.ports[link.intf1],
+                                       'dst_port_id': node2_port_id, 'dst_port': node2.ports[link.intf2]})
+        self.DCNetwork_graph.add_edge(node2.name, node1.name,
+                                      attr_dict={'src_port_id': node2_port_id, 'src_port': node2.ports[link.intf2],
+                                        'dst_port_id': node1_port_id, 'dst_port': node1.ports[link.intf1]})
 
         return link
 
@@ -154,9 +177,8 @@ class DCNetwork(Dockernet):
 
     def stop(self):
         # stop Ryu controller
-        self.ryu_process.terminate()
-        #self.ryu_process.kill()
         Dockernet.stop(self)
+        self.stopRyu()
 
     def CLI(self):
         CLI(self)
@@ -209,11 +231,18 @@ class DCNetwork(Dockernet):
         # start Ryu controller with rest-API
         python_install_path = site.getsitepackages()[0]
         ryu_path = python_install_path + '/ryu/app/simple_switch_13.py'
-        ryu_path2 =  python_install_path + '/ryu/app/ofctl_rest.py'
+        ryu_path2 = python_install_path + '/ryu/app/ofctl_rest.py'
         # change the default Openflow controller port to 6653 (official IANA-assigned port number), as used by Mininet
         # Ryu still uses 6633 as default
         ryu_option = '--ofp-tcp-listen-port'
         ryu_of_port = '6653'
-        ryu_cmd =  'ryu-manager'
-        FNULL = open(os.devnull, 'w')
-        self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
\ No newline at end of file
+        ryu_cmd = 'ryu-manager'
+        FNULL = open("/tmp/ryu.log", 'w')
+        self.ryu_process = Popen([ryu_cmd, ryu_path, ryu_path2, ryu_option, ryu_of_port], stdout=FNULL, stderr=FNULL)
+        time.sleep(1)
+
+    def stopRyu(self):
+        if self.ryu_process is not None:
+            self.ryu_process.terminate()
+            self.ryu_process.kill()
+