aboutsummaryrefslogtreecommitdiffstats
path: root/vnfs/qemu
diff options
context:
space:
mode:
Diffstat (limited to 'vnfs/qemu')
-rw-r--r--vnfs/qemu/qemu.py203
-rw-r--r--vnfs/qemu/qemu_dpdk.py115
-rw-r--r--vnfs/qemu/qemu_dpdk_vhost_cuse.py390
-rw-r--r--vnfs/qemu/qemu_dpdk_vhost_user.py387
4 files changed, 390 insertions, 705 deletions
diff --git a/vnfs/qemu/qemu.py b/vnfs/qemu/qemu.py
new file mode 100644
index 00000000..338ca771
--- /dev/null
+++ b/vnfs/qemu/qemu.py
@@ -0,0 +1,203 @@
+# Copyright 2015 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
+"""
+
+import os
+import logging
+import locale
+import re
+import subprocess
+
+from conf import settings as S
+from vnfs.vnf.vnf import IVnf
+
+class IVnfQemu(IVnf):
+ """
+ Abstract class for controling an instance of QEMU
+ """
+ _cmd = None
+ _expect = S.getValue('GUEST_PROMPT_LOGIN')
+ _proc_name = 'qemu'
+
+ class GuestCommandFilter(logging.Filter):
+ """
+ Filter out strings beginning with 'guestcmd :'.
+ """
+ def filter(self, record):
+ return record.getMessage().startswith(self.prefix)
+
+ def __init__(self):
+ """
+ :param timeout: Time to wait for login prompt. If set to
+ 0 do not wait.
+ :param number: Number of QEMU instance, used when multiple QEMU
+ instances are started at once.
+ :param args: Arguments to pass to QEMU.
+
+ :returns: None
+ """
+ super(IVnfQemu, self).__init__()
+ self._logger = logging.getLogger(__name__)
+ self._logfile = os.path.join(
+ S.getValue('LOG_DIR'),
+ S.getValue('LOG_FILE_QEMU')) + str(self._number)
+ self._timeout = 120
+ self._monitor = '%s/vm%dmonitor' % ('/tmp', self._number)
+
+ name = 'Client%d' % self._number
+ vnc = ':%d' % self._number
+ # don't use taskset to affinize main qemu process; It causes hangup
+ # of 2nd VM in case of DPDK. It also slows down VM responsivnes.
+ self._cmd = ['sudo', '-E', S.getValue('QEMU_BIN'),
+ '-m', S.getValue('GUEST_MEMORY')[self._number],
+ '-smp', str(S.getValue('GUEST_SMP')[self._number]),
+ '-cpu', 'host',
+ '-drive', 'if=scsi,file=' +
+ S.getValue('GUEST_IMAGE')[self._number],
+ '-drive',
+ 'if=scsi,file=fat:rw:%s,snapshot=off' %
+ S.getValue('GUEST_SHARE_DIR')[self._number],
+ '-boot', 'c', '--enable-kvm',
+ '-monitor', 'unix:%s,server,nowait' % self._monitor,
+ '-object',
+ 'memory-backend-file,id=mem,size=' +
+ str(S.getValue('GUEST_MEMORY')[self._number]) + 'M,' +
+ 'mem-path=' + S.getValue('HUGEPAGE_DIR') + ',share=on',
+ '-numa', 'node,memdev=mem -mem-prealloc',
+ '-nographic', '-vnc', str(vnc), '-name', name,
+ '-snapshot', '-net none', '-no-reboot',
+ ]
+ self._configure_logging()
+
+ def _configure_logging(self):
+ """
+ Configure logging.
+ """
+ self.GuestCommandFilter.prefix = self._log_prefix
+
+ logger = logging.getLogger()
+ cmd_logger = logging.FileHandler(
+ filename=os.path.join(S.getValue('LOG_DIR'),
+ S.getValue('LOG_FILE_GUEST_CMDS')) +
+ str(self._number))
+ cmd_logger.setLevel(logging.DEBUG)
+ cmd_logger.addFilter(self.GuestCommandFilter())
+ logger.addHandler(cmd_logger)
+
+ # startup/Shutdown
+
+ def start(self):
+ """
+ Start QEMU instance, login and prepare for commands.
+ """
+ super(IVnfQemu, self).start()
+ self._affinitize()
+
+ if self._timeout:
+ self._login()
+ self._config_guest_loopback()
+
+ # helper functions
+
+ def _login(self, timeout=120):
+ """
+ Login to QEMU instance.
+
+ This can be used immediately after booting the machine, provided a
+ sufficiently long ``timeout`` is given.
+
+ :param timeout: Timeout to wait for login to complete.
+
+ :returns: None
+ """
+ # if no timeout was set, we likely started QEMU without waiting for it
+ # to boot. This being the case, we best check that it has finished
+ # first.
+ if not self._timeout:
+ self._expect_process(timeout=timeout)
+
+ self._child.sendline(S.getValue('GUEST_USERNAME'))
+ self._child.expect(S.getValue('GUEST_PROMPT_PASSWORD'), timeout=5)
+ self._child.sendline(S.getValue('GUEST_PASSWORD'))
+
+ self._expect_process(S.getValue('GUEST_PROMPT'), timeout=5)
+
+ def send_and_pass(self, cmd, timeout=30):
+ """
+ Send ``cmd`` and wait ``timeout`` seconds for it to pass.
+
+ :param cmd: Command to send to guest.
+ :param timeout: Time to wait for prompt before checking return code.
+
+ :returns: None
+ """
+ self.execute(cmd)
+ self.wait(S.getValue('GUEST_PROMPT'), timeout=timeout)
+ self.execute('echo $?')
+ self._child.expect('^0$', timeout=1) # expect a 0
+ self.wait(S.getValue('GUEST_PROMPT'), timeout=timeout)
+
+ def _affinitize(self):
+ """
+ Affinitize the SMP cores of a QEMU instance.
+
+ This is a bit of a hack. The 'socat' utility is used to
+ interact with the QEMU HMP. This is necessary due to the lack
+ of QMP in older versions of QEMU, like v1.6.2. In future
+ releases, this should be replaced with calls to libvirt or
+ another Python-QEMU wrapper library.
+
+ :returns: None
+ """
+ thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
+
+ self._logger.info('Affinitizing guest...')
+
+ cur_locale = locale.getlocale()[1]
+ proc = subprocess.Popen(
+ ('echo', 'info cpus'), stdout=subprocess.PIPE)
+ output = subprocess.check_output(
+ ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
+ stdin=proc.stdout)
+ proc.wait()
+
+ for cpu in range(0, int(S.getValue('GUEST_SMP')[self._number])):
+ match = None
+ for line in output.decode(cur_locale).split('\n'):
+ match = re.search(thread_id % cpu, line)
+ if match:
+ self._affinitize_pid(
+ S.getValue('GUEST_CORE_BINDING')[self._number][cpu],
+ match.group(1))
+ break
+
+ if not match:
+ self._logger.error('Failed to affinitize guest core #%d. Could'
+ ' not parse tid.', cpu)
+
+ def _config_guest_loopback(self):
+ """
+ Configure VM to run VNF (e.g. port forwarding application)
+ """
+ pass
+
+ def wait(self, prompt=S.getValue('GUEST_PROMPT'), timeout=30):
+ super(IVnfQemu, self).wait(prompt=prompt, timeout=timeout)
+
+ def execute_and_wait(self, cmd, timeout=30,
+ prompt=S.getValue('GUEST_PROMPT')):
+ super(IVnfQemu, self).execute_and_wait(cmd, timeout=timeout,
+ prompt=prompt)
diff --git a/vnfs/qemu/qemu_dpdk.py b/vnfs/qemu/qemu_dpdk.py
new file mode 100644
index 00000000..0b8f90a0
--- /dev/null
+++ b/vnfs/qemu/qemu_dpdk.py
@@ -0,0 +1,115 @@
+# Copyright 2015 Intel Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
+"""
+
+from vnfs.qemu.qemu import IVnfQemu
+from conf import settings as S
+
+class IVnfQemuDpdk(IVnfQemu):
+ """
+ An abstract class for controling an instance of QEMU with DPDK vHost support
+ """
+
+ def __init__(self):
+ """
+ :param timeout: Time to wait for login prompt. If set to
+ 0 do not wait.
+ :param number: Number of QEMU instance, used when multiple QEMU
+ instances are started at once.
+ :param args: Arguments to pass to QEMU.
+
+ :returns: None
+ """
+ super(IVnfQemuDpdk, self).__init__()
+ self._cmd += []
+
+ def _modify_dpdk_makefile(self):
+ """
+ Modifies DPDK makefile in Guest before compilation
+ """
+ pass
+
+ def _config_guest_loopback(self):
+ """
+ Configure VM to run testpmd
+
+ Configure performs the following:
+ * Mount hugepages
+ * mount shared directory for copying DPDK
+ * Disable firewall
+ * Compile DPDK
+ * DPDK NIC bind
+ * Run testpmd
+ """
+
+ # Guest images _should_ have 1024 hugepages by default,
+ # but just in case:'''
+ self.execute_and_wait('sysctl vm.nr_hugepages=1024')
+
+ # Mount hugepages
+ self.execute_and_wait('mkdir -p /dev/hugepages')
+ self.execute_and_wait(
+ 'mount -t hugetlbfs hugetlbfs /dev/hugepages')
+
+ # mount shared directory
+ self.execute_and_wait('umount ' + S.getValue('OVS_DPDK_SHARE'))
+ self.execute_and_wait('rm -rf ' + S.getValue('GUEST_OVS_DPDK_DIR'))
+ self.execute_and_wait('mkdir -p ' + S.getValue('OVS_DPDK_SHARE'))
+ self.execute_and_wait('mount -o iocharset=utf8 /dev/sdb1 ' +
+ S.getValue('OVS_DPDK_SHARE'))
+ self.execute_and_wait('mkdir -p ' + S.getValue('GUEST_OVS_DPDK_DIR'))
+ self.execute_and_wait('cp -a ' + S.getValue('OVS_DPDK_SHARE') + '/* ' +
+ S.getValue('GUEST_OVS_DPDK_DIR'))
+ # Get VM info
+ self.execute_and_wait('cat /etc/default/grub')
+
+ # Disable services (F16)
+ self.execute_and_wait('systemctl status iptables.service')
+ self.execute_and_wait('systemctl stop iptables.service')
+
+ # build and configure system for dpdk
+ self.execute_and_wait('cd ' + S.getValue('GUEST_OVS_DPDK_DIR') +
+ '/DPDK')
+ self.execute_and_wait('export CC=gcc')
+ self.execute_and_wait('export RTE_SDK=' +
+ S.getValue('GUEST_OVS_DPDK_DIR') + '/DPDK')
+ self.execute_and_wait('export RTE_TARGET=%s' % S.getValue('RTE_TARGET'))
+
+ # modify makefile if needed
+ self._modify_dpdk_makefile()
+
+ self.execute_and_wait('make RTE_OUTPUT=$RTE_SDK/$RTE_TARGET -C '
+ '$RTE_SDK/lib/librte_eal/linuxapp/igb_uio')
+ self.execute_and_wait('modprobe uio')
+ self.execute_and_wait('insmod %s/kmod/igb_uio.ko' %
+ S.getValue('RTE_TARGET'))
+ self.execute_and_wait('./tools/dpdk_nic_bind.py --status')
+ self.execute_and_wait(
+ './tools/dpdk_nic_bind.py -b igb_uio' ' ' +
+ S.getValue('GUEST_NET1_PCI_ADDRESS')[self._number] + ' ' +
+ S.getValue('GUEST_NET2_PCI_ADDRESS')[self._number])
+
+ # build and run 'test-pmd'
+ self.execute_and_wait('cd ' + S.getValue('GUEST_OVS_DPDK_DIR') +
+ '/DPDK/app/test-pmd')
+ self.execute_and_wait('make clean')
+ self.execute_and_wait('make')
+ self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
+ ' --burst=64 -i --txqflags=0xf00 ' +
+ '--disable-hw-vlan', 60, "Done")
+ self.execute('set fwd mac_retry', 1)
+ self.execute_and_wait('start', 20,
+ 'TX RS bit threshold=0 - TXQ flags=0xf00')
diff --git a/vnfs/qemu/qemu_dpdk_vhost_cuse.py b/vnfs/qemu/qemu_dpdk_vhost_cuse.py
index 43b732fc..e5351813 100644
--- a/vnfs/qemu/qemu_dpdk_vhost_cuse.py
+++ b/vnfs/qemu/qemu_dpdk_vhost_cuse.py
@@ -15,374 +15,54 @@
"""Automation of QEMU hypervisor for launching vhost-cuse enabled guests.
"""
-import os
-import time
import logging
-import locale
-import re
-import subprocess
-from tools import tasks
-from conf import settings
-from vnfs.vnf.vnf import IVnf
+from conf import settings as S
+from vnfs.qemu.qemu_dpdk import IVnfQemuDpdk
-_QEMU_BIN = settings.getValue('QEMU_BIN')
-_RTE_TARGET = settings.getValue('RTE_TARGET')
-
-_GUEST_MEMORY = settings.getValue('GUEST_MEMORY')
-_GUEST_SMP = settings.getValue('GUEST_SMP')
-_GUEST_CORE_BINDING = settings.getValue('GUEST_CORE_BINDING')
-_QEMU_CORE = settings.getValue('QEMU_CORE')
-
-_GUEST_IMAGE = settings.getValue('GUEST_IMAGE')
-_GUEST_SHARE_DIR = settings.getValue('GUEST_SHARE_DIR')
-
-_GUEST_USERNAME = settings.getValue('GUEST_USERNAME')
-_GUEST_PASSWORD = settings.getValue('GUEST_PASSWORD')
-
-_GUEST_PROMPT_LOGIN = settings.getValue('GUEST_PROMPT_LOGIN')
-_GUEST_PROMPT_PASSWORD = settings.getValue('GUEST_PROMPT_PASSWORD')
-_GUEST_PROMPT = settings.getValue('GUEST_PROMPT')
-
-_QEMU_GUEST_DPDK_PROMPT = settings.getValue('QEMU_GUEST_DPDK_PROMPT')
-_QEMU_GUEST_TEST_PMD_PROMPT = settings.getValue('QEMU_GUEST_TEST_PMD_PROMPT')
-_HUGEPAGE_DIR = settings.getValue('HUGEPAGE_DIR')
-
-_GUEST_OVS_DPDK_DIR = settings.getValue('GUEST_OVS_DPDK_DIR')
-_OVS_DPDK_SHARE = settings.getValue('OVS_DPDK_SHARE')
-
-_LOG_FILE_QEMU = os.path.join(
- settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_QEMU'))
-_LOG_FILE_GUEST_CMDS = os.path.join(
- settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_GUEST_CMDS'))
-
-_OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR')
-_GUEST_NET1_MAC = settings.getValue('GUEST_NET1_MAC')
-_GUEST_NET2_MAC = settings.getValue('GUEST_NET2_MAC')
-_GUEST_NET1_PCI_ADDRESS = settings.getValue('GUEST_NET1_PCI_ADDRESS')
-_GUEST_NET2_PCI_ADDRESS = settings.getValue('GUEST_NET2_PCI_ADDRESS')
-
-class QemuDpdkVhostCuse(tasks.Process, IVnf):
+class QemuDpdkVhostCuse(IVnfQemuDpdk):
"""
- Control an instance of QEMU with vHost user guest communication.
+ Control an instance of QEMU with vHost cuse guest communication.
"""
- _bin = _QEMU_BIN
- _logfile = _LOG_FILE_QEMU
- _cmd = None
- _expect = _GUEST_PROMPT_LOGIN
- _proc_name = 'qemu'
- _number_vnfs = 0
-
- class GuestCommandFilter(logging.Filter):
- """
- Filter out strings beginning with 'guestcmd :'.
- """
- def filter(self, record):
- return record.getMessage().startswith(self.prefix)
-
- def __init__(self, memory=_GUEST_MEMORY, cpus=_GUEST_SMP,
- monitor_path='/tmp', shared_path_host=_GUEST_SHARE_DIR,
- args='', timeout=120, deployment="PVP"):
+ def __init__(self):
"""
Initialisation function.
-
- :param timeout: Time to wait for login prompt. If set to
- 0 do not wait.
- :param number: Number of QEMU instance, used when multiple QEMU
- instances are started at once.
- :param args: Arguments to pass to QEMU.
-
- :returns: None
"""
+ super(QemuDpdkVhostCuse, self).__init__()
self._logger = logging.getLogger(__name__)
- self._number = self._number_vnfs
- self._number_vnfs = self._number_vnfs + 1
- self._logfile = self._logfile + str(self._number)
- self._log_prefix = 'guest_%d_cmd : ' % self._number
- self._timeout = timeout
- self._monitor = '%s/vm%dmonitor' % (monitor_path, self._number)
- name = 'Client%d' % self._number
- vnc = ':%d' % self._number
- self._cmd = ['sudo', '-E', 'taskset ' + str(_QEMU_CORE), self._bin,
- '-m', str(memory), '-smp', str(cpus), '-cpu', 'host',
- '-drive', 'if=scsi,file='+_GUEST_IMAGE,
- '-drive',
- 'if=scsi,file=fat:rw:%s,snapshot=off' % shared_path_host,
- '-boot', 'c', '--enable-kvm',
- '-monitor', 'unix:%s,server,nowait' % self._monitor,
- '-object',
- 'memory-backend-file,id=mem,size=' + str(memory) + 'M,' +
- 'mem-path=' + _HUGEPAGE_DIR + ',share=on',
- '-numa', 'node,memdev=mem -mem-prealloc',
- '-net', 'none', '-no-reboot',
- '-netdev',
- 'type=tap,id=net1,script=no,downscript=no,' +
- 'ifname=dpdkvhostcuse0,vhost=on',
- '-device',
- 'virtio-net-pci,netdev=net1,mac=' + _GUEST_NET1_MAC,
- '-netdev',
- 'type=tap,id=net2,script=no,downscript=no,' +
- 'ifname=dpdkvhostcuse1,vhost=on',
- '-device',
- 'virtio-net-pci,netdev=net2,mac=' + _GUEST_NET2_MAC,
- '-nographic', '-vnc', str(vnc), '-name', name,
- '-snapshot',
- ]
- self._cmd.extend(args)
- self._configure_logging()
-
- def _configure_logging(self):
- """
- Configure logging.
- """
- self.GuestCommandFilter.prefix = self._log_prefix
-
- logger = logging.getLogger()
- cmd_logger = logging.FileHandler(
- filename=_LOG_FILE_GUEST_CMDS + str(self._number))
- cmd_logger.setLevel(logging.DEBUG)
- cmd_logger.addFilter(self.GuestCommandFilter())
- logger.addHandler(cmd_logger)
-
- # startup/Shutdown
-
- def start(self):
- """
- Start QEMU instance, login and prepare for commands.
- """
- super(QemuDpdkVhostCuse, self).start()
- self._affinitize()
-
- if self._timeout:
- self._login()
- self._config_guest_loopback()
-
- def stop(self):
- """
- Kill QEMU instance if it is alive.
- """
- self._logger.info('Killing QEMU...')
-
- super(QemuDpdkVhostCuse, self).kill()
+ # calculate indexes of guest devices (e.g. charx, dpdkvhostuserx)
+ i = self._number * 2
+ if1 = str(i)
+ if2 = str(i + 1)
+ net1 = 'net' + str(i + 1)
+ net2 = 'net' + str(i + 2)
+
+ self._cmd += ['-netdev',
+ 'type=tap,id=' + net1 + ',script=no,downscript=no,' +
+ 'ifname=dpdkvhostcuse' + if1 + ',vhost=on',
+ '-device',
+ 'virtio-net-pci,mac=' +
+ S.getValue('GUEST_NET1_MAC')[self._number] +
+ ',netdev=' + net1 + ',csum=off,gso=off,' +
+ 'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+ '-netdev',
+ 'type=tap,id=' + net2 +
+ ',script=no,downscript=no,' +
+ 'ifname=dpdkvhostcuse' + if2 + ',vhost=on',
+ '-device',
+ 'virtio-net-pci,mac=' +
+ S.getValue('GUEST_NET2_MAC')[self._number] +
+ ',netdev=' + net2 + ',csum=off,gso=off,' +
+ 'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+ ]
# helper functions
- def _login(self, timeout=120):
+ def _modify_dpdk_makefile(self):
"""
- Login to QEMU instance.
-
- This can be used immediately after booting the machine, provided a
- sufficiently long ``timeout`` is given.
-
- :param timeout: Timeout to wait for login to complete.
-
- :returns: None
+ Modifies DPDK makefile in Guest before compilation
"""
- # if no timeout was set, we likely started QEMU without waiting for it
- # to boot. This being the case, we best check that it has finished
- # first.
- if not self._timeout:
- self._expect_process(timeout=timeout)
-
- self._child.sendline(_GUEST_USERNAME)
- self._child.expect(_GUEST_PROMPT_PASSWORD, timeout=5)
- self._child.sendline(_GUEST_PASSWORD)
-
- self._expect_process(_GUEST_PROMPT, timeout=5)
-
- def execute(self, cmd, delay=0):
- """
- Send ``cmd`` with no wait.
-
- Useful for asynchronous commands.
-
- :param cmd: Command to send to guest.
- :param timeout: Delay to wait after sending command before returning.
-
- :returns: None
- """
- self._logger.debug('%s%s', self._log_prefix, cmd)
- self._child.sendline(cmd)
- time.sleep(delay)
-
- def wait(self, msg=_GUEST_PROMPT, timeout=30):
- """
- Wait for ``msg``.
-
- :param msg: Message to wait for from guest.
- :param timeout: Time to wait for message.
-
- :returns: None
- """
- self._child.expect(msg, timeout=timeout)
-
- def execute_and_wait(self, cmd, timeout=30, prompt=_GUEST_PROMPT):
- """
- Send ``cmd`` and wait ``timeout`` seconds for prompt.
-
- :param cmd: Command to send to guest.
- :param timeout: Time to wait for prompt.
-
- :returns: None
- """
- self.execute(cmd)
- self.wait(prompt, timeout=timeout)
-
- def send_and_pass(self, cmd, timeout=30):
- """
- Send ``cmd`` and wait ``timeout`` seconds for it to pass.
-
- :param cmd: Command to send to guest.
- :param timeout: Time to wait for prompt before checking return code.
-
- :returns: None
- """
- self.execute(cmd)
- self.wait(_GUEST_PROMPT, timeout=timeout)
- self.execute('echo $?')
- self._child.expect('^0$', timeout=1) # expect a 0
- self.wait(_GUEST_PROMPT, timeout=timeout)
-
- def _affinitize(self):
- """
- Affinitize the SMP cores of a QEMU instance.
-
- This is a bit of a hack. The 'socat' utility is used to
- interact with the QEMU HMP. This is necessary due to the lack
- of QMP in older versions of QEMU, like v1.6.2. In future
- releases, this should be replaced with calls to libvirt or
- another Python-QEMU wrapper library.
-
- :returns: None
- """
- thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
-
- self._logger.info('Affinitizing guest...')
-
- cur_locale = locale.getlocale()[1]
- proc = subprocess.Popen(
- ('echo', 'info cpus'), stdout=subprocess.PIPE)
- output = subprocess.check_output(
- ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
- stdin=proc.stdout)
- proc.wait()
-
- for cpu in range(0, int(_GUEST_SMP)):
- match = None
- for line in output.decode(cur_locale).split('\n'):
- match = re.search(thread_id % cpu, line)
- if match:
- self._affinitize_pid(
- _GUEST_CORE_BINDING[self._number - 1][cpu],
- match.group(1))
- break
-
- if not match:
- self._logger.error('Failed to affinitize guest core #%d. Could'
- ' not parse tid.', cpu)
-
- def _config_guest_loopback(self):
- """
- Configure VM to run testpmd
-
- Configure performs the following:
- * Mount hugepages
- * mount shared directory for copying DPDK
- * Disable firewall
- * Compile DPDK
- * DPDK NIC bind
- * Run testpmd
- """
-
- # Guest images _should_ have 1024 hugepages by default,
- # but just in case:'''
- self.execute_and_wait('sysctl vm.nr_hugepages=1024')
-
- # Mount hugepages
- self.execute_and_wait('mkdir -p /dev/hugepages')
- self.execute_and_wait(
- 'mount -t hugetlbfs hugetlbfs /dev/hugepages')
-
- # mount shared directory
- self.execute_and_wait('umount ' + _OVS_DPDK_SHARE)
- self.execute_and_wait('rm -rf ' + _GUEST_OVS_DPDK_DIR)
- self.execute_and_wait('mkdir -p ' + _OVS_DPDK_SHARE)
- self.execute_and_wait('mount -o iocharset=utf8 /dev/sdb1 ' +
- _OVS_DPDK_SHARE)
- self.execute_and_wait('mkdir -p ' + _GUEST_OVS_DPDK_DIR)
- self.execute_and_wait('cp -a ' + _OVS_DPDK_SHARE + '/* ' + _GUEST_OVS_DPDK_DIR)
-
- # Disable services (F16)
- self.execute_and_wait('systemctl status iptables.service')
- self.execute_and_wait('systemctl stop iptables.service')
-
- # build and configure system for dpdk
- self.execute_and_wait('cd ' + _GUEST_OVS_DPDK_DIR + '/DPDK',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('export CC=gcc', prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('export RTE_SDK=' + _GUEST_OVS_DPDK_DIR + '/DPDK',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('export RTE_TARGET=%s' % _RTE_TARGET,
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
self.execute_and_wait("sed -i -e 's/CONFIG_RTE_LIBRTE_VHOST_USER=n/" +
- "CONFIG_RTE_LIBRTE_VHOST_USER=y/g' config/common_linuxapp",
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('make uninstall', prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('make install T=%s -j 2' % _RTE_TARGET,
- timeout=300, prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('modprobe uio', prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('insmod %s/kmod/igb_uio.ko' % _RTE_TARGET,
- prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('./tools/dpdk_nic_bind.py --status',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('./tools/dpdk_nic_bind.py -b igb_uio'
- ' ' + _GUEST_NET1_PCI_ADDRESS + ' '
- + _GUEST_NET2_PCI_ADDRESS,
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('./tools/dpdk_nic_bind.py --status',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('cd ' + _RTE_TARGET + '/build/app/test-pmd',
- prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
-
- self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
- ' --burst=64 -i --txqflags=0xf00 ' +
- '--disable-hw-vlan', 20, "Done")
- self.execute('set fwd mac_retry', 1)
- self.execute_and_wait('start', 20,
- 'TX RS bit threshold=0 - TXQ flags=0xf00')
-
-
-if __name__ == '__main__':
- import sys
-
- with QemuDpdkVhostCuse() as vm1:
- print(
- '\n\n************************\n'
- 'Basic command line suitable for ls, cd, grep and cat.\n If you'
- ' try to run Vim from here you\'re going to have a bad time.\n'
- 'For more complex tasks please use \'vncviewer :1\' to connect to'
- ' this VM\nUsername: %s Password: %s\nPress ctrl-C to quit\n'
- '************************\n' % (_GUEST_USERNAME, _GUEST_PASSWORD))
-
- if sys.argv[1]:
- with open(sys.argv[1], 'r') as file_:
- for logline in file_:
- # lines are of format:
- # guest_N_cmd : <command>
- # and we only want the <command> piece
- cmdline = logline.split(':')[1].strip()
-
- # use a no timeout since we don't know how long we
- # should wait
- vm1.send_and_wait(cmdline, timeout=-1)
-
- while True:
- USER_INPUT = input()
- vm1.send_and_wait(USER_INPUT, timeout=5)
+ "CONFIG_RTE_LIBRTE_VHOST_USER=y/g'" +
+ "config/common_linuxapp")
diff --git a/vnfs/qemu/qemu_dpdk_vhost_user.py b/vnfs/qemu/qemu_dpdk_vhost_user.py
index a3b96b18..94d87f9e 100644
--- a/vnfs/qemu/qemu_dpdk_vhost_user.py
+++ b/vnfs/qemu/qemu_dpdk_vhost_user.py
@@ -15,365 +15,52 @@
"""Automation of QEMU hypervisor for launching vhost-user enabled guests.
"""
-import os
-import time
import logging
-import locale
-import re
-import subprocess
-from tools import tasks
-from conf import settings
-from vnfs.vnf.vnf import IVnf
+from conf import settings as S
+from vnfs.qemu.qemu_dpdk import IVnfQemuDpdk
-_QEMU_BIN = settings.getValue('QEMU_BIN')
-_RTE_TARGET = settings.getValue('RTE_TARGET')
-
-GUEST_MEMORY = '4096M'
-GUEST_SMP = '2'
-GUEST_CORE_BINDING = [(4, 5), (6, 7), (9, 10)]
-
-GUEST_IMAGE = settings.getValue('GUEST_IMAGE')
-GUEST_SHARE_DIR = settings.getValue('GUEST_SHARE_DIR')
-
-GUEST_USERNAME = settings.getValue('GUEST_USERNAME')
-GUEST_PASSWORD = settings.getValue('GUEST_PASSWORD')
-
-GUEST_PROMPT_LOGIN = settings.getValue('GUEST_PROMPT_LOGIN')
-GUEST_PROMPT_PASSWORD = settings.getValue('GUEST_PROMPT_PASSWORD')
-GUEST_PROMPT = settings.getValue('GUEST_PROMPT')
-
-_QEMU_GUEST_DPDK_PROMPT = settings.getValue('QEMU_GUEST_DPDK_PROMPT')
-_QEMU_GUEST_TEST_PMD_PROMPT = settings.getValue('QEMU_GUEST_TEST_PMD_PROMPT')
-_HUGEPAGE_DIR = settings.getValue('HUGEPAGE_DIR')
-
-_GUEST_OVS_DPDK_DIR = '/root/ovs_dpdk'
-_OVS_DPDK_SHARE = '/mnt/ovs_dpdk_share'
-
-LOG_FILE_QEMU = os.path.join(
- settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_QEMU'))
-LOG_FILE_GUEST_CMDS = os.path.join(
- settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_GUEST_CMDS'))
-
-VHOST_DEV_PATH = os.path.join('/dev', settings.getValue('VHOST_DEV_FILE'))
-
-_OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR')
-_GUEST_NET1_MAC = settings.getValue('GUEST_NET1_MAC')
-_GUEST_NET2_MAC = settings.getValue('GUEST_NET2_MAC')
-_GUEST_NET1_PCI_ADDRESS = settings.getValue('GUEST_NET1_PCI_ADDRESS')
-_GUEST_NET2_PCI_ADDRESS = settings.getValue('GUEST_NET2_PCI_ADDRESS')
-
-class QemuDpdkVhost(tasks.Process, IVnf):
+class QemuDpdkVhostUser(IVnfQemuDpdk):
"""
Control an instance of QEMU with vHost user guest communication.
"""
- _bin = _QEMU_BIN
- _logfile = LOG_FILE_QEMU
- _cmd = None
- _expect = GUEST_PROMPT_LOGIN
- _proc_name = 'qemu'
- _number_vnfs = 0
-
- class GuestCommandFilter(logging.Filter):
- """
- Filter out strings beginning with 'guestcmd :'.
- """
- def filter(self, record):
- return record.getMessage().startswith(self.prefix)
-
- def __init__(self, memory=GUEST_MEMORY, cpus=GUEST_SMP,
- monitor_path='/tmp', shared_path_host=GUEST_SHARE_DIR,
- args='', timeout=120, deployment="P2P"):
+ def __init__(self):
"""
Initialisation function.
-
- :param timeout: Time to wait for login prompt. If set to
- 0 do not wait.
- :param number: Number of QEMU instance, used when multiple QEMU
- instances are started at once.
- :param args: Arguments to pass to QEMU.
-
- :returns: None
"""
+ super(QemuDpdkVhostUser, self).__init__()
self._logger = logging.getLogger(__name__)
- self._number = self._number_vnfs
- self._number_vnfs = self._number_vnfs + 1
- self._logfile = self._logfile + str(self._number)
- self._log_prefix = 'guest_%d_cmd : ' % self._number
- self._timeout = timeout
- self._monitor = '%s/vm%dmonitor' % (monitor_path, self._number)
-
- name = 'Client%d' % self._number
- vnc = ':%d' % self._number
- self._cmd = ['sudo', '-E', self._bin, '-m', str(memory),
- '-smp', str(cpus), '-cpu', 'host',
- '-drive', 'if=scsi,file='+GUEST_IMAGE,
- '-drive',
- 'if=scsi,file=fat:rw:%s,snapshot=off' % shared_path_host,
- '-boot', 'c', '--enable-kvm', '-pidfile', '/tmp/vm1.pid',
- '-monitor', 'unix:%s,server,nowait' % self._monitor,
- '-object',
- 'memory-backend-file,id=mem,size=4096M,' +
- 'mem-path=' + _HUGEPAGE_DIR + ',share=on',
- '-numa', 'node,memdev=mem -mem-prealloc',
- '-chardev',
- 'socket,id=char0,path=' + _OVS_VAR_DIR + 'dpdkvhostuser0',
- '-chardev',
- 'socket,id=char1,path=' + _OVS_VAR_DIR + 'dpdkvhostuser1',
- '-netdev',
- 'type=vhost-user,id=net1,chardev=char0,vhostforce',
- '-device',
- 'virtio-net-pci,mac=' + _GUEST_NET1_MAC +
- ',netdev=net1,csum=off,gso=off,' +
- 'guest_tso4=off,guest_tso6=off,guest_ecn=off',
- '-netdev',
- 'type=vhost-user,id=net2,chardev=char1,vhostforce',
- '-device',
- 'virtio-net-pci,mac=' + _GUEST_NET2_MAC +
- ',netdev=net2,csum=off,gso=off,' +
- 'guest_tso4=off,guest_tso6=off,guest_ecn=off',
- '-nographic', '-vnc', str(vnc), '-name', name,
- '-snapshot',
- ]
- self._cmd.extend(args)
- self._configure_logging()
-
- def _configure_logging(self):
- """
- Configure logging.
- """
- self.GuestCommandFilter.prefix = self._log_prefix
-
- logger = logging.getLogger()
- cmd_logger = logging.FileHandler(
- filename=LOG_FILE_GUEST_CMDS + str(self._number))
- cmd_logger.setLevel(logging.DEBUG)
- cmd_logger.addFilter(self.GuestCommandFilter())
- logger.addHandler(cmd_logger)
-
- # startup/Shutdown
-
- def start(self):
- """
- Start QEMU instance, login and prepare for commands.
- """
- super(QemuDpdkVhost, self).start()
- self._affinitize()
-
- if self._timeout:
- self._login()
- self._config_guest_loopback()
-
- def stop(self):
- """
- Kill QEMU instance if it is alive.
- """
- self._logger.info('Killing QEMU...')
-
- super(QemuDpdkVhost, self).kill()
-
- # helper functions
-
- def _login(self, timeout=120):
- """
- Login to QEMU instance.
-
- This can be used immediately after booting the machine, provided a
- sufficiently long ``timeout`` is given.
-
- :param timeout: Timeout to wait for login to complete.
-
- :returns: None
- """
- # if no timeout was set, we likely started QEMU without waiting for it
- # to boot. This being the case, we best check that it has finished
- # first.
- if not self._timeout:
- self._expect_process(timeout=timeout)
-
- self._child.sendline(GUEST_USERNAME)
- self._child.expect(GUEST_PROMPT_PASSWORD, timeout=5)
- self._child.sendline(GUEST_PASSWORD)
-
- self._expect_process(GUEST_PROMPT, timeout=5)
-
- def execute(self, cmd, delay=0):
- """
- Send ``cmd`` with no wait.
-
- Useful for asynchronous commands.
-
- :param cmd: Command to send to guest.
- :param timeout: Delay to wait after sending command before returning.
-
- :returns: None
- """
- self._logger.debug('%s%s', self._log_prefix, cmd)
- self._child.sendline(cmd)
- time.sleep(delay)
-
- def wait(self, msg=GUEST_PROMPT, timeout=30):
- """
- Wait for ``msg``.
-
- :param msg: Message to wait for from guest.
- :param timeout: Time to wait for message.
-
- :returns: None
- """
- self._child.expect(msg, timeout=timeout)
-
- def execute_and_wait(self, cmd, timeout=30, prompt=GUEST_PROMPT):
- """
- Send ``cmd`` and wait ``timeout`` seconds for prompt.
-
- :param cmd: Command to send to guest.
- :param timeout: Time to wait for prompt.
-
- :returns: None
- """
- self.execute(cmd)
- self.wait(prompt, timeout=timeout)
-
- def send_and_pass(self, cmd, timeout=30):
- """
- Send ``cmd`` and wait ``timeout`` seconds for it to pass.
-
- :param cmd: Command to send to guest.
- :param timeout: Time to wait for prompt before checking return code.
-
- :returns: None
- """
- self.execute(cmd)
- self.wait(GUEST_PROMPT, timeout=timeout)
- self.execute('echo $?')
- self._child.expect('^0$', timeout=1) # expect a 0
- self.wait(GUEST_PROMPT, timeout=timeout)
-
- def _affinitize(self):
- """
- Affinitize the SMP cores of a QEMU instance.
-
- This is a bit of a hack. The 'socat' utility is used to
- interact with the QEMU HMP. This is necessary due to the lack
- of QMP in older versions of QEMU, like v1.6.2. In future
- releases, this should be replaced with calls to libvirt or
- another Python-QEMU wrapper library.
-
- :returns: None
- """
- thread_id = (r'.* CPU #%d: .* thread_id=(\d+)')
-
- self._logger.info('Affinitizing guest...')
-
- cur_locale = locale.getlocale()[1]
- proc = subprocess.Popen(
- ('echo', 'info cpus'), stdout=subprocess.PIPE)
- output = subprocess.check_output(
- ('sudo', 'socat', '-', 'UNIX-CONNECT:%s' % self._monitor),
- stdin=proc.stdout)
- proc.wait()
-
- for cpu in range(0, int(GUEST_SMP)):
- match = None
- for line in output.decode(cur_locale).split('\n'):
- match = re.search(thread_id % cpu, line)
- if match:
- self._affinitize_pid(
- GUEST_CORE_BINDING[self._number - 1][cpu],
- match.group(1))
- break
-
- if not match:
- self._logger.error('Failed to affinitize guest core #%d. Could'
- ' not parse tid.', cpu)
-
- def _config_guest_loopback(self):
- '''# mount hugepages
- # Guest images _should_ have 1024 hugepages by default,
- # but just in case:'''
- self.execute_and_wait('sysctl vm.nr_hugepages=1024')
- self.execute_and_wait('mkdir -p /dev/hugepages')
- self.execute_and_wait(
- 'mount -t hugetlbfs hugetlbfs /dev/hugepages')
-
- # mount shared directory
- self.execute_and_wait('umount ' + _OVS_DPDK_SHARE)
- self.execute_and_wait('rm -rf ' + _GUEST_OVS_DPDK_DIR)
- self.execute_and_wait('mkdir -p ' + _OVS_DPDK_SHARE)
- self.execute_and_wait('mount -o iocharset=utf8 /dev/sdb1 ' +
- _OVS_DPDK_SHARE)
- self.execute_and_wait('mkdir -p ' + _GUEST_OVS_DPDK_DIR)
- self.execute_and_wait('cp -a ' + _OVS_DPDK_SHARE + '/* ' + _GUEST_OVS_DPDK_DIR)
- # Get VM info
- self.execute_and_wait('cat /etc/default/grub')
-
- # Disable services (F16)
- self.execute_and_wait('systemctl status iptables.service')
- self.execute_and_wait('systemctl stop iptables.service')
-
- # build and configure system for dpdk
- self.execute_and_wait('cd ' + _GUEST_OVS_DPDK_DIR + '/DPDK',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('export CC=gcc', prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('export RTE_SDK=' + _GUEST_OVS_DPDK_DIR + '/DPDK',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('export RTE_TARGET=%s' % _RTE_TARGET,
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('make uninstall', prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('make install T=%s -j 2' % _RTE_TARGET,
- timeout=300, prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- self.execute_and_wait('modprobe uio', prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('insmod %s/kmod/igb_uio.ko' % _RTE_TARGET,
- prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('./tools/dpdk_nic_bind.py --status',
- prompt=_QEMU_GUEST_DPDK_PROMPT)
- self.execute_and_wait('./tools/dpdk_nic_bind.py -b igb_uio'
- ' ' + _GUEST_NET1_PCI_ADDRESS + ' '
- + _GUEST_NET2_PCI_ADDRESS,
- prompt=_QEMU_GUEST_DPDK_PROMPT)
-
- # build and run 'test-pmd'
- self.execute_and_wait('cd ' + _GUEST_OVS_DPDK_DIR +
- '/DPDK/app/test-pmd',
- prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
- self.execute_and_wait('make clean', prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
- self.execute_and_wait('make', prompt=_QEMU_GUEST_TEST_PMD_PROMPT)
- self.execute_and_wait('./testpmd -c 0x3 -n 4 --socket-mem 512 --'
- ' --burst=64 -i --txqflags=0xf00 ' +
- '--disable-hw-vlan', 20, "Done")
- self.execute('set fwd mac_retry', 1)
- self.execute_and_wait('start', 20,
- 'TX RS bit threshold=0 - TXQ flags=0xf00')
-
-
-if __name__ == '__main__':
- import sys
-
- with QemuDpdkVhost() as vm1:
- print(
- '\n\n************************\n'
- 'Basic command line suitable for ls, cd, grep and cat.\n If you'
- ' try to run Vim from here you\'re going to have a bad time.\n'
- 'For more complex tasks please use \'vncviewer :1\' to connect to'
- ' this VM\nUsername: %s Password: %s\nPress ctrl-C to quit\n'
- '************************\n' % (GUEST_USERNAME, GUEST_PASSWORD))
-
- if sys.argv[1]:
- with open(sys.argv[1], 'r') as file_:
- for logline in file_:
- # lines are of format:
- # guest_N_cmd : <command>
- # and we only want the <command> piece
- cmdline = logline.split(':')[1].strip()
- # use a no timeout since we don't know how long we
- # should wait
- vm1.send_and_wait(cmdline, timeout=-1)
+ # calculate indexes of guest devices (e.g. charx, dpdkvhostuserx)
+ i = self._number * 2
+ if1 = str(i)
+ if2 = str(i + 1)
+ net1 = 'net' + str(i + 1)
+ net2 = 'net' + str(i + 2)
+
+ self._cmd += ['-chardev',
+ 'socket,id=char' + if1 +
+ ',path=' + S.getValue('OVS_VAR_DIR') +
+ 'dpdkvhostuser' + if1,
+ '-chardev',
+ 'socket,id=char' + if2 +
+ ',path=' + S.getValue('OVS_VAR_DIR') +
+ 'dpdkvhostuser' + if2,
+ '-netdev',
+ 'type=vhost-user,id=' + net1 +
+ ',chardev=char' + if1 + ',vhostforce',
+ '-device',
+ 'virtio-net-pci,mac=' +
+ S.getValue('GUEST_NET1_MAC')[self._number] +
+ ',netdev=' + net1 + ',csum=off,gso=off,' +
+ 'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+ '-netdev',
+ 'type=vhost-user,id=' + net2 +
+ ',chardev=char' + if2 + ',vhostforce',
+ '-device',
+ 'virtio-net-pci,mac=' +
+ S.getValue('GUEST_NET2_MAC')[self._number] +
+ ',netdev=' + net2 + ',csum=off,gso=off,' +
+ 'guest_tso4=off,guest_tso6=off,guest_ecn=off',
+ ]
- while True:
- USER_INPUT = input()
- vm1.send_and_wait(USER_INPUT, timeout=5)