qemu/tests/migration/guestperf/shell.py
Hyman 1c3baa1ac4 tests/migration: introduce multifd into guestperf
Guestperf tool does not cover the multifd-enabled migration
currently, it is worth supporting so that developers can
analysis the migration performance with all kinds of
migration.

To request that multifd is enabled, with 4 channels:
$ ./tests/migration/guestperf.py \
    --multifd --multifd-channels 4 --output output.json

To run the entire standardized set of multifd-enabled
comparisons, with unix migration:
$ ./tests/migration/guestperf-batch.py \
    --dst-host localhost --transport unix \
    --filter compr-multifd* --output outputdir

Signed-off-by: Hyman Huang(黄勇) <huangy81@chinatelecom.cn>
Message-Id: <cfeeb04d17ad932c42a9871294058b77429ad1b7.1616171924.git.huangy81@chinatelecom.cn>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
2021-05-13 18:21:14 +01:00

277 lines
11 KiB
Python

#
# Migration test command line shell integration
#
# Copyright (c) 2016 Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
#
import argparse
import fnmatch
import os
import os.path
import platform
import sys
import logging
from guestperf.hardware import Hardware
from guestperf.engine import Engine
from guestperf.scenario import Scenario
from guestperf.comparison import COMPARISONS
from guestperf.plot import Plot
from guestperf.report import Report
class BaseShell(object):
def __init__(self):
parser = argparse.ArgumentParser(description="Migration Test Tool")
# Test args
parser.add_argument("--debug", dest="debug", default=False, action="store_true")
parser.add_argument("--verbose", dest="verbose", default=False, action="store_true")
parser.add_argument("--sleep", dest="sleep", default=15, type=int)
parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64")
parser.add_argument("--dst-host", dest="dst_host", default="localhost")
parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release())
parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img")
parser.add_argument("--transport", dest="transport", default="unix")
# Hardware args
parser.add_argument("--cpus", dest="cpus", default=1, type=int)
parser.add_argument("--mem", dest="mem", default=1, type=int)
parser.add_argument("--src-cpu-bind", dest="src_cpu_bind", default="")
parser.add_argument("--src-mem-bind", dest="src_mem_bind", default="")
parser.add_argument("--dst-cpu-bind", dest="dst_cpu_bind", default="")
parser.add_argument("--dst-mem-bind", dest="dst_mem_bind", default="")
parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False)
parser.add_argument("--huge-pages", dest="huge_pages", default=False)
parser.add_argument("--locked-pages", dest="locked_pages", default=False)
self._parser = parser
def get_engine(self, args):
return Engine(binary=args.binary,
dst_host=args.dst_host,
kernel=args.kernel,
initrd=args.initrd,
transport=args.transport,
sleep=args.sleep,
debug=args.debug,
verbose=args.verbose)
def get_hardware(self, args):
def split_map(value):
if value == "":
return []
return value.split(",")
return Hardware(cpus=args.cpus,
mem=args.mem,
src_cpu_bind=split_map(args.src_cpu_bind),
src_mem_bind=split_map(args.src_mem_bind),
dst_cpu_bind=split_map(args.dst_cpu_bind),
dst_mem_bind=split_map(args.dst_mem_bind),
locked_pages=args.locked_pages,
huge_pages=args.huge_pages,
prealloc_pages=args.prealloc_pages)
class Shell(BaseShell):
def __init__(self):
super(Shell, self).__init__()
parser = self._parser
parser.add_argument("--output", dest="output", default=None)
# Scenario args
parser.add_argument("--max-iters", dest="max_iters", default=30, type=int)
parser.add_argument("--max-time", dest="max_time", default=300, type=int)
parser.add_argument("--bandwidth", dest="bandwidth", default=125000, type=int)
parser.add_argument("--downtime", dest="downtime", default=500, type=int)
parser.add_argument("--pause", dest="pause", default=False, action="store_true")
parser.add_argument("--pause-iters", dest="pause_iters", default=5, type=int)
parser.add_argument("--post-copy", dest="post_copy", default=False, action="store_true")
parser.add_argument("--post-copy-iters", dest="post_copy_iters", default=5, type=int)
parser.add_argument("--auto-converge", dest="auto_converge", default=False, action="store_true")
parser.add_argument("--auto-converge-step", dest="auto_converge_step", default=10, type=int)
parser.add_argument("--compression-mt", dest="compression_mt", default=False, action="store_true")
parser.add_argument("--compression-mt-threads", dest="compression_mt_threads", default=1, type=int)
parser.add_argument("--compression-xbzrle", dest="compression_xbzrle", default=False, action="store_true")
parser.add_argument("--compression-xbzrle-cache", dest="compression_xbzrle_cache", default=10, type=int)
parser.add_argument("--multifd", dest="multifd", default=False,
action="store_true")
parser.add_argument("--multifd-channels", dest="multifd_channels",
default=2, type=int)
def get_scenario(self, args):
return Scenario(name="perfreport",
downtime=args.downtime,
bandwidth=args.bandwidth,
max_iters=args.max_iters,
max_time=args.max_time,
pause=args.pause,
pause_iters=args.pause_iters,
post_copy=args.post_copy,
post_copy_iters=args.post_copy_iters,
auto_converge=args.auto_converge,
auto_converge_step=args.auto_converge_step,
compression_mt=args.compression_mt,
compression_mt_threads=args.compression_mt_threads,
compression_xbzrle=args.compression_xbzrle,
compression_xbzrle_cache=args.compression_xbzrle_cache,
multifd=args.multifd,
multifd_channels=args.multifd_channels)
def run(self, argv):
args = self._parser.parse_args(argv)
logging.basicConfig(level=(logging.DEBUG if args.debug else
logging.INFO if args.verbose else
logging.WARN))
engine = self.get_engine(args)
hardware = self.get_hardware(args)
scenario = self.get_scenario(args)
try:
report = engine.run(hardware, scenario)
if args.output is None:
print(report.to_json())
else:
with open(args.output, "w") as fh:
print(report.to_json(), file=fh)
return 0
except Exception as e:
print("Error: %s" % str(e), file=sys.stderr)
if args.debug:
raise
return 1
class BatchShell(BaseShell):
def __init__(self):
super(BatchShell, self).__init__()
parser = self._parser
parser.add_argument("--filter", dest="filter", default="*")
parser.add_argument("--output", dest="output", default=os.getcwd())
def run(self, argv):
args = self._parser.parse_args(argv)
logging.basicConfig(level=(logging.DEBUG if args.debug else
logging.INFO if args.verbose else
logging.WARN))
engine = self.get_engine(args)
hardware = self.get_hardware(args)
try:
for comparison in COMPARISONS:
compdir = os.path.join(args.output, comparison._name)
for scenario in comparison._scenarios:
name = os.path.join(comparison._name, scenario._name)
if not fnmatch.fnmatch(name, args.filter):
if args.verbose:
print("Skipping %s" % name)
continue
if args.verbose:
print("Running %s" % name)
dirname = os.path.join(args.output, comparison._name)
filename = os.path.join(dirname, scenario._name + ".json")
if not os.path.exists(dirname):
os.makedirs(dirname)
report = engine.run(hardware, scenario)
with open(filename, "w") as fh:
print(report.to_json(), file=fh)
except Exception as e:
print("Error: %s" % str(e), file=sys.stderr)
if args.debug:
raise
class PlotShell(object):
def __init__(self):
super(PlotShell, self).__init__()
self._parser = argparse.ArgumentParser(description="Migration Test Tool")
self._parser.add_argument("--output", dest="output", default=None)
self._parser.add_argument("--debug", dest="debug", default=False, action="store_true")
self._parser.add_argument("--verbose", dest="verbose", default=False, action="store_true")
self._parser.add_argument("--migration-iters", dest="migration_iters", default=False, action="store_true")
self._parser.add_argument("--total-guest-cpu", dest="total_guest_cpu", default=False, action="store_true")
self._parser.add_argument("--split-guest-cpu", dest="split_guest_cpu", default=False, action="store_true")
self._parser.add_argument("--qemu-cpu", dest="qemu_cpu", default=False, action="store_true")
self._parser.add_argument("--vcpu-cpu", dest="vcpu_cpu", default=False, action="store_true")
self._parser.add_argument("reports", nargs='*')
def run(self, argv):
args = self._parser.parse_args(argv)
logging.basicConfig(level=(logging.DEBUG if args.debug else
logging.INFO if args.verbose else
logging.WARN))
if len(args.reports) == 0:
print("At least one report required", file=sys.stderr)
return 1
if not (args.qemu_cpu or
args.vcpu_cpu or
args.total_guest_cpu or
args.split_guest_cpu):
print("At least one chart type is required", file=sys.stderr)
return 1
reports = []
for report in args.reports:
reports.append(Report.from_json_file(report))
plot = Plot(reports,
args.migration_iters,
args.total_guest_cpu,
args.split_guest_cpu,
args.qemu_cpu,
args.vcpu_cpu)
plot.generate(args.output)