diff --git a/config/config.go b/config/config.go index b0755a0e..6e9b0086 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,12 @@ type ConfigStruct struct { Trans transSection `yaml:"trans"` ContactKeys []contactKey `yaml:"contactKeys"` NotifyChannels []string `yaml:"notifyChannels"` + Tpl tplSection `yaml:"tpl"` +} + +type tplSection struct { + AlertRulePath string `yaml:"alertRulePath"` + DashboardPath string `yaml:"dashboardPath"` } type alertSection struct { @@ -116,6 +122,8 @@ func Parse() error { viper.SetDefault("trans.backend.prometheus.maxConcurrentQuery", 30) viper.SetDefault("trans.backend.prometheus.maxSamples", 50000000) viper.SetDefault("trans.backend.prometheus.maxFetchAllSeriesLimitMinute", 5) + viper.SetDefault("tpl.alertRulePath", "./etc/alert_rule") + viper.SetDefault("tpl.dashboardPath", "./etc/dashboard") err = viper.Unmarshal(&Config) if err != nil { diff --git a/etc/alert_rule/linux_host b/etc/alert_rule/linux_host new file mode 100644 index 00000000..aa33bdde --- /dev/null +++ b/etc/alert_rule/linux_host @@ -0,0 +1,176 @@ +[ + { + "name": "内存利用率大于75%", + "type": 0, + "expression": { + "trigger_conditions": [ + { + "optr": ">", + "func": "all", + "metric": "system_mem_pct_used", + "params": [], + "threshold": 75 + } + ], + "tags_filters": [] + }, + "status": 0, + "enable_stime": "00:00", + "enable_etime": "23:59", + "enable_days_of_week": "1 2 3 4 5 6 7", + "recovery_notify": 0, + "priority": 2, + "notify_channels": "sms email", + "runbook_url": "", + "note": "", + "create_at": 1625831623, + "alert_duration": 60, + "notify_users_detail": null, + "notify_groups_detail": null + }, + { + "name": "机器loadavg大于16", + "type": 0, + "expression": { + "trigger_conditions": [ + { + "optr": ">", + "func": "all", + "metric": "node_load1", + "params": [], + "threshold": 16 + } + ], + "tags_filters": [] + }, + "status": 0, + "enable_stime": "00:00", + "enable_etime": "23:59", + "enable_days_of_week": "1 2 3 4 5 6 7", + "recovery_notify": 0, + "priority": 1, + "notify_channels": "sms email", + "runbook_url": "", + "note": "", + "create_at": 1625831689, + "alert_duration": 60, + "notify_users_detail": null, + "notify_groups_detail": null + }, + { + "name": "磁盘利用率达到85%", + "type": 0, + "expression": { + "trigger_conditions": [ + { + "optr": ">", + "func": "all", + "metric": "system_disk_in_use", + "params": [], + "threshold": 85 + } + ], + "tags_filters": [] + }, + "status": 0, + "enable_stime": "00:00", + "enable_etime": "23:59", + "enable_days_of_week": "1 2 3 4 5 6 7", + "recovery_notify": 0, + "priority": 3, + "notify_channels": "email", + "runbook_url": "", + "note": "", + "create_at": 1625831738, + "alert_duration": 60, + "notify_users_detail": null, + "notify_groups_detail": null + }, + { + "name": "磁盘利用率达到88%", + "type": 0, + "expression": { + "trigger_conditions": [ + { + "optr": ">", + "func": "all", + "metric": "system_disk_in_use", + "params": [], + "threshold": 88 + } + ], + "tags_filters": [] + }, + "status": 0, + "enable_stime": "00:00", + "enable_etime": "23:59", + "enable_days_of_week": "1 2 3 4 5 6 7", + "recovery_notify": 0, + "priority": 2, + "notify_channels": "email sms", + "runbook_url": "", + "note": "", + "create_at": 1625831774, + "alert_duration": 60, + "notify_users_detail": null, + "notify_groups_detail": null + }, + { + "name": "磁盘利用率达到92%", + "type": 0, + "expression": { + "trigger_conditions": [ + { + "optr": ">", + "func": "all", + "metric": "system_disk_in_use", + "params": [], + "threshold": 88 + } + ], + "tags_filters": [] + }, + "status": 0, + "enable_stime": "00:00", + "enable_etime": "23:59", + "enable_days_of_week": "1 2 3 4 5 6 7", + "recovery_notify": 0, + "priority": 1, + "notify_channels": "email sms voice", + "runbook_url": "", + "note": "", + "create_at": 1625831752, + "alert_duration": 60, + "notify_users_detail": null, + "notify_groups_detail": null + }, + { + "name": "端口挂了", + "type": 0, + "expression": { + "trigger_conditions": [ + { + "optr": "<", + "func": "all", + "metric": "proc_port_listen", + "params": [], + "threshold": 1 + } + ], + "tags_filters": [] + }, + "status": 0, + "enable_stime": "00:00", + "enable_etime": "23:59", + "enable_days_of_week": "1 2 3 4 5 6 7", + "recovery_notify": 0, + "priority": 2, + "notify_channels": "sms email", + "runbook_url": "", + "note": "", + "create_at": 1625831564, + "alert_duration": 60, + "notify_users_detail": null, + "notify_groups_detail": null + } +] \ No newline at end of file diff --git a/etc/dashboard/linux_host b/etc/dashboard/linux_host new file mode 100644 index 00000000..11bf5454 --- /dev/null +++ b/etc/dashboard/linux_host @@ -0,0 +1,42 @@ +[ + { + "id": 0, + "name": "日常巡检大盘", + "tags": "", + "configs": "", + "chart_groups": [ + { + "id": 0, + "dashboard_id": 0, + "name": "Default chart group", + "weight": 0, + "charts": [ + { + "id": 158, + "group_id": 60, + "configs": "{\"name\":\"CPU使用率\",\"mode\":\"nightingale\",\"metric\":[\"system_cpu_used\"],\"tags\":{},\"layout\":{\"h\":2,\"w\":2,\"x\":0,\"y\":0,\"i\":\"0\"}}", + "weight": 0 + }, + { + "id": 159, + "group_id": 60, + "configs": "{\"name\":\"硬盘使用率\",\"mode\":\"nightingale\",\"metric\":[\"system_disk_in_use\"],\"tags\":{},\"layout\":{\"h\":2,\"w\":2,\"x\":6,\"y\":0,\"i\":\"1\"}}", + "weight": 0 + }, + { + "id": 160, + "group_id": 60, + "configs": "{\"name\":\"内存使用率\",\"mode\":\"nightingale\",\"metric\":[\"system_mem_pct_used\"],\"tags\":{},\"layout\":{\"h\":2,\"w\":2,\"x\":2,\"y\":0,\"i\":\"2\"}}", + "weight": 0 + }, + { + "id": 161, + "group_id": 60, + "configs": "{\"name\":\"IO使用率\",\"mode\":\"nightingale\",\"metric\":[\"system_io_util\"],\"tags\":{},\"layout\":{\"h\":2,\"w\":2,\"x\":4,\"y\":0,\"i\":\"3\"}}", + "weight": 0 + } + ] + } + ] + } +] \ No newline at end of file diff --git a/http/router.go b/http/router.go index f7bf089a..b4ab3b86 100644 --- a/http/router.go +++ b/http/router.go @@ -158,11 +158,17 @@ func configRoutes(r *gin.Engine) { pages.GET("/contact-channels", contactChannelsGet) pages.GET("/notify-channels", notifyChannelsGet) + pages.GET("/tpl/list", tplNameGets) + pages.GET("/tpl/content", tplGet) + + pages.GET("/status", Status) + pages.POST("/query", GetData) pages.POST("/tag-keys", GetTagKeys) pages.POST("/tag-values", GetTagValues) pages.POST("/tag-metrics", GetMetrics) pages.POST("/tag-pairs", GetTagPairs) + } // for thirdparty, do not expose location in nginx.conf @@ -279,6 +285,8 @@ func configRoutes(r *gin.Engine) { v1.POST("/push", PushData) + v1.GET("/status", Status) + v1.POST("/query", GetData) v1.GET("/check-promql", checkPromeQl) v1.POST("/tag-keys", GetTagKeys) diff --git a/http/router_status.go b/http/router_status.go new file mode 100644 index 00000000..4c6817e4 --- /dev/null +++ b/http/router_status.go @@ -0,0 +1,42 @@ +package http + +import ( + "time" + + "github.com/didi/nightingale/v5/models" + "github.com/gin-gonic/gin" +) + +func Status(c *gin.Context) { + var err error + data := make(map[string]int64) + data["user_total"], err = models.UserTotal("") + dangerous(err) + + data["user_group_total"], err = models.UserGroupTotal("") + dangerous(err) + + data["resource_total"], err = models.ResourceTotal("") + dangerous(err) + + data["alert_rule_total"], err = models.AlertRuleTotal("") + dangerous(err) + + data["dashboard_total"], err = models.DashboardCount("") + dangerous(err) + + now := time.Now().Unix() + stime := now - 24*3600 + data["event_total_day"], err = models.AlertEventTotal(stime, now, "", -1, -1) + dangerous(err) + + stime = now - 7*24*3600 + data["event_total_week"], err = models.AlertEventTotal(stime, now, "", -1, -1) + dangerous(err) + + stime = now - 30*24*3600 + data["event_total_month"], err = models.AlertEventTotal(stime, now, "", -1, -1) + dangerous(err) + + renderData(c, data, nil) +} diff --git a/http/router_tpl.go b/http/router_tpl.go new file mode 100644 index 00000000..ac7110f9 --- /dev/null +++ b/http/router_tpl.go @@ -0,0 +1,58 @@ +package http + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "path" + + "github.com/didi/nightingale/v5/config" + + "github.com/gin-gonic/gin" + "github.com/toolkits/pkg/file" +) + +func tplNameGets(c *gin.Context) { + tplType := queryStr(c, "tpl_type") + + var files []string + var err error + switch tplType { + case "alert_rule": + files, err = file.FilesUnder(config.Config.Tpl.AlertRulePath) + dangerous(err) + case "dashboard": + files, err = file.FilesUnder(config.Config.Tpl.DashboardPath) + dangerous(err) + default: + bomb(http.StatusBadRequest, "tpl type not found") + } + + renderData(c, files, err) +} + +func tplGet(c *gin.Context) { + tplName := path.Base(queryStr(c, "tpl_name")) + tplType := queryStr(c, "tpl_type") + + var filePath string + switch tplType { + case "alert_rule": + filePath = config.Config.Tpl.AlertRulePath + "/" + tplName + case "dashboard": + filePath = config.Config.Tpl.DashboardPath + "/" + tplName + default: + bomb(http.StatusBadRequest, "tpl type not found") + } + + if !file.IsExist(filePath) { + bomb(http.StatusBadRequest, "tpl not found") + } + + b, err := ioutil.ReadFile(filePath) + dangerous(err) + + var content interface{} + err = json.Unmarshal(b, &content) + renderData(c, content, err) +} diff --git a/models/dashboard.go b/models/dashboard.go index c66b71d8..229dd04c 100644 --- a/models/dashboard.go +++ b/models/dashboard.go @@ -26,6 +26,10 @@ func (d *Dashboard) TableName() string { } func (d *Dashboard) Validate() error { + if d.Name == "" { + return _e("Dashboard name is empty") + } + if str.Dangerous(d.Name) { return _e("Dashboard name has invalid characters") }