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.
199 lines
10 KiB
199 lines
10 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
|
|
from itsi_py3 import _
|
|
from . import itoa_change_handler
|
|
from itsi.objects.itsi_service import ItsiService
|
|
from itsi.objects.itsi_sandbox_service import ItsiSandboxService
|
|
|
|
|
|
class ServiceDependencyHandler(itoa_change_handler.ItoaChangeHandler):
|
|
"""
|
|
Change handler that will update dependent services information with services_depends_on_me field
|
|
"""
|
|
|
|
def deferred(self, change, transaction_id=None):
|
|
"""
|
|
@type change: dict
|
|
@param change: dict which hold job information
|
|
{ _key: system generated key
|
|
create_time: epoch time of the CUD event that occurred
|
|
changed_object_key: [key(s) of the changed object(s)]
|
|
changed_object_type: String identifier of the object type in the changed_object
|
|
change_type: The type of change that occurred
|
|
object_type: 'refresh_job'
|
|
change_detail:
|
|
}
|
|
@type transaction_id: basestring
|
|
@param transaction_id: transaction id for instrumentation purposes.
|
|
@return: True if all operations were successful, False otherwise
|
|
@rtype: Boolean
|
|
"""
|
|
if change.get('changed_object_type') not in ['service', 'sandbox_service']:
|
|
raise Exception(_('Expected changed_object_type to be "service" or sandbox_service'))
|
|
|
|
if change.get('change_type') != 'service_dependency_changed':
|
|
raise Exception(_('Expected change_type to be "service_dependency_changed"'))
|
|
|
|
if change.get('changed_object_type') == 'service':
|
|
service_interface = ItsiService(self.session_key, 'nobody')
|
|
else:
|
|
service_interface = ItsiSandboxService(self.session_key, 'nobody')
|
|
|
|
updated_services = {}
|
|
|
|
change_detail = change.get("change_detail", {})
|
|
owner = 'nobody'
|
|
|
|
services_depends_on_update = change_detail.get('services_depends_on', {})
|
|
services_depending_on_me_update = change_detail.get('services_depending_on_me', {})
|
|
|
|
if len(services_depends_on_update) != 0:
|
|
|
|
#Bulk request all of the services
|
|
service_keys = []
|
|
for cd in services_depends_on_update.values():
|
|
for ad in cd['added_dependencies']:
|
|
service_keys.append(ad['target_service'])
|
|
for rd in cd['removed_dependencies']:
|
|
service_keys.append(rd['target_service'])
|
|
|
|
if not service_keys:
|
|
return True
|
|
|
|
filter_data = {
|
|
'$or': [{'_key': service_key} for service_key in service_keys]
|
|
}
|
|
services = service_interface.get_bulk(owner,
|
|
filter_data=filter_data,
|
|
req_source="ServiceDependencyHandler",
|
|
transaction_id=transaction_id)
|
|
|
|
# Translate to dict at first
|
|
target_services = {s["_key"]:s for s in services}
|
|
for service_key, detail in services_depends_on_update.items():
|
|
added_dependencies = detail['added_dependencies']
|
|
removed_dependencies = detail['removed_dependencies']
|
|
|
|
for added_dependency in added_dependencies:
|
|
target_service_key = added_dependency['target_service']
|
|
# service could have been updated previously, fetch it from updated_services if available
|
|
if target_service_key in updated_services:
|
|
target_service = updated_services.get(target_service_key)
|
|
# otherwise fetch from kvstore
|
|
else:
|
|
target_service = target_services.get(target_service_key)
|
|
if target_service is None:
|
|
self.logger.warning("Attempted to update missing service=%s", target_service_key)
|
|
continue
|
|
new_depending_kpis = added_dependency['depending_kpis']
|
|
existing_dependencies =\
|
|
target_service.get('services_depending_on_me') if target_service is not None else None
|
|
|
|
matched_dependencies = []
|
|
if existing_dependencies is not None:
|
|
matched_dependencies = [d for d in existing_dependencies if d.get('serviceid') == service_key]
|
|
if len(matched_dependencies) > 0:
|
|
# should only be 1
|
|
if len(matched_dependencies) > 1:
|
|
self.logger.info('Service "%s" is referenced more than once in the service dependencies.', service_key)
|
|
matched_dependencies[0].get('kpis_depending_on', []).extend(new_depending_kpis)
|
|
matched_dependencies[0]['kpis_depending_on'] = list(set(
|
|
matched_dependencies[0]['kpis_depending_on']
|
|
))
|
|
else:
|
|
if existing_dependencies is None:
|
|
existing_dependencies = []
|
|
existing_dependencies.append({
|
|
'serviceid': service_key,
|
|
'kpis_depending_on': list(set(new_depending_kpis))
|
|
})
|
|
#Patching the call, kept on getting a crash in this part of the code
|
|
if target_service is not None:
|
|
target_service['services_depending_on_me'] = existing_dependencies
|
|
updated_services[target_service_key] = target_service
|
|
|
|
self.logger.debug('removed dependencies: ' + str(removed_dependencies))
|
|
for removed_dependency in removed_dependencies:
|
|
target_service_key = removed_dependency['target_service']
|
|
self.logger.debug('removed dependency key: ' + target_service_key)
|
|
if target_service_key in updated_services:
|
|
target_service = updated_services.get(target_service_key)
|
|
else:
|
|
target_service = target_services.get(target_service_key)
|
|
depending_kpis_to_remove = removed_dependency['depending_kpis']
|
|
existing_dependencies = target_service.get('services_depending_on_me') if target_service is not None else None
|
|
|
|
matched_dependencies = []
|
|
if existing_dependencies is not None:
|
|
matched_dependencies = [d for d in existing_dependencies if d.get('serviceid') == service_key]
|
|
if len(matched_dependencies) > 0:
|
|
existing_kpis = matched_dependencies[0].get('kpis_depending_on')
|
|
existing_kpis = [x for x in existing_kpis if x not in depending_kpis_to_remove]
|
|
# if no KPI dependencies remaining for this service, remove this service as a dependency
|
|
if len(existing_kpis) == 0:
|
|
existing_dependencies = [d for d in existing_dependencies if d.get('serviceid') != service_key]
|
|
target_service['services_depending_on_me'] = existing_dependencies
|
|
# otherwise just remove this KPI
|
|
else:
|
|
self.logger.debug('existing kpis: ' + str(existing_kpis))
|
|
matched_dependencies[0]['kpis_depending_on'] = existing_kpis
|
|
updated_services[target_service_key] = target_service
|
|
else:
|
|
self.logger.error('Could not find service "' + service_key + '" in target service dependency')
|
|
|
|
|
|
# update services for the case of moving service between sec_groups and need to break
|
|
# services_depends_on based on services_depending_on_me change
|
|
if len(services_depending_on_me_update) != 0:
|
|
|
|
#Bulk request all of the services
|
|
service_keys = []
|
|
for cd in services_depending_on_me_update.values():
|
|
service_keys.extend(cd)
|
|
|
|
if not service_keys:
|
|
return True
|
|
|
|
filter_data = {
|
|
'$or': [{'_key': service_key} for service_key in service_keys]
|
|
}
|
|
services = service_interface.get_bulk(owner,
|
|
filter_data=filter_data,
|
|
req_source="ServiceDependencyHandler",
|
|
transaction_id=transaction_id)
|
|
|
|
# Translate to dict at first
|
|
target_services = {s["_key"]:s for s in services}
|
|
for service_key, removed_dependencies in services_depending_on_me_update.items():
|
|
|
|
self.logger.debug('removed dependencies: ' + str(removed_dependencies))
|
|
for target_service_key in removed_dependencies:
|
|
self.logger.debug('removed dependency key: ' + target_service_key)
|
|
if target_service_key in updated_services:
|
|
target_service = updated_services.get(target_service_key)
|
|
else:
|
|
target_service = target_services.get(target_service_key)
|
|
|
|
existing_dependencies = target_service.get('services_depends_on') if target_service is not None else None
|
|
|
|
matched_dependencies = []
|
|
if existing_dependencies is not None:
|
|
matched_dependencies = [d for d in existing_dependencies if d.get('serviceid') == service_key]
|
|
if len(matched_dependencies) > 0:
|
|
existing_dependencies = [d for d in existing_dependencies if d.get('serviceid') != service_key]
|
|
target_service['services_depends_on'] = existing_dependencies
|
|
updated_services[target_service_key] = target_service
|
|
else:
|
|
self.logger.error('Could not find service "' + service_key + '" in target service dependency')
|
|
|
|
|
|
if len(updated_services) == 0:
|
|
return True # Noop
|
|
|
|
try:
|
|
service_interface.batch_save_backend('nobody', list(updated_services.values()), transaction_id=transaction_id)
|
|
return True
|
|
except Exception as e:
|
|
self.logger.exception(e)
|
|
return False
|