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.
183 lines
7.0 KiB
183 lines
7.0 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
import time
|
|
|
|
from ITOA.setup_logging import logger
|
|
from ITOA.itoa_object import ItoaObject, CRUDMethodTypes
|
|
|
|
|
|
class ItsiRefreshQueueJob(ItoaObject):
|
|
"""
|
|
Implements ITSI Refresh Queue Job
|
|
"""
|
|
|
|
log_prefix = '[ITSI Refresh Queue Job] '
|
|
collection_name = 'itsi_refresh_queue'
|
|
logger = logger
|
|
|
|
def __init__(self, session_key, current_user_name):
|
|
super(ItsiRefreshQueueJob, self).__init__(session_key, current_user_name, 'refresh_queue_job',
|
|
collection_name=self.collection_name, title_validation_required=False)
|
|
|
|
def do_object_validation(self, owner, objects, validate_name=True, dupname_tag=None, transaction_id=None,
|
|
skip_local_failure=False):
|
|
"""
|
|
Generic object validation routine for the refresh job.
|
|
It consists of title-related validation and specific checks for required fields:
|
|
'change_type', 'changed_object_key', 'changed_object_type'. It also validates the 'priority' field.
|
|
All new object level validation should be invoked from here.
|
|
|
|
@type objects: list[dict]
|
|
@param objects: list of dict
|
|
@return: None. Throws exceptions on validation errors.
|
|
"""
|
|
super(ItsiRefreshQueueJob, self).do_object_validation(owner, objects, validate_name, dupname_tag,
|
|
transaction_id, skip_local_failure)
|
|
required_fields = ['change_type', 'changed_object_key', 'changed_object_type']
|
|
for json_data in objects:
|
|
priority = json_data.get('priority')
|
|
if priority and priority not in [0, -1, 1]:
|
|
self.raise_error_bad_validation(logger, 'Invalid priority: Priority must be either 0, -1, or 1.', 409)
|
|
for field in required_fields:
|
|
value = json_data.get(field)
|
|
if value is None:
|
|
self.raise_error_bad_validation(logger, f'Missing required field: {field}', 409)
|
|
|
|
def do_additional_setup(self, owner, objects, req_source='unknown', method=CRUDMethodTypes.METHOD_UPSERT,
|
|
transaction_id=None, skip_local_failure=False):
|
|
"""
|
|
@type owner: string
|
|
@param owner: user who is performing this operation
|
|
@type objects: list of dictionary
|
|
@param objects: list of objects being written
|
|
@type req_source: string
|
|
@param req_source: string identifying source of this request
|
|
@return: none, throws exceptions on errors
|
|
"""
|
|
for json_data in objects:
|
|
if 'priority' not in json_data:
|
|
json_data['priority'] = 0
|
|
if 'number_of_failures' not in json_data:
|
|
json_data['number_of_failures'] = 0
|
|
return
|
|
|
|
def get(self, owner, object_id, req_source='unknown', transaction_id=None):
|
|
"""
|
|
Over the parent method of retrieves object by id, add queue_time to the data
|
|
@type owner: basestring
|
|
@param owner: user who is performing this operation
|
|
@type object_id: basestring
|
|
|
|
@type object_id: string
|
|
@param object_id: id of object to retrieve
|
|
@type req_source: basestring
|
|
|
|
@type req_source: string
|
|
@param req_source: identified source initiating the operation
|
|
|
|
@rtype: dictionary
|
|
@return: object matching id on success, empty rows if object is not found, throws exceptions on errors
|
|
"""
|
|
job_data = super(ItsiRefreshQueueJob, self).get(owner, object_id, req_source, transaction_id)
|
|
self.add_queue_time(job_data)
|
|
return job_data
|
|
|
|
def get_bulk(
|
|
self,
|
|
owner,
|
|
sort_key=None,
|
|
sort_dir=None,
|
|
filter_data=None,
|
|
fields=None,
|
|
skip=None,
|
|
limit=None,
|
|
req_source='unknown',
|
|
transaction_id=None
|
|
):
|
|
"""
|
|
Override the parent method of retrieves objects matching criteria, if no filtering specified, retrieves all
|
|
objects of this object type, add queue_time
|
|
|
|
@type owner: string
|
|
@param owner: user who is performing this operation
|
|
|
|
@type sort_key: string
|
|
@param sort_key: string defining keys to sort by
|
|
|
|
@type sort_dir: string
|
|
@param sort_dir: string defining direction for sorting - asc or desc
|
|
|
|
@type filter_data: dictionary
|
|
@param filter_data: json filter constructed to filter data. Follows mongodb syntax
|
|
|
|
@type fields: list
|
|
@param fields: list of fields to retrieve, fetches all fields if not specified
|
|
|
|
@type skip: number
|
|
@param skip: number of items to skip from the start
|
|
|
|
@type limit: number
|
|
@param limit: maximum number of items to return
|
|
|
|
@type req_source: string
|
|
@param req_source: identified source initiating the operation
|
|
|
|
@type transaction_id: string
|
|
@param transaction_id: transaction id
|
|
|
|
@rtype: list of dictionary
|
|
@return: objects retrieved on success, throws exceptions on errors
|
|
"""
|
|
job_data_list = super(ItsiRefreshQueueJob, self).get_bulk(owner, sort_key, sort_dir, filter_data, fields, skip,
|
|
limit, req_source, transaction_id)
|
|
for job_data in job_data_list:
|
|
self.add_queue_time(job_data)
|
|
return job_data_list
|
|
|
|
def fetch_queued_jobs(self, owner, **kwargs):
|
|
"""
|
|
Fetch jobs that are considered queued (non-blocking).
|
|
|
|
:param owner: user who is performing this operation
|
|
:type owner: string
|
|
|
|
:return: objects retrieved on success, throws exceptions on errors
|
|
:rtype: list of dictionary
|
|
"""
|
|
return self.get_bulk(owner, filter_data={"number_of_failures": {"$lt": 1}}, **kwargs)
|
|
|
|
def add_queue_time(self, job_data):
|
|
"""
|
|
Add queue time field to refresh queue job
|
|
|
|
@type job_data: string
|
|
@param job_data: dict containing refresh queue job info
|
|
"""
|
|
if job_data:
|
|
last_queued_time = job_data.get("last_queued_time")
|
|
last_saved_queue_time = job_data.get("last_saved_queue_time", 0)
|
|
if job_data.get('number_of_failures', 0) > 0 or last_queued_time is None:
|
|
queue_time = last_saved_queue_time
|
|
else:
|
|
queue_time = time.time() - last_queued_time + last_saved_queue_time
|
|
job_data['queue_time'] = queue_time
|
|
|
|
def wait_for_unblocked_queue(self):
|
|
"""
|
|
Wait for the refresh queue to be unblocked
|
|
|
|
:return: Is the queue unblocked?
|
|
:rtype: Boolean
|
|
"""
|
|
is_queue_blocked = True
|
|
total_retries = 6
|
|
retry = 0
|
|
|
|
while is_queue_blocked and retry < total_retries:
|
|
if len(self.fetch_queued_jobs('nobody')) == 0:
|
|
is_queue_blocked = False
|
|
else:
|
|
time.sleep(5)
|
|
retry += 1
|
|
logger.info(f"waiting for empty queue (retry: {retry})")
|
|
return not is_queue_blocked
|