2021-06-21 09:30:42 -04:00
package model
2021-07-14 11:53:37 -04:00
import (
2024-08-23 23:11:06 -04:00
"slices"
2021-07-14 11:53:37 -04:00
"strings"
"time"
"gorm.io/gorm"
2024-08-10 22:35:19 -04:00
"github.com/naiba/nezha/pkg/utils"
2021-07-14 11:53:37 -04:00
)
2021-06-21 09:30:42 -04:00
const (
RuleCoverAll = iota
RuleCoverIgnoreAll
)
2021-07-14 11:53:37 -04:00
type NResult struct {
N uint64
}
2021-06-21 09:30:42 -04:00
type Rule struct {
// 指标类型, cpu、memory、swap、disk、net_in_speed、net_out_speed
// net_all_speed、transfer_in、transfer_out、transfer_all、offline
2021-07-14 11:53:37 -04:00
// transfer_in_cycle、transfer_out_cycle、transfer_all_cycle
Type string ` json:"type,omitempty" `
2021-08-15 04:38:05 -04:00
Min float64 ` json:"min,omitempty" ` // 最小阈值 (百分比、字节 kb ÷ 1024)
Max float64 ` json:"max,omitempty" ` // 最大阈值 (百分比、字节 kb ÷ 1024)
2022-02-28 21:19:23 -05:00
CycleStart * time . Time ` json:"cycle_start,omitempty" ` // 流量统计的开始时间
2022-01-13 00:46:41 -05:00
CycleInterval uint64 ` json:"cycle_interval,omitempty" ` // 流量统计周期
2022-02-28 21:19:23 -05:00
CycleUnit string ` json:"cycle_unit,omitempty" ` // 流量统计周期单位, 默认hour,可选(hour, day, week, month, year)
2021-07-14 11:53:37 -04:00
Duration uint64 ` json:"duration,omitempty" ` // 持续时间 (秒)
Cover uint64 ` json:"cover,omitempty" ` // 覆盖范围 RuleCoverAll/IgnoreAll
Ignore map [ uint64 ] bool ` json:"ignore,omitempty" ` // 覆盖范围的排除
// 只作为缓存使用,记录下次该检测的时间
2021-07-15 23:14:07 -04:00
NextTransferAt map [ uint64 ] time . Time ` json:"-" `
LastCycleStatus map [ uint64 ] interface { } ` json:"-" `
2021-06-21 09:30:42 -04:00
}
2021-08-15 04:38:05 -04:00
func percentage ( used , total uint64 ) float64 {
2021-06-21 09:30:42 -04:00
if total == 0 {
return 0
}
2021-08-15 04:38:05 -04:00
return float64 ( used ) * 100 / float64 ( total )
2021-06-21 09:30:42 -04:00
}
// Snapshot 未通过规则返回 struct{}{}, 通过返回 nil
2021-11-06 04:00:08 -04:00
func ( u * Rule ) Snapshot ( cycleTransferStats * CycleTransferStats , server * Server , db * gorm . DB ) interface { } {
2021-06-21 09:30:42 -04:00
// 监控全部但是排除了此服务器
if u . Cover == RuleCoverAll && u . Ignore [ server . ID ] {
return nil
}
// 忽略全部但是指定监控了此服务器
if u . Cover == RuleCoverIgnoreAll && ! u . Ignore [ server . ID ] {
return nil
}
2021-07-14 11:53:37 -04:00
// 循环区间流量检测 · 短期无需重复检测
if u . IsTransferDurationRule ( ) && u . NextTransferAt [ server . ID ] . After ( time . Now ( ) ) {
2021-07-15 23:14:07 -04:00
return u . LastCycleStatus [ server . ID ]
2021-07-14 11:53:37 -04:00
}
2021-08-15 04:38:05 -04:00
var src float64
2021-06-21 09:30:42 -04:00
switch u . Type {
case "cpu" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . CPU )
2024-10-24 08:41:47 -04:00
case "gpu_max" :
src = slices . Max ( server . State . GPU )
2021-06-21 09:30:42 -04:00
case "memory" :
src = percentage ( server . State . MemUsed , server . Host . MemTotal )
case "swap" :
src = percentage ( server . State . SwapUsed , server . Host . SwapTotal )
case "disk" :
src = percentage ( server . State . DiskUsed , server . Host . DiskTotal )
case "net_in_speed" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . NetInSpeed )
2021-06-21 09:30:42 -04:00
case "net_out_speed" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . NetOutSpeed )
2021-06-21 09:30:42 -04:00
case "net_all_speed" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . NetOutSpeed + server . State . NetOutSpeed )
2021-06-21 09:30:42 -04:00
case "transfer_in" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . NetInTransfer )
2021-06-21 09:30:42 -04:00
case "transfer_out" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . NetOutTransfer )
2021-06-21 09:30:42 -04:00
case "transfer_all" :
2021-08-15 04:38:05 -04:00
src = float64 ( server . State . NetOutTransfer + server . State . NetInTransfer )
2021-06-21 09:30:42 -04:00
case "offline" :
if server . LastActive . IsZero ( ) {
src = 0
} else {
2021-08-15 04:38:05 -04:00
src = float64 ( server . LastActive . Unix ( ) )
2021-06-21 09:30:42 -04:00
}
2021-07-14 11:53:37 -04:00
case "transfer_in_cycle" :
2024-08-10 22:35:19 -04:00
src = float64 ( utils . Uint64SubInt64 ( server . State . NetInTransfer , server . PrevTransferInSnapshot ) )
2022-01-13 00:46:41 -05:00
if u . CycleInterval != 0 {
2021-07-14 11:53:37 -04:00
var res NResult
2024-07-04 02:32:19 -04:00
db . Model ( & Transfer { } ) . Select ( "SUM(`in`) AS n" ) . Where ( "datetime(`created_at`) >= datetime(?) AND server_id = ?" , u . GetTransferDurationStart ( ) . UTC ( ) , server . ID ) . Scan ( & res )
2021-08-15 04:38:05 -04:00
src += float64 ( res . N )
2021-07-14 11:53:37 -04:00
}
case "transfer_out_cycle" :
2024-08-10 22:35:19 -04:00
src = float64 ( utils . Uint64SubInt64 ( server . State . NetOutTransfer , server . PrevTransferOutSnapshot ) )
2022-01-13 00:46:41 -05:00
if u . CycleInterval != 0 {
2021-07-14 11:53:37 -04:00
var res NResult
2024-07-04 02:32:19 -04:00
db . Model ( & Transfer { } ) . Select ( "SUM(`out`) AS n" ) . Where ( "datetime(`created_at`) >= datetime(?) AND server_id = ?" , u . GetTransferDurationStart ( ) . UTC ( ) , server . ID ) . Scan ( & res )
2021-08-15 04:38:05 -04:00
src += float64 ( res . N )
2021-07-14 11:53:37 -04:00
}
case "transfer_all_cycle" :
2024-08-10 22:35:19 -04:00
src = float64 ( utils . Uint64SubInt64 ( server . State . NetOutTransfer , server . PrevTransferOutSnapshot ) + utils . Uint64SubInt64 ( server . State . NetInTransfer , server . PrevTransferInSnapshot ) )
2022-01-13 00:46:41 -05:00
if u . CycleInterval != 0 {
2021-07-14 11:53:37 -04:00
var res NResult
2024-07-04 02:32:19 -04:00
db . Model ( & Transfer { } ) . Select ( "SUM(`in`+`out`) AS n" ) . Where ( "datetime(`created_at`) >= datetime(?) AND server_id = ?" , u . GetTransferDurationStart ( ) . UTC ( ) , server . ID ) . Scan ( & res )
2021-08-15 04:38:05 -04:00
src += float64 ( res . N )
2021-07-14 11:53:37 -04:00
}
2021-08-15 04:38:05 -04:00
case "load1" :
src = server . State . Load1
case "load5" :
src = server . State . Load5
case "load15" :
src = server . State . Load15
case "tcp_conn_count" :
src = float64 ( server . State . TcpConnCount )
case "udp_conn_count" :
src = float64 ( server . State . UdpConnCount )
case "process_count" :
src = float64 ( server . State . ProcessCount )
2024-07-03 00:34:45 -04:00
case "temperature_max" :
var temp [ ] float64
if server . State . Temperatures != nil {
for _ , tempStat := range server . State . Temperatures {
if tempStat . Temperature != 0 {
temp = append ( temp , tempStat . Temperature )
}
}
2024-08-23 23:11:06 -04:00
src = slices . Max ( temp )
2024-07-03 00:34:45 -04:00
}
2021-07-14 11:53:37 -04:00
}
// 循环区间流量检测 · 更新下次需要检测时间
if u . IsTransferDurationRule ( ) {
2021-11-06 06:52:10 -04:00
seconds := 1800 * ( ( u . Max - src ) / u . Max )
2021-07-14 11:53:37 -04:00
if seconds < 180 {
seconds = 180
}
if u . NextTransferAt == nil {
u . NextTransferAt = make ( map [ uint64 ] time . Time )
}
2021-07-15 23:14:07 -04:00
if u . LastCycleStatus == nil {
u . LastCycleStatus = make ( map [ uint64 ] interface { } )
}
2021-11-06 06:52:10 -04:00
u . NextTransferAt [ server . ID ] = time . Now ( ) . Add ( time . Second * time . Duration ( seconds ) )
2021-07-15 23:14:07 -04:00
if ( u . Max > 0 && src > u . Max ) || ( u . Min > 0 && src < u . Min ) {
u . LastCycleStatus [ server . ID ] = struct { } { }
} else {
u . LastCycleStatus [ server . ID ] = nil
}
2021-11-06 04:00:08 -04:00
if cycleTransferStats . ServerName [ server . ID ] != server . Name {
cycleTransferStats . ServerName [ server . ID ] = server . Name
}
cycleTransferStats . Transfer [ server . ID ] = uint64 ( src )
cycleTransferStats . NextUpdate [ server . ID ] = u . NextTransferAt [ server . ID ]
2021-12-28 10:47:19 -05:00
// 自动更新周期流量展示起止时间
cycleTransferStats . From = u . GetTransferDurationStart ( )
2022-01-11 05:15:43 -05:00
cycleTransferStats . To = u . GetTransferDurationEnd ( )
2021-06-21 09:30:42 -04:00
}
2021-08-15 04:38:05 -04:00
if u . Type == "offline" && float64 ( time . Now ( ) . Unix ( ) ) - src > 6 {
2021-06-21 09:30:42 -04:00
return struct { } { }
} else if ( u . Max > 0 && src > u . Max ) || ( u . Min > 0 && src < u . Min ) {
return struct { } { }
}
return nil
}
2021-07-14 11:53:37 -04:00
2022-04-11 10:51:02 -04:00
// IsTransferDurationRule 判断该规则是否属于周期流量规则 属于则返回true
2021-07-14 11:53:37 -04:00
func ( rule Rule ) IsTransferDurationRule ( ) bool {
return strings . HasSuffix ( rule . Type , "_cycle" )
}
2022-04-11 10:51:02 -04:00
// GetTransferDurationStart 获取周期流量的起始时间
2021-07-14 11:53:37 -04:00
func ( rule Rule ) GetTransferDurationStart ( ) time . Time {
2022-01-13 00:46:41 -05:00
// Accept uppercase and lowercase
unit := strings . ToLower ( rule . CycleUnit )
2022-02-28 21:19:23 -05:00
startTime := * rule . CycleStart
2022-01-13 00:46:41 -05:00
var nextTime time . Time
switch unit {
case "year" :
nextTime = startTime . AddDate ( int ( rule . CycleInterval ) , 0 , 0 )
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
nextTime = nextTime . AddDate ( int ( rule . CycleInterval ) , 0 , 0 )
}
case "month" :
nextTime = startTime . AddDate ( 0 , int ( rule . CycleInterval ) , 0 )
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
nextTime = nextTime . AddDate ( 0 , int ( rule . CycleInterval ) , 0 )
}
case "week" :
2022-02-28 21:19:23 -05:00
nextTime = startTime . AddDate ( 0 , 0 , 7 * int ( rule . CycleInterval ) )
2022-01-13 00:46:41 -05:00
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
2022-02-28 21:19:23 -05:00
nextTime = nextTime . AddDate ( 0 , 0 , 7 * int ( rule . CycleInterval ) )
2022-01-13 00:46:41 -05:00
}
case "day" :
nextTime = startTime . AddDate ( 0 , 0 , int ( rule . CycleInterval ) )
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
nextTime = nextTime . AddDate ( 0 , 0 , int ( rule . CycleInterval ) )
}
default :
// For hour unit or not set.
interval := 3600 * int64 ( rule . CycleInterval )
startTime = time . Unix ( rule . CycleStart . Unix ( ) + ( time . Now ( ) . Unix ( ) - rule . CycleStart . Unix ( ) ) / interval * interval , 0 )
2022-01-11 05:15:43 -05:00
}
2022-01-13 00:46:41 -05:00
return startTime
2022-01-11 05:15:43 -05:00
}
2022-04-11 10:51:02 -04:00
// GetTransferDurationEnd 获取周期流量结束时间
2022-01-11 05:15:43 -05:00
func ( rule Rule ) GetTransferDurationEnd ( ) time . Time {
2022-01-13 00:46:41 -05:00
// Accept uppercase and lowercase
unit := strings . ToLower ( rule . CycleUnit )
2022-02-28 21:19:23 -05:00
startTime := * rule . CycleStart
2022-01-13 00:46:41 -05:00
var nextTime time . Time
switch unit {
case "year" :
nextTime = startTime . AddDate ( int ( rule . CycleInterval ) , 0 , 0 )
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
nextTime = nextTime . AddDate ( int ( rule . CycleInterval ) , 0 , 0 )
}
case "month" :
nextTime = startTime . AddDate ( 0 , int ( rule . CycleInterval ) , 0 )
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
nextTime = nextTime . AddDate ( 0 , int ( rule . CycleInterval ) , 0 )
}
case "week" :
2022-02-28 21:19:23 -05:00
nextTime = startTime . AddDate ( 0 , 0 , 7 * int ( rule . CycleInterval ) )
2022-01-13 00:46:41 -05:00
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
2022-02-28 21:19:23 -05:00
nextTime = nextTime . AddDate ( 0 , 0 , 7 * int ( rule . CycleInterval ) )
2022-01-13 00:46:41 -05:00
}
case "day" :
nextTime = startTime . AddDate ( 0 , 0 , int ( rule . CycleInterval ) )
for time . Now ( ) . After ( nextTime ) {
startTime = nextTime
nextTime = nextTime . AddDate ( 0 , 0 , int ( rule . CycleInterval ) )
}
default :
// For hour unit or not set.
interval := 3600 * int64 ( rule . CycleInterval )
startTime = time . Unix ( rule . CycleStart . Unix ( ) + ( time . Now ( ) . Unix ( ) - rule . CycleStart . Unix ( ) ) / interval * interval , 0 )
2022-02-28 21:19:23 -05:00
nextTime = time . Unix ( startTime . Unix ( ) + interval , 0 )
2022-01-11 05:15:43 -05:00
}
2022-01-13 00:46:41 -05:00
return nextTime
2021-07-14 11:53:37 -04:00
}