5 # Copyright (c) 2019 Virtuozzo International GmbH.
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 sys
.path
.append(os
.path
.join(os
.path
.dirname(__file__
), '..', '..', 'python'))
28 from qemu
.machine
import QEMUMachine
29 from qemu
.qmp
import QMPConnectError
32 def bench_block_job(cmd
, cmd_args
, qemu_args
):
33 """Benchmark block-job
35 cmd -- qmp command to run block-job (like blockdev-backup)
36 cmd_args -- dict of qmp command arguments
37 qemu_args -- list of Qemu command line arguments, including path to Qemu
40 Returns {'seconds': int} on success and {'error': str} on failure, dict may
41 contain addional 'vm-log' field. Return value is compatible with
45 vm
= QEMUMachine(qemu_args
[0], args
=qemu_args
[1:])
50 return {'error': 'popen failed: ' + str(e
)}
51 except (QMPConnectError
, socket
.timeout
):
52 return {'error': 'qemu failed: ' + str(vm
.get_log())}
55 res
= vm
.qmp(cmd
, **cmd_args
)
56 if res
!= {'return': {}}:
58 return {'error': '"{}" command failed: {}'.format(cmd
, str(res
))}
60 e
= vm
.event_wait('JOB_STATUS_CHANGE')
61 assert e
['data']['status'] == 'created'
62 start_ms
= e
['timestamp']['seconds'] * 1000000 + \
63 e
['timestamp']['microseconds']
65 e
= vm
.events_wait((('BLOCK_JOB_READY', None),
66 ('BLOCK_JOB_COMPLETED', None),
67 ('BLOCK_JOB_FAILED', None)), timeout
=True)
68 if e
['event'] not in ('BLOCK_JOB_READY', 'BLOCK_JOB_COMPLETED'):
70 return {'error': 'block-job failed: ' + str(e
),
71 'vm-log': vm
.get_log()}
72 end_ms
= e
['timestamp']['seconds'] * 1000000 + \
73 e
['timestamp']['microseconds']
77 return {'seconds': (end_ms
- start_ms
) / 1000000.0}
80 # Bench backup or mirror
81 def bench_block_copy(qemu_binary
, cmd
, source
, target
):
82 """Helper to run bench_block_job() for mirror or backup"""
83 assert cmd
in ('blockdev-backup', 'blockdev-mirror')
85 source
['node-name'] = 'source'
86 target
['node-name'] = 'target'
88 return bench_block_job(cmd
,
89 {'job-id': 'job0', 'device': 'source',
90 'target': 'target', 'sync': 'full'},
92 '-blockdev', json
.dumps(source
),
93 '-blockdev', json
.dumps(target
)])
96 def drv_file(filename
):
97 return {'driver': 'file', 'filename': filename
,
98 'cache': {'direct': True}, 'aio': 'native'}
101 def drv_nbd(host
, port
):
102 return {'driver': 'nbd',
103 'server': {'type': 'inet', 'host': host
, 'port': port
}}
106 if __name__
== '__main__':
109 if len(sys
.argv
) < 4:
110 print('USAGE: {} <qmp block-job command name> '
111 '<json string of arguments for the command> '
112 '<qemu binary path and arguments>'.format(sys
.argv
[0]))
115 res
= bench_block_job(sys
.argv
[1], json
.loads(sys
.argv
[2]), sys
.argv
[3:])
117 print('{:.2f}'.format(res
['seconds']))