Netpulse_SasS/server/internal/store/ncm_syslog.go
byrsapty c28852df66
Some checks are pending
CI / web (push) Waiting to run
CI / server (push) Waiting to run
CI / agent (push) Waiting to run
Syslog: транспорт до сервера і тригер позачергового бекапу
Приймач на зонді лежав без діла — тепер під'єднаний. Окремий стрім
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

79 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package store
import (
"context"
"github.com/jackc/pgx/v5"
npv1 "github.com/netpulse/netpulse/gen/go/netpulse/v1"
)
// defaultSyslogMatch — за чим упізнаємо зміну конфігу, якщо політика
// власного зразка не задала.
//
// Три родини покривають майже все, що трапляється в мережах:
//
// %SYS-5-CONFIG_I Cisco IOS/NX-OS — вихід із режиму конфігурації
// CFGCHG / CONFIG_I HP, Huawei, Eltex і похідні
// commit / configure Juniper та MikroTik
//
// Зразок навмисно широкий: зайвий бекап коштує кількох секунд сесії,
// а пропущений — цілої зміни, про яку ніхто не дізнається.
const defaultSyslogMatch = `%SYS-[0-9]-CONFIG_I|CFGCHG|CONFIG_I|` +
`[Cc]ommit complete|[Cc]onfiguration changed|system,info configuration changed`
// SyslogBackupTriggers каже, яким хостам подія в журналі щойно
// призначила позачерговий збір конфігу.
//
// Порівняння зразка робить Postgres, а не Go: політика зберігає його
// рядком у базі, і тягнути всі політики в пам'ять заради кожної пачки
// журналу означало б робити роботу там, де для неї немає даних.
//
// Хости без device_id пропускаються самі собою: подія з невідомої адреси
// не належить нікому, і збирати з неї нічого.
func (s *Store) SyslogBackupTriggers(
ctx context.Context,
tenantID string,
entries []*npv1.SyslogEntry,
) ([]string, error) {
devices := make([]string, 0, len(entries))
messages := make([]string, 0, len(entries))
for _, e := range entries {
if e.GetDeviceId() == "" || e.GetMessage() == "" {
continue
}
devices = append(devices, e.GetDeviceId())
messages = append(messages, e.GetMessage())
}
if len(devices) == 0 {
return nil, nil
}
var out []string
err := s.InTenantTx(ctx, tenantID, func(tx pgx.Tx) error {
rows, err := tx.Query(ctx, `
WITH ev(device_id, message) AS (
SELECT * FROM unnest($2::uuid[], $3::text[])
)
SELECT DISTINCT ev.device_id::text
FROM ev
JOIN ncm.device_policies dp
ON dp.device_id = ev.device_id AND dp.tenant_id = $1
WHERE dp.on_syslog
AND dp.profile_id IS NOT NULL
AND ev.message ~ COALESCE(NULLIF(dp.syslog_match, ''), $4)
`, tenantID, devices, messages, defaultSyslogMatch)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
return err
}
out = append(out, id)
}
return rows.Err()
})
return out, err
}