1409437e1SDaniel P. Berrange# 2409437e1SDaniel P. Berrange# Migration test command line shell integration 3409437e1SDaniel P. Berrange# 4409437e1SDaniel P. Berrange# Copyright (c) 2016 Red Hat, Inc. 5409437e1SDaniel P. Berrange# 6409437e1SDaniel P. Berrange# This library is free software; you can redistribute it and/or 7409437e1SDaniel P. Berrange# modify it under the terms of the GNU Lesser General Public 8409437e1SDaniel P. Berrange# License as published by the Free Software Foundation; either 93a645d36SGan Qixin# version 2.1 of the License, or (at your option) any later version. 10409437e1SDaniel P. Berrange# 11409437e1SDaniel P. Berrange# This library is distributed in the hope that it will be useful, 12409437e1SDaniel P. Berrange# but WITHOUT ANY WARRANTY; without even the implied warranty of 13409437e1SDaniel P. Berrange# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14409437e1SDaniel P. Berrange# Lesser General Public License for more details. 15409437e1SDaniel P. Berrange# 16409437e1SDaniel P. Berrange# You should have received a copy of the GNU Lesser General Public 17409437e1SDaniel P. Berrange# License along with this library; if not, see <http://www.gnu.org/licenses/>. 18409437e1SDaniel P. Berrange# 19409437e1SDaniel P. Berrange 20409437e1SDaniel P. Berrange 210ea47d0fSStefan Hajnocziimport argparse 220ea47d0fSStefan Hajnocziimport fnmatch 23afb62496SEduardo Habkostimport os 24afb62496SEduardo Habkostimport os.path 250ea47d0fSStefan Hajnocziimport platform 26afb62496SEduardo Habkostimport sys 278af09b80SEduardo Habkostimport logging 28409437e1SDaniel P. Berrange 29409437e1SDaniel P. Berrangefrom guestperf.hardware import Hardware 30409437e1SDaniel P. Berrangefrom guestperf.engine import Engine 31409437e1SDaniel P. Berrangefrom guestperf.scenario import Scenario 32409437e1SDaniel P. Berrangefrom guestperf.comparison import COMPARISONS 33409437e1SDaniel P. Berrangefrom guestperf.plot import Plot 34409437e1SDaniel P. Berrangefrom guestperf.report import Report 35409437e1SDaniel P. Berrange 36409437e1SDaniel P. Berrange 37409437e1SDaniel P. Berrangeclass BaseShell(object): 38409437e1SDaniel P. Berrange 39409437e1SDaniel P. Berrange def __init__(self): 40409437e1SDaniel P. Berrange parser = argparse.ArgumentParser(description="Migration Test Tool") 41409437e1SDaniel P. Berrange 42409437e1SDaniel P. Berrange # Test args 43409437e1SDaniel P. Berrange parser.add_argument("--debug", dest="debug", default=False, action="store_true") 44409437e1SDaniel P. Berrange parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") 45409437e1SDaniel P. Berrange parser.add_argument("--sleep", dest="sleep", default=15, type=int) 46409437e1SDaniel P. Berrange parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") 47409437e1SDaniel P. Berrange parser.add_argument("--dst-host", dest="dst_host", default="localhost") 48409437e1SDaniel P. Berrange parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) 49409437e1SDaniel P. Berrange parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img") 50409437e1SDaniel P. Berrange parser.add_argument("--transport", dest="transport", default="unix") 51409437e1SDaniel P. Berrange 52409437e1SDaniel P. Berrange 53409437e1SDaniel P. Berrange # Hardware args 54409437e1SDaniel P. Berrange parser.add_argument("--cpus", dest="cpus", default=1, type=int) 55409437e1SDaniel P. Berrange parser.add_argument("--mem", dest="mem", default=1, type=int) 56409437e1SDaniel P. Berrange parser.add_argument("--src-cpu-bind", dest="src_cpu_bind", default="") 57409437e1SDaniel P. Berrange parser.add_argument("--src-mem-bind", dest="src_mem_bind", default="") 58409437e1SDaniel P. Berrange parser.add_argument("--dst-cpu-bind", dest="dst_cpu_bind", default="") 59409437e1SDaniel P. Berrange parser.add_argument("--dst-mem-bind", dest="dst_mem_bind", default="") 60409437e1SDaniel P. Berrange parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False) 61409437e1SDaniel P. Berrange parser.add_argument("--huge-pages", dest="huge_pages", default=False) 62409437e1SDaniel P. Berrange parser.add_argument("--locked-pages", dest="locked_pages", default=False) 634cc563d4SHyman Huang parser.add_argument("--dirty-ring-size", dest="dirty_ring_size", 644cc563d4SHyman Huang default=0, type=int) 65409437e1SDaniel P. Berrange 66409437e1SDaniel P. Berrange self._parser = parser 67409437e1SDaniel P. Berrange 68409437e1SDaniel P. Berrange def get_engine(self, args): 69409437e1SDaniel P. Berrange return Engine(binary=args.binary, 70409437e1SDaniel P. Berrange dst_host=args.dst_host, 71409437e1SDaniel P. Berrange kernel=args.kernel, 72409437e1SDaniel P. Berrange initrd=args.initrd, 73409437e1SDaniel P. Berrange transport=args.transport, 74409437e1SDaniel P. Berrange sleep=args.sleep, 75409437e1SDaniel P. Berrange debug=args.debug, 76409437e1SDaniel P. Berrange verbose=args.verbose) 77409437e1SDaniel P. Berrange 78409437e1SDaniel P. Berrange def get_hardware(self, args): 79409437e1SDaniel P. Berrange def split_map(value): 80409437e1SDaniel P. Berrange if value == "": 81409437e1SDaniel P. Berrange return [] 82409437e1SDaniel P. Berrange return value.split(",") 83409437e1SDaniel P. Berrange 84409437e1SDaniel P. Berrange return Hardware(cpus=args.cpus, 85409437e1SDaniel P. Berrange mem=args.mem, 86409437e1SDaniel P. Berrange 87409437e1SDaniel P. Berrange src_cpu_bind=split_map(args.src_cpu_bind), 88409437e1SDaniel P. Berrange src_mem_bind=split_map(args.src_mem_bind), 89409437e1SDaniel P. Berrange dst_cpu_bind=split_map(args.dst_cpu_bind), 90409437e1SDaniel P. Berrange dst_mem_bind=split_map(args.dst_mem_bind), 91409437e1SDaniel P. Berrange 92409437e1SDaniel P. Berrange locked_pages=args.locked_pages, 93409437e1SDaniel P. Berrange huge_pages=args.huge_pages, 944cc563d4SHyman Huang prealloc_pages=args.prealloc_pages, 954cc563d4SHyman Huang 964cc563d4SHyman Huang dirty_ring_size=args.dirty_ring_size) 97409437e1SDaniel P. Berrange 98409437e1SDaniel P. Berrange 99409437e1SDaniel P. Berrangeclass Shell(BaseShell): 100409437e1SDaniel P. Berrange 101409437e1SDaniel P. Berrange def __init__(self): 102409437e1SDaniel P. Berrange super(Shell, self).__init__() 103409437e1SDaniel P. Berrange 104409437e1SDaniel P. Berrange parser = self._parser 105409437e1SDaniel P. Berrange 106409437e1SDaniel P. Berrange parser.add_argument("--output", dest="output", default=None) 107409437e1SDaniel P. Berrange 108409437e1SDaniel P. Berrange # Scenario args 109409437e1SDaniel P. Berrange parser.add_argument("--max-iters", dest="max_iters", default=30, type=int) 110409437e1SDaniel P. Berrange parser.add_argument("--max-time", dest="max_time", default=300, type=int) 111409437e1SDaniel P. Berrange parser.add_argument("--bandwidth", dest="bandwidth", default=125000, type=int) 112409437e1SDaniel P. Berrange parser.add_argument("--downtime", dest="downtime", default=500, type=int) 113409437e1SDaniel P. Berrange 114409437e1SDaniel P. Berrange parser.add_argument("--pause", dest="pause", default=False, action="store_true") 115409437e1SDaniel P. Berrange parser.add_argument("--pause-iters", dest="pause_iters", default=5, type=int) 116409437e1SDaniel P. Berrange 117409437e1SDaniel P. Berrange parser.add_argument("--post-copy", dest="post_copy", default=False, action="store_true") 118409437e1SDaniel P. Berrange parser.add_argument("--post-copy-iters", dest="post_copy_iters", default=5, type=int) 119409437e1SDaniel P. Berrange 120409437e1SDaniel P. Berrange parser.add_argument("--auto-converge", dest="auto_converge", default=False, action="store_true") 121409437e1SDaniel P. Berrange parser.add_argument("--auto-converge-step", dest="auto_converge_step", default=10, type=int) 122409437e1SDaniel P. Berrange 123409437e1SDaniel P. Berrange parser.add_argument("--compression-mt", dest="compression_mt", default=False, action="store_true") 124409437e1SDaniel P. Berrange parser.add_argument("--compression-mt-threads", dest="compression_mt_threads", default=1, type=int) 125409437e1SDaniel P. Berrange 126409437e1SDaniel P. Berrange parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true") 127409437e1SDaniel P. Berrange parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int) 128409437e1SDaniel P. Berrange 1291c3baa1aSHyman parser.add_argument("--multifd", dest="multifd", default=False, 1301c3baa1aSHyman action="store_true") 1311c3baa1aSHyman parser.add_argument("--multifd-channels", dest="multifd_channels", 1321c3baa1aSHyman default=2, type=int) 1331c3baa1aSHyman 134*22b7cb2cSHyman Huang parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, 135*22b7cb2cSHyman Huang action="store_true") 136*22b7cb2cSHyman Huang 137*22b7cb2cSHyman Huang parser.add_argument("--x-vcpu-dirty-limit-period", 138*22b7cb2cSHyman Huang dest="x_vcpu_dirty_limit_period", 139*22b7cb2cSHyman Huang default=500, type=int) 140*22b7cb2cSHyman Huang 141*22b7cb2cSHyman Huang parser.add_argument("--vcpu-dirty-limit", 142*22b7cb2cSHyman Huang dest="vcpu_dirty_limit", 143*22b7cb2cSHyman Huang default=1, type=int) 144*22b7cb2cSHyman Huang 145409437e1SDaniel P. Berrange def get_scenario(self, args): 146409437e1SDaniel P. Berrange return Scenario(name="perfreport", 147409437e1SDaniel P. Berrange downtime=args.downtime, 148409437e1SDaniel P. Berrange bandwidth=args.bandwidth, 149409437e1SDaniel P. Berrange max_iters=args.max_iters, 150409437e1SDaniel P. Berrange max_time=args.max_time, 151409437e1SDaniel P. Berrange 152409437e1SDaniel P. Berrange pause=args.pause, 153409437e1SDaniel P. Berrange pause_iters=args.pause_iters, 154409437e1SDaniel P. Berrange 155409437e1SDaniel P. Berrange post_copy=args.post_copy, 156409437e1SDaniel P. Berrange post_copy_iters=args.post_copy_iters, 157409437e1SDaniel P. Berrange 158409437e1SDaniel P. Berrange auto_converge=args.auto_converge, 159409437e1SDaniel P. Berrange auto_converge_step=args.auto_converge_step, 160409437e1SDaniel P. Berrange 161409437e1SDaniel P. Berrange compression_mt=args.compression_mt, 162409437e1SDaniel P. Berrange compression_mt_threads=args.compression_mt_threads, 163409437e1SDaniel P. Berrange 164409437e1SDaniel P. Berrange compression_xbzrle=args.compression_xbzrle, 1651c3baa1aSHyman compression_xbzrle_cache=args.compression_xbzrle_cache, 1661c3baa1aSHyman 1671c3baa1aSHyman multifd=args.multifd, 168*22b7cb2cSHyman Huang multifd_channels=args.multifd_channels, 169*22b7cb2cSHyman Huang 170*22b7cb2cSHyman Huang dirty_limit=args.dirty_limit, 171*22b7cb2cSHyman Huang x_vcpu_dirty_limit_period=\ 172*22b7cb2cSHyman Huang args.x_vcpu_dirty_limit_period, 173*22b7cb2cSHyman Huang vcpu_dirty_limit=args.vcpu_dirty_limit) 174409437e1SDaniel P. Berrange 175409437e1SDaniel P. Berrange def run(self, argv): 176409437e1SDaniel P. Berrange args = self._parser.parse_args(argv) 1778af09b80SEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug else 1788af09b80SEduardo Habkost logging.INFO if args.verbose else 1798af09b80SEduardo Habkost logging.WARN)) 1808af09b80SEduardo Habkost 181409437e1SDaniel P. Berrange 182409437e1SDaniel P. Berrange engine = self.get_engine(args) 183409437e1SDaniel P. Berrange hardware = self.get_hardware(args) 184409437e1SDaniel P. Berrange scenario = self.get_scenario(args) 185409437e1SDaniel P. Berrange 186409437e1SDaniel P. Berrange try: 187409437e1SDaniel P. Berrange report = engine.run(hardware, scenario) 188409437e1SDaniel P. Berrange if args.output is None: 189f03868bdSEduardo Habkost print(report.to_json()) 190409437e1SDaniel P. Berrange else: 191409437e1SDaniel P. Berrange with open(args.output, "w") as fh: 192f03868bdSEduardo Habkost print(report.to_json(), file=fh) 193409437e1SDaniel P. Berrange return 0 194409437e1SDaniel P. Berrange except Exception as e: 195f03868bdSEduardo Habkost print("Error: %s" % str(e), file=sys.stderr) 196409437e1SDaniel P. Berrange if args.debug: 197409437e1SDaniel P. Berrange raise 198409437e1SDaniel P. Berrange return 1 199409437e1SDaniel P. Berrange 200409437e1SDaniel P. Berrange 201409437e1SDaniel P. Berrangeclass BatchShell(BaseShell): 202409437e1SDaniel P. Berrange 203409437e1SDaniel P. Berrange def __init__(self): 204409437e1SDaniel P. Berrange super(BatchShell, self).__init__() 205409437e1SDaniel P. Berrange 206409437e1SDaniel P. Berrange parser = self._parser 207409437e1SDaniel P. Berrange 208409437e1SDaniel P. Berrange parser.add_argument("--filter", dest="filter", default="*") 209409437e1SDaniel P. Berrange parser.add_argument("--output", dest="output", default=os.getcwd()) 210409437e1SDaniel P. Berrange 211409437e1SDaniel P. Berrange def run(self, argv): 212409437e1SDaniel P. Berrange args = self._parser.parse_args(argv) 2138af09b80SEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug else 2148af09b80SEduardo Habkost logging.INFO if args.verbose else 2158af09b80SEduardo Habkost logging.WARN)) 2168af09b80SEduardo Habkost 217409437e1SDaniel P. Berrange 218409437e1SDaniel P. Berrange engine = self.get_engine(args) 219409437e1SDaniel P. Berrange hardware = self.get_hardware(args) 220409437e1SDaniel P. Berrange 221409437e1SDaniel P. Berrange try: 222409437e1SDaniel P. Berrange for comparison in COMPARISONS: 223409437e1SDaniel P. Berrange compdir = os.path.join(args.output, comparison._name) 224409437e1SDaniel P. Berrange for scenario in comparison._scenarios: 225409437e1SDaniel P. Berrange name = os.path.join(comparison._name, scenario._name) 226409437e1SDaniel P. Berrange if not fnmatch.fnmatch(name, args.filter): 227409437e1SDaniel P. Berrange if args.verbose: 228f03868bdSEduardo Habkost print("Skipping %s" % name) 229409437e1SDaniel P. Berrange continue 230409437e1SDaniel P. Berrange 231409437e1SDaniel P. Berrange if args.verbose: 232f03868bdSEduardo Habkost print("Running %s" % name) 233409437e1SDaniel P. Berrange 234409437e1SDaniel P. Berrange dirname = os.path.join(args.output, comparison._name) 235409437e1SDaniel P. Berrange filename = os.path.join(dirname, scenario._name + ".json") 236409437e1SDaniel P. Berrange if not os.path.exists(dirname): 237409437e1SDaniel P. Berrange os.makedirs(dirname) 238409437e1SDaniel P. Berrange report = engine.run(hardware, scenario) 239409437e1SDaniel P. Berrange with open(filename, "w") as fh: 240f03868bdSEduardo Habkost print(report.to_json(), file=fh) 241409437e1SDaniel P. Berrange except Exception as e: 242f03868bdSEduardo Habkost print("Error: %s" % str(e), file=sys.stderr) 243409437e1SDaniel P. Berrange if args.debug: 244409437e1SDaniel P. Berrange raise 245409437e1SDaniel P. Berrange 246409437e1SDaniel P. Berrange 247409437e1SDaniel P. Berrangeclass PlotShell(object): 248409437e1SDaniel P. Berrange 249409437e1SDaniel P. Berrange def __init__(self): 250409437e1SDaniel P. Berrange super(PlotShell, self).__init__() 251409437e1SDaniel P. Berrange 252409437e1SDaniel P. Berrange self._parser = argparse.ArgumentParser(description="Migration Test Tool") 253409437e1SDaniel P. Berrange 254409437e1SDaniel P. Berrange self._parser.add_argument("--output", dest="output", default=None) 255409437e1SDaniel P. Berrange 256409437e1SDaniel P. Berrange self._parser.add_argument("--debug", dest="debug", default=False, action="store_true") 257409437e1SDaniel P. Berrange self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") 258409437e1SDaniel P. Berrange 259409437e1SDaniel P. Berrange self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true") 260409437e1SDaniel P. Berrange self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true") 261409437e1SDaniel P. Berrange self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true") 262409437e1SDaniel P. Berrange self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true") 263409437e1SDaniel P. Berrange self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true") 264409437e1SDaniel P. Berrange 265409437e1SDaniel P. Berrange self._parser.add_argument("reports", nargs='*') 266409437e1SDaniel P. Berrange 267409437e1SDaniel P. Berrange def run(self, argv): 268409437e1SDaniel P. Berrange args = self._parser.parse_args(argv) 2698af09b80SEduardo Habkost logging.basicConfig(level=(logging.DEBUG if args.debug else 2708af09b80SEduardo Habkost logging.INFO if args.verbose else 2718af09b80SEduardo Habkost logging.WARN)) 2728af09b80SEduardo Habkost 273409437e1SDaniel P. Berrange 274409437e1SDaniel P. Berrange if len(args.reports) == 0: 275f03868bdSEduardo Habkost print("At least one report required", file=sys.stderr) 276409437e1SDaniel P. Berrange return 1 277409437e1SDaniel P. Berrange 278409437e1SDaniel P. Berrange if not (args.qemu_cpu or 279409437e1SDaniel P. Berrange args.vcpu_cpu or 280409437e1SDaniel P. Berrange args.total_guest_cpu or 281409437e1SDaniel P. Berrange args.split_guest_cpu): 282f03868bdSEduardo Habkost print("At least one chart type is required", file=sys.stderr) 283409437e1SDaniel P. Berrange return 1 284409437e1SDaniel P. Berrange 285409437e1SDaniel P. Berrange reports = [] 286409437e1SDaniel P. Berrange for report in args.reports: 287409437e1SDaniel P. Berrange reports.append(Report.from_json_file(report)) 288409437e1SDaniel P. Berrange 289409437e1SDaniel P. Berrange plot = Plot(reports, 290409437e1SDaniel P. Berrange args.migration_iters, 291409437e1SDaniel P. Berrange args.total_guest_cpu, 292409437e1SDaniel P. Berrange args.split_guest_cpu, 293409437e1SDaniel P. Berrange args.qemu_cpu, 294409437e1SDaniel P. Berrange args.vcpu_cpu) 295409437e1SDaniel P. Berrange 296409437e1SDaniel P. Berrange plot.generate(args.output) 297