diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | rumba/testbeds/qemu.py | 167 |
2 files changed, 166 insertions, 4 deletions
@@ -93,3 +93,6 @@ ENV/ *rspec *.pem + +# PyCharm metadata folder +.idea diff --git a/rumba/testbeds/qemu.py b/rumba/testbeds/qemu.py index 158f360..2f5491a 100644 --- a/rumba/testbeds/qemu.py +++ b/rumba/testbeds/qemu.py @@ -17,16 +17,175 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA +import multiprocessing import rumba.model as mod +from subprocess import Popen + -# Fake testbed, useful for testing class Testbed(mod.Testbed): - def __init__(self, exp_name, username, proj_name="ARCFIRE", password=""): + def __init__(self, exp_name, username, vm_img_folder, proj_name="ARCFIRE", password="", use_vhost=True): mod.Testbed.__init__(self, exp_name, username, password, proj_name) + self.vms = {} + self.shims = [] + self.vm_img_path = vm_img_folder + self.vhost = use_vhost def create_experiment(self, experiment): - print("[QEMU testbed] experiment swapped in") + """ + :type experiment mod.Experiment + :rtype str + :param experiment: The experiment running + :return the script to be run to create the experiment's vms + """ + print("[QEMU testbed] swapping in") + command_str = "" + + # Building bridges and taps + + for shim in experiment.dif_ordering: + if not isinstance(shim, mod.ShimEthDIF): + # Nothing to do here + continue + self.shims.append(shim) + command_str += 'sudo brctl addbr %(br)s\n' \ + 'sudo ip link set %(br)s up\n' \ + '\n' % {'br': shim.name} + for node in shim.members: # type:mod.Node + name = node.name + vm = self.vms.setdefault(name, {'vm': node, 'ports': []}) + port_id = len(vm['ports']) + 1 + tap_id = '%s.%02x' % (name, port_id) + + command_str += 'sudo ip tuntap add mode tap name %(tap)s\n' \ + 'sudo ip link set %(tap)s up\n' \ + 'sudo brctl addif %(br)s %(tap)s\n\n' \ + % {'tap': tap_id, 'br': shim.name} + + if shim.link_speed > 0: + speed = '%dmbit' % shim.link_speed + + # Rate limit the traffic transmitted on the TAP interface + command_str += 'sudo tc qdisc add dev %(tap)s handle 1: root ' \ + 'htb default 11\n' \ + 'sudo 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 ' \ + '1:11 htb rate %(speed)s\n' \ + % {'tap': tap_id, 'speed': speed} + + vm['ports'].append({'tap_id': tap_id, 'shim': shim, 'port_id': port_id}) + # TODO deal with Ip address (shim UDP DIF). + + # Building vms + + boot_batch_size = max(1, multiprocessing.cpu_count() // 2) + booting_budget = boot_batch_size + boot_backoff = 12 + base_port = 2222 + vm_memory = 164 + vm_frontend = 'virtio-net-pci' + + vmid = 1 + + for node in experiment.nodes: + name = node.full_name + vm = self.vms.setdefault(name, {'vm': node, 'ports': []}) + fwdp = base_port + vmid + fwdc = fwdp + 10000 + mac = '00:0a:0a:0a:%02x:%02x' % (vmid, 99) + vm['ssh'] = fwdp + vm['id'] = vmid + + vars_dict = {'fwdp': fwdp, 'id': vmid, 'mac': mac, + 'vmimgpath': self.vm_img_path, 'fwdc': fwdc, + 'memory': vm_memory, 'frontend': vm_frontend, + 'vmname': name} + + host_fwd_str = 'hostfwd=tcp::%(fwdp)s-:22' % vars_dict + vars_dict['hostfwdstr'] = host_fwd_str + + command_str += 'qemu-system-x86_64 ' + # TODO manage non default images + command_str += '-kernel %(vmimgpath)s/bzImage ' \ + '-append "console=ttyS0" ' \ + '-initrd %(vmimgpath)s/rootfs.cpio ' % vars_dict + command_str += '-nographic ' \ + '-display none ' \ + '--enable-kvm ' \ + '-smp 1 ' \ + '-m %(memory)sM ' \ + '-device %(frontend)s,mac=%(mac)s,netdev=mgmt ' \ + '-netdev user,id=mgmt,%(hostfwdstr)s ' \ + '-vga std ' \ + '-pidfile rina-%(id)s.pid ' \ + '-serial file:%(vmname)s.log ' \ + % vars_dict + + del vars_dict + + for port in vm['ports']: + tap_id = port['tap_id'] + mac = '00:0a:0a:0a:%02x:%02x' % (vmid, port['port_id']) + port['mac'] = mac + + command_str += '-device %(frontend)s,mac=%(mac)s,netdev=data%(idx)s ' \ + '-netdev tap,ifname=%(tap)s,id=data%(idx)s,script=no,' \ + 'downscript=no%(vhost)s ' \ + % {'mac': mac, 'tap': tap_id, 'idx': port['port_id'], + 'frontend': vm_frontend, + 'vhost': ',vhost=on' if self.vhost else ''} + + command_str += '&\n' + + booting_budget -= 1 + if booting_budget <= 0: + command_str += 'sleep %s\n' % boot_backoff + booting_budget = boot_batch_size + + vmid += 1 + Popen([command_str], shell=True, executable="/bin/bash").wait() # TODO something more elegant maybe? + return command_str + + def _make_down_script(self): + """ + :rtype str + :return: The script to tear down the experiment + """ + command_str = 'kill_qemu() {\n' \ + ' PIDFILE=$1\n' \ + ' PID=$(cat $PIDFILE)\n' \ + ' if [ -n $PID ]; then\n' \ + ' kill $PID\n' \ + ' while [ -n "$(ps -p $PID -o comm=)" ]; do\n' \ + ' sleep 1\n' \ + ' done\n' \ + ' fi\n' \ + '\n' \ + ' rm $PIDFILE\n' \ + '}\n\n' + + for vm_name, vm in self.vms.items(): + command_str += 'kill_qemu rina-%(id)s.pid\n' % {'id': vm['id']} + + command_str += '\n' + + for vm_name, vm in self.vms.items(): + for port in vm['ports']: + tap = port['tap_id'] + shim = port['shim'] + + command_str += 'sudo brctl delif %(br)s %(tap)s\n' \ + 'sudo ip link set %(tap)s down\n' \ + 'sudo ip tuntap del mode tap name %(tap)s\n\n' \ + % {'tap': tap, 'br': shim.name} + + for shim in self.shims: + command_str += 'sudo ip link set %(br)s down\n' \ + 'sudo brctl delbr %(br)s\n' \ + '\n' % {'br': shim.name} + return command_str def __del__(self): - pass + script = self._make_down_script() + Popen([script], shell=True, executable="/bin/bash") |