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.
372 lines
14 KiB
372 lines
14 KiB
# Copyright (C) 2005-2025 Splunk Inc. All Rights Reserved.
|
|
|
|
"""
|
|
Use this module to work with your Events in ITSI!
|
|
|
|
Due to dependency on a Splunk ITSI module, this module must reside on the same
|
|
host where ITSI is currently installed. Eventually, we will be moving to
|
|
consuming standard REST interfaces and then it should be OK to move this module
|
|
to a different host.
|
|
"""
|
|
|
|
import itsi_py3
|
|
import sys
|
|
import time
|
|
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
|
|
from splunk import ResourceNotFound, RESTException
|
|
|
|
sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-ITOA', 'lib', 'ITOA']))
|
|
from ITOA.itoa_common import extract
|
|
|
|
sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-ITOA', 'lib', 'itsi', 'event_management']))
|
|
from itsi_notable_event import ItsiNotableEvent
|
|
|
|
from .eventing_base import EventBase
|
|
from ITOA.event_management.notable_event_utils import NotableEventConfiguration
|
|
|
|
########
|
|
# Setup a default Logger
|
|
########
|
|
from ITOA.setup_logging import logger
|
|
|
|
########
|
|
# APIs
|
|
########
|
|
|
|
|
|
class EventMeta(EventBase):
|
|
"""
|
|
Import this class to get information about ITSI Events.
|
|
- What are all the available status values I can set on an Event?
|
|
- What are all the available owners I can have for an Event?
|
|
- What are all the available severities I can set on an Event?
|
|
|
|
Usage:
|
|
>>> meta = EventMeta(session_key) # or
|
|
>>> meta = EventMeta(None, username, password)
|
|
|
|
Provide your own logger if you want to, else we'll default to default_logger
|
|
"""
|
|
def __init__(self, session_key, username=None, password=None, logger=logger):
|
|
"""
|
|
@type session_key: string
|
|
@param session_key: session key given by Splunkd when you log in
|
|
If you dont have one, pass in None. But you will have to provide
|
|
credentials
|
|
|
|
@type username: string
|
|
@param username: your username
|
|
|
|
@type password: string
|
|
@param password: your password
|
|
|
|
@type logger: object of type logger
|
|
@param logger: if you have an existing logger, pass it in, we'll log
|
|
stuff there...else check ITSI logs
|
|
"""
|
|
super(EventMeta, self).__init__(session_key, username, password, logger)
|
|
self.event_meta = NotableEventConfiguration(self.master_host_session_key, self.logger, host_base_uri=self.master_host_uri)
|
|
|
|
def get_all_statuses(self):
|
|
"""
|
|
get all possible valid status values for a Notable Event
|
|
Ex: `open`, `closed`, 'in progress`, `assigned` etc...
|
|
|
|
@rtype: list
|
|
@return: list of all possible configured statuses
|
|
#
|
|
"""
|
|
return self.event_meta.get_statuses()
|
|
|
|
def get_all_severities(self):
|
|
"""
|
|
get all possible valid severity values for a Notable Event
|
|
Ex: `high`, `critical` and so on...
|
|
|
|
@rtype: list
|
|
@return: list of all possible configured severities
|
|
"""
|
|
return self.event_meta.get_severities()
|
|
|
|
def get_all_owners(self):
|
|
"""
|
|
get all possible valid owner values for a Notable Event
|
|
Ex: `foo`, `bar`, `mr. booboo`
|
|
|
|
@rtype: list
|
|
@return: list of all possible configured owners
|
|
"""
|
|
return self.event_meta.get_owners()
|
|
|
|
|
|
###########################################################################################
|
|
# Starting from ITSI 4.0, the operations for events are moved to episode scope.
|
|
# So the Event create and update operations in Event class are not valid.
|
|
# In next SDK version the CRUD operations from Event class are ported to EventGroup Class.
|
|
###########################################################################################
|
|
class Event(EventBase):
|
|
"""
|
|
Import this class to operate on ITSI Events.
|
|
"""
|
|
def __init__(self, session_key, username=None, password=None, logger=logger):
|
|
"""
|
|
@type session_key: string
|
|
@param session_key: session key given by Splunkd when you log in
|
|
If you dont have one, pass in None. But you will have to provide
|
|
credentials
|
|
|
|
@type username: string
|
|
@param username: your username
|
|
|
|
@type password: string
|
|
@param password: your password
|
|
|
|
@type logger: object of type logger
|
|
@param logger: if you have an existing logger, pass it in, we'll log
|
|
stuff there...else check ITSI logs
|
|
"""
|
|
super(Event, self).__init__(session_key, username, password, logger)
|
|
try:
|
|
self.event = ItsiNotableEvent(self.session_key, logger=self.logger)
|
|
except Exception:
|
|
self.logger.exception('Errors while initializing Event object. Event class has been deprecated.')
|
|
self.event = None
|
|
|
|
def _get_from_index(self, event_ids, split_by="," , **kwargs):
|
|
"""
|
|
given an id or list of ids, return a dictionary of pertinent fields
|
|
for that event. This method runs a Splunk search.
|
|
|
|
@type event_id: str
|
|
@param event_id: a unique id for an event
|
|
|
|
@type split_by: str
|
|
@param split_by: if event_ids is of type basestring, we will split it
|
|
into many event ids into a list. What is the separator? Defaults to `,`
|
|
|
|
@type kwargs: dict
|
|
@param kwargs: send in keys `earliest_time` and
|
|
`latest_time` with corresponding values if you know what you are doing.
|
|
Ex:
|
|
'-15m' implies '15 mins ago'
|
|
'-15s' implies '15 seconds ago'
|
|
'-15d' implies '15 days ago'
|
|
'-15w' implies '15 weeks ago'
|
|
'rt' implies real time
|
|
'now' implies current time
|
|
no other values are supported
|
|
|
|
@return a valid Dict if event is found; None if no resource is found
|
|
"""
|
|
if isinstance(event_ids, itsi_py3.string_type):
|
|
event_ids = event_ids.split(',')
|
|
|
|
if not isinstance(event_ids, list):
|
|
raise TypeError('Expecting event_ids to be a string or list')
|
|
|
|
# we only care about `earliest_time` and `latest_time`. get rid of other
|
|
# keys
|
|
supported_params = ('earliest_time', 'latest_time')
|
|
for i in list(kwargs.keys()):
|
|
kwargs.pop(i) if i not in supported_params else None
|
|
|
|
objects = None
|
|
|
|
# indexing takes time and just because we have an event_id does not
|
|
# really means that event has been indexed. we shall therefore
|
|
# retry..yes, 10 times.
|
|
retries = 10
|
|
while retries:
|
|
time.sleep(5)
|
|
try:
|
|
objects = self.event.get_bulk(event_ids)
|
|
if objects:
|
|
break
|
|
retries -= 1
|
|
except ResourceNotFound:
|
|
self.logger.exception('Unable to find event_id: %s', event_ids)
|
|
break
|
|
except Exception:
|
|
self.logger.exception('Internal Error.')
|
|
break
|
|
return objects
|
|
|
|
def get_severity(self, events=None, event_ids=None, split_by=",", **kwargs):
|
|
"""
|
|
given a list of event ids, return their severities
|
|
@type events: list of dicts
|
|
@param events: each dict in the list represents an event that was sent
|
|
to us by Splunk as an outcome of a Custom Action. the get_event()
|
|
method in class CustomGroupActionBase generates such an item.
|
|
High performant.
|
|
|
|
@type event_ids: str or list
|
|
@param event_ids: a unique id of an event or list of event ids of events
|
|
Less performant.
|
|
|
|
@type split_by: str
|
|
@param split_by: if `event_ids` is of type basestring, we will split it
|
|
into many event ids;into a list. What is the separator? Defaults to `,`
|
|
|
|
@type kwargs: dict
|
|
@param kwargs: send in keys `earliest_time` and
|
|
`latest_time` with corresponding values if you know what you are doing.
|
|
Ex:
|
|
'-15m' implies '15 mins ago'
|
|
'-15s' implies '15 seconds ago'
|
|
'-15d' implies '15 days ago'
|
|
'-15w' implies '15 weeks ago'
|
|
'rt' implies real time
|
|
'now' implies current time
|
|
no other values are supported
|
|
|
|
@return a list of tuples (event_id: severity) on valid request
|
|
None on invalid request.
|
|
"""
|
|
# validate + normalize
|
|
if not events and not event_ids:
|
|
raise ValueError('Expecting either `events` or `event_ids`. Both are None.')
|
|
if events and isinstance(events, dict):
|
|
events = [events]
|
|
if events and not isinstance(events, list):
|
|
raise TypeError(('Invalid type for `events`. Expecting list. '
|
|
'Received type: {}').format(type(events).__name__))
|
|
|
|
# normalization complete....
|
|
if events:
|
|
# get event_ids from these events...
|
|
event_ids = extract(events, 'event_id')
|
|
else:
|
|
# we have event_ids, lets fetch events from index
|
|
if isinstance(event_ids, itsi_py3.string_type):
|
|
event_ids = event_ids.split(split_by)
|
|
if not isinstance(event_ids, list):
|
|
return None
|
|
events = self._get_from_index(event_ids, kwargs)
|
|
if not events:
|
|
return None
|
|
|
|
severities = extract(events, 'severity')
|
|
return list(zip(event_ids, severities))
|
|
|
|
def get_status(self, events=None, event_ids=None, split_by=",", **kwargs):
|
|
"""
|
|
given a list of events or event ids, return their statuses
|
|
|
|
@type events: list of dicts
|
|
@param events: each dict in the list represents an event that was sent
|
|
to us by Splunk as an outcome of a Custom Action. the get_event()
|
|
method in class CustomGroupActionBase generates such an item.
|
|
High performant.
|
|
|
|
@type event_ids: basestring/list
|
|
@param event_ids: a unique id for an event or a list of them
|
|
Less performant.
|
|
|
|
@type split_by: str
|
|
@param split_by: if `event_ids` is of type basestring, we will split it
|
|
into many event ids;into a list. What is the separator? Defaults to `,`
|
|
|
|
@type kwargs: dict
|
|
@param kwargs: send in keys `earliest_time` and
|
|
`latest_time` with corresponding values if you know what you are doing.
|
|
Ex:
|
|
'-15m' implies '15 mins ago'
|
|
'-15s' implies '15 seconds ago'
|
|
'-15d' implies '15 days ago'
|
|
'-15w' implies '15 weeks ago'
|
|
'rt' implies real time
|
|
'now' implies current time
|
|
no other values are supported
|
|
|
|
@return a list of tuples (event_id: severity) on valid request
|
|
None on an invalid request
|
|
"""
|
|
# validate + normalize
|
|
if not events and not event_ids:
|
|
raise ValueError('Expecting either `events` or `event_ids`. Both are None.')
|
|
if events and isinstance(events, dict):
|
|
events = [events]
|
|
if events and not isinstance(events, list):
|
|
raise TypeError(('Invalid type for `events`. Expecting list. '
|
|
'Received type: {}').format(type(events).__name__))
|
|
|
|
# normalization complete....
|
|
if events:
|
|
# get event_ids from these events...
|
|
event_ids = extract(events, 'event_id')
|
|
else:
|
|
# we have event_ids, lets fetch events from index
|
|
if isinstance(event_ids, itsi_py3.string_type):
|
|
event_ids = event_ids.split(split_by)
|
|
if not isinstance(event_ids, list):
|
|
return None
|
|
events = self._get_from_index(event_ids, kwargs)
|
|
if not events:
|
|
return None
|
|
|
|
statuses = extract(events, 'status')
|
|
return list(zip(event_ids, statuses))
|
|
|
|
def get_owner(self, events=None, event_ids=None, split_by=",", **kwargs):
|
|
"""
|
|
given an event id, return its owner
|
|
|
|
@type events: list of dicts
|
|
@param events: each dict in the list represents an event that was sent
|
|
to us by Splunk as an outcome of a Custom Action. the get_event()
|
|
method in class CustomGroupActionBase generates such an item.
|
|
High performant.
|
|
|
|
@type event_ids: basestring/list
|
|
@param event_ids: a unique id for an event or a list of them
|
|
Less performant.
|
|
|
|
@type split_by: str
|
|
@param split_by: if `event_ids` is of type basestring, we will split it
|
|
into many event ids; into a list. What is the separator? Defaults to `,`
|
|
|
|
@type kwargs: dict
|
|
@param kwargs: send in keys `earliest_time` and
|
|
`latest_time` with corresponding values if you know what you are doing.
|
|
Ex:
|
|
'-15m' implies '15 mins ago'
|
|
'-15s' implies '15 seconds ago'
|
|
'-15d' implies '15 days ago'
|
|
'-15w' implies '15 weeks ago'
|
|
'rt' implies real time
|
|
'now' implies current time
|
|
no other values are supported
|
|
|
|
@rtype: list of tuples
|
|
@return: [(event_id, owner)] if valid. None if invalid.
|
|
"""
|
|
# validate + normalize
|
|
if not events and not event_ids:
|
|
raise ValueError('Expecting either `events` or `event_ids`. Both are None.')
|
|
if events and isinstance(events, dict):
|
|
events = [events]
|
|
if events and not isinstance(events, list):
|
|
raise TypeError(('Invalid type for `events`. Expecting list. '
|
|
'Received type: {}').format(type(events).__name__))
|
|
|
|
# normalization complete....
|
|
if events:
|
|
# get event_ids from these events...
|
|
event_ids = extract(events, 'event_id')
|
|
else:
|
|
# we have event_ids, lets fetch events from index
|
|
if isinstance(event_ids, itsi_py3.string_type):
|
|
event_ids = event_ids.split(split_by)
|
|
if not isinstance(event_ids, list):
|
|
return None
|
|
events = self._get_from_index(event_ids, kwargs)
|
|
if not events:
|
|
return None
|
|
|
|
# extract owners now that we have events...
|
|
owners = extract(events, 'owner')
|
|
return list(zip(event_ids, owners))
|