126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type MaintenanceWindow struct {
|
|
Enabled bool `json:"enabled"`
|
|
Active bool `json:"active"`
|
|
StartedAt int64 `json:"startedAt"`
|
|
EndsAt int64 `json:"endsAt"`
|
|
Reason string `json:"reason"`
|
|
AllAlerts bool `json:"allAlerts"`
|
|
ServiceIDs []int64 `json:"serviceIds"`
|
|
VMIDs []int `json:"vmids"`
|
|
}
|
|
|
|
func (s *Store) MaintenanceWindow() MaintenanceWindow {
|
|
var value string
|
|
var window MaintenanceWindow
|
|
if s.db.QueryRow(`SELECT value FROM settings WHERE key='maintenance_window'`).Scan(&value) == nil {
|
|
_ = json.Unmarshal([]byte(value), &window)
|
|
}
|
|
now := time.Now().Unix()
|
|
window.Active = window.Enabled && window.StartedAt <= now && window.EndsAt > now
|
|
if window.Enabled && window.EndsAt <= now {
|
|
window.Enabled = false
|
|
data, _ := json.Marshal(window)
|
|
_, _ = s.db.Exec(`INSERT INTO settings(key,value) VALUES('maintenance_window',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, string(data))
|
|
}
|
|
return window
|
|
}
|
|
|
|
func (s *Store) SaveMaintenanceWindow(window MaintenanceWindow) (MaintenanceWindow, error) {
|
|
now := time.Now()
|
|
window.Reason = strings.TrimSpace(window.Reason)
|
|
if window.Reason == "" {
|
|
return window, fmt.Errorf("укажите причину плановых работ")
|
|
}
|
|
if window.EndsAt <= now.Unix() || window.EndsAt > now.Add(30*24*time.Hour).Unix() {
|
|
return window, fmt.Errorf("окончание должно быть в будущем, максимум через 30 дней")
|
|
}
|
|
if !window.AllAlerts && len(window.ServiceIDs) == 0 && len(window.VMIDs) == 0 {
|
|
return window, fmt.Errorf("выберите сервис, VM/LXC или все алерты")
|
|
}
|
|
window.Enabled = true
|
|
window.Active = true
|
|
window.StartedAt = now.Unix()
|
|
window.ServiceIDs = uniqueInt64(window.ServiceIDs)
|
|
window.VMIDs = uniqueInt(window.VMIDs)
|
|
data, _ := json.Marshal(window)
|
|
_, err := s.db.Exec(`INSERT INTO settings(key,value) VALUES('maintenance_window',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, string(data))
|
|
return window, err
|
|
}
|
|
|
|
func (s *Store) StopMaintenanceWindow() (MaintenanceWindow, error) {
|
|
window := s.MaintenanceWindow()
|
|
window.Enabled = false
|
|
window.Active = false
|
|
window.EndsAt = time.Now().Unix()
|
|
data, _ := json.Marshal(window)
|
|
_, err := s.db.Exec(`INSERT INTO settings(key,value) VALUES('maintenance_window',?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, string(data))
|
|
return window, err
|
|
}
|
|
|
|
func splitMaintenanceAlerts(alerts []Alert, window MaintenanceWindow) (active, planned []Alert) {
|
|
if !window.Active {
|
|
return alerts, nil
|
|
}
|
|
for _, alert := range alerts {
|
|
if maintenanceMatchesAlert(window, alert) {
|
|
planned = append(planned, alert)
|
|
} else {
|
|
active = append(active, alert)
|
|
}
|
|
}
|
|
return active, planned
|
|
}
|
|
|
|
func maintenanceMatchesAlert(window MaintenanceWindow, alert Alert) bool {
|
|
if window.AllAlerts {
|
|
return true
|
|
}
|
|
for _, id := range window.ServiceIDs {
|
|
value := strconv.FormatInt(id, 10)
|
|
if strings.HasPrefix(alert.ID, "service-"+value+"-") || strings.HasPrefix(alert.ID, "service-tls-"+value+"-") {
|
|
return true
|
|
}
|
|
}
|
|
for _, vmid := range window.VMIDs {
|
|
value := strconv.Itoa(vmid)
|
|
if strings.HasSuffix(alert.ID, "-"+value) || strings.Contains(alert.Message, "VMID "+value) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func uniqueInt64(values []int64) []int64 {
|
|
seen := map[int64]bool{}
|
|
result := make([]int64, 0, len(values))
|
|
for _, value := range values {
|
|
if value > 0 && !seen[value] {
|
|
seen[value] = true
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func uniqueInt(values []int) []int {
|
|
seen := map[int]bool{}
|
|
result := make([]int, 0, len(values))
|
|
for _, value := range values {
|
|
if value > 0 && !seen[value] {
|
|
seen[value] = true
|
|
result = append(result, value)
|
|
}
|
|
}
|
|
return result
|
|
}
|