add cron, nat api & refactor alert rule (#459)

* add cron api & refactor alert rule

* add nat api

* fix swagger

* remove unnecessary steps
This commit is contained in:
UUBulb
2024-10-26 23:57:47 +08:00
committed by GitHub
parent ebc4fad9bc
commit 68d7e16773
24 changed files with 573 additions and 144 deletions

View File

@@ -61,55 +61,55 @@ func (r *AlertRule) Enabled() bool {
return r.Enable != nil && *r.Enable
}
// Snapshot 对传入的Server进行该报警规则下所有type的检查 返回包含每项检查结果的空接口
func (r *AlertRule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) []interface{} {
var point []interface{}
for i := 0; i < len(r.Rules); i++ {
point = append(point, r.Rules[i].Snapshot(cycleTransferStats, server, db))
// Snapshot 对传入的Server进行该报警规则下所有type的检查 返回每项检查结果
func (r *AlertRule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) []bool {
point := make([]bool, 0, len(r.Rules))
for _, rule := range r.Rules {
point = append(point, rule.Snapshot(cycleTransferStats, server, db))
}
return point
}
// Check 传入包含当前报警规则下所有type检查结果的空接口 返回报警持续时间与是否通过报警检查(通过则返回true)
func (r *AlertRule) Check(points [][]interface{}) (int, bool) {
var maxNum int // 报警持续时间
var count int // 检查未通过的个数
for i := 0; i < len(r.Rules); i++ {
if r.Rules[i].IsTransferDurationRule() {
// Check 传入包含当前报警规则下所有type检查结果 返回报警持续时间与是否通过报警检查(通过则返回true)
func (r *AlertRule) Check(points [][]bool) (maxDuration int, passed bool) {
failCount := 0 // 检查未通过的个数
for i, rule := range r.Rules {
if rule.IsTransferDurationRule() {
// 循环区间流量报警
if maxNum < 1 {
maxNum = 1
if maxDuration < 1 {
maxDuration = 1
}
for j := len(points[i]) - 1; j >= 0; j-- {
if points[i][j] != nil {
count++
if !points[i][j] {
failCount++
break
}
}
} else {
// 常规报警
total := 0.0
fail := 0.0
num := int(r.Rules[i].Duration)
if num > maxNum {
maxNum = num
duration := int(rule.Duration)
if duration > maxDuration {
maxDuration = duration
}
if len(points) < num {
if len(points) < duration {
continue
}
for j := len(points) - 1; j >= 0 && len(points)-num <= j; j-- {
total, fail := 0.0, 0.0
for j := len(points) - duration; j < len(points); j++ {
total++
if points[j][i] != nil {
if !points[j][i] {
fail++
}
}
// 当70%以上的采样点未通过规则判断时 才认为当前检查未通过
if fail/total > 0.7 {
count++
failCount++
break
}
}
}
// 仅当所有检查均未通过时 返回false
return maxNum, count != len(r.Rules)
return maxDuration, failCount != len(r.Rules)
}

View File

@@ -1,7 +1,6 @@
package model
type AlertRuleForm struct {
ID uint64 `json:"id"`
Name string `json:"name"`
Rules []Rule `json:"rules"`
FailTriggerTasks []uint64 `json:"fail_trigger_tasks"` // 失败时触发的任务id

View File

@@ -18,19 +18,19 @@ const (
type Cron struct {
Common
Name string
TaskType uint8 `gorm:"default:0"` // 0:计划任务 1:触发任务
Scheduler string //分钟 小时 天 月 星期
Command string
Servers []uint64 `gorm:"-"`
PushSuccessful bool // 推送成功的通知
NotificationGroupID uint64 // 指定通知方式的分组
LastExecutedAt time.Time // 最后一次执行时间
LastResult bool // 最后一次执行结果
Cover uint8 // 计划任务覆盖范围 (0:仅覆盖特定服务器 1:仅忽略特定服务器 2:由触发该计划任务的服务器执行)
Name string `json:"name,omitempty"`
TaskType uint8 `gorm:"default:0" json:"task_type,omitempty"` // 0:计划任务 1:触发任务
Scheduler string `json:"scheduler,omitempty"` // 分钟 小时 天 月 星期
Command string `json:"command,omitempty"`
Servers []uint64 `gorm:"-" json:"servers,omitempty"`
PushSuccessful bool `json:"push_successful,omitempty"` // 推送成功的通知
NotificationGroupID uint64 `json:"notification_group_id,omitempty"` // 指定通知方式的分组
LastExecutedAt time.Time `json:"last_executed_at,omitempty"` // 最后一次执行时间
LastResult bool `json:"last_result,omitempty"` // 最后一次执行结果
Cover uint8 `json:"cover,omitempty"` // 计划任务覆盖范围 (0:仅覆盖特定服务器 1:仅忽略特定服务器 2:由触发该计划任务的服务器执行)
CronJobID cron.EntryID `gorm:"-"`
ServersRaw string
CronJobID cron.EntryID `gorm:"-" json:"cron_job_id,omitempty"`
ServersRaw string `json:"-"`
}
func (c *Cron) AfterFind(tx *gorm.DB) error {

13
model/cron_api.go Normal file
View File

@@ -0,0 +1,13 @@
package model
type CronForm struct {
ID uint64 `json:"id,omitempty"`
TaskType uint8 `json:"task_type,omitempty"` // 0:计划任务 1:触发任务
Name string `json:"name,omitempty"`
Scheduler string `json:"scheduler,omitempty"`
Command string `json:"command,omitempty"`
Servers []uint64 `json:"servers,omitempty"`
Cover uint8 `json:"cover,omitempty"`
PushSuccessful bool `json:"push_successful,omitempty"`
NotificationGroupID uint64 `json:"notification_group_id,omitempty"`
}

View File

@@ -1,7 +1,6 @@
package model
type DDNSForm struct {
ID uint64 `json:"id,omitempty"`
MaxRetries uint64 `json:"max_retries,omitempty"`
EnableIPv4 bool `json:"enable_ipv4,omitempty"`
EnableIPv6 bool `json:"enable_ipv6,omitempty"`

8
model/nat_api.go Normal file
View File

@@ -0,0 +1,8 @@
package model
type NATForm struct {
Name string `json:"name,omitempty"`
ServerID uint64 `json:"server_id,omitempty"`
Host string `json:"host,omitempty"`
Domain string `json:"domain,omitempty"`
}

View File

@@ -41,18 +41,6 @@ type Notification struct {
VerifySSL *bool `json:"verify_ssl,omitempty"`
}
type NotificationForm struct {
ID uint64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
RequestMethod int `json:"request_method,omitempty"`
RequestType int `json:"request_type,omitempty"`
RequestHeader string `json:"request_header,omitempty"`
RequestBody string `json:"request_body,omitempty"`
VerifySSL bool `json:"verify_ssl,omitempty"`
SkipCheck bool `json:"skip_check,omitempty"`
}
func (ns *NotificationServerBundle) reqURL(message string) string {
n := ns.Notification
return ns.replaceParamsInString(n.URL, message, func(msg string) string {

12
model/notification_api.go Normal file
View File

@@ -0,0 +1,12 @@
package model
type NotificationForm struct {
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
RequestMethod int `json:"request_method,omitempty"`
RequestType int `json:"request_type,omitempty"`
RequestHeader string `json:"request_header,omitempty"`
RequestBody string `json:"request_body,omitempty"`
VerifySSL bool `json:"verify_ssl,omitempty"`
SkipCheck bool `json:"skip_check,omitempty"`
}

View File

@@ -34,8 +34,8 @@ type Rule struct {
Ignore map[uint64]bool `json:"ignore,omitempty"` // 覆盖范围的排除
// 只作为缓存使用,记录下次该检测的时间
NextTransferAt map[uint64]time.Time `json:"-"`
LastCycleStatus map[uint64]interface{} `json:"-"`
NextTransferAt map[uint64]time.Time `json:"-"`
LastCycleStatus map[uint64]bool `json:"-"`
}
func percentage(used, total uint64) float64 {
@@ -45,15 +45,15 @@ func percentage(used, total uint64) float64 {
return float64(used) * 100 / float64(total)
}
// Snapshot 未通过规则返回 struct{}{}, 通过返回 nil
func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) interface{} {
// Snapshot 未通过规则返回 false, 通过返回 true
func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) bool {
// 监控全部但是排除了此服务器
if u.Cover == RuleCoverAll && u.Ignore[server.ID] {
return nil
return true
}
// 忽略全部但是指定监控了此服务器
if u.Cover == RuleCoverIgnoreAll && !u.Ignore[server.ID] {
return nil
return true
}
// 循环区间流量检测 · 短期无需重复检测
@@ -147,13 +147,13 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
u.NextTransferAt = make(map[uint64]time.Time)
}
if u.LastCycleStatus == nil {
u.LastCycleStatus = make(map[uint64]interface{})
u.LastCycleStatus = make(map[uint64]bool)
}
u.NextTransferAt[server.ID] = time.Now().Add(time.Second * time.Duration(seconds))
if (u.Max > 0 && src > u.Max) || (u.Min > 0 && src < u.Min) {
u.LastCycleStatus[server.ID] = struct{}{}
u.LastCycleStatus[server.ID] = false
} else {
u.LastCycleStatus[server.ID] = nil
u.LastCycleStatus[server.ID] = true
}
if cycleTransferStats.ServerName[server.ID] != server.Name {
cycleTransferStats.ServerName[server.ID] = server.Name
@@ -166,12 +166,12 @@ func (u *Rule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server,
}
if u.Type == "offline" && float64(time.Now().Unix())-src > 6 {
return struct{}{}
return false
} else if (u.Max > 0 && src > u.Max) || (u.Min > 0 && src < u.Min) {
return struct{}{}
return false
}
return nil
return true
}
// IsTransferDurationRule 判断该规则是否属于周期流量规则 属于则返回true