From 32d53fa835918353128152a54e8f8539a89f4388 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sat, 29 Apr 2017 18:30:37 +0200 Subject: examples: add example to run rlite on jFed --- examples/jfed-rlite.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100755 examples/jfed-rlite.py (limited to 'examples') diff --git a/examples/jfed-rlite.py b/examples/jfed-rlite.py new file mode 100755 index 0000000..ce058eb --- /dev/null +++ b/examples/jfed-rlite.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +from rumba.model import * + +import rumba.testbeds.jfed as jfed +import rumba.prototypes.rlite as rlite + +import rumba.log as log + +import argparse + + +description = "Script to run rlite on jfed" +epilog = "2017 H2020 ARCFIRE" + +argparser = argparse.ArgumentParser(description = description, + epilog = epilog) +argparser.add_argument('--user', type = str, default = 'vmaffio', + help = "jFed username") +argparser.add_argument('--cert', type = str, + help = "Absolute path to certificate (.pem) file" + " to be used with jFed", + default = '/home/vmaffione/Downloads/vmaffio-jfed.pem') +argparser.add_argument('--expname', type = str, default = 'pinocchio', + help = "Name of the experiment within the jFed testbed") + +args = argparser.parse_args() + +log.set_logging_level('DEBUG') + +n1 = NormalDIF("n1") + +e1 = ShimEthDIF("e1") + +a = Node("a", + difs = [n1, e1], + dif_registrations = {n1 : [e1]}) + +b = Node("b", + difs = [e1, n1], + dif_registrations = {n1 : [e1]}) + +tb = jfed.Testbed(exp_name = args.expname, + cert_file = args.cert, + username = args.user) + +exp = rlite.Experiment(tb, nodes = [a, b]) + +exp.swap_in() +exp.install_prototype() +exp.bootstrap_prototype() -- cgit v1.2.3 From a34a5d7ec2e6aa59a0a0aa0daba62f7738a1749a Mon Sep 17 00:00:00 2001 From: Sander Vrijders Date: Tue, 9 May 2017 11:50:17 +0200 Subject: rumba: model: Add StoryBoard for automated testing This adds an initial draft of the API to automate tests on a RINA network. --- examples/example.py | 20 ++++++++-------- rumba/model.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/example.py b/examples/example.py index 56193c2..383dbf1 100755 --- a/examples/example.py +++ b/examples/example.py @@ -17,7 +17,6 @@ import rumba.prototypes.irati as irati import rumba.log as log - log.set_logging_level('DEBUG') n1 = NormalDIF("n1", policies = {"rmt.pff": "lfa", @@ -27,19 +26,16 @@ e1 = ShimEthDIF("e1") a = Node("a", difs = [n1, e1], - dif_registrations = {n1 : [e1]}, - registrations = {"a.crap" : [n1]}, - bindings = {"a.crap" : "/usr/bin/crap"}) + dif_registrations = {n1 : [e1]}) b = Node("b", difs = [e1, n1], - dif_registrations = {n1 : [e1]}) + dif_registrations = {n1 : [e1]}, + end_user = True) -tb = qemu.Testbed(exp_name = "example1", - username = "root", - password = "root", - bzimage = '/home/vmaffione/git/rlite/demo/buildroot/bzImage', - initramfs = '/home/vmaffione/git/rlite/demo/buildroot/rootfs.cpio') +tb = jfed.Testbed(exp_name = "example1", + username = "user1", + cert_file = "/home/user1/cert.pem") exp = rl.Experiment(tb, nodes = [a, b]) @@ -48,5 +44,9 @@ print(exp) try: exp.swap_in() exp.bootstrap_prototype() + c1 = Client("rinaperf", options = "-t perf -s 1000 -c 10000") + s1 = Server("rinaperf", options = "-l", nodes = [a], clients = [c1]) + sb = StoryBoard(exp, 3600, servers = [s1]) + sb.start() finally: exp.swap_out() diff --git a/rumba/model.py b/rumba/model.py index 0b9fc7a..91f33a6 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -152,7 +152,7 @@ class SSHConfig: # class Node: def __init__(self, name, difs=None, dif_registrations=None, - registrations=None, bindings=None): + registrations=None, bindings=None, end_user=False): self.name = name if difs is None: difs = list() @@ -170,6 +170,7 @@ class Node: self.bindings = bindings self.ssh_config = SSHConfig(name) self.ipcps = [] + self.end_user = end_user self._validate() @@ -564,3 +565,67 @@ class Experiment: def swap_out(self): # Undo the testbed (testbed-specific) self.testbed.swap_out(self) + +# Base class for client programs +# +# @ap: Application Process binary +# @options: Options to pass to the binary +# +class Client: + def __init__(self, ap, options=None): + self.ap = ap + self.options = options + +# Base class for server programs +# +# @ap: Application Process binary +# @options: Options to pass to the binary +# @max_clients: Maximum number of clients to serve +# @clients: Client binaries that will use this server +# @nodes: Specific nodes to start this server on +# +class Server: + def __init__(self, ap, options=None, max_clients=None, + clients=None, nodes=None): + self.ap = ap + self.options = options + self.max_clients = max_clients + if clients is None: + clients = list() + self.clients = clients + self.nodes = nodes + + def add_client(self, client): + self.clients.append(client) + + def del_client(self, client): + self.clients.remove(client) + + def add_node(self, node): + self.nodes.append(node) + + def del_node(self, node): + self.nodes.remove(node) + +# Base class for ARCFIRE storyboards +# +# @experiment: Experiment to use as input +# @duration: Duration of the whole storyboard +# @servers: App servers available in the network +# +class StoryBoard: + def __init__(self, experiment, duration, servers=None): + self.experiment = experiment + self.duration = duration + if servers is None: + servers = list() + self.servers = servers + + def add_server(self, server): + self.servers.append(server) + + def del_server(self, server): + self.servers.remove(server) + + def start(self): + pass -- cgit v1.2.3 From ea1a4462a8b7a2fb7404e4652e0806795ba96863 Mon Sep 17 00:00:00 2001 From: Marco Capitani Date: Thu, 11 May 2017 10:51:26 +0200 Subject: Storyboard: added modifications to API to example.py --- examples/example.py | 4 ++-- rumba/model.py | 47 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/example.py b/examples/example.py index 383dbf1..7fe130d 100755 --- a/examples/example.py +++ b/examples/example.py @@ -44,8 +44,8 @@ print(exp) try: exp.swap_in() exp.bootstrap_prototype() - c1 = Client("rinaperf", options = "-t perf -s 1000 -c 10000") - s1 = Server("rinaperf", options = "-l", nodes = [a], clients = [c1]) + c1 = ClientBinary("rinaperf", options = "-t perf -s 1000 -c 10000") + s1 = Server("rinaperf", arrival_rate=2, mean_duration=5, options = "-l", nodes = [a], clients = [c1]) sb = StoryBoard(exp, 3600, servers = [s1]) sb.start() finally: diff --git a/rumba/model.py b/rumba/model.py index 97af536..36ac5c3 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -22,6 +22,8 @@ import abc import random +import time + import rumba.log as log @@ -567,16 +569,50 @@ class Experiment: # Undo the testbed (testbed-specific) self.testbed.swap_out(self) + # Base class for client programs # # @ap: Application Process binary # @options: Options to pass to the binary # -class Client: - def __init__(self, ap, duration, options=None): +class ClientBinary(object): + def __init__(self, ap, options=None): self.ap = ap self.options = options - self.duration = duration # in seconds + + def start_process(self, node, duration, start_time): + return ClientProcess(self.ap, node, duration, start_time, self.options) + + +# Base class for client processes +# +# @ap: Application Process binary +# @node: The node on which this process should run +# @duration: The time (in seconds) this process should run +# @start_time: The time at which this process is started. +# @options: Options to pass to the binary +# +class ClientProcess(ClientBinary): + def __init__(self, ap, node, duration, start_time, options=None): + super(ClientProcess, self).__init__(ap, options=options) + self.node = node + self.duration = duration + self.start_time = start_time + self.run() + self.running = True + + def run(self): + pass # TODO to be implemented + + def stop(self): + pass # TODO to be implemented + + def check(self, now): + if not self.running: + return + if now - self.start_time >= self.duration: + self.stop() + # Base class for server programs # @@ -623,12 +659,11 @@ class Server: """ pass - def make_client(self): + def make_client_process(self): """Returns a client of this server""" if len(self.clients) == 0: raise Exception("Server %s has empty client list," % (self,)) - placeholder = 10 # TODO: Should be a sample from Exp(mean_duration) - return Client(random.choice(self.clients), placeholder) + pass # TODO should return a ClientProcess # Base class for ARCFIRE storyboards -- cgit v1.2.3 From e78c92993406bc4ca24a1c38db7d3195901b28d9 Mon Sep 17 00:00:00 2001 From: Marco Capitani Date: Thu, 11 May 2017 11:32:11 +0200 Subject: Storyboard: small fixes to API --- examples/example.py | 2 +- rumba/model.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/example.py b/examples/example.py index 7fe130d..a3fe5ec 100755 --- a/examples/example.py +++ b/examples/example.py @@ -44,7 +44,7 @@ print(exp) try: exp.swap_in() exp.bootstrap_prototype() - c1 = ClientBinary("rinaperf", options = "-t perf -s 1000 -c 10000") + c1 = Client("rinaperf", options ="-t perf -s 1000 -c 10000") s1 = Server("rinaperf", arrival_rate=2, mean_duration=5, options = "-l", nodes = [a], clients = [c1]) sb = StoryBoard(exp, 3600, servers = [s1]) sb.start() diff --git a/rumba/model.py b/rumba/model.py index 36ac5c3..285d937 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -155,7 +155,7 @@ class SSHConfig: # class Node: def __init__(self, name, difs=None, dif_registrations=None, - registrations=None, bindings=None, end_user=False): + registrations=None, bindings=None, client=False): self.name = name if difs is None: difs = list() @@ -173,7 +173,7 @@ class Node: self.bindings = bindings self.ssh_config = SSHConfig(name) self.ipcps = [] - self.end_user = end_user + self.client = client self._validate() @@ -575,7 +575,7 @@ class Experiment: # @ap: Application Process binary # @options: Options to pass to the binary # -class ClientBinary(object): +class Client(object): def __init__(self, ap, options=None): self.ap = ap self.options = options @@ -592,7 +592,7 @@ class ClientBinary(object): # @start_time: The time at which this process is started. # @options: Options to pass to the binary # -class ClientProcess(ClientBinary): +class ClientProcess(Client): def __init__(self, ap, node, duration, start_time, options=None): super(ClientProcess, self).__init__(ap, options=options) self.node = node @@ -618,6 +618,7 @@ class ClientProcess(ClientBinary): # # @ap: Application Process binary # @arrival_rate: Average requests/s to be received by this server +# @mean_duration: Average duration of a client connection (in seconds) # @options: Options to pass to the binary # @max_clients: Maximum number of clients to serve # @clients: Client binaries that will use this server -- cgit v1.2.3 From 10da73cd31a26ad2b4a95bb1ec3d02f6afc57dc6 Mon Sep 17 00:00:00 2001 From: Marco Capitani Date: Thu, 11 May 2017 12:03:08 +0200 Subject: Storyboard missing end_user->client change --- examples/example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/example.py b/examples/example.py index a3fe5ec..1acc883 100755 --- a/examples/example.py +++ b/examples/example.py @@ -31,7 +31,7 @@ a = Node("a", b = Node("b", difs = [e1, n1], dif_registrations = {n1 : [e1]}, - end_user = True) + client = True) tb = jfed.Testbed(exp_name = "example1", username = "user1", -- cgit v1.2.3 From 2cf3cb3e9999a00ca21742f7a63c3cff15fce704 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Wed, 31 May 2017 14:16:22 +0200 Subject: examples: jfed-rlite: use try-finally --- examples/jfed-rlite.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/jfed-rlite.py b/examples/jfed-rlite.py index ce058eb..d80b56e 100755 --- a/examples/jfed-rlite.py +++ b/examples/jfed-rlite.py @@ -46,6 +46,9 @@ tb = jfed.Testbed(exp_name = args.expname, exp = rlite.Experiment(tb, nodes = [a, b]) -exp.swap_in() -exp.install_prototype() -exp.bootstrap_prototype() +try: + exp.swap_in() + exp.install_prototype() + exp.bootstrap_prototype() +finally: + exp.swap_out() -- cgit v1.2.3 From f75a6d9b31479060dd1d1a52102a714ae7c48154 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Thu, 1 Jun 2017 15:17:59 +0200 Subject: model: remove registration and bindings Fixes #7 --- examples/two-layers.py | 4 +--- rumba/model.py | 47 +++---------------------------------------- rumba/prototypes/irati.py | 8 +------- rumba/prototypes/ouroboros.py | 25 ----------------------- 4 files changed, 5 insertions(+), 79 deletions(-) (limited to 'examples') diff --git a/examples/two-layers.py b/examples/two-layers.py index 9d1a6b3..b4e4e64 100755 --- a/examples/two-layers.py +++ b/examples/two-layers.py @@ -32,9 +32,7 @@ e3 = ShimEthDIF("e3") a = Node("a", difs = [n3, n4, n1, e1], - dif_registrations = {n4: [n1], n3: [n1], n1 : [e1]}, - registrations = {"rinaperf.server" : [n3]}, - bindings = {"rinaperf.server" : "/usr/bin/rinaperf"}) + dif_registrations = {n4: [n1], n3: [n1], n1 : [e1]}) b = Node("b", difs = [n1, e1, e2], diff --git a/rumba/model.py b/rumba/model.py index 285d937..9fbda57 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -150,12 +150,10 @@ class SSHConfig: # # @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=None, dif_registrations=None, - registrations=None, bindings=None, client=False): + client=False): self.name = name if difs is None: difs = list() @@ -165,12 +163,6 @@ class Node: if dif_registrations is None: dif_registrations = dict() self.dif_registrations = dif_registrations - if registrations is None: - registrations = dict() - self.registrations = registrations - if bindings is None: - bindings = dict() - self.bindings = bindings self.ssh_config = SSHConfig(name) self.ipcps = [] self.client = client @@ -188,17 +180,13 @@ class Node: "to be part of DIF %s" % (self.name, dif.name)) def _validate(self): - # Check that DIFs referenced in self.dif_registrations and - # in self.registrations are part of self.difs + # Check that DIFs referenced in self.dif_registrations + # are part of self.difs for upper in self.dif_registrations: self._undeclared_dif(upper) for lower in self.dif_registrations[upper]: self._undeclared_dif(lower) - for appl in self.registrations: - for dif in self.registrations[appl]: - self._undeclared_dif(dif) - def __repr__(self): s = "Node " + self.name + ":\n" @@ -217,19 +205,6 @@ class Node: s += ", ".join(rl) s += " ]\n" - s += " Name registrations: [ " - for name in self.registrations: - difs = self.registrations[name] - s += "%s => [ " % name - s += ", ".join([dif.name for dif in difs]) - s += " ]" - s += " ]\n" - - s += " Bindings: [ " - s += ", ".join(["'%s' => '%s'" % (ap, self.bindings[ap]) - for ap in self.bindings]) - s += " ]\n" - return s def __hash__(self): @@ -259,22 +234,6 @@ class Node: self.dif_registrations[upper].remove(lower) self._validate() - def add_registration(self, name, dif): - self.dif_registrations[name].append(dif) - self._validate() - - def del_registration(self, name, dif): - self.dif_registrations[name].remove(dif) - self._validate() - - def add_binding(self, name, ap): - self.bindings[name] = ap - self._validate() - - def del_binding(self, name): - del self.bindings[name] - self._validate() - # Base class representing an IPC Process to be created in the experiment # diff --git a/rumba/prototypes/irati.py b/rumba/prototypes/irati.py index e24dd60..f9c715f 100644 --- a/rumba/prototypes/irati.py +++ b/rumba/prototypes/irati.py @@ -228,16 +228,10 @@ class Experiment(mod.Experiment): next_vlan += 10 self.shim2vlan[dif.name] = vlan - # TODO: what format are the mappings registered in? Is this ok? - app_mappings = [] - for node in self.nodes: - app_mappings += [{'name': app, 'dif': self.dif_name(dif)} - for app in node.registrations - for dif in node.registrations[app]] - # If some app directives were specified, use those to build da.map. # Otherwise, assume the standard applications are to be mapped in # the DIF with the highest rank. + app_mappings = [] if len(app_mappings) == 0: if len(self.dif_ordering) > 0: for adm in \ diff --git a/rumba/prototypes/ouroboros.py b/rumba/prototypes/ouroboros.py index 9c164e7..01b8b72 100644 --- a/rumba/prototypes/ouroboros.py +++ b/rumba/prototypes/ouroboros.py @@ -50,27 +50,6 @@ class Experiment(mod.Experiment): ssh.execute_commands(self.testbed, node.ssh_config, cmds, time_out=None) - 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.ssh_config, - cmds, time_out=None) - - def reg_names(self): - for node in self.nodes: - cmds = list() - for name, difs in node.registrations.items(): - cmd = "irm r n " + name - for dif in difs: - cmd += " dif " + dif.name - cmds.append(cmd) - - ssh.execute_commands(self.testbed, node.ssh_config, cmds, - time_out=None) - def create_ipcps(self): for node in self.nodes: cmds = list() @@ -147,12 +126,8 @@ class Experiment(mod.Experiment): def bootstrap_prototype(self): logger.info("Starting IRMd on all nodes...") self.setup_ouroboros() - logger.info("Binding names...") - self.bind_names() logger.info("Creating IPCPs") self.create_ipcps() logger.info("Enrolling IPCPs...") self.enroll_ipcps() - logger.info("Registering names...") - self.reg_names() logger.info("All done, have fun!") -- cgit v1.2.3 From 9424b5f36e2ef87d4bd01b9b4e5fc83965ec33b6 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Thu, 1 Jun 2017 17:12:28 +0200 Subject: qemu plugin: automatic download of buildroot images --- examples/two-layers.py | 6 ++---- rumba/testbeds/qemu.py | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/two-layers.py b/examples/two-layers.py index b4e4e64..b3622fd 100755 --- a/examples/two-layers.py +++ b/examples/two-layers.py @@ -48,11 +48,9 @@ d = Node("d", tb = qemu.Testbed(exp_name = "twolayers", username = "root", - password = "root", - bzimage = '/home/vmaffione/git/rlite/demo/buildroot/bzImage', - initramfs = '/home/vmaffione/git/rlite/demo/buildroot/rootfs.cpio') + password = "root") -exp = rl.Experiment(tb, nodes = [a, b, c, d]) +exp = irati.Experiment(tb, nodes = [a, b, c, d]) print(exp) diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py index a916525..894bee5 100644 --- a/rumba/testbeds/qemu.py +++ b/rumba/testbeds/qemu.py @@ -26,25 +26,41 @@ import os import rumba.model as mod import rumba.log as log import rumba.ssh_support as ssh_support +import wget logger = log.get_logger(__name__) class Testbed(mod.Testbed): - def __init__(self, exp_name, bzimage, initramfs, proj_name="ARCFIRE", + def __init__(self, exp_name, bzimage=None, initramfs=None, proj_name="ARCFIRE", password="root", username="root", use_vhost=True, qemu_logs_dir=None): mod.Testbed.__init__(self, exp_name, username, password, proj_name) self.vms = {} self.shims = [] - self.bzimage = bzimage - self.initramfs = initramfs self.vhost = use_vhost self.qemu_logs_dir = os.getcwd() if qemu_logs_dir is None \ else qemu_logs_dir self.boot_processes = [] + # Download the proper buildroot image, if not provided explicitely + url_prefix = "https://bitbucket.org/vmaffione/rina-images/downloads/" + if not bzimage: + bzimage = 'irati.bzImage' + if not os.path.exists(bzimage): + print("Downloading %s" % (url_prefix + bzimage)) + wget.download(url_prefix + bzimage) + print("\n") + if not initramfs: + initramfs = 'irati.rootfs.cpio' + if not os.path.exists(initramfs): + print("Downloading %s" % (url_prefix + initramfs)) + wget.download(url_prefix + initramfs) + print("\n") + self.bzimage = bzimage + self.initramfs = initramfs + @staticmethod def _run_command_chain(commands, results_queue, error_queue, ignore_errors=False): -- cgit v1.2.3 From acd8339aeaff05d678c3663b0a36af9e73fda618 Mon Sep 17 00:00:00 2001 From: Sander Vrijders Date: Wed, 7 Jun 2017 09:21:11 +0200 Subject: examples: Add mouse example This adds the infamous mouse to the examples directory. --- examples/mouse.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 examples/mouse.py (limited to 'examples') diff --git a/examples/mouse.py b/examples/mouse.py new file mode 100644 index 0000000..25e2487 --- /dev/null +++ b/examples/mouse.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python + +# An example script using the rumba package + +from rumba.model import * + +# import testbed plugins +import rumba.testbeds.emulab as emulab +import rumba.testbeds.jfed as jfed +import rumba.testbeds.faketestbed as fake +import rumba.testbeds.qemu as qemu + +# import prototype plugins +import rumba.prototypes.ouroboros as our +import rumba.prototypes.rlite as rl +import rumba.prototypes.irati as irati + +n01 = NormalDIF("n01") + +e01 = ShimEthDIF("e01") +e02 = ShimEthDIF("e02") +e03 = ShimEthDIF("e03") +e04 = ShimEthDIF("e04") +e05 = ShimEthDIF("e05") +e06 = ShimEthDIF("e06") +e07 = ShimEthDIF("e07") +e08 = ShimEthDIF("e08") +e09 = ShimEthDIF("e09") +e10 = ShimEthDIF("e10") +e11 = ShimEthDIF("e11") +e12 = ShimEthDIF("e12") +e13 = ShimEthDIF("e13") +e14 = ShimEthDIF("e14") +e15 = ShimEthDIF("e15") +e16 = ShimEthDIF("e16") +e17 = ShimEthDIF("e17") + + +a = Node("a", + difs = [n01, e01, e06, e13 ], + dif_registrations = {n01 : [e01, e06, e13]}) + +b = Node("b", + difs = [n01, e01, e02, e04], + dif_registrations = {n01 : [e01, e02, e04]}) + +c = Node("c", + difs = [n01, e02, e03], + dif_registrations = {n01 : [e02, e03]}) + +d = Node("d", + difs = [n01, e03, e04, e05], + dif_registrations = {n01 : [e03, e04, e05]}) + +e = Node("e", + difs = [n01, e05, e06, e07], + dif_registrations = {n01 : [e05, e06, e07]}) + +f = Node("f", + difs = [n01, e07, e08], + dif_registrations = {n01 : [e07, e08]}) + +g = Node("g", + difs = [n01, e08, e09, e14], + dif_registrations = {n01 : [e08, e09, e14]}) + +h = Node("h", + difs = [n01, e09, e10, e15], + dif_registrations = {n01 : [e09, e10, e15]}) + +i = Node("i", + difs = [n01, e10, e11, e16], + dif_registrations = {n01 : [e10, e11, e16]}) + +j = Node("j", + difs = [n01, e11, e12], + dif_registrations = {n01 : [e11, e12]}) + +k = Node("k", + difs = [n01, e12, e13], + dif_registrations = {n01 : [e12, e13]}) + +l = Node("l", + difs = [n01, e14, e15], + dif_registrations = {n01 : [e14, e15]}) + +m = Node("m", + difs = [n01, e16, e17], + dif_registrations = {n01 : [e16, e17]}) + +n = Node("n", + difs = [n01, e17], + dif_registrations = {n01 : [e17]}) + +tb = jfed.Testbed(exp_name = "mouse2", + cert_file = "/home/sander/cert.pem", + username = "sander") + +exp = rl.Experiment(tb, nodes = [a, b, c, d, e, f, g, h, i, j, k, l, m, n]) + +print(exp) + +exp.swap_in() +exp.install_prototype() +exp.bootstrap_prototype() -- cgit v1.2.3