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        parser.add_argument("--multifd", dest="multifd", default=False,
 126                            action="store_true")
 127        parser.add_argument("--multifd-channels", dest="multifd_channels",
 128                            default=2, type=int)
 129
 130    def get_scenario(self, args):
 131        return Scenario(name="perfreport",
 132                        downtime=args.downtime,
 133                        bandwidth=args.bandwidth,
 134                        max_iters=args.max_iters,
 135                        max_time=args.max_time,
 136
 137                        pause=args.pause,
 138                        pause_iters=args.pause_iters,
 139
 140                        post_copy=args.post_copy,
 141                        post_copy_iters=args.post_copy_iters,
 142
 143                        auto_converge=args.auto_converge,
 144                        auto_converge_step=args.auto_converge_step,
 145
 146                        compression_mt=args.compression_mt,
 147                        compression_mt_threads=args.compression_mt_threads,
 148
 149                        compression_xbzrle=args.compression_xbzrle,
 150                        compression_xbzrle_cache=args.compression_xbzrle_cache,
 151
 152                        multifd=args.multifd,
 153                        multifd_channels=args.multifd_channels)
 154
 155    def run(self, argv):
 156        args = self._parser.parse_args(argv)
 157        logging.basicConfig(level=(logging.DEBUG if args.debug else
 158                                   logging.INFO if args.verbose else
 159                                   logging.WARN))
 160
 161
 162        engine = self.get_engine(args)
 163        hardware = self.get_hardware(args)
 164        scenario = self.get_scenario(args)
 165
 166        try:
 167            report = engine.run(hardware, scenario)
 168            if args.output is None:
 169                print(report.to_json())
 170            else:
 171                with open(args.output, "w") as fh:
 172                    print(report.to_json(), file=fh)
 173            return 0
 174        except Exception as e:
 175            print("Error: %s" % str(e), file=sys.stderr)
 176            if args.debug:
 177                raise
 178            return 1
 179
 180
 181class BatchShell(BaseShell):
 182
 183    def __init__(self):
 184        super(BatchShell, self).__init__()
 185
 186        parser = self._parser
 187
 188        parser.add_argument("--filter", dest="filter", default="*")
 189        parser.add_argument("--output", dest="output", default=os.getcwd())
 190
 191    def run(self, argv):
 192        args = self._parser.parse_args(argv)
 193        logging.basicConfig(level=(logging.DEBUG if args.debug else
 194                                   logging.INFO if args.verbose else
 195                                   logging.WARN))
 196
 197
 198        engine = self.get_engine(args)
 199        hardware = self.get_hardware(args)
 200
 201        try:
 202            for comparison in COMPARISONS:
 203                compdir = os.path.join(args.output, comparison._name)
 204                for scenario in comparison._scenarios:
 205                    name = os.path.join(comparison._name, scenario._name)
 206                    if not fnmatch.fnmatch(name, args.filter):
 207                        if args.verbose:
 208                            print("Skipping %s" % name)
 209                        continue
 210
 211                    if args.verbose:
 212                        print("Running %s" % name)
 213
 214                    dirname = os.path.join(args.output, comparison._name)
 215                    filename = os.path.join(dirname, scenario._name + ".json")
 216                    if not os.path.exists(dirname):
 217                        os.makedirs(dirname)
 218                    report = engine.run(hardware, scenario)
 219                    with open(filename, "w") as fh:
 220                        print(report.to_json(), file=fh)
 221        except Exception as e:
 222            print("Error: %s" % str(e), file=sys.stderr)
 223            if args.debug:
 224                raise
 225
 226
 227class PlotShell(object):
 228
 229    def __init__(self):
 230        super(PlotShell, self).__init__()
 231
 232        self._parser = argparse.ArgumentParser(description="Migration Test Tool")
 233
 234        self._parser.add_argument("--output", dest="output", default=None)
 235
 236        self._parser.add_argument("--debug", dest="debug", default=False, action="store_true")
 237        self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true")
 238
 239        self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true")
 240        self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true")
 241        self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true")
 242        self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true")
 243        self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true")
 244
 245        self._parser.add_argument("reports", nargs='*')
 246
 247    def run(self, argv):
 248        args = self._parser.parse_args(argv)
 249        logging.basicConfig(level=(logging.DEBUG if args.debug else
 250                                   logging.INFO if args.verbose else
 251                                   logging.WARN))
 252
 253
 254        if len(args.reports) == 0:
 255            print("At least one report required", file=sys.stderr)
 256            return 1
 257
 258        if not (args.qemu_cpu or
 259                args.vcpu_cpu or
 260                args.total_guest_cpu or
 261                args.split_guest_cpu):
 262            print("At least one chart type is required", file=sys.stderr)
 263            return 1
 264
 265        reports = []
 266        for report in args.reports:
 267            reports.append(Report.from_json_file(report))
 268
 269        plot = Plot(reports,
 270                    args.migration_iters,
 271                    args.total_guest_cpu,
 272                    args.split_guest_cpu,
 273                    args.qemu_cpu,
 274                    args.vcpu_cpu)
 275
 276        plot.generate(args.output)
 277