1#!/usr/bin/env python3 2# 3# Bench backup block-job 4# 5# Copyright (c) 2020 Virtuozzo International GmbH. 6# 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. 11# 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. 16# 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/>. 19# 20 21import argparse 22import json 23 24import simplebench 25from results_to_text import results_to_text 26from bench_block_job import bench_block_copy, drv_file, drv_nbd 27 28 29def bench_func(env, case): 30 """ Handle one "cell" of benchmarking table. """ 31 cmd_options = env['cmd-options'] if 'cmd-options' in env else {} 32 return bench_block_copy(env['qemu-binary'], env['cmd'], 33 cmd_options, 34 case['source'], case['target']) 35 36 37def bench(args): 38 test_cases = [] 39 40 sources = {} 41 targets = {} 42 for d in args.dir: 43 label, path = d.split(':') # paths with colon not supported 44 sources[label] = drv_file(path + '/test-source') 45 targets[label] = drv_file(path + '/test-target') 46 47 if args.nbd: 48 nbd = args.nbd.split(':') 49 host = nbd[0] 50 port = '10809' if len(nbd) == 1 else nbd[1] 51 drv = drv_nbd(host, port) 52 sources['nbd'] = drv 53 targets['nbd'] = drv 54 55 for t in args.test: 56 src, dst = t.split(':') 57 58 test_cases.append({ 59 'id': t, 60 'source': sources[src], 61 'target': targets[dst] 62 }) 63 64 binaries = [] # list of (<label>, <path>, [<options>]) 65 for i, q in enumerate(args.env): 66 name_path = q.split(':') 67 if len(name_path) == 1: 68 label = f'q{i}' 69 path_opts = name_path[0].split(',') 70 else: 71 assert len(name_path) == 2 # paths with colon not supported 72 label = name_path[0] 73 path_opts = name_path[1].split(',') 74 75 binaries.append((label, path_opts[0], path_opts[1:])) 76 77 test_envs = [] 78 79 bin_paths = {} 80 for i, q in enumerate(args.env): 81 opts = q.split(',') 82 label_path = opts[0] 83 opts = opts[1:] 84 85 if ':' in label_path: 86 # path with colon inside is not supported 87 label, path = label_path.split(':') 88 bin_paths[label] = path 89 elif label_path in bin_paths: 90 label = label_path 91 path = bin_paths[label] 92 else: 93 path = label_path 94 label = f'q{i}' 95 bin_paths[label] = path 96 97 x_perf = {} 98 is_mirror = False 99 for opt in opts: 100 if opt == 'mirror': 101 is_mirror = True 102 elif opt == 'copy-range=on': 103 x_perf['use-copy-range'] = True 104 elif opt == 'copy-range=off': 105 x_perf['use-copy-range'] = False 106 elif opt.startswith('max-workers='): 107 x_perf['max-workers'] = int(opt.split('=')[1]) 108 109 if is_mirror: 110 assert not x_perf 111 test_envs.append({ 112 'id': f'mirror({label})', 113 'cmd': 'blockdev-mirror', 114 'qemu-binary': path 115 }) 116 else: 117 test_envs.append({ 118 'id': f'backup({label})\n' + '\n'.join(opts), 119 'cmd': 'blockdev-backup', 120 'cmd-options': {'x-perf': x_perf} if x_perf else {}, 121 'qemu-binary': path 122 }) 123 124 result = simplebench.bench(bench_func, test_envs, test_cases, count=3) 125 with open('results.json', 'w') as f: 126 json.dump(result, f, indent=4) 127 print(results_to_text(result)) 128 129 130class ExtendAction(argparse.Action): 131 def __call__(self, parser, namespace, values, option_string=None): 132 items = getattr(namespace, self.dest) or [] 133 items.extend(values) 134 setattr(namespace, self.dest, items) 135 136 137if __name__ == '__main__': 138 p = argparse.ArgumentParser('Backup benchmark', epilog=''' 139ENV format 140 141 (LABEL:PATH|LABEL|PATH)[,max-workers=N][,use-copy-range=(on|off)][,mirror] 142 143 LABEL short name for the binary 144 PATH path to the binary 145 max-workers set x-perf.max-workers of backup job 146 use-copy-range set x-perf.use-copy-range of backup job 147 mirror use mirror job instead of backup''', 148 formatter_class=argparse.RawTextHelpFormatter) 149 p.add_argument('--env', nargs='+', help='''\ 150Qemu binaries with labels and options, see below 151"ENV format" section''', 152 action=ExtendAction) 153 p.add_argument('--dir', nargs='+', help='''\ 154Directories, each containing "test-source" and/or 155"test-target" files, raw images to used in 156benchmarking. File path with label, like 157label:/path/to/directory''', 158 action=ExtendAction) 159 p.add_argument('--nbd', help='''\ 160host:port for remote NBD image, (or just host, for 161default port 10809). Use it in tests, label is "nbd" 162(but you cannot create test nbd:nbd).''') 163 p.add_argument('--test', nargs='+', help='''\ 164Tests, in form source-dir-label:target-dir-label''', 165 action=ExtendAction) 166 167 bench(p.parse_args()) 168