# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved. from itsi.objects.model.itsi_model_validator import ItsiModelValidator from migration.migration_precheck import MigrationPreCheck class SIMigrationPreCheck(MigrationPreCheck): """ SI migration prechecks. The checks being performed are: * Check for service templates that are not synced * Check for service templates that have dangling linked service refs * Check that a service template's linked services have a reciprocating base_service_template_id backref * Check that a service's base_service_template_id has have a reciprocating linked_services backref * Check for service objects that have dangling depends_on and/or depends_on_me service refs * Check for KPIs that have dangling shared_base search refs * Check for KPIs that have dangling kpi_threshold_template_id refs """ def __init__(self, session_key, logger, pre_checks, skip_pre_checks=[]): MigrationPreCheck.__init__(self, session_key, logger, pre_checks, skip_pre_checks) self.itsi_model_validator = ItsiModelValidator(session_key, logger) @staticmethod def collect_service_depends_info_strings(service_obj, services_depends_attr): """ Get a service's service dependency ids for the given service dependency attribute. :param service_obj: the service object to get service dependency ids for :param services_depends_attr: the service dependency attribute :return: list of service dependency ids """ service_depends_info_strings = ["%s (%s)" % (services_depend_obj['identifying_name'], services_depend_obj['_key']) for services_depend_obj in service_obj.get(services_depends_attr, [])] return service_depends_info_strings def precheck_SVC_SVCTMPL(self): """ * Check for service templates that are not synced * Check for service templates that have dangling linked service refs * Check that a service template's linked services have a reciprocating base_service_template_id backref * Check that a service's base_service_template_id has have a reciprocating linked_services backref * Check for service objects that have dangling depends_on and/or depends_on_me service refs :return: None """ # Loop through all service templates self.itsi_model_validator.check_service_templates() # Loop through all services in chunks self.itsi_model_validator.check_services() ret_list = [] # service templates with bad sync_status self.logger.info('Service templates with bad sync_status count: %s', len(self.itsi_model_validator.service_template_bad_sync_status)) for service_template_obj in self.itsi_model_validator.service_template_bad_sync_status: ret_list.append(self._make_result(False, "Service template: %s (%s) is not synced: %s" % ( service_template_obj.get('identifying_name', ''), service_template_obj['_key'], service_template_obj['sync_status']), "Error")) # service templates with dangling linked service ids self.logger.info('Service templates with dangling linked_services count: %s', len(self.itsi_model_validator.service_template_id_to_missing_linked_services_map)) for service_template_id in self.itsi_model_validator.service_template_id_to_missing_linked_services_map: service_template_obj = self.itsi_model_validator.service_template_id_to_missing_linked_services_map[ service_template_id] service_ids_missing = service_template_obj['linked_services'] ret_list.append(self._make_result(False, "Service template: %s (%s) has %s dangling linked_services" % ( service_template_obj.get('identifying_name', ''), service_template_obj['_key'], len(service_ids_missing) ), "Warn")) self.logger.warn("Service template: %s (%s) has %s dangling linked_services: %s" % ( service_template_obj.get('identifying_name', ''), service_template_obj['_key'], len(service_ids_missing), ','.join(service_ids_missing))) # check that a service template's linked services have a reciprocating base_service_template_id backref self.logger.info('Services with missing expected base_service_template_id count: %s', len(self.itsi_model_validator.service_objects_missing_expected_service_template_id)) for service_object_missing_expected_service_template_id in \ self.itsi_model_validator.service_objects_missing_expected_service_template_id: base_service_template_id = service_object_missing_expected_service_template_id.get( 'base_service_template_id', '') base_service_template_expected = service_object_missing_expected_service_template_id[ 'base_service_template_expected'] base_service_template_expected_id = base_service_template_expected['_key'] base_service_template_expected_name = base_service_template_expected['identifying_name'] ret_list.append( self._make_result( False, "Service: %s (%s) is missing expected base service template: %s (%s)\ where actual base_service_template_id is: %s" % ( service_object_missing_expected_service_template_id.get( 'identifying_name', ''), service_object_missing_expected_service_template_id['_key'], base_service_template_expected_name, base_service_template_expected_id, base_service_template_id), "Warn")) # check that a service's base_service_template_id has have a reciprocating linked_services backref self.logger.info('Service templates with missing expected linked_services count: %s', len(self.itsi_model_validator.service_template_objects_missing_expected_linked_services_map)) for service_template_id in \ self.itsi_model_validator.service_template_objects_missing_expected_linked_services_map: service_template_object_missing_expected_linked_services =\ self.itsi_model_validator.\ service_template_objects_missing_expected_linked_services_map[service_template_id] missing_linked_services = service_template_object_missing_expected_linked_services['linked_services'] ret_list.append( self._make_result( False, "Service template: %s (%s) is missing %s expected linked_services" % ( service_template_object_missing_expected_linked_services.get( 'identifying_name', ''), service_template_object_missing_expected_linked_services['_key'], len(missing_linked_services)), "Warn")) self.logger.warn("Service template: %s (%s) is missing %s expected linked_services: %s" % ( service_template_object_missing_expected_linked_services.get( 'identifying_name', ''), service_template_object_missing_expected_linked_services['_key'], len(missing_linked_services), ','.join( ["%s (%s)" % (missing_linked_service['identifying_name'], missing_linked_service['_key']) for missing_linked_service in missing_linked_services]))) # services with dangling service template id self.logger.info('Services with dangling base_service_template_id count: %s', len(self.itsi_model_validator.service_objs_missing_service_template_id)) for service_obj_missing_service_template_id in \ self.itsi_model_validator.service_objs_missing_service_template_id: ret_list.append(self._make_result(False, "Service: %s (%s) has a dangling base_service_template_id: %s" % ( service_obj_missing_service_template_id.get('identifying_name', ''), service_obj_missing_service_template_id['_key'], service_obj_missing_service_template_id['base_service_template_id']), "Warn")) # Check service bidirectional dependencies for each service, lookup services using services_depends_on. # For each of those services, ensure that services_depending_on_me contains the original service. # Remove verified ids from services_depends_on and services_depending_on_me. # Loop through services and for those that have non-empty services_depends_on or services_depending_on_me, # mark as error because they are dangling references self.logger.info('Services with dangling services_depends_on and/or services_depending_on_me count: %s', len(self.itsi_model_validator.service_objects_with_dangling_service_refs)) for service_obj in self.itsi_model_validator.service_objects_with_dangling_service_refs: services_depends_on_info_strings = set() self.itsi_model_validator.collect_service_depends_service_ids(service_obj, 'services_depends_on', services_depends_on_info_strings) services_depending_on_me_info_strings = set() self.itsi_model_validator.collect_service_depends_service_ids(service_obj, 'services_depending_on_me', services_depending_on_me_info_strings) if services_depends_on_info_strings: ret_list.append(self._make_result(False, "Service: %s (%s) has %s dangling services_depends_on" % ( service_obj.get('identifying_name', ''), service_obj['_key'], len(services_depends_on_info_strings)), "Warn")) self.logger.warn("Service: %s (%s) has %s dangling services_depends_on: %s" % ( service_obj.get('identifying_name', ''), service_obj['_key'], len(services_depends_on_info_strings), ','.join(services_depends_on_info_strings))) if services_depending_on_me_info_strings: ret_list.append(self._make_result(False, "Service: %s (%s) has %s dangling services_depending_on_me" % ( service_obj.get('identifying_name', ''), service_obj['_key'], len(services_depending_on_me_info_strings)), "Warn")) self.logger.warn("Service: %s (%s) has %s dangling services_depending_on_me: %s" % ( service_obj.get('identifying_name', ''), service_obj['_key'], len(services_depending_on_me_info_strings), ','.join(services_depending_on_me_info_strings))) self.logger.info('Services with missing expected services_depends_on and/or services_depending_on_me count: %s', len(self.itsi_model_validator.service_objects_with_missing_depends_service_refs_map)) for reverse_service_id in self.itsi_model_validator.service_objects_with_missing_depends_service_refs_map: reverse_service_obj = self.itsi_model_validator.\ service_objects_with_missing_depends_service_refs_map[reverse_service_id] services_depends_on_info_strings = self.collect_service_depends_info_strings( reverse_service_obj, 'services_depends_on') services_depending_on_me_info_strings = self.collect_service_depends_info_strings( reverse_service_obj, 'services_depending_on_me') if services_depends_on_info_strings: ret_list.append(self._make_result(False, "Service: %s (%s) has %s missing services_depends_on" % ( reverse_service_obj.get('identifying_name', ''), reverse_service_obj['_key'], len(services_depends_on_info_strings)), "Warn")) self.logger.warn("Service: %s (%s) has %s missing services_depends_on: %s" % ( reverse_service_obj.get('identifying_name', ''), reverse_service_obj['_key'], len(services_depends_on_info_strings), ','.join(services_depends_on_info_strings))) if services_depending_on_me_info_strings: ret_list.append(self._make_result(False, "Service: %s (%s) had missing services_depending_on_me refs: %s" % ( reverse_service_obj.get('identifying_name', ''), reverse_service_obj['_key'], ','.join(services_depending_on_me_info_strings)), "Warn")) self.logger.warn("Service: %s (%s) has %s missing services_depending_on_me: %s" % ( reverse_service_obj.get('identifying_name', ''), reverse_service_obj['_key'], len(services_depending_on_me_info_strings), ','.join(services_depending_on_me_info_strings))) if ret_list: return ret_list return [self._make_result(True, "Services and service template ref checks passed", "Success")] def _make_obj_with_corrupt_kpis_results(self, obj_with_corrupt_kpis): """ Make precheck failed results for object whose KPIs that have a mismatch of base_search_id and search_type. :param obj_with_corrupt_kpis: object with list of KPIs with corrupt base search :return: list of precheck results """ return [self._make_result( False, "%s: %s (%s) has KPI: %s that has mismatch of base_search_id: %s and search_type: %s" % ( obj_with_corrupt_kpis['object_type'], obj_with_corrupt_kpis.get('identifying_name', ''), obj_with_corrupt_kpis['_key'], kpi_object_corrupt_search.get('title', ''), kpi_object_corrupt_search.get('base_search_id', ''), kpi_object_corrupt_search.get('search_type', '') ), "Warn") for kpi_object_corrupt_search in obj_with_corrupt_kpis['kpis']] def _make_obj_with_dangling_shared_base_search_kpis_results(self, obj_with_dangling_shared_base_search_kpis): """ Make precheck failed results for object with KPIs whose base search id is dangling. :param obj_with_dangling_shared_base_search_kpis: object with list of KPIs with dangling base search ids :return: list of precheck results """ return [self._make_result(False, "%s: %s (%s) has KPI: %s that has a dangling base_search_id: %s" % ( obj_with_dangling_shared_base_search_kpis['object_type'], obj_with_dangling_shared_base_search_kpis.get('identifying_name', ''), obj_with_dangling_shared_base_search_kpis['_key'], dangling_shared_base_search_kpi.get('title', ''), dangling_shared_base_search_kpi.get('base_search_id', '') ), "Warn") for dangling_shared_base_search_kpi in obj_with_dangling_shared_base_search_kpis['kpis']] def _make_obj_with_dangling_kpi_threshold_templates_results(self, obj_with_dangling_kpi_threshold_templates): """ Make precheck failed results for object with KPIs whose KPI threshold template is dangling. :param obj_with_dangling_kpi_threshold_templates: object with list of KPIs with dangling KPI threshold templates :return: list of precheck results """ return [self._make_result(False, "%s: %s (%s) has KPI: %s that has a dangling kpi_threshold_template_id: %s" % ( obj_with_dangling_kpi_threshold_templates['object_type'], obj_with_dangling_kpi_threshold_templates.get('identifying_name', ''), obj_with_dangling_kpi_threshold_templates['_key'], dangling_kpi_threshold_template_kpi.get('title', ''), dangling_kpi_threshold_template_kpi.get('kpi_threshold_template_id', '') ), "Warn") for dangling_kpi_threshold_template_kpi in obj_with_dangling_kpi_threshold_templates['kpis']] def precheck_KPI(self): """ * Check for KPIs that have dangling shared_base search refs * Check for KPIs that have dangling kpi_threshold_template_id refs * Check for KPIs with empty threshold field :return: results of check """ self.itsi_model_validator.check_kpis_in_collections() self.itsi_model_validator.get_kpi_with_empty_threshold_field() ret_list = [] # KPI search type mismatch with base search id self.logger.info('Objects with corrupt KPI searches count: %s', len(self.itsi_model_validator.objs_with_corrupt_kpis)) ret_list.extend([result for obj_with_corrupt_kpis in self.itsi_model_validator.objs_with_corrupt_kpis for result in self._make_obj_with_corrupt_kpis_results(obj_with_corrupt_kpis)]) # KPIs missing base search object self.logger.info('Objects with KPIs that have dangling base_search_id count: %s', len(self.itsi_model_validator.objs_with_dangling_shared_base_search_kpis)) ret_list.extend( [result for obj_with_dangling_shared_base_search_kpis in self.itsi_model_validator.objs_with_dangling_shared_base_search_kpis for result in self._make_obj_with_dangling_shared_base_search_kpis_results(obj_with_dangling_shared_base_search_kpis)]) # KPIs missing kpi threshold template object self.logger.info('Objects with KPIs that have dangling kpi_threshold_template_id count: %s', len(self.itsi_model_validator.objs_with_dangling_kpi_threshold_templates)) ret_list.extend( [result for obj_with_dangling_kpi_threshold_templates in self.itsi_model_validator.objs_with_dangling_kpi_threshold_templates for result in self._make_obj_with_dangling_kpi_threshold_templates_results(obj_with_dangling_kpi_threshold_templates)]) # KPIs with empty threshold field self.logger.info('Objects with KPIs that have empty threshold field count: %s', len(self.itsi_model_validator.kpi_with_empty_threshold_field)) make_result_list = [self._make_result( False, "KPI: %s (%s) of %s type in service %s (%s) has empty threshold field" % ( kpi_with_empty_threshold.get('title', ''), kpi_with_empty_threshold.get('_key', ''), kpi_with_empty_threshold.get('search_type', ''), kpi_with_empty_threshold.get('service_title', ''), kpi_with_empty_threshold.get('service_id', '') ), "Warn") for kpi_with_empty_threshold in self.itsi_model_validator.kpi_with_empty_threshold_field] ret_list.extend(make_result_list) if ret_list: return ret_list return [self._make_result(True, "KPI checks passed", "Success")]