# Copyright (C) 2005-2024 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 json import time from copy import deepcopy from itsi_py3 import _ 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 posibble 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))