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.
570 lines
28 KiB
570 lines
28 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
|
|
|
|
import json
|
|
import splunk
|
|
from itsi_py3 import _
|
|
from splunk.util import normalizeBoolean
|
|
from ITOA.setup_logging import logger as itsi_logger
|
|
from . import itsi_module_common as utils
|
|
from .itsi_module_kpi_base_search import ItsiModuleKpiBaseSearch
|
|
from .itsi_module_kpi_group import ItsiModuleKpiGroup
|
|
from functools import reduce
|
|
|
|
|
|
class ItsiModuleServiceTemplate(object):
|
|
"""
|
|
Class to create, update, get, getCount, delete ServiceTemplate
|
|
"""
|
|
|
|
"""
|
|
Class variables
|
|
"""
|
|
_base_url = '/servicesNS/nobody/%s/configs/conf-itsi_service_template'
|
|
_base_args = '?output_mode=json&count=-1'
|
|
ACCEPTED_KEYS = ['title', 'description', 'entity_rules', 'recommended_kpis', 'optional_kpis']
|
|
SUMMARY_KEYS = ['title', 'description', 'entity_rules']
|
|
CONF_FILE = 'itsi_service_template'
|
|
|
|
_validation_error_messages = {
|
|
'id_prefix_mismatch': _('Service template ID needs to be prefixed with module ID.'),
|
|
'title_required': _('Service template requires a title.'),
|
|
'atleast_1_recommended_kpi': _('Service template requires at least one recommended KPI.'),
|
|
'recommended_kpi_not_exported': _('Recommended KPI refers to KPI ID {} that is not part of an exported KPI group.'),
|
|
'optional_kpi_not_exported': _('Optional KPI refers to KPI ID {} that is not part of an exported KPI group.')
|
|
}
|
|
|
|
_validation_info_messages = {
|
|
'service_template_missed': _('No service template defined.'),
|
|
'description_missed': _('Service template does not contain a description.')
|
|
}
|
|
|
|
def __init__(self, session_key, app='SA-ITOA', owner='nobody', logger=None):
|
|
"""
|
|
Initializes the ItsiModuleServiceTemplate object
|
|
|
|
@type session_key: string
|
|
@param session_key: the session key
|
|
|
|
@type app: string
|
|
@param app: the app context, defaults to SA-ITOA
|
|
|
|
@type owner: string
|
|
@param owner: the owner context, defaults to nobody
|
|
|
|
@type logger: string
|
|
@param logger: the logger
|
|
"""
|
|
self.session_key = session_key
|
|
self.owner = owner
|
|
self.app = app
|
|
self.object_type = 'service_template'
|
|
self.logger = logger if logger else itsi_logger
|
|
|
|
def create(self, itsi_module, data, **kwargs):
|
|
"""
|
|
Create itsi_service_template conf file
|
|
|
|
If itsi_object_id is given, then this endpoint can also accept 3 flags: include_kpi_base_search.
|
|
entity_source_template_id and include_kpi_group, which indicate whether or not itsi_kpi_base_search.conf,
|
|
inputs.conf and itsi_kpi_template.conf should be exported.
|
|
|
|
If itsi_object_id is not given, then emit the payload directly to conf file
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@type data: dict
|
|
@param data: key/value pairs to be written to conf file
|
|
|
|
@rtype: string
|
|
@return: newly created id (conf stanza name) or raise an exception
|
|
"""
|
|
service_id = data.get('itsi_object_id')
|
|
data_to_post = {}
|
|
if service_id:
|
|
# Templatize service object by id
|
|
templatize_response, templatize_content = utils.templatize_obj_by_id(self.session_key, 'service', service_id)
|
|
if templatize_response.status != 200:
|
|
self.logger.error('Error templatizing service: %s', service_id)
|
|
raise utils.ItsiModuleError(status=400, message=_('Error templatizing service: %s') % service_id)
|
|
service_object = json.loads(templatize_content)
|
|
kpis = [kpi for kpi in service_object.get('kpis') if kpi.get('title') != 'ServiceHealthScore']
|
|
service_object['kpis'] = kpis
|
|
|
|
# Filter out unwanted keys and reformat some of the values
|
|
data_to_post = utils.filter_keys_reformat_certain_values_from_payload(itsi_module, self.ACCEPTED_KEYS, service_object)
|
|
|
|
# Construct service_template_id (stanza name) and include it in the payload
|
|
service_template_id = utils.make_stanza_name(itsi_module, service_object.get('title'))
|
|
data_to_post['name'] = service_template_id
|
|
|
|
include_kpi_group = normalizeBoolean(data.get('include_kpi_group'))
|
|
include_kpi_base_search = normalizeBoolean(data.get('include_kpi_base_search'))
|
|
if not include_kpi_group and include_kpi_base_search:
|
|
self.logger.warning(
|
|
'include_kpi_base_search cannot be true if include_kpi_group is false. Assuming include_kpi_base_search as false and continuing...')
|
|
|
|
# If include_kpi_group is set to 1, then export itsi_kpi_template.conf based on the list of kpis and add return value to recommended_kpi
|
|
elif include_kpi_group:
|
|
kpi_group = ItsiModuleKpiGroup(self.session_key)
|
|
kwargs = {'suffix': 'KPIs'}
|
|
kpi_group_id = utils.make_stanza_name(itsi_module, service_object.get('title'), **kwargs)
|
|
self.logger.debug('Creating KPI group for service %s: %s', service_id, kpi_group_id)
|
|
results = kpi_group.create(itsi_module, {'itsi_object_id': kpi_group_id,
|
|
'kpis': service_object.get('kpis'),
|
|
'title': service_object.get('title') + ' KPIs',
|
|
'description': 'KPIs for %s' % service_object.get('title'),
|
|
'include_kpi_base_search': include_kpi_base_search})
|
|
|
|
# Return type from kpi_group should be {kpi_group_id: <list of kpi_template_kpi_id>}
|
|
recommended_kpis_list = list(results.values())[0]
|
|
self.logger.debug('Generated following recommended KPIs: %s', recommended_kpis_list)
|
|
if isinstance(recommended_kpis_list, list):
|
|
# Results should only have 1 key/value pair
|
|
data_to_post['recommended_kpis'] = ','.join(recommended_kpis_list)
|
|
else:
|
|
self.logger.error('Error writing stanza %s into itsi_kpi_template file', list(results.keys())[0])
|
|
raise utils.ItsiModuleError(status=400, message=_('Error writing stanza %s into itsi_kpi_template file.') % list(results.keys())[0])
|
|
else:
|
|
# Construct service_template_id if not given
|
|
service_template_id = data.get('name')
|
|
if not service_template_id:
|
|
service_template_id = utils.make_stanza_name(itsi_module, data.get('title'))
|
|
# Filter out unwanted keys and re-format some values
|
|
data_to_post = utils.filter_keys_reformat_certain_values_from_payload(itsi_module, self.ACCEPTED_KEYS, data)
|
|
data_to_post['name'] = service_template_id
|
|
|
|
create_conf_response, create_conf_content = utils.create_conf_stanza(self.session_key, self.CONF_FILE, data_to_post, itsi_module)
|
|
if create_conf_response.status == 200 or create_conf_response.status == 201:
|
|
self.logger.debug(
|
|
'Created service template %s in module %s with payload: %s',
|
|
service_template_id,
|
|
itsi_module,
|
|
data_to_post)
|
|
return service_template_id
|
|
else:
|
|
raise utils.ItsiModuleError(status=400, message=_('Error writing into conf file %s.') % create_conf_content)
|
|
|
|
def get(self, itsi_module, object_id, **kwargs):
|
|
"""
|
|
Returns the count of service_template in a given module
|
|
|
|
If itsi_module is specified as "-", returns counts of service templates for all modules
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module requested
|
|
|
|
@type object_id: string
|
|
@param object_id: ID of service template being requested
|
|
"""
|
|
if normalizeBoolean(kwargs.get('get_summary', False)):
|
|
return self.get_summary(itsi_module, **kwargs)
|
|
|
|
req_args = {}
|
|
|
|
# Determine whether resolve_kpis flag is present, and parse into python type if so
|
|
resolve_kpis = normalizeBoolean(kwargs['resolve_kpis']) if 'resolve_kpis' in kwargs else False
|
|
|
|
# Determine whether resolve_kpis_from_itsi flag is present and parse into python type if so
|
|
resolve_kpis_from_itsi = normalizeBoolean(kwargs['resolve_kpis_from_itsi']) if 'resolve_kpis_from_itsi' in kwargs else False
|
|
|
|
# Both flags cannot be allowed to be set, raise a BadRequest
|
|
if resolve_kpis and resolve_kpis_from_itsi:
|
|
raise splunk.BadRequest(_('Either the flag "resolve_kpis" or "resolve_kpis_from_itsi" can be set, not both.'))
|
|
|
|
# If flag 'resolve_kpis_from_itsi' is set, get the filter and fields params from the URL
|
|
if resolve_kpis_from_itsi:
|
|
kpi_fields_string = kwargs['fields'] if 'fields' in kwargs else ''
|
|
kpi_filter_string = kwargs['filter'] if 'filter' in kwargs else ''
|
|
|
|
# Build a request object from all URL params to return correct output to service template
|
|
# Even though both 'resolve_kpis' and 'resolve_kpis_from_itsi' are given, only one can be true
|
|
# due to previous check
|
|
try:
|
|
kpi_filter = json.loads(kpi_filter_string) if kpi_filter_string else {'$and': []}
|
|
except Exception:
|
|
raise splunk.BadRequest(_('The filter provided is invalid JSON.'))
|
|
|
|
payload_filter = self._compute_filter(itsi_module, kpi_filter)
|
|
payload_fields = self._compute_fields(kpi_fields_string)
|
|
|
|
if payload_filter:
|
|
req_args['filter'] = json.dumps(payload_filter)
|
|
if kpi_fields_string:
|
|
req_args['fields'] = payload_fields
|
|
|
|
# Args that determine whether KPIs are to be resolved from itsi_module_interface
|
|
# or from KV Store
|
|
resolve_args = {
|
|
'resolve_kpis': resolve_kpis,
|
|
'resolve_kpis_from_itsi': resolve_kpis_from_itsi
|
|
}
|
|
|
|
# Get the endpoint for service templates
|
|
service_template_endpoint = utils.get_object_endpoint(self._base_url, self._base_args, itsi_module, object_id)
|
|
self.logger.debug('Attempting get from service template endpoint: %s', service_template_endpoint)
|
|
|
|
# Construct the response object based on the request for service_template
|
|
response = utils.construct_get_response(service_template_endpoint, self.object_type, object_id, self.session_key, ['entity_rules'])
|
|
|
|
# Then, process service template(s) comma separated lists for recommended/optional KPIs and resolve KPIs
|
|
# from either KV Store or module interface if requested
|
|
parsed_response = self._make_optional_recommended_kpis_list(response, req_args, resolve_args, itsi_module)
|
|
|
|
self.logger.debug('Get response for service template %s in module %s: %s', object_id, itsi_module, json.dumps(response))
|
|
return parsed_response
|
|
|
|
def get_summary(self, itsi_module, **kwargs):
|
|
"""
|
|
Get basic information of services in a module
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module requested
|
|
"""
|
|
service_template_endpoint = utils.get_object_endpoint(self._base_url, self._base_args, itsi_module, None)
|
|
return utils.get_simple_response(service_template_endpoint, self.SUMMARY_KEYS, self.session_key, itsi_module)
|
|
|
|
def get_count(self, itsi_module, **kwargs):
|
|
"""
|
|
Returns the count of service_template in a given module
|
|
|
|
If itsi_module is specified as "-", returns counts of service templates for all modules
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module requested
|
|
"""
|
|
# Set up the endpoint
|
|
service_template_endpoint = utils.get_object_endpoint(self._base_url, self._base_args, itsi_module, None)
|
|
self.logger.debug('Attempting get_count from service template endpoint: %s', service_template_endpoint)
|
|
|
|
# Construct the response object based on the request for service_template
|
|
response = utils.construct_count_response(service_template_endpoint, itsi_module, self.object_type, self.session_key)
|
|
self.logger.debug('Get_count response for service template %s in module %s: %s', service_template_endpoint, itsi_module, json.dumps(response))
|
|
return response
|
|
|
|
def update(self, itsi_module, object_id, data, **kwargs):
|
|
"""
|
|
Update itsi_service_template conf file
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@type object_id: string
|
|
@param object_id: id of the object, it's also the stanza name in the conf file
|
|
|
|
@type data: dict
|
|
@param data: key/value pairs to be written to conf file
|
|
|
|
@rtype: string
|
|
@return: newly created id (conf stanza name) or raise an exception
|
|
"""
|
|
data_to_post = utils.filter_keys_reformat_certain_values_from_payload(itsi_module, self.ACCEPTED_KEYS, data)
|
|
response, content = utils.update_conf_stanza(self.session_key, self.CONF_FILE, object_id, data_to_post, itsi_module)
|
|
if response.status == 200 or response.status == 201:
|
|
self.logger.debug('Successfully updated service template %s in module %s', itsi_module, object_id)
|
|
return object_id
|
|
else:
|
|
self.logger.error('Failed to update service template object id %s', object_id)
|
|
raise utils.ItsiModuleError(status=400, message=_('Failed updating object id %s: %s.') % (object_id, content))
|
|
|
|
def delete(self, itsi_module, object_id, **kwargs):
|
|
"""
|
|
Delete stanza from itsi_service_template conf file
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@type object_id: string
|
|
@param object_id: stanza name (object id)
|
|
|
|
@rtype: None
|
|
@return: return nothing or raise an exception
|
|
"""
|
|
kpi_group = ItsiModuleKpiGroup(self.session_key)
|
|
try:
|
|
kpi_group.delete(itsi_module, object_id + '_KPIs')
|
|
except utils.ItsiModuleError as e:
|
|
self.logger.error('Failed to delete dependent KPI group. {}'.format(e.args[0]))
|
|
|
|
try:
|
|
existing_groups = kpi_group.get(itsi_module, None)
|
|
base_searches_used_by_other_service = set()
|
|
for group in existing_groups:
|
|
for kpi in group.get('content').get('kpis'):
|
|
base_searches_used_by_other_service.add(kpi.get('base_search_id'))
|
|
|
|
kpi_base_search = ItsiModuleKpiBaseSearch(self.session_key)
|
|
for search in kpi_base_search.get_existing_base_search_keys_from_conf(itsi_module):
|
|
if search not in base_searches_used_by_other_service:
|
|
kpi_base_search.delete(itsi_module, search)
|
|
except Exception as e:
|
|
self.logger.error('Failed to delete dependent KPI base search. {}'.format(e.args[0]))
|
|
|
|
response, content = utils.delete_conf_stanza(self.session_key, self.CONF_FILE, object_id, itsi_module)
|
|
if response.status == 200 or response.status == 201:
|
|
self.logger.debug('Successfully deleted service template %s in module %s', itsi_module, object_id)
|
|
return
|
|
else:
|
|
self.logger.error('Failed deleting service template object id: %s', object_id)
|
|
raise utils.ItsiModuleError(status=400, message=_('Failed deleting object id %s: %s.') % (object_id, content))
|
|
|
|
def validate(self, itsi_module, object_id):
|
|
"""
|
|
Validates the service template objects
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@type object_id: string
|
|
@param object_id: stanza name (object id)
|
|
|
|
@rtype: dictionary
|
|
@return: dictionary of validation result type to actual contents
|
|
"""
|
|
validation_errors = []
|
|
validation_infos = []
|
|
service_templates = self.get(itsi_module, object_id)
|
|
|
|
if len(service_templates) == 0:
|
|
validation_infos.append(
|
|
[itsi_module, itsi_module, self._validation_info_messages['service_template_missed']])
|
|
|
|
self.logger.debug('Found following validation information for service template %s in module %s: %s',
|
|
object_id, itsi_module, validation_infos)
|
|
|
|
else:
|
|
kpi_group_object = ItsiModuleKpiGroup(self.session_key)
|
|
kpi_groups = kpi_group_object.get(itsi_module, None)
|
|
self.logger.debug('kpi_groups is: %s', kpi_groups)
|
|
|
|
def get_kpi_template_ids_from_kpi_group(kpi_group):
|
|
return [kpi.get('kpi_template_kpi_id') for kpi in kpi_group.get('kpis')]
|
|
|
|
kpi_template_kpi_ids = reduce(
|
|
lambda id_list, kpi_group: id_list + get_kpi_template_ids_from_kpi_group(kpi_group.get('content', {})), kpi_groups, [])
|
|
|
|
for service_template in service_templates:
|
|
if not service_template.get('id', '').startswith(service_template.get('source_itsi_module')):
|
|
validation_errors.append(
|
|
utils.generate_validation_error_line(service_template, self._validation_error_messages['id_prefix_mismatch']))
|
|
|
|
if not service_template.get('content', {}).get('title'):
|
|
validation_errors.append(
|
|
utils.generate_validation_error_line(service_template, self._validation_error_messages['title_required']))
|
|
|
|
if not service_template.get('content', {}).get('description'):
|
|
validation_infos.append(
|
|
utils.generate_validation_error_line(service_template, self._validation_info_messages['description_missed']))
|
|
|
|
# Extracts the recommended_kpis string to make sure at least 1 recommended KPI is provided
|
|
recommended_kpis = service_template.get('content', {}).get('recommended_kpis', [])
|
|
|
|
if len(recommended_kpis) == 0:
|
|
validation_errors.append(
|
|
utils.generate_validation_error_line(service_template, self._validation_error_messages['atleast_1_recommended_kpi']))
|
|
|
|
# Now, extracts optional_kpis string, since it's not required
|
|
optional_kpis = service_template.get('content', {}).get('optional_kpis', [])
|
|
|
|
for kpi in recommended_kpis:
|
|
if kpi not in kpi_template_kpi_ids:
|
|
validation_errors.append(
|
|
utils.generate_validation_error_line(
|
|
service_template,
|
|
self._validation_error_messages['recommended_kpi_not_exported'].format(kpi)))
|
|
|
|
for kpi in optional_kpis:
|
|
if kpi not in kpi_template_kpi_ids:
|
|
validation_errors.append(
|
|
utils.generate_validation_error_line(
|
|
service_template,
|
|
self._validation_error_messages['optional_kpi_not_exported'].format(kpi)))
|
|
|
|
self.logger.debug('Found following validation errors for service template %s in module %s: %s', object_id, itsi_module, validation_errors)
|
|
self.logger.debug('Found following validation information for service template %s in module %s: %s', object_id, itsi_module, validation_infos)
|
|
|
|
return utils.construct_validation_result(errors=validation_errors, infos=validation_infos)
|
|
|
|
def _build_module_kpi_mapping(self, itsi_module, resolve_path, req_args, **kwargs):
|
|
"""
|
|
Build the mapping between modules, KPI IDs, and KPI objects by calling build_module_kpi_mapping in ItsiModuleKpiGroup
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module that is being requested for KPI groups
|
|
|
|
@type resolve_path: string
|
|
@param resolve_path: This can be either 'kv_store' or 'itsi_module_interface', and it determines
|
|
whether to build the mapping and resolve the KPIs from either calling the itsi_module_interface
|
|
KPI group endpoint or the KV store KPI template endpoint
|
|
|
|
@type req_args: dict
|
|
@param req_args: Arguments that are provided to filter or request specific fields when making
|
|
a request from the KV store
|
|
"""
|
|
if resolve_path == 'itsi_module_interface':
|
|
self.logger.debug('resolve_kpis flag has been set to true. Building module-KPI mapping from itsi_module_interface...')
|
|
kpi_groups = ItsiModuleKpiGroup(self.session_key)
|
|
return kpi_groups.build_module_kpi_mapping(itsi_module=itsi_module)
|
|
else:
|
|
self.logger.debug('resolve_kpis_from_itsi has been set to true. Building module-KPI mapping from KV store...')
|
|
return utils.build_module_kpi_mapping_kv_store(session_key=self.session_key, kv_store_args=req_args)
|
|
|
|
def _strip_kpi(self, kpi):
|
|
"""
|
|
Strips the leading and trailing characters of each KPI
|
|
|
|
@type kpi: string
|
|
@param kpi: KPI ID
|
|
"""
|
|
return kpi.strip()
|
|
|
|
def _parse_recommended_and_optional_kpis(self, service_template, itsi_module, **kwargs):
|
|
"""
|
|
Parses out recommended KPIs comma separated string to an array
|
|
|
|
Parses out optional KPIs comma separated string to an array if field exists
|
|
|
|
If string ends up being empty for optional KPIs, deletes it out
|
|
|
|
@type service_template: dict
|
|
@param service_template: Service template dictionary
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: Requested ITSI module
|
|
"""
|
|
# Goes through keys 'recommended_kpis' and 'optional_kpis'. If they exist, split by comma
|
|
# into list, and if they exist but are empty strings, initialize to empty list
|
|
keys_to_check = ['recommended_kpis', 'optional_kpis']
|
|
for key in keys_to_check:
|
|
if key not in service_template['content'] or service_template['content'][key] == '':
|
|
service_template['content'][key] = []
|
|
else:
|
|
# Stripping leading/trailing characters for each KPI
|
|
service_template['content'][key] = list(map(self._strip_kpi, service_template['content'][key].split(',')))
|
|
return service_template
|
|
|
|
def _compute_fields(self, kpi_fields_string):
|
|
"""
|
|
Computes the fields string to only return the number of fields requested
|
|
Regardless of fields requested, every request must return 'source_itsi_da'
|
|
and 'kpis.kpi_template_kpi_id' so that the module mapping can be generated
|
|
in order to resolve the KPIs
|
|
|
|
@type kpi_fields_string: string
|
|
@param kpi_fields_string: comma separated string of fields requested in the URL along
|
|
with the base fields requested above
|
|
"""
|
|
base_fields = ['source_itsi_da', 'kpis.kpi_template_kpi_id']
|
|
kpi_fields = (kpi_fields_string.split(',') + base_fields) if kpi_fields_string else base_fields
|
|
return ','.join(kpi_fields)
|
|
|
|
def _compute_filter(self, itsi_module, url_filter):
|
|
"""
|
|
If a module context is given that isn't a request for all modules, returns the
|
|
base filter that includes the requested 'source_itsi_da' as the module context
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: Requested ITSI module
|
|
|
|
@type url_filter: dict
|
|
@param url_filter: Items to filter the query to KV store by
|
|
"""
|
|
base_filter = {
|
|
'$and': [
|
|
]
|
|
}
|
|
if itsi_module == '-':
|
|
base_filter = {}
|
|
else:
|
|
base_filter['$and'].append({'source_itsi_da': itsi_module})
|
|
base_filter['$and'] = base_filter['$and'] + url_filter['$and']
|
|
return base_filter
|
|
|
|
def _map_kpi_id_to_payload(self, module_kpi_mapping, kpi_id, service_template):
|
|
"""
|
|
Utility method called by map which returns the KPI object given a KPI ID
|
|
|
|
@type module_kpi_mapping: dict
|
|
@param module_kpi_mapping: A mapping from the module to KPI IDs, and below that the KPI IDs
|
|
to the physical KPI objects
|
|
|
|
@type kpi_id: string
|
|
@param kpi_id: The ID of the KPI being retrieved from the map
|
|
|
|
@param service_template: dict
|
|
@type service_template: Service template object
|
|
"""
|
|
return module_kpi_mapping[service_template['source_itsi_module']][kpi_id]
|
|
|
|
def _resolve_kpis(self, module_kpi_mapping, service_template):
|
|
"""
|
|
Resolves the KPIs given in a service template by using the mapping between modules and KPI ids
|
|
to replace each KPI id in the reccommended/optional KPI lists with their entire objects
|
|
|
|
@type module_kpi_mapping: dict
|
|
@param module_kpi_mapping: A mapping from the module to KPI IDs, and below that the KPI IDs
|
|
to the physical KPI objects
|
|
|
|
@type service_template: dict
|
|
@param service_template: Service template object
|
|
"""
|
|
# Aliases to cut down on amount of typing in subsequent lines
|
|
rec_kpis = service_template['content']['recommended_kpis']
|
|
opt_kpis = service_template['content']['optional_kpis']
|
|
|
|
# Map all of the KPI ids to their physical objects
|
|
service_template['content']['recommended_kpis'] = \
|
|
[self._map_kpi_id_to_payload(module_kpi_mapping, rec_kpi, service_template) for rec_kpi in rec_kpis]
|
|
service_template['content']['optional_kpis'] = \
|
|
[self._map_kpi_id_to_payload(module_kpi_mapping, opt_kpi, service_template) for opt_kpi in opt_kpis]
|
|
return service_template
|
|
|
|
def _make_optional_recommended_kpis_list(self, response, req_args, resolve_args, itsi_module):
|
|
"""
|
|
Converts the response from an endpoint from the comma separated list of recommended and optional KPIs to arrays.
|
|
|
|
Either does this for a single service template if requested by an ID, or for all the service templates returned in the list
|
|
|
|
If resolve_kpis is specified as true, fetches the KPI groups from its endpoint,
|
|
builds a mapping from modules to their KPIs by ID, and then resolves the KPIs
|
|
in the place of the ID
|
|
|
|
@type response: dict or array
|
|
@param response: Either a single service template dict or a list of service templates
|
|
|
|
@type req_args: dict
|
|
@param req_args: Arguments given to itsi_module_interface which provide the necessary filter and fields
|
|
requested arguments if KPIs should be resolved from KV store
|
|
|
|
@type resolve_args: dict
|
|
@param resolve_args: Arguments given to determine whether KPIs should be resolved, and if so,
|
|
whether to be resolved via itsi_module_interface or KV store
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: Requested ITSI module
|
|
"""
|
|
# Get the mapping between module(s) requested and their KPIs
|
|
# Only one of these can evaluate by definition, since both flags set to true will throw
|
|
# an exception up the call stack
|
|
module_kpi_mapping = {}
|
|
if resolve_args['resolve_kpis']:
|
|
module_kpi_mapping = self._build_module_kpi_mapping(itsi_module, 'itsi_module_interface', req_args)
|
|
if resolve_args['resolve_kpis_from_itsi']:
|
|
module_kpi_mapping = self._build_module_kpi_mapping(itsi_module, 'kv_store', req_args)
|
|
|
|
# Single service template request by ID
|
|
if isinstance(response, dict):
|
|
response = self._parse_recommended_and_optional_kpis(response, response['source_itsi_module'])
|
|
if resolve_args['resolve_kpis'] or resolve_args['resolve_kpis_from_itsi']:
|
|
return self._resolve_kpis(module_kpi_mapping, response)
|
|
|
|
# Either request for all service templates for single module or all modules
|
|
else:
|
|
for service_template in response:
|
|
# Parse the recommended and optional KPIs to an array from their comma separated string
|
|
parsed_service_template = self._parse_recommended_and_optional_kpis(
|
|
service_template, service_template['source_itsi_module'])
|
|
service_template = self._resolve_kpis(module_kpi_mapping, parsed_service_template) \
|
|
if resolve_args['resolve_kpis'] or resolve_args['resolve_kpis_from_itsi'] else parsed_service_template
|
|
return response
|