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.
823 lines
34 KiB
823 lines
34 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
|
|
import sys
|
|
import os
|
|
from collections import defaultdict
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
|
|
sys.path.append(make_splunkhome_path(["etc", "apps", "SA-ITOA", "lib"]))
|
|
|
|
from migration_utility.migration_utility_manifest import (
|
|
precheck_failure_severity,
|
|
precheck_documentation_details,
|
|
dict_for_mapping_precheck_ids,
|
|
dict_for_mapping_category,
|
|
dict_for_mapping_precheck_class,
|
|
dict_for_mapping_precheck_class_and_id,
|
|
)
|
|
from itsi.objects.itsi_kpi_base_search import ItsiKPIBaseSearch
|
|
from ITOA.itoa_common import (
|
|
get_conf_stanza_single_entry,
|
|
get_object_batch_size,
|
|
SplunkUser,
|
|
)
|
|
from ITOA.setup_logging import getLogger
|
|
from migration.migration import MigrationBaseMethod
|
|
from migration_utility.constants import (
|
|
MODES,
|
|
MODES_STR_MAP,
|
|
UPGRADE_READINESS_JOB_TIMEOUT_LIMIT,
|
|
)
|
|
from itsi.objects.model.itsi_model_validator import ItsiModelValidator
|
|
from itsi.objects.itsi_service import ItsiService
|
|
from itsi.objects.itsi_entity import ItsiEntity
|
|
from itsi.objects.itsi_service_template import ItsiBaseServiceTemplate
|
|
from itsi.objects.itsi_upgrade_readiness_prechecks import ItsiUpgradeReadinessPrechecks
|
|
from itsi.objects.itsi_refresh_queue_job import ItsiRefreshQueueJob
|
|
from itsi.service_template.service_template_utils import ServiceTemplateUtils
|
|
from itsi.objects.itsi_backup_restore import ItsiBackupRestore
|
|
from ITOA.itoa_common import get_current_utc_epoch
|
|
|
|
logger = getLogger(
|
|
logger_file="itsi_migration_utility.log",
|
|
logger_name="itsi_migration_utility_prechecker",
|
|
)
|
|
|
|
|
|
class ItsiMigrationUtilityHandler:
|
|
def __init__(self, sessionkey, transaction_id) -> None:
|
|
self.sessionkey = sessionkey
|
|
self.transaction_id = transaction_id
|
|
self.dict_for_mapping_precheck_ids = dict_for_mapping_precheck_ids
|
|
self.dict_for_mapping_precheck_class = dict_for_mapping_precheck_class
|
|
self.dict_for_mapping_precheck_class_and_id = (
|
|
dict_for_mapping_precheck_class_and_id
|
|
)
|
|
self.precheck_failure_severity = precheck_failure_severity
|
|
self.dict_for_mapping_category = dict_for_mapping_category
|
|
self.precheck_documentation_details = precheck_documentation_details
|
|
self.upgrade_readiness_precheck_obj = ItsiUpgradeReadinessPrechecks(
|
|
self.sessionkey, "nobody"
|
|
)
|
|
self.failed_precheck_count = 0
|
|
self.large_number_of_kpis_per_base_search_threshold = 600
|
|
self.dangling_service_reference_in_entities_disabled = int(
|
|
get_conf_stanza_single_entry(
|
|
self.sessionkey,
|
|
"itsi_settings",
|
|
"upgrade_readiness",
|
|
"dangling_service_reference_in_entities_disabled",
|
|
).get("content", 0)
|
|
)
|
|
self.itsi_default_authorize_conf_file = make_splunkhome_path(
|
|
["etc", "apps", "itsi", "default", "authorize.conf"]
|
|
)
|
|
self.system_default_authorize_conf_file = make_splunkhome_path(
|
|
["etc", "system", "default", "authorize.conf"]
|
|
)
|
|
self.system_local_authorize_conf_file = make_splunkhome_path(
|
|
["etc", "system", "local", "authorize.conf"]
|
|
)
|
|
self.metric_ad_authorize_conf_file = make_splunkhome_path(
|
|
["etc", "apps", "SA-ITSI-MetricAD", "default", "authorize.conf"]
|
|
)
|
|
self.current_user_context = SplunkUser.get_current_user_context(
|
|
self.sessionkey, logger
|
|
)
|
|
self.parent_role_to_get_user_capabilites = "itoa_admin"
|
|
self.role_to_get_user_capabilites = "itoa_team_admin"
|
|
self.native_user_capabilities = self.get_native_capabilities(self.itsi_default_authorize_conf_file, self.system_default_authorize_conf_file, self.system_local_authorize_conf_file, self.metric_ad_authorize_conf_file, self.role_to_get_user_capabilites)
|
|
self.disabled_capabilities = self.get_disabled_capabilities(self.parent_role_to_get_user_capabilites)
|
|
self.modified_native_capabilities = self.get_modified_capabilities(self.native_user_capabilities, self.disabled_capabilities)
|
|
self.modified_capability_for_current_user = self.get_modified_capability_for_current_user(self.current_user_context, self.modified_native_capabilities, self.native_user_capabilities)
|
|
|
|
def get_precheck_details(self, itsi_muh, precheck_id):
|
|
precheck = [
|
|
name
|
|
for name, pre_id in self.dict_for_mapping_precheck_ids.items()
|
|
if pre_id == precheck_id
|
|
]
|
|
precheck_class = self.dict_for_mapping_precheck_class[precheck[0]]
|
|
description_details, resolution_details = precheck_class(
|
|
itsi_muh
|
|
).get_precheck_details()
|
|
documentation_details = self.precheck_documentation_details[precheck_id]
|
|
|
|
return description_details, resolution_details, documentation_details
|
|
|
|
def get_auto_remediation_details(self, itsi_muh, precheck_id):
|
|
precheck = [
|
|
name
|
|
for name, pre_id in self.dict_for_mapping_precheck_ids.items()
|
|
if pre_id == precheck_id
|
|
]
|
|
precheck_class = self.dict_for_mapping_precheck_class[precheck[0]]
|
|
description_details, category_fixed = precheck_class(
|
|
itsi_muh
|
|
).get_auto_remediation_details()
|
|
return description_details, category_fixed
|
|
|
|
def set_prechecks(self, precheck_ids):
|
|
# list of prechecks on which operations to be performed
|
|
self.prechecks = []
|
|
if precheck_ids is None:
|
|
self.prechecks = list(
|
|
self.dict_for_mapping_precheck_class.values())
|
|
else:
|
|
for key, value in self.dict_for_mapping_precheck_ids.items():
|
|
if value in precheck_ids:
|
|
self.prechecks.append(
|
|
self.dict_for_mapping_precheck_class[key])
|
|
|
|
def execute(self, itsi_muh, operation_mode, precheck_ids=None):
|
|
"""
|
|
Takes prechecks classes' list and operation mode to be executed and
|
|
after the execution proper output in console and itsi_migration_utility.log will be logged
|
|
: param itsi_muh: object of ItsiMigrationUtilityHandler() class
|
|
: param operation_mode: mode of operation -- MODE 1: Precheck or MODE 2: Auto-remediation
|
|
: precheck_ids: list of ids of prechecks to be executed.
|
|
If None, all prechecks will be executed
|
|
"""
|
|
|
|
result = True
|
|
message = ""
|
|
self.set_prechecks(precheck_ids)
|
|
|
|
# Check for in progress jobs before starting job.
|
|
other_in_progress_jobs = self.upgrade_readiness_precheck_obj.get_in_progress_upgrade_readiness_prechecks(
|
|
lookback_time=UPGRADE_READINESS_JOB_TIMEOUT_LIMIT,
|
|
exclude_transaction_id=self.transaction_id,
|
|
)
|
|
other_in_progress_job_ids = [
|
|
other_in_progress_job["transaction_id"]
|
|
for other_in_progress_job in other_in_progress_jobs
|
|
]
|
|
other_in_progress_job_start_times = [
|
|
other_in_progress_job["start_time"]
|
|
for other_in_progress_job in other_in_progress_jobs
|
|
]
|
|
|
|
if len(other_in_progress_jobs) > 0:
|
|
logger.error(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[operation_mode={MODES_STR_MAP[operation_mode]}] "
|
|
f"Execution failed because other in progress job(s) were found, transaction_id(s): {other_in_progress_job_ids}."
|
|
)
|
|
return False, f"OTHER_JOB_{other_in_progress_job_start_times}", self.failed_precheck_count
|
|
|
|
if operation_mode == MODES["PRECHECK"]:
|
|
message = "PRECHECK"
|
|
self.perform_precheck(itsi_muh)
|
|
elif operation_mode == MODES["AUTO_REMEDIATION"]:
|
|
result, message = self.perform_remediation(itsi_muh)
|
|
|
|
return result, message, self.failed_precheck_count
|
|
|
|
def perform_precheck(self, itsi_muh):
|
|
# list to store the detailed output for logging in
|
|
# itsi_migration_utility.log file
|
|
list_to_store_detailed_output = []
|
|
self.setup_data()
|
|
time_now = get_current_utc_epoch()
|
|
self.upgrade_readiness_precheck_obj.update_upgrade_readiness_precheck_job(
|
|
self.transaction_id, precheck_started=True, precheck_start_time=time_now
|
|
)
|
|
for precheck in self.prechecks:
|
|
precheck_obj = precheck(itsi_muh)
|
|
detailed_output = precheck_obj.precheck_result()
|
|
if detailed_output:
|
|
# Logging the precheck failure details
|
|
list_to_store_detailed_output.append(detailed_output)
|
|
|
|
# to print in log file
|
|
for detailed_output in list_to_store_detailed_output:
|
|
if "[precheck_passed" in detailed_output:
|
|
logger.info(detailed_output)
|
|
else:
|
|
self.failed_precheck_count += 1
|
|
logger.error(detailed_output)
|
|
|
|
logger.info(
|
|
f"[transaction_id={self.transaction_id}] Precheck Execution Completed"
|
|
)
|
|
|
|
def perform_remediation(self, itsi_muh):
|
|
# list to store the detailed output for logging in
|
|
# itsi_migration_utility.log file
|
|
list_to_store_detailed_output = []
|
|
operation_mode = MODES["AUTO_REMEDIATION"]
|
|
self.setup_data()
|
|
time_now = get_current_utc_epoch()
|
|
self.upgrade_readiness_precheck_obj.update_upgrade_readiness_precheck_job(
|
|
self.transaction_id,
|
|
remediation_started=True,
|
|
remediation_start_time=time_now,
|
|
)
|
|
# Fail job and return if refresh queue is not empty after a period of time
|
|
refresh_queue_job_interface = ItsiRefreshQueueJob(self.sessionkey, "nobody")
|
|
if not refresh_queue_job_interface.wait_for_unblocked_queue():
|
|
logger.error(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[operation_mode={MODES_STR_MAP[operation_mode]}] "
|
|
"Execution timed out waiting for refresh queue to empty. Not proceeding with remediation."
|
|
)
|
|
return False, "REFRESH_QUEUE"
|
|
|
|
# Fail job and return if backup/restore jobs are in progress
|
|
backup_restore_interface = ItsiBackupRestore(self.sessionkey, "nobody")
|
|
if backup_restore_interface.is_any_backup_restore_job_in_progress("nobody"):
|
|
logger.error(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[operation_mode={MODES_STR_MAP[operation_mode]}] "
|
|
"Backup/restore jobs are in progress. Auto-remediation is not allowed while backup/restore jobs are in progress."
|
|
)
|
|
return False, "BACKUP/RESTORE"
|
|
|
|
# Fail job and return if service template sync is in progress
|
|
status = ServiceTemplateUtils(
|
|
self.sessionkey, "nobody"
|
|
).service_template_sync_job_in_progress_or_sync_now()
|
|
if status.get("status", False):
|
|
logger.error(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[operation_mode={MODES_STR_MAP[operation_mode]}] "
|
|
"One or more service templates are currently syncing. Auto-remediation is not allowed while service template sync is in progress."
|
|
)
|
|
return False, "TEMPLATE SYNC"
|
|
|
|
for precheck in self.prechecks:
|
|
precheck_obj = precheck(itsi_muh)
|
|
precheck_id = self.dict_for_mapping_precheck_class_and_id[precheck]
|
|
detailed_output_of_precheck = precheck_obj.precheck_result()
|
|
if "[precheck_passed" not in detailed_output_of_precheck:
|
|
detailed_output_of_remediation = precheck_obj.auto_remediation()
|
|
if detailed_output_of_remediation:
|
|
# Logging the details of updated objects
|
|
list_to_store_detailed_output.append(
|
|
detailed_output_of_remediation)
|
|
list_to_store_detailed_output.append(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[precheck_id={precheck_id}] "
|
|
"The error identified by the upgrade readiness check was fixed."
|
|
)
|
|
else:
|
|
list_to_store_detailed_output.append(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[precheck_id={precheck_id}] "
|
|
"No automatic fix ran because no error exists."
|
|
)
|
|
|
|
# converting dict into list
|
|
list_of_updated_objs = list(itsi_muh.dict_of_original_objs.values())
|
|
|
|
# update into kvstore
|
|
self.update_kvstore_objects(list_of_updated_objs)
|
|
|
|
# to print in log file
|
|
for detailed_output in list_to_store_detailed_output:
|
|
logger.info(detailed_output)
|
|
logger.info(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[operation_mode={MODES_STR_MAP[operation_mode]}] "
|
|
"The errors identified by the upgrade readiness check were fixed."
|
|
)
|
|
|
|
# perform another precheck after remediation is done
|
|
logger.info(
|
|
f"[transaction_id={self.transaction_id}] "
|
|
f"[operation_mode={MODES_STR_MAP[operation_mode]}] "
|
|
"Running another precheck after completing remediation."
|
|
)
|
|
self.set_prechecks(None)
|
|
self.perform_precheck(itsi_muh)
|
|
|
|
return True, ""
|
|
|
|
def setup_data(self):
|
|
self.imv = ItsiModelValidator(self.sessionkey, logger)
|
|
self.imv.check_service_templates()
|
|
self.imv.check_services()
|
|
self.imv.get_services_and_service_templates()
|
|
self.imv.check_kpis_in_collections()
|
|
self.imv.get_services_having_no_entity_filter_rule()
|
|
self.imv.get_kpi_base_search_having_service_entity_filter_rule()
|
|
self.imv.get_base_search_id_having_service_entity_filter_rule()
|
|
self.imv.get_kpi_base_searches()
|
|
self.imv.get_service_templates_with_correct_services()
|
|
self.imv.get_entities_services_map() if not self.dangling_service_reference_in_entities_disabled else None
|
|
self.mi_method = MigrationBaseMethod(self.sessionkey, logger=logger)
|
|
self.owner = "nobody"
|
|
|
|
# Set the threshold from the limits.conf file
|
|
self.large_number_of_kpis_per_base_search_threshold = int(get_conf_stanza_single_entry(
|
|
self.sessionkey, 'itsi_settings', 'upgrade_readiness', 'large_number_of_kpis_per_base_search_threshold').get('content', 600))
|
|
|
|
# List of KPIs having empty threshold field
|
|
self.kpi_with_empty_threshold_field = []
|
|
# List of KPI Base Searches having no metrics configured
|
|
self.kpi_base_search_empty_metrics = []
|
|
|
|
self.get_kpi_with_empty_threshold_field()
|
|
|
|
# List of entities link to each services
|
|
self.list_of_entities_link_to_service = dict(
|
|
self.imv.list_of_entities_link_to_service
|
|
)
|
|
|
|
self.service_objects_with_dangling_service_refs = (
|
|
self.imv.service_objects_with_dangling_service_refs
|
|
)
|
|
self.kpi_base_search_having_service_entity_filter_rule = list(
|
|
self.imv.kpi_base_search_id_having_entity_filter_rule_enabled
|
|
)
|
|
|
|
# seperating the service_objects_with_dangling_service_refs into 2 different
|
|
# objs with dangling services depending on me references and
|
|
# objs with dangling services depends on references
|
|
self.service_objects_with_dangling_services_depending_on_me_refs = defaultdict(
|
|
list
|
|
)
|
|
self.service_objects_with_dangling_services_depends_on_refs = defaultdict(
|
|
list)
|
|
|
|
for service_obj in self.service_objects_with_dangling_service_refs:
|
|
service_id = service_obj["_key"]
|
|
for services_depends_on_obj in service_obj["services_depends_on"]:
|
|
if "serviceid" in services_depends_on_obj:
|
|
self.service_objects_with_dangling_services_depends_on_refs[
|
|
service_id
|
|
].append(services_depends_on_obj["serviceid"])
|
|
for services_depending_on_me_obj in service_obj["services_depending_on_me"]:
|
|
if "serviceid" in services_depending_on_me_obj:
|
|
self.service_objects_with_dangling_services_depending_on_me_refs[
|
|
service_id
|
|
].append(services_depending_on_me_obj["serviceid"])
|
|
self.service_template_id_to_missing_linked_services_map = (
|
|
self.imv.service_template_id_to_missing_linked_services_map
|
|
)
|
|
|
|
self.enabled_services_with_no_entity_filter_rule = (
|
|
self.imv.enabled_services_with_no_entity_filter_rule
|
|
)
|
|
|
|
self.service_template_objects_missing_expected_linked_services_map = (
|
|
self.imv.service_template_objects_missing_expected_linked_services_map
|
|
)
|
|
|
|
self.service_templates_with_linked_services = (
|
|
self.imv.service_templates_with_linked_services
|
|
)
|
|
|
|
self.services_without_base_service_template_id = (
|
|
self.imv.services_without_base_service_template_id
|
|
)
|
|
|
|
self.service_objects_with_missing_depends_service_refs_map = (
|
|
self.imv.service_objects_with_missing_depends_service_refs_map
|
|
)
|
|
self.objs_with_corrupt_kpis = self.imv.objs_with_corrupt_kpis
|
|
self.objs_with_dangling_shared_base_search_kpis = (
|
|
self.imv.objs_with_dangling_shared_base_search_kpis
|
|
)
|
|
self.objs_with_dangling_kpi_threshold_templates = (
|
|
self.imv.objs_with_dangling_kpi_threshold_templates
|
|
)
|
|
self.service_template_bad_sync_status = (
|
|
self.imv.service_template_bad_sync_status
|
|
)
|
|
self.entity_interface = ItsiEntity(self.sessionkey, "nobody")
|
|
self.service_interface = ItsiService(self.sessionkey, "nobody")
|
|
self.service_template_interface = ItsiBaseServiceTemplate(
|
|
self.sessionkey, "nobody"
|
|
)
|
|
self.kpi_base_search_interface = ItsiKPIBaseSearch(
|
|
self.sessionkey, "nobody"
|
|
)
|
|
self.kpi_base_search_with_count_of_kpis = self.imv.kpi_base_search_with_count_of_kpis
|
|
self.service_template_with_correct_linked_services = self.imv.service_template_with_correct_linked_services
|
|
|
|
# fetch all the objects from kvstore
|
|
self.original_entities = self.entity_interface.get_bulk(
|
|
"nobody", transaction_id=None
|
|
)
|
|
self.original_services = self.service_interface.get_bulk(
|
|
"nobody", transaction_id=None
|
|
)
|
|
self.original_base_service_templates = self.service_template_interface.get_bulk(
|
|
"nobody", transaction_id=None
|
|
)
|
|
self.original_kpi_templates = self.service_interface.get_bulk(
|
|
"nobody", transaction_id=None
|
|
)
|
|
self.original_kpi_base_search = self.kpi_base_search_interface.get_bulk(
|
|
"nobody", transaction_id=None
|
|
)
|
|
self.dict_of_original_entities = {
|
|
entity["_key"]: entity for entity in self.original_entities
|
|
}
|
|
self.dict_of_original_services = {
|
|
service["_key"]: service for service in self.original_services
|
|
}
|
|
self.dict_of_original_base_service_templates = {
|
|
base_service_template["_key"]: base_service_template
|
|
for base_service_template in self.original_base_service_templates
|
|
}
|
|
self.dict_of_original_kpi_templates = {
|
|
kpi_template["_key"]: kpi_template
|
|
for kpi_template in self.original_kpi_templates
|
|
}
|
|
self.dict_of_original_kpi_base_search = {
|
|
kpi_base_search["_key"]: kpi_base_search
|
|
for kpi_base_search in self.original_kpi_base_search
|
|
}
|
|
self.list_of_dicts = [
|
|
self.dict_of_original_base_service_templates,
|
|
self.dict_of_original_kpi_templates,
|
|
self.dict_of_original_services,
|
|
self.dict_of_original_entities,
|
|
self.dict_of_original_kpi_base_search,
|
|
]
|
|
self.dict_of_original_objs = {}
|
|
for self.item in self.list_of_dicts:
|
|
self.dict_of_original_objs.update(self.item)
|
|
self.all_updated_objs = []
|
|
|
|
def update_kvstore_objects(self, updated_objects):
|
|
"""
|
|
Takes list of updated objects after performing auto-remediation
|
|
and then stores them in kvstore using rest call
|
|
: param updated_objects: list of updated objects
|
|
"""
|
|
updated_base_service_templates = []
|
|
updated_services = []
|
|
updated_kpi_templates = []
|
|
updated_kpi_base_search = []
|
|
for updated_obj in updated_objects:
|
|
if updated_obj["object_type"] == "base_service_template":
|
|
updated_base_service_templates.append(updated_obj)
|
|
elif updated_obj["object_type"] == "service" or updated_obj["object_type"] == "entity":
|
|
updated_services.append(updated_obj)
|
|
elif updated_obj["object_type"] == "kpi_template":
|
|
updated_kpi_templates.append(updated_obj)
|
|
elif updated_obj["object_type"] == "kpi_base_search":
|
|
updated_kpi_base_search.append(updated_obj)
|
|
|
|
# Saving the updated objects in the backend
|
|
if updated_base_service_templates:
|
|
self.service_template_interface.batch_save_backend(
|
|
"nobody", updated_base_service_templates, transaction_id=None
|
|
)
|
|
if updated_services:
|
|
self.service_interface.batch_save_backend(
|
|
"nobody", updated_services, transaction_id=None
|
|
)
|
|
if updated_kpi_templates:
|
|
self.service_interface.batch_save_backend(
|
|
"nobody", updated_kpi_templates, transaction_id=None
|
|
)
|
|
if updated_kpi_base_search:
|
|
self.kpi_base_search_interface.batch_save_backend(
|
|
"nobody", updated_kpi_base_search, transaction_id=None
|
|
)
|
|
|
|
def make_logging_details(
|
|
self, precheck_id, total_count, severity, category, block_upgrade
|
|
):
|
|
logging_details = (
|
|
"[transaction_id={}] [precheck_id={}] [total_count={}] "
|
|
'[severity={}] [category="{}"] [blocks_upgrade={}] '.format(
|
|
self.transaction_id,
|
|
precheck_id,
|
|
total_count,
|
|
severity,
|
|
category,
|
|
block_upgrade,
|
|
)
|
|
)
|
|
|
|
return logging_details
|
|
|
|
def make_result(self, passed, precheck, message, object_type, object_id, **kwargs):
|
|
"""
|
|
Takes required parameters of the object that failed the prechecks and
|
|
then returns the dict of all these parameters so that proper formatted result
|
|
will get logged in the log file
|
|
: param passed: boolean for prechecks failed or passed
|
|
: param precheck: precheck type
|
|
: param message: required message to be added
|
|
: param object_type: type of the object (service, base service template, kpi template)
|
|
: param object_id: '_key' of the object
|
|
: return: dict of these parameters received
|
|
"""
|
|
precheck_dict = {
|
|
"passed": passed,
|
|
"pre-check": precheck,
|
|
"message": message,
|
|
"Object_type": object_type,
|
|
"object_id": object_id,
|
|
}
|
|
precheck_dict.update(kwargs)
|
|
return precheck_dict
|
|
|
|
def _check_empty_threshold_kpi(self, kpi):
|
|
"""
|
|
Check if the threshold fields in the KPIs of service is empty
|
|
1.If the KPI is shared base verify the metric field in the base search used by the KPI
|
|
2.If not then consider threshold field
|
|
:param kpi: ITSI Object for KPI
|
|
:return : None
|
|
"""
|
|
if not kpi.get("threshold_field", "") and (
|
|
kpi.get("search_type", "") == "shared_base"
|
|
and kpi.get("base_search_id", "") in self.kpi_base_search_empty_metrics
|
|
or kpi.get("search_type", "") == "adhoc"
|
|
):
|
|
self.kpi_with_empty_threshold_field.append(kpi)
|
|
|
|
def _check_threshold_in_kpi(self, service_obj):
|
|
"""
|
|
Check the threhold field in each KPI
|
|
:param service_obj: ITSI object with services and KPIs
|
|
:return : None
|
|
"""
|
|
kpi_list = service_obj.get("kpis", [])
|
|
|
|
def check_kpis(kpi):
|
|
if kpi["title"] != "ServiceHealthScore":
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
filtered_kpi_list = filter(check_kpis, kpi_list)
|
|
|
|
for kpi in filtered_kpi_list:
|
|
self._check_empty_threshold_kpi(kpi)
|
|
|
|
def _check_empty_metrics_for_kpi_base_search(self):
|
|
"""
|
|
Check for the empty metrics field in KPI Base search
|
|
:param : None
|
|
:return : None
|
|
"""
|
|
kpi_base_search_object = ItsiKPIBaseSearch(self.sessionkey, "unknown")
|
|
all_objects = kpi_base_search_object.get_bulk(owner=self.owner)
|
|
|
|
# Find all the objects that have empty metrics field
|
|
def get_kpi_base_obj_keys(kpi_base_obj):
|
|
if len(kpi_base_obj.get("metrics", [])) == 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
get_filtered_kpi_base_search_objs = filter(
|
|
get_kpi_base_obj_keys, all_objects)
|
|
|
|
for kpi_base_obj in get_filtered_kpi_base_search_objs:
|
|
self.kpi_base_search_empty_metrics.append(kpi_base_obj.get("_key"))
|
|
|
|
def get_kpi_with_empty_threshold_field(self):
|
|
"""
|
|
Get the KPIs that have empty threshold fields for each Service
|
|
: param : None
|
|
: return : None
|
|
"""
|
|
service_iter = self.mi_method.migration_get(
|
|
"service",
|
|
limit=get_object_batch_size(self.sessionkey),
|
|
get_raw=True,
|
|
fields=["_key", "identifying_name", "object_type", "kpis"],
|
|
source_kvstore=True,
|
|
get_raw_kwargs={"collection": "itsi_services"},
|
|
filter_data={"kpis.threshold_field": ""},
|
|
)
|
|
|
|
self._check_empty_metrics_for_kpi_base_search()
|
|
for service in service_iter:
|
|
self._check_threshold_in_kpi(service)
|
|
|
|
def parse_conf_file(self, file_path, stanza):
|
|
"""
|
|
Get conf stanza from file
|
|
: param file_path : file located at path
|
|
: param stanza : stanza name to get values of
|
|
: return : dict of key/values
|
|
"""
|
|
result = {}
|
|
current_stanza = None
|
|
if not os.path.isfile(file_path):
|
|
return result
|
|
with open(file_path, "r") as file:
|
|
for line in file:
|
|
line = line.strip()
|
|
|
|
if line.startswith("[") and line.endswith("]"):
|
|
current_stanza = line[1:-1]
|
|
elif current_stanza == stanza and "=" in line:
|
|
key, value = line.split("=", 1)
|
|
result[key.strip()] = value.strip()
|
|
|
|
return result
|
|
|
|
def parse_authorize_conf(self, file_path):
|
|
"""
|
|
Parse the authorize.conf file and return a dictionary of roles and their imported roles.
|
|
|
|
: param file_path : The file path of the authorize.conf file.
|
|
: return : A dictionary containing roles and their imported roles.
|
|
"""
|
|
roles = {}
|
|
if not os.path.isfile(file_path):
|
|
return roles
|
|
with open(file_path, "r") as file:
|
|
lines = file.readlines()
|
|
current_role = None
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line.startswith("[") and line.endswith("]"):
|
|
current_role = line[1:-1]
|
|
if current_role.startswith("role_"):
|
|
roles[current_role] = []
|
|
elif line.startswith("importRoles"):
|
|
if current_role is not None:
|
|
import_roles = line.split("=")[1].strip().split(";")
|
|
roles[current_role].extend(import_roles)
|
|
return roles
|
|
|
|
def get_all_import_roles(self, role, conf_files):
|
|
"""
|
|
Get all the roles imported by the given role from the specified configuration files.
|
|
|
|
: param role : The role to get imported roles for.
|
|
: param conf_files : A list of configuration files to parse.
|
|
: return : A list of roles imported by the given role.
|
|
"""
|
|
roles_data = {}
|
|
|
|
for conf_file in conf_files:
|
|
parsed_role = self.parse_authorize_conf(conf_file)
|
|
for key, value in parsed_role.items():
|
|
if value:
|
|
roles_data[key] = value
|
|
|
|
def filter_roles(role_dict):
|
|
updated_dict = {}
|
|
for key, value in role_dict.items():
|
|
updated_key = key.replace("role_", "")
|
|
updated_dict[updated_key] = value
|
|
return updated_dict
|
|
|
|
filtered_roles = filter_roles(roles_data)
|
|
|
|
def get_nested_roles(role_dict, target_role):
|
|
if target_role not in role_dict:
|
|
return []
|
|
|
|
nested_roles = []
|
|
stack = [target_role]
|
|
|
|
while stack:
|
|
current_role = stack.pop()
|
|
nested_roles.append(current_role)
|
|
if current_role in role_dict:
|
|
stack.extend(role_dict[current_role])
|
|
|
|
return nested_roles
|
|
|
|
all_roles = get_nested_roles(filtered_roles, role)
|
|
return list(set(all_roles))
|
|
|
|
def get_capabilities(self, file_paths, roles):
|
|
"""
|
|
Get the capabilities for the given roles from the specified configuration files.
|
|
|
|
: param file_paths : A list of file paths for the configuration files to parse.
|
|
: param roles : A list of roles to get capabilities for.
|
|
: return : A dictionary containing the capabilities for the given roles.
|
|
"""
|
|
all_capabilities = {}
|
|
for file_path in file_paths:
|
|
for role in roles:
|
|
capabilities = self.parse_conf_file(file_path, role)
|
|
all_capabilities.update(capabilities)
|
|
return {
|
|
key: value
|
|
for key, value in all_capabilities.items()
|
|
if value in ["enabled", "disabled"]
|
|
}
|
|
|
|
def get_native_capabilities(
|
|
self,
|
|
itsi_app_default_file_path,
|
|
system_default_file_path,
|
|
system_local_default_path,
|
|
metric_ad_authorize_conf_file,
|
|
role,
|
|
):
|
|
"""
|
|
Get the native capabilities for the given role.
|
|
|
|
: param itsi_app_default_file_path : Default file path of authorize.conf
|
|
: param role : Role to get native capabilities
|
|
: return native_capabilities : Dict containing native capabilities of given role
|
|
"""
|
|
default_capabilities = self.parse_conf_file(
|
|
itsi_app_default_file_path, "role_itoa_admin"
|
|
)
|
|
imported_roles = self.get_all_import_roles(
|
|
role,
|
|
[
|
|
itsi_app_default_file_path,
|
|
system_default_file_path,
|
|
system_local_default_path,
|
|
metric_ad_authorize_conf_file,
|
|
],
|
|
)
|
|
all_imported_roles = ["role_" + value for value in imported_roles]
|
|
inherited_capabilities = self.get_capabilities(
|
|
[
|
|
itsi_app_default_file_path,
|
|
system_default_file_path,
|
|
system_local_default_path,
|
|
metric_ad_authorize_conf_file,
|
|
],
|
|
all_imported_roles,
|
|
)
|
|
native_capabilities = {
|
|
key: value
|
|
for key, value in default_capabilities.items()
|
|
if key not in inherited_capabilities
|
|
}
|
|
|
|
return native_capabilities
|
|
|
|
def get_disabled_capabilities(self, role):
|
|
"""
|
|
Get capabilities that have been disabled by the user's for given role
|
|
: param role : role for which capabilities have been disabled
|
|
: return : Dict of disabled capabilities in context of itsi, and system
|
|
"""
|
|
system_local_file_path = make_splunkhome_path(
|
|
["etc", "system", "local", "authorize.conf"]
|
|
)
|
|
disabled_capabilities = {}
|
|
if os.path.isfile(system_local_file_path):
|
|
system_modified_capabilities = self.parse_conf_file(
|
|
system_local_file_path, "role_" + role
|
|
)
|
|
if system_modified_capabilities:
|
|
disabled_capabilities["system"] = {
|
|
key: value
|
|
for key, value in system_modified_capabilities.items()
|
|
if value == "disabled"
|
|
}
|
|
return disabled_capabilities
|
|
|
|
def get_modified_capabilities(self, native_capabilities, updated_capabilities):
|
|
"""
|
|
Get the modified capabilities based on the native capabilities and the updated capabilities.
|
|
|
|
: param native_capabilities : A dictionary containing native capabilities.
|
|
: param updated_capabilities : A dictionary containing updated capabilities.
|
|
: return : A list of modified capabilities.
|
|
"""
|
|
native_capability_keys = set(native_capabilities.keys())
|
|
|
|
modified_capability_keys = set()
|
|
for value in updated_capabilities.values():
|
|
if isinstance(value, dict):
|
|
modified_capability_keys.update(value.keys())
|
|
|
|
common_capabilities = native_capability_keys.intersection(
|
|
modified_capability_keys
|
|
)
|
|
|
|
return list(common_capabilities)
|
|
|
|
def get_modified_capability_for_current_user(
|
|
self,
|
|
current_user_context,
|
|
modified_native_capabilities,
|
|
native_user_capabilities,
|
|
):
|
|
"""
|
|
Determine the modified capabilities for the current user based on the native capabilities and the modified capabilities.
|
|
|
|
: param current_user_context : A dictionary containing information about the current user, including their username and capabilities.
|
|
: param modified_native_capabilities : A list of capabilities that have been modified from their native state.
|
|
: param native_user_capabilities : A list of capabilities that are native to the user.
|
|
: return : A list of capabilities that have been modified for the current user.
|
|
"""
|
|
removed_capability = []
|
|
filtered_native_user_capabilities = [
|
|
key
|
|
for key, value in native_user_capabilities.items()
|
|
if value.lower() in ["enabled", "disabled"]
|
|
]
|
|
if current_user_context.get("username") in ["admin", "splunk-system-user"]:
|
|
return modified_native_capabilities
|
|
else:
|
|
for capability in filtered_native_user_capabilities:
|
|
if capability not in current_user_context.get("capabilities"):
|
|
removed_capability.append(capability)
|
|
return removed_capability
|