From fd37777d333c991c094aa128ae5f30d9fa80a9d6 Mon Sep 17 00:00:00 2001 From: Sander Vrijders Date: Thu, 9 Feb 2017 15:43:15 +0100 Subject: rumba: Rename package to rumba This renames the package to rumba, since rhumba was already taken on the PyPi. --- examples/example.py | 26 ++--- rhumba/__init__.py | 0 rhumba/model.py | 255 ----------------------------------------- rhumba/prototypes/__init__.py | 0 rhumba/prototypes/irati.py | 48 -------- rhumba/prototypes/ouroboros.py | 58 ---------- rhumba/prototypes/rlite.py | 49 -------- rhumba/ssh_support.py | 156 ------------------------- rhumba/testbeds/__init__.py | 0 rhumba/testbeds/emulab.py | 244 --------------------------------------- rhumba/testbeds/faketestbed.py | 30 ----- rhumba/testbeds/jfed.py | 148 ------------------------ rumba/__init__.py | 0 rumba/model.py | 255 +++++++++++++++++++++++++++++++++++++++++ rumba/prototypes/__init__.py | 0 rumba/prototypes/irati.py | 48 ++++++++ rumba/prototypes/ouroboros.py | 58 ++++++++++ rumba/prototypes/rlite.py | 49 ++++++++ rumba/ssh_support.py | 156 +++++++++++++++++++++++++ rumba/testbeds/__init__.py | 0 rumba/testbeds/emulab.py | 244 +++++++++++++++++++++++++++++++++++++++ rumba/testbeds/faketestbed.py | 30 +++++ rumba/testbeds/jfed.py | 152 ++++++++++++++++++++++++ setup.py | 8 +- 24 files changed, 1009 insertions(+), 1005 deletions(-) delete mode 100644 rhumba/__init__.py delete mode 100644 rhumba/model.py delete mode 100644 rhumba/prototypes/__init__.py delete mode 100644 rhumba/prototypes/irati.py delete mode 100644 rhumba/prototypes/ouroboros.py delete mode 100644 rhumba/prototypes/rlite.py delete mode 100644 rhumba/ssh_support.py delete mode 100644 rhumba/testbeds/__init__.py delete mode 100644 rhumba/testbeds/emulab.py delete mode 100644 rhumba/testbeds/faketestbed.py delete mode 100644 rhumba/testbeds/jfed.py create mode 100644 rumba/__init__.py create mode 100644 rumba/model.py create mode 100644 rumba/prototypes/__init__.py create mode 100644 rumba/prototypes/irati.py create mode 100644 rumba/prototypes/ouroboros.py create mode 100644 rumba/prototypes/rlite.py create mode 100644 rumba/ssh_support.py create mode 100644 rumba/testbeds/__init__.py create mode 100644 rumba/testbeds/emulab.py create mode 100644 rumba/testbeds/faketestbed.py create mode 100644 rumba/testbeds/jfed.py diff --git a/examples/example.py b/examples/example.py index ad4116e..4430fb5 100755 --- a/examples/example.py +++ b/examples/example.py @@ -1,18 +1,18 @@ #!/usr/bin/env python -# An example script using rhumba.py +# An example script using the rumba package -from rhumba.model import * +from rumba.model import * # import testbed plugins -import rhumba.testbeds.emulab as emulab -import rhumba.testbeds.jfed as jfed -import rhumba.testbeds.faketestbed as fake +import rumba.testbeds.emulab as emulab +import rumba.testbeds.jfed as jfed +import rumba.testbeds.faketestbed as fake # import prototype plugins -import rhumba.prototypes.ouroboros as our -import rhumba.prototypes.rlite as rl -import rhumba.prototypes.irati as irati +import rumba.prototypes.ouroboros as our +import rumba.prototypes.rlite as rl +import rumba.prototypes.irati as irati n1 = NormalDIF("n1", policies = {"rmt.pff": "lfa", "security-manager": "passwd"}) @@ -29,11 +29,11 @@ b = Node("b", difs = [e1, n1], dif_registrations = {n1 : [e1]}) -#tb = jfed.jFedTestbed(exp_name = "letest", - # username = "sander", - # cert_file = "cert.pem", - # jfed_jar = "jfed_cli/experimenter-cli.jar") -tb = fake.FakeTestbed(exp_name = "x", username = "yy") +tb = jfed.jFedTestbed(exp_name = "letest", + username = "sander", + cert_file = "cert.pem", + jfed_jar = "jfed_cli/experimenter-cli.jar") +#tb = fake.FakeTestbed(exp_name = "x", username = "yy") exp = irati.IRATIExperiment(tb, nodes = [a, b]) diff --git a/rhumba/__init__.py b/rhumba/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/rhumba/model.py b/rhumba/model.py deleted file mode 100644 index 0346a8c..0000000 --- a/rhumba/model.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# A library to manage ARCFIRE experiments -# -# Sander Vrijders -# Vincenzo Maffione -# -# 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/rhumba/prototypes/__init__.py b/rhumba/prototypes/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/rhumba/prototypes/irati.py b/rhumba/prototypes/irati.py deleted file mode 100644 index beb266f..0000000 --- a/rhumba/prototypes/irati.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# Commands to setup and instruct IRATI -# -# Vincenzo Maffione -# -# 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 rhumba.ssh_support as ssh -from rhumba.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/rhumba/prototypes/ouroboros.py b/rhumba/prototypes/ouroboros.py deleted file mode 100644 index eb1f824..0000000 --- a/rhumba/prototypes/ouroboros.py +++ /dev/null @@ -1,58 +0,0 @@ -# -# Commands to instruct Ouroboros -# -# Sander Vrijders -# -# 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 rhumba.ssh_support as ssh -from rhumba.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/rhumba/prototypes/rlite.py b/rhumba/prototypes/rlite.py deleted file mode 100644 index e68377c..0000000 --- a/rhumba/prototypes/rlite.py +++ /dev/null @@ -1,49 +0,0 @@ -# -# Commands to setup and instruct rlite -# -# Vincenzo Maffione -# -# 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 rhumba.ssh_support as ssh -from rhumba.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/rhumba/ssh_support.py b/rhumba/ssh_support.py deleted file mode 100644 index 6bc892f..0000000 --- a/rhumba/ssh_support.py +++ /dev/null @@ -1,156 +0,0 @@ -# -# SSH support for Rhumba -# -# Sander Vrijders -# -# 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/rhumba/testbeds/__init__.py b/rhumba/testbeds/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/rhumba/testbeds/emulab.py b/rhumba/testbeds/emulab.py deleted file mode 100644 index ec8e6d9..0000000 --- a/rhumba/testbeds/emulab.py +++ /dev/null @@ -1,244 +0,0 @@ -# -# Emulab support for Rhumba -# -# Sander Vrijders -# Wouter Tavernier -# -# 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 rhumba.ssh_support as ssh -from rhumba.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 Rhumba\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/rhumba/testbeds/faketestbed.py b/rhumba/testbeds/faketestbed.py deleted file mode 100644 index 14ce2b3..0000000 --- a/rhumba/testbeds/faketestbed.py +++ /dev/null @@ -1,30 +0,0 @@ -# -# Fake testbed for Rhumba testing -# -# Vincenzo Maffione -# -# 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 rhumba.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/rhumba/testbeds/jfed.py b/rhumba/testbeds/jfed.py deleted file mode 100644 index 7a504cd..0000000 --- a/rhumba/testbeds/jfed.py +++ /dev/null @@ -1,148 +0,0 @@ -# -# jFed support for Rhumba -# -# Sander Vrijders -# -# 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 rhumba.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_id = link.int_a.id = node.name + ":if" + str(node.ifs) - if link.node_b == node: - link_id = link.int_b.id = node.name + ":if" + str(node.ifs) - - 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") diff --git a/rumba/__init__.py b/rumba/__init__.py new file mode 100644 index 0000000..e69de29 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 +# Vincenzo Maffione +# +# 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 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 +# +# 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 +# +# 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 +# +# 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 +# +# 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 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 +# Wouter Tavernier +# +# 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 +# +# 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 +# +# 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") diff --git a/setup.py b/setup.py index d09f64f..4b8afae 100644 --- a/setup.py +++ b/setup.py @@ -8,15 +8,15 @@ with open(path.join(here, 'README'), encoding='utf-8') as f: long_description = f.read() setup( - name = "Rhumba", + name = "Rumba", version = "0.1", - url = "https://gitlab.com/arcfire/rhumba", + url = "https://gitlab.com/arcfire/rumba", keywords = "rina measurement testbed", author = "Sander Vrijders", author_email = "sander.vrijders@intec.ugent.be", license = "LGPL", - description = "Rhumba measurement framework for RINA", + description = "Rumba measurement framework for RINA", long_description = long_description, - packages = ["rhumba", "rhumba.testbeds", "rhumba.prototypes"], + packages = ["rumba", "rumba.testbeds", "rumba.prototypes"], install_requires = ["paramiko", "wheel"] ) -- cgit v1.2.3