Commit 591b94e6 authored by Andrey Vertiprahov's avatar Andrey Vertiprahov
Browse files

Merge branch 'noc-static-alarms' into 'master'

correlator: static groups in raise request

See merge request noc/noc!5880
parents 1a283661 427a7ea7
......@@ -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 |
......@@ -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):
......
......@@ -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]
......@@ -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,
)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment