aboutsummaryrefslogtreecommitdiff
path: root/rumba
diff options
context:
space:
mode:
authorMarco Capitani <m.capitani@nextworks.it>2017-06-13 10:09:54 +0200
committerMarco Capitani <m.capitani@nextworks.it>2017-06-13 10:09:54 +0200
commit457977f337a47caddf8788e1d4e1d1736f2a6ccb (patch)
tree12fa2a2be0f57be3ea2acd3623b01f55c8de1092 /rumba
parent3081d070cda223afd548645143142e1104b07d83 (diff)
parent53602860e17d650f9ab850cf9a206de6a8712c15 (diff)
downloadrumba-457977f337a47caddf8788e1d4e1d1736f2a6ccb.tar.gz
rumba-457977f337a47caddf8788e1d4e1d1736f2a6ccb.zip
Merge branch 'master' into policies
Diffstat (limited to 'rumba')
-rw-r--r--rumba/model.py234
-rwxr-xr-xrumba/prototypes/enroll.py8
-rw-r--r--rumba/prototypes/irati.py52
-rw-r--r--rumba/prototypes/ouroboros.py28
-rw-r--r--rumba/prototypes/rlite.py35
-rw-r--r--rumba/ssh_support.py63
-rw-r--r--rumba/testbeds/jfed.py51
-rw-r--r--rumba/testbeds/qemu.py31
8 files changed, 366 insertions, 136 deletions
diff --git a/rumba/model.py b/rumba/model.py
index 442933c..e0f1dcc 100644
--- a/rumba/model.py
+++ b/rumba/model.py
@@ -20,6 +20,9 @@
# MA 02110-1301 USA
import abc
+import random
+
+import time
import rumba.log as log
@@ -35,11 +38,18 @@ logger = log.get_logger(__name__)
# @exp_name [string] experiment name
#
class Testbed:
- def __init__(self, exp_name, username, password, proj_name):
+ def __init__(self,
+ exp_name,
+ username,
+ password,
+ proj_name,
+ http_proxy=None):
self.username = username
self.password = password
self.proj_name = proj_name
self.exp_name = exp_name
+ self.http_proxy = http_proxy
+ self.flags = {'no_vlan_offload': False}
@abc.abstractmethod
def swap_in(self, experiment):
@@ -152,13 +162,12 @@ 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
# @policies: dict of dif -> policy dict to apply for that dif in this node
#
+#
class Node:
def __init__(self, name, difs=None, dif_registrations=None,
- registrations=None, bindings=None, policies=None):
+ client=False, policies=None):
self.name = name
if difs is None:
difs = list()
@@ -168,12 +177,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 = []
if policies is None:
@@ -183,6 +186,7 @@ class Node:
if hasattr(dif, 'policy'):
self.policies[dif] = \
Policy(dif, self, policies.get(dif.name, {}))
+ self.client = client
self._validate()
@@ -197,18 +201,14 @@ 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): # TODO add policies in repr?
+ def __repr__(self): # TODO add policies in repr
s = "Node " + self.name + ":\n"
s += " DIFs: [ "
@@ -226,19 +226,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):
@@ -275,22 +262,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()
-
def add_policy(self, dif, component_name, policy_name, **parameters):
self.policies[dif].add_policy(component_name, policy_name, **parameters)
@@ -311,7 +282,7 @@ class IPCP:
self.dif = dif
self.registrations = []
- # Is this node the first in the DIF, so that it does not need
+ # Is this IPCP the first in its DIF, so that it does not need
# to enroll to anyone ?
self.dif_bootstrapper = False
@@ -424,6 +395,10 @@ class Experiment:
difsdeps_inc = dict()
for node in self.nodes:
+ for dif in node.difs:
+ if dif not in difsdeps_adj:
+ difsdeps_adj[dif] = set()
+
for upper in node.dif_registrations:
for lower in node.dif_registrations[upper]:
if upper not in difsdeps_inc:
@@ -445,6 +420,14 @@ class Experiment:
difsdeps_inc_cnt[dif] = len(difsdeps_inc[dif])
del difsdeps_inc
+ # Init difsdeps_inc_cnt for those DIFs that do not
+ # act as lower IPCPs nor upper IPCPs for registration
+ # operations
+ for node in self.nodes:
+ for dif in node.difs:
+ if dif not in difsdeps_inc_cnt:
+ difsdeps_inc_cnt[dif] = 0
+
# Run Kahn's algorithm to compute topological
# ordering on the DIFs graph.
frontier = set()
@@ -576,6 +559,7 @@ class Experiment:
if dif not in node.difs:
continue
+ # Create an instance of the required IPCP class
ipcp = dif.get_ipcp_class()(
name='%s.%s' % (dif.name, node.name),
node=node, dif=dif)
@@ -584,23 +568,34 @@ class Experiment:
for lower in node.dif_registrations[dif]:
ipcp.registrations.append(lower)
+ node.ipcps.append(ipcp)
+ dif.ipcps.append(ipcp)
+
+ def compute_bootstrappers(self):
+ for node in self.nodes:
+ for ipcp in node.ipcps:
ipcp.dif_bootstrapper = True
for el in self.enrollments:
for e in el:
- if e['dif'] != dif:
+ if e['dif'] != ipcp.dif:
# Skip this DIF
break
- if e['enrollee'] == node:
+ if e['enrollee'] == ipcp:
ipcp.dif_bootstrapper = False
# Exit the loops
break
if not ipcp.dif_bootstrapper:
break
- node.ipcps.append(ipcp)
- dif.ipcps.append(ipcp)
-
- logger.info("IPCP for node %s: %s", node.name, node.ipcps)
+ def dump_ssh_info(self):
+ f = open('ssh_info', 'w')
+ for node in self.nodes:
+ f.write("%s;%s;%s;%s;%s\n" % (node.name,
+ self.testbed.username,
+ node.ssh_config.hostname,
+ node.ssh_config.port,
+ node.ssh_config.proxycommand))
+ f.close()
# Examine the nodes and DIFs, compute the registration and enrollment
# order, the list of IPCPs to create, registrations, ...
@@ -608,19 +603,148 @@ class Experiment:
self.compute_dif_ordering()
self.compute_ipcps()
self.compute_enrollments()
+ self.compute_bootstrappers()
+ for node in self.nodes:
+ logger.info("IPCPs for node %s: %s", node.name, node.ipcps)
@abc.abstractmethod
def install_prototype(self):
- raise Exception('run_prototype() method not implemented')
+ raise Exception('install_prototype() method not implemented')
@abc.abstractmethod
def bootstrap_prototype(self):
- raise Exception('run_prototype() method not implemented')
+ raise Exception('bootstrap_prototype() method not implemented')
+
+ @abc.abstractmethod
+ def prototype_name(self):
+ raise Exception('prototype_name() method not implemented')
def swap_in(self):
# Realize the experiment testbed (testbed-specific)
self.testbed.swap_in(self)
+ self.dump_ssh_info()
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(object):
+ def __init__(self, ap, options=None):
+ self.ap = ap
+ self.options = options
+
+ 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(Client):
+ 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
+#
+# @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
+# @nodes: Specific nodes to start this server on
+#
+class Server:
+ def __init__(self, ap, arrival_rate, mean_duration,
+ 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
+ self.arrival_rate = arrival_rate # mean requests/s
+ self.mean_duration = mean_duration # in seconds
+
+ 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)
+
+ def get_new_clients(self, interval):
+ """
+ Returns a list of clients of size appropriate to the server's rate.
+
+ The list's size should be a sample from Poisson(arrival_rate) over
+ interval seconds.
+ Hence, the average size should be interval * arrival_rate.
+ """
+ pass
+
+ 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,))
+ pass # TODO should return a ClientProcess
+
+
+# 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
diff --git a/rumba/prototypes/enroll.py b/rumba/prototypes/enroll.py
index 458736a..99b49a6 100755
--- a/rumba/prototypes/enroll.py
+++ b/rumba/prototypes/enroll.py
@@ -78,8 +78,8 @@ if connected:
get_response(s)
# Send the IPCP list command
- cmd = 'list-ipcps\n'
- s.sendall(bytes(cmd, 'ascii'))
+ cmd = u'list-ipcps\n'
+ s.sendall(cmd.encode('ascii'))
# Get the list of IPCPs and parse it to look for the enroller ID
print('Looking up identifier for IPCP %s' % args.enrollee_name)
@@ -98,11 +98,11 @@ if connected:
raise Exception()
# Send the enroll command
- cmd = 'enroll-to-dif %s %s %s %s 1\n' \
+ cmd = u'enroll-to-dif %s %s %s %s 1\n' \
% (enrollee_id, args.dif, args.lower_dif, args.enroller_name)
print(cmd)
- s.sendall(bytes(cmd, 'ascii'))
+ s.sendall(cmd.encode('ascii'))
# Get the enroll command answer
lines = get_response(s)
diff --git a/rumba/prototypes/irati.py b/rumba/prototypes/irati.py
index b2f54d9..42afe3b 100644
--- a/rumba/prototypes/irati.py
+++ b/rumba/prototypes/irati.py
@@ -21,8 +21,6 @@
import copy
import json
-import subprocess
-
import os
import time
@@ -38,6 +36,9 @@ logger = log.get_logger(__name__)
# An experiment over the IRATI implementation
class Experiment(mod.Experiment):
+ def prototype_name(self):
+ return 'irati'
+
@staticmethod
def real_sudo(s):
return 'sudo ' + s
@@ -69,25 +70,24 @@ class Experiment(mod.Experiment):
def install(self):
"""Installs IRATI on the nodes."""
- 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 = [self.sudo("apt-get update"),
+ "export https_proxy=\"https://proxy.atlantis.ugent.be:8080\"; "
+ + self.sudo("apt-get install g++ gcc "
+ "protobuf-compiler libprotobuf-dev git --yes "
+ "pkg-config "
+ "libnl-3-dev libnl-genl-3-dev"),
+ self.sudo("rm -rf ~/irati"),
+ "cd ~; "
+ "export https_proxy=\"https://proxy.atlantis.ugent.be:8080\"; "
+ + "git clone https://github.com/IRATI/stack irati",
+ "cd ~/irati && git checkout arcfire",
+ "cd ~/irati && "
+ + self.sudo("./install-from-scratch")]
for node in self.nodes:
- ssh.execute_commands(self.testbed, node.ssh_config,
+ ssh.execute_proxy_commands(self.testbed, node.ssh_config,
cmds, time_out=None)
- def setup(self):
- for node in self.nodes:
- ssh.execute_command(self.testbed, node.ssh_config,
- "sudo nohup ipcm &> ipcm.log &",
- time_out=None)
-
def bootstrap_network(self):
"""Creates the network by enrolling and configuring the nodes"""
for node in self.nodes:
@@ -101,8 +101,6 @@ class Experiment(mod.Experiment):
def bootstrap_prototype(self):
logger.info("setting up")
- self.setup()
- logger.info("software initialized on all nodes")
self.conf_files = self.write_conf()
logger.info("configuration files generated for all nodes")
self.bootstrap_network()
@@ -144,7 +142,6 @@ class Experiment(mod.Experiment):
'genfiles': gen_files,
'genfilesconf': ' '.join(gen_files_conf),
'genfilesbin': gen_files_bin,
- 'installpath': '/usr',
'verb': 'DBG',
'ipcmcomps': ipcm_components}
@@ -155,13 +152,16 @@ class Experiment(mod.Experiment):
'')
cmds = [self.sudo('hostname %(name)s' % format_args),
+ self.sudo('modprobe rina-irati-core'),
self.sudo('chmod a+rw /dev/irati'),
self.sudo('mv %(genfilesconf)s /etc' % format_args),
self.sudo('mv %(genfilesbin)s /usr/bin') % format_args,
self.sudo('chmod a+x /usr/bin/enroll.py') % format_args]
cmds += [self.sudo('modprobe rina-default-plugin'),
- self.sudo('%(installpath)s/bin/ipcm -a \"%(ipcmcomps)s\" '
+ self.sudo('modprobe shim-eth-vlan'),
+ self.sudo('modprobe normal-ipcp'),
+ self.sudo('ipcm -a \"%(ipcmcomps)s\" '
'-c /etc/%(name)s.ipcm.conf -l %(verb)s &> log &'
% format_args)]
@@ -170,6 +170,8 @@ class Experiment(mod.Experiment):
def enroll_nodes(self):
"""Runs the enrollments one by one, respecting dependencies"""
+ logger.info("Waiting 5 seconds for the ipcm to start.")
+ time.sleep(5)
for enrollment_list in self.enrollments:
for e in enrollment_list:
logger.info(
@@ -225,16 +227,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..9ac1425 100644
--- a/rumba/prototypes/ouroboros.py
+++ b/rumba/prototypes/ouroboros.py
@@ -32,6 +32,9 @@ class Experiment(mod.Experiment):
def __init__(self, testbed, nodes=None):
mod.Experiment.__init__(self, testbed, nodes)
+ def prototype_name(self):
+ return 'ouroboros'
+
def setup_ouroboros(self):
for node in self.nodes:
ssh.execute_command(self.testbed, node.ssh_config,
@@ -50,27 +53,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 +129,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!")
diff --git a/rumba/prototypes/rlite.py b/rumba/prototypes/rlite.py
index 8a06b44..cc38255 100644
--- a/rumba/prototypes/rlite.py
+++ b/rumba/prototypes/rlite.py
@@ -34,21 +34,32 @@ class Experiment(mod.Experiment):
def __init__(self, testbed, nodes=None):
mod.Experiment.__init__(self, testbed, nodes)
+ def prototype_name(self):
+ return 'rlite'
+
def execute_commands(self, node, cmds):
ssh.execute_commands(self.testbed, node.ssh_config,
cmds, time_out=None)
+ def execute_proxy_commands(self, node, cmds):
+ ssh.execute_proxy_commands(self.testbed, node.ssh_config,
+ cmds, time_out=None)
+
+ # Prepend sudo to all commands if the user is not 'root'
+ def may_sudo(self, cmds):
+ if self.testbed.username != 'root':
+ for i in range(len(cmds)):
+ cmds[i] = "sudo %s" % cmds[i]
+
def init_nodes(self):
+ # Load kernel modules and start the uipcps daemon
cmds = ["modprobe rlite",
"modprobe rlite-normal",
"modprobe rlite-shim-eth",
"modprobe rlite-shim-udp4",
"modprobe rlite-shim-loopback",
"rlite-uipcps -v DBG -k 0 &> uipcp.log &"]
-
- # Load kernel modules
-
- # Start the uipcps daemon
+ self.may_sudo(cmds)
for node in self.nodes:
self.execute_commands(node, cmds)
@@ -80,6 +91,7 @@ class Experiment(mod.Experiment):
cmds.append("rlite-ctl ipcp-config %s netdev %s"
% (ipcp.name, ipcp.ifname))
+ self.may_sudo(cmds)
self.execute_commands(node, cmds)
def register_ipcps(self):
@@ -91,6 +103,7 @@ class Experiment(mod.Experiment):
cmds.append("rlite-ctl ipcp-register %s %s"
% (ipcp.name, lower.name))
+ self.may_sudo(cmds)
self.execute_commands(node, cmds)
def enroll_ipcps(self):
@@ -103,21 +116,25 @@ class Experiment(mod.Experiment):
}
cmd = "rlite-ctl ipcp-enroll %(enrollee)s %(dif)s "\
"%(lower_dif)s %(enroller)s" % d
- self.execute_commands(e['enrollee'].node, [cmd])
+ cmds = [cmd]
+ self.may_sudo(cmds)
+ self.execute_commands(e['enrollee'].node, cmds)
time.sleep(1)
def install_prototype(self):
logger.info("installing rlite on all nodes")
- cmds = ["apt-get update",
- "apt-get install g++ gcc cmake "
+ cmds = ["sudo apt-get update",
+ "sudo -E apt-get install g++ gcc cmake "
"linux-headers-$(uname -r) "
"protobuf-compiler libprotobuf-dev git --yes",
"rm -rf ~/rlite",
"cd ~; git clone https://github.com/vmaffione/rlite",
- "cd ~/rlite && ./configure && make && sudo make install"]
+ "cd ~/rlite && ./configure && make && sudo make install",
+ "cd ~/rlite && sudo make depmod"
+ ]
for node in self.nodes:
- self.execute_commands(node, cmds)
+ self.execute_proxy_commands(node, cmds)
logger.info("installation complete")
def bootstrap_prototype(self):
diff --git a/rumba/ssh_support.py b/rumba/ssh_support.py
index 26d64fb..a1e1ba4 100644
--- a/rumba/ssh_support.py
+++ b/rumba/ssh_support.py
@@ -43,6 +43,51 @@ def _print_stream(stream):
return o
+def execute_proxy_commands(testbed, ssh_config, commands, time_out=3):
+ """
+ Remote execution of a list of shell command on hostname, using the
+ http and https proxy specified by the testbed. By
+ default this function will exit (timeout) after 3 seconds.
+
+ @param testbed: testbed info
+ @param ssh_config: ssh config of the node
+ @param commands: *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
+ """
+ new_commands = []
+ for command in commands:
+ proxy = testbed.http_proxy
+ if proxy is not None:
+ proxy_command = 'export http_proxy=' + proxy + '; ' \
+ + 'export https_proxy=' + proxy + ';'
+ new_commands.append(proxy_command + ' ' + command)
+ else:
+ new_commands.append(command)
+ return execute_commands(testbed, ssh_config, new_commands, time_out)
+
+
+def execute_proxy_command(testbed, ssh_config, command, time_out=3):
+ """
+ Remote execution of a list of shell command on hostname, using
+ a proxy http and https.
+ By default this function will exit (timeout) after 3 seconds.
+
+ @param testbed: testbed info
+ @param ssh_config: ssh config 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
+ """
+ o = execute_proxy_commands(testbed, ssh_config, [command], time_out)
+ if o is not None:
+ return o
+
+
def execute_commands(testbed, ssh_config, commands, time_out=3):
"""
Remote execution of a list of shell command on hostname. By
@@ -160,6 +205,11 @@ def copy_paths_to_testbed(testbed, ssh_config, paths, destination):
"""
ssh_client = get_ssh_client()
+ if ssh_config.proxycommand is not None:
+ proxy = paramiko.ProxyCommand(ssh_config.proxycommand)
+ else:
+ proxy = None
+
if destination is not '' and not destination.endswith('/'):
destination = destination + '/'
@@ -167,7 +217,8 @@ def copy_paths_to_testbed(testbed, ssh_config, paths, destination):
ssh_client.connect(ssh_config.hostname, ssh_config.port,
testbed.username,
testbed.password,
- look_for_keys=True)
+ look_for_keys=True,
+ sock=proxy)
sftp_client = ssh_client.open_sftp()
@@ -224,9 +275,9 @@ def setup_vlan(testbed, node, vlan_id, int_name):
% args),
sudo("ifconfig %(ifname)s.%(vlan)s up"
% args)]
- # TODO: is ethtool needed? Should install or check if it is present.
- # cmds += [sudo("ethtool -K %(ifname)s rxvlan off"
- # % args),
- # sudo("ethtool -K %(ifname)s txvlan off"
- # % args)]
+ if testbed.flags['no_vlan_offload']:
+ cmds += [sudo("ethtool -K %(ifname)s rxvlan off"
+ % args),
+ sudo("ethtool -K %(ifname)s txvlan off"
+ % args)]
execute_commands(testbed, node.ssh_config, cmds)
diff --git a/rumba/testbeds/jfed.py b/rumba/testbeds/jfed.py
index 5394146..83fbce7 100644
--- a/rumba/testbeds/jfed.py
+++ b/rumba/testbeds/jfed.py
@@ -27,7 +27,7 @@ import tarfile
import rumba.model as mod
import rumba.log as log
-
+from rumba import ssh_support
logger = log.get_logger(__name__)
@@ -38,7 +38,12 @@ class Testbed(mod.Testbed):
proj_name="ARCFIRE", authority="wall2.ilabt.iminds.be",
image=None):
passwd = getpass.getpass(prompt="Password for certificate file: ")
- mod.Testbed.__init__(self, exp_name, username, passwd, proj_name)
+ mod.Testbed.__init__(self,
+ exp_name,
+ username,
+ passwd,
+ proj_name,
+ http_proxy="https://proxy.atlantis.ugent.be:8080")
self.authority = "urn:publicid:IDN+" + authority + "+authority+cm"
self.auth_name = authority
self.cert_file = cert_file
@@ -49,7 +54,7 @@ class Testbed(mod.Testbed):
self.jfed_jar = "jfed_cli/experimenter-cli.jar"
if image is not None:
self.image = "urn:publicid:IDN+" + authority + \
- "+image+GeniSlices:" + image
+ "+image+wall2-ilabt-iminds-be:" + image
else:
self.image = None
@@ -63,6 +68,7 @@ class Testbed(mod.Testbed):
tar.close()
logger.info("Extracted in current directory")
os.remove(tarball)
+ self.flags['no_vlan_offload'] = True
def create_rspec(self, experiment):
impl = xml.getDOMImplementation()
@@ -164,22 +170,55 @@ class Testbed(mod.Testbed):
rspec = xml.parse(self.manifest)
xml_nodes = rspec.getElementsByTagName("node")
+ dir_path = os.path.dirname(os.path.abspath(__file__))
+
# Complete details of the nodes after swapin
for xml_node in xml_nodes:
n_name = xml_node.getAttribute("client_id")
intfs = xml_node.getElementsByTagName("interface")
+ got = False
for node in experiment.nodes:
if node.name == n_name:
node_n = node
+ got = True
+ if not got:
+ logger.error("Not found node %s", n_name)
for intf in intfs:
+ aux_mac_address = intf.getAttribute("mac_address")
+ mac = ":".join(
+ [aux_mac_address[i:i+2] for i in range(0, 12, 2)]
+ )
+ command = (
+ 'echo "mac=\\"\$1\\"; cd / && ./sbin/ifconfig -a | '
+ 'awk \'/^[a-z]/ { if ( \\"\'\\"\$mac\\"\'\\" == \$5 )'
+ ' print \$1}\'" > mac2ifname.sh')
+ ssh_support.execute_command(self, node_n.ssh_config, command)
+
+ # ssh_support.copy_path_to_testbed(
+ # self,
+ # node_n.ssh_config,
+ # os.path.join(dir_path, 'mac2ifname.sh'),
+ # '')
+ ssh_support.execute_command(
+ self,
+ node_n.ssh_config,
+ 'cd ~ && chmod a+x mac2ifname.sh')
+ ifname = ssh_support.execute_command(
+ self,
+ node_n.ssh_config,
+ './mac2ifname.sh ' + mac
+ )
i_name = intf.getAttribute("client_id")
for ipcp in node_n.ipcps:
if isinstance(ipcp, mod.ShimEthIPCP):
if self.if_id[ipcp] == i_name:
- comp_id = intf.getAttribute("component_id")
- comp_arr = comp_id.split(":")
- ipcp.ifname = comp_arr[-1]
+ ipcp.ifname = ifname
+ logger.debug("Node %s interface %s has name %s."
+ % (node_n.name, mac, ifname))
+ # comp_id = intf.getAttribute("component_id")
+ # comp_arr = comp_id.split(":")
+ # ipcp.ifname = comp_arr[-1]
# xml_ip = intf.getElementsByTagName("ip")
# interface.ip = xml_ip[0].getAttribute("address")
diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py
index a916525..df02ab6 100644
--- a/rumba/testbeds/qemu.py
+++ b/rumba/testbeds/qemu.py
@@ -26,24 +26,25 @@ 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 = []
+ self.bzimage = bzimage
+ self.initramfs = initramfs
@staticmethod
def _run_command_chain(commands, results_queue,
@@ -110,10 +111,34 @@ class Testbed(mod.Testbed):
if os.geteuid() != 0:
try:
subprocess.check_call(["sudo", "-v"])
+ if not os.access("/dev/vhost-net", os.R_OK) \
+ or not os.access("/dev/vhost-net", os.W_OK) \
+ or not os.access("/dev/kvm", os.R_OK) \
+ or not os.access("/dev/kvm", os.W_OK):
+ raise Exception('Cannot open vhost device. Make sure it is'
+ 'available and you have rw permissions '
+ 'on /dev/vhost-net')
except subprocess.CalledProcessError:
raise Exception('Not authenticated')
logger.info("swapping in")
+
+ # Download the proper buildroot images, if the user did not specify
+ # local images
+ url_prefix = "https://bitbucket.org/vmaffione/rina-images/downloads/"
+ if not self.bzimage:
+ self.bzimage = '%s.bzImage' % (experiment.prototype_name())
+ if not os.path.exists(self.bzimage):
+ logger.info("Downloading %s" % (url_prefix + self.bzimage))
+ wget.download(url_prefix + self.bzimage)
+ print("\n")
+ if not self.initramfs:
+ self.initramfs = '%s.rootfs.cpio' % (experiment.prototype_name())
+ if not os.path.exists(self.initramfs):
+ logger.info("Downloading %s" % (url_prefix + self.initramfs))
+ wget.download(url_prefix + self.initramfs)
+ print("\n")
+
logger.info('Setting up interfaces.')
# Building bridges and taps