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.

107 lines
4.9 KiB

# Copyright (C) 2005-2025 Splunk Inc. All Rights Reserved.
import copy
import datetime
from ITOA.itoa_common import is_valid_dict, is_string_numeric, is_valid_str
from ITOA.setup_logging import logger
import custom_threshold_windows.constants as CustomThresholdWindowConstants
def update_custom_aggregate_thresholds(threshold_object, percentage_modifier):
"""
Modifies an aggregate_threshold policy by the percentage adjustment specified (total change to be passed in)
@param threshold_object: the aggregate_threshold policy
@param percentage_modifier: float that should already be calculated
"""
if 'thresholdLevels' in threshold_object:
for level in threshold_object.get('thresholdLevels'):
level['thresholdValue'] = round(level['thresholdValue'] * percentage_modifier, 3)
return True
return False
def calculate_custom_thresholds(kpi, active_window):
"""
If the given KPI needs to recalculate the custom thresholds for the active window, perform calculations and
update existing entries on the KPI.
@type kpi: dict
@param kpi: the kpi object itself
@type active_window: dict
@param active_window: the custom threshold window with threshold percentage differences
@rtype: dict
@return: the kpi dict (modified or not)
"""
if not kpi.get('active_custom_threshold_window', ''):
logger.warn(
'KPI {} could not recalculate custom thresholds because no custom threshold window was marked active.'
.format(kpi.get('_key', ''))
)
return kpi
kpi['recalculate_custom_thresholds'] = False
kpi_window = kpi.get('active_custom_threshold_window', '')
if not is_valid_str(kpi_window):
logger.error('Somehow custom threshold windows stored on KPI {} is malformed. Fixing KPI and skipping.'
.format(kpi.get('_key')))
kpi['active_custom_threshold_window'] = ''
return kpi
if not active_window:
logger.error('No custom threshold window passed in when trying to calculate new thresholds.')
return kpi
if active_window.get('window_type', '') == CustomThresholdWindowConstants.TYPE_PERCENTAGE:
percentage_config = active_window.get('window_config_percentage', 0)
if percentage_config == 0:
# If value is for some reason not set, do not modify
logger.warn(
'Custom threshold window {} does not have a percentage modification set.'
.format(active_window.get('_key')))
return kpi
elif abs(percentage_config) > 200:
logger.warn('Custom threshold window {} has a percentage set as greater than maximum'
' absolute value of 200. Calculating custom thresholds based on 200.'
.format(active_window.get('_key')))
percentage_config = 200 if percentage_config > 0 else -200
percentage_modifier = 1.00 + (percentage_config / 100)
if kpi.get('time_variate_thresholds', False):
kpi['time_variate_thresholds_specification_custom'] = copy.deepcopy(
kpi.get('time_variate_thresholds_specification'))
for policy in kpi['time_variate_thresholds_specification_custom']['policies']:
successful_update = update_custom_aggregate_thresholds(
kpi['time_variate_thresholds_specification_custom']['policies'][policy]['aggregate_thresholds'],
percentage_modifier)
if not successful_update:
logger.error('KPI {} had no thresholdLevels to update for an aggregate threshold '
'configuration. No modifications were applied to the base threshold levels.'
.format(kpi.get('_key')))
else:
kpi['aggregate_thresholds_custom'] = copy.deepcopy(kpi.get('aggregate_thresholds'))
successful_update = update_custom_aggregate_thresholds(kpi['aggregate_thresholds_custom'],
percentage_modifier)
if not successful_update:
logger.error('KPI {} had no thresholdLevels to update for its aggregate thresholds. No '
'modification were applied to the base threshold levels.'
.format(kpi.get('_key')))
else:
logger.error('Customer set static custom threshold window thresholds, which is not available in the'
'beta. Please fix and use the percentage adjustment for thresholds.')
return kpi
def get_human_readable_time_str(timestamp):
"""
Get a human-readable string representing the provided timestamp in the machine's timezone.
We use the machine timezone as we don't have any user-specific information.
:param timestamp: Unix-timestamp
:type timestamp: int
:return: str
"""
return datetime.datetime.utcfromtimestamp(timestamp).astimezone().strftime('%c (%Z)')