1# 2# Migration test command line shell integration 3# 4# Copyright (c) 2016 Red Hat, Inc. 5# 6# This library is free software; you can redistribute it and/or 7# modify it under the terms of the GNU Lesser General Public 8# License as published by the Free Software Foundation; either 9# version 2 of the License, or (at your option) any later version. 10# 11# This library is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14# Lesser General Public License for more details. 15# 16# You should have received a copy of the GNU Lesser General Public 17# License along with this library; if not, see <http://www.gnu.org/licenses/>. 18# 19 20 21import argparse 22import fnmatch 23import os 24import os.path 25import platform 26import sys 27 28from guestperf.hardware import Hardware 29from guestperf.engine import Engine 30from guestperf.scenario import Scenario 31from guestperf.comparison import COMPARISONS 32from guestperf.plot import Plot 33from guestperf.report import Report 34 35 36class BaseShell(object): 37 38 def __init__(self): 39 parser = argparse.ArgumentParser(description="Migration Test Tool") 40 41 # Test args 42 parser.add_argument("--debug", dest="debug", default=False, action="store_true") 43 parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") 44 parser.add_argument("--sleep", dest="sleep", default=15, type=int) 45 parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") 46 parser.add_argument("--dst-host", dest="dst_host", default="localhost") 47 parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) 48 parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img") 49 parser.add_argument("--transport", dest="transport", default="unix") 50 51 52 # Hardware args 53 parser.add_argument("--cpus", dest="cpus", default=1, type=int) 54 parser.add_argument("--mem", dest="mem", default=1, type=int) 55 parser.add_argument("--src-cpu-bind", dest="src_cpu_bind", default="") 56 parser.add_argument("--src-mem-bind", dest="src_mem_bind", default="") 57 parser.add_argument("--dst-cpu-bind", dest="dst_cpu_bind", default="") 58 parser.add_argument("--dst-mem-bind", dest="dst_mem_bind", default="") 59 parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False) 60 parser.add_argument("--huge-pages", dest="huge_pages", default=False) 61 parser.add_argument("--locked-pages", dest="locked_pages", default=False) 62 63 self._parser = parser 64 65 def get_engine(self, args): 66 return Engine(binary=args.binary, 67 dst_host=args.dst_host, 68 kernel=args.kernel, 69 initrd=args.initrd, 70 transport=args.transport, 71 sleep=args.sleep, 72 debug=args.debug, 73 verbose=args.verbose) 74 75 def get_hardware(self, args): 76 def split_map(value): 77 if value == "": 78 return [] 79 return value.split(",") 80 81 return Hardware(cpus=args.cpus, 82 mem=args.mem, 83 84 src_cpu_bind=split_map(args.src_cpu_bind), 85 src_mem_bind=split_map(args.src_mem_bind), 86 dst_cpu_bind=split_map(args.dst_cpu_bind), 87 dst_mem_bind=split_map(args.dst_mem_bind), 88 89 locked_pages=args.locked_pages, 90 huge_pages=args.huge_pages, 91 prealloc_pages=args.prealloc_pages) 92 93 94class Shell(BaseShell): 95 96 def __init__(self): 97 super(Shell, self).__init__() 98 99 parser = self._parser 100 101 parser.add_argument("--output", dest="output", default=None) 102 103 # Scenario args 104 parser.add_argument("--max-iters", dest="max_iters", default=30, type=int) 105 parser.add_argument("--max-time", dest="max_time", default=300, type=int) 106 parser.add_argument("--bandwidth", dest="bandwidth", default=125000, type=int) 107 parser.add_argument("--downtime", dest="downtime", default=500, type=int) 108 109 parser.add_argument("--pause", dest="pause", default=False, action="store_true") 110 parser.add_argument("--pause-iters", dest="pause_iters", default=5, type=int) 111 112 parser.add_argument("--post-copy", dest="post_copy", default=False, action="store_true") 113 parser.add_argument("--post-copy-iters", dest="post_copy_iters", default=5, type=int) 114 115 parser.add_argument("--auto-converge", dest="auto_converge", default=False, action="store_true") 116 parser.add_argument("--auto-converge-step", dest="auto_converge_step", default=10, type=int) 117 118 parser.add_argument("--compression-mt", dest="compression_mt", default=False, action="store_true") 119 parser.add_argument("--compression-mt-threads", dest="compression_mt_threads", default=1, type=int) 120 121 parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true") 122 parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int) 123 124 def get_scenario(self, args): 125 return Scenario(name="perfreport", 126 downtime=args.downtime, 127 bandwidth=args.bandwidth, 128 max_iters=args.max_iters, 129 max_time=args.max_time, 130 131 pause=args.pause, 132 pause_iters=args.pause_iters, 133 134 post_copy=args.post_copy, 135 post_copy_iters=args.post_copy_iters, 136 137 auto_converge=args.auto_converge, 138 auto_converge_step=args.auto_converge_step, 139 140 compression_mt=args.compression_mt, 141 compression_mt_threads=args.compression_mt_threads, 142 143 compression_xbzrle=args.compression_xbzrle, 144 compression_xbzrle_cache=args.compression_xbzrle_cache) 145 146 def run(self, argv): 147 args = self._parser.parse_args(argv) 148 149 engine = self.get_engine(args) 150 hardware = self.get_hardware(args) 151 scenario = self.get_scenario(args) 152 153 try: 154 report = engine.run(hardware, scenario) 155 if args.output is None: 156 print report.to_json() 157 else: 158 with open(args.output, "w") as fh: 159 print >>fh, report.to_json() 160 return 0 161 except Exception as e: 162 print >>sys.stderr, "Error: %s" % str(e) 163 if args.debug: 164 raise 165 return 1 166 167 168class BatchShell(BaseShell): 169 170 def __init__(self): 171 super(BatchShell, self).__init__() 172 173 parser = self._parser 174 175 parser.add_argument("--filter", dest="filter", default="*") 176 parser.add_argument("--output", dest="output", default=os.getcwd()) 177 178 def run(self, argv): 179 args = self._parser.parse_args(argv) 180 181 engine = self.get_engine(args) 182 hardware = self.get_hardware(args) 183 184 try: 185 for comparison in COMPARISONS: 186 compdir = os.path.join(args.output, comparison._name) 187 for scenario in comparison._scenarios: 188 name = os.path.join(comparison._name, scenario._name) 189 if not fnmatch.fnmatch(name, args.filter): 190 if args.verbose: 191 print "Skipping %s" % name 192 continue 193 194 if args.verbose: 195 print "Running %s" % name 196 197 dirname = os.path.join(args.output, comparison._name) 198 filename = os.path.join(dirname, scenario._name + ".json") 199 if not os.path.exists(dirname): 200 os.makedirs(dirname) 201 report = engine.run(hardware, scenario) 202 with open(filename, "w") as fh: 203 print >>fh, report.to_json() 204 except Exception as e: 205 print >>sys.stderr, "Error: %s" % str(e) 206 if args.debug: 207 raise 208 209 210class PlotShell(object): 211 212 def __init__(self): 213 super(PlotShell, self).__init__() 214 215 self._parser = argparse.ArgumentParser(description="Migration Test Tool") 216 217 self._parser.add_argument("--output", dest="output", default=None) 218 219 self._parser.add_argument("--debug", dest="debug", default=False, action="store_true") 220 self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true") 221 222 self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true") 223 self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true") 224 self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true") 225 self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true") 226 self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true") 227 228 self._parser.add_argument("reports", nargs='*') 229 230 def run(self, argv): 231 args = self._parser.parse_args(argv) 232 233 if len(args.reports) == 0: 234 print >>sys.stderr, "At least one report required" 235 return 1 236 237 if not (args.qemu_cpu or 238 args.vcpu_cpu or 239 args.total_guest_cpu or 240 args.split_guest_cpu): 241 print >>sys.stderr, "At least one chart type is required" 242 return 1 243 244 reports = [] 245 for report in args.reports: 246 reports.append(Report.from_json_file(report)) 247 248 plot = Plot(reports, 249 args.migration_iters, 250 args.total_guest_cpu, 251 args.split_guest_cpu, 252 args.qemu_cpu, 253 args.vcpu_cpu) 254 255 plot.generate(args.output) 256