# # 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 emulab_support as es 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, username = "", password = "", proj_name = "", exp_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): return @abc.abstractmethod def swap_exp_in(self): return @abc.abstractmethod def wait_until_nodes_up(self): return @abc.abstractmethod def complete_experiment_graph(self, nodes, links): return @abc.abstractmethod def execute_command(self, hostname, command, time_out = 3): return @abc.abstractmethod def copy_file_to_testbed(self, hostname, text, file_name): return # Represents an emulab testbed info # # @url [string] URL of the testbed # @image [string] specific image to use # class EmulabTestbed: def __init__(self, username = "", password = "", proj_name = "ARCFIRE", exp_name = "", url = "wall1.ilabt.iminds.be", image = "UBUNTU14-64-STD"): Testbed.__init__(self, username, password, proj_name, exp_name) self.url = url self.image = image def create_experiment(self, nodes, links): es.create_experiment(self, nodes, links) def swap_exp_in(self): es.swap_exp_in(self) def wait_until_nodes_up(self): es.wait_until_nodes_up(self) def complete_experiment_graph(self, nodes, links): es.complete_experiment_graph(self, nodes, links) def execute_command(self, hostname, command, time_out = 3): es.execute_command(self, hostname, command, time_out = 3) def copy_file_to_testbed(self, hostname, text, file_name): es.copy_file_to_testbed(self, hostname, text, file_name) # 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 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 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, name, testbed, nodes = list()): self.name = name self.nodes = nodes self.testbed = testbed def __repr__(self): s = "%s:" % self.name for n in self.nodes: s += "\n" + str(n) return s def add_node(self, node): self.nodes.append(node) def add_node(self, node): self.nodes.remove(node) def run(self): self.links = get_links(self.nodes) self.testbed.create_experiment(self.nodes, self.links) self.testbed.swap_exp_in() self.testbed.wait_until_nodes_up() self.testbed.complete_experiment_graph(self.nodes, self.links) # An experiment over the IRATI implementation class IRATIExperiment(Experiment): def __init__(self, name, testbed, nodes = list()): Experiment.__init__(self, name, testbed, nodes) def run(self): print("[IRATI experiment %s] start" % self.name) Experiment.run(self) print("[IRATI experiment %s] end" % self.name) # An experiment over the RLITE implementation class RLITEExperiment(Experiment): def __init__(self, name, testbed, nodes = list()): Experiment.__init__(self, name, testbed, nodes) def run(self): print("[RLITE experiment %s] start" % self.name) Experiment.run(self) print("[RLITE experiment %s] end" % self.name) # An experiment over the Ouroboros implementation class OuroborosExperiment(Experiment): def __init__(self, name, testbed, nodes = list()): Experiment.__init__(self, name, testbed, nodes) def run(self): print("[Ouroboros experiment %s] start" % self.name) Experiment.run(self) print("[Ouroboros experiment %s] end" % self.name)