e020ae154877f5f4ea6a7a4c6f3417d8a41b88f3
[integration/test.git] / csit / scripts / generate_visState.py
1 # SPDX-License-Identifier: EPL-1.0
2 ##############################################################################
3 # Copyright (c) 2018 The Linux Foundation and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Eclipse Public License v1.0
7 # which accompanies this distribution, and is available at
8 # http://www.eclipse.org/legal/epl-v10.html
9 ##############################################################################
10 import yaml
11 from copy import deepcopy as dc
12
13 import json
14
15
16 def p(x):
17     print(json.dumps(x, indent=4, sort_keys=True))
18
19
20 class visState:
21     def __init__(self):
22         self.content = {
23             'title': None,
24             'type': None,
25             'params': {
26                 'type': None,
27                 'grid': {
28                     'categoryLines': False,
29                     'style': {
30                         'color': '#eee'
31                     }
32                 },
33                 'categoryAxes': None,
34                 'valueAxes': None,
35                 'seriesParams': None,
36                 'addTooltip': True,
37                 'addLegend': True,
38                 'legendPosition': 'right',
39                 'times': [],
40                 'addTimeMarker': False
41             },
42             'aggs': None
43         }
44
45     def create(self, config):
46         temp = self.content
47         temp['title'] = config['title']
48         temp['type'] = temp['params']['type'] = config['type']
49
50         cat = categoryAxes()
51         temp['params']['categoryAxes'] = [dc(
52             cat.create()) for i in range(config['num_cat_axes'])]
53
54         val = ValueAxes()
55         temp['params']['valueAxes'] = [dc(val.create(position=i['position'],
56                                                      title=i['title']))
57                                        for _, i in
58                                        config['value_axes'].items()]
59
60         agg = aggs()
61
62         temp['aggs'] = [dc(agg.create(field=i['field'],
63                                       custom_label=i['custom_label'],
64                                       schema=i['schema']))
65                         for _, i in
66                         config['aggs'].items()]
67
68         temp['params']['seriesParams'] = [seriesParams(i['data_type'],
69                                                        i['mode'],
70                                                        i['label'],
71                                                        i['agg_id'],
72                                                        i['value_axis'])
73                                           .create()
74                                           for _, i in
75                                           config['seriesParams'].items()]
76
77         return temp
78
79
80 class categoryAxes:
81     def __init__(self):
82         self.content = {
83             'id': None,
84             'type': 'category',
85             'position': 'bottom',
86             'show': True,
87             'style': {},
88             'scale': {
89                 'type': 'linear'
90             },
91             'labels': {
92                 'show': True,
93                 'truncate': 100
94             },
95             'title': {}
96         }
97         self.counter = 0
98
99     def create(self):
100         self.counter += 1
101         temp = dc(self.content)
102         temp['id'] = 'CategoryAxis-{}'.format(self.counter)
103         return temp
104
105
106 class ValueAxes:
107     def __init__(self):
108         self.content = {
109             'id': None,
110             'name': None,
111             'type': 'value',
112             'position': 'left',
113             'show': True,
114             'style': {},
115             'scale': {
116                 'type': 'linear',
117                 'mode': 'normal'
118             },
119             'labels': {
120                 'show': True,
121                 'rotate': 0,
122                 'filter': False,
123                 'truncate': 100
124             },
125             'title': {
126                 'text': None
127             }
128         }
129         self.counter = 0
130
131     def create(self, position='left', title='Value'):
132         self.counter += 1
133         temp = dc(self.content)
134         temp['id'] = 'ValueAxis-{}'.format(self.counter)
135         if position == 'left':
136             temp['name'] = 'LeftAxis-{}'.format(self.counter)
137         elif position == 'right':
138             temp['name'] = 'RightAxis-{}'.format(self.counter)
139         else:
140             # raise ValueError('Not one of left or right')
141             # assuming default
142             temp['name'] = 'LeftAxis-{}'.format(self.counter)
143
144         temp['title']['text'] = title
145
146         return temp
147
148
149 class seriesParams:
150     def __init__(self, data_type, mode, label, agg_id, value_axis):
151         self.content = {
152             'show': True,
153             'type': data_type,
154             'mode': mode,
155             'data': {
156                 'label': label,
157                 'id': str(agg_id)
158             },
159             'valueAxis': 'ValueAxis-{}'.format(value_axis),
160             'drawLinesBetweenPoints': True,
161             'showCircles': True
162         }
163
164     def create(self):
165         return self.content
166
167
168 class aggs:
169     def __init__(self):
170         self.content = {
171             'id': None,
172             'enabled': True,
173             'type': None,
174             'schema': None,
175             'params': {
176                 'field': None,
177                 'customLabel': None
178             }
179         }
180         self.counter = 0
181
182     def create(self, field, custom_label, schema):
183         self.counter += 1
184         temp = dc(self.content)
185         temp['id'] = str(self.counter)
186         temp['params']['field'] = field
187         temp['params']['customLabel'] = custom_label
188         temp['schema'] = schema
189         if schema == 'metric':
190             temp['type'] = 'max'
191             return temp
192         elif schema == 'segment':
193             temp['type'] = 'terms'
194             temp['params']['size'] = 20  # default
195             temp['params']['order'] = 'asc'
196             temp['params']['orderBy'] = '_term'
197         return temp
198
199
200 # generate takes both the template config and project specific config and
201 # parses and organizes as much info available from that and
202 # generates an intermediate format first which
203 # contains all necessary info to deterministically create the visState to
204 # be sent to Kibana. Hence, any error occuring in the visualizaton side
205 # must first be checked by looking at the intermediate format.
206
207 def generate(dash_config, viz_config):
208
209     format = {
210         "type": None,
211         "value_axes": {},
212         "seriesParams": {},
213         "index_pattern": None,
214         "desc": None,
215         "id": None,
216         "aggs": {},
217         "title": None,
218         "num_cat_axes": None
219     }
220
221     value_axes_format = {
222         "index": {
223             "position": None,
224             "title": None
225         }
226     }
227
228     seriesParams_format = {
229         "index": {
230             "value_axis": None,
231             "data_type": None,
232             "mode": None,
233             "label": None,
234             "agg_id": None
235         }
236     }
237
238     aggs_format = {
239         "index": {
240             "custom_label": None,
241             "field": None,
242             "schema": None
243         }
244     }
245
246     # all general description must be present in either of the config files
247     for config in [viz_config, dash_config]:
248         general_fields = ['type', 'index_pattern',
249                           'num_cat_axes', 'title', 'desc', 'id']
250         for i in general_fields:
251             try:
252                 format[i] = config[i]
253             except KeyError as e:
254                 pass
255
256     # setting any default values if available
257     mappings = {'value_axes': value_axes_format,
258                 'seriesParams': seriesParams_format, 'aggs': aggs_format}
259     for index, container in mappings.items():
260         try:
261             default_values = viz_config[index]['default']
262             for i in default_values:
263                 container['index'][i] = default_values[i]
264         except Exception:
265             pass
266
267     value_axes_counter = 1
268     for m in viz_config['value_axes']:
269         if m != "default":
270             temp = dc(value_axes_format)
271             temp[str(value_axes_counter)] = temp['index']
272             for i in ['position', 'title']:
273                 try:
274                     temp[str(value_axes_counter)
275                          ][i] = viz_config['value_axes'][m][i]
276                 except KeyError:
277                     pass
278             format['value_axes'].update(temp)
279             value_axes_counter += 1
280
281     seriesParams_counter = 1
282     seriesParams_fields = ['value_axis',
283                            'data_type', 'mode', 'label', 'agg_id']
284     try:
285         for m in viz_config['seriesParams']:
286             if m != 'default':
287                 temp = dc(seriesParams_format)
288                 temp[str(seriesParams_counter)] = temp['index']
289                 for i in seriesParams_fields:
290                     try:
291                         temp[str(seriesParams_counter)
292                              ][i] = viz_config['seriesParams'][m][i]
293                     except KeyError:
294                         pass
295                 format['seriesParams'].update(temp)
296                 seriesParams_counter += 1
297     except KeyError:
298         pass
299
300     agg_counter = 1
301     try:
302         for m in viz_config['aggs']:
303             if m != 'default':
304                 temp = dc(aggs_format)
305                 temp[str(agg_counter)] = temp['index']
306                 for i in ['field', 'custom_label', 'schema']:
307                     try:
308                         temp[str(agg_counter)][i] = viz_config['aggs'][m][i]
309                     except KeyError:
310                         pass
311                 format['aggs'].update(temp)
312                 agg_counter += 1
313     except KeyError:
314         pass
315
316     configs = []
317     try:
318         viz_config['series']
319         configs.append(viz_config)
320     except KeyError:
321         pass
322
323     try:
324         dash_config['y-axis']['series']
325         configs.append(dash_config['y-axis'])
326     except KeyError:
327         pass
328
329     for config in configs:
330         try:
331             value_axes_counter = 1
332             for key in config['value_axes']:
333
334                 value_axes_temp = dc(value_axes_format)
335                 value_axes_temp[str(value_axes_counter)
336                                 ] = value_axes_temp['index']
337
338                 for index in ['position', 'title']:
339                     try:
340                         value_axes_temp[str(
341                             value_axes_counter)][index] = \
342                             config['value_axes'][key][index]
343                     except KeyError as e:
344                         pass
345                 format['value_axes'].update(value_axes_temp)
346                 value_axes_counter += 1
347
348         except KeyError as e:
349             pass
350
351         try:
352             for key in config['series']:
353                 try:
354                     # check if this key is present or not
355                     config['series'][key]['not_in_seriesParams']
356                 except KeyError:
357                     seriesParams_temp = dc(seriesParams_format)
358                     seriesParams_temp[str(
359                         seriesParams_counter)] = seriesParams_temp['index']
360                     for index in ['value_axis', 'data_type', 'mode', 'label']:
361                         try:
362                             seriesParams_temp[str(
363                                 seriesParams_counter)][index] = \
364                                 config['series'][key][index]
365                         except KeyError as e:
366                             pass
367                     seriesParams_temp[str(
368                         seriesParams_counter)]['agg_id'] = agg_counter
369                     format['seriesParams'].update(seriesParams_temp)
370                     seriesParams_counter += 1
371                 finally:
372                     agg_temp = dc(aggs_format)
373                     agg_temp[str(agg_counter)] = agg_temp['index']
374                     for index in ['field', 'schema']:
375                         try:
376                             agg_temp[str(agg_counter)
377                                      ][index] = config['series'][key][index]
378                         except KeyError as e:
379                             pass
380                     agg_temp[str(
381                         agg_counter)]['custom_label'] = \
382                         config['series'][key]['label']
383                     format['aggs'].update(agg_temp)
384                     agg_counter += 1
385         except KeyError as e:
386             print("required fields are empty!")
387
388     # to remove the default template index
389     for i in ['value_axes', 'seriesParams', 'aggs']:
390         try:
391             format[i].pop('index')
392         except KeyError:
393             # print("No default index found")
394             pass
395
396     if not config_validator(format):
397         raise ValueError('Missing required field values')
398
399     p(format)
400
401     vis = visState()
402     generated_visState = vis.create(format)
403
404     # checking incase there are None values \
405     # in the format indicating missing fields
406
407     if not config_validator(generated_visState):
408         raise KeyError('required fields are missing values!')
409     return format, generated_visState
410
411
412 # Check the generated format if it contains any key with None
413 # as it's value which indicates incomplete information
414 def config_validator(val):
415     flag = True
416     for _, i in val.items():
417         if isinstance(i, dict):
418             flag = config_validator(i)
419         if i is None:
420             return False
421     return flag
422
423
424 if __name__ == '__main__':
425     with open('viz.yaml', 'r') as f:
426         viz_config = yaml.safe_load(f)
427
428     with open('dashboard.yaml', 'r') as f:
429         dash_config = yaml.safe_load(f)
430
431     generate(dash_config['dashboard']['viz'][2],
432              viz_config['opendaylight-test-performance'])
433     # generate(dash_config['dashboard']['viz'][3],viz_config['opendaylight-test-performance'])
434     # generate(dash_config['dashboard']['viz'][1],viz_config['opendaylight-test-feature'])