From 440ab00aa57dd8c7c0076d93011814b7fb25ec76 Mon Sep 17 00:00:00 2001 From: Sander Vrijders Date: Wed, 13 Sep 2017 11:26:53 +0200 Subject: build: Add continuous integration This adds CI to Rumba. --- .gitlab-ci.yml | 9 ++++++++ examples/mouse.py | 16 +++++++++------ examples/vpn.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ rumba/model.py | 6 ++++++ rumba/ssh_support.py | 47 +++++++++++++++++++++++++----------------- rumba/testbeds/jfed.py | 10 ++++++--- rumba/testbeds/qemu.py | 54 +++++++++++++++++++++++++++++++----------------- setup.py | 22 ++++++++++---------- 8 files changed, 162 insertions(+), 58 deletions(-) create mode 100644 .gitlab-ci.yml mode change 100644 => 100755 examples/mouse.py create mode 100755 examples/vpn.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..10bf999 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,9 @@ +before_script: + - apt-get update -qy + - apt-get install -y python-dev python-pip bridge-utils qemu + - pip install setuptools --upgrade + - python setup.py install + +irati-test: + script: + - python examples/vpn.py \ No newline at end of file diff --git a/examples/mouse.py b/examples/mouse.py old mode 100644 new mode 100755 index 25e2487..5ff5c6a --- a/examples/mouse.py +++ b/examples/mouse.py @@ -15,6 +15,10 @@ import rumba.prototypes.ouroboros as our import rumba.prototypes.rlite as rl import rumba.prototypes.irati as irati + +log.set_logging_level('DEBUG') + + n01 = NormalDIF("n01") e01 = ShimEthDIF("e01") @@ -92,14 +96,14 @@ n = Node("n", difs = [n01, e17], dif_registrations = {n01 : [e17]}) -tb = jfed.Testbed(exp_name = "mouse2", - cert_file = "/home/sander/cert.pem", - username = "sander") +tb = qemu.Testbed(exp_name = "mouse2") exp = rl.Experiment(tb, nodes = [a, b, c, d, e, f, g, h, i, j, k, l, m, n]) print(exp) -exp.swap_in() -exp.install_prototype() -exp.bootstrap_prototype() +try: + exp.swap_in() + exp.bootstrap_prototype() +finally: + exp.swap_out() diff --git a/examples/vpn.py b/examples/vpn.py new file mode 100755 index 0000000..4ad9f6d --- /dev/null +++ b/examples/vpn.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +# An example script using the rumba package + +from rumba.model import * + +# import testbed plugins +import rumba.testbeds.emulab as emulab +import rumba.testbeds.jfed as jfed +import rumba.testbeds.faketestbed as fake +import rumba.testbeds.qemu as qemu + +# import prototype plugins +import rumba.prototypes.ouroboros as our +import rumba.prototypes.rlite as rl +import rumba.prototypes.irati as irati + +import rumba.log as log + +log.set_logging_level('DEBUG') + + +n1 = NormalDIF("n1") +n2 = NormalDIF("n2") +e1 = ShimEthDIF("e1") +e2 = ShimEthDIF("e2") +e3 = ShimEthDIF("e3") + +a = Node("a", + difs = [e1, n1, n2], + dif_registrations = {n1 : [e1], n2 : [n1]}) + +b = Node("b", + difs = [e1, e2, n1], + dif_registrations = {n1 : [e1, e2]}) + +c = Node("c", + difs = [e2, e3, n1], + dif_registrations = {n1 : [e2, e3]}) + +d = Node("d", + difs = [e3, n1, n2], + dif_registrations = {n1 : [e3], n2 : [n1]}) + +tb = qemu.Testbed(exp_name = 'example1', + username = 'sander') + +exp = our.Experiment(tb, nodes = [a, b, c, d]) + +print(exp) + +#try: +# exp.swap_in() +# exp.bootstrap_prototype() +#finally: +# exp.swap_out() diff --git a/rumba/model.py b/rumba/model.py index 6303799..06d45ec 100644 --- a/rumba/model.py +++ b/rumba/model.py @@ -55,6 +55,12 @@ except OSError: # Already there, nothing to do pass +cache_parent_dir = os.path.join(os.path.expanduser("~"), '.cache/') +try: + os.mkdir(cache_parent_dir) +except OSError: + # Already there, nothing to do + pass cache_dir = os.path.join(os.path.expanduser("~"), '.cache/rumba/') try: os.mkdir(cache_dir) diff --git a/rumba/ssh_support.py b/rumba/ssh_support.py index 9261ce0..dfa290a 100644 --- a/rumba/ssh_support.py +++ b/rumba/ssh_support.py @@ -32,6 +32,9 @@ import rumba.log as log logger = log.get_logger(__name__) +class SSHException(Exception): + pass + def get_ssh_client(): ssh_client = paramiko.SSHClient() ssh_client.load_system_host_keys() @@ -118,23 +121,29 @@ def execute_commands(testbed, ssh_config, commands, time_out=3): testbed.username, testbed.password, look_for_keys=True, timeout=time_out, sock=proxy) - o = "" - for command in commands: - logger.debug("%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) - o = _print_stream(stdout) - _print_stream(stderr) - ssh_client.close() - return o + except paramiko.ssh_exception.SSHException as e: + raise SSHException('Failed to connect to host') - except Exception as e: - logger.error(str(e)) - return + o = "" + for command in commands: + logger.debug("%s@%s:%s >> %s" % (testbed.username, + ssh_config.hostname, + ssh_config.port, + command)) + envars = '. /etc/profile;' + command = envars + ' ' + command + chan = ssh_client.get_transport().open_session() + stdout = chan.makefile() + try: + chan.exec_command(command) + except paramiko.ssh_exception.SSHException as e: + raise SSHException('Failed to execute command') + if (chan.recv_exit_status() != 0): + raise SSHException('A remote command returned an error') + o = _print_stream(stdout) + + ssh_client.close() + return o def execute_command(testbed, ssh_config, command, time_out=3): @@ -195,8 +204,8 @@ def write_text_to_file(testbed, ssh_config, text, file_name): remote_file.write(text) remote_file.close() - except Exception as e: - logger.error(str(e)) + except SSHException as e: + raise SSHException('Failed to write text to remote file') def copy_files_to_testbed(testbed, ssh_config, paths, destination): @@ -239,7 +248,7 @@ def copy_files_to_testbed(testbed, ssh_config, paths, destination): sftp_client.put(path, dest_file) except Exception as e: - logger.error(str(e)) + raise SSHException('Failed to copy files to testbed') def copy_file_to_testbed(testbed, ssh_config, path, destination): diff --git a/rumba/testbeds/jfed.py b/rumba/testbeds/jfed.py index c541037..d938179 100644 --- a/rumba/testbeds/jfed.py +++ b/rumba/testbeds/jfed.py @@ -27,10 +27,14 @@ import subprocess import getpass import xml.dom.minidom as xml import os.path - import time -import wget import tarfile +import sys + +if sys.version_info[0] >= 3: + from urllib.request import urlretrieve +else: + from urllib import urlretrieve import rumba.model as mod import rumba.log as log @@ -70,7 +74,7 @@ class Testbed(mod.Testbed): logger.info("Couldn't find jFed CLI. Downloading.") tarball = "jfed_cli.tar.gz" url = "http://jfed.iminds.be/downloads/stable/jar/" + tarball - wget.download(url) + urlretrieve(url, filename=tarball) tar = tarfile.open(tarball) tar.extractall() tar.close() diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py index 47f4670..80a3d4c 100644 --- a/rumba/testbeds/qemu.py +++ b/rumba/testbeds/qemu.py @@ -27,11 +27,16 @@ import multiprocessing import time import subprocess import os +import sys + +if sys.version_info[0] >= 3: + from urllib.request import urlretrieve +else: + from urllib import urlretrieve import rumba.model as mod import rumba.log as log import rumba.ssh_support as ssh_support -import wget logger = log.get_logger(__name__) @@ -48,6 +53,12 @@ class Testbed(mod.Testbed): self.bzimage_path = bzimage_path self.initramfs_path = initramfs_path + # Prepend sudo to all commands if the user is not 'root' + def may_sudo(self, cmds): + if os.geteuid() != 0: + for i in range(len(cmds)): + cmds[i] = "sudo %s" % cmds[i] + @staticmethod def _run_command_chain(commands, results_queue, error_queue, ignore_errors=False): @@ -72,7 +83,7 @@ class Testbed(mod.Testbed): logger.debug('executing >> %s', command) try: subprocess.check_call(command.split()) - except subprocess.CalledProcessError as e: + except (subprocess.CalledProcessError, IOError) as e: error_queue.put(str(e)) errors += 1 if not ignore_errors: @@ -134,14 +145,16 @@ class Testbed(mod.Testbed): self.bzimage_path = os.path.join(mod.cache_dir, bzimage) if not os.path.exists(self.bzimage_path): logger.info("Downloading %s" % (url_prefix + bzimage)) - wget.download(url_prefix + bzimage, out=self.bzimage_path) + urlretrieve(url_prefix + bzimage, + filename=self.bzimage_path) print("\n") if not self.initramfs_path: initramfs = '%s.rootfs.cpio' % (experiment.prototype_name()) self.initramfs_path = os.path.join(mod.cache_dir, initramfs) if not os.path.exists(self.initramfs_path): logger.info("Downloading %s" % (url_prefix + initramfs)) - wget.download(url_prefix + initramfs, out=self.initramfs_path) + urlretrieve(url_prefix + initramfs, + filename=self.initramfs_path) print("\n") logger.info('Setting up interfaces.') @@ -157,8 +170,8 @@ class Testbed(mod.Testbed): self.shims.append(shim) ipcps = shim.ipcps command_list = [] - command_list += ('sudo brctl addbr %(br)s\n' - 'sudo ip link set dev %(br)s up' + command_list += ('brctl addbr %(br)s\n' + 'ip link set dev %(br)s up' % {'br': shim.name} ).split('\n') for node in shim.members: # type:mod.Node @@ -167,9 +180,9 @@ class Testbed(mod.Testbed): port_id = len(vm['ports']) + 1 tap_id = '%s.%02x' % (name, port_id) - command_list += ('sudo ip tuntap add mode tap name %(tap)s\n' - 'sudo ip link set dev %(tap)s up\n' - 'sudo brctl addif %(br)s %(tap)s' + command_list += ('ip tuntap add mode tap name %(tap)s\n' + 'ip link set dev %(tap)s up\n' + 'brctl addif %(br)s %(tap)s' % {'tap': tap_id, 'br': shim.name} ).split('\n') @@ -178,11 +191,11 @@ class Testbed(mod.Testbed): # Rate limit the traffic transmitted on the TAP interface command_list += ( - 'sudo tc qdisc add dev %(tap)s handle 1: root ' + 'tc qdisc add dev %(tap)s handle 1: root ' 'htb default 11\n' - 'sudo tc class add dev %(tap)s parent 1: classid ' + 'tc class add dev %(tap)s parent 1: classid ' '1:1 htb rate 10gbit\n' - 'sudo tc class add dev %(tap)s parent 1:1 classid ' + 'tc class add dev %(tap)s parent 1:1 classid ' '1:11 htb rate %(speed)s' % {'tap': tap_id, 'speed': speed} ).split('\n') @@ -206,6 +219,7 @@ class Testbed(mod.Testbed): if not e_queue.empty(): break # Launch commands asynchronously + self.may_sudo(command_list) process = multiprocessing.Process(target=self._run_command_chain, args=(command_list, r_queue, @@ -283,9 +297,10 @@ class Testbed(mod.Testbed): '-append "console=ttyS0" ' '-initrd %(initramfs)s ' % vars_dict) + if os.path.exists('/dev/kvm'): + command += '--enable-kvm ' command += ('-vga std ' '-display none ' - '--enable-kvm ' '-smp 1 ' '-m %(memory)sM ' '-device %(frontend)s,mac=%(mac)s,netdev=mgmt ' @@ -367,12 +382,12 @@ class Testbed(mod.Testbed): shim = port['shim'] commands = [] - - commands += ('sudo brctl delif %(br)s %(tap)s\n' - 'sudo ip link set dev %(tap)s down\n' - 'sudo ip tuntap del mode tap name %(tap)s' + commands += ('brctl delif %(br)s %(tap)s\n' + 'ip link set dev %(tap)s down\n' + 'ip tuntap del mode tap name %(tap)s' % {'tap': tap, 'br': shim.name} ).split('\n') + self.may_sudo(commands) process = multiprocessing.Process( target=self._run_command_chain, args=(commands, results_queue, error_queue), @@ -407,10 +422,11 @@ class Testbed(mod.Testbed): for shim in self.shims: commands = [] - commands += ('sudo ip link set dev %(br)s down\n' - 'sudo brctl delbr %(br)s' + commands += ('ip link set dev %(br)s down\n' + 'brctl delbr %(br)s' % {'br': shim.name} ).split('\n') + self.may_sudo(commands) process = multiprocessing.Process(target=self._run_command_chain, args=(commands, results_queue, diff --git a/setup.py b/setup.py index 2648f89..02e5aef 100755 --- a/setup.py +++ b/setup.py @@ -4,16 +4,16 @@ import setuptools setuptools.setup( - name="Rumba", - version="0.4", - url="https://gitlab.com/arcfire/rumba", - keywords="rina measurement testbed", - author="Sander Vrijders", - author_email="sander.vrijders@intec.ugent.be", - license="LGPL", - description="Rumba measurement framework for RINA", - packages=["rumba", "rumba.testbeds", "rumba.prototypes"], - install_requires=["paramiko", "wget", 'repoze.lru; python_version<"3.2"'], - extras_require={"NumpyAcceleration": ["numpy"]}, + name='Rumba', + version='0.4', + url='https://gitlab.com/arcfire/rumba', + keywords='rina measurement testbed', + author='Sander Vrijders', + author_email='sander.vrijders@ugent.be', + license='LGPL', + description='Rumba measurement framework for RINA', + packages=['rumba', 'rumba.testbeds', 'rumba.prototypes'], + install_requires=['paramiko', 'repoze.lru; python_version<"3.2"'], + extras_require={'NumpyAcceleration': ['numpy']}, scripts=['tools/rumba-access'] ) -- cgit v1.2.3