1 # Copyright (c) 2019 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """QEMU utilities library."""
16 from time import sleep
17 from string import Template
20 # Disable due to pylint bug
21 # pylint: disable=no-name-in-module,import-error
22 from distutils.version import StrictVersion
24 from robot.api import logger
25 from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
26 from resources.libraries.python.Constants import Constants
27 from resources.libraries.python.DpdkUtil import DpdkUtil
28 from resources.libraries.python.DUTSetup import DUTSetup
29 from resources.libraries.python.topology import NodeType, Topology
30 from resources.libraries.python.VppConfigGenerator import VppConfigGenerator
31 from resources.libraries.python.VPPUtil import VPPUtil
33 __all__ = ["QemuOptions", "QemuUtils"]
36 class QemuOptions(object):
39 The class can handle input parameters that acts as QEMU command line
40 parameters. The only variable is a list of dictionaries where dictionaries
41 can be added multiple times. This emulates the QEMU behavior where one
42 command line parameter can be used multiple times (1..N). Example can be
43 device or object (so it is not an issue to have one memory
44 block of 2G and and second memory block of 512M but from other numa).
46 Class does support get value or string representation that will return
47 space separated, dash prefixed string of key value pairs used for command
51 # Use one instance of class per tests.
52 ROBOT_LIBRARY_SCOPE = 'TEST CASE'
55 self.variables = list()
57 def add(self, variable, value):
58 """Add parameter to the list.
60 :param variable: QEMU parameter name (without dash).
61 :param value: Paired value.
63 :type value: str or int
65 self.variables.append({str(variable): value})
68 """Return space separated string of key value pairs.
70 The format is suitable to be pasted to qemu command line.
72 :returns: Space separated string of key value pairs.
75 return " ".join(["-{k} {v}".format(k=d.keys()[0], v=d.values()[0])
76 for d in self.variables])
79 class QemuUtils(object):
82 # Use one instance of class per tests.
83 ROBOT_LIBRARY_SCOPE = 'TEST CASE'
85 def __init__(self, node, qemu_id=1, smp=1, mem=512, vnf=None,
86 img='/var/lib/vm/vhost-nested.img', bin_path='/usr/bin'):
87 """Initialize QemuUtil class.
89 :param node: Node to run QEMU on.
90 :param qemu_id: QEMU identifier.
91 :param smp: Number of virtual SMP units (cores).
92 :param mem: Amount of memory.
93 :param vnf: Network function workload.
94 :param img: QEMU disk image or kernel image path.
95 :param bin_path: QEMU binary path.
107 'host': node['host'],
109 'port': 10021 + qemu_id,
110 'serial': 4555 + qemu_id,
115 if node['port'] != 22:
116 self._vm_info['host_port'] = node['port']
117 self._vm_info['host_username'] = node['username']
118 self._vm_info['host_password'] = node['password']
121 self._opt['qemu_id'] = qemu_id
122 self._opt['bin_path'] = bin_path
123 self._opt['mem'] = int(mem)
124 self._opt['smp'] = int(smp)
125 self._opt['img'] = img
126 self._opt['vnf'] = vnf
129 self._temp['pidfile'] = '/var/run/qemu_{id}.pid'.format(id=qemu_id)
130 if '/var/lib/vm/' in img:
131 self._opt['vm_type'] = 'nestedvm'
132 self._temp['qmp'] = '/var/run/qmp_{id}.sock'.format(id=qemu_id)
133 self._temp['qga'] = '/var/run/qga_{id}.sock'.format(id=qemu_id)
134 elif '/opt/boot/vmlinuz' in img:
135 self._opt['vm_type'] = 'kernelvm'
136 self._temp['log'] = '/tmp/serial_{id}.log'.format(id=qemu_id)
137 self._temp['ini'] = '/etc/vm_init_{id}.conf'.format(id=qemu_id)
139 raise RuntimeError('QEMU: Unknown VM image option!')
140 # Computed parameters for QEMU command line.
141 self._params = QemuOptions()
144 def add_params(self):
145 """Set QEMU command line parameters."""
146 self.add_default_params()
147 if self._opt.get('vm_type', '') == 'nestedvm':
148 self.add_nestedvm_params()
149 elif self._opt.get('vm_type', '') == 'kernelvm':
150 self.add_kernelvm_params()
152 raise RuntimeError('QEMU: Unsupported VM type!')
154 def add_default_params(self):
155 """Set default QEMU command line parameters."""
156 self._params.add('daemonize', '')
157 self._params.add('nodefaults', '')
158 self._params.add('name', 'vnf{qemu},debug-threads=on'.
159 format(qemu=self._opt.get('qemu_id')))
160 self._params.add('no-user-config', '')
161 self._params.add('monitor', 'none')
162 self._params.add('display', 'none')
163 self._params.add('vga', 'none')
164 self._params.add('enable-kvm', '')
165 self._params.add('pidfile', '{pidfile}'.
166 format(pidfile=self._temp.get('pidfile')))
167 self._params.add('cpu', 'host')
168 self._params.add('machine', 'pc,accel=kvm,usb=off,mem-merge=off')
169 self._params.add('smp', '{smp},sockets=1,cores={smp},threads=1'.
170 format(smp=self._opt.get('smp')))
171 self._params.add('object',
172 'memory-backend-file,id=mem,size={mem}M,'
173 'mem-path=/dev/hugepages,share=on'.
174 format(mem=self._opt.get('mem')))
175 self._params.add('m', '{mem}M'.
176 format(mem=self._opt.get('mem')))
177 self._params.add('numa', 'node,memdev=mem')
178 self._params.add('balloon', 'none')
180 def add_nestedvm_params(self):
181 """Set NestedVM QEMU parameters."""
182 self._params.add('net', 'nic,macaddr=52:54:00:00:{qemu:02x}:ff'.
183 format(qemu=self._opt.get('qemu_id')))
184 self._params.add('net', 'user,hostfwd=tcp::{info[port]}-:22'.
185 format(info=self._vm_info))
186 # TODO: Remove try except after fully migrated to Bionic or
187 # qemu_set_node is removed.
189 locking = ',file.locking=off'\
190 if self.qemu_version(version='2.10') else ''
191 except AttributeError:
193 self._params.add('drive',
194 'file={img},format=raw,cache=none,if=virtio{locking}'.
195 format(img=self._opt.get('img'), locking=locking))
196 self._params.add('qmp', 'unix:{qmp},server,nowait'.
197 format(qmp=self._temp.get('qmp')))
198 self._params.add('chardev', 'socket,host=127.0.0.1,port={info[serial]},'
199 'id=gnc0,server,nowait'.format(info=self._vm_info))
200 self._params.add('device', 'isa-serial,chardev=gnc0')
201 self._params.add('chardev',
202 'socket,path={qga},server,nowait,id=qga0'.
203 format(qga=self._temp.get('qga')))
204 self._params.add('device', 'isa-serial,chardev=qga0')
206 def add_kernelvm_params(self):
207 """Set KernelVM QEMU parameters."""
208 self._params.add('chardev', 'file,id=char0,path={log}'.
209 format(log=self._temp.get('log')))
210 self._params.add('device', 'isa-serial,chardev=char0')
211 self._params.add('fsdev', 'local,id=root9p,path=/,security_model=none')
212 self._params.add('device',
213 'virtio-9p-pci,fsdev=root9p,mount_tag=/dev/root')
214 self._params.add('kernel', '$(readlink -m {img}* | tail -1)'.
215 format(img=self._opt.get('img')))
216 self._params.add('append',
217 '"ro rootfstype=9p rootflags=trans=virtio '
218 'console=ttyS0 tsc=reliable hugepages=256 '
219 'init={init}"'.format(init=self._temp.get('ini')))
221 def create_kernelvm_config_vpp(self, **kwargs):
222 """Create QEMU VPP config files.
224 :param kwargs: Key-value pairs to replace content of VPP configuration
228 startup = ('/etc/vpp/vm_startup_{id}.conf'.
229 format(id=self._opt.get('qemu_id')))
230 running = ('/etc/vpp/vm_running_{id}.exec'.
231 format(id=self._opt.get('qemu_id')))
233 self._temp['startup'] = startup
234 self._temp['running'] = running
235 self._opt['vnf_bin'] = ('/usr/bin/vpp -c {startup}'.
236 format(startup=startup))
238 # Create VPP startup configuration.
239 vpp_config = VppConfigGenerator()
240 vpp_config.set_node(self._node)
241 vpp_config.add_unix_nodaemon()
242 vpp_config.add_unix_cli_listen()
243 vpp_config.add_unix_exec(running)
244 vpp_config.add_cpu_main_core('0')
245 vpp_config.add_cpu_corelist_workers('1-{smp}'.
246 format(smp=self._opt.get('smp')-1))
247 vpp_config.add_dpdk_dev('0000:00:06.0', '0000:00:07.0')
248 vpp_config.add_dpdk_log_level('debug')
249 if not kwargs['jumbo_frames']:
250 vpp_config.add_dpdk_no_multi_seg()
251 vpp_config.add_dpdk_no_tx_checksum_offload()
252 vpp_config.add_plugin('disable', 'default')
253 vpp_config.add_plugin('enable', 'dpdk_plugin.so')
254 vpp_config.apply_config(startup, restart_vpp=False)
256 # Create VPP running configuration.
257 template = '{res}/{tpl}.exec'.format(res=Constants.RESOURCES_TPL_VM,
258 tpl=self._opt.get('vnf'))
259 exec_cmd_no_error(self._node, 'rm -f {running}'.format(running=running),
262 with open(template, 'r') as src_file:
263 src = Template(src_file.read())
264 exec_cmd_no_error(self._node, "echo '{out}' | sudo tee {running}".
265 format(out=src.safe_substitute(**kwargs),
268 def create_kernelvm_config_testpmd_io(self, **kwargs):
269 """Create QEMU testpmd-io command line.
271 :param kwargs: Key-value pairs to construct command line parameters.
274 testpmd_path = ('{path}/{arch}-native-linuxapp-gcc/app'.
275 format(path=Constants.QEMU_PERF_VM_DPDK,
276 arch=Topology.get_node_arch(self._node)))
277 testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
278 eal_corelist='0-{smp}'.format(smp=self._opt.get('smp') - 1),
282 pmd_rxq=kwargs['queues'],
283 pmd_txq=kwargs['queues'],
284 pmd_tx_offloads=False,
285 pmd_disable_hw_vlan=False,
286 pmd_max_pkt_len=9200 if kwargs['jumbo_frames'] else None,
287 pmd_nb_cores=str(self._opt.get('smp') - 1))
289 self._opt['vnf_bin'] = ('{testpmd_path}/{testpmd_cmd}'.
290 format(testpmd_path=testpmd_path,
291 testpmd_cmd=testpmd_cmd))
293 def create_kernelvm_config_testpmd_mac(self, **kwargs):
294 """Create QEMU testpmd-mac command line.
296 :param kwargs: Key-value pairs to construct command line parameters.
299 testpmd_path = ('{path}/{arch}-native-linuxapp-gcc/app'.
300 format(path=Constants.QEMU_PERF_VM_DPDK,
301 arch=Topology.get_node_arch(self._node)))
302 testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
303 eal_corelist='0-{smp}'.format(smp=self._opt.get('smp') - 1),
308 pmd_eth_peer_0='0,{mac}'.format(mac=kwargs['vif1_mac']),
309 pmd_eth_peer_1='1,{mac}'.format(mac=kwargs['vif2_mac']),
310 pmd_rxq=kwargs['queues'],
311 pmd_txq=kwargs['queues'],
312 pmd_tx_offloads=False,
313 pmd_disable_hw_vlan=False,
314 pmd_max_pkt_len=9200 if kwargs['jumbo_frames'] else None,
315 pmd_nb_cores=str(self._opt.get('smp') - 1))
317 self._opt['vnf_bin'] = ('{testpmd_path}/{testpmd_cmd}'.
318 format(testpmd_path=testpmd_path,
319 testpmd_cmd=testpmd_cmd))
321 def create_kernelvm_init(self, **kwargs):
322 """Create QEMU init script.
324 :param kwargs: Key-value pairs to replace content of init startup file.
327 template = '{res}/init.sh'.format(res=Constants.RESOURCES_TPL_VM)
328 init = self._temp.get('ini')
329 exec_cmd_no_error(self._node, 'rm -f {init}'.format(init=init),
332 with open(template, 'r') as src_file:
333 src = Template(src_file.read())
334 exec_cmd_no_error(self._node, "echo '{out}' | sudo tee {init}".
335 format(out=src.safe_substitute(**kwargs),
337 exec_cmd_no_error(self._node, "chmod +x {init}".
338 format(init=init), sudo=True)
340 def configure_kernelvm_vnf(self, **kwargs):
341 """Create KernelVM VNF configurations.
343 :param kwargs: Key-value pairs for templating configs.
346 if 'vpp' in self._opt.get('vnf'):
347 self.create_kernelvm_config_vpp(**kwargs)
348 elif 'testpmd_io' in self._opt.get('vnf'):
349 self.create_kernelvm_config_testpmd_io(**kwargs)
350 elif 'testpmd_mac' in self._opt.get('vnf'):
351 self.create_kernelvm_config_testpmd_mac(**kwargs)
353 raise RuntimeError('QEMU: Unsupported VNF!')
354 self.create_kernelvm_init(vnf_bin=self._opt.get('vnf_bin'))
356 def get_qemu_pids(self):
357 """Get QEMU CPU pids.
359 :returns: List of QEMU CPU pids.
362 command = ("grep -rwl 'CPU' /proc/$(sudo cat {pidfile})/task/*/comm ".
363 format(pidfile=self._temp.get('pidfile')))
364 command += (r"| xargs dirname | sed -e 's/\/.*\///g' | uniq")
366 stdout, _ = exec_cmd_no_error(self._node, command)
367 return stdout.splitlines()
369 def qemu_set_affinity(self, *host_cpus):
370 """Set qemu affinity by getting thread PIDs via QMP and taskset to list
371 of CPU cores. Function tries to execute 3 times to avoid race condition
372 in getting thread PIDs.
374 :param host_cpus: List of CPU cores.
375 :type host_cpus: list
379 qemu_cpus = self.get_qemu_pids()
381 if len(qemu_cpus) != len(host_cpus):
384 for qemu_cpu, host_cpu in zip(qemu_cpus, host_cpus):
385 command = ('taskset -pc {host_cpu} {thread}'.
386 format(host_cpu=host_cpu, thread=qemu_cpu))
387 message = ('QEMU: Set affinity failed on {host}!'.
388 format(host=self._node['host']))
389 exec_cmd_no_error(self._node, command, sudo=True,
392 except (RuntimeError, ValueError):
397 raise RuntimeError('Failed to set Qemu threads affinity!')
399 def qemu_set_scheduler_policy(self):
400 """Set scheduler policy to SCHED_RR with priority 1 for all Qemu CPU
403 :raises RuntimeError: Set scheduler policy failed.
406 qemu_cpus = self.get_qemu_pids()
408 for qemu_cpu in qemu_cpus:
409 command = ('chrt -r -p 1 {thread}'.
410 format(thread=qemu_cpu))
411 message = ('QEMU: Set SCHED_RR failed on {host}'.
412 format(host=self._node['host']))
413 exec_cmd_no_error(self._node, command, sudo=True,
415 except (RuntimeError, ValueError):
419 def qemu_add_vhost_user_if(self, socket, server=True, jumbo_frames=False,
420 queue_size=None, queues=1):
421 """Add Vhost-user interface.
423 :param socket: Path of the unix socket.
424 :param server: If True the socket shall be a listening socket.
425 :param jumbo_frames: Set True if jumbo frames are used in the test.
426 :param queue_size: Vring queue size.
427 :param queues: Number of queues.
430 :type jumbo_frames: bool
431 :type queue_size: int
435 self._params.add('chardev',
436 'socket,id=char{vhost},path={socket}{server}'.
437 format(vhost=self._vhost_id, socket=socket,
438 server=',server' if server is True else ''))
439 self._params.add('netdev',
440 'vhost-user,id=vhost{vhost},'
441 'chardev=char{vhost},queues={queues}'.
442 format(vhost=self._vhost_id, queues=queues))
443 mac = ('52:54:00:00:{qemu:02x}:{vhost:02x}'.
444 format(qemu=self._opt.get('qemu_id'), vhost=self._vhost_id))
445 queue_size = ('rx_queue_size={queue_size},tx_queue_size={queue_size}'.
446 format(queue_size=queue_size)) if queue_size else ''
447 mbuf = 'on,host_mtu=9200'
448 self._params.add('device',
449 'virtio-net-pci,netdev=vhost{vhost},'
450 'mac={mac},bus=pci.0,addr={addr}.0,mq=on,'
451 'vectors={vectors},csum=off,gso=off,'
452 'guest_tso4=off,guest_tso6=off,guest_ecn=off,'
453 'mrg_rxbuf={mbuf},{queue_size}'.
454 format(addr=self._vhost_id+5,
455 vhost=self._vhost_id, mac=mac,
456 mbuf=mbuf if jumbo_frames else 'off',
457 queue_size=queue_size,
458 vectors=(2 * queues + 2)))
460 # Add interface MAC and socket to the node dict.
461 if_data = {'mac_address': mac, 'socket': socket}
462 if_name = 'vhost{vhost}'.format(vhost=self._vhost_id)
463 self._vm_info['interfaces'][if_name] = if_data
464 # Add socket to temporary file list.
465 self._temp[if_name] = socket
467 def _qemu_qmp_exec(self, cmd):
468 """Execute QMP command.
470 QMP is JSON based protocol which allows to control QEMU instance.
472 :param cmd: QMP command to execute.
474 :returns: Command output in python representation of JSON format. The
475 { "return": {} } response is QMP's success response. An error
476 response will contain the "error" keyword instead of "return".
478 # To enter command mode, the qmp_capabilities command must be issued.
479 command = ('echo "{{ \\"execute\\": \\"qmp_capabilities\\" }}'
480 '{{ \\"execute\\": \\"{cmd}\\" }}" | '
481 'sudo -S socat - UNIX-CONNECT:{qmp}'.
482 format(cmd=cmd, qmp=self._temp.get('qmp')))
483 message = ('QMP execute "{cmd}" failed on {host}'.
484 format(cmd=cmd, host=self._node['host']))
485 stdout, _ = exec_cmd_no_error(self._node, command, sudo=False,
488 # Skip capabilities negotiation messages.
489 out_list = stdout.splitlines()
490 if len(out_list) < 3:
491 raise RuntimeError('Invalid QMP output on {host}'.
492 format(host=self._node['host']))
493 return json.loads(out_list[2])
495 def _qemu_qga_flush(self):
496 """Flush the QGA parser state."""
497 command = ('(printf "\xFF"; sleep 1) | '
498 'sudo -S socat - UNIX-CONNECT:{qga}'.
499 format(qga=self._temp.get('qga')))
500 message = ('QGA flush failed on {host}'.format(host=self._node['host']))
501 stdout, _ = exec_cmd_no_error(self._node, command, sudo=False,
504 return json.loads(stdout.split('\n', 1)[0]) if stdout else dict()
506 def _qemu_qga_exec(self, cmd):
507 """Execute QGA command.
509 QGA provide access to a system-level agent via standard QMP commands.
511 :param cmd: QGA command to execute.
514 command = ('(echo "{{ \\"execute\\": \\"{cmd}\\" }}"; sleep 1) | '
515 'sudo -S socat - UNIX-CONNECT:{qga}'.
516 format(cmd=cmd, qga=self._temp.get('qga')))
517 message = ('QGA execute "{cmd}" failed on {host}'.
518 format(cmd=cmd, host=self._node['host']))
519 stdout, _ = exec_cmd_no_error(self._node, command, sudo=False,
522 return json.loads(stdout.split('\n', 1)[0]) if stdout else dict()
524 def _wait_until_vm_boot(self):
525 """Wait until QEMU with NestedVM is booted."""
526 if self._opt.get('vm_type') == 'nestedvm':
527 self._wait_until_nestedvm_boot()
528 self._update_vm_interfaces()
529 elif self._opt.get('vm_type') == 'kernelvm':
530 self._wait_until_kernelvm_boot()
532 raise RuntimeError('QEMU: Unsupported VM type!')
534 def _wait_until_nestedvm_boot(self, retries=12):
535 """Wait until QEMU with NestedVM is booted.
537 First try to flush qga until there is output.
538 Then ping QEMU guest agent each 5s until VM booted or timeout.
540 :param retries: Number of retries with 5s between trials.
543 for _ in range(retries):
546 out = self._qemu_qga_flush()
548 logger.trace('QGA qga flush unexpected output {out}'.
550 # Empty output - VM not booted yet
556 raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
557 format(host=self._node['host']))
558 for _ in range(retries):
561 out = self._qemu_qga_exec('guest-ping')
563 logger.trace('QGA guest-ping unexpected output {out}'.
565 # Empty output - VM not booted yet.
568 # Non-error return - VM booted.
569 elif out.get('return') is not None:
571 # Skip error and wait.
572 elif out.get('error') is not None:
575 # If there is an unexpected output from QGA guest-info, try
576 # again until timeout.
577 logger.trace('QGA guest-ping unexpected output {out}'.
580 raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
581 format(host=self._node['host']))
583 def _wait_until_kernelvm_boot(self, retries=60):
584 """Wait until QEMU KernelVM is booted.
586 :param retries: Number of retries.
589 vpp_ver = VPPUtil.vpp_show_version(self._node)
591 for _ in range(retries):
592 command = ('tail -1 {log}'.format(log=self._temp.get('log')))
595 stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
599 if vpp_ver in stdout or 'Press enter to exit' in stdout:
601 if 'reboot: Power down' in stdout:
602 raise RuntimeError('QEMU: NF failed to run on {host}!'.
603 format(host=self._node['host']))
605 raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
606 format(host=self._node['host']))
608 def _update_vm_interfaces(self):
609 """Update interface names in VM node dict."""
610 # Send guest-network-get-interfaces command via QGA, output example:
611 # {"return": [{"name": "eth0", "hardware-address": "52:54:00:00:04:01"},
612 # {"name": "eth1", "hardware-address": "52:54:00:00:04:02"}]}.
613 out = self._qemu_qga_exec('guest-network-get-interfaces')
614 interfaces = out.get('return')
617 raise RuntimeError('Get VM interface list failed on {host}'.
618 format(host=self._node['host']))
619 # Create MAC-name dict.
620 for interface in interfaces:
621 if 'hardware-address' not in interface:
623 mac_name[interface['hardware-address']] = interface['name']
624 # Match interface by MAC and save interface name.
625 for interface in self._vm_info['interfaces'].values():
626 mac = interface.get('mac_address')
627 if_name = mac_name.get(mac)
629 logger.trace('Interface name for MAC {mac} not found'.
632 interface['name'] = if_name
634 def qemu_start(self):
635 """Start QEMU and wait until VM boot.
637 :returns: VM node info.
640 command = ('{bin_path}/qemu-system-{arch} {params}'.
641 format(bin_path=self._opt.get('bin_path'),
642 arch=Topology.get_node_arch(self._node),
643 params=self._params))
644 message = ('QEMU: Start failed on {host}!'.
645 format(host=self._node['host']))
647 DUTSetup.check_huge_page(self._node, '/dev/hugepages',
648 self._opt.get('mem'))
650 exec_cmd_no_error(self._node, command, timeout=300, sudo=True,
652 self._wait_until_vm_boot()
659 """Kill qemu process."""
660 exec_cmd(self._node, 'chmod +r {pidfile}'.
661 format(pidfile=self._temp.get('pidfile')), sudo=True)
662 exec_cmd(self._node, 'kill -SIGKILL $(cat {pidfile})'.
663 format(pidfile=self._temp.get('pidfile')), sudo=True)
665 for value in self._temp.values():
666 exec_cmd(self._node, 'cat {value}'.format(value=value), sudo=True)
667 exec_cmd(self._node, 'rm -f {value}'.format(value=value), sudo=True)
669 def qemu_kill_all(self):
670 """Kill all qemu processes on DUT node if specified."""
671 exec_cmd(self._node, 'pkill -SIGKILL qemu', sudo=True)
673 for value in self._temp.values():
674 exec_cmd(self._node, 'cat {value}'.format(value=value), sudo=True)
675 exec_cmd(self._node, 'rm -f {value}'.format(value=value), sudo=True)
677 def qemu_version(self, version=None):
678 """Return Qemu version or compare if version is higher than parameter.
680 :param version: Version to compare.
682 :returns: Qemu version or Boolean if version is higher than parameter.
685 command = ('{bin_path}/qemu-system-{arch} --version'.
686 format(bin_path=self._opt.get('bin_path'),
687 arch=Topology.get_node_arch(self._node)))
689 stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
690 ver = match(r'QEMU emulator version ([\d.]*)', stdout).group(1)
691 return StrictVersion(ver) > StrictVersion(version) \
698 def build_qemu(node, force_install=False, apply_patch=False):
699 """Build QEMU from sources.
701 :param node: Node to build QEMU on.
702 :param force_install: If True, then remove previous build.
703 :param apply_patch: If True, then apply patches from qemu_patches dir.
705 :type force_install: bool
706 :type apply_patch: bool
707 :raises RuntimeError: If building QEMU failed.
709 directory = (' --directory={install_dir}{patch}'.
710 format(install_dir=Constants.QEMU_INSTALL_DIR,
711 patch='-patch' if apply_patch else '-base'))
712 version = (' --version={install_version}'.
713 format(install_version=Constants.QEMU_INSTALL_VERSION))
714 force = ' --force' if force_install else ''
715 patch = ' --patch' if apply_patch else ''
716 target_list = (' --target-list={arch}-softmmu'.
717 format(arch=Topology.get_node_arch(node)))
719 command = ("sudo -E sh -c "
720 "'{fw_dir}/{lib_sh}/qemu_build.sh{version}{directory}"
721 "{force}{patch}{target_list}'".
722 format(fw_dir=Constants.REMOTE_FW_DIR,
723 lib_sh=Constants.RESOURCES_LIB_SH,
724 version=version, directory=directory, force=force,
725 patch=patch, target_list=target_list))
726 message = ('QEMU: Build failed on {host}!'.format(host=node['host']))
727 exec_cmd_no_error(node, command, sudo=False, message=message,