diff options
-rw-r--r-- | rumba/model.py | 163 | ||||
-rw-r--r-- | rumba/prototypes/irati.py | 105 | ||||
-rw-r--r-- | rumba/ssh_support.py | 43 | ||||
-rw-r--r-- | rumba/testbeds/qemu.py | 39 | ||||
-rwxr-xr-x | tools/democonf2rumba.py | 256 |
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.") |