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.
222 lines
10 KiB
222 lines
10 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
|
|
import ITOA.itoa_common as utils
|
|
|
|
from ITOA.controller_utils import ITOAError
|
|
from ITOA.itoa_factory import instantiate_object
|
|
from ITOA.setup_logging import getLogger
|
|
from itsi.upgrade.timezones import get_new_time_blocks, _is_maintenance_end_time_indefinite, \
|
|
apply_timezone_offset, apply_timezone_offset_for_kpi_threshold_template
|
|
|
|
logger = getLogger(logger_name="ITOA.itoa_shift_time_offset")
|
|
logger.debug("Initialized itoa shift time offset handler log")
|
|
MAX_TIME_OFFSET_IN_MINUTES = 1440
|
|
|
|
|
|
def construct_filter_for_linked_thr_tmp(thr_tmp_object_ids):
|
|
"""
|
|
Create a filter for bulk filtering services containing specified kpi threshold template IDs
|
|
|
|
@type: list
|
|
@param thr_tmp_object_ids: list of object IDs of kpi threshold template to filter
|
|
|
|
@rtype dict | None
|
|
@return Filter object for services
|
|
"""
|
|
if len(thr_tmp_object_ids) >= 1 and len(thr_tmp_object_ids[0]) > 0:
|
|
get_bulk_filter = {'$or': []}
|
|
for thr_tmp_object_id in thr_tmp_object_ids:
|
|
get_bulk_filter['$or'].append({'kpis.kpi_threshold_template_id': thr_tmp_object_id})
|
|
return get_bulk_filter
|
|
else:
|
|
return None
|
|
|
|
|
|
def construct_filter_for_object_keys(object_keys_list):
|
|
"""
|
|
Create a filter for bulk filtering objects containing specified object keys
|
|
|
|
@type: list
|
|
@param object_keys_list: list of object IDs of specific object to filter
|
|
|
|
@rtype: dict
|
|
@return: Filter object for specific object type
|
|
"""
|
|
if len(object_keys_list) >= 1 and len(object_keys_list[0]) > 0:
|
|
get_bulk_filter = {'$or': []}
|
|
for object_key in object_keys_list:
|
|
get_bulk_filter['$or'].append({'_key': object_key})
|
|
return get_bulk_filter
|
|
else:
|
|
return {}
|
|
|
|
|
|
def apply_timezone_offset_for_service(service, offset_in_min, change_linked=False, threshold_template_ids=[]):
|
|
"""
|
|
Given the number of hours needed to offset time_blocks, apply the change to unlinked KPIs in a service
|
|
if change_linked is false, else update only KPIs linked with threshold template
|
|
|
|
@type service: object
|
|
@param service: Service object instance to update
|
|
|
|
@type offset_in_min: int
|
|
@param offset_in_min: the # of hours of offset to apply
|
|
|
|
@type change_linked: boolean
|
|
@param change_linked: if True change only KPIs with linked threshold template
|
|
|
|
@type threshold_template_ids: list
|
|
@param threshold_template_ids: List of threshold template IDs whose linked KPIs to update
|
|
|
|
@rtype: None
|
|
@return: None although service input reference will be updated
|
|
"""
|
|
if not (MAX_TIME_OFFSET_IN_MINUTES * -1 <= offset_in_min <= MAX_TIME_OFFSET_IN_MINUTES):
|
|
message = 'Timezone offset specified is invalid. Must be within a 24-hour (1440 minute) range. Specified value: %s ' % offset_in_min
|
|
logger.error(message)
|
|
raise ITOAError(status=400, message=message)
|
|
|
|
for kpi in service.get('kpis', []):
|
|
if not isinstance(kpi, dict):
|
|
continue
|
|
if kpi.get('_key').startswith('SHKPI'):
|
|
logger.debug('Skipping ServiceHealthScore kpi for service: %s', service.get('title'))
|
|
continue
|
|
if kpi.get('time_variate_thresholds') is False:
|
|
logger.debug('Skipping KPI %s as time policy is not enabled.', kpi.get('title'))
|
|
continue
|
|
linked_thr_tmp_id = kpi.get('kpi_threshold_template_id')
|
|
if change_linked:
|
|
if linked_thr_tmp_id == '' or linked_thr_tmp_id is None or linked_thr_tmp_id == 'custom' or linked_thr_tmp_id not in threshold_template_ids:
|
|
# Skipping the KPIs which are not linked with specific Threshold templates
|
|
logger.debug('Skipping the KPI "%s" which is not linked with any KPI threshold template', kpi.get('title'))
|
|
continue
|
|
else:
|
|
if linked_thr_tmp_id is not None and linked_thr_tmp_id != '' and linked_thr_tmp_id != 'custom':
|
|
# Skipping the KPIs which are linked with specific Threshold templates
|
|
logger.debug('Skipping the KPI "%s" which are already linked with KPI threshold template %s', kpi.get('title'), linked_thr_tmp_id)
|
|
continue
|
|
|
|
policy_spec = kpi.get('time_variate_thresholds_specification')
|
|
if not isinstance(policy_spec, dict):
|
|
continue
|
|
policies = policy_spec.get('policies')
|
|
if not isinstance(policies, dict):
|
|
continue
|
|
for policy_key, policy in policies.items():
|
|
if not isinstance(policy, dict):
|
|
continue
|
|
time_blocks = policy.get('time_blocks')
|
|
if not isinstance(time_blocks, list):
|
|
continue
|
|
kpi['time_variate_thresholds_specification']['policies'][policy_key]['time_blocks'] = get_new_time_blocks(time_blocks, offset_in_min)
|
|
|
|
|
|
class ShiftTimeOffsetUtils(object):
|
|
"""
|
|
Class for Utility of shift time offset functions
|
|
"""
|
|
|
|
def __init__(self, session_key, owner):
|
|
self._session_key = session_key
|
|
self.owner = owner
|
|
|
|
def get_objects_to_update(self, object_type, payload_data):
|
|
"""
|
|
Helper function to get bulk objects for object type based on
|
|
the filtering of payload data
|
|
|
|
@type: string
|
|
@param object_type: the ITOA object type
|
|
|
|
@type: dict
|
|
@param payload_data: data passed in the endpoint to apply time offset to
|
|
|
|
@rtype: dict
|
|
@return: Instance Object, Keys of Objects fetched, Objects fetched upon filtering
|
|
"""
|
|
if '_keys' not in payload_data.get(object_type):
|
|
message = '_keys is a mandatory attrbute.'
|
|
logger.error(message)
|
|
raise ITOAError(status=400, message=message)
|
|
|
|
object_keys = utils.get_object(payload_data.get(object_type).get('_keys'))
|
|
object_key_filter = construct_filter_for_object_keys(object_keys)
|
|
try:
|
|
object_type_instance = instantiate_object(self._session_key, self.owner, object_type)
|
|
except Exception as e:
|
|
logger.exception(e)
|
|
raise ITOAError(status=400, message=e)
|
|
return object_type_instance, object_keys, object_type_instance.get_bulk(self.owner, filter_data=object_key_filter)
|
|
|
|
def update_object_time_policy(self, data, offset):
|
|
"""
|
|
Function to handle all object types in data from shift_time_offset endpoint
|
|
and change the time blocks for specified object keys
|
|
|
|
@type data: dict
|
|
@param data: payload data from the endpoint argument
|
|
|
|
@type offset: int
|
|
@param offset: offset in seconds to shift time policy with
|
|
|
|
@rtype: dict
|
|
@return: Success message or ITOA Error(if any)
|
|
"""
|
|
if 'service' in data:
|
|
object_type_instance, service_object_keys, objects = self.get_objects_to_update('service', data)
|
|
if len(objects) < 1:
|
|
logger.debug('No service objects matched request')
|
|
else:
|
|
for json_data in objects:
|
|
apply_timezone_offset_for_service(json_data, int(offset / 60))
|
|
# Here, passing skip_kpi_consistancy_check True, and ignore_refresh_impacted_objects True as
|
|
# there is no need to perform additional validations on objects that are already created
|
|
object_type_instance.save_batch(self.owner, objects, validate_names=False, ignore_refresh_impacted_objects=True, skip_kpi_consistancy_check=True)
|
|
|
|
if 'kpi_threshold_template' in data:
|
|
object_type_instance, thr_tmp_object_keys, objects = self.get_objects_to_update('kpi_threshold_template', data)
|
|
if len(objects) < 1:
|
|
logger.debug('No kpi threshold template objects matched request')
|
|
else:
|
|
for json_data in objects:
|
|
apply_timezone_offset_for_kpi_threshold_template(json_data, int(offset / 60))
|
|
object_type_instance.save_batch(self.owner, objects, False)
|
|
|
|
# Now get all linked KPI services
|
|
|
|
if len(thr_tmp_object_keys) == 0:
|
|
thr_tmp_object_keys = [data.get('_key') for data in objects]
|
|
linked_thr_tmp_filter = construct_filter_for_linked_thr_tmp(thr_tmp_object_keys)
|
|
if linked_thr_tmp_filter is not None:
|
|
try:
|
|
object_type_instance = instantiate_object(self._session_key, self.owner, 'service')
|
|
except Exception as e:
|
|
logger.exception(e)
|
|
raise ITOAError(status=400, message=e)
|
|
|
|
service_objects = object_type_instance.get_bulk(self.owner, filter_data=linked_thr_tmp_filter)
|
|
if len(service_objects) > 0:
|
|
for json_data in service_objects:
|
|
apply_timezone_offset_for_service(json_data, int(offset / 60), True, thr_tmp_object_keys)
|
|
# Here, passing skip_kpi_consistancy_check True, and ignore_refresh_impacted_objects True as
|
|
# there is no need to perform additional validations on objects that are already created
|
|
object_type_instance.save_batch(self.owner, service_objects, validate_names=False, ignore_refresh_impacted_objects=True, skip_kpi_consistancy_check=True)
|
|
else:
|
|
logger.debug("No Service Kpis are linked to Specified KPI threshold template " + str([data.get('_key') for data in objects]))
|
|
|
|
if 'maintenance_calendar' in data:
|
|
object_type_instance, mtw_object_keys, mw_objects = self.get_objects_to_update('maintenance_calendar', data)
|
|
if len(mw_objects) < 1:
|
|
logger.debug('No maintenance calendar objects matched request')
|
|
else:
|
|
for json_data in mw_objects:
|
|
for json_field in ['start_time', 'end_time']:
|
|
# Only convert offset if end_time is not set to "Indefinite" for maintenance calendars
|
|
if json_field == 'end_time' and _is_maintenance_end_time_indefinite(json_data):
|
|
continue
|
|
apply_timezone_offset(json_data, json_field, offset)
|
|
object_type_instance.save_batch(self.owner, mw_objects, False)
|
|
|
|
return {"message": "success"}
|