aboutsummaryrefslogtreecommitdiff
path: root/rumba
diff options
context:
space:
mode:
authorSander Vrijders <sander.vrijders@intec.ugent.be>2017-02-09 15:43:15 +0100
committerSander Vrijders <sander.vrijders@intec.ugent.be>2017-02-09 15:43:15 +0100
commitfd37777d333c991c094aa128ae5f30d9fa80a9d6 (patch)
treed48bab60bf92a223222e6af62a0748da376981e0 /rumba
parent53a11eac43187ff12d82605cf8379803697db7b7 (diff)
downloadrumba-fd37777d333c991c094aa128ae5f30d9fa80a9d6.tar.gz
rumba-fd37777d333c991c094aa128ae5f30d9fa80a9d6.zip
rumba: Rename package to rumba
This renames the package to rumba, since rhumba was already taken on the PyPi.
Diffstat (limited to 'rumba')
-rw-r--r--rumba/__init__.py0
-rw-r--r--rumba/model.py255
-rw-r--r--rumba/prototypes/__init__.py0
-rw-r--r--rumba/prototypes/irati.py48
-rw-r--r--rumba/prototypes/ouroboros.py58
-rw-r--r--rumba/prototypes/rlite.py49
-rw-r--r--rumba/ssh_support.py156
-rw-r--r--rumba/testbeds/__init__.py0
-rw-r--r--rumba/testbeds/emulab.py244
-rw-r--r--rumba/testbeds/faketestbed.py30
-rw-r--r--rumba/testbeds/jfed.py152
11 files changed, 992 insertions, 0 deletions
diff --git a/rumba/__init__.py b/rumba/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rumba/__init__.py
diff --git a/rumba/model.py b/rumba/model.py
new file mode 100644
index 0000000..0346a8c
--- /dev/null
+++ b/rumba/model.py
@@ -0,0 +1,255 @@
+#
+# A library to manage ARCFIRE experiments
+#
+# Sander Vrijders <sander.vrijders@intec.ugent.be>
+# Vincenzo Maffione <v.maffione@nextworks.it>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import abc
+
+# Represents generic testbed info
+#
+# @username [string] user name
+# @password [string] password
+# @proj_name [string] project name
+# @exp_name [string] experiment name
+#
+class Testbed:
+ def __init__(self, exp_name, username, password, proj_name):
+ self.username = username
+ self.password = password
+ self.proj_name = proj_name
+ self.exp_name = exp_name
+
+ @abc.abstractmethod
+ def create_experiment(self, nodes, links):
+ raise Exception('create_experiment() not implemented')
+
+
+# Represents an interface on a node
+#
+# @name [string] interface name
+# @ip [int] IP address of that interface
+#
+class Interface:
+ def __init__(self, name = "", ip = ""):
+ self.name = name
+ self.ip = ip
+
+# Represents a link in the physical graph
+#
+# @name [string] Link name
+#
+class Link:
+ def __init__(self, name):
+ self.name = name
+
+# Represents a point-to-point link in the physical graph
+#
+# @name [string] DIF name
+#
+class P2PLink(Link):
+ def __init__(self, name, node_a, node_b,
+ int_a = Interface(),
+ int_b = Interface()):
+ Link.__init__(self, name)
+ self.node_a = node_a
+ self.node_b = node_b
+ self.int_a = int_a
+ self.int_b = int_b
+
+def get_links(nodes):
+ difs = set()
+ links = list()
+ for node in nodes:
+ for dif in node.difs:
+ if type(dif) is ShimEthDIF:
+ difs.add(dif)
+
+ for dif in difs:
+ # Point-to-point link
+ if len(dif.members) == 2:
+ node_a = dif.members[0]
+ node_b = dif.members[1]
+ link = P2PLink(node_a.name + "-" + node_b.name,
+ node_a, node_b)
+ links.append(link)
+
+ return links
+
+# Base class for DIFs
+#
+# @name [string] DIF name
+#
+class DIF:
+ def __init__(self, name, members = list()):
+ self.name = name
+ self.members = members
+
+ def __repr__(self):
+ s = "DIF %s" % self.name
+ return s
+
+ def add_member(self, node):
+ self.members.append(node)
+
+ def del_member(self, node):
+ self.members.remove(node)
+
+# Shim over UDP
+#
+class ShimUDPDIF(DIF):
+ def __init__(self, name, members = list()):
+ DIF.__init__(self, name, members)
+
+# Shim over Ethernet
+#
+# @link_speed [int] Speed of the Ethernet network, in Mbps
+#
+class ShimEthDIF(DIF):
+ def __init__(self, name, members = list(), link_speed = 0):
+ DIF.__init__(self, name, members)
+ self.link_speed = int(link_speed)
+ if self.link_speed < 0:
+ raise ValueError("link_speed must be a non-negative number")
+
+# Normal DIF
+#
+# @policies [dict] Policies of the normal DIF
+#
+class NormalDIF(DIF):
+ def __init__(self, name, members = list(), policies = dict()):
+ DIF.__init__(self, name, members)
+ self.policies = policies
+
+ def add_policy(self, comp, pol):
+ self.policies[comp] = pol
+
+ def del_policy(self, comp):
+ del self.policies[comp]
+
+ def __repr__(self):
+ s = DIF.__repr__(self)
+ for comp, pol in self.policies.items():
+ s += "\n Component %s has policy %s" % (comp, pol)
+ return s
+
+# A node in the experiment
+#
+# @difs: DIFs the node will have an IPCP in
+# @dif_registrations: Which DIF is registered in which DIF
+# @registrations: Registrations of names in DIFs
+# @bindings: Binding of names on the processing system
+#
+class Node:
+ def __init__(self, name, difs = list(),
+ dif_registrations = dict(),
+ registrations = dict(),
+ bindings = dict()):
+ self.name = name
+ self.difs = difs
+ for dif in difs:
+ dif.add_member(self)
+ self.dif_registrations = dif_registrations
+ self.registrations = registrations
+ self.bindings = bindings
+ self.full_name = name
+
+ def __repr__(self):
+ s = "Node " + self.name + ":\n"
+ s += " IPCPs in DIFs: ["
+ for d in self.difs:
+ s += " %s" % d.name
+ s += " ]\n"
+ s += " DIF registrations: [ "
+ for dif_a, difs in self.dif_registrations.items():
+ s += "%s => [" % dif_a.name
+ for dif_b in difs:
+ s += " %s" % dif_b.name
+ s += " ]"
+ s += " ]\n"
+ s += " Name registrations: [ "
+ for name, difs in self.registrations.items():
+ s += "%s => [" % name
+ for dif in difs:
+ s += " %s" % dif.name
+ s += " ]"
+ s += " ]\n"
+ s += " Bindings: [ "
+ for ap, name in self.bindings.items():
+ s += "'%s' => '%s'" % (ap, name)
+ s += " ]\n"
+ return s
+
+ def add_dif(self, dif):
+ self.difs.append(dif)
+ dif.add_member(self)
+
+ def del_dif(self, dif):
+ self.difs.remove(dif)
+ dif.del_member(self)
+
+ def add_dif_registration(self, dif_a, dif_b):
+ self.dif_registrations[dif_a].append(dif_b)
+
+ def del_dif_registration(self, dif_a, dif_b):
+ self.dif_registrations[dif_a].remove(dif_b)
+
+ def add_registration(self, name, dif):
+ self.dif_registrations[name].append(dif)
+
+ def del_registration(self, name, dif):
+ self.dif_registrations[name].remove(dif)
+
+ def add_binding(self, name, ap):
+ self.dif_bindings[name] = ap
+
+ def del_binding(self, name):
+ del self.dif_bindings[name]
+
+# Base class for ARCFIRE experiments
+#
+# @name [string] Name of the experiment
+# @nodes: Nodes in the experiment
+#
+class Experiment:
+ def __init__(self, testbed, nodes = list()):
+ self.nodes = nodes
+ self.testbed = testbed
+
+ def __repr__(self):
+ s = ""
+ for n in self.nodes:
+ s += "\n" + str(n)
+
+ return s
+
+ def add_node(self, node):
+ self.nodes.append(node)
+
+ def del_node(self, node):
+ self.nodes.remove(node)
+
+ # Realize the experiment, using a testbed-specific setup
+ def swap_in(self):
+ self.links = get_links(self.nodes)
+ self.testbed.create_experiment(self.nodes, self.links)
+
+ @abc.abstractmethod
+ def run(self):
+ raise Exception('run() method not implemented')
+
diff --git a/rumba/prototypes/__init__.py b/rumba/prototypes/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rumba/prototypes/__init__.py
diff --git a/rumba/prototypes/irati.py b/rumba/prototypes/irati.py
new file mode 100644
index 0000000..af98f2e
--- /dev/null
+++ b/rumba/prototypes/irati.py
@@ -0,0 +1,48 @@
+#
+# Commands to setup and instruct IRATI
+#
+# Vincenzo Maffione <v.maffione@nextworks.it>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import rumba.ssh_support as ssh
+from rumba.model import Experiment
+
+# An experiment over the IRATI implementation
+class IRATIExperiment(Experiment):
+ def __init__(self, testbed, nodes = list()):
+ Experiment.__init__(self, testbed, nodes)
+
+ def setup(self):
+ cmds = list()
+
+ cmds.append("sudo apt-get update")
+ cmds.append("sudo apt-get install g++ gcc "
+ "protobuf-compiler libprotobuf-dev git --yes")
+ cmds.append("sudo rm -rf ~/irati")
+ cmds.append("cd && git clone https://github.com/IRATI/stack irati")
+ cmds.append("cd ~/irati && sudo ./install-from-scratch")
+ cmds.append("sudo nohup ipcm &> ipcm.log &")
+
+ for node in self.nodes:
+ ssh.execute_commands(self.testbed, node.full_name, cmds, time_out = None)
+
+ def run(self):
+ print("[IRATI experiment] start")
+ self.swap_in()
+ print("Setting up IRATI on the nodes...")
+ self.setup()
+ print("[IRATI experiment] end")
diff --git a/rumba/prototypes/ouroboros.py b/rumba/prototypes/ouroboros.py
new file mode 100644
index 0000000..0f60ee8
--- /dev/null
+++ b/rumba/prototypes/ouroboros.py
@@ -0,0 +1,58 @@
+#
+# Commands to instruct Ouroboros
+#
+# Sander Vrijders <sander.vrijders@intec.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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import rumba.ssh_support as ssh
+from rumba.model import Experiment
+
+# An experiment over the Ouroboros implementation
+class OuroborosExperiment(Experiment):
+ def __init__(self, testbed, nodes = list()):
+ Experiment.__init__(self, testbed, nodes)
+
+ def setup_ouroboros(self):
+ cmds = list()
+
+ cmds.append("sudo apt-get update")
+ cmds.append("sudo apt-get install cmake protobuf-c-compiler git --yes")
+ cmds.append("sudo rm -r ~/ouroboros/build")
+ cmds.append("cd ~/ouroboros; sudo ./install_release.sh")
+ cmds.append("sudo nohup irmd > /dev/null &")
+
+ for node in self.nodes:
+ ssh.execute_commands(self.testbed, node.full_name, cmds, time_out = None)
+ return
+
+ def bind_names(self):
+ for node in self.nodes:
+ cmds = list()
+ for name, ap in node.bindings.items():
+ cmds.append("irm b ap " + ap + " n " + name)
+
+ ssh.execute_commands(self.testbed, node.full_name, cmds, time_out = None)
+
+ def run(self):
+ print("[Ouroboros experiment] start")
+ print("Creating resources...")
+ self.swap_in()
+ print("Setting up Ouroboros...")
+ self.setup_ouroboros()
+ print("Binding names...")
+ self.bind_names()
+ print("[Ouroboros experiment] end")
diff --git a/rumba/prototypes/rlite.py b/rumba/prototypes/rlite.py
new file mode 100644
index 0000000..9148bfa
--- /dev/null
+++ b/rumba/prototypes/rlite.py
@@ -0,0 +1,49 @@
+#
+# Commands to setup and instruct rlite
+#
+# Vincenzo Maffione <v.maffione@nextworks.it>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import rumba.ssh_support as ssh
+from rumba.model import Experiment
+
+# An experiment over the RLITE implementation
+class RLITEExperiment(Experiment):
+ def __init__(self, testbed, nodes = list()):
+ Experiment.__init__(self, testbed, nodes)
+
+ def setup(self):
+ cmds = list()
+
+ cmds.append("sudo apt-get update")
+ cmds.append("sudo apt-get install g++ gcc cmake "
+ "linux-headers-$(uname -r) "
+ "protobuf-compiler libprotobuf-dev git --yes")
+ cmds.append("sudo rm -rf ~/rlite")
+ cmds.append("cd ~; git clone https://github.com/vmaffione/rlite")
+ cmds.append("cd ~/rlite && ./configure && make && sudo make install")
+ cmds.append("sudo nohup rlite-uipcps -v DBG -k 0 -U -A &> uipcp.log &")
+
+ for node in self.nodes:
+ ssh.execute_commands(self.testbed, node.full_name, cmds, time_out = None)
+
+ def run(self):
+ print("[RLITE experiment] start")
+ self.swap_in()
+ print("Setting up rlite on the nodes...")
+ self.setup()
+ print("[RLITE experiment] end")
diff --git a/rumba/ssh_support.py b/rumba/ssh_support.py
new file mode 100644
index 0000000..552c43f
--- /dev/null
+++ b/rumba/ssh_support.py
@@ -0,0 +1,156 @@
+#
+# SSH support for Rumba
+#
+# Sander Vrijders <sander.vrijders@intec.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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import paramiko
+
+def get_ssh_client():
+ ssh_client = paramiko.SSHClient()
+ ssh_client.load_system_host_keys()
+ ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+
+ return ssh_client
+
+def execute_commands(testbed, hostname, commands, time_out = 3):
+ '''
+ Remote execution of a list of shell command on hostname. By
+ default this function will exit (timeout) after 3 seconds.
+
+ @param testbed: testbed info
+ @param hostname: host name or ip address of the node
+ @param command: *nix shell command
+ @param time_out: time_out value in seconds, error will be generated if
+ no result received in given number of seconds, the value None can
+ be used when no timeout is needed
+ '''
+ ssh_client = get_ssh_client()
+
+ try:
+ ssh_client.connect(hostname, 22,
+ testbed.username, testbed.password,
+ look_for_keys = True, timeout = time_out)
+ for command in commands:
+ stdin, stdout, stderr = ssh_client.exec_command(command)
+ err = str(stderr.read()).strip('b\'\"\\n')
+ if err != "":
+ err_array = err.split('\\n')
+ for erra in err_array:
+ print(erra)
+ ssh_client.close()
+
+ except Exception as e:
+ print(str(e))
+ return
+
+def execute_command(testbed, hostname, command, time_out = 3):
+ '''
+ Remote execution of a list of shell command on hostname. By
+ default this function will exit (timeout) after 3 seconds.
+
+ @param testbed: testbed info
+ @param hostname: host name or ip address of the node
+ @param command: *nix shell command
+ @param time_out: time_out value in seconds, error will be generated if
+ no result received in given number of seconds, the value None can
+ be used when no timeout is needed
+
+ @return: stdout resulting from the command
+ '''
+ ssh_client = get_ssh_client()
+
+ try:
+ ssh_client.connect(hostname, 22,
+ testbed.username, testbed.password,
+ look_for_keys = True, timeout = time_out)
+ stdin, stdout, stderr = ssh_client.exec_command(command)
+ err = str(stderr.read()).strip('b\'\"\\n')
+ if err != "":
+ print(err)
+ output = str(stdout.read()).strip('b\'\"\\n')
+ ssh_client.close()
+
+ return output
+
+ except Exception as e:
+ print(str(e))
+ return
+
+def copy_file_to_testbed(testbed, hostname, text, file_name):
+ '''
+ Write a string to a given remote file.
+ Overwrite the complete file if it already exists!
+
+ @param testbed: testbed info
+ @param hostname: host name or ip address of the node
+ @param text: string to be written in file
+ @param file_name: file name (including full path) on the host
+ '''
+ ssh_client = get_ssh_client()
+
+ try:
+ ssh_client.connect(hostname, 22,
+ testbed.username,
+ testbed.password,
+ look_for_keys=True)
+
+ cmd = "touch " + file_name + \
+ "; chmod a+rwx " + file_name
+
+ stdin, stdout, stderr = ssh_client.exec_command(cmd)
+ err = str(stderr.read()).strip('b\'\"\\n')
+ if err != "":
+ print(err)
+
+ sftp_client = ssh_client.open_sftp()
+ remote_file = sftp_client.open(file_name, 'w')
+
+ remote_file.write(text)
+ remote_file.close()
+
+ except Exception as e:
+ print(str(e))
+
+def setup_vlan(testbed, node_name, vlan_id, int_name):
+ '''
+ Gets the interface (ethx) to link mapping
+
+ @param testbed: testbed info
+ @param node_name: the node to create the VLAN on
+ @param vlan_id: the VLAN id
+ @param int_name: the name of the interface
+ '''
+ print("Setting up VLAN on node " + node_name)
+
+ node_full_name = full_name(node_name, testbed)
+ cmd = "sudo ip link add link " + \
+ str(int_name) + \
+ " name " + str(int_name) + \
+ "." + str(vlan_id) + \
+ " type vlan id " + str(vlan_id)
+ execute_command(testbed, node_full_name, cmd)
+ cmd = "sudo ifconfig " + \
+ str(int_name) + "." + \
+ str(vlan_id) + " up"
+ execute_command(node_full_name, cmd, testbed)
+ cmd = "sudo ethtool -K " + \
+ str(int_name) + " rxvlan off"
+ execute_command(node_full_name, cmd, testbed)
+ cmd = "sudo ethtool -K " + \
+ str(int_name) + " txvlan off"
+ execute_command(node_full_name, cmd, testbed)
diff --git a/rumba/testbeds/__init__.py b/rumba/testbeds/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/rumba/testbeds/__init__.py
diff --git a/rumba/testbeds/emulab.py b/rumba/testbeds/emulab.py
new file mode 100644
index 0000000..735bf3a
--- /dev/null
+++ b/rumba/testbeds/emulab.py
@@ -0,0 +1,244 @@
+#
+# Emulab support for Rumba
+#
+# Sander Vrijders <sander.vrijders@intec.ugent.be>
+# Wouter Tavernier <wouter.tavernier@intec.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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import os
+import time
+import re
+from ast import literal_eval
+
+import rumba.ssh_support as ssh
+from rumba.model import Testbed
+
+import warnings
+warnings.filterwarnings("ignore")
+
+# Represents an emulab testbed info
+#
+# @url [string] URL of the testbed
+# @image [string] specific image to use
+#
+class EmulabTestbed(Testbed):
+ def __init__(self, exp_name, username, password = "",
+ proj_name = "ARCFIRE", url = "wall1.ilabt.iminds.be",
+ image = "UBUNTU14-64-STD"):
+ Testbed.__init__(self, exp_name, username, password, proj_name)
+ self.url = url
+ self.image = image
+
+ def ops_server(self):
+ '''
+ Return server name of the ops-server (is testbed specific)
+
+ @param self: testbed info
+
+ @return: server name of the ops-server
+ '''
+ return 'ops.' + self.url
+
+ def full_name(self, node_name):
+ '''
+ Return server name of a node
+
+ @param node_name: name of the node
+ @param self: testbed info
+
+ @return: server name of the node
+ '''
+ return node_name + '.' + self.exp_name + '.' + \
+ self.proj_name + '.' + self.url
+
+ def get_experiment_list(self, project_name = None):
+ '''
+ Get list of made emulab experiments accessible with your credentials
+
+ @param self: testbed info
+ @param project_name: optional filter on project
+
+ @return: list of created experiments (strings)
+ '''
+ cmd = '/usr/testbed/bin/sslxmlrpc_client.py -m experiment getlist'
+ out = ssh.execute_command(self, self.ops_server(), cmd)
+
+ try:
+ if project_name != None:
+ return literal_eval(out)[project_name][project_name]
+ else:
+ return literal_eval(out)
+ except:
+ return { project_name: { project_name: [] }}
+
+ def swap_exp_in(self):
+ '''
+ Swaps experiment in
+
+ @param self: testbed info
+ '''
+ cmd = '/usr/testbed/bin/sslxmlrpc_client.py swapexp proj=' + \
+ self.proj_name + \
+ ' exp=' + \
+ self.exp_name + \
+ ' direction=in'
+
+ output = ssh.execute_command(self, self.ops_server(), cmd)
+
+ return output
+
+ def _create_experiment(self, nodes, links):
+ '''
+ Creates an emulab experiment
+
+ @param self: testbed info
+ @param nodes: holds the nodes in the experiment
+ @param links: holds the links in the experiment
+ '''
+ proj_name = self.proj_name
+ exp_name = self.exp_name
+
+ exp_list = self.get_experiment_list()
+
+ try:
+ if exp_name in exp_list[proj_name][proj_name]:
+ print("Experiment already exists.")
+ return
+ except:
+ print("First experiment to be created for that project.")
+
+ ns = self.generate_ns_script(nodes, links)
+ dest_file_name = '/users/'+ self.username + \
+ '/temp_ns_file.%s.ns' % os.getpid()
+ ssh.copy_file_to_testbed(self, self.ops_server(), ns, dest_file_name)
+
+ cmd = '/usr/testbed/bin/sslxmlrpc_client.py startexp ' + \
+ 'batch=false wait=true proj="' + proj_name + \
+ '" exp="' + exp_name + '" noswapin=true ' + \
+ 'nsfilepath="' + dest_file_name + '"'
+
+ ssh.execute_command(self, self.ops_server(), cmd, time_out = None)
+ ssh.execute_command(self, self.ops_server(),'rm ' + dest_file_name)
+ print("New experiment succesfully created.")
+
+ def generate_ns_script(self, nodes, p2plinks):
+ '''
+ Generate ns script based on network graph.
+ Enables to customize default node image.
+
+ @param nodes: holds the nodes in the experiment
+ @param links: holds the links in the experiment
+ @param self: testbed info
+
+ @return: ns2 script for Emulab experiment
+ '''
+
+ ns2_script = "# ns script generated by Rumba\n"
+ ns2_script += "set ns [new Simulator]\n"
+ ns2_script += "source tb_compat.tcl\n"
+
+ for node in nodes:
+ ns2_script += "set " + node.name + " [$ns node]\n"
+ ns2_script += "tb-set-node-os $" + node.name + " " + \
+ self.image + "\n"
+
+ for link in p2plinks:
+ ns2_script += "set " + link.name + \
+ " [$ns duplex-link $" + \
+ link.node_a.name + " $" + \
+ link.node_b.name + " 1000Mb 0ms DropTail]\n"
+
+ ns2_script += "$ns run\n"
+
+ return ns2_script
+
+ def wait_until_nodes_up(self):
+ '''
+ Checks if nodes are up
+
+ @param self: testbed info
+ '''
+ print("Waiting until all nodes are up")
+
+ cmd = '/usr/testbed/bin/script_wrapper.py expinfo -e' + \
+ self.proj_name + \
+ ',' + \
+ self.exp_name + \
+ ' -a | grep State | cut -f2,2 -d " "'
+
+ res = ssh.execute_command(self, self.ops_server(), cmd)
+ active = False
+ if res == "active":
+ active = True
+ while active != True:
+ res = ssh.execute_command(self, self.ops_server(), cmd)
+ if res == "active":
+ active = True
+ print("Still waiting")
+ time.sleep(5)
+
+ def complete_experiment_graph(self, nodes, p2plinks):
+ '''
+ Gets the interface (ethx) to link mapping
+
+ @param self: testbed info
+ @param nodes: holds the nodes in the experiment
+ @param links: holds the links in the experiment
+ '''
+
+ node_full_name = full_name(self, nodes[0].name)
+ cmd = 'cat /var/emulab/boot/topomap'
+ topomap = ssh.execute_command(self, node_full_name, cmd)
+ # Almost as ugly as yo momma
+ index = topomap.rfind("# lans")
+ topo_array = topomap[:index].split('\\n')[1:-1]
+ # Array contains things like 'r2b1,link7:10.1.6.3 link6:10.1.5.3'
+ for item in topo_array:
+ item_array = re.split(',? ?', item)
+ node_name = item_array[0]
+ for item2 in item_array[1:]:
+ item2 = item2.split(':')
+ link_name = item2[0]
+ link_ip = item2[1]
+ for link in p2plinks:
+ if link.name == link_name:
+ if link.node_a.name == node_name:
+ link.int_a.ip = link_ip
+ elif link.node_b.name == node_name:
+ link.int_b.ip = link_ip
+
+ for node in nodes:
+ cmd = 'cat /var/emulab/boot/ifmap'
+ node_full_name = full_name(self, node.name)
+ output = ssh.execute_command(self, node_full_name, cmd)
+ output = re.split('\\\\n', output)
+ for item in output:
+ item = item.split()
+ for link in p2plinks:
+ if link.node_a.name == node.name and \
+ link.int_a.ip == item[1]:
+ link.int_a.name = item[0]
+ elif link.node_b.name == node.name and \
+ link.int_b.ip == item[1]:
+ link.int_b.name = item[0]
+ node.full_name = self.full_name(node.name)
+
+ def create_experiment(self, nodes, links):
+ self._create_experiment(nodes, links)
+ self.swap_exp_in()
+ self.wait_until_nodes_up()
+ es.complete_experiment_graph(nodes, links)
diff --git a/rumba/testbeds/faketestbed.py b/rumba/testbeds/faketestbed.py
new file mode 100644
index 0000000..f163411
--- /dev/null
+++ b/rumba/testbeds/faketestbed.py
@@ -0,0 +1,30 @@
+#
+# Fake testbed for Rumba testing
+#
+# Vincenzo Maffione <v.maffione@nextworks.it>
+#
+# 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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+from rumba.model import Testbed
+
+# Fake testbed, useful for testing
+class FakeTestbed(Testbed):
+ def __init__(self, exp_name, username, proj_name = "ARCFIRE",
+ password = ""):
+ Testbed.__init__(self, exp_name, username, password, proj_name)
+
+ def create_experiment(self, nodes, links):
+ print("[Fake testbed] experiment swapped in")
diff --git a/rumba/testbeds/jfed.py b/rumba/testbeds/jfed.py
new file mode 100644
index 0000000..5f4d496
--- /dev/null
+++ b/rumba/testbeds/jfed.py
@@ -0,0 +1,152 @@
+#
+# jFed support for Rumba
+#
+# Sander Vrijders <sander.vrijders@intec.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., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA
+
+import subprocess
+import getpass
+import xml.dom.minidom as xml
+
+from rumba.model import Testbed
+
+class jFedTestbed(Testbed):
+ def __init__(self, exp_name, username, cert_file, jfed_jar, exp_hours = "2",
+ proj_name = "ARCFIRE", authority = "wall2.ilabt.iminds.be"):
+ passwd = getpass.getpass(prompt = "Password for certificate file: ")
+ Testbed.__init__(self, exp_name, username, passwd, proj_name)
+ self.authority = "urn:publicid:IDN+" + authority + "+authority+cm"
+ self.auth_name = authority
+ self.cert_file = cert_file
+ self.jfed_jar = jfed_jar
+ self.exp_hours = exp_hours
+
+ def create_rspec(self, nodes, links):
+ self.rspec = self.exp_name + ".rspec"
+
+ impl = xml.getDOMImplementation()
+ doc = impl.createDocument(None, "rspec", None)
+
+ top_el = doc.documentElement
+ top_el.setAttribute("xmlns", "http://www.geni.net/resources/rspec/3")
+ top_el.setAttribute("type", "request")
+ top_el.setAttribute("xmlns:emulab", "http://www.protogeni.net/" +
+ "resources/rspec/ext/emulab/1")
+ top_el.setAttribute("xmlns:jfedBonfire", "http://jfed.iminds.be/" +
+ "rspec/ext/jfed-bonfire/1")
+ top_el.setAttribute("xmlns:delay", "http://www.protogeni.net/" +
+ "resources/rspec/ext/delay/1")
+ top_el.setAttribute("xmlns:jfed-command", "http://jfed.iminds.be/" +
+ "rspec/ext/jfed-command/1")
+ top_el.setAttribute("xmlns:client", "http://www.protogeni.net/" +
+ "resources/rspec/ext/client/1")
+ top_el.setAttribute("xmlns:jfed-ssh-keys", "http://jfed.iminds.be/" +
+ "rspec/ext/jfed-ssh-keys/1")
+ top_el.setAttribute("xmlns:jfed", "http://jfed.iminds.be/rspec/" +
+ "ext/jfed/1")
+ top_el.setAttribute("xmlns:sharedvlan", "http://www.protogeni.net/" +
+ "resources/rspec/ext/shared-vlan/1")
+ top_el.setAttribute("xmlns:xsi", "http://www.w3.org/2001/" +
+ "XMLSchema-instance")
+ top_el.setAttribute("xsi:schemaLocation", "http://www.geni.net/" +
+ "resources/rspec/3 http://www.geni.net/" +
+ "resources/rspec/3/request.xsd")
+
+ for node in nodes:
+ el = doc.createElement("node")
+ top_el.appendChild(el)
+ el.setAttribute("client_id", node.name)
+ el.setAttribute("exclusive", "true")
+ el.setAttribute("component_manager_id", self.authority)
+
+ el2 = doc.createElement("sliver_type")
+ el.appendChild(el2)
+ el2.setAttribute("name", "raw-pc")
+
+ node.ifs = 0
+ for link in links:
+ if link.node_a == node or link.node_b == node:
+ el3 = doc.createElement("interface")
+ if link.node_a == node:
+ link.int_a.id = node.name + ":if" + str(node.ifs)
+ link_id = link.int_a.id
+ if link.node_b == node:
+ link.int_b.id = node.name + ":if" + str(node.ifs)
+ link_id = link.int_b.id
+
+ el3.setAttribute("client_id", link_id)
+ node.ifs += 1
+ el.appendChild(el3)
+
+ for link in links:
+ el = doc.createElement("link")
+ top_el.appendChild(el)
+ el.setAttribute("client_id", link.name)
+
+ el2 = doc.createElement("component_manager_id")
+ el2.setAttribute("name", self.authority)
+ el.appendChild(el2)
+
+ el3 = doc.createElement("interface_ref")
+ el3.setAttribute("client_id", link.int_a.id)
+ el.appendChild(el3)
+
+ el4 = doc.createElement("interface_ref")
+ el4.setAttribute("client_id", link.int_b.id)
+ el.appendChild(el4)
+
+ file = open(self.rspec, "w")
+ file.write(doc.toprettyxml())
+ file.close()
+
+ def create_experiment(self, nodes, links):
+ self.create_rspec(nodes, links)
+ self.manifest = self.exp_name + ".rrspec"
+
+ for node in nodes:
+ auth_name_r = self.auth_name.replace(".", "-")
+ node.full_name = node.name + "." + self.exp_name + "." + \
+ self.proj_name + "." + auth_name_r + \
+ "." + self.auth_name
+
+ subprocess.call(["java", "-jar", self.jfed_jar, "create", "-S", \
+ self.proj_name, "--rspec", \
+ self.rspec, "-s", \
+ self.exp_name, "-p", self.cert_file, "-k", \
+ "usercert,userkeys,shareduserallkeys", \
+ "--create-slice",\
+ "--manifest", self.manifest,
+ "-P", self.password, \
+ "-e", self.exp_hours])
+
+ rspec = xml.parse(self.manifest)
+ xml_nodes = rspec.getElementsByTagName("node")
+
+ for xml_node in xml_nodes:
+ n_name = xml_node.getAttribute("client_id")
+ intfs = xml_node.getElementsByTagName("interface")
+ for link in links:
+ if link.node_a.name == n_name:
+ interface = link.int_a
+ if link.node_b.name == n_name:
+ interface = link.int_b
+ for intf in intfs:
+ comp_id = intf.getAttribute("component_id")
+ comp_arr = comp_id.split(":")
+ interface.name = comp_arr[-1]
+ xml_ip = intf.getElementsByTagName("ip")
+ interface.ip = xml_ip[0].getAttribute("address")