Commit graph

9 commits

Author SHA1 Message Date
e98d51fe76 Трапи: приймач каже про готовність тоді, коли справді готовий
All checks were successful
CI / hygiene (push) Successful in 8s
CI / web (push) Successful in 44s
CI / server (push) Successful in 1m18s
CI / agent (push) Successful in 38s
Перший прогін CI завалив роботу agent на TestInformIsAcknowledged:
«connection refused» від власного слухача. Локально той самий тест
проходив — тобто плаваючий, а плаваючий тест отруює CI сильніше за
відсутність CI: він привчає не дивитись на червоне.

Причина — гонка в тесті. Run піднімається в горутині, а відправник шле
одразу, не чекаючи. Між стартом горутини й зайняттям сокета є вікно, і
датаграма, що в нього потрапила, отримує від ядра «port unreachable».
На вільній машині вікно програє гонку майже завжди, під навантаженням
раннера — виграє.

Заодно знайшлось гірше, і вже не в тесті: рядок «приймач трапів слухає»
друкувався ПЕРЕД tl.Listen(). Тобто журнал стверджував успіх до спроби,
і навіть тоді, коли порт зайняти не вдалося, — а для 162 це саме той
випадок, коли приймач, який мовчить, виглядає точнісінько як спокійна
мережа. Тепер повідомлення йде після фактичного зайняття, і поруч
з'явився Listening(): канал, який закривається тоді ж.

Перевірено 20 прогонів поспіль — зелено. І окремо варте уваги: цю ваду
не спіймала б жодна з наших перевірок, окрім справжнього CI під
навантаженням. Він виправдав себе на першому ж запуску.
2026-08-27 18:11:23 +03:00
ed8fc831bf Дві сесії роботи: 0058–0068, розгортання однією командою, тести
Один коміт, а не десяток тематичних, свідомо: теми переплетені в
спільних файлах (store.go, docker-compose.yml, deploy/README.md), і
розділити їх можна було б лише індексуванням шматків. Коміти, які не
збираються, гірші за один великий — тим паче що це рівно той стан, який
перевірявся разом.

ЩО ПРАЦЮЄ НА СТЕНДІ Й ПЕРЕВІРЕНО ТАМ

  0058  подієві алерти: syslog, ncm, compliance спрацьовують у мить
        події; правило з нереалізованим джерелом більше не зберігається
        мовчки
  0059  snmp.walk і прототипи шаблонів — таблиці з динамічним індексом
        описуються шаблоном, а не Go
  0060  відкат конфігу: план як різниця, маскування паролів із підписом
        плану, обов'язковий контрольний збір, verifying при обриві
  0061  кнопки Telegram: довге опитування, авторизація не з callback_data
  0062  аудит і архів хостів; тест на AST, що падає на ключі без назви
  0063  RLS: три ролі, окремий пул для фонових тактів
  0064  строки зберігання даних і сторінка сховища
  0065  приймач SNMP-трапів; перевірено справжніми пакетами по дроту,
        переклад v1→v2 за RFC 3584 дає правильний OID
  0066  ескалації сповіщень
  0067  алерт про вичерпання диска
  0068  поля заливки конфігу переїхали в каталог профілів

Плюс: 137 тестів вебу з нуля (їх не було взагалі), одинадцять справжніх
вад, знайдених ними й виправлених, і виправлення двох інтеграційних
тестів grpcapi, які мовчки пропускались півтора року.

ЩО ЩЕ НЕ ЗАПУСКАЛОСЬ

  netpulse            установник: одна команда замість 18 змінних і
                      593 рядків інструкції
  RLS з першого запуску  нова інсталяція під політиками одразу;
                      RLS-EXISTING-INSTALL.md лишається тільки для
                      старих інсталяцій
  .forgejo + CI       раннер не зареєстрований

Ці три перевірені компіляцією й міркуванням, але не виконанням.

ГОЛОВНИЙ ВИСНОВОК ДВОХ СЕСІЙ

Зелена перевірка доводить рівно те, що вона перевіряє. Тест ізоляції RLS
був правильний і зелений — і пропустив зламаний вхід, бо перевіряв «чи
не видно чужого», коли зламалось «чи видно своє». Інтеграційні тести
grpcapi були зелені, бо не виконувались. Схема, довідник і протокол
описували те, чого в коді не існувало, і виглядало це як готове.

Тому в кожному завданні цих сесій стояла вимога назвати НЕПОКРИТЕ, а
чотири задачі закінчились не можливістю, а відмовою: правило з
нереалізованим джерелом не зберігається, профіль без команд заливки
каже про це замість мовчазної кнопки, міграція RLS валить сама себе на
таблиці без політики, тест словника аудиту падає на ключі без назви.

Подробиці — HISTORY.md, розділи за 26 і 27 серпня.
2026-08-27 17:32:49 +03:00
32b0b003df Дрібний борг: модуль http/ssl і зрозумілі помилки на мапі
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Плагін http стояв у сіді як базовий — тобто обіцяний усім одразу, — а
модуля не існувало. Тепер є: код відповіді, час, збіг слова в тілі й
залишок днів до кінця сертифіката.

Недоступність повертається нулем, а не помилкою чека: на графіку це
читається як провал, і саме за цим ставлять тригер. Довіру до ланцюга
сертифікатів свідомо не перевіряємо — питають строк, а самопідписаний
теж має дату.

Повторна лінія між вузлами й повторно доданий хост давали однакове
«такий запис уже існує». Тепер кожен випадок каже, що робити далі.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 18:37:58 +03:00
29f440d436 Автопризначення шаблонів за sysObjectID
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Пристрій сам каже, що він таке, і шаблон чіпляється без натискань.
Системна група знімається тією ж SNMP-сесією, що й обхід топології:
три зайві PDU дешевші за окремий чек із власним розкладом.

Збіг за префіксом на межі компонента: моделей у виробника тисячі, і
повний збіг означав би рядок на кожну коробку. Довший префікс
перемагає. Дванадцять вбудованих правил на основних виробників.

Шаблони тільки додаються, ніколи не знімаються: автоматика знає модель
пристрою, але не знає, чому цьому хосту дали ще один шаблон руками.

DiscoveredDevice отримав device_id: зіставляти за адресою не можна —
за одним NAT кілька хостів мають ту саму адресу опитування.

Заразом дубль перевірки перестав давати «внутрішню помилку».

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 16:29:39 +03:00
c28852df66 Syslog: транспорт до сервера і тригер позачергового бекапу
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Приймач на зонді лежав без діла — тепер під'єднаний. Окремий стрім
StreamLogs, а не контрольний канал: сплеск логів під час аварії не має
заважати heartbeat і командам.

Хост зіставляється за адресою джерела на зонді: у сервера немає
контексту мережі клієнта, а один приватний діапазон трапляється в
десятках кабінетів. Невідома адреса не привід викинути подію.

Подія, що збіглася зі зразком у ncm.device_policies.syslog_match,
ставить позачерговий збір конфігу. Типовий зразок покриває Cisco,
HP/Huawei, Juniper і MikroTik — навмисно широкий: зайвий бекап коштує
секунд, пропущений — цілої зміни.

Заразом увесь репозиторій прогнано через gofmt: CI, написаний два
кроки тому, перевіряє це і впав би на 29 файлах із порушеннями,
накопиченими за весь проєкт.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 15:20:32 +03:00
0bc2335661 Приймач syslog на зонді: розбір RFC3164/5424, черга, ліміти
Модуль слухає UDP у мережі клієнта: комутатор у закритій мережі до
сервера не достукається, а зонд уже має вихідний канал.

Розбір трьома рівнями суворості — 5424, 3164 і «як є». Останній не
запасний варіант, а робочий режим: пристрої, що шлють голий текст,
існують, і втратити подію гірше, ніж зберегти саме повідомлення.
Текст Cisco "%SYS-5-CONFIG_I: ..." свідомо НЕ ріжеться в поле tag —
саме за ним шукають зміну конфігу.

Ліміт частоти на джерело, а не спільний: комутатор, що зациклився на
помилці, інакше витіснив би з журналу всю решту мережі — тобто рівно
те, що треба бачити під час аварії. З тієї ж причини переповнена черга
викидає найстаріше, а не найновіше.

Транспорт до сервера ще не під'єднано: серверний StreamLogs уже є,
лишається цикл відправки на зонді.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 13:31:40 +03:00
aaf067a46b Етап 2: сервер сам заводить snmp.if-чеки з виявлених інтерфейсів
Автовиявлення наповнювало inv.interfaces, але їх ніхто не опитував: на
мапі були лінки й не було трафіку. Тепер сервер формує snmp.if-чек зі
складу портів і штовхає його живій сесії як TaskDelta — без цього після
кожного нового комутатора були б години порожніх графіків.

Чек оновлюється, а не задвоюється: унікальний індекс core.checks включає
md5(params). Склад портів порівнюється як множина, бо порядок ключів у
jsonb не гарантований. Без SNMP-креденшела чек не створюється.

Живий прогін знайшов помилку: OID у запиті йшов без провідної крапки, а
pdu.Name повертається з нею — пошук у мапі мовчки не знаходив нічого, і
чек виглядав як "жоден інтерфейс не відповів". Канонізація тепер у
snmpx.Normalize, застосована з обох боків.

Перевірено наскрізь: виявлення -> автостворення чека -> справжні
HC-лічильники -> ts.if_counters, без жодного ручного кроку.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 15:10:02 +03:00
ad7672c32f Етап 2: модуль topology — LLDP/CDP/ARP/FDB та інвентар портів
Спільний SNMP-транспорт винесено в snmpx: ним користуються два модулі.
Звіти автовиявлення йдуть окремим RPC, не телеметричним стрімом — вони
рідкі, великі й не прив'язані до моменту часу так, як метрики.

Живий прогін проти справжнього snmpd+lldpd знайшов дві помилки:

1. Префікс типу чека не збігався з ключем модуля (topo.discover при
   плагіні topology). Агент маршрутизує задачі саме за префіксом, тож
   зонд відхиляв би їх. Інваріант закріплено обмеженням у БД.
2. Унікальний індекс topo.neighbors схлопував ARP-сусідів: ключ не
   включав MAC, а chassis_id/port_id в ARP немає взагалі.

Перевірено на живих даних: інвентар із ifTable, 2 ARP-сусіди, зіставлення
шлюзу за MAC (впевненість 90), зведений лінк із capacity 10 Гбіт/с.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:42:01 +03:00
c8075ee2a6 Етап 2: Go-агент — планувальник, буфер, сесія, модулі ICMP і SNMP
Модулі вкомпільовані (без .so): один бінарник на Alpine, Windows і роутер.
Креденшели беруться на момент виконання, бо мають TTL. Розклад вирівняний
по сітці інтервалу, тому переживає рестарт. Буфер обмежений і за кількістю,
і за пам'яттю; при переповненні викидає найстаріше, зміни статусу — останніми.

Перевірено на Debian 13 / Go 1.25: go vet чисто, go test -race усі пакети ok.
Релізний бінарник 12 МБ, базовий RSS 11.6 МБ при бюджеті 30 МБ.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 03:46:46 +03:00