Ejemplo n.º 1
0
    def test_add_to_agregate(self):
        """Ajout d'un événement brut à un évènement corrélé déjà existant"""
        # On crée 2 couples host/service.
        host1 = functions.add_host(u'messagerie')
        service1 = functions.add_lowlevelservice(host1, u'Processes')
        service2 = functions.add_lowlevelservice(host1, u'CPU')

        # On ajoute 1 couple événement/agrégat à la BDD.
        event2 = functions.add_event(service2, u'WARNING', 'WARNING: CPU is overloaded')
        events_aggregate1 = functions.add_correvent([event2])

        # On ajoute un nouvel événement à la BDD.
        event1 = functions.add_event(service1, u'WARNING', 'WARNING: Processes are not responding')

        # On ajoute ce nouvel événement à l'agrégat existant.
        ctx = helpers.ContextStub(42)
        yield add_to_aggregate(
            event1.idevent,
            events_aggregate1.idcorrevent,
            DummyDatabaseWrapper(True),
            ctx,
            123,
            False
        )
        DBSession.flush()

        # On vérifie que l'événement a bien été ajouté à l'agrégat.
        DBSession.refresh(events_aggregate1)
        expected = sorted([event1.idevent, event2.idevent])
        actual = sorted([event.idevent for event in events_aggregate1.events])
        print "actual = %r, expected = %r" % (actual, expected)
        self.assertEquals(actual, expected)
Ejemplo n.º 2
0
    def _disaggregate(self, ctx, correvent, update_id, timestamp):
        """
        Procède à la désagrégation de l'événement corrélé courant.

        @param ctx: Contexte de corrélation.
        @type ctx: L{Context}
        @param correvent: Événement corrélé en cours de modification.
        @type correvent: L{CorrEvent}
        @param update_id: Identifiant de l'événement corrélé courant.
        @type update_id: C{int}
        @param timestamp: Horodatage de l'événement.
        @type timestamp: C{datetime.DateTime}
        """
        cause = aliased(Event)
        others = aliased(Event)
        # On détermine les causes des nouveaux événements corrélés
        # (ceux obtenus par désagrégation de l'événement courant).
        new_causes = yield self.database.run(
            DBSession.query(
                EventsAggregate.idevent,
                DependencyGroup.iddependent,
            ).join(
                (CorrEvent,
                    CorrEvent.idcorrevent == EventsAggregate.idcorrevent),
                (others, others.idevent == EventsAggregate.idevent),
                (DependencyGroup,
                    DependencyGroup.iddependent == others.idsupitem),
                (Dependency, Dependency.idgroup == DependencyGroup.idgroup),
                (cause, cause.idevent == CorrEvent.idcause),
            ).filter(EventsAggregate.idevent != CorrEvent.idcause
            ).filter(Dependency.idsupitem == cause.idsupitem
            ).filter(Dependency.distance == 1
            ).filter(DependencyGroup.role == u'topology'
            ).filter(CorrEvent.idcorrevent == update_id).all,
            transaction=False
        )

        # Pour chacune des nouvelles causes, on crée
        # le nouvel événement corrélé.
        for new_cause in new_causes:
            LOGGER.debug(_('Creating new aggregate with cause #%(cause)d '
                           '(#%(supitem)d) from aggregate #%(original)d'),
                           {
                                'original': update_id,
                                'cause': new_cause.idevent,
                                'supitem': new_cause.iddependent,
                            })
            # Inutile d'appeler remove_from_all_aggregates() ici
            # car on désagrège déjà manuellement l'agrégat initial.
            new_correvent = CorrEvent(
                idcause=new_cause.idevent,
                priority=settings['correlator'].as_int(
                            'unknown_priority_value'),
                # On ne recopie pas le ticket d'incident
                # et on place l'événement corrélé dans
                # l'état d'acquittement initial.
                ack=CorrEvent.ACK_NONE,
                occurrence=1,
                timestamp_active=timestamp, # @XXX: ou datetime.now() ?
            )
            yield self.database.run(
                DBSession.add, new_correvent,
                transaction=False,
            )

            # Retrait de l'événement brut cause des autres agrégats.
            yield self.database.run(
                DBSession.query(
                    EventsAggregate
                ).filter(EventsAggregate.idevent == new_cause.idevent,
                ).filter(EventsAggregate.idcorrevent != update_id
                ).delete,
                transaction=False,
            )

            yield self.database.run(
                DBSession.flush,
                transaction=False,
            )

            # On ajoute à cet agrégat les événements bruts
            # qui s'y rapportent (cf. topologie réseau).
            raw_events = yield self.database.run(
                DBSession.query(
                    Event.idevent,
                    Event.idsupitem,
                ).join(
                    (DependencyGroup,
                        DependencyGroup.iddependent == Event.idsupitem),
                    (Dependency, Dependency.idgroup == DependencyGroup.idgroup),
                    (EventsAggregate, EventsAggregate.idevent == Event.idevent),
                ).filter(Dependency.idsupitem == new_cause.iddependent
                ).filter(DependencyGroup.role == u'topology'
                ).filter(EventsAggregate.idcorrevent == update_id).all,
                transaction=False,
            )
            for raw_event in raw_events:
                yield add_to_aggregate(
                    raw_event.idevent,
                    new_correvent.idcorrevent,
                    self.database,
                    ctx,
                    raw_event.idsupitem,
                    merging=False
                )

            # Association de l'événement brut cause dans le nouvel agrégat.
            yield add_to_aggregate(
                new_cause.idevent,
                new_correvent.idcorrevent,
                self.database,
                ctx,
                new_cause.iddependent,
                merging=False
            )
            # @XXX: redemander l'état de l'équipement à Nagios ?

        # Prise en compte des modifications précédentes.
        yield self.database.run(DBSession.flush, transaction=False)

        # Suppression de l'association entre l'ancien agrégat
        # et les événements bruts qu'il contenait (sauf pour sa cause).
        yield self.database.run(
            DBSession.query(EventsAggregate
                ).filter(EventsAggregate.idcorrevent == update_id
                ).filter(EventsAggregate.idevent != correvent.idcause
                ).delete,
            transaction=False,
        )
        yield self.database.run(DBSession.flush, transaction=False)
Ejemplo n.º 3
0
    def make_correvent(self, info_dictionary):
        """
        Récupère dans le contexte les informations transmises par
        les règles, crée les événements corrélés (agrégats d'événements)
        nécessaires dans la base de données et les transmet au bus.

        Permet de satisfaire les exigences suivantes :
            - VIGILO_EXIG_VIGILO_COR_0040,

        @param info_dictionary: Dictionnaire contenant les informations
            concernant l'événement en cours de trairement.
        @type info_dictionary: C{dict}
        @return: Instance de l'événement corrélé créé ou mis à jour
            ou C{None} si le traitement n'a pas engendré la création
            ni la mise à jour d'un événement corrélé.
        @rtype: L{CorrEvent}
        """
        ctx = self.context_factory(info_dictionary["id"], transaction=False)
        raw_event_id = yield ctx.get('raw_event_id')

        # Il peut y avoir plusieurs raisons à l'absence d'un ID brut :
        # - l'alerte brute portait sur un HLS; dans ce cas il ne s'agit pas
        #   vraiment d'une erreur (on n'enregistre pas d'événement corrélé).
        # - l'alerte brute portait sur un élément non configuré; dans ce cas il
        #   s'agit d'une véritable erreur, mais le log est déjà enregistré dans
        #   db_insertion.py. Inutile de répéter les logs ici.
        # - l'alerte indique un état UP/OK et aucun événement déjà ouvert n'a
        #   pu être trouvé. Inutile d'alerter l'opérateur car tout va bien.
        #   Un log est enregistré dans db_insertion.py et on peut ignorer le
        #   problème ici.
        if raw_event_id is None:
            defer.returnValue(None)

        state = info_dictionary['state']
        timestamp = info_dictionary['timestamp']

        # Si une règle ou un callback demande explicitement qu'aucune
        # alerte ne soit générée pour cet événement, on lui obéit ici.
        stop = yield ctx.get('no_alert')
        if stop:
            hostname = info_dictionary['host']
            servicename = info_dictionary['service']
            LOGGER.info(_(
                'Ignoring event #%(idevent)d on (%(host)r, %(service)r) '
                'as requested by the correlation rules') % {
                    'idevent': raw_event_id,
                    'host': hostname,
                    'service': servicename,
            })
            defer.returnValue(None)

        item_id = yield ctx.get('idsupitem')

        # Identifiant de l'événement corrélé à mettre à jour.
        update_id = yield self._get_update_id(item_id)

        correvent = None
        if update_id is not None:
            # On récupère l'événement corrélé existant pour mise à jour.
            correvent = yield self._get_updated_correvent(update_id, timestamp)

        # Il s'agit d'une création ou bien l'événement corrélé
        # indiqué n'existe pas.
        if correvent is None:
            update_id = None

            # Si l'état de l'alerte brute est 'OK' ou 'UP', on ne fait rien.
            if state in ("OK", "UP"):
                LOGGER.info(_('Raw event ignored. Reason: status = %r'), state)
                defer.returnValue(None)

        aggregated = yield self._aggregate_topologically(ctx, correvent, raw_event_id,
                                                             item_id)
        if aggregated:
           LOGGER.debug("Event #%d masked due to topological aggregation",
                        raw_event_id)
           defer.returnValue(None)

        if correvent is None:
            # Lorsqu'un nouvel agrégat doit être créé, il se peut que la cause
            # ait anciennement fait partie d'autres agrégats désormais OK/UP.
            # On doit supprimer ces associations avant de continuer (cf. #1027).
            yield remove_from_all_aggregates(raw_event_id, self.database)

            # Création du nouvel agrégat à partir de son événement cause.
            LOGGER.debug(_('Creating a new correlated event'))
            correvent = CorrEvent()
            correvent.idcause = raw_event_id

        # On remplit l'événement corrélé et le dictionnaire d'infos
        # à partir du contexte de corrélation.
        yield self._fill_with_context(ctx, info_dictionary,
                                      correvent, timestamp)

        if correvent.ack == CorrEvent.ACK_CLOSED:
            yield self._handle_closed_correvent(ctx, correvent, state,
                                                timestamp, item_id)

        # On sauvegarde l'événement corrélé dans la base de données.
        yield self.database.run(DBSession.add, correvent, transaction=False)
        yield self.database.run(DBSession.flush, transaction=False)

        # Récupération de l'identifiant de l'agrégat pour plus tard.
        # Doit être fait via db_thread. En pratique, cela signifie qu'on
        # doit faire un merge() pour fetcher à nouveau tous les attributs
        # avant de pouvoir y accéder depuis le thread principal.
        correvent = yield self.database.run(
            DBSession.merge,
            correvent,
            transaction=False)
        idcorrevent = correvent.idcorrevent

        # Ajout de l'alerte brute dans l'agrégat.
        yield add_to_aggregate(
            raw_event_id,
            idcorrevent,
            self.database,
            ctx,
            item_id,
            merging=False
        )

        if update_id is None:
            info_dictionary['update'] = False
            info_dictionary['idcorrevent'] = idcorrevent
        else:
            info_dictionary['update'] = True
            info_dictionary['idcorrevent'] = update_id

            if state in ('OK', 'UP'):
                # La cause de l'événement corrélé n'est plus en panne,
                # on tente de désagréger les événements bruts associés.
                yield self._disaggregate(ctx, correvent, update_id, timestamp)

        # On envoie le message correvent correspondant sur le bus
        # et on enregistre une trace dans les logs.
        yield self.publisher.sendMessage(info_dictionary)
        self._log_correvent(info_dictionary)

        yield self._aggregate_successors(ctx, idcorrevent)
        yield self.database.run(DBSession.flush, transaction=False)
        defer.returnValue(correvent)
Ejemplo n.º 4
0
    def _aggregate_topologically(self, ctx, correvent, raw_event_id, item_id):
        """
        Procède à une première passe d'agrégation topologique,
        basée sur les agrégats prédécesseurs de l'alerte courante.

        @param ctx: Contexte de corrélation.
        @type ctx: L{Context}
        @param raw_event_id: Identifiant de l'événement brut.
        @type raw_event_id: C{int}
        @param item_id: Identifiant de l'objet supervisé concerné.
        @type item_id: C{int}
        @return: Deferred indiquant si l'événement corrélé a été agrégé
            dans un autre (C{True}) ou non (C{False}).
        @rtype: L{bool}
        """
        # Ajoute l'alerte aux agrégats prédécesseurs dont elle dépend.
        predecessing_aggregates_id = yield ctx.get('predecessors_aggregates')
        if not predecessing_aggregates_id:
            defer.returnValue(False)

        succeeding_aggregates_id = yield ctx.get('successors_aggregates')
        dependent_event_list = set()
        is_built_dependent_event_list = False
        predecessors_count = 0

        # Pour chaque agrégat dont l'alerte dépend,
        for predecessing_aggregate_id in predecessing_aggregates_id:
            predecessing_aggregate_id = int(predecessing_aggregate_id)
            try:
                yield self.database.run(
                    DBSession.query(CorrEvent).filter(
                        CorrEvent.idcorrevent == predecessing_aggregate_id
                    ).one,
                    transaction=False,
                )
                predecessors_count += 1
            except NoResultFound:
                LOGGER.error(_('Got a reference to a nonexistent '
                        'correlated event (%r), skipping this aggregate'),
                        int(predecessing_aggregate_id))

            else:
                # D'abord on rattache l'alerte
                # courante à cet agrégat dans la BDD.
                yield add_to_aggregate(
                    raw_event_id,
                    predecessing_aggregate_id,
                    self.database,
                    ctx,
                    item_id,
                    merging=True
                )

                # Ensuite on fusionne les éventuels agrégats
                # dépendant de l'alerte courante avec cet agrégat.
                if succeeding_aggregates_id:
                    for succeeding_aggregate_id in succeeding_aggregates_id:
                        events = yield merge_aggregates(
                            int(succeeding_aggregate_id),
                            predecessing_aggregate_id,
                            self.database,
                            ctx
                        )
                        if not is_built_dependent_event_list:
                            dependent_event_list.update(events)
                            is_built_dependent_event_list = True

        # On rattache l'alerte courante aux agrégats sur le bus.
        yield self.publisher.publish_aggregate(
                          predecessing_aggregates_id, [raw_event_id])

        if succeeding_aggregates_id:
            # On publie également sur le bus la
            # liste des alertes brutes (dépendantes de
            # l'alerte courante) à rattacher à ces agrégats.
            yield self.publisher.publish_aggregate(
                predecessing_aggregates_id,
                list(dependent_event_list))
            # Enfin on supprime du bus les agrégats
            # qui dépendaient de l'alerte courante.
            yield self.publisher.delete_published_aggregates(succeeding_aggregates_id)

        if correvent:
            # On supprime l'agrégat courant (fusionné dans ses prédécesseurs).
            LOGGER.debug('Deleting obsolete aggregate #%d', correvent.idcorrevent)
            yield self.database.run(
                DBSession.query(CorrEvent).filter(
                    CorrEvent.idcorrevent == correvent.idcorrevent).delete,
                transaction=False
            )
            yield self.publisher.delete_published_aggregates(correvent.idcorrevent)

        yield self.database.run(DBSession.flush, transaction=False)
        defer.returnValue(predecessors_count != 0)