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.
244 lines
10 KiB
244 lines
10 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
import sys
|
|
import traceback
|
|
from collections import defaultdict
|
|
|
|
from splunk.persistconn.application import PersistentServerConnectionApplication
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
|
|
sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-ITOA', 'lib']))
|
|
|
|
import itsi_path
|
|
from itsi_py3 import _
|
|
import itsi_py3
|
|
|
|
from ITOA.setup_logging import getLogger
|
|
from ITOA.controller_utils import ITOAError, ItoaValidationError
|
|
from ITOA.rest_interface_provider_base import SplunkdRestInterfaceBase
|
|
from itsi.itoa_rest_interface_provider.itoa_rest_interface_provider import ItoaInterfaceProvider
|
|
|
|
from itsi.objects.itsi_service import ItsiService
|
|
from itsi.objects.itsi_kpi_threshold_template import ItsiKpiThresholdTemplate
|
|
|
|
|
|
logger = getLogger()
|
|
logger.debug('Initialized AT Usage REST splunkd handler interface log')
|
|
|
|
|
|
class ATUsageProviderSplunkd(ItoaInterfaceProvider):
|
|
'''
|
|
This wrapper class for the REST provider in internal ATUsageProvider which
|
|
handles all access check decorators and passes on to provider to serve
|
|
rest of the request
|
|
'''
|
|
def __init__(self, system_auth_token):
|
|
'''
|
|
Constructor for provider for the interface
|
|
|
|
@type: string
|
|
@param system_auth_token: the splunkd system level authorization token
|
|
|
|
'''
|
|
self.session_key = system_auth_token
|
|
self.owner = 'nobody'
|
|
|
|
def get_at_usage_data(self):
|
|
try:
|
|
at_services = self.fetch_at_enabled_services()
|
|
pred_templates = [template['_key'] for template in self.fetch_predefined_kpi_threshold_templates()]
|
|
|
|
usage_data = {
|
|
'kpis_using_at': 0,
|
|
'kpis_using_policies': defaultdict(int),
|
|
'kpis_using_threshold_templates': defaultdict(int),
|
|
'kpis_using_custom_threshold_templates': defaultdict(int),
|
|
'kpis_using_custom_thresholds': 0,
|
|
'kpis_using_recommendation': 0,
|
|
'kpis_with_recommendations_modified': 0,
|
|
'services_using_at': 0,
|
|
'services_using_custom_thresholds': 0,
|
|
'services_using_recommendation': 0,
|
|
'services_with_recommendations_modified': 0,
|
|
'min_threshold_level': defaultdict(int),
|
|
'max_threshold_level': defaultdict(int)
|
|
}
|
|
|
|
for service in at_services:
|
|
service_data = {
|
|
'kpis_using_at': 0,
|
|
'kpis_using_recommendation': 0,
|
|
'kpis_using_custom_thresholds': 0,
|
|
'kpis_with_recommendations_modified': 0
|
|
}
|
|
for kpi in service['kpis']:
|
|
|
|
# Collect data if and only if AT enabled
|
|
if not kpi.get('adaptive_thresholds_is_enabled'):
|
|
continue
|
|
|
|
service_data['kpis_using_at'] += 1
|
|
kpi_threshold_template_id = kpi.get('kpi_threshold_template_id')
|
|
|
|
if kpi.get('is_recommended_time_policies', False):
|
|
service_data['kpis_using_recommendation'] += 1
|
|
if kpi.get('was_recommendation_modified', False):
|
|
# Modified recommendations
|
|
service_data['kpis_with_recommendations_modified'] += 1
|
|
elif kpi_threshold_template_id:
|
|
if kpi_threshold_template_id in pred_templates:
|
|
usage_data['kpis_using_threshold_templates'][kpi_threshold_template_id] += 1
|
|
else:
|
|
usage_data['kpis_using_custom_threshold_templates'][kpi_threshold_template_id] += 1
|
|
else:
|
|
service_data['kpis_using_custom_thresholds'] += 1
|
|
temp_kpi_usage_data = defaultdict(bool)
|
|
time_variate_thresholds_spec = kpi.get('time_variate_thresholds_specification', None)
|
|
if not time_variate_thresholds_spec or not time_variate_thresholds_spec.get('policies', None):
|
|
# Skip when time_variate_thresholds_specifications are not set on the KPI
|
|
continue
|
|
for policy_title, policy in time_variate_thresholds_spec['policies'].items():
|
|
policy_type = policy['policy_type']
|
|
temp_kpi_usage_data[policy_type] = True
|
|
# Collect data for only AT policies
|
|
if policy_type == 'static':
|
|
continue
|
|
for t_level in policy['aggregate_thresholds']['thresholdLevels']:
|
|
# Minimum threshold levels for each policy
|
|
usage_data['min_threshold_level'][policy_type] = min(
|
|
usage_data['min_threshold_level'][policy_type],
|
|
t_level['dynamicParam'])
|
|
# Maximun threshold levels for each policy
|
|
usage_data['max_threshold_level'][policy_type] = max(
|
|
usage_data['max_threshold_level'][policy_type],
|
|
t_level['dynamicParam'])
|
|
|
|
for type, being_used in temp_kpi_usage_data.items():
|
|
if being_used:
|
|
usage_data['kpis_using_policies'][type] += 1
|
|
|
|
# Gather services data
|
|
if service_data['kpis_using_at'] > 0:
|
|
usage_data['services_using_at'] += 1
|
|
usage_data['kpis_using_at'] += service_data['kpis_using_at']
|
|
if service_data['kpis_using_custom_thresholds'] > 0:
|
|
usage_data['services_using_custom_thresholds'] += 1
|
|
usage_data['kpis_using_custom_thresholds'] += service_data['kpis_using_custom_thresholds']
|
|
if service_data['kpis_using_recommendation'] > 0:
|
|
usage_data['services_using_recommendation'] += 1
|
|
usage_data['kpis_using_recommendation'] += service_data['kpis_using_recommendation']
|
|
if service_data['kpis_with_recommendations_modified'] > 0:
|
|
usage_data['services_with_recommendations_modified'] += 1
|
|
usage_data['kpis_with_recommendations_modified'] += service_data['kpis_with_recommendations_modified']
|
|
|
|
return self.render_json({'data': usage_data})
|
|
except Exception:
|
|
tb = traceback.format_exc()
|
|
return self.render_json({
|
|
'status': 500,
|
|
'payload': _('Error: ') + str(tb)
|
|
})
|
|
|
|
def fetch_at_enabled_services(self):
|
|
"""
|
|
Fetches all ITSI services which are enabled and using Adaptive Thresholding
|
|
@return list of services
|
|
"""
|
|
|
|
service_object = ItsiService(self.session_key, self.owner)
|
|
return service_object.get_bulk(
|
|
'nobody',
|
|
fields=['title', '_key',
|
|
'kpis._key',
|
|
'kpis.time_variate_thresholds_specification',
|
|
'kpis.kpi_threshold_template_id',
|
|
'kpis.is_recommended_time_policies',
|
|
'kpis.was_recommendation_modified',
|
|
'kpis.adaptive_thresholds_is_enabled'],
|
|
filter_data={'$and': [
|
|
{'enabled': 1},
|
|
{'kpis.adaptive_thresholds_is_enabled': True},
|
|
]}
|
|
)
|
|
|
|
def fetch_predefined_kpi_threshold_templates(self):
|
|
"""
|
|
Fetches all predefind KPI threshold templates
|
|
@return list of predefined templates
|
|
"""
|
|
tampate_object = ItsiKpiThresholdTemplate(self.session_key, self.owner)
|
|
return tampate_object.get_bulk(
|
|
'nobody',
|
|
fields=['_key'],
|
|
filter_data={'_immutable': 1}
|
|
)
|
|
|
|
|
|
class ATUsageInterfaceSplunkd(PersistentServerConnectionApplication, SplunkdRestInterfaceBase):
|
|
|
|
'''
|
|
Class implementation for REST handler for Adaptive Thresholding Usage
|
|
'''
|
|
def __init__(self, command_line, command_arg):
|
|
'''
|
|
Basic constructor
|
|
|
|
@type: string
|
|
@param command_line: command line invoked for handler
|
|
|
|
@type: string
|
|
@param command_arg: args for invoked command line for handler
|
|
'''
|
|
super(ATUsageInterfaceSplunkd, self).__init__()
|
|
|
|
def migration_check(self, session_key):
|
|
'''
|
|
Override migration_check in SplunkdRestInterfaceBase
|
|
MigrationInterfaceSplunkd should be accessible during migration and serve request regardless of migration
|
|
running
|
|
Thus override migration_check of SplunkdRestInterfaceBase
|
|
'''
|
|
pass
|
|
|
|
def handle(self, args):
|
|
'''
|
|
Blanket handler for all REST calls on the interface routing the GET/POST/PUT/DELETE requests.
|
|
Derived implementation from PersistentServerConnectionApplication.
|
|
|
|
@type args: json
|
|
@param args: a JSON string representing a dictionary of arguments to the REST call.
|
|
|
|
@rtype: json
|
|
@return: a valid REST response
|
|
'''
|
|
return self._default_handle(args)
|
|
|
|
def _dispatch_to_provider(self, args):
|
|
'''
|
|
Parses the REST path on the interface to help route to respective handlers
|
|
This handler's thin layer parses the paths and routes actual handling for the call
|
|
to ATUsageInterfaceSplunkd
|
|
|
|
@type: dict
|
|
@param args: the args routed for the REST method
|
|
|
|
@rtype: dict
|
|
@return: results of the REST method
|
|
'''
|
|
if not isinstance(args, dict):
|
|
message = _('Invalid REST args received by Adaptive Thresholding Usage interface - {}').format(args)
|
|
raise ItoaValidationError(message=message, logger=logger)
|
|
|
|
system_auth_token = args['system_authtoken']
|
|
|
|
rest_path = args['rest_path']
|
|
if not isinstance(rest_path, itsi_py3.string_type):
|
|
message = _('Invalid REST path received by Adaptive Thresholding Usage interface - {}').format(rest_path)
|
|
raise ItoaValidationError(message=message, logger=logger)
|
|
|
|
path_parts = rest_path.strip().strip('/').split('/')
|
|
if (not isinstance(path_parts, list)) or (len(path_parts) < 1) or (path_parts[0] != 'at_usage_data'):
|
|
raise ITOAError(status=404, message=_('Specified REST url/path is invalid - {}.').format(rest_path))
|
|
|
|
interface_provider = ATUsageProviderSplunkd(system_auth_token)
|
|
return interface_provider.get_at_usage_data()
|