]> git.arvados.org - arvados.git/blob - tools/crunchstat-summary/crunchstat_summary/dygraphs.py
23062: Hide/disable workflow>collection linking until 23057.
[arvados.git] / tools / crunchstat-summary / crunchstat_summary / dygraphs.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 import importlib.resources
6 import json
7 from arvados._internal.report_template import ReportTemplate
8
9 class DygraphsChart(ReportTemplate):
10     """Crunchstat report using dygraphs for charting.
11     """
12
13     CSS = 'https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.min.css'
14     JSLIB = 'https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.min.js'
15     JSASSETS = ['synchronizer.js','dygraphs.js']
16
17     def __init__(self, label, summarizers, beforechart, afterchart):
18         super().__init__(label)
19         self.summarizers = summarizers
20         self.beforechart = beforechart
21         self.afterchart = afterchart
22
23     def html(self):
24         self.cards.extend(self.beforechart)
25         self.cards.append("""
26                 <h2>Graph</h2>
27                 <div id="chart"></div>
28             """)
29         self.cards.extend(self.afterchart)
30
31         return super().html()
32
33     def js(self):
34         return '''
35         <script type="text/javascript" src="{jslib}"></script>
36         <script type="text/javascript">
37         var chartdata = {chartdata};\n{jsassets}
38         </script>'''.format(
39             jslib=self.JSLIB,
40             chartdata=json.dumps(self.sections()),
41             jsassets='\n'.join(
42                 importlib.resources.read_text('crunchstat_summary', jsa, encoding='utf-8')
43                 for jsa in self.JSASSETS
44             ),
45         )
46
47     def sections(self):
48         return [
49             {
50                 'label': s.long_label(),
51                 'charts': [
52                     self.chartdata(s.label, s.tasks, stat)
53                     for stat in (('cpu', ['user+sys__rate', 'user__rate', 'sys__rate']),
54                                  ('mem', ['rss']),
55                                  ('net:eth0', ['tx+rx__rate','rx__rate','tx__rate']),
56                                  ('net:keep0', ['tx+rx__rate','rx__rate','tx__rate']),
57                                  ('statfs', ['used', 'total']),
58                                  )
59                     ],
60             }
61             for s in self.summarizers]
62
63     def chartdata(self, label, tasks, stats):
64         '''For Crunch2, label is the name of container request,
65         tasks is the top level container and
66         stats is index by a tuple of (category, metric).
67         '''
68         return {
69             'data': self._collate_data(tasks, stats),
70             'options': {
71                 'legend': 'always',
72                 'connectSeparatedPoints': True,
73                 'labels': ['elapsed'] +  stats[1],
74                 'includeZero': True,
75                 'title': '{}: {}'.format(label, stats[0]) if label else stats[0],
76             },
77         }
78
79     def _collate_data(self, tasks, stats):
80         data = []
81         nulls = []
82         # uuid is category for crunch2
83         for uuid, task in tasks.items():
84             # All stats in a category are assumed to have the same time base and same number of samples
85             category = stats[0]
86             series_names = stats[1]
87             sn0 = series_names[0]
88             series = task.series[(category,sn0)]
89             for i in range(len(series)):
90                 pt = series[i]
91                 vals = [task.series[(category,stat)][i][1] for stat in series_names[1:]]
92                 data.append([pt[0].total_seconds()] + nulls + [pt[1]] + vals)
93             nulls.append(None)
94         return sorted(data)
95
96     def style(self):
97         return '\n'.join((super().style(),
98                          '<link rel="stylesheet" href="{}">\n'.format(self.CSS)))