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