aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rumba/model.py163
-rw-r--r--rumba/prototypes/irati.py105
-rw-r--r--rumba/ssh_support.py43
-rw-r--r--rumba/testbeds/qemu.py39
-rwxr-xr-xtools/democonf2rumba.py256
5 files changed, 400 insertions, 206 deletions
diff --git a/rumba/model.py b/rumba/model.py
index 5f4f162..6f031ac 100644
--- a/rumba/model.py
+++ b/rumba/model.py
@@ -20,7 +20,7 @@
# MA 02110-1301 USA
import abc
-import re
+
# Represents generic testbed info
#
@@ -65,7 +65,7 @@ class DIF:
return hash(self.name)
def __eq__(self, other):
- return other != None and self.name == other.name
+ return other is not None and self.name == other.name
def __neq__(self, other):
return not self == other
@@ -79,6 +79,7 @@ class DIF:
def get_ipcp_class(self):
return IPCP
+
# Shim over UDP
#
class ShimUDPDIF(DIF):
@@ -88,6 +89,7 @@ class ShimUDPDIF(DIF):
def get_ipcp_class(self):
return ShimUDPIPCP
+
# Shim over Ethernet
#
# @link_speed [int] Speed of the Ethernet network, in Mbps
@@ -102,6 +104,7 @@ class ShimEthDIF(DIF):
def get_ipcp_class(self):
return ShimEthIPCP
+
# Normal DIF
#
# @policies [dict] Policies of the normal DIF
@@ -125,6 +128,7 @@ class NormalDIF(DIF):
s += "\n Component %s has policy %s" % (comp, pol)
return s
+
# SSH Configuration
#
class SSHConfig:
@@ -132,6 +136,7 @@ class SSHConfig:
self.hostname = hostname
self.port = port
+
# A node in the experiment
#
# @difs: DIFs the node will have an IPCP in
@@ -164,7 +169,7 @@ class Node:
def _undeclared_dif(self, dif):
if dif not in self.difs:
- raise Exception("Invalid registration: node %s is not declared "\
+ raise Exception("Invalid registration: node %s is not declared "
"to be part of DIF %s" % (self.name, dif.name))
def _validate(self):
@@ -206,8 +211,8 @@ class Node:
s += " ]\n"
s += " Bindings: [ "
- s += ", ".join(["'%s' => '%s'" % (ap, self.bindings[ap]) \
- for ap in self.bindings])
+ s += ", ".join(["'%s' => '%s'" % (ap, self.bindings[ap])
+ for ap in self.bindings])
s += " ]\n"
return s
@@ -216,7 +221,7 @@ class Node:
return hash(self.name)
def __eq__(self, other):
- return other != None and self.name == other.name
+ return other is not None and self.name == other.name
def __neq__(self, other):
return not self == other
@@ -255,6 +260,7 @@ class Node:
del self.bindings[name]
self._validate()
+
# Base class representing an IPC Process to be created in the experiment
#
# @name [string]: IPCP name
@@ -277,28 +283,31 @@ class IPCP:
(self.name, self.dif.name,
' '.join([dif.name for dif in self.registrations]),
',bootstrapper' if self.dif_bootstrapper else ''
- )
+ )
def __hash__(self):
return hash((self.name, self.dif.name))
def __eq__(self, other):
- return other != None and self.name == other.name \
+ return other is not None and self.name == other.name \
and self.dif == other.dif
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
@@ -312,121 +321,11 @@ class Experiment:
self.testbed = testbed
self.enrollment_strategy = 'minimal' # 'full-mesh', 'manual'
self.dif_ordering = []
- self.enrollments = [] # a list of per-DIF lists of enrollments
+ self.enrollments = [] # a list of per-DIF lists of enrollments
# Generate missing information
self.generate()
- @classmethod
- def from_config_file(cls, testbed, filename='demo.conf'):
- """
- :type testbed: Testbed
- :rtype: Experiment
- :param testbed: the testbed for the experiment
- :param filename: name of the .conf file
- :return: an Experiment object
- """
-
- shims = {}
- nodes = {}
- difs = {}
- with open(filename, 'r') as conf:
-
- line_cnt = 0
-
- while 1:
- line = conf.readline()
- if line == '':
- break
- line_cnt += 1
-
- line = line.replace('\n', '').strip()
-
- if line.startswith('#') or line == "":
- continue
-
- m = re.match(r'\s*eth\s+([\w-]+)\s+(\d+)([GMK])bps\s+(\w.*)$',
- line)
- if m:
- shim = m.group(1)
- speed = int(m.group(2))
- speed_unit = m.group(3).lower()
- vm_list = m.group(4).split()
-
- if shim in shims or shim in difs:
- print('Error: Line %d: shim %s already defined'
- % (line_cnt, shim))
- continue
-
- if speed_unit == 'K':
- speed = speed // 1000
- if speed_unit == 'G':
- speed = speed * 1000
-
- shims[shim] = {'name': shim, 'speed': speed, 'type': 'eth'}
-
- for vm in vm_list:
- nodes.setdefault(vm, {'name': vm,
- 'difs': [],
- 'dif_registrations': {},
- 'registrations': {}})
- nodes[vm]['difs'].append(shim)
- continue
-
- m = re.match(r'\s*dif\s+([\w-]+)\s+([\w-]+)\s+(\w.*)$', line)
- if m:
- dif = m.group(1)
- vm = m.group(2)
- dif_list = m.group(3).split()
-
- if dif in shims:
- print('Error: Line %d: dif %s already defined as shim'
- % (line_cnt, dif))
- continue
-
- difs.setdefault(dif, {'name': dif})
-
- if vm in nodes and dif in nodes[vm]['dif_registrations']:
- print(
- 'Error: Line %d: vm %s in dif %s already specified'
- % (line_cnt, vm, dif))
- continue
-
- nodes.setdefault(vm, {'name': vm,
- 'difs': [],
- 'dif_registrations': {},
- 'registrations': {}})
- nodes[vm]['difs'].append(dif)
- nodes[vm]['dif_registrations'][dif] = dif_list
- # It is not defined yet, per check above.
-
- continue
-
- # No match, spit a warning
- print('Warning: Line %d unrecognized and ignored' % line_cnt)
-
- # File parsed
-
- parsed_difs = {}
-
- for shim_name, shim in shims.items():
- parsed_difs[shim_name] = (ShimEthDIF(shim_name,
- link_speed=shim['speed']))
-
- for dif_name, dif in difs.items():
- parsed_difs[dif_name] = (NormalDIF(dif_name))
-
- parsed_nodes = []
- for node, node_data in nodes.items():
- name = node_data['name']
- difs = [parsed_difs[x] for x in node_data['difs']]
- dif_registrations = {parsed_difs[x]: [parsed_difs[y] for y in l]
- for x, l
- in node_data['dif_registrations'].items()}
- parsed_nodes.append(Node(name, difs, dif_registrations))
-
- return cls(testbed=testbed, nodes=parsed_nodes)
-
def __repr__(self):
s = ""
for n in self.nodes:
@@ -470,8 +369,8 @@ class Experiment:
difsdeps_inc_cnt[dif] = len(difsdeps_inc[dif])
del difsdeps_inc
- #print(difsdeps_adj)
- #print(difsdeps_inc_cnt)
+ # print(difsdeps_adj)
+ # print(difsdeps_inc_cnt)
# Run Kahn's algorithm to compute topological
# ordering on the DIFs graph.
@@ -490,12 +389,12 @@ class Experiment:
frontier.add(nxt)
difsdeps_adj[cur] = set()
- circular_set = [dif for dif in difsdeps_inc_cnt \
+ 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" \
+ raise Exception("Fatal error: The specified DIFs topology"
+ "has one or more"
+ "circular dependencies, involving the following"
" DIFs: %s" % circular_set)
print("DIF topological ordering: %s" % self.dif_ordering)
@@ -516,8 +415,8 @@ class Experiment:
for node in self.nodes:
if dif in node.dif_registrations:
- dif_graphs[dif][node] = [] # init for later use
- if first is None: # pick any node for later use
+ dif_graphs[dif][node] = [] # init for later use
+ if first is None: # pick any node for later use
first = node
for lower_dif in node.dif_registrations[dif]:
if lower_dif not in neighsets:
@@ -578,11 +477,11 @@ class Experiment:
print("Enrollments:")
for el in self.enrollments:
for e in el:
- print(" [%s] %s --> %s through N-1-DIF %s" % \
- (e['dif'],
- e['enrollee'].name,
- e['enroller'].name,
- e['lower_dif']))
+ print(" [%s] %s --> %s through N-1-DIF %s" %
+ (e['dif'],
+ e['enrollee'].name,
+ e['enroller'].name,
+ e['lower_dif']))
def compute_ipcps(self):
# For each node, compute the required IPCP instances, and associated
diff --git a/rumba/prototypes/irati.py b/rumba/prototypes/irati.py
index ba8a152..44eadb0 100644
--- a/rumba/prototypes/irati.py
+++ b/rumba/prototypes/irati.py
@@ -23,6 +23,8 @@ import json
import subprocess
+import os
+
import rumba.ssh_support as ssh
import rumba.model as mod
import rumba.prototypes.irati_templates as irati_templates
@@ -53,19 +55,21 @@ class Experiment(mod.Experiment):
def setup(self):
"""Installs IRATI on the vms."""
- cmds = list()
-
- cmds.append("sudo apt-get update")
- cmds.append("sudo apt-get install g++ gcc "
- "protobuf-compiler libprotobuf-dev git --yes")
- cmds.append("sudo rm -rf ~/irati")
- cmds.append("cd && git clone https://github.com/IRATI/stack irati")
- cmds.append("cd ~/irati && sudo ./install-from-scratch")
- cmds.append("sudo nohup ipcm &> ipcm.log &")
-
- for node in self.nodes:
- ssh.execute_commands(self.testbed, node.ssh_config,
- cmds, time_out=None)
+ setup_irati = False
+ if setup_irati:
+ cmds = list()
+
+ cmds.append("sudo apt-get update")
+ cmds.append("sudo apt-get install g++ gcc "
+ "protobuf-compiler libprotobuf-dev git --yes")
+ cmds.append("sudo rm -rf ~/irati")
+ cmds.append("cd && git clone https://github.com/IRATI/stack irati")
+ cmds.append("cd ~/irati && sudo ./install-from-scratch")
+ cmds.append("sudo nohup ipcm &> ipcm.log &")
+
+ for node in self.nodes:
+ ssh.execute_commands(self.testbed, node.ssh_config,
+ cmds, time_out=None)
def bootstrap_network(self):
"""Creates the network by enrolling and configuring the nodes"""
@@ -73,10 +77,16 @@ class Experiment(mod.Experiment):
self.process_node(node)
self.enroll_nodes()
+ def run_experiment(self):
+ input('Press ENTER to quit.')
+
def run_prototype(self):
print("[IRATI experiment] start")
print("Setting up IRATI on the nodes...")
self.setup()
+ self.write_conf()
+ self.bootstrap_network()
+ self.run_experiment()
print("[IRATI experiment] end")
def process_node(self, node):
@@ -89,17 +99,19 @@ class Experiment(mod.Experiment):
name = node.name
gen_files_conf = 'shimeth.%(name)s.*.dif da.map %(name)s.ipcm.conf' % {
'name': name}
- if any(name in dif.members for dif in self.dif_ordering):
+ if any(node in dif.members for dif in self.dif_ordering):
gen_files_conf = ' '.join(
[gen_files_conf, 'normal.%(name)s.*.dif' % {'name': name}])
+ dir_path = os.path.dirname(os.path.abspath(__file__))
gen_files_bin = 'enroll.py'
+ gen_files_bin_full = os.path.join(dir_path, 'enroll.py')
ipcm_components = ['scripting', 'console']
if self.manager:
ipcm_components.append('mad')
ipcm_components = ', '.join(ipcm_components)
- gen_files = ' '.join([gen_files_conf, gen_files_bin])
+ gen_files = ' '.join([gen_files_conf, gen_files_bin_full])
sshopts = ('-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null'
' -o IdentityFile=buildroot/irati_rsa')
@@ -114,16 +126,20 @@ class Experiment(mod.Experiment):
'verb': 'DBG',
'ipcmcomps': ipcm_components}
try:
+ print('DEBUG: executing >> '
+ 'scp %(sshopts)s -r -P %(ssh)s '
+ '%(genfiles)s %(username)s@localhost:'
+ % format_args)
subprocess.check_call(('scp %(sshopts)s -r -P %(ssh)s '
'%(genfiles)s %(username)s@localhost:'
- % format_args).split())
+ % format_args), shell=True)
except subprocess.CalledProcessError as e:
raise Exception(str(e))
# TODO: review ssh opts through ssh support
cmds = [self.sudo('hostname %(name)s' % format_args),
- self.sudo('chmd a+rw /dev/irati'),
+ self.sudo('chmod a+rw /dev/irati'),
self.sudo('mv %(genfilesconf)s /etc' % format_args),
self.sudo('mv %(genfilesbin)s /usr/bin') % format_args]
@@ -134,6 +150,10 @@ class Experiment(mod.Experiment):
'-c /etc/%(name)s.ipcm.conf -l %(verb)s &> log &'
% format_args)]
+ print('DEBUG: sending node setup via ssh.')
+ # print('Credentials:')
+ # print(node.ssh_config.hostname, node.ssh_config.port,
+ # self.testbed.username, self.testbed.password)
ssh_support.execute_commands(self.testbed, node.ssh_config, cmds)
def enroll_nodes(self):
@@ -159,24 +179,18 @@ class Experiment(mod.Experiment):
'--ipcm-conf /etc/%(name)s.ipcm.conf '
'--enrollee-name %(dif)s.%(name)s.IPCP '
'--enroller-name %(dif)s.%(o_name)s.IPCP'
- % e_args)
+ % e_args)
+ print('DEBUG: sending enrollment operation via ssh.')
+ # print('Credentials:')
+ # print(e['enrollee'].ssh_config.hostname,
+ # e['enrollee'].ssh_config.port,
+ # self.testbed.username, self.testbed.password)
ssh_support.execute_command(self.testbed,
e['enrollee'].ssh_config,
cmd)
-
-class ConfBuilder(object):
-
- def __init__(self, experiment, manager):
- self.write_conf(experiment, manager)
-
- @staticmethod
- def write_conf(experiment, manager):
- """
- :type experiment: Experiment
- :param experiment: the experiment to be configured
- :param manager: boolean indicating if a manager is requested
- """
+ def write_conf(self):
+ """Write the configuration files"""
# Constants and initializations
ipcmconfs = dict()
difconfs = dict()
@@ -186,7 +200,7 @@ class ConfBuilder(object):
# TODO: what format are the mappings registered in? Is this ok?
app_mappings = []
- for node in experiment.nodes:
+ for node in self.nodes:
app_mappings += [{'name': app, 'dif': dif.name}
for app in node.registrations
for dif in node.registrations[app]]
@@ -195,10 +209,10 @@ class ConfBuilder(object):
# Otherwise, assume the standard applications are to be mapped in
# the DIF with the highest rank.
if len(app_mappings) == 0:
- if len(experiment.dif_ordering) > 0:
+ if len(self.dif_ordering) > 0:
for adm in \
irati_templates.da_map_base["applicationToDIFMappings"]:
- adm["difName"] = "%s" % (experiment.dif_ordering[-1],)
+ adm["difName"] = "%s" % (self.dif_ordering[-1],)
else:
irati_templates.da_map_base["applicationToDIFMappings"] = []
for apm in app_mappings:
@@ -211,7 +225,7 @@ class ConfBuilder(object):
# and in that case we should add it to the qemu plugin too...
# Where should we take it in input?
- if manager:
+ if self.manager:
# Add MAD/Manager configuration
irati_templates.ipcmconf_base["addons"] = {
"mad": {
@@ -225,15 +239,15 @@ class ConfBuilder(object):
}
node_number = 1
- for node in experiment.nodes: # type: mod.Node
+ for node in self.nodes: # type: mod.Node
node2id_map[node.name] = node_number
node_number += 1
ipcmconfs[node.name] = copy.deepcopy(irati_templates.ipcmconf_base)
- if manager:
+ if self.manager:
ipcmconfs[node.name]["addons"]["mad"]["managerAppName"] \
= "%s.mad-1--" % (node.name,)
- for dif in experiment.dif_ordering: # type: mod.DIF
+ for dif in self.dif_ordering: # type: mod.DIF
if isinstance(dif, mod.ShimEthDIF):
ipcp2shim_map.update({ipcp.name: dif for ipcp in dif.ipcps})
elif isinstance(dif, mod.NormalDIF):
@@ -243,21 +257,20 @@ class ConfBuilder(object):
irati_templates.normal_dif_base
)
- for node in experiment.nodes: # type: mod.Node
+ for node in self.nodes: # type: mod.Node
ipcmconf = ipcmconfs[node.name]
for ipcp in node.ipcps: # type: mod.ShimEthIPCP
if isinstance(ipcp, mod.ShimEthIPCP):
- node_name, port_id = ipcp.ifname.split('.')
shim = ipcp2shim_map[ipcp.name] # type: mod.ShimEthDIF
ipcmconf["ipcProcessesToCreate"].append({
- "apName": "eth.%d.IPCP" % int(port_id),
+ "apName": "eth.%s.IPCP" % ipcp.name,
"apInstance": "1",
"difName": shim.name
})
template_file_name = 'shimeth.%s.%s.dif' \
- % (node_name, shim.name)
+ % (node.name, shim.name)
ipcmconf["difConfigurations"].append({
"name": shim.name,
"template": template_file_name
@@ -267,7 +280,7 @@ class ConfBuilder(object):
fout.write(json.dumps(
{"difType": "shim-eth-vlan",
"configParameters": {
- "interface-name": "ifc%d" % (int(port_id),)
+ "interface-name": ipcp.ifname
}
},
indent=4, sort_keys=True))
@@ -280,7 +293,7 @@ class ConfBuilder(object):
# This would happen because at the moment of registration,
# it may be that the IPCP of the lower DIF has not been created yet.
shims = ipcp2shim_map.values()
- for dif in experiment.dif_ordering: # type: mod.NormalDIF
+ for dif in self.dif_ordering: # type: mod.NormalDIF
if dif in shims:
# Shims are managed separately, in the previous loop
@@ -326,7 +339,7 @@ class ConfBuilder(object):
indent=4,
sort_keys=True)
- for node in experiment.nodes:
+ for node in self.nodes:
# Dump the IPCM configuration files
with open('%s.ipcm.conf' % (node.name,), 'w') as node_file:
json.dump(ipcmconfs[node.name],
@@ -334,7 +347,7 @@ class ConfBuilder(object):
indent=4,
sort_keys=True)
- for dif in experiment.dif_ordering: # type: mod.DIF
+ for dif in self.dif_ordering: # type: mod.DIF
dif_conf = difconfs.get(dif.name, None)
if dif_conf:
# Dump the normal DIF configuration files
diff --git a/rumba/ssh_support.py b/rumba/ssh_support.py
index ad32d13..c9ae687 100644
--- a/rumba/ssh_support.py
+++ b/rumba/ssh_support.py
@@ -27,6 +27,13 @@ def get_ssh_client():
return ssh_client
+def _print_stream(stream):
+ o = str(stream.read()).strip('b\'\"\\n')
+ if o != "":
+ o_array = o.split('\\n')
+ for oi in o_array:
+ print(oi)
+
def execute_commands(testbed, ssh_config, commands, time_out=3):
'''
Remote execution of a list of shell command on hostname. By
@@ -34,7 +41,7 @@ def execute_commands(testbed, ssh_config, commands, time_out=3):
@param testbed: testbed info
@param ssh_config: ssh config of the node
- @param command: *nix shell command
+ @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
@@ -42,19 +49,17 @@ def execute_commands(testbed, ssh_config, commands, time_out=3):
ssh_client = get_ssh_client()
try:
- #print("Connecting to %s@%s:%s (pwd=%s)" % (testbed.username,
- # ssh_config.hostname, ssh_config.port, testbed.password))
ssh_client.connect(ssh_config.hostname, ssh_config.port,
testbed.username, testbed.password,
look_for_keys=True, timeout=time_out)
for command in commands:
+ print("%s@%s:%s >> %s" % (testbed.username,
+ ssh_config.hostname, ssh_config.port, command))
+ envars = '. /etc/profile;'
+ command = envars + ' ' + command
stdin, stdout, stderr = ssh_client.exec_command(command)
- del stdin, stdout
- err = str(stderr.read()).strip('b\'\"\\n')
- if err != "":
- err_array = err.split('\\n')
- for erra in err_array:
- print(erra)
+ _print_stream(stdout)
+ _print_stream(stderr)
ssh_client.close()
except Exception as e:
@@ -75,25 +80,7 @@ def execute_command(testbed, ssh_config, command, time_out=3):
@return: stdout resulting from the command
'''
- ssh_client = get_ssh_client()
-
- try:
- ssh_client.connect(ssh_config.hostname, ssh_config.port,
- testbed.username, testbed.password,
- look_for_keys=True, timeout=time_out)
- stdin, stdout, stderr = ssh_client.exec_command(command)
- del stdin
- err = str(stderr.read()).strip('b\'\"\\n')
- if err != "":
- print(err)
- output = str(stdout.read()).strip('b\'\"\\n')
- ssh_client.close()
-
- return output
-
- except Exception as e:
- print(str(e))
- return
+ return execute_commands(testbed, ssh_config, [command], time_out)
def copy_file_to_testbed(testbed, ssh_config, text, file_name):
'''
diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py
index da3c7b9..d3e1698 100644
--- a/rumba/testbeds/qemu.py
+++ b/rumba/testbeds/qemu.py
@@ -24,6 +24,7 @@ import subprocess
import os
import rumba.model as mod
+from rumba import ssh_support
class Testbed(mod.Testbed):
@@ -74,6 +75,41 @@ class Testbed(mod.Testbed):
else:
results_queue.put("Command chain ran with %d errors" % errors)
+ def recover_if_names(self, experiment):
+ for node in experiment.nodes:
+ for ipcp in node.ipcps:
+ if isinstance(ipcp, mod.ShimEthIPCP):
+ shim_name, node_name = ipcp.name.split('.')
+ port_set = [x for x in self.vms[node_name]['ports']
+ if x['shim'].name == shim_name]
+ port = port_set[0]
+ port_id = port['port_id']
+ vm_id = self.vms[node_name]['id']
+ mac = '00:0a:0a:0a:%02x:%02x' % (vm_id, port_id)
+ print('DEBUG: recovering ifname for port: '
+ + port['tap_id'] + '.')
+ output = ssh_support.return_commands(
+ self,
+ node.ssh_config,
+ ['mac2ifname ' + mac])
+ print('DEBUG: output is %s' % output)
+ if not hasattr(output, '__len__') or len(output) != 1:
+ raise Exception("Could not retrieve ifname for ipcp %s."
+ % ipcp.name)
+ ipcp.ifname = output[0]
+ args = {'vlan': int(port['shim'].name), 'port': ipcp.ifname}
+ cmds = ['ip link set %(port)s up'
+ % args,
+ 'ip link add link %(port)s name %(port)s.%(vlan)s '
+ 'type vlan id %(vlan)s'
+ % args,
+ 'ip link set %(port)s.%(vlan)s up'
+ % args]
+ ssh_support.execute_commands(self,
+ node.ssh_config,
+ cmds)
+
+
def swap_in(self, experiment):
"""
:type experiment mod.Experiment
@@ -190,6 +226,7 @@ class Testbed(mod.Testbed):
for node in experiment.nodes:
name = node.name
vm = self.vms.setdefault(name, {'vm': node, 'ports': []})
+ vm['id'] = vmid
fwdp = base_port + vmid
fwdc = fwdp + 10000
mac = '00:0a:0a:0a:%02x:%02x' % (vmid, 99)
@@ -266,6 +303,8 @@ class Testbed(mod.Testbed):
print('Sleeping %s secs waiting for the last VMs to boot' % tsleep)
time.sleep(tsleep)
+ self.recover_if_names(experiment)
+
def swap_out(self, experiment):
"""
:rtype str
diff --git a/tools/democonf2rumba.py b/tools/democonf2rumba.py
new file mode 100755
index 0000000..d9ea8e7
--- /dev/null
+++ b/tools/democonf2rumba.py
@@ -0,0 +1,256 @@
+#!/usr/bin/env python
+
+import argparse
+import re
+
+import rumba.model as mod
+
+
+def make_experiment(filename, experiment_class, experiment_kwargs,
+ testbed_class, testbed_kwargs):
+ """
+ :type filename str
+ :param filename: path to the .conf file
+ :param experiment_class: subclass of mod.Experiment
+ :param experiment_kwargs: args dict for experiment constructor
+ (nodes will be overwritten)
+ :param testbed_class: subclass of mod.Testbed
+ :param testbed_kwargs: args dict for experiment constructor
+ (nodes will be overwritten)
+ """
+ shims = {}
+ nodes = {}
+ difs = {}
+
+ print('Reading file %s.' % (filename,))
+
+ with open(filename, 'r') as conf:
+
+ line_cnt = 0
+
+ while 1:
+ line = conf.readline()
+ if line == '':
+ break
+ line_cnt += 1
+
+ line = line.replace('\n', '').strip()
+
+ if line.startswith('#') or line == "":
+ continue
+
+ m = re.match(r'\s*eth\s+([\w-]+)\s+(\d+)([GMK])bps\s+(\w.*)$', line)
+ if m:
+ shim = m.group(1)
+ speed = int(m.group(2))
+ speed_unit = m.group(3).lower()
+ vm_list = m.group(4).split()
+
+ if shim in shims or shim in difs:
+ print('Error: Line %d: shim %s already defined'
+ % (line_cnt, shim))
+ continue
+
+ if speed_unit == 'K':
+ speed = speed // 1000
+ if speed_unit == 'G':
+ speed = speed * 1000
+
+ shims[shim] = {'name': shim,
+ 'speed': speed,
+ 'type': 'eth'}
+
+ for vm in vm_list:
+ nodes.setdefault(vm, {'name': vm, 'difs': [],
+ 'dif_registrations': {},
+ 'registrations': {}})
+ nodes[vm]['difs'].append(shim)
+ continue
+
+ m = re.match(r'\s*dif\s+([\w-]+)\s+([\w-]+)\s+(\w.*)$', line)
+ if m:
+ dif = m.group(1)
+ vm = m.group(2)
+ dif_list = m.group(3).split()
+
+ if dif in shims:
+ print('Error: Line %d: dif %s already defined as shim'
+ % (line_cnt, dif))
+ continue
+
+ difs.setdefault(dif, {
+ 'name': dif}) # Other dict contents might be policies.
+
+ if vm in nodes and dif in nodes[vm]['dif_registrations']:
+ print('Error: Line %d: vm %s in dif %s already specified'
+ % (line_cnt, vm, dif))
+ continue
+
+ nodes.setdefault(vm, {'name': vm, 'difs': [],
+ 'dif_registrations': {},
+ 'registrations': {}})
+ nodes[vm]['difs'].append(dif)
+ nodes[vm]['dif_registrations'] \
+ [dif] = dif_list
+ # It is not defined yet, per check above.
+
+ continue
+
+ # No match, spit a warning
+ print('Warning: Line %d unrecognized and ignored' % line_cnt)
+
+ # File parsed
+
+ parsed_difs = {}
+
+ for shim_name, shim in shims.items():
+ parsed_difs[shim_name] = (mod.ShimEthDIF(shim_name,
+ link_speed=shim['speed']))
+
+ for dif_name, dif in difs.items():
+ parsed_difs[dif_name] = (mod.NormalDIF(dif_name))
+
+ parsed_nodes = []
+ for node, node_data in nodes.items():
+ name = node_data['name']
+ difs = [parsed_difs[x] for x in node_data['difs']]
+ dif_registrations = {parsed_difs[x]: [parsed_difs[y] for y in l]
+ for x, l in node_data['dif_registrations']
+ .items()}
+ parsed_nodes.append(mod.Node(name, difs, dif_registrations))
+
+ testbed = testbed_class(**testbed_kwargs)
+
+ experiment_kwargs['testbed'] = testbed
+ experiment_kwargs['nodes'] = parsed_nodes
+
+ return experiment_class(**experiment_kwargs).run()
+
+
+def setup_testbed_common_args(t_p):
+
+ t_p.add_argument('-E', '--exp_name', metavar='EXP_NAME', type=str,
+ required=True,
+ help='Experiment name')
+ t_p.add_argument('-U', '--username', metavar='USERNAME', type=str,
+ required=True,
+ help='Testbed user name')
+ t_p.add_argument('-P', '--proj_name', metavar='PROJECT_NAME', type=str,
+ help='Project name')
+ t_p.add_argument('-W', '--password', metavar='PASSWORD', type=str,
+ help='Testbed password')
+
+
+if __name__ == '__main__':
+ description = "Demonstrator config file to rumba script converter"
+ epilog = "2017 Marco Capitani <m.capitani@nextworks.it>"
+
+ parser = argparse.ArgumentParser(description=description,
+ epilog=epilog)
+
+ parser.add_argument('-P', '--prototype', type=str, required=True,
+ choices=['irati', 'ouroboros', 'rlite'],
+ help='The kind of prototype plugin to use to run'
+ ' the experiment.')
+
+ parser.add_argument('-C', '--conf', metavar='CONFIG', type=str,
+ required=True,
+ help='Path to the config file to parse')
+
+ subparsers = parser.add_subparsers(dest='testbed')
+ emulab_p = subparsers.add_parser('emulab', help='Use emulab testbed')
+ jfed_p = subparsers.add_parser('jfed', help='Use jfed testbed')
+ qemu_p = subparsers.add_parser('qemu', help='Use qemu testbed')
+ fake_p = subparsers.add_parser('fake', help='Use fake testbed')
+
+ for t in [emulab_p, jfed_p, qemu_p, fake_p]:
+ setup_testbed_common_args(t)
+
+ qemu_p.add_argument('-B', '--bzimage', metavar='BZIMAGE', type=str,
+ required=True,
+ help='path to the bzImage file to use')
+ qemu_p.add_argument('-I', '--initramfs', metavar='INITRAMFS', type=str,
+ required=True,
+ help='path to the initramfs file to use')
+ qemu_p.add_argument('-V', '--use_vhost', action='store_true',
+ default=False, help='Use vhost')
+ qemu_p.add_argument('-Q', '--qemu_logs_dir', metavar='QEMU_LOGS', type=str,
+ default=None, help='path to the folder for qemu logs')
+
+ emulab_p.add_argument('-R', '--url', metavar='URL', type=str,
+ default="wall2.ilabt.iminds.be",
+ help='Url')
+ emulab_p.add_argument('-I', '--image', metavar='IMG', type=str,
+ default="UBUNTU14-64-STD",
+ help='Ubuntu image')
+
+ jfed_p.add_argument('-C', '--cert_file', metavar='CERT', type=str,
+ required=True,
+ help='Certificate file')
+ jfed_p.add_argument('-J', '--jar', metavar='JAR', type=str,
+ required=True,
+ help='Jfed jar')
+ jfed_p.add_argument('-H', '--exp_hours', metavar='HOURS', type=int,
+ default=2, help='Experiment hours')
+ jfed_p.add_argument('-A', '--authority', metavar='AUTH', type=str,
+ default="wall2.ilabt.iminds.be",
+ help='Authority')
+
+ args = parser.parse_args()
+
+ if args.testbed == 'emulab':
+ import rumba.testbeds.emulab as emulab
+ test_class = emulab.Testbed
+ testbed_args = {a.dest: getattr(args, a.dest)
+ for a in emulab_p._actions
+ if a.dest != 'help'
+ and getattr(args, a.dest) is not None}
+ elif args.testbed == 'jfed':
+ import rumba.testbeds.jfed as jfed
+ test_class = jfed.Testbed
+ testbed_args = {a.dest: getattr(args, a.dest)
+ for a in jfed_p._actions
+ if a.dest != 'help'
+ and getattr(args, a.dest) is not None}
+ elif args.testbed == 'qemu':
+ import rumba.testbeds.qemu as qemu
+ test_class = qemu.Testbed
+ testbed_args = {a.dest: getattr(args, a.dest)
+ for a in qemu_p._actions
+ if a.dest != 'help'
+ and getattr(args, a.dest) is not None}
+ elif args.testbed == 'fake':
+ import rumba.testbeds.faketestbed as fake
+ test_class = fake.Testbed
+ testbed_args = {a.dest: getattr(args, a.dest)
+ for a in fake_p._actions
+ if a.dest != 'help'
+ and getattr(args, a.dest) is not None}
+ else:
+ if args.testbed is None:
+ print('Testbed type must be specified!')
+ print(parser.format_help())
+ exit(1)
+ raise ValueError('Unexpected testbed: %s.' % args.testbed)
+
+ if args.prototype == 'irati':
+ import rumba.prototypes.irati as irati
+ exp_class = irati.Experiment
+ elif args.prototype == 'ouroboros':
+ import rumba.prototypes.ouroboros as ouroboros
+ exp_class = ouroboros.Experiment
+ elif args.prototype == 'rlite':
+ import rumba.prototypes.rlite as rlite
+ exp_class = rlite.Experiment
+ else:
+ raise ValueError('Unexpected prototype: %s.' % args.testbed)
+
+ try:
+ make_experiment(args.conf,
+ experiment_class=exp_class,
+ experiment_kwargs={},
+ testbed_class=test_class,
+ testbed_kwargs=testbed_args)
+
+ except KeyboardInterrupt:
+ print("Interrupted. Closing down.")