aboutsummaryrefslogtreecommitdiff
path: root/rumba/testbeds/dockertb.py
diff options
context:
space:
mode:
Diffstat (limited to 'rumba/testbeds/dockertb.py')
-rw-r--r--rumba/testbeds/dockertb.py190
1 files changed, 190 insertions, 0 deletions
diff --git a/rumba/testbeds/dockertb.py b/rumba/testbeds/dockertb.py
new file mode 100644
index 0000000..3895a7e
--- /dev/null
+++ b/rumba/testbeds/dockertb.py
@@ -0,0 +1,190 @@
+#
+# A library to manage ARCFIRE experiments
+#
+# Copyright (C) 2017 Nextworks S.r.l.
+# Copyright (C) 2017 imec
+#
+# Sander Vrijders <sander.vrijders@ugent.be>
+# Dimitri Staessens <dimitri.staessens@ugent.be>
+# Vincenzo Maffione <v.maffione@nextworks.it>
+# Marco Capitani <m.capitani@nextworks.it>
+# Nick Aerts <nick.aerts@ugent.be>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., http://www.fsf.org/about/contact/.
+#
+import os
+from time import sleep
+import docker
+import subprocess
+
+import rumba.model as mod
+import rumba.log as log
+from rumba.executors.docker import DockerExecutor
+
+
+logger = log.get_logger(__name__)
+
+class Testbed(mod.Testbed):
+ def __init__(self, exp_name, base_image, pull_image=True,
+ use_ovs=False):
+ mod.Testbed.__init__(self, exp_name, "", "", "")
+
+ img = base_image.rsplit(":", 1)
+
+ self.base_image_repo = img[0]
+ self.base_image_tag = "latest" if len(img) == 1 else img[1]
+ self.base_image = "%s:%s" % (self.base_image_repo, self.base_image_tag)
+ self.pull_image = pull_image
+ self.use_ovs = use_ovs
+
+ self.running_containers = {}
+ self.active_bridges = set()
+ self.active_ipcps = set()
+
+ self.docker_client = docker.from_env()
+ self.executor = DockerExecutor(self)
+
+ def swap_in(self, experiment):
+ docker_client = self.docker_client
+
+ mod.Testbed.swap_in(self, experiment)
+
+ # Pull image
+ if self.pull_image:
+ docker_client.images.pull(self.base_image_repo,
+ self.base_image_tag)
+
+ docker_client.images.get("%s:%s" % (self.base_image_repo,
+ self.base_image_tag))
+
+ # Start all nodes
+ for node in experiment.nodes:
+ self.running_containers[node.name] = docker_client.containers.run(
+ self.base_image, command=node.startup_command, name=node.name,
+ detach=True, network="none", privileged=True,
+ devices=["/dev/fuse"])
+
+ logger.info("Nodes starting")
+
+ if not os.path.exists("/var/run/netns"):
+ subprocess.check_call('sudo mkdir /var/run/netns'.split())
+
+ for shim in experiment.dif_ordering:
+ if not isinstance(shim, mod.ShimEthDIF):
+ # Nothing to do here
+ continue
+
+ cmd = ""
+ if self.use_ovs:
+ cmd += 'sudo ovs-vsctl add-br %(shim)s'
+ else:
+ cmd += 'sudo ip link add %(shim)s type bridge'
+ cmd = cmd % {'shim': shim.name}
+
+ self.active_bridges.add(shim.name)
+
+ subprocess.check_call(cmd.split())
+
+ if not self.use_ovs:
+ cmd = 'sudo ip link set dev %(shim)s up' % {'shim': shim.name}
+ subprocess.check_call(cmd.split())
+ for node in experiment.nodes:
+ container = self.running_containers[node.name]
+
+ container.reload()
+
+ state = container.attrs["State"]
+
+ while not state["Running"]:
+ sleep(0.2)
+ container.reload()
+ state = container.attrs["State"]
+
+ pid = state["Pid"]
+
+ subprocess.check_call(('sudo ln -s /proc/%(pid)i/ns/net '
+ '/var/run/netns/%(pid)i' % {'pid': pid}).split())
+
+ for ipcp in node.ipcps:
+ if isinstance(ipcp, mod.ShimEthIPCP):
+ if ipcp.ifname is None:
+ ipcp.ifname = "eth%i" % node.ipcps.index(ipcp)
+
+ cmd = ('sudo ip link add %(node)s.%(ifname)s type veth '
+ 'peer name _%(node)s.%(ifname)s'\
+ % {'node': node.name, 'ifname': ipcp.ifname})
+ subprocess.check_call(cmd.split())
+
+ cmd = ""
+ if self.use_ovs:
+ cmd += ('sudo ovs-vsctl add-port %(dif)s %(node)s.%('
+ 'ifname)s')
+ else:
+ cmd += ('sudo ip link set %(node)s.%(ifname)s master '
+ '%(dif)s')
+
+ cmd = (cmd % {'node': node.name,
+ 'ifname': ipcp.ifname,
+ 'dif': ipcp.dif.name})
+
+ subprocess.check_call(cmd.split())
+
+ cmd = ('sudo ip link set _%(node)s.%(ifname)s '
+ 'netns %(pid)i '
+ 'name %(ifname)s'
+ % {'node': node.name,
+ 'pid': pid,
+ 'ifname': ipcp.ifname})
+ subprocess.check_call(cmd.split())
+
+ cmd = ('sudo ip link set dev %(node)s.%(ifname)s up'
+ % {'node': node.name, 'ifname': ipcp.ifname})
+ subprocess.check_call(cmd.split())
+
+ cmd = ('sudo ip netns exec %(pid)i ip link set dev '
+ '%(ifname)s up'
+ % {'pid': pid, 'ifname': ipcp.ifname})
+ subprocess.check_call(cmd.split())
+
+ self.active_ipcps.add(ipcp)
+
+ logger.info("Experiment swapped in")
+
+ def swap_out(self, experiment):
+ for name, container in self.running_containers.items():
+ container.remove(force=True)
+
+ for shim in experiment.dif_ordering:
+ if isinstance(shim, mod.ShimEthDIF) and shim.name in self.active_bridges:
+ cmd = ""
+ if self.use_ovs:
+ cmd += 'sudo ovs-vsctl del-br %(shim)s'
+ else:
+ cmd += 'sudo ip link del %(shim)s'
+ cmd = cmd % {'shim': shim.name}
+
+ subprocess.check_call(cmd.split())
+
+ self.active_bridges.remove(shim.name)
+
+ for name, container in self.running_containers.items():
+ pid = container.attrs["State"]["Pid"]
+
+ cmd = 'sudo rm /var/run/netns/%(pid)i' % {'pid': pid}
+ subprocess.check_call(cmd.split())
+
+ self.running_containers = {}
+
+ logger.info("Experiment swapped out") \ No newline at end of file