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 # Disable due to pylint bug
17 # pylint: disable=no-name-in-module,import-error
18 from distutils.version import StrictVersion
21 from string import Template
22 from time import sleep
24 from robot.api import logger
25 from resources.libraries.python.Constants import Constants
26 from resources.libraries.python.DpdkUtil import DpdkUtil
27 from resources.libraries.python.DUTSetup import DUTSetup
28 from resources.libraries.python.OptionString import OptionString
29 from resources.libraries.python.VppConfigGenerator import VppConfigGenerator
30 from resources.libraries.python.VPPUtil import VPPUtil
31 from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
32 from resources.libraries.python.topology import NodeType, Topology
34 __all__ = ["QemuUtils"]
37 class QemuUtils(object):
40 # Use one instance of class per tests.
41 ROBOT_LIBRARY_SCOPE = 'TEST CASE'
43 def __init__(self, node, qemu_id=1, smp=1, mem=512, vnf=None,
44 img=Constants.QEMU_VM_IMAGE):
45 """Initialize QemuUtil class.
47 :param node: Node to run QEMU on.
48 :param qemu_id: QEMU identifier.
49 :param smp: Number of virtual SMP units (cores).
50 :param mem: Amount of memory.
51 :param vnf: Network function workload.
52 :param img: QEMU disk image or kernel image path.
62 self._arch = Topology.get_node_arch(self._node)
63 dpdk_target = 'arm64-armv8a' if self._arch == 'aarch64' \
65 self._testpmd_path = '{path}/{dpdk_target}-linuxapp-gcc/app'\
66 .format(path=Constants.QEMU_VM_DPDK, dpdk_target=dpdk_target)
70 'port': 10021 + qemu_id,
71 'serial': 4555 + qemu_id,
76 if node['port'] != 22:
77 self._vm_info['host_port'] = node['port']
78 self._vm_info['host_username'] = node['username']
79 self._vm_info['host_password'] = node['password']
82 self._opt['qemu_id'] = qemu_id
83 self._opt['mem'] = int(mem)
84 self._opt['smp'] = int(smp)
85 self._opt['img'] = img
86 self._opt['vnf'] = vnf
89 self._temp['pidfile'] = '/var/run/qemu_{id}.pid'.format(id=qemu_id)
90 if img == Constants.QEMU_VM_IMAGE:
91 self._opt['vm_type'] = 'nestedvm'
92 self._temp['qmp'] = '/var/run/qmp_{id}.sock'.format(id=qemu_id)
93 self._temp['qga'] = '/var/run/qga_{id}.sock'.format(id=qemu_id)
94 elif img == Constants.QEMU_VM_KERNEL:
95 self._opt['img'], _ = exec_cmd_no_error(
97 'ls -1 {img}* | tail -1'.format(img=Constants.QEMU_VM_KERNEL),
98 message='Qemu Kernel VM image not found!')
99 self._opt['vm_type'] = 'kernelvm'
100 self._temp['log'] = '/tmp/serial_{id}.log'.format(id=qemu_id)
101 self._temp['ini'] = '/etc/vm_init_{id}.conf'.format(id=qemu_id)
102 self._opt['initrd'], _ = exec_cmd_no_error(
104 'ls -1 {initrd}* | tail -1'.format(
105 initrd=Constants.QEMU_VM_KERNEL_INITRD),
106 message='Qemu Kernel initrd image not found!')
108 raise RuntimeError('QEMU: Unknown VM image option: {}'.format(img))
109 # Computed parameters for QEMU command line.
110 self._params = OptionString(prefix='-')
113 def add_params(self):
114 """Set QEMU command line parameters."""
115 self.add_default_params()
116 if self._opt.get('vm_type', '') == 'nestedvm':
117 self.add_nestedvm_params()
118 elif self._opt.get('vm_type', '') == 'kernelvm':
119 self.add_kernelvm_params()
121 raise RuntimeError('QEMU: Unsupported VM type!')
123 def add_default_params(self):
124 """Set default QEMU command line parameters."""
125 self._params.add('daemonize')
126 self._params.add('nodefaults')
127 self._params.add_with_value('name', 'vnf{qemu},debug-threads=on'.format(
128 qemu=self._opt.get('qemu_id')))
129 self._params.add('no-user-config')
130 self._params.add_with_value('monitor', 'none')
131 self._params.add_with_value('display', 'none')
132 self._params.add_with_value('vga', 'none')
133 self._params.add('enable-kvm')
134 self._params.add_with_value('pidfile', self._temp.get('pidfile'))
135 self._params.add_with_value('cpu', 'host')
137 if self._arch == 'aarch64':
138 machine_args = 'virt,accel=kvm,usb=off,mem-merge=off,gic-version=3'
140 machine_args = 'pc,accel=kvm,usb=off,mem-merge=off'
141 self._params.add_with_value(
142 'machine', machine_args)
143 self._params.add_with_value(
144 'smp', '{smp},sockets=1,cores={smp},threads=1'.format(
145 smp=self._opt.get('smp')))
146 self._params.add_with_value(
147 'object', 'memory-backend-file,id=mem,size={mem}M,'
148 'mem-path=/dev/hugepages,share=on'.format(mem=self._opt.get('mem')))
149 self._params.add_with_value(
150 'm', '{mem}M'.format(mem=self._opt.get('mem')))
151 self._params.add_with_value('numa', 'node,memdev=mem')
152 self._params.add_with_value('balloon', 'none')
154 def add_nestedvm_params(self):
155 """Set NestedVM QEMU parameters."""
156 self._params.add_with_value(
157 'net', 'nic,macaddr=52:54:00:00:{qemu:02x}:ff'.format(
158 qemu=self._opt.get('qemu_id')))
159 self._params.add_with_value(
160 'net', 'user,hostfwd=tcp::{info[port]}-:22'.format(
162 # TODO: Remove try except after fully migrated to Bionic or
163 # qemu_set_node is removed.
165 locking = ',file.locking=off'\
166 if self.qemu_version(version='2.10') else ''
167 except AttributeError:
169 self._params.add_with_value(
170 'drive', 'file={img},format=raw,cache=none,if=virtio{locking}'.
171 format(img=self._opt.get('img'), locking=locking))
172 self._params.add_with_value(
173 'qmp', 'unix:{qmp},server,nowait'.format(qmp=self._temp.get('qmp')))
174 self._params.add_with_value(
175 'chardev', 'socket,host=127.0.0.1,port={info[serial]},'
176 'id=gnc0,server,nowait'.format(info=self._vm_info))
177 self._params.add_with_value('device', 'isa-serial,chardev=gnc0')
178 self._params.add_with_value(
179 'chardev', 'socket,path={qga},server,nowait,id=qga0'.format(
180 qga=self._temp.get('qga')))
181 self._params.add_with_value('device', 'isa-serial,chardev=qga0')
183 def add_kernelvm_params(self):
184 """Set KernelVM QEMU parameters."""
185 console = 'ttyAMA0' if self._arch == 'aarch64' else 'ttyS0'
186 self._params.add_with_value('serial', 'file:{log}'.format(
187 log=self._temp.get('log')))
188 self._params.add_with_value(
189 'fsdev', 'local,id=root9p,path=/,security_model=none')
190 self._params.add_with_value(
191 'device', 'virtio-9p-pci,fsdev=root9p,mount_tag=virtioroot')
192 self._params.add_with_value(
193 'kernel', '{img}'.format(img=self._opt.get('img')))
194 self._params.add_with_value(
195 'initrd', '{initrd}'.format(initrd=self._opt.get('initrd')))
196 self._params.add_with_value(
197 'append', '"ro rootfstype=9p rootflags=trans=virtio '
198 'root=virtioroot console={console} tsc=reliable '
199 'hugepages=256 init={init}"'.format(
200 console=console, init=self._temp.get('ini')))
202 def create_kernelvm_config_vpp(self, **kwargs):
203 """Create QEMU VPP config files.
205 :param kwargs: Key-value pairs to replace content of VPP configuration
209 startup = ('/etc/vpp/vm_startup_{id}.conf'.
210 format(id=self._opt.get('qemu_id')))
211 running = ('/etc/vpp/vm_running_{id}.exec'.
212 format(id=self._opt.get('qemu_id')))
214 self._temp['startup'] = startup
215 self._temp['running'] = running
216 self._opt['vnf_bin'] = ('/usr/bin/vpp -c {startup}'.
217 format(startup=startup))
219 # Create VPP startup configuration.
220 vpp_config = VppConfigGenerator()
221 vpp_config.set_node(self._node)
222 vpp_config.add_unix_nodaemon()
223 vpp_config.add_unix_cli_listen()
224 vpp_config.add_unix_exec(running)
225 vpp_config.add_socksvr()
226 vpp_config.add_cpu_main_core('0')
227 if self._opt.get('smp') > 1:
228 vpp_config.add_cpu_corelist_workers('1-{smp}'.format(
229 smp=self._opt.get('smp')-1))
230 vpp_config.add_dpdk_dev('0000:00:06.0', '0000:00:07.0')
231 vpp_config.add_dpdk_dev_default_rxq(kwargs['queues'])
232 vpp_config.add_dpdk_log_level('debug')
233 if not kwargs['jumbo_frames']:
234 vpp_config.add_dpdk_no_multi_seg()
235 vpp_config.add_dpdk_no_tx_checksum_offload()
236 vpp_config.add_plugin('disable', 'default')
237 vpp_config.add_plugin('enable', 'dpdk_plugin.so')
238 vpp_config.write_config(startup)
240 # Create VPP running configuration.
241 template = '{res}/{tpl}.exec'.format(res=Constants.RESOURCES_TPL_VM,
242 tpl=self._opt.get('vnf'))
243 exec_cmd_no_error(self._node, 'rm -f {running}'.format(running=running),
246 with open(template, 'r') as src_file:
247 src = Template(src_file.read())
249 self._node, "echo '{out}' | sudo tee {running}".format(
250 out=src.safe_substitute(**kwargs), running=running))
252 def create_kernelvm_config_testpmd_io(self, **kwargs):
253 """Create QEMU testpmd-io command line.
255 :param kwargs: Key-value pairs to construct command line parameters.
258 testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
259 eal_corelist='0-{smp}'.format(smp=self._opt.get('smp') - 1),
263 pmd_rxq=kwargs['queues'],
264 pmd_txq=kwargs['queues'],
265 pmd_tx_offloads=False,
266 pmd_disable_hw_vlan=False,
267 pmd_max_pkt_len=9200 if kwargs['jumbo_frames'] else None,
268 pmd_nb_cores=str(self._opt.get('smp') - 1))
270 self._opt['vnf_bin'] = ('{testpmd_path}/{testpmd_cmd}'.
271 format(testpmd_path=self._testpmd_path,
272 testpmd_cmd=testpmd_cmd))
274 def create_kernelvm_config_testpmd_mac(self, **kwargs):
275 """Create QEMU testpmd-mac command line.
277 :param kwargs: Key-value pairs to construct command line parameters.
280 testpmd_cmd = DpdkUtil.get_testpmd_cmdline(
281 eal_corelist='0-{smp}'.format(smp=self._opt.get('smp') - 1),
286 pmd_eth_peer_0='0,{mac}'.format(mac=kwargs['vif1_mac']),
287 pmd_eth_peer_1='1,{mac}'.format(mac=kwargs['vif2_mac']),
288 pmd_rxq=kwargs['queues'],
289 pmd_txq=kwargs['queues'],
290 pmd_tx_offloads=False,
291 pmd_disable_hw_vlan=False,
292 pmd_max_pkt_len=9200 if kwargs['jumbo_frames'] else None,
293 pmd_nb_cores=str(self._opt.get('smp') - 1))
295 self._opt['vnf_bin'] = ('{testpmd_path}/{testpmd_cmd}'.
296 format(testpmd_path=self._testpmd_path,
297 testpmd_cmd=testpmd_cmd))
299 def create_kernelvm_init(self, **kwargs):
300 """Create QEMU init script.
302 :param kwargs: Key-value pairs to replace content of init startup file.
305 template = '{res}/init.sh'.format(res=Constants.RESOURCES_TPL_VM)
306 init = self._temp.get('ini')
308 self._node, 'rm -f {init}'.format(init=init), sudo=True)
310 with open(template, 'r') as src_file:
311 src = Template(src_file.read())
313 self._node, "echo '{out}' | sudo tee {init}".format(
314 out=src.safe_substitute(**kwargs), init=init))
316 self._node, "chmod +x {init}".format(init=init), sudo=True)
318 def configure_kernelvm_vnf(self, **kwargs):
319 """Create KernelVM VNF configurations.
321 :param kwargs: Key-value pairs for templating configs.
324 if 'vpp' in self._opt.get('vnf'):
325 self.create_kernelvm_config_vpp(**kwargs)
326 elif 'testpmd_io' in self._opt.get('vnf'):
327 self.create_kernelvm_config_testpmd_io(**kwargs)
328 elif 'testpmd_mac' in self._opt.get('vnf'):
329 self.create_kernelvm_config_testpmd_mac(**kwargs)
331 raise RuntimeError('QEMU: Unsupported VNF!')
332 self.create_kernelvm_init(vnf_bin=self._opt['vnf_bin'])
334 def get_qemu_pids(self):
335 """Get QEMU CPU pids.
337 :returns: List of QEMU CPU pids.
340 command = ("grep -rwl 'CPU' /proc/$(sudo cat {pidfile})/task/*/comm ".
341 format(pidfile=self._temp.get('pidfile')))
342 command += (r"| xargs dirname | sed -e 's/\/.*\///g' | uniq")
344 stdout, _ = exec_cmd_no_error(self._node, command)
345 return stdout.splitlines()
347 def qemu_set_affinity(self, *host_cpus):
348 """Set qemu affinity by getting thread PIDs via QMP and taskset to list
349 of CPU cores. Function tries to execute 3 times to avoid race condition
350 in getting thread PIDs.
352 :param host_cpus: List of CPU cores.
353 :type host_cpus: list
357 qemu_cpus = self.get_qemu_pids()
359 if len(qemu_cpus) != len(host_cpus):
362 for qemu_cpu, host_cpu in zip(qemu_cpus, host_cpus):
363 command = ('taskset -pc {host_cpu} {thread}'.
364 format(host_cpu=host_cpu, thread=qemu_cpu))
365 message = ('QEMU: Set affinity failed on {host}!'.
366 format(host=self._node['host']))
367 exec_cmd_no_error(self._node, command, sudo=True,
370 except (RuntimeError, ValueError):
375 raise RuntimeError('Failed to set Qemu threads affinity!')
377 def qemu_set_scheduler_policy(self):
378 """Set scheduler policy to SCHED_RR with priority 1 for all Qemu CPU
381 :raises RuntimeError: Set scheduler policy failed.
384 qemu_cpus = self.get_qemu_pids()
386 for qemu_cpu in qemu_cpus:
387 command = ('chrt -r -p 1 {thread}'.
388 format(thread=qemu_cpu))
389 message = ('QEMU: Set SCHED_RR failed on {host}'.
390 format(host=self._node['host']))
391 exec_cmd_no_error(self._node, command, sudo=True,
393 except (RuntimeError, ValueError):
397 def qemu_add_vhost_user_if(self, socket, server=True, jumbo_frames=False,
398 queue_size=None, queues=1):
399 """Add Vhost-user interface.
401 :param socket: Path of the unix socket.
402 :param server: If True the socket shall be a listening socket.
403 :param jumbo_frames: Set True if jumbo frames are used in the test.
404 :param queue_size: Vring queue size.
405 :param queues: Number of queues.
408 :type jumbo_frames: bool
409 :type queue_size: int
413 self._params.add_with_value(
414 'chardev', 'socket,id=char{vhost},path={socket}{server}'.format(
415 vhost=self._vhost_id, socket=socket,
416 server=',server' if server is True else ''))
417 self._params.add_with_value(
418 'netdev', 'vhost-user,id=vhost{vhost},chardev=char{vhost},'
419 'queues={queues}'.format(vhost=self._vhost_id, queues=queues))
420 mac = ('52:54:00:00:{qemu:02x}:{vhost:02x}'.
421 format(qemu=self._opt.get('qemu_id'), vhost=self._vhost_id))
422 queue_size = ('rx_queue_size={queue_size},tx_queue_size={queue_size}'.
423 format(queue_size=queue_size)) if queue_size else ''
424 mbuf = 'on,host_mtu=9200'
425 self._params.add_with_value(
426 'device', 'virtio-net-pci,netdev=vhost{vhost},mac={mac},'
427 'addr={addr}.0,mq=on,vectors={vectors},csum=off,gso=off,'
428 'guest_tso4=off,guest_tso6=off,guest_ecn=off,mrg_rxbuf={mbuf},'
429 '{queue_size}'.format(
430 addr=self._vhost_id+5, vhost=self._vhost_id, mac=mac,
431 mbuf=mbuf if jumbo_frames else 'off', queue_size=queue_size,
432 vectors=(2 * queues + 2)))
434 # Add interface MAC and socket to the node dict.
435 if_data = {'mac_address': mac, 'socket': socket}
436 if_name = 'vhost{vhost}'.format(vhost=self._vhost_id)
437 self._vm_info['interfaces'][if_name] = if_data
438 # Add socket to temporary file list.
439 self._temp[if_name] = socket
441 def _qemu_qmp_exec(self, cmd):
442 """Execute QMP command.
444 QMP is JSON based protocol which allows to control QEMU instance.
446 :param cmd: QMP command to execute.
448 :returns: Command output in python representation of JSON format. The
449 { "return": {} } response is QMP's success response. An error
450 response will contain the "error" keyword instead of "return".
452 # To enter command mode, the qmp_capabilities command must be issued.
453 command = ('echo "{{ \\"execute\\": \\"qmp_capabilities\\" }}'
454 '{{ \\"execute\\": \\"{cmd}\\" }}" | '
455 'sudo -S socat - UNIX-CONNECT:{qmp}'.
456 format(cmd=cmd, qmp=self._temp.get('qmp')))
457 message = ('QMP execute "{cmd}" failed on {host}'.
458 format(cmd=cmd, host=self._node['host']))
459 stdout, _ = exec_cmd_no_error(
460 self._node, command, sudo=False, message=message)
462 # Skip capabilities negotiation messages.
463 out_list = stdout.splitlines()
464 if len(out_list) < 3:
466 'Invalid QMP output on {host}'.format(host=self._node['host']))
467 return json.loads(out_list[2])
469 def _qemu_qga_flush(self):
470 """Flush the QGA parser state."""
471 command = ('(printf "\xFF"; sleep 1) | '
472 'sudo -S socat - UNIX-CONNECT:{qga}'.
473 format(qga=self._temp.get('qga')))
474 message = ('QGA flush failed on {host}'.format(host=self._node['host']))
475 stdout, _ = exec_cmd_no_error(
476 self._node, command, sudo=False, message=message)
478 return json.loads(stdout.split('\n', 1)[0]) if stdout else dict()
480 def _qemu_qga_exec(self, cmd):
481 """Execute QGA command.
483 QGA provide access to a system-level agent via standard QMP commands.
485 :param cmd: QGA command to execute.
488 command = ('(echo "{{ \\"execute\\": \\"{cmd}\\" }}"; sleep 1) | '
489 'sudo -S socat - UNIX-CONNECT:{qga}'.
490 format(cmd=cmd, qga=self._temp.get('qga')))
491 message = ('QGA execute "{cmd}" failed on {host}'.
492 format(cmd=cmd, host=self._node['host']))
493 stdout, _ = exec_cmd_no_error(
494 self._node, command, sudo=False, message=message)
496 return json.loads(stdout.split('\n', 1)[0]) if stdout else dict()
498 def _wait_until_vm_boot(self):
499 """Wait until QEMU with NestedVM is booted."""
500 if self._opt.get('vm_type') == 'nestedvm':
501 self._wait_until_nestedvm_boot()
502 self._update_vm_interfaces()
503 elif self._opt.get('vm_type') == 'kernelvm':
504 self._wait_until_kernelvm_boot()
506 raise RuntimeError('QEMU: Unsupported VM type!')
508 def _wait_until_nestedvm_boot(self, retries=12):
509 """Wait until QEMU with NestedVM is booted.
511 First try to flush qga until there is output.
512 Then ping QEMU guest agent each 5s until VM booted or timeout.
514 :param retries: Number of retries with 5s between trials.
517 for _ in range(retries):
520 out = self._qemu_qga_flush()
522 logger.trace('QGA qga flush unexpected output {out}'.
524 # Empty output - VM not booted yet
530 raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
531 format(host=self._node['host']))
532 for _ in range(retries):
535 out = self._qemu_qga_exec('guest-ping')
537 logger.trace('QGA guest-ping unexpected output {out}'.
539 # Empty output - VM not booted yet.
542 # Non-error return - VM booted.
543 elif out.get('return') is not None:
545 # Skip error and wait.
546 elif out.get('error') is not None:
549 # If there is an unexpected output from QGA guest-info, try
550 # again until timeout.
551 logger.trace('QGA guest-ping unexpected output {out}'.
554 raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
555 format(host=self._node['host']))
557 def _wait_until_kernelvm_boot(self, retries=60):
558 """Wait until QEMU KernelVM is booted.
560 :param retries: Number of retries.
563 vpp_ver = VPPUtil.vpp_show_version(self._node)
565 for _ in range(retries):
566 command = ('tail -1 {log}'.format(log=self._temp.get('log')))
569 stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
573 if vpp_ver in stdout or 'Press enter to exit' in stdout:
575 if 'reboot: Power down' in stdout:
576 raise RuntimeError('QEMU: NF failed to run on {host}!'.
577 format(host=self._node['host']))
579 raise RuntimeError('QEMU: Timeout, VM not booted on {host}!'.
580 format(host=self._node['host']))
582 def _update_vm_interfaces(self):
583 """Update interface names in VM node dict."""
584 # Send guest-network-get-interfaces command via QGA, output example:
585 # {"return": [{"name": "eth0", "hardware-address": "52:54:00:00:04:01"},
586 # {"name": "eth1", "hardware-address": "52:54:00:00:04:02"}]}.
587 out = self._qemu_qga_exec('guest-network-get-interfaces')
588 interfaces = out.get('return')
591 raise RuntimeError('Get VM interface list failed on {host}'.
592 format(host=self._node['host']))
593 # Create MAC-name dict.
594 for interface in interfaces:
595 if 'hardware-address' not in interface:
597 mac_name[interface['hardware-address']] = interface['name']
598 # Match interface by MAC and save interface name.
599 for interface in self._vm_info['interfaces'].values():
600 mac = interface.get('mac_address')
601 if_name = mac_name.get(mac)
604 'Interface name for MAC {mac} not found'.format(mac=mac))
606 interface['name'] = if_name
608 def qemu_start(self):
609 """Start QEMU and wait until VM boot.
611 :returns: VM node info.
614 cmd_opts = OptionString()
615 cmd_opts.add('{bin_path}/qemu-system-{arch}'.format(
616 bin_path=Constants.QEMU_BIN_PATH, arch=self._arch))
617 cmd_opts.extend(self._params)
618 message = ('QEMU: Start failed on {host}!'.
619 format(host=self._node['host']))
621 DUTSetup.check_huge_page(
622 self._node, '/dev/hugepages', self._opt.get('mem'))
625 self._node, cmd_opts, timeout=300, sudo=True, message=message)
626 self._wait_until_vm_boot()
633 """Kill qemu process."""
634 exec_cmd(self._node, 'chmod +r {pidfile}'.
635 format(pidfile=self._temp.get('pidfile')), sudo=True)
636 exec_cmd(self._node, 'kill -SIGKILL $(cat {pidfile})'.
637 format(pidfile=self._temp.get('pidfile')), sudo=True)
639 for value in self._temp.values():
640 exec_cmd(self._node, 'cat {value}'.format(value=value), sudo=True)
641 exec_cmd(self._node, 'rm -f {value}'.format(value=value), sudo=True)
643 def qemu_kill_all(self):
644 """Kill all qemu processes on DUT node if specified."""
645 exec_cmd(self._node, 'pkill -SIGKILL qemu', sudo=True)
647 for value in self._temp.values():
648 exec_cmd(self._node, 'cat {value}'.format(value=value), sudo=True)
649 exec_cmd(self._node, 'rm -f {value}'.format(value=value), sudo=True)
651 def qemu_version(self, version=None):
652 """Return Qemu version or compare if version is higher than parameter.
654 :param version: Version to compare.
656 :returns: Qemu version or Boolean if version is higher than parameter.
659 command = ('{bin_path}/qemu-system-{arch} --version'.format(
660 bin_path=Constants.QEMU_BIN_PATH,
663 stdout, _ = exec_cmd_no_error(self._node, command, sudo=True)
664 ver = match(r'QEMU emulator version ([\d.]*)', stdout).group(1)
665 return StrictVersion(ver) > StrictVersion(version) \