alert-autoconf
232 строки · 6.2 Кб
1from uuid import UUID2from datetime import time3from enum import Enum4from typing import Dict, List, Optional5from pydantic import BaseModel, AnyHttpUrl, validator, root_validator6
7
8class ContactTypeEnum(Enum):9JIRA = "jira"10MAIL = "mail"11PUSHOVER = "pushover"12SEND_SMS = "send-sms"13SLACK = "slack"14TELEGRAM = "telegram"15TWILIO_SMS = "twilio sms"16TWILIO_VOICE = "twilio voice"17
18
19class DaysEnum(Enum):20MON = "Mon"21TUE = "Tue"22WED = "Wed"23THU = "Thu"24FRI = "Fri"25SAT = "Sat"26SUN = "Sun"27
28
29class TtlStateEnum(Enum):30DEL = "DEL"31ERROR = "ERROR"32NODATA = "NODATA"33OK = "OK"34WARN = "WARN"35
36
37class ParentTriggerRef(BaseModel):38tags: List[str]39name: str40
41def __hash__(self):42return hash((43frozenset(tags),44name,45))46
47def __eq__(self, other):48return (49set(self.tags) == set(other.tags)50and self.name == other.name51)52
53
54class Saturation(BaseModel):55type: str56fallback: Optional[str] = None57parameters: Optional[dict] = None58
59def to_custom_dict(self) -> Dict:60result = {61"type": self.type,62}63if self.fallback is not None:64result["fallback"] = self.fallback65if self.parameters is not None:66result["extra_parameters"] = self.parameters67return result68
69@classmethod70def from_moira_client_model(cls, moira_saturation: "moira_client.models.trigger.Saturation"):71d = moira_saturation.to_dict()72d["parameters"] = d.pop("extra_parameters", None)73return cls(**d)74
75def __hash__(self):76dct = self.to_custom_dict()77return hash(_freeze_dict(dct))78
79def __eq__(self, other):80if isinstance(other, Saturation):81return self.to_custom_dict() == other.to_custom_dict()82else:83raise ValueError("Incomparable types")84
85
86def _freeze_dict(dct):87"""Tries to freeze a dict to make it hashable."""88result = []89for key, value in dct.items():90if isinstance(value, dict):91value = _freeze_dict(value)92result.append((key, value))93result.sort()94return tuple(result)95
96
97class Trigger(BaseModel):98id: Optional[str] = None99name: str100tags: List[str]101targets: List[str]102warn_value: Optional[int] = None103error_value: Optional[int] = None104desc: str = ""105ttl: int = 600106ttl_state: TtlStateEnum = TtlStateEnum.NODATA107expression: Optional[str] = ""108
109is_pull_type: bool = False110dashboard: Optional[AnyHttpUrl] = None111pending_interval: Optional[int] = 0112
113day_disable: List[DaysEnum] = []114time_start: Optional[time] = time(hour=0, minute=0)115time_end: Optional[time] = time(hour=23, minute=59)116
117parents: Optional[List[str]]118
119saturation: Optional[List[Saturation]] = list()120
121@validator("id")122def id_uuid(cls, v):123try:124UUID(v)125except ValueError:126raise127return v128
129@root_validator130def check_thresholds_values(cls, values):131warn_value, error_value = (132values.get('warn_value') is not None,133values.get('error_value') is not None,134)135if warn_value ^ error_value:136raise ValueError('must provide warn_value and error_value')137
138if (139warn_value & error_value140and len(values.get('targets')) > 1141and values.get('expression') is None142):143raise ValueError('must use single target with warn_value and error_value')144
145return values146
147def to_custom_dict(self) -> Dict:148return {149'name': self.name,150'tags': self.tags,151'targets': self.targets,152'warn_value': self.warn_value,153'error_value': self.error_value,154'desc': self.desc,155'ttl': self.ttl,156'ttl_state': self.ttl_state.value,157'expression': self.expression,158'is_pull_type': self.is_pull_type,159'dashboard': self.dashboard,160'pending_interval': self.pending_interval,161'sched': {162'startOffset': self.time_start.hour * 60 + self.time_start.minute,163'endOffset': self.time_end.hour * 60 + self.time_end.minute,164'tzOffset': 0,165'days': [166{'name': day.value, 'enabled': day not in self.day_disable}167for day in DaysEnum168],169},170'parents': self.parents,171'saturation': [172s.to_custom_dict()173for s in self.saturation174],175}176
177
178class TriggerFile(Trigger):179parents: Optional[List[ParentTriggerRef]]180
181
182class Contact(BaseModel):183id: Optional[str] = None184type: ContactTypeEnum185value: str186fallback_value: Optional[str] = None187
188def __hash__(self):189return f"{self.type}:{self.value}:{self.fallback_value}".__hash__()190
191
192class Escalation(BaseModel):193contacts: List[Contact]194offset_in_minutes: int = 0195
196
197class Subscription(BaseModel):198tags: List[str]199contacts: Optional[List[Contact]] = []200escalations: Optional[List[Escalation]] = []201day_disable: List[DaysEnum] = []202time_start: Optional[time] = time(hour=0, minute=0)203time_end: Optional[time] = time(hour=23, minute=59)204
205def to_custom_dict(self) -> Dict:206return {207'tags': self.tags,208'contacts': [c.id for c in self.contacts],209'escalations': [210{211'contacts': [c.id for c in e.contacts],212'offset_in_minutes': e.offset_in_minutes,213}214for e in self.escalations215],216'sched': {217'startOffset': self.time_start.hour * 60 + self.time_start.minute,218'endOffset': self.time_end.hour * 60 + self.time_end.minute,219'tzOffset': 0,220'days': [221{'name': day.value, 'enabled': day not in self.day_disable}222for day in DaysEnum223],224},225}226
227
228class Alerts(BaseModel):229version: float = 1230prefix: str = ""231triggers: List[TriggerFile] = []232alerting: List[Subscription] = []233