qemu/scripts/simplebench/bench_block_job.py
John Snow 37094b6dd5 python: rename qemu.aqmp to qemu.qmp
Now that we are fully switched over to the new QMP library, move it back
over the old namespace. This is being done primarily so that we may
upload this package simply as "qemu.qmp" without introducing confusion
over whether or not "aqmp" is a new protocol or not.

The trade-off is increased confusion inside the QEMU developer
tree. Sorry!

Note: the 'private' member "_aqmp" in legacy.py also changes to "_qmp";
not out of necessity, but just to remove any traces of the "aqmp"
name.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Beraldo Leal <bleal@redhat.com>
Acked-by: Hanna Reitz <hreitz@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@openvz.org>
Message-id: 20220330172812.3427355-8-jsnow@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
2022-04-21 11:01:00 -04:00

159 lines
5.0 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Benchmark block jobs
#
# Copyright (c) 2019 Virtuozzo International GmbH.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import sys
import os
import subprocess
import socket
import json
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
from qemu.machine import QEMUMachine
from qemu.qmp import ConnectError
def bench_block_job(cmd, cmd_args, qemu_args):
"""Benchmark block-job
cmd -- qmp command to run block-job (like blockdev-backup)
cmd_args -- dict of qmp command arguments
qemu_args -- list of Qemu command line arguments, including path to Qemu
binary
Returns {'seconds': int} on success and {'error': str} on failure, dict may
contain addional 'vm-log' field. Return value is compatible with
simplebench lib.
"""
vm = QEMUMachine(qemu_args[0], args=qemu_args[1:])
try:
vm.launch()
except OSError as e:
return {'error': 'popen failed: ' + str(e)}
except (ConnectError, socket.timeout):
return {'error': 'qemu failed: ' + str(vm.get_log())}
try:
res = vm.qmp(cmd, **cmd_args)
if res != {'return': {}}:
vm.shutdown()
return {'error': '"{}" command failed: {}'.format(cmd, str(res))}
e = vm.event_wait('JOB_STATUS_CHANGE')
assert e['data']['status'] == 'created'
start_ms = e['timestamp']['seconds'] * 1000000 + \
e['timestamp']['microseconds']
e = vm.events_wait((('BLOCK_JOB_READY', None),
('BLOCK_JOB_COMPLETED', None),
('BLOCK_JOB_FAILED', None)), timeout=True)
if e['event'] not in ('BLOCK_JOB_READY', 'BLOCK_JOB_COMPLETED'):
vm.shutdown()
return {'error': 'block-job failed: ' + str(e),
'vm-log': vm.get_log()}
if 'error' in e['data']:
vm.shutdown()
return {'error': 'block-job failed: ' + e['data']['error'],
'vm-log': vm.get_log()}
end_ms = e['timestamp']['seconds'] * 1000000 + \
e['timestamp']['microseconds']
finally:
vm.shutdown()
return {'seconds': (end_ms - start_ms) / 1000000.0}
def get_image_size(path):
out = subprocess.run(['qemu-img', 'info', '--out=json', path],
stdout=subprocess.PIPE, check=True).stdout
return json.loads(out)['virtual-size']
def get_blockdev_size(obj):
img = obj['filename'] if 'filename' in obj else obj['file']['filename']
return get_image_size(img)
# Bench backup or mirror
def bench_block_copy(qemu_binary, cmd, cmd_options, source, target):
"""Helper to run bench_block_job() for mirror or backup"""
assert cmd in ('blockdev-backup', 'blockdev-mirror')
if target['driver'] == 'qcow2':
try:
os.remove(target['file']['filename'])
except OSError:
pass
subprocess.run(['qemu-img', 'create', '-f', 'qcow2',
target['file']['filename'],
str(get_blockdev_size(source))],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, check=True)
source['node-name'] = 'source'
target['node-name'] = 'target'
cmd_options['job-id'] = 'job0'
cmd_options['device'] = 'source'
cmd_options['target'] = 'target'
cmd_options['sync'] = 'full'
return bench_block_job(cmd, cmd_options,
[qemu_binary,
'-blockdev', json.dumps(source),
'-blockdev', json.dumps(target)])
def drv_file(filename, o_direct=True):
node = {'driver': 'file', 'filename': filename}
if o_direct:
node['cache'] = {'direct': True}
node['aio'] = 'native'
return node
def drv_nbd(host, port):
return {'driver': 'nbd',
'server': {'type': 'inet', 'host': host, 'port': port}}
def drv_qcow2(file):
return {'driver': 'qcow2', 'file': file}
if __name__ == '__main__':
import sys
if len(sys.argv) < 4:
print('USAGE: {} <qmp block-job command name> '
'<json string of arguments for the command> '
'<qemu binary path and arguments>'.format(sys.argv[0]))
exit(1)
res = bench_block_job(sys.argv[1], json.loads(sys.argv[2]), sys.argv[3:])
if 'seconds' in res:
print('{:.2f}'.format(res['seconds']))
else:
print(res)