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