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.
216 lines
9.5 KiB
216 lines
9.5 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
import json
|
|
import re
|
|
|
|
import splunk.rest as rest
|
|
|
|
from ITOA.itoa_common import get_conf_data, normalize_bool_flag, convert_to_bytes
|
|
from ITOA.saved_search_utility import SavedSearch
|
|
|
|
from itsi.upgrade.constants import CONF_COLLECTIONS, CONF_TRANSFORMS, OP_SET_EQUAL, OP_BOOL_EQUAL, OP_GREATER_EQUAL,\
|
|
OP_EQUAL, EXPECTED_COLLECTIONS, COLLECTION_STATS_URL, KVSTORE_COLLECTION_SIZE_LIMIT_DEFAULT, CONF_COMMANDS,\
|
|
CONF_SAVEDSEARCHES
|
|
|
|
from migration.migration_precheck import MigrationPreCheck
|
|
|
|
|
|
class EAPrechecks(MigrationPreCheck):
|
|
def __init__(self, session_key, logger, pre_checks, skip_pre_checks):
|
|
MigrationPreCheck.__init__(self, session_key, logger, pre_checks, skip_pre_checks)
|
|
|
|
def precheck_RECHK(self):
|
|
'''
|
|
check if rules engine is enabled/disabled
|
|
and populate precheck_results list accordingly
|
|
|
|
@rtype: list
|
|
@return: list of dictionaries that has following key/values:
|
|
{bool} if the precheck passed
|
|
{string} corresponding recommendation
|
|
'''
|
|
self.rules_engine_saved_search = SavedSearch.get_search(
|
|
self.session_key, 'itsi_event_grouping')
|
|
if int(self.rules_engine_saved_search['disabled']) == 1:
|
|
return [self._make_result(False, 'Rules Engine is not running', 'Warn')]
|
|
return [self._make_result(True, 'Rules Engine is up and running', 'Success' )]
|
|
|
|
def precheck_KVSIZECHK(self):
|
|
'''
|
|
check if kv store size checks limits are not exceeded
|
|
and populate precheck_results list accordingly
|
|
|
|
@rtype: list
|
|
@return: list of dictionaries that has following key/values:
|
|
{bool} if the precheck passed
|
|
{string} corresponding recommendation
|
|
'''
|
|
results = self.get_collection_sizes()
|
|
if isinstance(results, tuple):
|
|
return [self._make_result(results[0], results[1], 'Error')]
|
|
|
|
kv_store_collection_size_limit = KVSTORE_COLLECTION_SIZE_LIMIT_DEFAULT
|
|
stanzas = get_conf_data(self.session_key, 'itsi_event_management', app='SA-ITOA')
|
|
if 'precheck' in stanzas and 'kv_store_collection_size_limit' in stanzas['precheck']:
|
|
kv_store_collection_size_limit = int(stanzas['precheck']['kv_store_collection_size_limit'])
|
|
self.logger.info('Got kv_store_collection_size_limit=%s from itsi_event_management.conf'
|
|
% kv_store_collection_size_limit)
|
|
|
|
ret_list = []
|
|
collection_list = []
|
|
for each_result in results:
|
|
self.logger.info('Collection %s for app %s has %s objects.' %
|
|
(each_result['collection'], each_result['app'], each_result['count']))
|
|
if int(each_result['count']) >= kv_store_collection_size_limit:
|
|
ret_list.append(self._make_result(
|
|
False, 'The %s collection contains %s objects which exceeds the KV store size limit of %s objects.' %
|
|
(each_result['collection'], each_result['count'], kv_store_collection_size_limit), 'Warn'))
|
|
if each_result['collection'] in EXPECTED_COLLECTIONS:
|
|
collection_list.append(each_result['collection'])
|
|
if len(collection_list) < len(EXPECTED_COLLECTIONS):
|
|
ret_list.append('Some KVStore collection(s) are missing: %s' %
|
|
filter(lambda x: x not in EXPECTED_COLLECTIONS, collection_list))
|
|
if len(ret_list) == 0:
|
|
return [self._make_result(True, 'KV store size checks are within limits', 'Success')]
|
|
return ret_list
|
|
|
|
def get_collection_sizes(self):
|
|
'''
|
|
Get collection sizes
|
|
|
|
@rtype: list or tuple
|
|
@return: list of collections and sizes in the system if success, otherwise return tuple for any failures.
|
|
'''
|
|
try:
|
|
response, content = rest.simpleRequest(COLLECTION_STATS_URL, sessionKey=self.session_key, method='GET',
|
|
getargs={'output_mode': 'json'}, rawResult=True)
|
|
except Exception as e:
|
|
self.logger.exception(e)
|
|
return False, "Failed to fetch KV store statistics."
|
|
|
|
if response.status != 200:
|
|
message = 'Error retrieving KV store statistics. Response: %s, Content: %s' % (response, content)
|
|
self.logger.error(message)
|
|
return False, message
|
|
|
|
data = json.loads(content)
|
|
if 'entry' not in data or len(data['entry']) < 1:
|
|
message = 'Cannot find entry in the collection statistics.'
|
|
self.logger.error(message)
|
|
return False, message
|
|
ret_list = []
|
|
itsi_collection = ['SA-ITOA', 'SA-ITSI-ATAD', 'SA-ITSI-MetricAD', 'SA-UserAccess']
|
|
try:
|
|
for d in data['entry'][0]['content']['data']:
|
|
item = json.loads(d)
|
|
ns = item['ns'].split('.')
|
|
if ns[0] in itsi_collection:
|
|
ret_list.append({
|
|
'app': ns[0],
|
|
'collection': ns[1],
|
|
'count': int(item['count'])
|
|
})
|
|
except Exception as e:
|
|
message = 'Cannot load collection entry from response: %s' % e.message
|
|
self.logger.error(message)
|
|
return False, message
|
|
return ret_list
|
|
|
|
def precheck_EACONFIG(self):
|
|
'''
|
|
check if various EA config checks limits are not exceeded
|
|
and populate precheck_results list accordingly
|
|
|
|
@rtype: list
|
|
@return: list of dictionaries that has following key/values:
|
|
{bool} if the precheck passed
|
|
{string} corresponding recommendation
|
|
'''
|
|
ret_list = self.check_config('collections', CONF_COLLECTIONS)
|
|
ret_list.extend(self.check_config('transforms', CONF_TRANSFORMS))
|
|
ret_list.extend(self.check_config('commands', CONF_COMMANDS))
|
|
ret_list.extend(self.check_config('savedsearches', CONF_SAVEDSEARCHES))
|
|
if len(ret_list) == 0:
|
|
return [self._make_result(True, 'Event Analytics configurations are expected', 'Success')]
|
|
return ret_list
|
|
|
|
def criteria_to_string(self, criteria):
|
|
'''
|
|
Convert criteria to human readable string
|
|
|
|
@type criteria: dict
|
|
@param criteria: the criteria to be converted
|
|
|
|
@rtype: basestring
|
|
@return: the human readable string
|
|
'''
|
|
if criteria['op'] == OP_SET_EQUAL:
|
|
return 'Expected values in the list are %s' % ', '.join(['"%s"' % s for s in criteria['value']])
|
|
return 'Expected value is "%s"' % criteria['value']
|
|
|
|
def check_config(self, conf_file, stanza_config, app='SA-ITOA'):
|
|
'''
|
|
Check whether configuration in a config file satisfy the criteria
|
|
|
|
@type conf_file: basestring
|
|
@param conf_file: The config file name without .conf extention
|
|
|
|
@type stanza_config: dict
|
|
@param stanza_config: the configuration of the criterias for the stanzas to be checked.
|
|
|
|
@type app: basestring
|
|
@param app: the app context
|
|
|
|
@rtype: list of dict
|
|
@return: the dict of the results
|
|
'''
|
|
ret_list = []
|
|
stanza_dict = get_conf_data(self.session_key, conf_file, app=app)
|
|
for stanza in stanza_dict:
|
|
if stanza not in stanza_config:
|
|
continue
|
|
params = stanza_dict.get(stanza)
|
|
for param, check in stanza_config[stanza].items():
|
|
if param not in params:
|
|
self.logger.error('Cannot find param %s in stanza %s in file %s' % (param, stanza, conf_file))
|
|
continue
|
|
value = params.get(param)
|
|
|
|
# extract number from a string if conf file is commands.conf
|
|
# the expected string format is command.arg.1=-J-Xmx8192M
|
|
tmp = re.search(r'[A-Za-z]*(\d+[KMGTkmgt])', value)
|
|
if tmp:
|
|
value = tmp.group(1)
|
|
value = convert_to_bytes(value)
|
|
|
|
self.logger.info('Verifying parameter %s with criteria %s on value %s (config file: %s, stanza: %s)'
|
|
% (param, check, value, conf_file, stanza))
|
|
result = self.verify_value(check, value)
|
|
if not result:
|
|
message = 'Validation failed for parameter in %s.conf: \'%s\' = "%s" in the [%s] stanza. %s' %\
|
|
(conf_file, param, value, stanza, self.criteria_to_string(check))
|
|
self.logger.warn(message)
|
|
ret_list.append(self._make_result(False, message, 'Warn'))
|
|
return ret_list
|
|
|
|
def verify_value(self, check_criteria, actual_value):
|
|
"""
|
|
Verify if the actual value match the check criteria
|
|
|
|
@type check_criteria: dict
|
|
@param check_criteria: the check criteria to pass
|
|
|
|
@type actual_value: basestring
|
|
@param actual_value: the actual value in string type
|
|
|
|
@rtype: bool
|
|
@return: True means pass, False means failed.
|
|
"""
|
|
if check_criteria['op'] == OP_EQUAL:
|
|
return actual_value == check_criteria['value']
|
|
if check_criteria['op'] == OP_SET_EQUAL:
|
|
return set([v.strip() for v in actual_value.split(',')]) == set(check_criteria['value'])
|
|
if check_criteria['op'] == OP_GREATER_EQUAL:
|
|
return actual_value >= check_criteria['value']
|
|
if check_criteria['op'] == OP_BOOL_EQUAL:
|
|
return normalize_bool_flag(actual_value) == normalize_bool_flag(check_criteria['value'])
|