1 # SPDX-License-Identifier: EPL-1.0
2 ##############################################################################
3 # Copyright (c) 2018 The Linux Foundation and others.
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 ##############################################################################
11 from copy import deepcopy as dc
19 print(json.dumps(x, indent=4, sort_keys=True))
31 'categoryLines': False,
41 'legendPosition': 'right',
43 'addTimeMarker': False
48 def create(self, config):
50 temp['title'] = config['title']
51 temp['type'] = temp['params']['type'] = config['type']
54 temp['params']['categoryAxes'] = [dc(
55 cat.create()) for i in range(config['num_cat_axes'])]
58 temp['params']['valueAxes'] = [dc(val.create(position=i['position'],
61 config['value_axes'].items()]
67 field=config['aggs'][i]['field'],
68 custom_label=config['aggs'][i]['custom_label'],
69 schema=config['aggs'][i]['schema']))
70 for i in range(1, len(config['aggs']) + 1)]
72 temp['params']['seriesParams'] = [seriesParams(i['data_type'],
79 config['seriesParams'].items()]
103 # Category axes are named as CategoryAxis-i
106 temp = dc(self.content)
107 temp['id'] = 'CategoryAxis-{}'.format(self.counter)
136 def create(self, position='left', title='Value'):
138 temp = dc(self.content)
139 temp['id'] = 'ValueAxis-{}'.format(self.counter)
140 if position == 'left':
141 temp['name'] = 'LeftAxis-{}'.format(self.counter)
142 elif position == 'right':
143 temp['name'] = 'RightAxis-{}'.format(self.counter)
145 # raise ValueError('Not one of left or right')
147 temp['name'] = 'LeftAxis-{}'.format(self.counter)
149 temp['title']['text'] = title
153 # 'seriesParams' are the ones that actually show up in the plots.
154 # They point to a data source a.k.a 'aggs' (short for aggregation)
159 def __init__(self, data_type, mode, label, agg_id, value_axis):
166 'id': str(agg_id) # the id of the aggregation they point to
168 'valueAxis': 'ValueAxis-{}'.format(value_axis),
169 'drawLinesBetweenPoints': True,
176 # 'aggs' or aggregation refers to collection of values. They are the data
177 # source which are used by seriesParams. and as expected they take 'field'
178 # as the nested name of the key.
180 # Example, if your value is in {
187 # then I would have to use, 'performance.plots.rate' as the 'field' for aggs
188 # the 'schema' of an agg is 'metric' which are to be
189 # plotted in the Y-axis and 'segment' for the ones in X-axis
206 def create(self, id, field, custom_label, schema):
207 temp = dc(self.content)
209 temp['params']['field'] = field
210 temp['params']['customLabel'] = custom_label
211 temp['schema'] = schema
212 if schema == 'metric':
215 elif schema == 'segment':
216 temp['type'] = 'terms'
217 temp['params']['size'] = 20 # default
218 temp['params']['order'] = 'asc'
219 temp['params']['orderBy'] = '_term'
223 # 'series' actually combines and simplifies both 'seriesParams' and 'aggs'
224 # Both 'seriesParams' and 'aggs' support 'default' to set default values
226 # generate takes both the template config and project specific config and
227 # parses and organizes as much info available from that and
228 # generates an intermediate format first which
229 # contains all necessary info to deterministically create the visState to
230 # be sent to Kibana. Hence, any error occuring in the visualizaton side
231 # must first be checked by looking at the intermediate format.
233 def generate(dash_config, viz_config):
239 "index_pattern": None,
247 value_axes_format = {
254 seriesParams_format = {
266 "custom_label": None,
272 # all general description must be present in either of the config files
273 for config in [viz_config, dash_config]:
274 general_fields = ['type', 'index_pattern',
275 'num_cat_axes', 'title', 'desc', 'id']
276 for i in general_fields:
278 format[i] = config[i]
279 except KeyError as e:
282 # setting any default values if available
283 mappings = {'value_axes': value_axes_format,
284 'seriesParams': seriesParams_format, 'aggs': aggs_format}
285 for index, container in mappings.items():
287 default_values = viz_config[index]['default']
288 for i in default_values:
289 container['index'][i] = default_values[i]
293 ####################################################################
294 # Extract 'value_axes', 'seriesParams' or 'aggs' if present in viz_config
295 value_axes_counter = 1
296 for m in viz_config['value_axes']:
298 temp = dc(value_axes_format)
299 temp[str(value_axes_counter)] = temp['index']
300 for i in ['position', 'title']:
302 temp[str(value_axes_counter)
303 ][i] = viz_config['value_axes'][m][i]
306 format['value_axes'].update(temp)
307 value_axes_counter += 1
309 seriesParams_fields = ['value_axis',
310 'data_type', 'mode', 'label', 'agg_id']
312 for m in viz_config['seriesParams']:
314 temp = dc(seriesParams_format)
315 temp[m] = temp['index']
316 for i in seriesParams_fields:
318 temp[m][i] = viz_config['seriesParams'][m][i]
321 format['seriesParams'].update(temp)
327 for m in viz_config['aggs']:
329 temp = dc(aggs_format)
330 temp[m] = temp['index']
331 for i in ['field', 'custom_label', 'schema']:
333 temp[m][i] = viz_config['aggs'][m][i]
336 format['aggs'].update(temp)
339 ####################################################################
341 # collect 'series' from both the configs
345 configs.append(viz_config)
350 dash_config['y-axis']['series']
351 configs.append(dash_config['y-axis'])
355 ########################################################################
356 # Extract 'series' from either of the configs
357 for config in configs:
359 value_axes_counter = 1
360 for key in config['value_axes']:
362 value_axes_temp = dc(value_axes_format)
363 value_axes_temp[str(value_axes_counter)
364 ] = value_axes_temp['index']
366 for index in ['position', 'title']:
369 value_axes_counter)][index] = \
370 config['value_axes'][key][index]
371 except KeyError as e:
373 format['value_axes'].update(value_axes_temp)
374 value_axes_counter += 1
376 except KeyError as e:
380 for key in config['series']:
382 # check if this key is present or not
383 config['series'][key]['not_in_seriesParams']
385 seriesParams_temp = dc(seriesParams_format)
386 seriesParams_temp[key] = seriesParams_temp['index']
387 for index in ['value_axis', 'data_type', 'mode', 'label']:
389 seriesParams_temp[key][index] = \
390 config['series'][key][index]
391 except KeyError as e:
393 seriesParams_temp[key]['agg_id'] = key
394 format['seriesParams'].update(seriesParams_temp)
396 agg_temp = dc(aggs_format)
397 agg_temp[key] = agg_temp['index']
398 for index in ['field', 'schema']:
400 agg_temp[key][index] = config['series'][key][index]
401 except KeyError as e:
403 agg_temp[key]['custom_label'] = \
404 config['series'][key]['label']
405 format['aggs'].update(agg_temp)
406 except KeyError as e:
407 print("required fields are empty!")
409 ##########################################################################
411 # to remove the default template index
412 for i in ['value_axes', 'seriesParams', 'aggs']:
414 format[i].pop('index')
416 # print("No default index found")
419 missing = config_validator(format)
421 raise ValueError('Missing required field values :-', *missing)
426 generated_visState = vis.create(format)
428 # checking incase there are None values
429 # in the format indicating missing fields
431 missing = config_validator(generated_visState)
433 raise ValueError('required fields are missing values! ', *missing)
434 return format, generated_visState
437 # Check the generated format if it contains any key with None
438 # as it's value which indicates incomplete information
440 def config_validator(val, missing=[]):
441 for key, value in val.items():
442 if isinstance(value, dict):
443 config_validator(value)
449 if __name__ == '__main__':
450 with open('viz_config.yaml', 'r') as f:
451 viz_config = yaml.safe_load(f)
453 with open('dash_config.yaml', 'r') as f:
454 dash_config = yaml.safe_load(f)
456 generate(dash_config['dashboard']['viz'][2],
457 viz_config['opendaylight-test-performance'])
458 # generate(dash_config['dashboard']['viz'][3],viz_config['opendaylight-test-performance'])
459 # generate(dash_config['dashboard']['viz'][1],viz_config['opendaylight-test-feature'])