Правило «процесор вище 85% — це проблема» описує клас пристроїв, а не окремий хост. Тепер воно живе поруч із перевірками, які дають йому дані, а не окремою сторінкою, де його доводилось повторювати руками для кожного комутатора. Тригер розгортається в ОДНЕ правило alr.rules із селектором за шаблоном, а не в правило на кожен хост: призначили шаблон новому пристрою — він одразу під правилом, без перегенерації. Форма шаблону розкладена на вкладки Загальне/Перевірки/Графіки/Тригери з лічильниками; смуга вкладок винесена у спільний компонент. Умова тригера редагується полями, JSON лишився запасним виходом. Правила з шаблону в списку правил помічені й не редагуються там. Клон копіює тригери вимкненими, щоб копія не подвоїла сповіщення. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
105 lines
5.8 KiB
SQL
105 lines
5.8 KiB
SQL
-- =====================================================================
|
||
-- NetPulse :: 0024_template_triggers.sql
|
||
-- Тригери описуються в шаблоні разом із перевірками й графіками.
|
||
--
|
||
-- Причина та сама, що для метрик і графіків: «завантаження процесора
|
||
-- вище 85% п'ять хвилин — це проблема» — властивість класу пристроїв, а
|
||
-- не окремого хоста. Заводити те саме правило руками на кожен комутатор
|
||
-- означає повторювати одне рішення стільки разів, скільки в мережі
|
||
-- заліза, і забути про половину при наступній зміні порога.
|
||
--
|
||
-- Окрема сторінка «Правила» лишається — але для того, для чого вона
|
||
-- справді потрібна: разових правил на конкретний хост і правил, що
|
||
-- перетинають класи («будь-що в стійці 3»).
|
||
-- =====================================================================
|
||
|
||
CREATE TABLE tpl.triggers (
|
||
id uuid PRIMARY KEY DEFAULT core.new_id(),
|
||
template_id uuid NOT NULL REFERENCES tpl.templates(id) ON DELETE CASCADE,
|
||
key core.slug NOT NULL,
|
||
name text NOT NULL,
|
||
description text NOT NULL DEFAULT '',
|
||
|
||
source alr.rule_source NOT NULL DEFAULT 'metric',
|
||
severity alr.severity NOT NULL DEFAULT 'warning',
|
||
|
||
-- Той самий формат, що в alr.rules.condition — тригер шаблону і є
|
||
-- правилом, просто описаним один раз на клас пристроїв.
|
||
condition jsonb NOT NULL,
|
||
recovery jsonb,
|
||
for_seconds int NOT NULL DEFAULT 60 CHECK (for_seconds BETWEEN 0 AND 86400),
|
||
depends_on_topology boolean NOT NULL DEFAULT true,
|
||
|
||
enabled boolean NOT NULL DEFAULT true,
|
||
position int NOT NULL DEFAULT 0,
|
||
created_at timestamptz NOT NULL DEFAULT now(),
|
||
UNIQUE (template_id, key)
|
||
);
|
||
CREATE INDEX tpl_triggers_template_idx ON tpl.triggers (template_id);
|
||
|
||
ALTER TABLE tpl.triggers ENABLE ROW LEVEL SECURITY;
|
||
ALTER TABLE tpl.triggers FORCE ROW LEVEL SECURITY;
|
||
|
||
-- Видимість успадковується від шаблону: власний tenant_id у тригера
|
||
-- завжди дорівнював би шаблоновому, а два джерела однієї істини рано чи
|
||
-- пізно розходяться.
|
||
CREATE POLICY triggers_follow_template ON tpl.triggers
|
||
USING (EXISTS (
|
||
SELECT 1 FROM tpl.templates t
|
||
WHERE t.id = tpl.triggers.template_id
|
||
AND (t.tenant_id IS NULL OR t.tenant_id = core.current_tenant())
|
||
))
|
||
WITH CHECK (EXISTS (
|
||
SELECT 1 FROM tpl.templates t
|
||
WHERE t.id = tpl.triggers.template_id AND t.tenant_id = core.current_tenant()
|
||
));
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Звідки взялося правило
|
||
-- ---------------------------------------------------------------------
|
||
|
||
-- Тригер шаблону розгортається В ОДНЕ правило, а не в правило на кожен
|
||
-- хост. Прив'язка йде через селектор `{"template_ids":[...]}`: хост,
|
||
-- якому щойно призначили шаблон, одразу підпадає під його тригери, і
|
||
-- нічого перегенеровувати не треба.
|
||
--
|
||
-- Правило на кожен хост дало б тисячі рядків, які треба тримати в
|
||
-- синхроні з призначеннями шаблонів — і кожне розходження виглядало б
|
||
-- як «алерт не спрацював», найгірший спосіб дізнатись про баг.
|
||
ALTER TABLE alr.rules
|
||
ADD COLUMN template_id uuid REFERENCES tpl.templates(id) ON DELETE CASCADE,
|
||
ADD COLUMN template_trigger_key text;
|
||
|
||
-- Часткова унікальність: правила, заведені руками, обмежень не мають.
|
||
CREATE UNIQUE INDEX alr_rules_from_template_uniq
|
||
ON alr.rules (template_id, template_trigger_key)
|
||
WHERE template_id IS NOT NULL;
|
||
|
||
COMMENT ON COLUMN alr.rules.template_id IS
|
||
'Не NULL — правило породжене тригером шаблону; правити його треба в шаблоні';
|
||
|
||
-- ---------------------------------------------------------------------
|
||
-- Готові тригери до вбудованих шаблонів
|
||
-- ---------------------------------------------------------------------
|
||
|
||
-- Без них шаблон «icmp-basic» лишається німим: перевірка є, а сказати
|
||
-- про недоступність нікому. Саме цього й чекають від коробки.
|
||
INSERT INTO tpl.triggers (template_id, key, name, description, source, severity,
|
||
condition, for_seconds, position)
|
||
SELECT t.id, 'icmp-down', 'Хост недоступний',
|
||
'Пінг не проходить довше хвилини',
|
||
'icmp', 'high',
|
||
'{"op":"loss_pct >","value":99}'::jsonb, 60, 0
|
||
FROM tpl.templates t
|
||
WHERE t.key = 'icmp-basic' AND t.tenant_id IS NULL
|
||
ON CONFLICT DO NOTHING;
|
||
|
||
INSERT INTO tpl.triggers (template_id, key, name, description, source, severity,
|
||
condition, for_seconds, position)
|
||
SELECT t.id, 'icmp-loss', 'Втрати пакетів',
|
||
'Понад 20% втрат протягом трьох хвилин — канал є, але поганий',
|
||
'icmp', 'warning',
|
||
'{"op":"loss_pct >","value":20}'::jsonb, 180, 1
|
||
FROM tpl.templates t
|
||
WHERE t.key = 'icmp-basic' AND t.tenant_id IS NULL
|
||
ON CONFLICT DO NOTHING;
|