diff --git a/docs/en/docs/dev/reference/streams/dispose.md b/docs/en/docs/dev/reference/streams/dispose.md index efc646d6fb0b3480dc80b44a2b82ed63f26d9d58..dad36f32e559c03e53a953866ebfe504007fc4d4 100644 --- a/docs/en/docs/dev/reference/streams/dispose.md +++ b/docs/en/docs/dev/reference/streams/dispose.md @@ -40,16 +40,20 @@ in the `$op` field. Unknown message types and malformed messages are discarded. ### raise message `raise` message represents a direct alarm rising request, issued by an external mechanism. -| Field | Type | Description | -| ---------------- | -------------------- | --------------------------------------------------------------------------------------- | -| `$op` | String | Equals to `raise` | -| `reference` | String | Alarm reference. See [alarm reference format](../alarm-reference-format.md) for details | -| `timestamp` | String | Optional timestamp in ISO 8601 format | -| `managed_object` | String | Managed Object'd ID | -| `alarm_class` | String | Alarm class name | -| `vars` | Object {{ comples }} | Alarm variables | -| `remote_system` | String | Optional Remote System ID | -| `remote_id` | String | Optional Remote ID | +| Field | Type | Description | +| ----------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------ | +| `$op` | String | Equals to `raise` | +| `reference` | String | Alarm reference. See [alarm reference format](../alarm-reference-format.md) for details | +| `timestamp` | String | Optional timestamp in ISO 8601 format | +| `managed_object` | String | Managed Object'd ID | +| `alarm_class` | String | Alarm class name | +| `groups` | Array of Object {{ complex }} | Optional static groups | +| {{ tab }} `reference` | String | Optional Group Alarm reference. See [alarm reference format](../alarm-reference-format.md) for details | +| {{ tab }} `name` | String | Optional group name | +| {{ tab }} `alarm_class` | String | Optional group alarm class name | +| `vars` | Object {{ complex }} | Alarm variables | +| `remote_system` | String | Optional Remote System ID | +| `remote_id` | String | Optional Remote ID | ### clear message `clear` message represents a direct alarm clear request, issued by an external mechanism. @@ -58,4 +62,4 @@ in the `$op` field. Unknown message types and malformed messages are discarded. | ----------- | ------ | ----------------------------------------------------------------------------------- | | `$op` | String | Equals to `clear` | | `reference` | String | Alarm reference. Should be the same as in previous [raise](#raise-message) message. | -| `timestamp` | String | Optional timestamp in ISO 8601 format | \ No newline at end of file +| `timestamp` | String | Optional timestamp in ISO 8601 format | diff --git a/fm/models/alarmclass.py b/fm/models/alarmclass.py index c6c137d78cd889ca8ff0a3930e56e0b4a14048d1..857711bde2087aa14930938e51d468bd5e979cb6 100644 --- a/fm/models/alarmclass.py +++ b/fm/models/alarmclass.py @@ -9,6 +9,7 @@ import os from threading import Lock import operator +from typing import Optional # Third-party modules from mongoengine.document import Document @@ -195,17 +196,17 @@ class AlarmClass(Document): @classmethod @cachetools.cachedmethod(operator.attrgetter("_id_cache"), lock=lambda _: id_lock) - def get_by_id(cls, id): + def get_by_id(cls, id) -> Optional["AlarmClass"]: return AlarmClass.objects.filter(id=id).first() @classmethod @cachetools.cachedmethod(operator.attrgetter("_bi_id_cache"), lock=lambda _: id_lock) - def get_by_bi_id(cls, id): + def get_by_bi_id(cls, id: int) -> Optional["AlarmClass"]: return AlarmClass.objects.filter(bi_id=id).first() @classmethod @cachetools.cachedmethod(operator.attrgetter("_name_cache"), lock=lambda _: id_lock) - def get_by_name(cls, name): + def get_by_name(cls, name: str) -> Optional["AlarmClass"]: return AlarmClass.objects.filter(name=name).first() def get_handlers(self): diff --git a/services/correlator/models/raisereq.py b/services/correlator/models/raisereq.py index 82d0716ba2e3acb57196f6920ee4b88799bc1aeb..47583b1e56736cf687ddd24042fc6da75b0f2505 100644 --- a/services/correlator/models/raisereq.py +++ b/services/correlator/models/raisereq.py @@ -6,7 +6,7 @@ # --------------------------------------------------------------------- # Python modules -from typing import Optional, Dict, Any +from typing import Optional, Dict, Any, List try: from typing import Literal @@ -18,12 +18,19 @@ except ImportError: from pydantic import BaseModel, Field +class GroupItem(BaseModel): + reference: str + alarm_class: Optional[str] + name: Optional[str] + + class RaiseRequest(BaseModel): op: Literal["raise"] = Field(None, alias="$op") reference: str managed_object: str alarm_class: str timestamp: Optional[str] + groups: Optional[List[GroupItem]] vars: Optional[Dict[str, Any]] remote_system: Optional[str] remote_id: Optional[str] diff --git a/services/correlator/service.py b/services/correlator/service.py index e91be393f7ac9d26a17208bb5be9c11ee82d714e..82e881897c7638dbe7a68e71f6f6a1841ac380da 100755 --- a/services/correlator/service.py +++ b/services/correlator/service.py @@ -29,7 +29,7 @@ from noc.core.service.tornado import TornadoService from noc.core.scheduler.scheduler import Scheduler from noc.core.mongo.connection import connect from noc.sa.models.managedobject import ManagedObject -from noc.services.correlator.alarmrule import AlarmRuleSet +from noc.services.correlator.alarmrule import AlarmRuleSet, AlarmRule as CAlarmRule from noc.services.correlator.rule import Rule from noc.services.correlator.rcacondition import RCACondition from noc.services.correlator.trigger import Trigger @@ -364,6 +364,7 @@ class CorrelatorService(TornadoService): reference: Optional[str] = None, remote_system: Optional[RemoteSystem] = None, remote_id: Optional[str] = None, + groups: Optional[List[GroupItem]] = None, ) -> Optional[ActiveAlarm]: """ Raise alarm @@ -375,6 +376,7 @@ class CorrelatorService(TornadoService): :param reference: :param remote_system: :param remote_id: + :param groups: :returns: Alarm, if created, None otherwise """ scope_label = str(event.id) if event else "DIRECT" @@ -397,11 +399,11 @@ class CorrelatorService(TornadoService): ) if alarm: self.logger.info( - "[%s|%s|%s] Contributing event %s to active alarm %s(%s)", + "[%s|%s|%s] Contributing %s to active alarm %s(%s)", scope_label, managed_object.name, managed_object.address, - event.event_class.name, + f"event {event.event_class.name}" if event else "DIRECT", alarm.alarm_class.name, alarm.id, ) @@ -457,14 +459,18 @@ class CorrelatorService(TornadoService): ) a.effective_labels = a.iter_effective_labels(a) a.raw_reference = reference - # @todo: Static groups + # Static groups + alarm_groups: Dict[str, GroupItem] = {} + if groups: + for gi in groups: + if gi.reference and gi.reference not in alarm_groups: + alarm_groups[gi.reference] = gi # Apply rules - groups: Dict[str, GroupItem] = {} for rule in self.alarm_rule_set.iter_rules(a): for gi in rule.iter_groups(a): - if gi.reference and gi.reference not in groups: - groups[gi.reference] = gi - all_groups = await self.get_groups(a, groups.values()) + if gi.reference and gi.reference not in alarm_groups: + alarm_groups[gi.reference] = gi + all_groups = await self.get_groups(a, alarm_groups.values()) a.groups = [g.reference for g in all_groups] # Save a.save() @@ -715,6 +721,16 @@ class CorrelatorService(TornadoService): if not alarm_class: self.logger.error("Invalid alarm class: %s", req.alarm_class) return + # Groups + if req.groups: + groups = [] + for gi in req.groups: + ac = AlarmClass.get_by_name(gi.alarm_class) if gi.alarm_class else None + ac = ac or CAlarmRule.get_default_alarm_class() + gi_name = gi.name or "Alarm Group" + groups.append(GroupItem(reference=gi.reference, alarm_class=ac, title=gi_name)) + else: + groups = None # Remote system if req.remote_system and req.remote_id: remote_system = RemoteSystem.get_by_id(req.remote_system) @@ -727,6 +743,7 @@ class CorrelatorService(TornadoService): alarm_class=alarm_class, vars=req.vars, reference=req.reference, + groups=groups, remote_system=remote_system, remote_id=req.remote_id if remote_system else None, )