qemu/tests/migration/guestperf/shell.py
<<
>>
Prefs
   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.1 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
  27import logging
  28
  29from guestperf.hardware import Hardware
  30from guestperf.engine import Engine
  31from guestperf.scenario import Scenario
  32from guestperf.comparison import COMPARISONS
  33from guestperf.plot import Plot
  34from guestperf.report import Report
  35
  36
  37class BaseShell(object):
  38
  39    def __init__(self):
  40        parser = argparse.ArgumentParser(description="Migration Test Tool")
  41
  42        # Test args
  43        parser.add_argument("--debug", dest="debug", default=False, action="store_true")
  44        parser.add_argument("--verbose", dest="verbose", default=False, action="store_true")
  45        parser.add_argument("--sleep", dest="sleep", default=15, type=int)
  46        parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64")
  47        parser.add_argument("--dst-host", dest="dst_host", default="localhost")
  48        parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release())
  49        parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img")
  50        parser.add_argument("--transport", dest="transport", default="unix")
  51
  52
  53        # Hardware args
  54        parser.add_argument("--cpus", dest="cpus", default=1, type=int)
  55        parser.add_argument("--mem", dest="mem", default=1, type=int)
  56        parser.add_argument("--src-cpu-bind", dest="src_cpu_bind", default="")
  57        parser.add_argument("--src-mem-bind", dest="src_mem_bind", default="")
  58        parser.add_argument("--dst-cpu-bind", dest="dst_cpu_bind", default="")
  59        parser.add_argument("--dst-mem-bind", dest="dst_mem_bind", default="")
  60        parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False)
  61        parser.add_argument("--huge-pages", dest="huge_pages", default=False)
  62        parser.add_argument("--locked-pages", dest="locked_pages", default=False)
  63
  64        self._parser = parser
  65
  66    def get_engine(self, args):
  67        return Engine(binary=args.binary,
  68                      dst_host=args.dst_host,
  69                      kernel=args.kernel,
  70                      initrd=args.initrd,
  71                      transport=args.transport,
  72                      sleep=args.sleep,
  73                      debug=args.debug,
  74                      verbose=args.verbose)
  75
  76    def get_hardware(self, args):
  77        def split_map(value):
  78            if value == "":
  79                return []
  80            return value.split(",")
  81
  82        return Hardware(cpus=args.cpus,
  83                        mem=args.mem,
  84
  85                        src_cpu_bind=split_map(args.src_cpu_bind),
  86                        src_mem_bind=split_map(args.src_mem_bind),
  87                        dst_cpu_bind=split_map(args.dst_cpu_bind),
  88                        dst_mem_bind=split_map(args.dst_mem_bind),
  89
  90                        locked_pages=args.locked_pages,
  91                        huge_pages=args.huge_pages,
  92                        prealloc_pages=args.prealloc_pages)
  93
  94
  95class Shell(BaseShell):
  96
  97    def __init__(self):
  98        super(Shell, self).__init__()
  99
 100        parser = self._parser
 101
 102        parser.add_argument("--output", dest="output", default=None)
 103
 104        # Scenario args
 105        parser.add_argument("--max-iters", dest="max_iters", default=30, type=int)
 106        parser.add_argument("--max-time", dest="max_time", default=300, type=int)
 107        parser.add_argument("--bandwidth", dest="bandwidth", default=125000, type=int)
 108        parser.add_argument("--downtime", dest="downtime", default=500, type=int)
 109
 110        parser.add_argument("--pause", dest="pause", default=False, action="store_true")
 111        parser.add_argument("--pause-iters", dest="pause_iters", default=5, type=int)
 112
 113        parser.add_argument("--post-copy", dest="post_copy", default=False, action="store_true")
 114        parser.add_argument("--post-copy-iters", dest="post_copy_iters", default=5, type=int)
 115
 116        parser.add_argument("--auto-converge", dest="auto_converge", default=False, action="store_true")
 117        parser.add_argument("--auto-converge-step", dest="auto_converge_step", default=10, type=int)
 118
 119        parser.add_argument("--compression-mt", dest="compression_mt", default=False, action="store_true")
 120        parser.add_argument("--compression-mt-threads", dest="compression_mt_threads", default=1, type=int)
 121
 122        parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true")
 123        parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int)
 124
 125    def get_scenario(self, args):
 126        return Scenario(name="perfreport",
 127                        downtime=args.downtime,
 128                        bandwidth=args.bandwidth,
 129                        max_iters=args.max_iters,
 130                        max_time=args.max_time,
 131
 132                        pause=args.pause,
 133                        pause_iters=args.pause_iters,
 134
 135                        post_copy=args.post_copy,
 136                        post_copy_iters=args.post_copy_iters,
 137
 138                        auto_converge=args.auto_converge,
 139                        auto_converge_step=args.auto_converge_step,
 140
 141                        compression_mt=args.compression_mt,
 142                        compression_mt_threads=args.compression_mt_threads,
 143
 144                        compression_xbzrle=args.compression_xbzrle,
 145                        compression_xbzrle_cache=args.compression_xbzrle_cache)
 146
 147    def run(self, argv):
 148        args = self._parser.parse_args(argv)
 149        logging.basicConfig(level=(logging.DEBUG if args.debug else
 150                                   logging.INFO if args.verbose else
 151                                   logging.WARN))
 152
 153
 154        engine = self.get_engine(args)
 155        hardware = self.get_hardware(args)
 156        scenario = self.get_scenario(args)
 157
 158        try:
 159            report = engine.run(hardware, scenario)
 160            if args.output is None:
 161                print(report.to_json())
 162            else:
 163                with open(args.output, "w") as fh:
 164                    print(report.to_json(), file=fh)
 165            return 0
 166        except Exception as e:
 167            print("Error: %s" % str(e), file=sys.stderr)
 168            if args.debug:
 169                raise
 170            return 1
 171
 172
 173class BatchShell(BaseShell):
 174
 175    def __init__(self):
 176        super(BatchShell, self).__init__()
 177
 178        parser = self._parser
 179
 180        parser.add_argument("--filter", dest="filter", default="*")
 181        parser.add_argument("--output", dest="output", default=os.getcwd())
 182
 183    def run(self, argv):
 184        args = self._parser.parse_args(argv)
 185        logging.basicConfig(level=(logging.DEBUG if args.debug else
 186                                   logging.INFO if args.verbose else
 187                                   logging.WARN))
 188
 189
 190        engine = self.get_engine(args)
 191        hardware = self.get_hardware(args)
 192
 193        try:
 194            for comparison in COMPARISONS:
 195                compdir = os.path.join(args.output, comparison._name)
 196                for scenario in comparison._scenarios:
 197                    name = os.path.join(comparison._name, scenario._name)
 198                    if not fnmatch.fnmatch(name, args.filter):
 199                        if args.verbose:
 200                            print("Skipping %s" % name)
 201                        continue
 202
 203                    if args.verbose:
 204                        print("Running %s" % name)
 205
 206                    dirname = os.path.join(args.output, comparison._name)
 207                    filename = os.path.join(dirname, scenario._name + ".json")
 208                    if not os.path.exists(dirname):
 209                        os.makedirs(dirname)
 210                    report = engine.run(hardware, scenario)
 211                    with open(filename, "w") as fh:
 212                        print(report.to_json(), file=fh)
 213        except Exception as e:
 214            print("Error: %s" % str(e), file=sys.stderr)
 215            if args.debug:
 216                raise
 217
 218
 219class PlotShell(object):
 220
 221    def __init__(self):
 222        super(PlotShell, self).__init__()
 223
 224        self._parser = argparse.ArgumentParser(description="Migration Test Tool")
 225
 226        self._parser.add_argument("--output", dest="output", default=None)
 227
 228        self._parser.add_argument("--debug", dest="debug", default=False, action="store_true")
 229        self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true")
 230
 231        self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true")
 232        self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true")
 233        self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true")
 234        self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true")
 235        self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true")
 236
 237        self._parser.add_argument("reports", nargs='*')
 238
 239    def run(self, argv):
 240        args = self._parser.parse_args(argv)
 241        logging.basicConfig(level=(logging.DEBUG if args.debug else
 242                                   logging.INFO if args.verbose else
 243                                   logging.WARN))
 244
 245
 246        if len(args.reports) == 0:
 247            print("At least one report required", file=sys.stderr)
 248            return 1
 249
 250        if not (args.qemu_cpu or
 251                args.vcpu_cpu or
 252                args.total_guest_cpu or
 253                args.split_guest_cpu):
 254            print("At least one chart type is required", file=sys.stderr)
 255            return 1
 256
 257        reports = []
 258        for report in args.reports:
 259            reports.append(Report.from_json_file(report))
 260
 261        plot = Plot(reports,
 262                    args.migration_iters,
 263                    args.total_guest_cpu,
 264                    args.split_guest_cpu,
 265                    args.qemu_cpu,
 266                    args.vcpu_cpu)
 267
 268        plot.generate(args.output)
 269