You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
6.2 KiB
184 lines
6.2 KiB
# Copyright (C) 2005-2025 Splunk Inc. All Rights Reserved.
|
|
|
|
"""
|
|
This module accepts a classic GT json and produces UDF GT json
|
|
"""
|
|
|
|
from .utils import dict_merge
|
|
from .layout import LayoutConverter
|
|
from .visualization import VizConverter
|
|
from .datasource import DataSourceConverter
|
|
from .udf_definition_builder import UDFDefinitionBuilder
|
|
|
|
|
|
class GTConverter(object):
|
|
def __init__(
|
|
self, session_key=None, layout_converter=None, viz_converter=None, datasource_converter=None, services=[]):
|
|
"""
|
|
Initializer
|
|
:param session_key: splunkd session key
|
|
:param layout_converter: LayoutConverter object for overriding default behavior
|
|
:param viz_converter: VizConverter object for overriding default behavior
|
|
:param datasource_converter: DataSourceConverter object for overriding default behavior
|
|
:param services: Optional prefetched list of ITSI services
|
|
"""
|
|
self.gt = None
|
|
|
|
self.layout_converter = layout_converter if layout_converter else LayoutConverter()
|
|
self.viz_converter = viz_converter if viz_converter else VizConverter(session_key=session_key)
|
|
self.datasource_converter = datasource_converter if datasource_converter else \
|
|
DataSourceConverter(session_key=session_key, services=services)
|
|
|
|
def parse_glasstable(self, gt):
|
|
"""
|
|
Main method
|
|
:param gt: input glass table
|
|
:return: converted UDF json
|
|
"""
|
|
self.gt = gt
|
|
if not self.should_convert():
|
|
return self.gt
|
|
|
|
# start with a clean slate
|
|
self.definition_builder = UDFDefinitionBuilder()
|
|
|
|
self.parse_metadata()
|
|
|
|
layout_options = self.layout_converter.convert(self.gt)
|
|
if layout_options:
|
|
self.definition_builder.update_layout_options(layout_options)
|
|
|
|
self.parse_contents()
|
|
|
|
return self.definition_builder.get_definition()
|
|
|
|
def should_convert(self):
|
|
"""
|
|
Should provided json be converted at all
|
|
:return: Boolean
|
|
"""
|
|
if not self.gt:
|
|
return False
|
|
content = self.gt.get('content')
|
|
gt_version = self.gt.get('gt_version')
|
|
return content is not None and gt_version != 'beta'
|
|
|
|
def parse_metadata(self):
|
|
title = self.gt.get('title')
|
|
description = self.gt.get('description')
|
|
self.definition_builder.update_meta(title, description)
|
|
|
|
def parse_contents(self):
|
|
"""
|
|
Goes over all original widgets and does vis, layout and datasource conversion
|
|
"""
|
|
content = self.gt.get('content')
|
|
scale_factor = self.layout_converter.get_scale_factor()
|
|
|
|
for item in content:
|
|
viz_item = self.convert_viz(item, scale_factor)
|
|
if viz_item:
|
|
self.definition_builder.add_visualization(viz_item)
|
|
|
|
layout_item = self.layout_converter.convert_item(item)
|
|
if layout_item:
|
|
self.definition_builder.add_layout_item(layout_item)
|
|
|
|
if item.get('labelFlag') and len(item.get('label')) > 0:
|
|
label = self.create_label(item, layout_item)
|
|
if label:
|
|
self.definition_builder.add_visualization(label['viz'])
|
|
self.definition_builder.add_layout_item(label['layout'])
|
|
|
|
if self.is_searchable(item) and item.get('searchFigure'):
|
|
ds_id = 'ds_%s' % str(item.get('id'))
|
|
viz_item = self.viz_bind_datasource(viz_item, ds_id)
|
|
self.definition_builder.update_visualization(item.get('id'), viz_item)
|
|
|
|
data_source = self.datasource_converter.convert(item, ds_id)
|
|
if data_source:
|
|
self.definition_builder.add_data_source(data_source)
|
|
|
|
def convert_viz(self, item, scale_factor):
|
|
"""
|
|
Produces a UDF vis object
|
|
:param item: original GT widget
|
|
:param scale_factor: scale factor applied to the classic GT
|
|
:return: id-wrapped converted object
|
|
"""
|
|
viz_item = None
|
|
viz = self.viz_converter.convert(item, scale_factor)
|
|
if viz:
|
|
viz_item = {}
|
|
viz_id = 'viz_%s' % str(item.get('id'))
|
|
viz_item[viz_id] = viz
|
|
return viz_item
|
|
|
|
def viz_bind_datasource(self, viz_item, ds_id):
|
|
"""
|
|
Adds a datasource id to the viz definition.
|
|
:param viz_item: viz_item object
|
|
:param ds_id: datasource id to bind
|
|
:return:
|
|
"""
|
|
ds = {
|
|
'dataSources': {
|
|
'primary': ds_id
|
|
}
|
|
}
|
|
first_key = list(viz_item.keys())[0]
|
|
dict_merge(viz_item[first_key], ds)
|
|
return viz_item
|
|
|
|
def is_searchable(self, item):
|
|
"""
|
|
Can current widget support datasources
|
|
:return: Bool
|
|
"""
|
|
supported = [
|
|
'SingleValue',
|
|
'Sparkline',
|
|
'SingleValueDelta',
|
|
'PolyIcon',
|
|
'Gauge',
|
|
'Rectangle',
|
|
'Ellipse',
|
|
'Line'
|
|
]
|
|
return item.get('name') in supported
|
|
|
|
def create_label(self, item, layout_item):
|
|
"""
|
|
Create a label widget for an item
|
|
:param item: gt item
|
|
:param layout_item: converted layout item for gt item
|
|
:return: {'viz': viz, 'layout': layout} or None
|
|
"""
|
|
if item.get('name') == 'Connection':
|
|
# we don't support converting connection label
|
|
return None
|
|
|
|
label_id = 'viz_%s_label' % item.get('id')
|
|
label_location = item.get('labelLocation')
|
|
|
|
label_def = self.viz_converter.create_label(item)
|
|
if not label_def:
|
|
return None
|
|
|
|
label_viz = {
|
|
label_id: label_def
|
|
}
|
|
|
|
if item.get('name') == 'Line':
|
|
label_layout = {
|
|
'item': label_id,
|
|
'position': self.layout_converter.find_label_position_line(
|
|
label_location, layout_item['position'], item)
|
|
}
|
|
else:
|
|
label_layout = {
|
|
'item': label_id,
|
|
'position': self.layout_converter.find_label_position(label_location, layout_item['position'])
|
|
}
|
|
return {'viz': label_viz, 'layout': label_layout}
|