xref: /openbmc/qemu/tests/migration/guestperf/plot.py (revision 683685e7)
1409437e1SDaniel P. Berrange#
2409437e1SDaniel P. Berrange# Migration test graph plotting
3409437e1SDaniel P. Berrange#
4409437e1SDaniel P. Berrange# Copyright (c) 2016 Red Hat, Inc.
5409437e1SDaniel P. Berrange#
6409437e1SDaniel P. Berrange# This library is free software; you can redistribute it and/or
7409437e1SDaniel P. Berrange# modify it under the terms of the GNU Lesser General Public
8409437e1SDaniel P. Berrange# License as published by the Free Software Foundation; either
9*3a645d36SGan Qixin# version 2.1 of the License, or (at your option) any later version.
10409437e1SDaniel P. Berrange#
11409437e1SDaniel P. Berrange# This library is distributed in the hope that it will be useful,
12409437e1SDaniel P. Berrange# but WITHOUT ANY WARRANTY; without even the implied warranty of
13409437e1SDaniel P. Berrange# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14409437e1SDaniel P. Berrange# Lesser General Public License for more details.
15409437e1SDaniel P. Berrange#
16409437e1SDaniel P. Berrange# You should have received a copy of the GNU Lesser General Public
17409437e1SDaniel P. Berrange# License along with this library; if not, see <http://www.gnu.org/licenses/>.
18409437e1SDaniel P. Berrange#
19409437e1SDaniel P. Berrange
20409437e1SDaniel P. Berrangeimport sys
21409437e1SDaniel P. Berrange
22409437e1SDaniel P. Berrange
23409437e1SDaniel P. Berrangeclass Plot(object):
24409437e1SDaniel P. Berrange
25409437e1SDaniel P. Berrange    # Generated using
26409437e1SDaniel P. Berrange    # http://tools.medialab.sciences-po.fr/iwanthue/
27409437e1SDaniel P. Berrange    COLORS = ["#CD54D0",
28409437e1SDaniel P. Berrange              "#79D94C",
29409437e1SDaniel P. Berrange              "#7470CD",
30409437e1SDaniel P. Berrange              "#D2D251",
31409437e1SDaniel P. Berrange              "#863D79",
32409437e1SDaniel P. Berrange              "#76DDA6",
33409437e1SDaniel P. Berrange              "#D4467B",
34409437e1SDaniel P. Berrange              "#61923D",
35409437e1SDaniel P. Berrange              "#CB9CCA",
36409437e1SDaniel P. Berrange              "#D98F36",
37409437e1SDaniel P. Berrange              "#8CC8DA",
38409437e1SDaniel P. Berrange              "#CE4831",
39409437e1SDaniel P. Berrange              "#5E7693",
40409437e1SDaniel P. Berrange              "#9B803F",
41409437e1SDaniel P. Berrange              "#412F4C",
42409437e1SDaniel P. Berrange              "#CECBA6",
43409437e1SDaniel P. Berrange              "#6D3229",
44409437e1SDaniel P. Berrange              "#598B73",
45409437e1SDaniel P. Berrange              "#C8827C",
46409437e1SDaniel P. Berrange              "#394427"]
47409437e1SDaniel P. Berrange
48409437e1SDaniel P. Berrange    def __init__(self,
49409437e1SDaniel P. Berrange                 reports,
50409437e1SDaniel P. Berrange                 migration_iters,
51409437e1SDaniel P. Berrange                 total_guest_cpu,
52409437e1SDaniel P. Berrange                 split_guest_cpu,
53409437e1SDaniel P. Berrange                 qemu_cpu,
54409437e1SDaniel P. Berrange                 vcpu_cpu):
55409437e1SDaniel P. Berrange
56409437e1SDaniel P. Berrange        self._reports = reports
57409437e1SDaniel P. Berrange        self._migration_iters = migration_iters
58409437e1SDaniel P. Berrange        self._total_guest_cpu = total_guest_cpu
59409437e1SDaniel P. Berrange        self._split_guest_cpu = split_guest_cpu
60409437e1SDaniel P. Berrange        self._qemu_cpu = qemu_cpu
61409437e1SDaniel P. Berrange        self._vcpu_cpu = vcpu_cpu
62409437e1SDaniel P. Berrange        self._color_idx = 0
63409437e1SDaniel P. Berrange
64409437e1SDaniel P. Berrange    def _next_color(self):
65409437e1SDaniel P. Berrange        color = self.COLORS[self._color_idx]
66409437e1SDaniel P. Berrange        self._color_idx += 1
67409437e1SDaniel P. Berrange        if self._color_idx >= len(self.COLORS):
68409437e1SDaniel P. Berrange            self._color_idx = 0
69409437e1SDaniel P. Berrange        return color
70409437e1SDaniel P. Berrange
71409437e1SDaniel P. Berrange    def _get_progress_label(self, progress):
72409437e1SDaniel P. Berrange        if progress:
73409437e1SDaniel P. Berrange            return "\n\n" + "\n".join(
74409437e1SDaniel P. Berrange                ["Status: %s" % progress._status,
75409437e1SDaniel P. Berrange                 "Iteration: %d" % progress._ram._iterations,
76409437e1SDaniel P. Berrange                 "Throttle: %02d%%" % progress._throttle_pcent,
77409437e1SDaniel P. Berrange                 "Dirty rate: %dMB/s" % (progress._ram._dirty_rate_pps * 4 / 1024.0)])
78409437e1SDaniel P. Berrange        else:
79409437e1SDaniel P. Berrange            return "\n\n" + "\n".join(
80409437e1SDaniel P. Berrange                ["Status: %s" % "none",
81409437e1SDaniel P. Berrange                 "Iteration: %d" % 0])
82409437e1SDaniel P. Berrange
83409437e1SDaniel P. Berrange    def _find_start_time(self, report):
84409437e1SDaniel P. Berrange        startqemu = report._qemu_timings._records[0]._timestamp
85409437e1SDaniel P. Berrange        startguest = report._guest_timings._records[0]._timestamp
86409437e1SDaniel P. Berrange        if startqemu < startguest:
87409437e1SDaniel P. Berrange            return startqemu
88409437e1SDaniel P. Berrange        else:
89409437e1SDaniel P. Berrange            return stasrtguest
90409437e1SDaniel P. Berrange
91409437e1SDaniel P. Berrange    def _get_guest_max_value(self, report):
92409437e1SDaniel P. Berrange        maxvalue = 0
93409437e1SDaniel P. Berrange        for record in report._guest_timings._records:
94409437e1SDaniel P. Berrange            if record._value > maxvalue:
95409437e1SDaniel P. Berrange                maxvalue = record._value
96409437e1SDaniel P. Berrange        return maxvalue
97409437e1SDaniel P. Berrange
98409437e1SDaniel P. Berrange    def _get_qemu_max_value(self, report):
99409437e1SDaniel P. Berrange        maxvalue = 0
100409437e1SDaniel P. Berrange        oldvalue = None
101409437e1SDaniel P. Berrange        oldtime = None
102409437e1SDaniel P. Berrange        for record in report._qemu_timings._records:
103409437e1SDaniel P. Berrange            if oldvalue is not None:
104409437e1SDaniel P. Berrange                cpudelta = (record._value - oldvalue) / 1000.0
105409437e1SDaniel P. Berrange                timedelta = record._timestamp - oldtime
106409437e1SDaniel P. Berrange                if timedelta == 0:
107409437e1SDaniel P. Berrange                    continue
108409437e1SDaniel P. Berrange                util = cpudelta / timedelta * 100.0
109409437e1SDaniel P. Berrange            else:
110409437e1SDaniel P. Berrange                util = 0
111409437e1SDaniel P. Berrange            oldvalue = record._value
112409437e1SDaniel P. Berrange            oldtime = record._timestamp
113409437e1SDaniel P. Berrange
114409437e1SDaniel P. Berrange            if util > maxvalue:
115409437e1SDaniel P. Berrange                maxvalue = util
116409437e1SDaniel P. Berrange        return maxvalue
117409437e1SDaniel P. Berrange
118409437e1SDaniel P. Berrange    def _get_total_guest_cpu_graph(self, report, starttime):
119409437e1SDaniel P. Berrange        xaxis = []
120409437e1SDaniel P. Berrange        yaxis = []
121409437e1SDaniel P. Berrange        labels = []
122409437e1SDaniel P. Berrange        progress_idx = -1
123409437e1SDaniel P. Berrange        for record in report._guest_timings._records:
124409437e1SDaniel P. Berrange            while ((progress_idx + 1) < len(report._progress_history) and
125409437e1SDaniel P. Berrange                   report._progress_history[progress_idx + 1]._now < record._timestamp):
126409437e1SDaniel P. Berrange                progress_idx = progress_idx + 1
127409437e1SDaniel P. Berrange
128409437e1SDaniel P. Berrange            if progress_idx >= 0:
129409437e1SDaniel P. Berrange                progress = report._progress_history[progress_idx]
130409437e1SDaniel P. Berrange            else:
131409437e1SDaniel P. Berrange                progress = None
132409437e1SDaniel P. Berrange
133409437e1SDaniel P. Berrange            xaxis.append(record._timestamp - starttime)
134409437e1SDaniel P. Berrange            yaxis.append(record._value)
135409437e1SDaniel P. Berrange            labels.append(self._get_progress_label(progress))
136409437e1SDaniel P. Berrange
137409437e1SDaniel P. Berrange        from plotly import graph_objs as go
138409437e1SDaniel P. Berrange        return go.Scatter(x=xaxis,
139409437e1SDaniel P. Berrange                          y=yaxis,
140409437e1SDaniel P. Berrange                          name="Guest PIDs: %s" % report._scenario._name,
141409437e1SDaniel P. Berrange                          mode='lines',
142409437e1SDaniel P. Berrange                          line={
143409437e1SDaniel P. Berrange                              "dash": "solid",
144409437e1SDaniel P. Berrange                              "color": self._next_color(),
145409437e1SDaniel P. Berrange                              "shape": "linear",
146409437e1SDaniel P. Berrange                              "width": 1
147409437e1SDaniel P. Berrange                          },
148409437e1SDaniel P. Berrange                          text=labels)
149409437e1SDaniel P. Berrange
150409437e1SDaniel P. Berrange    def _get_split_guest_cpu_graphs(self, report, starttime):
151409437e1SDaniel P. Berrange        threads = {}
152409437e1SDaniel P. Berrange        for record in report._guest_timings._records:
153409437e1SDaniel P. Berrange            if record._tid in threads:
154409437e1SDaniel P. Berrange                continue
155409437e1SDaniel P. Berrange            threads[record._tid] = {
156409437e1SDaniel P. Berrange                "xaxis": [],
157409437e1SDaniel P. Berrange                "yaxis": [],
158409437e1SDaniel P. Berrange                "labels": [],
159409437e1SDaniel P. Berrange            }
160409437e1SDaniel P. Berrange
161409437e1SDaniel P. Berrange        progress_idx = -1
162409437e1SDaniel P. Berrange        for record in report._guest_timings._records:
163409437e1SDaniel P. Berrange            while ((progress_idx + 1) < len(report._progress_history) and
164409437e1SDaniel P. Berrange                   report._progress_history[progress_idx + 1]._now < record._timestamp):
165409437e1SDaniel P. Berrange                progress_idx = progress_idx + 1
166409437e1SDaniel P. Berrange
167409437e1SDaniel P. Berrange            if progress_idx >= 0:
168409437e1SDaniel P. Berrange                progress = report._progress_history[progress_idx]
169409437e1SDaniel P. Berrange            else:
170409437e1SDaniel P. Berrange                progress = None
171409437e1SDaniel P. Berrange
172409437e1SDaniel P. Berrange            threads[record._tid]["xaxis"].append(record._timestamp - starttime)
173409437e1SDaniel P. Berrange            threads[record._tid]["yaxis"].append(record._value)
174409437e1SDaniel P. Berrange            threads[record._tid]["labels"].append(self._get_progress_label(progress))
175409437e1SDaniel P. Berrange
176409437e1SDaniel P. Berrange
177409437e1SDaniel P. Berrange        graphs = []
178409437e1SDaniel P. Berrange        from plotly import graph_objs as go
179409437e1SDaniel P. Berrange        for tid in threads.keys():
180409437e1SDaniel P. Berrange            graphs.append(
181409437e1SDaniel P. Berrange                go.Scatter(x=threads[tid]["xaxis"],
182409437e1SDaniel P. Berrange                           y=threads[tid]["yaxis"],
183409437e1SDaniel P. Berrange                           name="PID %s: %s" % (tid, report._scenario._name),
184409437e1SDaniel P. Berrange                           mode="lines",
185409437e1SDaniel P. Berrange                           line={
186409437e1SDaniel P. Berrange                               "dash": "solid",
187409437e1SDaniel P. Berrange                               "color": self._next_color(),
188409437e1SDaniel P. Berrange                               "shape": "linear",
189409437e1SDaniel P. Berrange                               "width": 1
190409437e1SDaniel P. Berrange                           },
191409437e1SDaniel P. Berrange                           text=threads[tid]["labels"]))
192409437e1SDaniel P. Berrange        return graphs
193409437e1SDaniel P. Berrange
194409437e1SDaniel P. Berrange    def _get_migration_iters_graph(self, report, starttime):
195409437e1SDaniel P. Berrange        xaxis = []
196409437e1SDaniel P. Berrange        yaxis = []
197409437e1SDaniel P. Berrange        labels = []
198409437e1SDaniel P. Berrange        for progress in report._progress_history:
199409437e1SDaniel P. Berrange            xaxis.append(progress._now - starttime)
200409437e1SDaniel P. Berrange            yaxis.append(0)
201409437e1SDaniel P. Berrange            labels.append(self._get_progress_label(progress))
202409437e1SDaniel P. Berrange
203409437e1SDaniel P. Berrange        from plotly import graph_objs as go
204409437e1SDaniel P. Berrange        return go.Scatter(x=xaxis,
205409437e1SDaniel P. Berrange                          y=yaxis,
206409437e1SDaniel P. Berrange                          text=labels,
207409437e1SDaniel P. Berrange                          name="Migration iterations",
208409437e1SDaniel P. Berrange                          mode="markers",
209409437e1SDaniel P. Berrange                          marker={
210409437e1SDaniel P. Berrange                              "color": self._next_color(),
211409437e1SDaniel P. Berrange                              "symbol": "star",
212409437e1SDaniel P. Berrange                              "size": 5
213409437e1SDaniel P. Berrange                          })
214409437e1SDaniel P. Berrange
215409437e1SDaniel P. Berrange    def _get_qemu_cpu_graph(self, report, starttime):
216409437e1SDaniel P. Berrange        xaxis = []
217409437e1SDaniel P. Berrange        yaxis = []
218409437e1SDaniel P. Berrange        labels = []
219409437e1SDaniel P. Berrange        progress_idx = -1
220409437e1SDaniel P. Berrange
221409437e1SDaniel P. Berrange        first = report._qemu_timings._records[0]
222409437e1SDaniel P. Berrange        abstimestamps = [first._timestamp]
223409437e1SDaniel P. Berrange        absvalues = [first._value]
224409437e1SDaniel P. Berrange
225409437e1SDaniel P. Berrange        for record in report._qemu_timings._records[1:]:
226409437e1SDaniel P. Berrange            while ((progress_idx + 1) < len(report._progress_history) and
227409437e1SDaniel P. Berrange                   report._progress_history[progress_idx + 1]._now < record._timestamp):
228409437e1SDaniel P. Berrange                progress_idx = progress_idx + 1
229409437e1SDaniel P. Berrange
230409437e1SDaniel P. Berrange            if progress_idx >= 0:
231409437e1SDaniel P. Berrange                progress = report._progress_history[progress_idx]
232409437e1SDaniel P. Berrange            else:
233409437e1SDaniel P. Berrange                progress = None
234409437e1SDaniel P. Berrange
235409437e1SDaniel P. Berrange            oldvalue = absvalues[-1]
236409437e1SDaniel P. Berrange            oldtime = abstimestamps[-1]
237409437e1SDaniel P. Berrange
238409437e1SDaniel P. Berrange            cpudelta = (record._value - oldvalue) / 1000.0
239409437e1SDaniel P. Berrange            timedelta = record._timestamp - oldtime
240409437e1SDaniel P. Berrange            if timedelta == 0:
241409437e1SDaniel P. Berrange                continue
242409437e1SDaniel P. Berrange            util = cpudelta / timedelta * 100.0
243409437e1SDaniel P. Berrange
244409437e1SDaniel P. Berrange            abstimestamps.append(record._timestamp)
245409437e1SDaniel P. Berrange            absvalues.append(record._value)
246409437e1SDaniel P. Berrange
247409437e1SDaniel P. Berrange            xaxis.append(record._timestamp - starttime)
248409437e1SDaniel P. Berrange            yaxis.append(util)
249409437e1SDaniel P. Berrange            labels.append(self._get_progress_label(progress))
250409437e1SDaniel P. Berrange
251409437e1SDaniel P. Berrange        from plotly import graph_objs as go
252409437e1SDaniel P. Berrange        return go.Scatter(x=xaxis,
253409437e1SDaniel P. Berrange                          y=yaxis,
254409437e1SDaniel P. Berrange                          yaxis="y2",
255409437e1SDaniel P. Berrange                          name="QEMU: %s" % report._scenario._name,
256409437e1SDaniel P. Berrange                          mode='lines',
257409437e1SDaniel P. Berrange                          line={
258409437e1SDaniel P. Berrange                              "dash": "solid",
259409437e1SDaniel P. Berrange                              "color": self._next_color(),
260409437e1SDaniel P. Berrange                              "shape": "linear",
261409437e1SDaniel P. Berrange                              "width": 1
262409437e1SDaniel P. Berrange                          },
263409437e1SDaniel P. Berrange                          text=labels)
264409437e1SDaniel P. Berrange
265409437e1SDaniel P. Berrange    def _get_vcpu_cpu_graphs(self, report, starttime):
266409437e1SDaniel P. Berrange        threads = {}
267409437e1SDaniel P. Berrange        for record in report._vcpu_timings._records:
268409437e1SDaniel P. Berrange            if record._tid in threads:
269409437e1SDaniel P. Berrange                continue
270409437e1SDaniel P. Berrange            threads[record._tid] = {
271409437e1SDaniel P. Berrange                "xaxis": [],
272409437e1SDaniel P. Berrange                "yaxis": [],
273409437e1SDaniel P. Berrange                "labels": [],
274409437e1SDaniel P. Berrange                "absvalue": [record._value],
275409437e1SDaniel P. Berrange                "abstime": [record._timestamp],
276409437e1SDaniel P. Berrange            }
277409437e1SDaniel P. Berrange
278409437e1SDaniel P. Berrange        progress_idx = -1
279409437e1SDaniel P. Berrange        for record in report._vcpu_timings._records:
280409437e1SDaniel P. Berrange            while ((progress_idx + 1) < len(report._progress_history) and
281409437e1SDaniel P. Berrange                   report._progress_history[progress_idx + 1]._now < record._timestamp):
282409437e1SDaniel P. Berrange                progress_idx = progress_idx + 1
283409437e1SDaniel P. Berrange
284409437e1SDaniel P. Berrange            if progress_idx >= 0:
285409437e1SDaniel P. Berrange                progress = report._progress_history[progress_idx]
286409437e1SDaniel P. Berrange            else:
287409437e1SDaniel P. Berrange                progress = None
288409437e1SDaniel P. Berrange
289409437e1SDaniel P. Berrange            oldvalue = threads[record._tid]["absvalue"][-1]
290409437e1SDaniel P. Berrange            oldtime = threads[record._tid]["abstime"][-1]
291409437e1SDaniel P. Berrange
292409437e1SDaniel P. Berrange            cpudelta = (record._value - oldvalue) / 1000.0
293409437e1SDaniel P. Berrange            timedelta = record._timestamp - oldtime
294409437e1SDaniel P. Berrange            if timedelta == 0:
295409437e1SDaniel P. Berrange                continue
296409437e1SDaniel P. Berrange            util = cpudelta / timedelta * 100.0
297409437e1SDaniel P. Berrange            if util > 100:
298409437e1SDaniel P. Berrange                util = 100
299409437e1SDaniel P. Berrange
300409437e1SDaniel P. Berrange            threads[record._tid]["absvalue"].append(record._value)
301409437e1SDaniel P. Berrange            threads[record._tid]["abstime"].append(record._timestamp)
302409437e1SDaniel P. Berrange
303409437e1SDaniel P. Berrange            threads[record._tid]["xaxis"].append(record._timestamp - starttime)
304409437e1SDaniel P. Berrange            threads[record._tid]["yaxis"].append(util)
305409437e1SDaniel P. Berrange            threads[record._tid]["labels"].append(self._get_progress_label(progress))
306409437e1SDaniel P. Berrange
307409437e1SDaniel P. Berrange
308409437e1SDaniel P. Berrange        graphs = []
309409437e1SDaniel P. Berrange        from plotly import graph_objs as go
310409437e1SDaniel P. Berrange        for tid in threads.keys():
311409437e1SDaniel P. Berrange            graphs.append(
312409437e1SDaniel P. Berrange                go.Scatter(x=threads[tid]["xaxis"],
313409437e1SDaniel P. Berrange                           y=threads[tid]["yaxis"],
314409437e1SDaniel P. Berrange                           yaxis="y2",
315409437e1SDaniel P. Berrange                           name="VCPU %s: %s" % (tid, report._scenario._name),
316409437e1SDaniel P. Berrange                           mode="lines",
317409437e1SDaniel P. Berrange                           line={
318409437e1SDaniel P. Berrange                               "dash": "solid",
319409437e1SDaniel P. Berrange                               "color": self._next_color(),
320409437e1SDaniel P. Berrange                               "shape": "linear",
321409437e1SDaniel P. Berrange                               "width": 1
322409437e1SDaniel P. Berrange                           },
323409437e1SDaniel P. Berrange                           text=threads[tid]["labels"]))
324409437e1SDaniel P. Berrange        return graphs
325409437e1SDaniel P. Berrange
326409437e1SDaniel P. Berrange    def _generate_chart_report(self, report):
327409437e1SDaniel P. Berrange        graphs = []
328409437e1SDaniel P. Berrange        starttime = self._find_start_time(report)
329409437e1SDaniel P. Berrange        if self._total_guest_cpu:
330409437e1SDaniel P. Berrange            graphs.append(self._get_total_guest_cpu_graph(report, starttime))
331409437e1SDaniel P. Berrange        if self._split_guest_cpu:
332409437e1SDaniel P. Berrange            graphs.extend(self._get_split_guest_cpu_graphs(report, starttime))
333409437e1SDaniel P. Berrange        if self._qemu_cpu:
334409437e1SDaniel P. Berrange            graphs.append(self._get_qemu_cpu_graph(report, starttime))
335409437e1SDaniel P. Berrange        if self._vcpu_cpu:
336409437e1SDaniel P. Berrange            graphs.extend(self._get_vcpu_cpu_graphs(report, starttime))
337409437e1SDaniel P. Berrange        if self._migration_iters:
338409437e1SDaniel P. Berrange            graphs.append(self._get_migration_iters_graph(report, starttime))
339409437e1SDaniel P. Berrange        return graphs
340409437e1SDaniel P. Berrange
341409437e1SDaniel P. Berrange    def _generate_annotation(self, starttime, progress):
342409437e1SDaniel P. Berrange        return {
343409437e1SDaniel P. Berrange            "text": progress._status,
344409437e1SDaniel P. Berrange            "x": progress._now - starttime,
345409437e1SDaniel P. Berrange            "y": 10,
346409437e1SDaniel P. Berrange        }
347409437e1SDaniel P. Berrange
348409437e1SDaniel P. Berrange    def _generate_annotations(self, report):
349409437e1SDaniel P. Berrange        starttime = self._find_start_time(report)
350409437e1SDaniel P. Berrange        annotations = {}
351409437e1SDaniel P. Berrange        started = False
352409437e1SDaniel P. Berrange        for progress in report._progress_history:
353409437e1SDaniel P. Berrange            if progress._status == "setup":
354409437e1SDaniel P. Berrange                continue
355409437e1SDaniel P. Berrange            if progress._status not in annotations:
356409437e1SDaniel P. Berrange                annotations[progress._status] = self._generate_annotation(starttime, progress)
357409437e1SDaniel P. Berrange
358409437e1SDaniel P. Berrange        return annotations.values()
359409437e1SDaniel P. Berrange
360409437e1SDaniel P. Berrange    def _generate_chart(self):
361409437e1SDaniel P. Berrange        from plotly.offline import plot
362409437e1SDaniel P. Berrange        from plotly import graph_objs as go
363409437e1SDaniel P. Berrange
364409437e1SDaniel P. Berrange        graphs = []
365409437e1SDaniel P. Berrange        yaxismax = 0
366409437e1SDaniel P. Berrange        yaxismax2 = 0
367409437e1SDaniel P. Berrange        for report in self._reports:
368409437e1SDaniel P. Berrange            graphs.extend(self._generate_chart_report(report))
369409437e1SDaniel P. Berrange
370409437e1SDaniel P. Berrange            maxvalue = self._get_guest_max_value(report)
371409437e1SDaniel P. Berrange            if maxvalue > yaxismax:
372409437e1SDaniel P. Berrange                yaxismax = maxvalue
373409437e1SDaniel P. Berrange
374409437e1SDaniel P. Berrange            maxvalue = self._get_qemu_max_value(report)
375409437e1SDaniel P. Berrange            if maxvalue > yaxismax2:
376409437e1SDaniel P. Berrange                yaxismax2 = maxvalue
377409437e1SDaniel P. Berrange
378409437e1SDaniel P. Berrange        yaxismax += 100
379409437e1SDaniel P. Berrange        if not self._qemu_cpu:
380409437e1SDaniel P. Berrange            yaxismax2 = 110
381409437e1SDaniel P. Berrange        yaxismax2 += 10
382409437e1SDaniel P. Berrange
383409437e1SDaniel P. Berrange        annotations = []
384409437e1SDaniel P. Berrange        if self._migration_iters:
385409437e1SDaniel P. Berrange            for report in self._reports:
386409437e1SDaniel P. Berrange                annotations.extend(self._generate_annotations(report))
387409437e1SDaniel P. Berrange
388409437e1SDaniel P. Berrange        layout = go.Layout(title="Migration comparison",
389409437e1SDaniel P. Berrange                           xaxis={
390409437e1SDaniel P. Berrange                               "title": "Wallclock time (secs)",
391409437e1SDaniel P. Berrange                               "showgrid": False,
392409437e1SDaniel P. Berrange                           },
393409437e1SDaniel P. Berrange                           yaxis={
394409437e1SDaniel P. Berrange                               "title": "Memory update speed (ms/GB)",
395409437e1SDaniel P. Berrange                               "showgrid": False,
396409437e1SDaniel P. Berrange                               "range": [0, yaxismax],
397409437e1SDaniel P. Berrange                           },
398409437e1SDaniel P. Berrange                           yaxis2={
399409437e1SDaniel P. Berrange                               "title": "Hostutilization (%)",
400409437e1SDaniel P. Berrange                               "overlaying": "y",
401409437e1SDaniel P. Berrange                               "side": "right",
402409437e1SDaniel P. Berrange                               "range": [0, yaxismax2],
403409437e1SDaniel P. Berrange                               "showgrid": False,
404409437e1SDaniel P. Berrange                           },
405409437e1SDaniel P. Berrange                           annotations=annotations)
406409437e1SDaniel P. Berrange
407409437e1SDaniel P. Berrange        figure = go.Figure(data=graphs, layout=layout)
408409437e1SDaniel P. Berrange
409409437e1SDaniel P. Berrange        return plot(figure,
410409437e1SDaniel P. Berrange                    show_link=False,
411409437e1SDaniel P. Berrange                    include_plotlyjs=False,
412409437e1SDaniel P. Berrange                    output_type="div")
413409437e1SDaniel P. Berrange
414409437e1SDaniel P. Berrange
415409437e1SDaniel P. Berrange    def _generate_report(self):
416409437e1SDaniel P. Berrange        pieces = []
417409437e1SDaniel P. Berrange        for report in self._reports:
418409437e1SDaniel P. Berrange            pieces.append("""
419409437e1SDaniel P. Berrange<h3>Report %s</h3>
420409437e1SDaniel P. Berrange<table>
421409437e1SDaniel P. Berrange""" % report._scenario._name)
422409437e1SDaniel P. Berrange
423409437e1SDaniel P. Berrange            pieces.append("""
424409437e1SDaniel P. Berrange  <tr class="subhead">
425409437e1SDaniel P. Berrange    <th colspan="2">Test config</th>
426409437e1SDaniel P. Berrange  </tr>
427409437e1SDaniel P. Berrange  <tr>
428409437e1SDaniel P. Berrange    <th>Emulator:</th>
429409437e1SDaniel P. Berrange    <td>%s</td>
430409437e1SDaniel P. Berrange  </tr>
431409437e1SDaniel P. Berrange  <tr>
432409437e1SDaniel P. Berrange    <th>Kernel:</th>
433409437e1SDaniel P. Berrange    <td>%s</td>
434409437e1SDaniel P. Berrange  </tr>
435409437e1SDaniel P. Berrange  <tr>
436409437e1SDaniel P. Berrange    <th>Ramdisk:</th>
437409437e1SDaniel P. Berrange    <td>%s</td>
438409437e1SDaniel P. Berrange  </tr>
439409437e1SDaniel P. Berrange  <tr>
440409437e1SDaniel P. Berrange    <th>Transport:</th>
441409437e1SDaniel P. Berrange    <td>%s</td>
442409437e1SDaniel P. Berrange  </tr>
443409437e1SDaniel P. Berrange  <tr>
444409437e1SDaniel P. Berrange    <th>Host:</th>
445409437e1SDaniel P. Berrange    <td>%s</td>
446409437e1SDaniel P. Berrange  </tr>
447409437e1SDaniel P. Berrange""" % (report._binary, report._kernel,
448409437e1SDaniel P. Berrange       report._initrd, report._transport, report._dst_host))
449409437e1SDaniel P. Berrange
450409437e1SDaniel P. Berrange            hardware = report._hardware
451409437e1SDaniel P. Berrange            pieces.append("""
452409437e1SDaniel P. Berrange  <tr class="subhead">
453409437e1SDaniel P. Berrange    <th colspan="2">Hardware config</th>
454409437e1SDaniel P. Berrange  </tr>
455409437e1SDaniel P. Berrange  <tr>
456409437e1SDaniel P. Berrange    <th>CPUs:</th>
457409437e1SDaniel P. Berrange    <td>%d</td>
458409437e1SDaniel P. Berrange  </tr>
459409437e1SDaniel P. Berrange  <tr>
460409437e1SDaniel P. Berrange    <th>RAM:</th>
461409437e1SDaniel P. Berrange    <td>%d GB</td>
462409437e1SDaniel P. Berrange  </tr>
463409437e1SDaniel P. Berrange  <tr>
464409437e1SDaniel P. Berrange    <th>Source CPU bind:</th>
465409437e1SDaniel P. Berrange    <td>%s</td>
466409437e1SDaniel P. Berrange  </tr>
467409437e1SDaniel P. Berrange  <tr>
468409437e1SDaniel P. Berrange    <th>Source RAM bind:</th>
469409437e1SDaniel P. Berrange    <td>%s</td>
470409437e1SDaniel P. Berrange  </tr>
471409437e1SDaniel P. Berrange  <tr>
472409437e1SDaniel P. Berrange    <th>Dest CPU bind:</th>
473409437e1SDaniel P. Berrange    <td>%s</td>
474409437e1SDaniel P. Berrange  </tr>
475409437e1SDaniel P. Berrange  <tr>
476409437e1SDaniel P. Berrange    <th>Dest RAM bind:</th>
477409437e1SDaniel P. Berrange    <td>%s</td>
478409437e1SDaniel P. Berrange  </tr>
479409437e1SDaniel P. Berrange  <tr>
480409437e1SDaniel P. Berrange    <th>Preallocate RAM:</th>
481409437e1SDaniel P. Berrange    <td>%s</td>
482409437e1SDaniel P. Berrange  </tr>
483409437e1SDaniel P. Berrange  <tr>
484409437e1SDaniel P. Berrange    <th>Locked RAM:</th>
485409437e1SDaniel P. Berrange    <td>%s</td>
486409437e1SDaniel P. Berrange  </tr>
487409437e1SDaniel P. Berrange  <tr>
488409437e1SDaniel P. Berrange    <th>Huge pages:</th>
489409437e1SDaniel P. Berrange    <td>%s</td>
490409437e1SDaniel P. Berrange  </tr>
491409437e1SDaniel P. Berrange""" % (hardware._cpus, hardware._mem,
492409437e1SDaniel P. Berrange       ",".join(hardware._src_cpu_bind),
493409437e1SDaniel P. Berrange       ",".join(hardware._src_mem_bind),
494409437e1SDaniel P. Berrange       ",".join(hardware._dst_cpu_bind),
495409437e1SDaniel P. Berrange       ",".join(hardware._dst_mem_bind),
496409437e1SDaniel P. Berrange       "yes" if hardware._prealloc_pages else "no",
497409437e1SDaniel P. Berrange       "yes" if hardware._locked_pages else "no",
498409437e1SDaniel P. Berrange       "yes" if hardware._huge_pages else "no"))
499409437e1SDaniel P. Berrange
500409437e1SDaniel P. Berrange            scenario = report._scenario
501409437e1SDaniel P. Berrange            pieces.append("""
502409437e1SDaniel P. Berrange  <tr class="subhead">
503409437e1SDaniel P. Berrange    <th colspan="2">Scenario config</th>
504409437e1SDaniel P. Berrange  </tr>
505409437e1SDaniel P. Berrange  <tr>
506409437e1SDaniel P. Berrange    <th>Max downtime:</th>
507409437e1SDaniel P. Berrange    <td>%d milli-sec</td>
508409437e1SDaniel P. Berrange  </tr>
509409437e1SDaniel P. Berrange  <tr>
510409437e1SDaniel P. Berrange    <th>Max bandwidth:</th>
511409437e1SDaniel P. Berrange    <td>%d MB/sec</td>
512409437e1SDaniel P. Berrange  </tr>
513409437e1SDaniel P. Berrange  <tr>
514409437e1SDaniel P. Berrange    <th>Max iters:</th>
515409437e1SDaniel P. Berrange    <td>%d</td>
516409437e1SDaniel P. Berrange  </tr>
517409437e1SDaniel P. Berrange  <tr>
518409437e1SDaniel P. Berrange    <th>Max time:</th>
519409437e1SDaniel P. Berrange    <td>%d secs</td>
520409437e1SDaniel P. Berrange  </tr>
521409437e1SDaniel P. Berrange  <tr>
522409437e1SDaniel P. Berrange    <th>Pause:</th>
523409437e1SDaniel P. Berrange    <td>%s</td>
524409437e1SDaniel P. Berrange  </tr>
525409437e1SDaniel P. Berrange  <tr>
526409437e1SDaniel P. Berrange    <th>Pause iters:</th>
527409437e1SDaniel P. Berrange    <td>%d</td>
528409437e1SDaniel P. Berrange  </tr>
529409437e1SDaniel P. Berrange  <tr>
530409437e1SDaniel P. Berrange    <th>Post-copy:</th>
531409437e1SDaniel P. Berrange    <td>%s</td>
532409437e1SDaniel P. Berrange  </tr>
533409437e1SDaniel P. Berrange  <tr>
534409437e1SDaniel P. Berrange    <th>Post-copy iters:</th>
535409437e1SDaniel P. Berrange    <td>%d</td>
536409437e1SDaniel P. Berrange  </tr>
537409437e1SDaniel P. Berrange  <tr>
538409437e1SDaniel P. Berrange    <th>Auto-converge:</th>
539409437e1SDaniel P. Berrange    <td>%s</td>
540409437e1SDaniel P. Berrange  </tr>
541409437e1SDaniel P. Berrange  <tr>
542409437e1SDaniel P. Berrange    <th>Auto-converge iters:</th>
543409437e1SDaniel P. Berrange    <td>%d</td>
544409437e1SDaniel P. Berrange  </tr>
545409437e1SDaniel P. Berrange  <tr>
546409437e1SDaniel P. Berrange    <th>MT compression:</th>
547409437e1SDaniel P. Berrange    <td>%s</td>
548409437e1SDaniel P. Berrange  </tr>
549409437e1SDaniel P. Berrange  <tr>
550409437e1SDaniel P. Berrange    <th>MT compression threads:</th>
551409437e1SDaniel P. Berrange    <td>%d</td>
552409437e1SDaniel P. Berrange  </tr>
553409437e1SDaniel P. Berrange  <tr>
554409437e1SDaniel P. Berrange    <th>XBZRLE compression:</th>
555409437e1SDaniel P. Berrange    <td>%s</td>
556409437e1SDaniel P. Berrange  </tr>
557409437e1SDaniel P. Berrange  <tr>
558409437e1SDaniel P. Berrange    <th>XBZRLE compression cache:</th>
559409437e1SDaniel P. Berrange    <td>%d%% of RAM</td>
560409437e1SDaniel P. Berrange  </tr>
561409437e1SDaniel P. Berrange""" % (scenario._downtime, scenario._bandwidth,
562409437e1SDaniel P. Berrange       scenario._max_iters, scenario._max_time,
563409437e1SDaniel P. Berrange       "yes" if scenario._pause else "no", scenario._pause_iters,
564409437e1SDaniel P. Berrange       "yes" if scenario._post_copy else "no", scenario._post_copy_iters,
565409437e1SDaniel P. Berrange       "yes" if scenario._auto_converge else "no", scenario._auto_converge_step,
566409437e1SDaniel P. Berrange       "yes" if scenario._compression_mt else "no", scenario._compression_mt_threads,
567409437e1SDaniel P. Berrange       "yes" if scenario._compression_xbzrle else "no", scenario._compression_xbzrle_cache))
568409437e1SDaniel P. Berrange
569409437e1SDaniel P. Berrange            pieces.append("""
570409437e1SDaniel P. Berrange</table>
571409437e1SDaniel P. Berrange""")
572409437e1SDaniel P. Berrange
573409437e1SDaniel P. Berrange        return "\n".join(pieces)
574409437e1SDaniel P. Berrange
575409437e1SDaniel P. Berrange    def _generate_style(self):
576409437e1SDaniel P. Berrange        return """
577409437e1SDaniel P. Berrange#report table tr th {
578409437e1SDaniel P. Berrange    text-align: right;
579409437e1SDaniel P. Berrange}
580409437e1SDaniel P. Berrange#report table tr td {
581409437e1SDaniel P. Berrange    text-align: left;
582409437e1SDaniel P. Berrange}
583409437e1SDaniel P. Berrange#report table tr.subhead th {
584409437e1SDaniel P. Berrange    background: rgb(192, 192, 192);
585409437e1SDaniel P. Berrange    text-align: center;
586409437e1SDaniel P. Berrange}
587409437e1SDaniel P. Berrange
588409437e1SDaniel P. Berrange"""
589409437e1SDaniel P. Berrange
590409437e1SDaniel P. Berrange    def generate_html(self, fh):
591f03868bdSEduardo Habkost        print("""<html>
592409437e1SDaniel P. Berrange  <head>
593409437e1SDaniel P. Berrange    <script type="text/javascript" src="plotly.min.js">
594409437e1SDaniel P. Berrange    </script>
595409437e1SDaniel P. Berrange    <style type="text/css">
596409437e1SDaniel P. Berrange%s
597409437e1SDaniel P. Berrange    </style>
598409437e1SDaniel P. Berrange    <title>Migration report</title>
599409437e1SDaniel P. Berrange  </head>
600409437e1SDaniel P. Berrange  <body>
601409437e1SDaniel P. Berrange    <h1>Migration report</h1>
602409437e1SDaniel P. Berrange    <h2>Chart summary</h2>
603409437e1SDaniel P. Berrange    <div id="chart">
604f03868bdSEduardo Habkost""" % self._generate_style(), file=fh)
605f03868bdSEduardo Habkost        print(self._generate_chart(), file=fh)
606f03868bdSEduardo Habkost        print("""
607409437e1SDaniel P. Berrange    </div>
608409437e1SDaniel P. Berrange    <h2>Report details</h2>
609409437e1SDaniel P. Berrange    <div id="report">
610f03868bdSEduardo Habkost""", file=fh)
611f03868bdSEduardo Habkost        print(self._generate_report(), file=fh)
612f03868bdSEduardo Habkost        print("""
613409437e1SDaniel P. Berrange    </div>
614409437e1SDaniel P. Berrange  </body>
615409437e1SDaniel P. Berrange</html>
616f03868bdSEduardo Habkost""", file=fh)
617409437e1SDaniel P. Berrange
618409437e1SDaniel P. Berrange    def generate(self, filename):
619409437e1SDaniel P. Berrange        if filename is None:
620409437e1SDaniel P. Berrange            self.generate_html(sys.stdout)
621409437e1SDaniel P. Berrange        else:
622409437e1SDaniel P. Berrange            with open(filename, "w") as fh:
623409437e1SDaniel P. Berrange                self.generate_html(fh)
624