def change_of_status(self, alarm, old_status, status, event): """ Archive status change when ``archive_status()`` detected a status change. :param alarm: Associated alarm to status change event :type alarm: dict :param old_status: Previous status :type old_status: int :param status: New status :type status: int :param event: Associated event :type event: dict """ if status > old_status: task = get_task( 'alerts.systemaction.status_increase', cacheonly=True ) elif status < old_status: task = get_task( 'alerts.systemaction.status_decrease', cacheonly=True ) value = alarm.get(self[Alerts.ALARM_STORAGE].VALUE) new_value = task(self, value, status, event) self.update_current_alarm(alarm, new_value)
def test_acknowledge(self): event = { 'timestamp': 0, 'source_type': 'component', 'connector': 'c', 'connector_name': 'cn', 'component': 'cm', } task = get_task('alerts.useraction.ack') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm[AlarmField.ack.value] is not None) self.assertEqual(alarm[AlarmField.ack.value]['t'], 0) self.assertEqual(alarm[AlarmField.ack.value]['a'], 'testauthor') self.assertEqual(alarm[AlarmField.ack.value]['m'], 'test message') self.assertTrue(alarm[AlarmField.ack.value] is get_previous_step(alarm, 'ack')) self.event_publisher.publish_statcounterinc_event.assert_not_called() self.event_publisher.publish_statduration_event.assert_called_once_with( 0, StatDurations.ack_time, 0, {}, alarm, 'testauthor')
def test_acknowledge_twice(self): event = { 'timestamp': 0, 'source_type': 'component', 'connector': 'c', 'connector_name': 'cn', 'component': 'cm', } ack_task = get_task('alerts.useraction.ack') #ackremove_task = get_task('alerts.useraction.ackremove') alarm = ack_task( self.manager, self.alarm, 'testauthor', 'test message', event ) ack_task( self.manager, alarm, 'testauthor', 'test message', event ) self.event_publisher.publish_statcounterinc_event.assert_not_called() self.event_publisher.publish_statduration_event.assert_called_once_with( 0, StatDurations.ack_time, 0, {}, alarm, 'testauthor')
def test_restore(self): event = {'timestamp': 0} task = get_task('alerts.useraction.uncancel') self.alarm[AlarmField.canceled.value] = { '_t': 'cancel', 't': 0, 'a': 'testauthor', 'm': 'test message' } alarm, _ = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm[AlarmField.canceled.value] is None) uncancel = get_previous_step(alarm, 'uncancel') self.assertFalse(uncancel is None) self.assertEqual(uncancel['t'], 0) self.assertEqual(uncancel['a'], 'testauthor') self.assertEqual(uncancel['m'], 'test message')
def test_snooze(self): event = { 'connector': 'test', 'connector_name': 'test0', 'timestamp': 0, 'output': 'test message', 'duration': 3600, } task = get_task('alerts.useraction.snooze') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event, ) self.assertIsNot(alarm[AlarmField.snooze.value], None) self.assertEqual(alarm[AlarmField.snooze.value]['t'], 0) self.assertEqual(alarm[AlarmField.snooze.value]['a'], 'testauthor') self.assertEqual(alarm[AlarmField.snooze.value]['m'], 'test message') self.assertEqual(alarm[AlarmField.snooze.value]['val'], 0 + 3600) self.assertTrue( alarm[AlarmField.snooze.value] is get_previous_step(alarm, 'snooze') )
def test_hard_limit(self): class Manager(object): hard_limit = 100 mgr = Manager() alarm = {'hard_limit': None, 'steps': []} task = get_task('alerts.systemaction.hard_limit') alarm = task(mgr, alarm) self.assertIsNot(alarm['hard_limit'], None) self.assertEqual(len(alarm['steps']), 1) self.assertEqual(alarm['steps'][0], alarm['hard_limit']) self.assertEqual(alarm['hard_limit']['_t'], 'hardlimit') self.assertIs(type(alarm['hard_limit']['t']), int) self.assertEqual(alarm['hard_limit']['a'], '__canopsis__') self.assertEqual( alarm['hard_limit']['m'], ( 'This alarm has reached an hard limit (100 steps recorded) : ' 'no more steps will be appended. Please cancel this alarm or ' 'extend hard limit value.' ) ) self.assertEqual(alarm['hard_limit']['val'], 100)
def test_get_unregisteredtask(self): """ Test to get unregistered task. """ getTaskTest = path(GetTaskTest) task = get_task(getTaskTest) self.assertEqual(task, GetTaskTest)
def change_of_status(self, alarm, old_status, status, event): """ Change status when ``update_status()`` detected a status change. :param dict alarm: Associated alarm to status change event :param int old_status: Previous status :param int status: New status :param dict event: Associated event :return: alarm with changed status :rtype: dict """ if status > old_status: task = get_task( 'alerts.systemaction.status_increase', cacheonly=True ) elif status < old_status: task = get_task( 'alerts.systemaction.status_decrease', cacheonly=True ) value = alarm.get(self.alerts_storage.VALUE) new_value = task(self, value, status, event) new_value[AlarmField.last_update_date.value] = int(time()) alarm[self.alerts_storage.VALUE] = new_value entity_id = alarm[self.alerts_storage.DATA_ID] if status == CANCELED: entity = self.context_manager.get_entities_by_id(entity_id) try: entity = entity[0] except IndexError: entity = {} self.event_publisher.publish_statcounterinc_event( new_value[AlarmField.last_update_date.value], StatCounters.alarms_canceled, entity, new_value, event.get(self.AUTHOR)) return alarm
def test_get_registeredtask(self): """ Test to get registered task. """ _id = 'a' register_tasks(force=True, **{_id: GetTaskTest}) task = get_task(_id=_id) self.assertEqual(task, GetTaskTest)
def crop_flapping_steps(self, alarm): """ Remove old state changes for alarms that are flapping over long periods of time. :param dict alarm: Alarm value :return: Alarm with cropped steps or alarm if nothing to remove :rtype: dict """ p_steps = self.flapping_persistant_steps if p_steps < 0: self.logger.warning( "Peristant steps is {} (< 0) : aborting flapping steps crop " "operation".format(p_steps) ) return alarm last_status_i = alarm[AlarmField.steps.value].index( alarm[AlarmField.status.value]) state_changes = filter( lambda step: step['_t'] in ['stateinc', 'statedec'], alarm[AlarmField.steps.value][last_status_i + 1:] ) number_to_crop = len(state_changes) - p_steps if not number_to_crop > 0: return alarm crop_counter = {} # Removed steps are supposed unique due to their timestamps, so as # `remove` method does not cause any collisions. for i in range(number_to_crop): # Increase statedec or stateinc counter t = state_changes[i]['_t'] crop_counter[t] = crop_counter.get(t, 0) + 1 # Increase {0,1,2,3} counter s = 'state:{}'.format(state_changes[i]['val']) crop_counter[s] = crop_counter.get(s, 0) + 1 alarm[AlarmField.steps.value].remove(state_changes[i]) task = get_task('alerts.crop.update_state_counter') alarm = task(alarm, crop_counter) return alarm
def change_of_status(self, alarm, old_status, status, event): """ Change status when ``update_status()`` detected a status change. :param alarm: Associated alarm to status change event :type alarm: dict :param old_status: Previous status :type old_status: int :param status: New status :type status: int :param event: Associated event :type event: dict :return: alarm with changed status :rtype: dict """ if status > old_status: task = get_task( 'alerts.systemaction.status_increase', cacheonly=True ) elif status < old_status: task = get_task( 'alerts.systemaction.status_decrease', cacheonly=True ) value = alarm.get(self[Alerts.ALARM_STORAGE].VALUE) new_value = task(self, value, status, event) alarm[self[Alerts.ALARM_STORAGE].VALUE] = new_value return alarm
def test_comment(self): event = {'timestamp': 0} task = get_task('alerts.useraction.comment') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertFalse(alarm[AlarmField.comment.value] is None) self.assertEqual(alarm[AlarmField.comment.value]['t'], 0) self.assertEqual(alarm[AlarmField.comment.value]['a'], 'testauthor') self.assertEqual(alarm[AlarmField.comment.value]['m'], 'test message')
def test_register_force(self): """ Test to register existing tasks with force. """ self.assertNotIn('d', TASKS_BY_ID) new_tasks = {'a': 'a', 'c': 'c', 'd': 'd'} old_tasks = register_tasks( force=True, **new_tasks ) for new_task in new_tasks: self.assertEqual(get_task(new_task), new_tasks[new_task]) self.assertNotIn('b', old_tasks) self_tasks_wo_b = self.tasks del self_tasks_wo_b['b'] self.assertEqual(old_tasks, self_tasks_wo_b)
def test_acknowledge(self): event = {'timestamp': 0} task = get_task('alerts.useraction.ack') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm['ack'] is not None) self.assertEqual(alarm['ack']['t'], 0) self.assertEqual(alarm['ack']['a'], 'testauthor') self.assertEqual(alarm['ack']['m'], 'test message') self.assertTrue(alarm['ack'] is get_previous_step(alarm, 'ack'))
def archive(self, event): """ Archive event in corresponding alarm history. :param event: Event to archive :type event: dict """ entity = self[Alerts.CONTEXT_MANAGER].get_entity(event) entity_id = self[Alerts.CONTEXT_MANAGER].get_entity_id(entity) author = event.get('author', None) message = event.get('output', None) if event['event_type'] == Check.EVENT_TYPE: if event[Check.STATE] != Check.OK: self.make_alarm(entity_id, event) alarm = self.get_current_alarm(entity_id) if alarm is not None: self.archive_state(alarm, event[Check.STATE], event) else: try: task = get_task('alerts.useraction.{0}'.format( event['event_type'] ), cacheonly=True) except ImportError: task = None if task is not None: alarm = self.get_current_alarm(entity_id) value = alarm.get(self[Alerts.ALARM_STORAGE].VALUE) new_value = task(self, value, author, message, event) status = None if isinstance(new_value, tuple): new_value, status = new_value self.update_current_alarm(alarm, new_value) if status is not None: self.archive_status(alarm, status, event)
def test_cancel(self): event = {'timestamp': 0} task = get_task('alerts.useraction.cancel') alarm, statusval = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertEqual(statusval, CANCELED) self.assertTrue(alarm[AlarmField.canceled.value] is not None) self.assertEqual(alarm[AlarmField.canceled.value]['t'], 0) self.assertEqual(alarm[AlarmField.canceled.value]['a'], 'testauthor') self.assertEqual(alarm[AlarmField.canceled.value]['m'], 'test message') self.assertTrue( alarm[AlarmField.canceled.value] is get_previous_step(alarm, 'cancel') )
def test_declare_ticket(self): event = {'timestamp': 0} task = get_task('alerts.useraction.declareticket') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm[AlarmField.ticket.value] is not None) self.assertEqual(alarm[AlarmField.ticket.value]['t'], 0) self.assertEqual(alarm[AlarmField.ticket.value]['a'], 'testauthor') self.assertEqual(alarm[AlarmField.ticket.value]['m'], None) self.assertEqual(alarm[AlarmField.ticket.value]['val'], None) self.assertTrue( alarm[AlarmField.ticket.value] is get_previous_step(alarm, 'declareticket') )
def test_status_increase(self): event = { 'connector': 'test', 'connector_name': 'test0', 'timestamp': 0, 'output': 'test message' } statusval = 2 task = get_task('alerts.systemaction.status_increase') alarm = task(self.manager, self.alarm, statusval, event) self.assertTrue(alarm['status'] is not None) self.assertEqual(alarm['status']['t'], 0) self.assertEqual(alarm['status']['a'], 'test.test0') self.assertEqual(alarm['status']['m'], 'test message') self.assertEqual(alarm['status']['val'], statusval) self.assertTrue( alarm['status'] is get_previous_step(alarm, 'statusinc') )
def test_state_increase(self): event = { 'connector': 'test', 'connector_name': 'test0', 'timestamp': 0, 'output': 'test message' } state = 2 task = get_task('alerts.systemaction.state_increase') alarm, _ = task(self.manager, self.alarm, state, event) self.assertTrue(alarm[AlarmField.state.value] is not None) self.assertEqual(alarm[AlarmField.state.value]['t'], 0) self.assertEqual(alarm[AlarmField.state.value]['a'], 'test.test0') self.assertEqual(alarm[AlarmField.state.value]['m'], 'test message') self.assertEqual(alarm[AlarmField.state.value]['val'], state) self.assertTrue( alarm[AlarmField.state.value] is get_previous_step(alarm, 'stateinc') )
def test_status_decrease(self): event = { 'connector': 'test', 'connector_name': 'test0', 'timestamp': 0, 'output': 'test message' } statusval = 0 task = get_task('alerts.systemaction.status_decrease') alarm = task(self.manager, self.alarm, statusval, event) self.assertTrue(alarm[AlarmField.status.value] is not None) self.assertEqual(alarm[AlarmField.status.value]['t'], 0) self.assertEqual(alarm[AlarmField.status.value]['a'], DEFAULT_AUTHOR) self.assertEqual(alarm[AlarmField.status.value]['m'], 'test message') self.assertEqual(alarm[AlarmField.status.value]['val'], statusval) self.assertTrue( alarm[AlarmField.status.value] is get_previous_step(alarm, 'statusdec') )
def test_unacknowledge(self): event = {'timestamp': 0} task = get_task('alerts.useraction.ackremove') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm[AlarmField.ack.value] is None) unack = get_previous_step(alarm, 'ackremove') self.assertEqual(unack['t'], 0) self.assertEqual(unack['a'], 'testauthor') self.assertEqual(unack['m'], 'test message') self.event_publisher.publish_statcounterinc_event.assert_not_called() self.event_publisher.publish_statduration_event.assert_not_called()
def check_hard_limit(self, alarm): """ Update hard limit informations if number of steps has exceeded this limit. :param dict alarm: Alarm value :return: Alarm with hard limit informations or alarm if nothing to do :rtype: dict """ limit = alarm.get(AlarmField.hard_limit.value, None) if limit is not None and limit['val'] >= self.hard_limit: return alarm if len(alarm[AlarmField.steps.value]) >= self.hard_limit: task = get_task('alerts.check.hard_limit') return task(self, alarm) return alarm
def event_processing(self, value): """ Change of event_processing. :param value: new event_processing to use. If None or wrong value, event_processing is used :type value: NoneType, str or function """ # by default, load default event processing if value is None: value = event_processing # if str, load the related function elif isinstance(value, basestring): try: value = get_task(value) except ImportError: self.logger.error(u'Impossible to load %s' % value) value = event_processing # set _event_processing and work self._event_processing = value
def check_hard_limit(self, alarm): """ Update hard limit informations if number of steps has exceeded this limit. :param dict alarm: Alarm value :return: Alarm with hard limit informations or alarm if nothing to do :rtype: dict """ limit = alarm.get('hard_limit', None) if limit is not None: if limit['val'] >= self.hard_limit: return alarm if len(alarm['steps']) >= self.hard_limit: task = get_task('alerts.systemaction.hard_limit') return task(self, alarm) else: return alarm
def test_assoc_ticket(self): event = { 'timestamp': 0, 'ticket': 1234 } task = get_task('alerts.useraction.assocticket') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm['ticket'] is not None) self.assertEqual(alarm['ticket']['t'], 0) self.assertEqual(alarm['ticket']['a'], 'testauthor') self.assertEqual(alarm['ticket']['m'], 'test message') self.assertEqual(alarm['ticket']['val'], 1234) self.assertTrue( alarm['ticket'] is get_previous_step(alarm, 'assocticket') )
def test_change_state(self): event = { 'timestamp': 0, 'state': 2 } task = get_task('alerts.useraction.changestate') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm['state'] is not None) self.assertEqual(alarm['state']['t'], 0) self.assertEqual(alarm['state']['a'], 'testauthor') self.assertEqual(alarm['state']['m'], 'test message') self.assertEqual(alarm['state']['val'], 2) self.assertTrue( alarm['state'] is get_previous_step(alarm, 'changestate') )
def _lookup(self, alarms, lookups): """ Add extra keys to a list of alarms. :param list alarms: List of alarms as dict :param list lookups: List of extra keys to add. :return: Alarms with extra keys :rtype: list """ for lookup in lookups: task = get_task( 'alerts.lookup.{}'.format(lookup), cacheonly=True ) if task is None: raise ValueError('Unknown lookup "{}"'.format(lookup)) for alarm in alarms: alarm = task(self, alarm) return alarms
def test_change_state(self): event = { 'timestamp': 0, 'state': 2 } task = get_task('alerts.useraction.changestate') alarm = task( self.manager, self.alarm, 'testauthor', 'test message', event ) self.assertTrue(alarm[AlarmField.state.value] is not None) self.assertEqual(alarm[AlarmField.state.value]['t'], 0) self.assertEqual(alarm[AlarmField.state.value]['a'], 'testauthor') self.assertEqual(alarm[AlarmField.state.value]['m'], 'test message') self.assertEqual(alarm[AlarmField.state.value]['val'], 2) self.assertTrue( alarm[AlarmField.state.value] is get_previous_step(alarm, States.changestate.value) ) self.assertTrue(is_keeped_state(alarm))
def change_of_state(self, alarm, old_state, state, event): """ Change state when ``update_state()`` detected a state change. :param dict alarm: Associated alarm to state change event :param int old_state: Previous state :param int state: New state :param dict event: Associated event :return: alarm with changed state :rtype: dict """ storage_value = self.alerts_storage.VALUE # Check for a forced state on this alarm if is_keeped_state(alarm['value']): if state == Check.OK: # Disengaging 'keepstate' flag alarm[storage_value][AlarmField.state.value]['_t'] = None else: self.logger.info('Entity {} not allowed to change state: ' 'ignoring'.format(alarm['data_id'])) return alarm # Escalation if state > old_state: task = get_task('alerts.systemaction.state_increase', cacheonly=True) elif state < old_state: task = get_task('alerts.systemaction.state_decrease', cacheonly=True) # Executing task now = int(time()) value = alarm.get(self.alerts_storage.VALUE) new_value, status = task(self, value, state, event) new_value[AlarmField.last_update_date.value] = now entity_id = alarm[self.alerts_storage.DATA_ID] try: entity = self.context_manager.get_entities_by_id(entity_id)[0] except IndexError: entity = {} # Send statistics event last_state_change = entity.get(Entity.LAST_STATE_CHANGE) if last_state_change: self.event_publisher.publish_statstateinterval_event( now, StatStateIntervals.time_in_state, now - last_state_change, old_state, entity, new_value) if state == AlarmState.CRITICAL: self.event_publisher.publish_statcounterinc_event( now, StatCounters.downtimes, entity, new_value, event.get(self.AUTHOR)) # Update entity's last_state_change if entity: entity[Entity.LAST_STATE_CHANGE] = now self.context_manager.update_entity_body(entity) alarm[storage_value] = new_value return self.update_status(alarm, status, event)
def consolidation( self, serieconf, perfdatas, timewindow, period=None, usenan=True, fixed=True ): """ Get consolidated point from serie. :param serieconf: Serie used for consolidation :type serieconf: dict :param perfdatas: Aggregated perfdatas from ``aggregation()`` method :type perfdatas: dict :param timewindow: Time window used for consolidation :type timewindow: canopsis.timeserie.timewindow.TimeWindow :returns: Consolidated points """ # configure consolidation period (same as aggregation period) tw, period, usenan, fixed = self.get_timewindow_period_usenan_fixed( serieconf, timewindow, period, usenan, fixed ) intervals = Interval.get_intervals_by_period( tw.start(), tw.stop(), period ) points = [] # generator consolidation operators operatorset = get_task('serie.operatorset') # generate one point per aggregation interval in timewindow for interval in intervals: tw = TimeWindow( start=interval['begin'], stop=interval['end'] - 1 ) # operators are acting on a specific timewindow operators = operatorset(self, period, perfdatas, tw, usenan) # execute formula in sand-boxed environment restricted_globals = { '__builtins__': safe_builtins, } restricted_globals.update(operators) formula = serieconf['formula'] code = compile_restricted(formula, '<string>', 'eval') try: val = eval(code, restricted_globals) except Exception as ex: self.logger.warning( 'Wrong serie formula: {0}/{1} ({2})'.format( serieconf['crecord_name'], formula, ex ) ) val = float('nan') else: if isnan(val): self.logger.warning( 'Formula result is nan: {0}/{1}.'.format( serieconf['crecord_name'], formula ) ) # result contains consolidated value # point is computed at the start of interval points.append((interval['begin'], val)) return points
def archive(self, event): """ Archive event in corresponding alarm history. :param event: Event to archive :type event: dict """ entity = self[Alerts.CONTEXT_MANAGER].get_entity(event) entity_id = self[Alerts.CONTEXT_MANAGER].get_entity_id(entity) author = event.get('author', None) message = event.get('output', None) if event['event_type'] == Check.EVENT_TYPE: alarm = self.get_current_alarm(entity_id) if alarm is None: if event[Check.STATE] == Check.OK: # If a check event with an OK state concerns an entity for # which no alarm is opened, there is no point continuing return # Check is not OK alarm = self.make_alarm(entity_id, event) alarm = self.update_state(alarm, event[Check.STATE], event) else: # Alarm is already opened value = alarm.get(self[Alerts.ALARM_STORAGE].VALUE) if self.is_hard_limit_reached(value): return alarm = self.update_state(alarm, event[Check.STATE], event) value = alarm.get(self[Alerts.ALARM_STORAGE].VALUE) value = self.crop_flapping_steps(value) value = self.check_hard_limit(value) self.update_current_alarm(alarm, value) else: try: task = get_task('alerts.useraction.{0}'.format( event['event_type'] ), cacheonly=True) except ImportError: task = None if task is not None: alarm = self.get_current_alarm(entity_id) if alarm is None: self.logger.warning( 'Entity {} has no current alarm : ignoring'.format( entity_id ) ) return value = alarm.get(self[Alerts.ALARM_STORAGE].VALUE) if self.is_hard_limit_reached(value): # Only cancel is allowed when hard limit has been reached if event['event_type'] != 'cancel': return new_value = task(self, value, author, message, event) status = None if isinstance(new_value, tuple): new_value, status = new_value new_value = self.check_hard_limit(new_value) self.update_current_alarm(alarm, new_value) if status is not None: alarm = self.update_status(alarm, status, event) new_value = alarm[self[Alerts.ALARM_STORAGE].VALUE] self.update_current_alarm(alarm, new_value)
def test_update_state_counter(self): cases = [ { 'alarm': { 'status': { 'a': 'ut', 't': 0 }, 'steps': [{'a': 'ut', 't': 0}] }, 'diff_counter': {}, 'expected_steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {} } ] }, { 'alarm': { 'status': { 'a': 'ut', 't': 0 }, 'steps': [{'a': 'ut', 't': 0}] }, 'diff_counter': {'item': 0}, 'expected_steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {'item': 0} } ] }, { 'alarm': { 'status': { 'a': 'ut', 't': 0 }, 'steps': [{'a': 'ut', 't': 0}, {'_t': 'customstep'}] }, 'diff_counter': {'item1': 10, 'item2': 15}, 'expected_steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {'item1': 10, 'item2': 15} }, { '_t': 'customstep' } ] }, { 'alarm': { 'status': { 'a': 'ut', 't': 0 }, 'steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {'item1': 3} }, { '_t': 'customstep' } ] }, 'diff_counter': {'item1': 2, 'item2': 4}, 'expected_steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {'item1': 5, 'item2': 4} }, { '_t': 'customstep' } ] }, { 'alarm': { 'status': { 'a': 'ut', 't': 10 }, 'steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {'item1': 3} }, { '_t': 'customstep' }, { 'a': 'ut', 't': 10 } ] }, 'diff_counter': {'item1': 2, 'item2': 4}, 'expected_steps': [ { 'a': 'ut', 't': 0 }, { '_t': 'statecounter', 'a': 'ut', 't': 0, 'm': '', 'val': {'item1': 3} }, { '_t': 'customstep' }, { 'a': 'ut', 't': 10 }, { '_t': 'statecounter', 'a': 'ut', 't': 10, 'm': '', 'val': {'item1': 2, 'item2': 4} } ] } ] task = get_task('alerts.systemaction.update_state_counter') for case in cases: alarm = task(case['alarm'], case['diff_counter']) self.assertEqual(alarm['steps'], case['expected_steps'])
def execute_task(self, name, event, entity_id, author=None, new_state=None, diff_counter=None): """ Find and execute a task. :param str name: Name of the task to execute :param dict event: Event to archive :param str entity_id: Id of the alarm :param str author: If needed, the author of the event :param int new_state: If needed, the new state in the event :param int diff_counter: For crop events, the new value of the counter """ # Find the corresponding task try: task = get_task(name, cacheonly=True) # FIXIT: https://git.canopsis.net/canopsis/canopsis/issues/298 if not callable(task): raise ImportError('cannot import task "{}"'.format(name)) except ImportError: self.logger.debug('Unknown task {}'.format(name)) return # Find the corresponding alarm alarm = self.get_current_alarm(entity_id) if alarm is None: self.logger.debug( 'Entity {} has no current alarm : ignoring'.format(entity_id)) return value = alarm.get(self.alerts_storage.VALUE) if self.is_hard_limit_reached(value): # Only cancel and ack are allowed when hard limit has been reached if event['event_type'] != 'cancel' and event['event_type'] != 'ack': self.logger.debug('Hard limit reached. Cancelling') return # Execute the desired task if '.systemaction' in name: new_value = task(self, value, new_state, event) elif '.useraction' in name: message = event.get('output', None) new_value = task(self, value, author, message, event) elif '.lookup' in name or '.check' in name: new_value = task(self, value) elif '.crop' in name: new_value = task(self, value, diff_counter) else: self.logger.warning('Unknown task type for {}'.format(name)) return # Some tasks return two values (a value and a status) status = None if isinstance(new_value, tuple): new_value, status = new_value if event['event_type'] != 'cancel' and event['event_type'] != 'ack': new_value = self.check_hard_limit(new_value) self.update_current_alarm(alarm, new_value) # If needed, update status if status is not None: alarm = self.update_status(alarm, status, event) new_value = alarm[self.alerts_storage.VALUE] self.update_current_alarm(alarm, new_value) return new_value
def change_of_state(self, alarm, old_state, state, event): """ Change state when ``update_state()`` detected a state change. :param dict alarm: Associated alarm to state change event :param int old_state: Previous state :param int state: New state :param dict event: Associated event :return: alarm with changed state :rtype: dict """ storage_value = self.alerts_storage.VALUE # Check for a forced state on this alarm if is_keeped_state(alarm['value']): if state == Check.OK: # Disengaging 'keepstate' flag alarm[storage_value][AlarmField.state.value]['_t'] = None else: self.logger.info('Entity {} not allowed to change state: ' 'ignoring'.format(alarm['data_id'])) return alarm # Escalation if state > old_state: task = get_task( 'alerts.systemaction.state_increase', cacheonly=True ) elif state < old_state: task = get_task( 'alerts.systemaction.state_decrease', cacheonly=True ) # Executing task now = int(time()) value = alarm.get(self.alerts_storage.VALUE) new_value, status = task(self, value, state, event) new_value[AlarmField.last_update_date.value] = now entity_id = alarm[self.alerts_storage.DATA_ID] try: entity = self.context_manager.get_entities_by_id(entity_id)[0] except IndexError: entity = {} # Send statistics event last_state_change = entity.get(Entity.LAST_STATE_CHANGE) if last_state_change: self.event_publisher.publish_statstateinterval_event( now, StatStateIntervals.time_in_state, now - last_state_change, old_state, entity, new_value) if state == AlarmState.CRITICAL: self.event_publisher.publish_statcounterinc_event( now, StatCounters.downtimes, entity, new_value, event.get(self.AUTHOR)) # Update entity's last_state_change if entity: entity[Entity.LAST_STATE_CHANGE] = now self.context_manager.update_entity_body(entity) alarm[storage_value] = new_value return self.update_status(alarm, status, event)