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.
802 lines
27 KiB
802 lines
27 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
|
|
"""
|
|
Utility module for itsi_module.
|
|
"""
|
|
import os
|
|
import re
|
|
import sys
|
|
import json
|
|
from urllib.parse import quote_plus
|
|
|
|
import splunk.rest as rest
|
|
from .itsi_module_package import itsi_module_builder_util as builder_util
|
|
|
|
from itsi_py3 import _
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
from splunk import ResourceNotFound, RESTException
|
|
from ITOA.setup_logging import logger
|
|
from ITOA.controller_utils import HTTPError
|
|
from splunk.util import normalizeBoolean
|
|
|
|
_ALL_MODULES = '-'
|
|
_KV_STORE_BASE_URL = 'servicesNS/nobody/SA-ITOA/itoa_interface/kpi_template'
|
|
|
|
_ICON_BASE_ENDPOINT = 'servicesNS/nobody/{}/static/{}'
|
|
_SETTINGS_CONF_FILE = 'itsi_module_settings'
|
|
|
|
|
|
class ItsiModuleError(HTTPError):
|
|
"""
|
|
Set the status and msg on the response
|
|
|
|
I.e.
|
|
raise ITOAEntityError(
|
|
status=500, message=_("Your call is very important to us ..."))
|
|
"""
|
|
|
|
def get_error_page(self, *args, **kwargs):
|
|
"""
|
|
Returns the error page
|
|
"""
|
|
kwargs['noexname'] = 'true'
|
|
return super(ItsiModuleError, self).get_error_page(*args, **kwargs)
|
|
|
|
|
|
def get_object_endpoint(base_url, base_args, itsi_module, object_id, **kwargs):
|
|
"""
|
|
Constructs the endpoint for a request
|
|
|
|
@type base_url: string
|
|
@param base_url: the base url
|
|
|
|
@type base_args: string
|
|
@param base_args: base query parameters/arguments
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: the module ID
|
|
|
|
@type object_id: string
|
|
@param object_id: the ITSI object ID
|
|
|
|
@type kwargs: dict
|
|
@param kwargs: additional arguments
|
|
|
|
@rtype: string
|
|
@return: the constructed endpoint
|
|
"""
|
|
# If an object_id is given, set the request endpoint to be "/conf-itsi_kpi_template/<object_id>"
|
|
if object_id is not None:
|
|
return (base_url % itsi_module) + '/' + object_id + base_args
|
|
# Otherwise, the request endpoint will find all kpi templates for the given module,
|
|
# or if module is given as "-", will find kpi templates across all modules
|
|
else:
|
|
url_search = (('&search=eai:acl.app=' + itsi_module) if itsi_module != '-' else '&search=DA-ITSI')
|
|
url = (base_url % itsi_module) + base_args
|
|
return url + url_search
|
|
|
|
|
|
def make_http_get(endpoint, session_key, **kwargs):
|
|
"""
|
|
Makes an HTTP GET request, and returns the payload if successful, otherwise throws a 404 if content was not found
|
|
|
|
@type endpoint: string
|
|
@param endpoint: endpoint to make request to
|
|
|
|
@type session_key: string
|
|
@param session_key: session key to make sure HTTP request is authenticated
|
|
"""
|
|
try:
|
|
# Makes an HTTP GET request to the endpoint for the objects
|
|
response, payload = rest.simpleRequest(endpoint, method='GET', sessionKey=session_key, getargs=kwargs)
|
|
return payload
|
|
except ResourceNotFound:
|
|
# Raise an exception if the module or ID doesn't exist
|
|
raise ResourceNotFound(_('The requested module or ID was not found.'))
|
|
|
|
|
|
def extract_metadata_each_module(module, itsi_module_settings):
|
|
"""
|
|
Used to modify each module object that is returned from REST call in order to only return relevant information to the end-user
|
|
|
|
Includes the base64 encoded icon for the module if the flag "include_icon_data" is passed when making
|
|
the request
|
|
|
|
@type module: dict
|
|
@param itsi_module_settings: dict contains module settings
|
|
|
|
@type module: dict
|
|
@param module: The object describing each module from apps/local
|
|
"""
|
|
content = module['content']
|
|
name = module['name']
|
|
content['package_name'] = name
|
|
del content['disabled']
|
|
content['is_read_only'] = itsi_module_settings.get(name, False)
|
|
content['last_exported_date'] = get_last_exported_date(name)
|
|
return content
|
|
|
|
|
|
def get_itsi_module_settings(session_key):
|
|
get_args = {'output_mode': 'json'}
|
|
try:
|
|
content = json.loads(make_http_get(make_conf_uri(_SETTINGS_CONF_FILE, 'SA-ITOA'), session_key,
|
|
**get_args))
|
|
except Exception as e:
|
|
logger.error('Error while reading itsi_module_settings. {}'.format(e.args[0]))
|
|
return {}
|
|
settings_stanza = 'settings://'
|
|
readonly_settings = {}
|
|
for entry in content['entry']:
|
|
if entry['name'].startswith(settings_stanza):
|
|
app = entry['name'].split(settings_stanza)[1]
|
|
readonly_settings[app] = normalizeBoolean(entry['content'].get('is_read_only'))
|
|
return readonly_settings
|
|
|
|
|
|
def include_meta_file_info(itsi_module, metadata):
|
|
app_folder = make_splunkhome_path(['etc', 'apps', itsi_module])
|
|
readme = os.path.join(app_folder, 'README.txt')
|
|
small_icon_file = 'appIcon.png'
|
|
large_icon_file = 'appIcon_2x.png'
|
|
small_icon = os.path.join(app_folder, 'static', small_icon_file)
|
|
large_icon = os.path.join(app_folder, 'static', large_icon_file)
|
|
|
|
metadata['readme'] = os.path.isfile(readme)
|
|
metadata['small_icon'] = _ICON_BASE_ENDPOINT.format(itsi_module, small_icon_file) \
|
|
if os.path.isfile(small_icon) else ''
|
|
metadata['large_icon'] = _ICON_BASE_ENDPOINT.format(itsi_module, large_icon_file) \
|
|
if os.path.isfile(large_icon) else ''
|
|
return metadata
|
|
|
|
|
|
def construct_metadata_response(endpoint, itsi_module, session_key, **kwargs):
|
|
"""
|
|
Constructs the response for the metadata endpoint at /itsi_module_interface/:module
|
|
|
|
@type endpoint: string
|
|
@param endpoint: HTTP endpoint that was requested
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module that was requested
|
|
|
|
@type session_key: string
|
|
@param session_key: session key to make sure HTTP request is authenticated
|
|
"""
|
|
payload = make_http_get(endpoint, session_key)
|
|
|
|
# Response loaded as a string, we want to modify it as a dict
|
|
payload_dict = json.loads(payload)
|
|
|
|
itsi_module_settings = get_itsi_module_settings(session_key)
|
|
|
|
# If request is for a single module, just return the one, otherwise map through
|
|
# the list of module metadata and return that result
|
|
if itsi_module != _ALL_MODULES:
|
|
metadata = extract_metadata_each_module(payload_dict['entry'][0], itsi_module_settings)
|
|
return include_meta_file_info(itsi_module, metadata)
|
|
return [extract_metadata_each_module(module, itsi_module_settings) for module in payload_dict['entry']]
|
|
|
|
|
|
def strip_eai_keys(object):
|
|
"""
|
|
Removes all keys from response object of the form "eai:<>"
|
|
|
|
@type object: dict
|
|
@param object: the object from which to strip the eai key
|
|
|
|
@rtype: list
|
|
@return: the list of removed keys
|
|
"""
|
|
if type(object) is dict:
|
|
eai_keys = list(k for k, v in list(object.items()) if k.startswith('eai:'))
|
|
for key in eai_keys:
|
|
del object[key]
|
|
return eai_keys
|
|
|
|
|
|
def parse_json_blob_fields(object, obj_json_blob_fields):
|
|
"""
|
|
Converts a string field into a dict/JSON object in the same field
|
|
|
|
@type object: dict
|
|
@param object: the object content field from within the fields are to be parsed
|
|
|
|
@type obj_json_blob_fields: list
|
|
@param obj_json_blob_fields: a list of fields that this operation is applied on within the object
|
|
"""
|
|
if type(obj_json_blob_fields) is list:
|
|
for field in obj_json_blob_fields:
|
|
try:
|
|
object['content'][field] = json.loads(object['content'][field])
|
|
except Exception as e:
|
|
logger.exception(e)
|
|
continue
|
|
return object
|
|
|
|
|
|
def construct_get_response(endpoint, object_type, object_id, session_key, obj_json_blob_fields, **kwargs):
|
|
"""
|
|
This constructs the response for the HTTP get from either /itsi_module_interface/:module/:object or from /itsi_module_interface/:module/:object/:id_
|
|
|
|
@type endpoint: string
|
|
@param endpoint: Endpoint from which to retrieve object
|
|
|
|
@type object_type: string
|
|
@param object_type: object type being included. Can be "kpi_group", "kpi_base_search", "service_template"
|
|
or "entity_source_template"
|
|
|
|
@type object_id: string
|
|
@param object_id: when provided, the ID of the object to fetch
|
|
|
|
@type session_key: string
|
|
@param session_key: The active session key to authenticate HTTP requests with
|
|
|
|
@rtype: list
|
|
@return: the list of object(s)
|
|
"""
|
|
# Parse the response string into a dict
|
|
payload_dict = json.loads(make_http_get(endpoint, session_key))
|
|
|
|
response_obj_list = []
|
|
|
|
object_type = object_type if object_type is not None else ''
|
|
logger.debug('construct_get_response: object_type=%s', object_type)
|
|
|
|
# If the request only specified a single object (given an ID), return the
|
|
# first index from content in the payload along with other metadata
|
|
if object_id is not None:
|
|
# Strip all keys that contain "eai" in them
|
|
strip_eai_keys(payload_dict['entry'][0]['content'])
|
|
response_obj = {
|
|
'source_itsi_module': payload_dict['entry'][0]['acl']['app'],
|
|
'object_type': object_type,
|
|
# Note that object_id could be url encoded, for example, for entity_source_template.
|
|
# So set id using entry name from the request response instead.
|
|
'id': payload_dict['entry'][0]['name'],
|
|
'content': payload_dict['entry'][0]['content']
|
|
}
|
|
response_obj_list.append(parse_json_blob_fields(response_obj, obj_json_blob_fields))
|
|
# Otherwise, construct list of objects from the HTTP response for a module
|
|
else:
|
|
for entry_item in payload_dict['entry']:
|
|
# Strip all keys that contain "eai" in them
|
|
strip_eai_keys(entry_item['content'])
|
|
if entry_item['acl']['app'].startswith('DA-ITSI'):
|
|
obj_to_add = {
|
|
'source_itsi_module': entry_item['acl']['app'],
|
|
'object_type': object_type,
|
|
'id': entry_item['name'],
|
|
'content': entry_item['content']
|
|
}
|
|
obj_to_add = parse_json_blob_fields(obj_to_add, obj_json_blob_fields)
|
|
response_obj_list.append(obj_to_add)
|
|
|
|
return response_obj_list
|
|
|
|
|
|
def construct_count_response(endpoint, itsi_module, object_type, session_key, **kwargs):
|
|
"""
|
|
Constructs the response for the metadata endpoint at /itsi_module_interface/:module
|
|
|
|
@type endpoint: string
|
|
@param endpoint: HTTP endpoint that was requested
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module that was requested
|
|
|
|
@type object_type: string
|
|
@param object_type: object type being included. Can be "kpi_group", "kpi_base_search", "service_template"
|
|
or "entity_source_template"
|
|
|
|
@type session_key: string
|
|
@param session_key: session key to make sure HTTP request is authenticated
|
|
"""
|
|
# Parse the response string into a dict
|
|
payload_dict = json.loads(make_http_get(endpoint, session_key))
|
|
|
|
# If the request only specified a single module, construct response object directly from size field
|
|
if itsi_module != '-':
|
|
return {
|
|
object_type: payload_dict['paging']['total']
|
|
}
|
|
|
|
# Otherwise, loop through response object and construct counts for each service template in all modules
|
|
else:
|
|
response_dict = {}
|
|
read_kpi_count = object_type == 'kpi_group'
|
|
for element in payload_dict['entry']:
|
|
curr_module = element['acl']['app']
|
|
if curr_module.startswith('DA-ITSI'):
|
|
_increment_count(curr_module, object_type, response_dict)
|
|
if read_kpi_count:
|
|
try:
|
|
# In case of malformatted kpis, ignore this field
|
|
kpis = json.loads(element['content']['kpis'])
|
|
_increment_count(curr_module, 'kpis', response_dict, len(kpis))
|
|
except Exception as e:
|
|
logger.exception(e)
|
|
return response_dict
|
|
|
|
|
|
def _increment_count(curr_module, object_type, response_dict, count=1):
|
|
"""
|
|
Increment the count of specified module and object type, create the entry if it doesn't exist
|
|
@type curr_module: string
|
|
@param curr_module: module name
|
|
|
|
@type object_type: string
|
|
@param object_type: object name
|
|
|
|
@type response_dict: dict
|
|
@param response_dict: the count dict
|
|
|
|
@type count: int
|
|
@param count: amount to increment, defaults to 1
|
|
@return: None
|
|
"""
|
|
if curr_module not in response_dict:
|
|
response_dict[curr_module] = {}
|
|
if object_type not in response_dict[curr_module]:
|
|
response_dict[curr_module][object_type] = 0
|
|
response_dict[curr_module][object_type] += count
|
|
|
|
|
|
def make_conf_uri(conf_name, itsi_module):
|
|
"""
|
|
Construct uri for editing conf files
|
|
|
|
@type conf_name: string
|
|
@param conf_name: name of the conf file
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@rtype: string
|
|
@return: url for editing conf files
|
|
"""
|
|
return rest.makeSplunkdUri() + 'servicesNS/nobody/' + itsi_module + '/configs/conf-' + conf_name
|
|
|
|
|
|
def create_conf_stanza(session_key, conf_name, conf_stanza, itsi_module):
|
|
"""
|
|
Create conf stanza by calling splunk conf endpoints
|
|
|
|
@type session_key: string
|
|
@param session_key: session_key
|
|
|
|
@type conf_name: string
|
|
@param conf_name: conf file name
|
|
|
|
@type conf_stanza: dict
|
|
@param conf_stanza: dict of data to post
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: itsi_module name
|
|
|
|
@rtype: tuple
|
|
@return: response and content or raise an exception
|
|
"""
|
|
postargs = conf_stanza
|
|
postargs['output_mode'] = 'json'
|
|
conf_uri = make_conf_uri(conf_name, itsi_module)
|
|
try:
|
|
response, content = rest.simpleRequest(
|
|
conf_uri,
|
|
method="POST",
|
|
postargs=postargs,
|
|
sessionKey=session_key,
|
|
raiseAllErrors=True
|
|
)
|
|
return response, content
|
|
except ResourceNotFound:
|
|
raise ItsiModuleError(status=404, message=_('Requested itsi_module does not exist.'))
|
|
except RESTException as restException:
|
|
raise ItsiModuleError(status=400, message=restException.get_message_text())
|
|
except:
|
|
raise ItsiModuleError(status=400, message=_('Error writing data into conf: %s.') % (sys.exc_info()[0]))
|
|
|
|
|
|
def update_conf_stanza(session_key, conf_name, conf_stanza_name, data_to_post, itsi_module):
|
|
"""
|
|
Update conf stanza by calling splunk conf endpoints
|
|
|
|
@type session_key: string
|
|
@param session_key: session_key
|
|
|
|
@type conf_name: string
|
|
@param conf_name: conf file name
|
|
|
|
@type conf_stanza_name: string
|
|
@param conf_stanza: stanza name to update
|
|
|
|
@type data_to_post: dict
|
|
@param data_to_post: dict of data
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: itsi_module name
|
|
|
|
@rtype: tuple
|
|
@return: response and content or raise an exception
|
|
"""
|
|
postargs = data_to_post
|
|
postargs['output_mode'] = 'json'
|
|
conf_uri = make_conf_uri(
|
|
conf_name, itsi_module) + '/' + quote_plus(conf_stanza_name)
|
|
try:
|
|
response, content = rest.simpleRequest(
|
|
conf_uri,
|
|
method="POST",
|
|
postargs=postargs,
|
|
sessionKey=session_key,
|
|
raiseAllErrors=True
|
|
)
|
|
return response, content
|
|
except ResourceNotFound:
|
|
raise ItsiModuleError(status=404, message=_('Requested itsi_module does not exist.'))
|
|
except:
|
|
raise ItsiModuleError(status=400, message=_('Error updating %s: %s.') % (conf_stanza_name, sys.exc_info()[0]))
|
|
|
|
|
|
def delete_conf_stanza(session_key, conf_name, conf_stanza_name, itsi_module):
|
|
"""
|
|
Delete conf stanza by calling splunk conf endpoints
|
|
|
|
@type session_key: string
|
|
@param session_key: session_key
|
|
|
|
@type conf_name: string
|
|
@param conf_name: conf file name
|
|
|
|
@type conf_stanza_name: string
|
|
@param conf_stanza: stanza name to update
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: itsi_module name
|
|
|
|
@rtype: tuple
|
|
@return: response and content or raise an exception
|
|
"""
|
|
conf_uri = make_conf_uri(conf_name, itsi_module) + '/' + \
|
|
quote_plus(conf_stanza_name)
|
|
try:
|
|
response, content = rest.simpleRequest(
|
|
conf_uri,
|
|
method="DELETE",
|
|
sessionKey=session_key,
|
|
raiseAllErrors=True
|
|
)
|
|
return response, content
|
|
except ResourceNotFound:
|
|
raise ItsiModuleError(status=404, message=_('Requested itsi_module does not exist.'))
|
|
except:
|
|
raise ItsiModuleError(status=400, message=_('Error updating %s: %s.') % (conf_stanza_name, sys.exc_info()[0]))
|
|
|
|
|
|
def templatize_obj_by_id(session_key, object, object_id):
|
|
"""
|
|
Templatize an object by id
|
|
|
|
@type session_key: string
|
|
@param session_key: session_key
|
|
|
|
@type object: string
|
|
@param object: object defined in manifest
|
|
|
|
@type object_id: string
|
|
@param object_id: id of the object
|
|
|
|
@rtype: tuple
|
|
@return: response and content or raise an exception
|
|
"""
|
|
templatize_uri = rest.makeSplunkdUri() + 'servicesNS/nobody/SA-ITOA/itoa_interface/%s/%s/templatize' % (
|
|
object, object_id)
|
|
try:
|
|
response, content = rest.simpleRequest(
|
|
templatize_uri,
|
|
method='GET',
|
|
sessionKey=session_key,
|
|
raiseAllErrors=True
|
|
)
|
|
return response, content
|
|
|
|
except RESTException:
|
|
raise ItsiModuleError(status=404,
|
|
message=_('Requested itsi_object: %s / object_id=%s does not exist.') % (object, object_id))
|
|
except:
|
|
raise ItsiModuleError(status=400, message=_('Error templatizing %s id: %s.') % (object, object_id))
|
|
|
|
|
|
def make_stanza_name(itsi_module, object_title, **kwargs):
|
|
"""
|
|
Make object id (stanza name) using the convention <itsi_module>-<title with space replaced by _)
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@type object_title: string
|
|
@param object_title: value of the object title
|
|
|
|
@rtype: string
|
|
@return: generated stanza_name or raise an exception
|
|
"""
|
|
if not object_title:
|
|
raise ItsiModuleError(status=400, message=_('Object title cannot be empty or none.'))
|
|
object_title_with_no_special_chars = replace_special_chars_with_underscore(object_title.strip())
|
|
stanza_name = '-'.join([itsi_module, object_title_with_no_special_chars])
|
|
if 'prefix' in kwargs and kwargs['prefix']:
|
|
stanza_name = kwargs['prefix'] + stanza_name
|
|
if 'suffix' in kwargs and kwargs['suffix']:
|
|
suffix = kwargs['suffix'].strip().replace(' ', '_')
|
|
stanza_name = stanza_name + '_' + suffix
|
|
return stanza_name
|
|
|
|
|
|
def format_value(value):
|
|
"""
|
|
Format value based on its type
|
|
|
|
@type value: dict,list or string
|
|
@param value: value that needs to be re-formatted
|
|
|
|
@rtype: string
|
|
@return: re-formatted value
|
|
"""
|
|
if isinstance(value, dict) or isinstance(value, list):
|
|
return json.dumps(value, sort_keys=True, indent=4)
|
|
else:
|
|
return str(value)
|
|
|
|
|
|
def filter_keys_reformat_certain_values_from_payload(itsi_module, accepted_keys, data):
|
|
"""
|
|
Filter keys and reformat certain values for writing into conf
|
|
|
|
For example, metrics field's value needs to be written to conf
|
|
as json format.
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: ITSI module name
|
|
|
|
@type accepted_keys: list
|
|
@param accepted_keys: a list of all accepted keys
|
|
|
|
@type data: dict
|
|
@param data: dict of key, values to be filtered
|
|
|
|
@rtype: dict
|
|
@return: dict of data with filtered keys and re-formatted data
|
|
"""
|
|
filtered_payload = {}
|
|
# Take intersection of accepted_keys and data
|
|
keys = [key for key in data if key in accepted_keys]
|
|
|
|
for key in keys:
|
|
if key == 'metrics':
|
|
filtered_payload[key] = format_value(data.get('metrics'))
|
|
elif key == 'source_itsi_da':
|
|
filtered_payload[key] = itsi_module
|
|
elif key == 'kpis':
|
|
filtered_payload[key] = format_value(data.get('kpis'))
|
|
elif key == 'entity_rules':
|
|
filtered_payload[key] = format_value(data.get('entity_rules'))
|
|
else:
|
|
filtered_payload[key] = data.get(key)
|
|
return filtered_payload
|
|
|
|
|
|
def build_module_kpi_mapping_kv_store(session_key, kv_store_args):
|
|
"""
|
|
Builds the mapping from module -> KPI id -> KPI, except the values
|
|
for KPIs are retrieved from KV-store
|
|
|
|
@type session_key: string
|
|
@param session_key: Session key for HTTP request
|
|
|
|
@type kv_store_args: dict
|
|
@param kv_store_args: arguments that determine any filter or fields requested
|
|
"""
|
|
kpi_groups = {}
|
|
module_kpi_mapping = {}
|
|
response, content = rest.simpleRequest(
|
|
rest.makeSplunkdUri() + _KV_STORE_BASE_URL,
|
|
method='GET',
|
|
getargs=kv_store_args,
|
|
sessionKey=session_key
|
|
)
|
|
if response.status == 500:
|
|
raise ItsiModuleError(status=500, message=content)
|
|
try:
|
|
kpi_groups = json.loads(content)
|
|
except Exception as e:
|
|
raise ItsiModuleError(status=400, message=e.args[0])
|
|
|
|
for kpi_group in kpi_groups:
|
|
if kpi_group['source_itsi_da'] not in module_kpi_mapping:
|
|
module_kpi_mapping[kpi_group['source_itsi_da']] = {}
|
|
for kpi in kpi_group['kpis']:
|
|
module_kpi_mapping[kpi_group['source_itsi_da']][kpi['kpi_template_kpi_id']] = kpi
|
|
return module_kpi_mapping
|
|
|
|
|
|
def get_conf_by_namespace(session_key, conf_name, app='itsi', count=-1):
|
|
"""
|
|
Get content of a specific conf file under given namespace
|
|
|
|
@type session_key: string
|
|
@param session_key: splunk session_key
|
|
|
|
@type conf_name: string
|
|
@param conf_name: conf file name
|
|
|
|
@type filter: dict
|
|
@param filter: filter params
|
|
supported by splunk: http://docs.splunk.com/Documentation/Splunk/6.4.2/RESTREF/RESTprolog#Pagination_and_filtering_parameters
|
|
|
|
@type app: string
|
|
@param app: namespace to be filtered by
|
|
|
|
@type count: int
|
|
@param count: number of results that will be returned
|
|
|
|
@rtype: tuple
|
|
@return: tuple of response and content or raise an exception
|
|
|
|
"""
|
|
getargs = {
|
|
'output_mode': 'json',
|
|
'count': count,
|
|
'search': 'eai:acl.app=%s' % app
|
|
}
|
|
conf_uri = make_conf_uri(conf_name, app)
|
|
try:
|
|
response, content = rest.simpleRequest(
|
|
conf_uri,
|
|
method="GET",
|
|
getargs=getargs,
|
|
sessionKey=session_key,
|
|
raiseAllErrors=True
|
|
)
|
|
return response, content
|
|
except ResourceNotFound:
|
|
raise ItsiModuleError(status=404, message=_('Requested module/conf file does not exist.'))
|
|
except:
|
|
raise ItsiModuleError(status=400,
|
|
message=_('Error getting content of conf file %s: %s.') % (conf_name, sys.exc_info()[0]))
|
|
|
|
|
|
def generate_validation_error_line(object_instance, message):
|
|
"""
|
|
Generates a list that contains details about a validation error
|
|
|
|
@type object_instance: object
|
|
@param object_instance: the module object
|
|
|
|
@type message: string
|
|
@param message: the error message
|
|
|
|
@rtype: list
|
|
@return: a list of module name, object ID and the message
|
|
|
|
"""
|
|
return [object_instance['source_itsi_module'], object_instance['id'], message]
|
|
|
|
|
|
def get_obj_by_id(session_key, object, object_id):
|
|
"""
|
|
Get an ITSI object by id
|
|
|
|
@type session_key: string
|
|
@param session_key: session_key
|
|
|
|
@type object: string
|
|
@param object: object defined in manifest
|
|
|
|
@type object_id: string
|
|
@param object_id: id of the object
|
|
|
|
@rtype: tuple
|
|
@return: response and content or raise an exception
|
|
"""
|
|
uri = rest.makeSplunkdUri() + 'servicesNS/nobody/SA-ITOA/itoa_interface/%s/%s' % (object, object_id)
|
|
try:
|
|
response, content = rest.simpleRequest(
|
|
uri,
|
|
method='GET',
|
|
sessionKey=session_key,
|
|
raiseAllErrors=True
|
|
)
|
|
return (response, content)
|
|
|
|
except ResourceNotFound:
|
|
raise ItsiModuleError(status=404,
|
|
message=_('Requested itsi_object: %s / object_id=%s does not exist.') % (object, object_id))
|
|
except:
|
|
raise ItsiModuleError(status=400, message=_('Error getting %s id: %s.') % (object, object_id))
|
|
|
|
|
|
def replace_special_chars_with_underscore(string):
|
|
"""
|
|
Replace special characters (except _.-) in a string with _
|
|
|
|
@type string: string
|
|
@param string: string to replace special chars
|
|
|
|
@rtype: string
|
|
@return: updated string with special characters replaced by _
|
|
"""
|
|
try:
|
|
string_with_no_special_chars = re.sub('[^a-zA-Z0-9\._-]+', '_', string)
|
|
return re.sub('\_+', '_', string_with_no_special_chars)
|
|
except TypeError:
|
|
raise TypeError(_('Cannot replace special characters in string: %s.') % string)
|
|
|
|
|
|
def construct_validation_result(**kwargs):
|
|
"""
|
|
Construct validation result based on arguments.
|
|
|
|
@type kwargs: args
|
|
@param kwargs: Key word arguments
|
|
|
|
@rtype: dictionary
|
|
@return: dictionary of validation result type to actual contents
|
|
"""
|
|
validation_result = {}
|
|
|
|
if kwargs:
|
|
for key, value in kwargs.items():
|
|
if value and isinstance(value, list) and len(value):
|
|
validation_result[key] = value
|
|
return validation_result
|
|
|
|
|
|
def get_last_exported_date(itsi_module):
|
|
package_name = builder_util.get_download_package_name(itsi_module)
|
|
full_path = builder_util.get_package_file_full_path_with_package_name(package_name)
|
|
return os.path.getmtime(full_path) if os.path.isfile(full_path) else 0
|
|
|
|
|
|
def get_simple_response(endpoint, keys, session_key, itsi_module):
|
|
"""
|
|
Get simplified response for a module object request. only include id and requested keys in response
|
|
@type endpoint: string
|
|
@param endpoint: the conf endpoint
|
|
|
|
@type keys: list
|
|
@param keys: requested keys
|
|
|
|
@type session_key: string
|
|
@param session_key: the session key
|
|
|
|
@type itsi_module: string
|
|
@param itsi_module: the requested module
|
|
|
|
@rtype: dict
|
|
@return: extracted response
|
|
"""
|
|
entries = json.loads(make_http_get(endpoint, session_key))['entry']
|
|
response = []
|
|
is_all_module = itsi_module == _ALL_MODULES
|
|
for entry in entries:
|
|
if is_all_module or entry['acl']['app'] == itsi_module:
|
|
entry = parse_json_blob_fields(entry, keys)
|
|
content = entry['content']
|
|
extracted = {k: content[k] for k in keys if k in content}
|
|
extracted['id'] = entry['name']
|
|
response.append(extracted)
|
|
return response
|
|
|
|
|
|
def filter_kpis(parsed_response, services, service_id):
|
|
filtered_response = []
|
|
for service in services:
|
|
if service['id'] == service_id:
|
|
kpi_ids = set(service['content']['recommended_kpis'] + service['content']['optional_kpis'])
|
|
for response in parsed_response:
|
|
for kpi in response['content']['kpis']:
|
|
if kpi['kpi_template_kpi_id'] in kpi_ids:
|
|
filtered_response.append(response)
|
|
break
|
|
return filtered_response
|