feat: CID-based routing rules engine with flexible match conditions
Routing rules: - RouteRule model: 3 match fields (caller_id, dest, ingress_trunk regex), 3 action types (node/pool/auto), time-based (days of week, time range) - Engine router.go: CompiledRule with pre-compiled regex, PickNodeForCall with cascade (dead node → next rule), pickNodeFromPool - Separate sync.RWMutex for Router — zero contention admin vs traffic - Validate-before-save pattern: engine validates before JSON save - Graceful degradation: corrupted rules skipped, no rules → PickNode() Persistence: - data/routing.json with atomic save (tmp→rename), config.Manager - Auto-created empty on first start API (admin only): - GET/POST/PUT/DELETE /api/routing/rules - PUT /api/routing/rules/:id/toggle - PUT /api/routing/rules/reorder Integration: - /api/route now uses PickNodeForCall with caller_id/dest/ingress_trunk query params (already sent by route.lua) - RouteResponse includes matched_rule field for debugging - Main.go loads routing rules on startup with graceful fallback Web UI: - /routing page: table with priority arrows, inline edit, create form, regex test tool, toggle, delete, action badges, hits counter Tests: - 28 tests: matchRule, time matching, PickNodeForCall cascade, pool scoring, validation, hit counting, compilation
This commit is contained in:
@@ -63,7 +63,16 @@ func main() {
|
||||
// 3. Engine
|
||||
eng := engine.NewEngine(cfg)
|
||||
|
||||
// 3.1 Запускаем эвикцию stale-нод (очистка нод без метрик > 5 минут, проверка каждые 60 сек)
|
||||
// 3.1 Загружаем routing rules из routing.json (graceful degradation: битые правила скипаются)
|
||||
rules, err := cfgMgr.ReadRoutingRules()
|
||||
if err != nil {
|
||||
log.Printf("[main] ошибка чтения routing.json: %v (работаем без правил)", err)
|
||||
} else if len(rules) > 0 {
|
||||
eng.LoadRules(rules)
|
||||
log.Printf("[main] загружено %d правил маршрутизации из routing.json", len(rules))
|
||||
}
|
||||
|
||||
// 3.2 Запускаем эвикцию stale-нод (очистка нод без метрик > 5 минут, проверка каждые 60 сек)
|
||||
eng.StartEvictionLoop(60*time.Second, 5*time.Minute)
|
||||
|
||||
// 4. ASCII логгер метрик
|
||||
@@ -257,6 +266,7 @@ func main() {
|
||||
log.Printf(" GET /api/nodes — список нод")
|
||||
log.Printf(" GET /api/trunks — управление транками")
|
||||
log.Printf(" GET /api/users — управление пользователями")
|
||||
log.Printf(" GET /api/routing/rules — правила маршрутизации (CID/dest/trunk/time)")
|
||||
log.Printf(" GET /api/monitoring/zabbix — Zabbix LLD + items")
|
||||
log.Printf(" GET /api/monitoring/prometheus — Prometheus метрики")
|
||||
log.Printf(" WS /ws/metrics — real-time метрики на фронт")
|
||||
|
||||
Reference in New Issue
Block a user