aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--rumba/testbeds/qemu.py167
2 files changed, 166 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 146de1b..dc1959d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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")