From 6c5ef6498549db725a59638753cd491c4d8bd573 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sat, 18 Mar 2017 18:31:09 +0100 Subject: examples: add two layers example (from demonstrator) --- examples/two-layers.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100755 examples/two-layers.py diff --git a/examples/two-layers.py b/examples/two-layers.py new file mode 100755 index 0000000..3fba54e --- /dev/null +++ b/examples/two-layers.py @@ -0,0 +1,52 @@ +#!/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 prototype plugins +import rumba.prototypes.ouroboros as our +import rumba.prototypes.rlite as rl +import rumba.prototypes.irati as irati + +n1 = NormalDIF("n1") +n2 = NormalDIF("n2") +n3 = NormalDIF("n3") + +e1 = ShimEthDIF("e1") +e2 = ShimEthDIF("e2") +e3 = ShimEthDIF("e3") + +a = Node("a", + difs = [n3, n1, e1], + dif_registrations = {n3: [n1], n1 : [e1]}, + registrations = {"rinaperf.server" : [n3]}, + bindings = {"rinaperf.server" : "/usr/bin/rinaperf"}) + +b = Node("b", + difs = [n1, e1, e2], + dif_registrations = {n1 : [e1, e2]}) + +c = Node("c", + difs = [n3, n1, n2, e2, e3], + dif_registrations = {n3: [n1, n2], n1 : [e2], n2: [e3]}) + +d = Node("d", + difs = [n3, n2, e3], + dif_registrations = {n3: [n2], n2 : [e3]}) + +tb = fake.Testbed(exp_name = "twolayers", + username = "vmaffio", + proj_name = "cert.pem", + password = "") + +exp = irati.Experiment(tb, nodes = [a, b, c, d]) + +print(exp) + +exp.run() -- cgit v1.2.3 From f9a1c59b35931fc55cc9d09385c0c127c2e7e1d4 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sat, 18 Mar 2017 19:17:02 +0100 Subject: model: fix __repr__ to correctly handle multiple registration entries --- rumba/model.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index 23db86f..19c1182 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -168,28 +168,32 @@ class Node: def __repr__(self): s = "Node " + self.name + ":\n" - s += " IPCPs in DIFs: [" - for d in self.difs: - s += " %s" % d.name + + s += " DIFs: [ " + s += " ".join([d.name for d in self.difs]) s += " ]\n" + s += " DIF registrations: [ " + rl = [] 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 += " ]" + x = "%s => [" % dif_a.name + x += " ".join([dif_b.name for dif_b in difs]) + x += "]" + rl.append(x) + s += ", ".join(rl) 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 => [ " % name + s += " ".join([dif.name for dif in difs]) s += " ]" s += " ]\n" + s += " Bindings: [ " - for ap, name in self.bindings.items(): - s += "'%s' => '%s'" % (ap, name) + s += ", ".join(["'%s' => '%s'" % (ap, name) for ap, name in self.bindings.items()]) s += " ]\n" + return s def add_dif(self, dif): -- cgit v1.2.3 From 6acb3dc53fbf1ce1835673b8df6647421416df4d Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sat, 18 Mar 2017 19:26:12 +0100 Subject: rumba: avoid dict.items to ease portability across Python versions --- rumba/model.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index 19c1182..2337d19 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -175,7 +175,8 @@ class Node: s += " DIF registrations: [ " rl = [] - for dif_a, difs in self.dif_registrations.items(): + for dif_a in self.dif_registrations: + difs = self.dif_registrations[dif_a] x = "%s => [" % dif_a.name x += " ".join([dif_b.name for dif_b in difs]) x += "]" @@ -184,14 +185,16 @@ class Node: s += " ]\n" s += " Name registrations: [ " - for name, difs in self.registrations.items(): + for name in self.registrations: + difs = self.registrations[name] s += "%s => [ " % name - s += " ".join([dif.name for dif in difs]) + s += ", ".join([dif.name for dif in difs]) s += " ]" s += " ]\n" s += " Bindings: [ " - s += ", ".join(["'%s' => '%s'" % (ap, name) for ap, name in self.bindings.items()]) + s += ", ".join(["'%s' => '%s'" % (ap, self.bindings[ap]) \ + for ap in self.bindings]) s += " ]\n" return s -- cgit v1.2.3 From 88a556c77eca9c6b4902caba4718848b58fbc363 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sat, 18 Mar 2017 22:54:06 +0100 Subject: model: compute DIF topological ordering --- rumba/model.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/rumba/model.py b/rumba/model.py index 2337d19..f48b0dd 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -269,6 +269,69 @@ class Experiment: def del_node(self, node): self.nodes.remove(node) + # Examine the nodes, DIFs, registrations and compute the registration + # and enrollment order, etc. + def generate(self): + + ###### Compute registration/enrollment order for DIFs ####### + + # Compute DIFs dependency graph, as both adjacency and incidence list. + difsdeps_adj = dict() + difsdeps_inc = dict() + + for node in self.nodes: + for upper in node.dif_registrations: + for lower in node.dif_registrations[upper]: + if upper not in difsdeps_inc: + difsdeps_inc[upper] = set() + if lower not in difsdeps_inc: + difsdeps_inc[lower] = set() + if upper not in difsdeps_adj: + difsdeps_adj[upper] = set() + if lower not in difsdeps_adj: + difsdeps_adj[lower] = set() + difsdeps_inc[upper].add(lower) + difsdeps_adj[lower].add(upper) + + # Kahn's algorithm below only needs per-node count of + # incident edges, so we compute these counts from the + # incidence list and drop the latter. + difsdeps_inc_cnt = dict() + for dif in difsdeps_inc: + difsdeps_inc_cnt[dif] = len(difsdeps_inc[dif]) + del difsdeps_inc + + #print(difsdeps_adj) + #print(difsdeps_inc_cnt) + + # Run Kahn's algorithm to compute topological ordering on the DIFs graph. + frontier = set() + self.dif_ordering = [] + for dif in difsdeps_inc_cnt: + if difsdeps_inc_cnt[dif] == 0: + frontier.add(dif) + + while len(frontier): + cur = frontier.pop() + self.dif_ordering.append(cur) + for nxt in difsdeps_adj[cur]: + difsdeps_inc_cnt[nxt] -= 1 + if difsdeps_inc_cnt[nxt] == 0: + frontier.add(nxt) + difsdeps_adj[cur] = set() + + circular_set = [dif for dif in difsdeps_inc_cnt if difsdeps_inc_cnt[dif] != 0] + if len(circular_set): + raise Exception("Fatal error: The specified DIFs topology has one or more"\ + "circular dependencies, involving the following"\ + " DIFs: %s" % circular_set) + + del difsdeps_inc_cnt + del difsdeps_adj + del circular_set + + print(self.dif_ordering) + # Realize the experiment, using a testbed-specific setup def swap_in(self): self.testbed.create_experiment(self.nodes, self.get_links()) -- cgit v1.2.3 From d0e0210ba5c17fe1cd86af4ffadb8cc16fc88533 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 10:08:29 +0100 Subject: mode: compute per-DIF enrollments --- rumba/model.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index f48b0dd..5c22185 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -236,6 +236,7 @@ class Experiment: nodes = list() self.nodes = nodes self.testbed = testbed + self.enrollment_strategy = 'minimal' # 'full-mesh', 'manual' def __repr__(self): s = "" @@ -269,12 +270,8 @@ class Experiment: def del_node(self, node): self.nodes.remove(node) - # Examine the nodes, DIFs, registrations and compute the registration - # and enrollment order, etc. - def generate(self): - - ###### Compute registration/enrollment order for DIFs ####### - + # Compute registration/enrollment order for DIFs + def compute_dif_ordering(self): # Compute DIFs dependency graph, as both adjacency and incidence list. difsdeps_adj = dict() difsdeps_inc = dict() @@ -326,11 +323,90 @@ class Experiment: "circular dependencies, involving the following"\ " DIFs: %s" % circular_set) - del difsdeps_inc_cnt - del difsdeps_adj - del circular_set + print("DIF topological ordering: %s" % self.dif_ordering) + + # Compute per-DIF graphs, to be called after compute_dif_ordering() + def compute_enrollments(self): + dif_graphs = dict() + self.enrollments = dict() + + for dif in self.dif_ordering: + neighsets = dict() + dif_graphs[dif] = dict() + first = None + + # For each N-1-DIF supporting this DIF, compute the set of nodes that + # share such N-1-DIF. This set will be called the 'neighset' of + # the N-1-DIF for the current DIF. + + for node in self.nodes: + if dif in node.dif_registrations: + dif_graphs[dif][node] = [] # init for later use + if first == None: # pick any node for later use + first = node + for lower_dif in node.dif_registrations[dif]: + if lower_dif not in neighsets: + neighsets[lower_dif] = [] + neighsets[lower_dif].append(node) + + # Build the graph, represented as adjacency list + for lower_dif in neighsets: + # Each neighset corresponds to a complete (sub)graph. + for node1 in neighsets[lower_dif]: + for node2 in neighsets[lower_dif]: + if node1 != node2: + dif_graphs[dif][node1].append((node2, lower_dif)) + + if first == None: + # This is a shim DIF, nothing to do + continue + + print("DIF graphs for %s" % dif) + for node in dif_graphs[dif]: + for edge in dif_graphs[dif][node]: + print("%s --> %s [%s]" % (node.name, edge[0].name, edge[1])) + + self.enrollments[dif] = [] + + if self.enrollment_strategy == 'minimal': + # To generate the list of enrollments, we simulate one, + # using breadth-first trasversal. + enrolled = set([first]) + frontier = set([first]) + while len(frontier): + cur = frontier.pop() + for edge in dif_graphs[dif][cur]: + if edge[0] not in enrolled: + enrolled.add(edge[0]) + self.enrollments[dif].append({'enrollee': edge[0], + 'enroller': cur, + 'lower_dif': edge[1]}) + frontier.add(edge[0]) + + elif self.enrollment_strategy == 'full-mesh': + for cur in dif_graphs[dif]: + for edge in dif_graphs[dif][cur]: + if cur < edge[0]: + self.enrollments[dif].append({'enrollee': cur, + 'enroller': edge[0], + 'lower_dif': edge[1]}) + + else: + # This is a bug + assert(False) + + print("Enrollments for %s" % dif) + for e in self.enrollments[dif]: + print(" %s --> %s through N-1-DIF %s" % \ + (e['enrollee'].name, + e['enroller'].name, + e['lower_dif'])) - print(self.dif_ordering) + # Examine the nodes, DIFs, registrations and compute the registration + # and enrollment order, etc. + def generate(self): + self.compute_dif_ordering() + self.compute_enrollments() # Realize the experiment, using a testbed-specific setup def swap_in(self): -- cgit v1.2.3 From 23b46068be8b698c55ab854eb5c2de2a3a14d453 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 10:22:36 +0100 Subject: class DIF and Node: add necessary methods for use in dict() and set() --- rumba/model.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rumba/model.py b/rumba/model.py index 5c22185..45d0473 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -91,6 +91,15 @@ class DIF: s = "DIF %s" % self.name return s + def __hash__(self): + return hash(self.name) + + def __eq__(self, other): + return other != None and self.name == other.name + + def __neq__(self, other): + return not (self == other) + def add_member(self, node): self.members.append(node) @@ -199,6 +208,15 @@ class Node: return s + def __hash__(self): + return hash(self.name) + + def __eq__(self, other): + return other != None and self.name == other.name + + def __neq__(self, other): + return not (self == other) + def add_dif(self, dif): self.difs.append(dif) dif.add_member(self) -- cgit v1.2.3 From d08f8d43a7446be09da5135aa1471836a215347b Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 10:27:35 +0100 Subject: model: more compact debug print for DIF graphs --- rumba/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index 45d0473..946d85d 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -379,10 +379,11 @@ class Experiment: # This is a shim DIF, nothing to do continue - print("DIF graphs for %s" % dif) + er = [] for node in dif_graphs[dif]: for edge in dif_graphs[dif][node]: - print("%s --> %s [%s]" % (node.name, edge[0].name, edge[1])) + er.append("%s --[%s]--> %s" % (node.name, edge[1].name, edge[0].name)) + print("DIF graph for %s: %s" % (dif, ', '.join(er))) self.enrollments[dif] = [] -- cgit v1.2.3 From 59d62c1acb7e9d7d362486636acc3843a7cc80ed Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 12:21:24 +0100 Subject: model: add IPCP class --- rumba/model.py | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index 946d85d..1ed67dc 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -243,6 +243,31 @@ class Node: def del_binding(self, name): del self.dif_bindings[name] +# Class representing an IPC Process to be created in the experiment +# +# @name [string]: IPCP name +# @node: Node where the IPCP gets created +# @dif: the DIF the IPCP belongs to +# +class IPCP: + def __init__(self, name, node, dif): + self.name = name + self.node = node + self.dif = dif + + def __repr__(self): + return "IPCP %s in DIF %s" % (self.name, self.dif.name) + + def __hash__(self): + return hash((self.name, self.dif.name)) + + def __eq__(self, other): + return other != None and self.name == other.name \ + and self.dif == other.dif + + def __neq__(self, other): + return not (self == other) + # Base class for ARCFIRE experiments # # @name [string] Name of the experiment @@ -255,6 +280,8 @@ class Experiment: self.nodes = nodes self.testbed = testbed self.enrollment_strategy = 'minimal' # 'full-mesh', 'manual' + self.dif_ordering = [] + self.enrollments = dict() def __repr__(self): s = "" @@ -343,7 +370,7 @@ class Experiment: print("DIF topological ordering: %s" % self.dif_ordering) - # Compute per-DIF graphs, to be called after compute_dif_ordering() + # Compute per-DIF enrollments, to be called after compute_dif_ordering() def compute_enrollments(self): dif_graphs = dict() self.enrollments = dict() @@ -421,11 +448,15 @@ class Experiment: e['enroller'].name, e['lower_dif'])) - # Examine the nodes, DIFs, registrations and compute the registration - # and enrollment order, etc. + def compute_ipcps(self): + pass + + # Examine the nodes and DIFs, compute the registration nd enrollment + # order, the list of IPCPs to create, registrations, ... def generate(self): self.compute_dif_ordering() self.compute_enrollments() + self.compute_ipcps() # Realize the experiment, using a testbed-specific setup def swap_in(self): -- cgit v1.2.3 From 450523ca3741a55c0663cca6f6a84afa6e084f00 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 12:41:28 +0100 Subject: model: add Node._validate() to check for consistency --- rumba/model.py | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index 1ed67dc..f34df52 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -174,6 +174,26 @@ class Node: bindings = dict() self.bindings = bindings self.full_name = name + self.ipcps = [] + + self._validate() + + def _undeclared_dif(self, dif): + if dif not in self.difs: + raise Exception("Invalid registration: node %s is not declared "\ + "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 + 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" @@ -184,10 +204,10 @@ class Node: s += " DIF registrations: [ " rl = [] - for dif_a in self.dif_registrations: - difs = self.dif_registrations[dif_a] - x = "%s => [" % dif_a.name - x += " ".join([dif_b.name for dif_b in difs]) + for upper in self.dif_registrations: + difs = self.dif_registrations[upper] + x = "%s => [" % upper.name + x += " ".join([lower.name for lower in difs]) x += "]" rl.append(x) s += ", ".join(rl) @@ -220,28 +240,36 @@ class Node: def add_dif(self, dif): self.difs.append(dif) dif.add_member(self) + self._validate() def del_dif(self, dif): self.difs.remove(dif) dif.del_member(self) + self._validate() - def add_dif_registration(self, dif_a, dif_b): - self.dif_registrations[dif_a].append(dif_b) + def add_dif_registration(self, upper, lower): + self.dif_registrations[upper].append(lower) + self._validate() - def del_dif_registration(self, dif_a, dif_b): - self.dif_registrations[dif_a].remove(dif_b) + def del_dif_registration(self, upper, lower): + 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.dif_bindings[name] = ap + self._validate() def del_binding(self, name): del self.dif_bindings[name] + self._validate() # Class representing an IPC Process to be created in the experiment # -- cgit v1.2.3 From c3d66b2da3c2f3e994ef83298fe0190f4cdb00dc Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 16:14:12 +0100 Subject: model: compute_ipcps(): create IPCP instances --- rumba/model.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rumba/model.py b/rumba/model.py index f34df52..93b901b 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -477,7 +477,15 @@ class Experiment: e['lower_dif'])) def compute_ipcps(self): - pass + # For each node, compute the required IPCP instances + for node in self.nodes: + node.ipcps = [] + for dif in node.difs: + ipcp = IPCP(name = '%s.%s.IPCP' % (dif.name, node.name), + node = node, dif = dif) + node.ipcps.append(ipcp) + + print("IPCP for node %s: %s" % (node.name, node.ipcps)) # Examine the nodes and DIFs, compute the registration nd enrollment # order, the list of IPCPs to create, registrations, ... -- cgit v1.2.3 From 48fffffde971102a07db602afd1bfa557d321c81 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 16:26:01 +0100 Subject: model: compute_ipcps(): compute registrations --- rumba/model.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rumba/model.py b/rumba/model.py index 93b901b..0adb503 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -282,9 +282,12 @@ class IPCP: self.name = name self.node = node self.dif = dif + self.registrations = [] def __repr__(self): - return "IPCP %s in DIF %s" % (self.name, self.dif.name) + return "IPCP=%s DIF=%s N-1-DIFs=(%s)" % \ + (self.name, self.dif.name, + ' '.join([dif.name for dif in self.registrations])) def __hash__(self): return hash((self.name, self.dif.name)) @@ -483,6 +486,11 @@ class Experiment: for dif in node.difs: ipcp = IPCP(name = '%s.%s.IPCP' % (dif.name, node.name), node = node, dif = dif) + + if dif in node.dif_registrations: + for lower in node.dif_registrations[dif]: + ipcp.registrations.append(lower) + node.ipcps.append(ipcp) print("IPCP for node %s: %s" % (node.name, node.ipcps)) -- cgit v1.2.3 From 9bc830fc85831db2550a8bbebe943609185726bf Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 16:31:43 +0100 Subject: model: Node.ipcps list generated in topological order --- rumba/model.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rumba/model.py b/rumba/model.py index 0adb503..694ce33 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -483,7 +483,11 @@ class Experiment: # For each node, compute the required IPCP instances for node in self.nodes: node.ipcps = [] - for dif in node.difs: + # We want also the node.ipcps list to be generated in + # topological ordering + for dif in self.dif_ordering: + if dif not in node.difs: + continue ipcp = IPCP(name = '%s.%s.IPCP' % (dif.name, node.name), node = node, dif = dif) -- cgit v1.2.3 From a3b5e4464c5897e30c39e0f9971d2e5d6cd04443 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 16:49:51 +0100 Subject: model: compute_ipcps(): compute enrollments --- rumba/model.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index 694ce33..1e28147 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -283,11 +283,15 @@ class IPCP: self.node = node self.dif = dif self.registrations = [] + self.enrollments = [] def __repr__(self): - return "IPCP=%s DIF=%s N-1-DIFs=(%s)" % \ + return "{IPCP=%s,DIF=%s,N-1-DIFs=(%s),enrollments=(%s)}" % \ (self.name, self.dif.name, - ' '.join([dif.name for dif in self.registrations])) + ' '.join([dif.name for dif in self.registrations]), + ' '.join(['{neigh=%s,N-1-DIF=%s}' % (e['enroller'].name, + e['lower_dif'].name) for e in self.enrollments]) + ) def __hash__(self): return hash((self.name, self.dif.name)) @@ -433,6 +437,8 @@ class Experiment: if node1 != node2: dif_graphs[dif][node1].append((node2, lower_dif)) + self.enrollments[dif] = [] + if first == None: # This is a shim DIF, nothing to do continue @@ -443,8 +449,6 @@ class Experiment: er.append("%s --[%s]--> %s" % (node.name, edge[1].name, edge[0].name)) print("DIF graph for %s: %s" % (dif, ', '.join(er))) - self.enrollments[dif] = [] - if self.enrollment_strategy == 'minimal': # To generate the list of enrollments, we simulate one, # using breadth-first trasversal. @@ -480,7 +484,8 @@ class Experiment: e['lower_dif'])) def compute_ipcps(self): - # For each node, compute the required IPCP instances + # For each node, compute the required IPCP instances, and associated + # registrations and enrollments for node in self.nodes: node.ipcps = [] # We want also the node.ipcps list to be generated in @@ -488,13 +493,18 @@ class Experiment: for dif in self.dif_ordering: if dif not in node.difs: continue - ipcp = IPCP(name = '%s.%s.IPCP' % (dif.name, node.name), + ipcp = IPCP(name = '%s.%s' % (dif.name, node.name), node = node, dif = dif) if dif in node.dif_registrations: for lower in node.dif_registrations[dif]: ipcp.registrations.append(lower) + for e in self.enrollments[dif]: + if e['enrollee'] == node: + ipcp.enrollments.append({'enroller': e['enroller'], + 'lower_dif': e['lower_dif']}) + node.ipcps.append(ipcp) print("IPCP for node %s: %s" % (node.name, node.ipcps)) -- cgit v1.2.3 From 8ed85669f0e189b8023acc7cc67866431c0c80f0 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 16:54:08 +0100 Subject: model: call Experiment.generate() in Experiment constructor --- rumba/model.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rumba/model.py b/rumba/model.py index 1e28147..ddfc959 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -318,6 +318,9 @@ class Experiment: self.dif_ordering = [] self.enrollments = dict() + # Generate missing information + self.generate() + def __repr__(self): s = "" for n in self.nodes: @@ -346,9 +349,11 @@ class Experiment: def add_node(self, node): self.nodes.append(node) + self.generate() def del_node(self, node): self.nodes.remove(node) + self.generate() # Compute registration/enrollment order for DIFs def compute_dif_ordering(self): -- cgit v1.2.3 From 1ab559ab4458fb4d0cc3be323d0f0fe5a622dc52 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 17:07:49 +0100 Subject: testbeds: placeholder for QEMU testbed --- examples/example.py | 1 + examples/two-layers.py | 12 +++++++----- rumba/model.py | 2 +- rumba/testbeds/qemu.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 rumba/testbeds/qemu.py diff --git a/examples/example.py b/examples/example.py index 2222ee0..e173ef0 100755 --- a/examples/example.py +++ b/examples/example.py @@ -8,6 +8,7 @@ from rumba.model import * 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 diff --git a/examples/two-layers.py b/examples/two-layers.py index 3fba54e..7b5c868 100755 --- a/examples/two-layers.py +++ b/examples/two-layers.py @@ -8,6 +8,7 @@ from rumba.model import * 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 @@ -17,14 +18,15 @@ import rumba.prototypes.irati as irati n1 = NormalDIF("n1") n2 = NormalDIF("n2") n3 = NormalDIF("n3") +n4 = NormalDIF("n4") e1 = ShimEthDIF("e1") e2 = ShimEthDIF("e2") e3 = ShimEthDIF("e3") a = Node("a", - difs = [n3, n1, e1], - dif_registrations = {n3: [n1], n1 : [e1]}, + difs = [n3, n4, n1, e1], + dif_registrations = {n4: [n1], n3: [n1], n1 : [e1]}, registrations = {"rinaperf.server" : [n3]}, bindings = {"rinaperf.server" : "/usr/bin/rinaperf"}) @@ -33,14 +35,14 @@ b = Node("b", dif_registrations = {n1 : [e1, e2]}) c = Node("c", - difs = [n3, n1, n2, e2, e3], - dif_registrations = {n3: [n1, n2], n1 : [e2], n2: [e3]}) + difs = [n3, n4, n1, n2, e2, e3], + dif_registrations = {n4: [n1], n3: [n1, n2], n1 : [e2], n2: [e3]}) d = Node("d", difs = [n3, n2, e3], dif_registrations = {n3: [n2], n2 : [e3]}) -tb = fake.Testbed(exp_name = "twolayers", +tb = qemu.Testbed(exp_name = "twolayers", username = "vmaffio", proj_name = "cert.pem", password = "") diff --git a/rumba/model.py b/rumba/model.py index ddfc959..a043384 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -514,7 +514,7 @@ class Experiment: print("IPCP for node %s: %s" % (node.name, node.ipcps)) - # Examine the nodes and DIFs, compute the registration nd enrollment + # Examine the nodes and DIFs, compute the registration and enrollment # order, the list of IPCPs to create, registrations, ... def generate(self): self.compute_dif_ordering() diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py new file mode 100644 index 0000000..4e767e3 --- /dev/null +++ b/rumba/testbeds/qemu.py @@ -0,0 +1,30 @@ +# +# QEMU testbed for Rumba +# +# 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.model as mod + +# Fake testbed, useful for testing +class Testbed(mod.Testbed): + def __init__(self, exp_name, username, proj_name = "ARCFIRE", + password = ""): + mod.Testbed.__init__(self, exp_name, username, password, proj_name) + + def create_experiment(self, nodes, links): + print("[QEMU testbed] experiment swapped in") -- cgit v1.2.3 From cde49edb6d1d4e3a431469f2462126787a154f79 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 17:14:09 +0100 Subject: model: add missing __repr__ methods --- rumba/model.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rumba/model.py b/rumba/model.py index a043384..d1d9156 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -50,6 +50,9 @@ class Interface: self.name = name self.ip = ip + def __repr__(self): + return self.name + # Represents a link in the physical graph # # @name [string] Link name @@ -58,6 +61,9 @@ class Link: def __init__(self, name): self.name = name + def __repr__(self): + return self.name + # Represents a point-to-point link in the physical graph # # @name [string] DIF name @@ -76,6 +82,10 @@ class P2PLink(Link): int_b = Interface() self.int_b = int_b + def __repr__(self): + return '%s:%s--%s:%s' % (self.node_a.name, self.int_a, + self.node_b.name, self.int_b) + # Base class for DIFs # # @name [string] DIF name -- cgit v1.2.3 From 921f2fd0423bc82c071f90fb38122e1118461984 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 17:37:05 +0100 Subject: README: add sketch of the high level design --- README | 28 ++++++++++++++++++++++++++++ rumba/testbeds/qemu.py | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/README b/README index 04b2623..cb29f09 100644 --- a/README +++ b/README @@ -1,2 +1,30 @@ # measurement-framework Part of ARCFIRE 2020, WP3 work package. + +Workflow, both external and internal: + + (1) user defines the network graph, creating instances of model.Node + and model.DIF classes + + (2) user creates an instance of a Testbed class + + (3) user creates an instance of prototype.Experiment class, passing + the testbed instance and a list of Node instances + - at the endo of the base Experiment constructor, the + generate function is called to generate information about + per-node IPCPs, registrations and enrollment, ready to be + used by the plugins + + (4) user calls run() on the prototype.Experiment instance: + - First, run() calls Experiment.swap_in(), which + in turns calls Testbed.create_experiment(), passing the + nodes and links (?) + TODO: fix this interface: what should swap_in(), and + so create_experiment() return exactly? Current interface + seems broken + + - Second, run() calls a prototype-specific setup function, + to create the required IPCPs, perform registrations, + enrollments, etc. + + - Third, perform tests (TODO) diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py index 4e767e3..d44bb3e 100644 --- a/rumba/testbeds/qemu.py +++ b/rumba/testbeds/qemu.py @@ -27,4 +27,8 @@ class Testbed(mod.Testbed): mod.Testbed.__init__(self, exp_name, username, password, proj_name) def create_experiment(self, nodes, links): + print(links) print("[QEMU testbed] experiment swapped in") + + def __del__(self): + pass -- cgit v1.2.3 From deb35ef8497e1f792a6b84afe9d9b5da424f22b3 Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Sun, 19 Mar 2017 17:50:21 +0100 Subject: model: add IPCP subclasses and DIF.get_ipcp_class() --- rumba/model.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/rumba/model.py b/rumba/model.py index d1d9156..6d6ffee 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -116,12 +116,18 @@ class DIF: def del_member(self, node): self.members.remove(node) + def get_ipcp_class(self): + return IPCP + # Shim over UDP # class ShimUDPDIF(DIF): def __init__(self, name, members = None): DIF.__init__(self, name, members) + def get_ipcp_class(self): + return ShimUDPIPCP + # Shim over Ethernet # # @link_speed [int] Speed of the Ethernet network, in Mbps @@ -133,6 +139,9 @@ class ShimEthDIF(DIF): if self.link_speed < 0: raise ValueError("link_speed must be a non-negative number") + def get_ipcp_class(self): + return ShimEthIPCP + # Normal DIF # # @policies [dict] Policies of the normal DIF @@ -281,7 +290,7 @@ class Node: del self.dif_bindings[name] self._validate() -# Class representing an IPC Process to be created in the experiment +# Base class representing an IPC Process to be created in the experiment # # @name [string]: IPCP name # @node: Node where the IPCP gets created @@ -313,6 +322,16 @@ class IPCP: def __neq__(self, other): return not (self == other) +class ShimEthIPCP(IPCP): + def __init__(self, name, node, dif, ifname = None): + IPCP.__init__(self, name, node, dif) + self.ifname = ifname + +class ShimUDPIPCP(IPCP): + def __init__(self, name, node, dif): + IPCP.__init__(self, name, node, dif) + # TODO add IP and port + # Base class for ARCFIRE experiments # # @name [string] Name of the experiment @@ -508,8 +527,10 @@ class Experiment: for dif in self.dif_ordering: if dif not in node.difs: continue - ipcp = IPCP(name = '%s.%s' % (dif.name, node.name), - node = node, dif = dif) + + ipcp = dif.get_ipcp_class()( + name = '%s.%s' % (dif.name, node.name), + node = node, dif = dif) if dif in node.dif_registrations: for lower in node.dif_registrations[dif]: -- cgit v1.2.3 From 79a6808c292c4690c92314468ff4ff0d5d8ad72e Mon Sep 17 00:00:00 2001 From: Vincenzo Maffione Date: Mon, 20 Mar 2017 15:03:03 +0100 Subject: examples: fix qemu testbed construction --- examples/two-layers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/two-layers.py b/examples/two-layers.py index 7b5c868..29faebe 100755 --- a/examples/two-layers.py +++ b/examples/two-layers.py @@ -43,9 +43,7 @@ d = Node("d", dif_registrations = {n3: [n2], n2 : [e3]}) tb = qemu.Testbed(exp_name = "twolayers", - username = "vmaffio", - proj_name = "cert.pem", - password = "") + username = "vmaffio") exp = irati.Experiment(tb, nodes = [a, b, c, d]) -- cgit v1.2.3