mirror of
https://github.com/ccfos/nightingale.git
synced 2026-03-03 14:38:55 +00:00
Compare commits
60 Commits
stable09
...
stable-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b3434e87 | ||
|
|
2576a0f815 | ||
|
|
0ac4bc7421 | ||
|
|
95e6ea98f4 | ||
|
|
dc60c74c0d | ||
|
|
a15adc196d | ||
|
|
f89ef04e85 | ||
|
|
f55cd9b32e | ||
|
|
305a898f8b | ||
|
|
60c31d8eb2 | ||
|
|
7da49a8c68 | ||
|
|
65b1410b09 | ||
|
|
3901671c0e | ||
|
|
9c02937e81 | ||
|
|
0a255ee33a | ||
|
|
8dc198b4b1 | ||
|
|
9696f63a71 | ||
|
|
03f56f73b4 | ||
|
|
7b415c91af | ||
|
|
2abf089444 | ||
|
|
e504dab359 | ||
|
|
989ed62e8d | ||
|
|
b7197d10eb | ||
|
|
f4de256388 | ||
|
|
3f5126923f | ||
|
|
5d3e70bc4c | ||
|
|
bb2c5202ad | ||
|
|
3acf3d7bf9 | ||
|
|
a79810b15d | ||
|
|
f61cb532f8 | ||
|
|
34a5a752f4 | ||
|
|
9be3deeebd | ||
|
|
2ceed84120 | ||
|
|
8fbe257090 | ||
|
|
ae35d780c6 | ||
|
|
4d2cdfce53 | ||
|
|
a0e4d0d46e | ||
|
|
dd07d04e2f | ||
|
|
61203e8b75 | ||
|
|
f24bc53c94 | ||
|
|
ef6abe3fdc | ||
|
|
461361d3d0 | ||
|
|
52b3afbd97 | ||
|
|
652439bb85 | ||
|
|
6f0c13d4e7 | ||
|
|
c9f46bad02 | ||
|
|
75146f3626 | ||
|
|
50aafbd73d | ||
|
|
b975cb3c9d | ||
|
|
11deb4ba26 | ||
|
|
ec927297d6 | ||
|
|
f476d7cd63 | ||
|
|
410f3bbceb | ||
|
|
2ad53d6862 | ||
|
|
fc392e4af1 | ||
|
|
9c83c7881a | ||
|
|
f1259d1dff | ||
|
|
d9d59b3205 | ||
|
|
d11cfb0278 | ||
|
|
5adcfc6eaa |
@@ -90,12 +90,11 @@
|
||||
- 推荐搜索关注夜莺公众号,第一时间获取社区动态:`夜莺监控Nightingale`
|
||||
- 日常问题交流:
|
||||
- QQ群:730841964
|
||||
- [加入微信群](https://download.flashcat.cloud/ulric/20240830175821.png)
|
||||
- [加入微信群](https://download.flashcat.cloud/ulric/20241022141621.png),如果二维码过期了,可以联系我(我的微信:`picobyte`)拉群,备注: `夜莺互助群`
|
||||
|
||||
## 广受关注
|
||||
[](https://star-history.com/#ccfos/nightingale&Date)
|
||||
|
||||
|
||||
## 社区共建
|
||||
- ❇️ 请阅读浏览[夜莺开源项目和社区治理架构草案](./doc/community-governance.md),真诚欢迎每一位用户、开发者、公司以及组织,使用夜莺监控、积极反馈 Bug、提交功能需求、分享最佳实践,共建专业、活跃的夜莺开源社区。
|
||||
- ❤️ 夜莺贡献者
|
||||
|
||||
@@ -60,10 +60,6 @@ func (a *Alert) PreCheck(configDir string) {
|
||||
a.Heartbeat.Interval = 1000
|
||||
}
|
||||
|
||||
if a.Heartbeat.EngineName == "" {
|
||||
a.Heartbeat.EngineName = "default"
|
||||
}
|
||||
|
||||
if a.EngineDelay == 0 {
|
||||
a.EngineDelay = 30
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
userCache := memsto.NewUserCache(ctx, syncStats)
|
||||
userGroupCache := memsto.NewUserGroupCache(ctx, syncStats)
|
||||
taskTplsCache := memsto.NewTaskTplCache(ctx)
|
||||
configCvalCache := memsto.NewCvalCache(ctx, syncStats)
|
||||
|
||||
promClients := prom.NewPromClient(ctx)
|
||||
tdengineClients := tdengine.NewTdengineClient(ctx, config.Alert.Heartbeat)
|
||||
@@ -70,7 +71,8 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
|
||||
Start(config.Alert, config.Pushgw, syncStats, alertStats, externalProcessors, targetCache, busiGroupCache, alertMuteCache, alertRuleCache, notifyConfigCache, taskTplsCache, dsCache, ctx, promClients, tdengineClients, userCache, userGroupCache)
|
||||
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP)
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP,
|
||||
configCvalCache.PrintBodyPaths, configCvalCache.PrintAccessLog)
|
||||
rt := router.New(config.HTTP, config.Alert, alertMuteCache, targetCache, busiGroupCache, alertStats, ctx, externalProcessors)
|
||||
|
||||
if config.Ibex.Enable {
|
||||
|
||||
@@ -5,18 +5,20 @@ import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
type AnomalyPoint struct {
|
||||
Key string `json:"key"`
|
||||
Labels model.Metric `json:"labels"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Value float64 `json:"value"`
|
||||
Severity int `json:"severity"`
|
||||
Triggered bool `json:"triggered"`
|
||||
Query string `json:"query"`
|
||||
Values string `json:"values"`
|
||||
Key string `json:"key"`
|
||||
Labels model.Metric `json:"labels"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Value float64 `json:"value"`
|
||||
Severity int `json:"severity"`
|
||||
Triggered bool `json:"triggered"`
|
||||
Query string `json:"query"`
|
||||
Values string `json:"values"`
|
||||
RecoverConfig models.RecoverConfig `json:"recover_config"`
|
||||
}
|
||||
|
||||
func NewAnomalyPoint(key string, labels map[string]string, ts int64, value float64, severity int) AnomalyPoint {
|
||||
|
||||
@@ -286,6 +286,7 @@ func (e *Dispatch) SendCallbacks(rule *models.AlertRule, notifyTarget *NotifyTar
|
||||
|
||||
uids := notifyTarget.ToUidList()
|
||||
urls := notifyTarget.ToCallbackList()
|
||||
whMap := notifyTarget.ToWebhookMap()
|
||||
for _, urlStr := range urls {
|
||||
if len(urlStr) == 0 {
|
||||
continue
|
||||
@@ -293,6 +294,11 @@ func (e *Dispatch) SendCallbacks(rule *models.AlertRule, notifyTarget *NotifyTar
|
||||
|
||||
cbCtx := sender.BuildCallBackContext(e.ctx, urlStr, rule, []*models.AlertCurEvent{event}, uids, e.userCache, e.alerting.WebhookBatchSend, e.Astats)
|
||||
|
||||
if wh, ok := whMap[cbCtx.CallBackURL]; ok && wh.Enable {
|
||||
logger.Debugf("SendCallbacks: webhook[%s] is in global conf.", cbCtx.CallBackURL)
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(urlStr, "${ibex}") {
|
||||
e.CallBacks[models.IbexDomain].CallBack(cbCtx)
|
||||
continue
|
||||
|
||||
@@ -100,8 +100,32 @@ func (s *NotifyTarget) ToWebhookList() []*models.Webhook {
|
||||
return webhooks
|
||||
}
|
||||
|
||||
func (s *NotifyTarget) ToWebhookMap() map[string]*models.Webhook {
|
||||
webhookMap := make(map[string]*models.Webhook, len(s.webhooks))
|
||||
for _, wh := range s.webhooks {
|
||||
if wh.Batch == 0 {
|
||||
wh.Batch = 1000
|
||||
}
|
||||
|
||||
if wh.Timeout == 0 {
|
||||
wh.Timeout = 10
|
||||
}
|
||||
|
||||
if wh.RetryCount == 0 {
|
||||
wh.RetryCount = 10
|
||||
}
|
||||
|
||||
if wh.RetryInterval == 0 {
|
||||
wh.RetryInterval = 10
|
||||
}
|
||||
|
||||
webhookMap[wh.Url] = wh
|
||||
}
|
||||
return webhookMap
|
||||
}
|
||||
|
||||
func (s *NotifyTarget) ToUidList() []int64 {
|
||||
uids := make([]int64, len(s.userMap))
|
||||
uids := make([]int64, 0, len(s.userMap))
|
||||
for uid, _ := range s.userMap {
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -160,13 +161,13 @@ func (arw *AlertRuleWorker) Eval() {
|
||||
now := time.Now().Unix()
|
||||
for _, point := range pointsMap {
|
||||
str := fmt.Sprintf("%v", point.Value)
|
||||
arw.processor.RecoverSingle(process.Hash(cachedRule.Id, arw.processor.DatasourceId(), point), now, &str)
|
||||
arw.processor.RecoverSingle(true, process.Hash(cachedRule.Id, arw.processor.DatasourceId(), point), now, &str)
|
||||
}
|
||||
} else {
|
||||
now := time.Now().Unix()
|
||||
for _, point := range recoverPoints {
|
||||
str := fmt.Sprintf("%v", point.Value)
|
||||
arw.processor.RecoverSingle(process.Hash(cachedRule.Id, arw.processor.DatasourceId(), point), now, &str)
|
||||
arw.processor.RecoverSingle(true, process.Hash(cachedRule.Id, arw.processor.DatasourceId(), point), now, &str)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +268,7 @@ func (arw *AlertRuleWorker) GetTdengineAnomalyPoint(rule *models.AlertRule, dsId
|
||||
if len(ruleQuery.Queries) > 0 {
|
||||
seriesStore := make(map[uint64]models.DataResp)
|
||||
// 将不同查询的 hash 索引分组存放
|
||||
seriesTagIndexes := make([]map[uint64][]uint64, 0)
|
||||
seriesTagIndexes := make(map[string]map[uint64][]uint64)
|
||||
|
||||
for _, query := range ruleQuery.Queries {
|
||||
seriesTagIndex := make(map[uint64][]uint64)
|
||||
@@ -292,7 +293,13 @@ func (arw *AlertRuleWorker) GetTdengineAnomalyPoint(rule *models.AlertRule, dsId
|
||||
// 此条日志很重要,是告警判断的现场值
|
||||
logger.Debugf("rule_eval rid:%d req:%+v resp:%+v", rule.Id, query, series)
|
||||
MakeSeriesMap(series, seriesTagIndex, seriesStore)
|
||||
seriesTagIndexes = append(seriesTagIndexes, seriesTagIndex)
|
||||
ref, err := GetQueryRef(query)
|
||||
if err != nil {
|
||||
logger.Warningf("rule_eval rid:%d query ref error: %v query:%+v", rule.Id, err, query)
|
||||
arw.processor.Stats.CounterRuleEvalErrorTotal.WithLabelValues(fmt.Sprintf("%v", arw.processor.DatasourceId()), GET_RULE_CONFIG).Inc()
|
||||
continue
|
||||
}
|
||||
seriesTagIndexes[ref] = seriesTagIndex
|
||||
}
|
||||
|
||||
points, recoverPoints = GetAnomalyPoint(rule.Id, ruleQuery, seriesTagIndexes, seriesStore)
|
||||
@@ -369,11 +376,6 @@ func (arw *AlertRuleWorker) GetHostAnomalyPoint(ruleConfig string) []common.Anom
|
||||
}
|
||||
m["ident"] = target.Ident
|
||||
|
||||
bg := arw.processor.BusiGroupCache.GetByBusiGroupId(target.GroupId)
|
||||
if bg != nil && bg.LabelEnable == 1 {
|
||||
m["busigroup"] = bg.LabelValue
|
||||
}
|
||||
|
||||
lst = append(lst, common.NewAnomalyPoint(trigger.Type, m, now, float64(now-target.UpdateAt), trigger.Severity))
|
||||
}
|
||||
case "offset":
|
||||
@@ -422,11 +424,6 @@ func (arw *AlertRuleWorker) GetHostAnomalyPoint(ruleConfig string) []common.Anom
|
||||
}
|
||||
m["ident"] = host
|
||||
|
||||
bg := arw.processor.BusiGroupCache.GetByBusiGroupId(target.GroupId)
|
||||
if bg != nil && bg.LabelEnable == 1 {
|
||||
m["busigroup"] = bg.LabelValue
|
||||
}
|
||||
|
||||
lst = append(lst, common.NewAnomalyPoint(trigger.Type, m, now, float64(offset), trigger.Severity))
|
||||
}
|
||||
case "pct_target_miss":
|
||||
@@ -455,7 +452,7 @@ func (arw *AlertRuleWorker) GetHostAnomalyPoint(ruleConfig string) []common.Anom
|
||||
return lst
|
||||
}
|
||||
|
||||
func GetAnomalyPoint(ruleId int64, ruleQuery models.RuleQuery, seriesTagIndexes []map[uint64][]uint64, seriesStore map[uint64]models.DataResp) ([]common.AnomalyPoint, []common.AnomalyPoint) {
|
||||
func GetAnomalyPoint(ruleId int64, ruleQuery models.RuleQuery, seriesTagIndexes map[string]map[uint64][]uint64, seriesStore map[uint64]models.DataResp) ([]common.AnomalyPoint, []common.AnomalyPoint) {
|
||||
points := []common.AnomalyPoint{}
|
||||
recoverPoints := []common.AnomalyPoint{}
|
||||
|
||||
@@ -463,61 +460,13 @@ func GetAnomalyPoint(ruleId int64, ruleQuery models.RuleQuery, seriesTagIndexes
|
||||
return points, recoverPoints
|
||||
}
|
||||
|
||||
if len(seriesTagIndexes) == 0 {
|
||||
return points, recoverPoints
|
||||
}
|
||||
|
||||
for _, trigger := range ruleQuery.Triggers {
|
||||
// seriesTagIndex 的 key 仅做分组使用,value 为每组 series 的 hash
|
||||
seriesTagIndex := make(map[uint64][]uint64)
|
||||
|
||||
if len(trigger.Joins) == 0 {
|
||||
// 没有 join 条件,走原逻辑
|
||||
last := seriesTagIndexes[0]
|
||||
for i := 1; i < len(seriesTagIndexes); i++ {
|
||||
last = originalJoin(last, seriesTagIndexes[i])
|
||||
}
|
||||
seriesTagIndex = last
|
||||
} else {
|
||||
// 有 join 条件,按条件依次合并
|
||||
if len(seriesTagIndexes) != len(trigger.Joins)+1 {
|
||||
logger.Errorf("rule_eval rid:%d queries' count: %d not match join condition's count: %d", ruleId, len(seriesTagIndexes), len(trigger.Joins))
|
||||
continue
|
||||
}
|
||||
|
||||
last := seriesTagIndexes[0]
|
||||
lastRehashed := rehashSet(last, seriesStore, trigger.Joins[0].On)
|
||||
for i := range trigger.Joins {
|
||||
cur := seriesTagIndexes[i+1]
|
||||
switch trigger.Joins[i].JoinType {
|
||||
case "original":
|
||||
last = originalJoin(last, cur)
|
||||
case "none":
|
||||
last = noneJoin(last, cur)
|
||||
case "cartesian":
|
||||
last = cartesianJoin(last, cur)
|
||||
case "inner_join":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = onJoin(lastRehashed, curRehashed, Inner)
|
||||
last = flatten(lastRehashed)
|
||||
case "left_join":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = onJoin(lastRehashed, curRehashed, Left)
|
||||
last = flatten(lastRehashed)
|
||||
case "right_join":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = onJoin(curRehashed, lastRehashed, Right)
|
||||
last = flatten(lastRehashed)
|
||||
case "left_exclude":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = exclude(lastRehashed, curRehashed)
|
||||
last = flatten(lastRehashed)
|
||||
case "right_exclude":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = exclude(curRehashed, lastRehashed)
|
||||
last = flatten(lastRehashed)
|
||||
default:
|
||||
logger.Warningf("rule_eval rid:%d join type:%s not support", ruleId, trigger.Joins[i].JoinType)
|
||||
}
|
||||
}
|
||||
seriesTagIndex = last
|
||||
}
|
||||
seriesTagIndex := ProcessJoins(ruleId, trigger, seriesTagIndexes, seriesStore)
|
||||
|
||||
for _, seriesHash := range seriesTagIndex {
|
||||
sort.Slice(seriesHash, func(i, j int) bool {
|
||||
@@ -564,23 +513,37 @@ func GetAnomalyPoint(ruleId int64, ruleQuery models.RuleQuery, seriesTagIndexes
|
||||
}
|
||||
|
||||
point := common.AnomalyPoint{
|
||||
Key: sample.MetricName(),
|
||||
Labels: sample.Metric,
|
||||
Timestamp: int64(ts),
|
||||
Value: value,
|
||||
Values: values,
|
||||
Severity: trigger.Severity,
|
||||
Triggered: isTriggered,
|
||||
Query: fmt.Sprintf("query:%+v trigger:%+v", ruleQuery.Queries, trigger),
|
||||
Key: sample.MetricName(),
|
||||
Labels: sample.Metric,
|
||||
Timestamp: int64(ts),
|
||||
Value: value,
|
||||
Values: values,
|
||||
Severity: trigger.Severity,
|
||||
Triggered: isTriggered,
|
||||
Query: fmt.Sprintf("query:%+v trigger:%+v", ruleQuery.Queries, trigger),
|
||||
RecoverConfig: trigger.RecoverConfig,
|
||||
}
|
||||
|
||||
if sample.Query != "" {
|
||||
point.Query = sample.Query
|
||||
}
|
||||
|
||||
// 恢复条件判断经过讨论是只在表达式模式下支持,表达式模式会通过 isTriggered 判断是告警点还是恢复点
|
||||
// 1. 不设置恢复判断,满足恢复条件产生 recoverPoint 恢复,无数据不产生 anomalyPoint 恢复
|
||||
// 2. 设置满足条件才恢复,仅可通过产生 recoverPoint 恢复,不能通过不产生 anomalyPoint 恢复
|
||||
// 3. 设置无数据不恢复,仅可通过产生 recoverPoint 恢复,不产生 anomalyPoint 恢复
|
||||
if isTriggered {
|
||||
points = append(points, point)
|
||||
} else {
|
||||
switch trigger.RecoverConfig.JudgeType {
|
||||
case models.Origin:
|
||||
// 对齐原实现 do nothing
|
||||
case models.RecoverOnCondition:
|
||||
// 额外判断恢复条件,满足才恢复
|
||||
fulfill := parser.Calc(trigger.RecoverConfig.RecoverExp, m)
|
||||
if !fulfill {
|
||||
continue
|
||||
}
|
||||
}
|
||||
recoverPoints = append(recoverPoints, point)
|
||||
}
|
||||
}
|
||||
@@ -613,7 +576,7 @@ func flatten(rehashed map[uint64][][]uint64) map[uint64][]uint64 {
|
||||
// [[A3{data_base=2, table=board},B2{data_base=2, table=alert}],[A4{data_base=2, table=alert},B2{data_base=2, table=alert}]]
|
||||
func onJoin(reHashTagIndex1 map[uint64][][]uint64, reHashTagIndex2 map[uint64][][]uint64, joinType JoinType) map[uint64][][]uint64 {
|
||||
reHashTagIndex := make(map[uint64][][]uint64)
|
||||
for rehash, _ := range reHashTagIndex1 {
|
||||
for rehash := range reHashTagIndex1 {
|
||||
if _, ok := reHashTagIndex2[rehash]; ok {
|
||||
// 若有 rehash 相同的记录,两两合并
|
||||
for i1 := range reHashTagIndex1[rehash] {
|
||||
@@ -656,6 +619,7 @@ func rehashSet(seriesTagIndex1 map[uint64][]uint64, seriesStore map[uint64]model
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
rehash := hash.GetTargetTagHash(series.Metric, on)
|
||||
if _, ok := reHashTagIndex[rehash]; !ok {
|
||||
reHashTagIndex[rehash] = make([][]uint64, 0)
|
||||
@@ -747,3 +711,100 @@ func mergeNewArray(arg ...[]uint64) []uint64 {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func ProcessJoins(ruleId int64, trigger models.Trigger, seriesTagIndexes map[string]map[uint64][]uint64, seriesStore map[uint64]models.DataResp) map[uint64][]uint64 {
|
||||
last := make(map[uint64][]uint64)
|
||||
if len(seriesTagIndexes) == 0 {
|
||||
return last
|
||||
}
|
||||
|
||||
if len(trigger.Joins) == 0 {
|
||||
idx := 0
|
||||
for _, seriesTagIndex := range seriesTagIndexes {
|
||||
if idx == 0 {
|
||||
last = seriesTagIndex
|
||||
} else {
|
||||
last = originalJoin(last, seriesTagIndex)
|
||||
}
|
||||
idx++
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
// 有 join 条件,按条件依次合并
|
||||
if len(seriesTagIndexes) < len(trigger.Joins)+1 {
|
||||
logger.Errorf("rule_eval rid:%d queries' count: %d not match join condition's count: %d", ruleId, len(seriesTagIndexes), len(trigger.Joins))
|
||||
return nil
|
||||
}
|
||||
|
||||
last = seriesTagIndexes[trigger.JoinRef]
|
||||
lastRehashed := rehashSet(last, seriesStore, trigger.Joins[0].On)
|
||||
for i := range trigger.Joins {
|
||||
cur := seriesTagIndexes[trigger.Joins[i].Ref]
|
||||
switch trigger.Joins[i].JoinType {
|
||||
case "original":
|
||||
last = originalJoin(last, cur)
|
||||
case "none":
|
||||
last = noneJoin(last, cur)
|
||||
case "cartesian":
|
||||
last = cartesianJoin(last, cur)
|
||||
case "inner_join":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = onJoin(lastRehashed, curRehashed, Inner)
|
||||
last = flatten(lastRehashed)
|
||||
case "left_join":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = onJoin(lastRehashed, curRehashed, Left)
|
||||
last = flatten(lastRehashed)
|
||||
case "right_join":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = onJoin(curRehashed, lastRehashed, Right)
|
||||
last = flatten(lastRehashed)
|
||||
case "left_exclude":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = exclude(lastRehashed, curRehashed)
|
||||
last = flatten(lastRehashed)
|
||||
case "right_exclude":
|
||||
curRehashed := rehashSet(cur, seriesStore, trigger.Joins[i].On)
|
||||
lastRehashed = exclude(curRehashed, lastRehashed)
|
||||
last = flatten(lastRehashed)
|
||||
default:
|
||||
logger.Warningf("rule_eval rid:%d join type:%s not support", ruleId, trigger.Joins[i].JoinType)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
func GetQueryRef(query interface{}) (string, error) {
|
||||
// 首先检查是否为 map
|
||||
if m, ok := query.(map[string]interface{}); ok {
|
||||
if ref, exists := m["ref"]; exists {
|
||||
if refStr, ok := ref.(string); ok {
|
||||
return refStr, nil
|
||||
}
|
||||
return "", fmt.Errorf("ref 字段不是字符串类型")
|
||||
}
|
||||
return "", fmt.Errorf("query 中没有找到 ref 字段")
|
||||
}
|
||||
|
||||
// 如果不是 map,则按原来的方式处理结构体
|
||||
v := reflect.ValueOf(query)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
return "", fmt.Errorf("query not a struct or map")
|
||||
}
|
||||
|
||||
refField := v.FieldByName("Ref")
|
||||
if !refField.IsValid() {
|
||||
return "", fmt.Errorf("not find ref field")
|
||||
}
|
||||
|
||||
if refField.Kind() != reflect.String {
|
||||
return "", fmt.Errorf("ref not a string")
|
||||
}
|
||||
|
||||
return refField.String(), nil
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func BgNotMatchMuteStrategy(rule *models.AlertRule, event *models.AlertCurEvent,
|
||||
target, exists := targetCache.Get(ident)
|
||||
// 对于包含ident的告警事件,check一下ident所属bg和rule所属bg是否相同
|
||||
// 如果告警规则选择了只在本BG生效,那其他BG的机器就不能因此规则产生告警
|
||||
if exists && target.GroupId != rule.GroupId {
|
||||
if exists && !target.MatchGroupId(rule.GroupId) {
|
||||
logger.Debugf("[%s] mute: rule_eval:%d cluster:%s", "BgNotMatchMuteStrategy", rule.Id, event.Cluster)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -170,7 +170,9 @@ func (p *Processor) Handle(anomalyPoints []common.AnomalyPoint, from string, inh
|
||||
p.handleEvent(events)
|
||||
}
|
||||
|
||||
p.HandleRecover(alertingKeys, now, inhibit)
|
||||
if from == "inner" {
|
||||
p.HandleRecover(alertingKeys, now, inhibit)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Processor) BuildEvent(anomalyPoint common.AnomalyPoint, from string, now int64) *models.AlertCurEvent {
|
||||
@@ -211,6 +213,7 @@ func (p *Processor) BuildEvent(anomalyPoint common.AnomalyPoint, from string, no
|
||||
event.Severity = anomalyPoint.Severity
|
||||
event.ExtraConfig = p.rule.ExtraConfigJSON
|
||||
event.PromQl = anomalyPoint.Query
|
||||
event.RecoverConfig = anomalyPoint.RecoverConfig
|
||||
|
||||
if p.target != "" {
|
||||
if pt, exist := p.TargetCache.Get(p.target); exist {
|
||||
@@ -290,7 +293,7 @@ func (p *Processor) HandleRecover(alertingKeys map[string]struct{}, now int64, i
|
||||
}
|
||||
|
||||
hashArr := make([]string, 0, len(alertingKeys))
|
||||
for hash := range p.fires.GetAll() {
|
||||
for hash, _ := range p.fires.GetAll() {
|
||||
if _, has := alertingKeys[hash]; has {
|
||||
continue
|
||||
}
|
||||
@@ -309,7 +312,7 @@ func (p *Processor) HandleRecoverEvent(hashArr []string, now int64, inhibit bool
|
||||
|
||||
if !inhibit {
|
||||
for _, hash := range hashArr {
|
||||
p.RecoverSingle(hash, now, nil)
|
||||
p.RecoverSingle(false, hash, now, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -337,11 +340,11 @@ func (p *Processor) HandleRecoverEvent(hashArr []string, now int64, inhibit bool
|
||||
}
|
||||
|
||||
for _, event := range eventMap {
|
||||
p.RecoverSingle(event.Hash, now, nil)
|
||||
p.RecoverSingle(false, event.Hash, now, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Processor) RecoverSingle(hash string, now int64, value *string, values ...string) {
|
||||
func (p *Processor) RecoverSingle(byRecover bool, hash string, now int64, value *string, values ...string) {
|
||||
cachedRule := p.rule
|
||||
if cachedRule == nil {
|
||||
return
|
||||
@@ -367,6 +370,12 @@ func (p *Processor) RecoverSingle(hash string, now int64, value *string, values
|
||||
}
|
||||
}
|
||||
|
||||
// 如果设置了恢复条件,则不能在此处恢复,必须依靠 recoverPoint 来恢复
|
||||
if event.RecoverConfig.JudgeType != models.Origin && !byRecover {
|
||||
logger.Debugf("rule_eval:%s event:%v not recover", p.Key(), event)
|
||||
return
|
||||
}
|
||||
|
||||
if value != nil {
|
||||
event.TriggerValue = *value
|
||||
if len(values) > 0 {
|
||||
@@ -527,6 +536,11 @@ func (p *Processor) RecoverAlertCurEventFromDb() {
|
||||
}
|
||||
|
||||
event.DB2Mem()
|
||||
target, exists := p.TargetCache.Get(event.TargetIdent)
|
||||
if exists {
|
||||
event.Target = target
|
||||
}
|
||||
|
||||
fireMap[event.Hash] = event
|
||||
e := *event
|
||||
pendingsUseByRecoverMap[event.Hash] = &e
|
||||
|
||||
@@ -34,7 +34,7 @@ func (rt *Router) pushEventToQueue(c *gin.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
arr := strings.Split(pair, "=")
|
||||
arr := strings.SplitN(pair, "=", 2)
|
||||
if len(arr) != 2 {
|
||||
continue
|
||||
}
|
||||
@@ -129,7 +129,7 @@ func (rt *Router) makeEvent(c *gin.Context) {
|
||||
} else {
|
||||
for _, vector := range events[i].AnomalyPoints {
|
||||
readableString := vector.ReadableValue()
|
||||
go ruleWorker.RecoverSingle(process.Hash(events[i].RuleId, events[i].DatasourceId, vector), vector.Timestamp, &readableString)
|
||||
go ruleWorker.RecoverSingle(false, process.Hash(events[i].RuleId, events[i].DatasourceId, vector), vector.Timestamp, &readableString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func InitEmailSender(ctx *ctx.Context, ncc *memsto.NotifyConfigCacheType) {
|
||||
mailch = make(chan *EmailContext, 100000)
|
||||
go updateSmtp(ctx, ncc)
|
||||
smtpConfig = ncc.GetSMTP()
|
||||
startEmailSender(ctx, smtpConfig)
|
||||
go startEmailSender(ctx, smtpConfig)
|
||||
}
|
||||
|
||||
func updateSmtp(ctx *ctx.Context, ncc *memsto.NotifyConfigCacheType) {
|
||||
@@ -143,6 +143,7 @@ func startEmailSender(ctx *ctx.Context, smtp aconf.SMTPConfig) {
|
||||
conf := smtp
|
||||
if conf.Host == "" || conf.Port == 0 {
|
||||
logger.Warning("SMTP configurations invalid")
|
||||
<-mailQuit
|
||||
return
|
||||
}
|
||||
logger.Infof("start email sender... conf.Host:%+v,conf.Port:%+v", conf.Host, conf.Port)
|
||||
|
||||
@@ -56,8 +56,8 @@ const (
|
||||
Triggered = "triggered"
|
||||
)
|
||||
|
||||
var (
|
||||
body = feishuCard{
|
||||
func createFeishuCardBody() feishuCard {
|
||||
return feishuCard{
|
||||
feishu: feishu{Msgtype: "interactive"},
|
||||
Card: Cards{
|
||||
Config: Conf{
|
||||
@@ -90,7 +90,7 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func (fs *FeishuCardSender) CallBack(ctx CallBackContext) {
|
||||
if len(ctx.Events) == 0 || len(ctx.CallBackURL) == 0 {
|
||||
@@ -121,6 +121,7 @@ func (fs *FeishuCardSender) CallBack(ctx CallBackContext) {
|
||||
}
|
||||
|
||||
SendTitle := fmt.Sprintf("🔔 %s", ctx.Events[0].RuleName)
|
||||
body := createFeishuCardBody()
|
||||
body.Card.Header.Title.Content = SendTitle
|
||||
body.Card.Header.Template = color
|
||||
body.Card.Elements[0].Text.Content = message
|
||||
@@ -153,6 +154,7 @@ func (fs *FeishuCardSender) Send(ctx MessageContext) {
|
||||
}
|
||||
|
||||
SendTitle := fmt.Sprintf("🔔 %s", ctx.Events[0].RuleName)
|
||||
body := createFeishuCardBody()
|
||||
body.Card.Header.Title.Content = SendTitle
|
||||
body.Card.Header.Template = color
|
||||
body.Card.Elements[0].Text.Content = message
|
||||
|
||||
@@ -108,7 +108,7 @@ func CallIbex(ctx *ctx.Context, id int64, host string,
|
||||
continue
|
||||
}
|
||||
|
||||
arr := strings.Split(pair, "=")
|
||||
arr := strings.SplitN(pair, "=", 2)
|
||||
if len(arr) != 2 {
|
||||
continue
|
||||
}
|
||||
@@ -181,10 +181,15 @@ func canDoIbex(username string, tpl *models.TaskTpl, host string, targetCache *m
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return target.GroupId == tpl.GroupId, nil
|
||||
return target.MatchGroupId(tpl.GroupId), nil
|
||||
}
|
||||
|
||||
func TaskAdd(f models.TaskForm, authUser string, isCenter bool) (int64, error) {
|
||||
if storage.Cache == nil {
|
||||
logger.Warning("event_callback_ibex: redis cache is nil")
|
||||
return 0, fmt.Errorf("redis cache is nil")
|
||||
}
|
||||
|
||||
hosts := cleanHosts(f.Hosts)
|
||||
if len(hosts) == 0 {
|
||||
return 0, fmt.Errorf("arg(hosts) empty")
|
||||
|
||||
@@ -42,6 +42,7 @@ func (fs *LarkCardSender) CallBack(ctx CallBackContext) {
|
||||
}
|
||||
|
||||
SendTitle := fmt.Sprintf("🔔 %s", ctx.Events[0].RuleName)
|
||||
body := createFeishuCardBody()
|
||||
body.Card.Header.Title.Content = SendTitle
|
||||
body.Card.Header.Template = color
|
||||
body.Card.Elements[0].Text.Content = message
|
||||
@@ -74,6 +75,7 @@ func (fs *LarkCardSender) Send(ctx MessageContext) {
|
||||
}
|
||||
|
||||
SendTitle := fmt.Sprintf("🔔 %s", ctx.Events[0].RuleName)
|
||||
body := createFeishuCardBody()
|
||||
body.Card.Header.Title.Content = SendTitle
|
||||
body.Card.Header.Template = color
|
||||
body.Card.Elements[0].Text.Content = message
|
||||
|
||||
@@ -166,10 +166,10 @@ func StartConsumer(ctx *ctx.Context, queue *WebhookQueue, popSize int, webhook *
|
||||
retryCount := 0
|
||||
for retryCount < webhook.RetryCount {
|
||||
needRetry, res, err := sendWebhook(webhook, events, stats)
|
||||
go RecordEvents(ctx, webhook, events, stats, res, err)
|
||||
if !needRetry {
|
||||
break
|
||||
}
|
||||
go RecordEvents(ctx, webhook, events, stats, res, err)
|
||||
retryCount++
|
||||
time.Sleep(time.Second * time.Duration(webhook.RetryInterval) * time.Duration(retryCount))
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ type Center struct {
|
||||
FlashDuty FlashDuty
|
||||
EventHistoryGroupView bool
|
||||
CleanNotifyRecordDay int
|
||||
MigrateBusiGroupLabel bool
|
||||
}
|
||||
|
||||
type Plugin struct {
|
||||
|
||||
@@ -72,7 +72,7 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
integration.Init(ctx, config.Center.BuiltinIntegrationsDir)
|
||||
go integration.Init(ctx, config.Center.BuiltinIntegrationsDir)
|
||||
var redis storage.Redis
|
||||
redis, err = storage.NewRedis(config.Redis)
|
||||
if err != nil {
|
||||
@@ -95,6 +95,7 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
userCache := memsto.NewUserCache(ctx, syncStats)
|
||||
userGroupCache := memsto.NewUserGroupCache(ctx, syncStats)
|
||||
taskTplCache := memsto.NewTaskTplCache(ctx)
|
||||
configCvalCache := memsto.NewCvalCache(ctx, syncStats)
|
||||
|
||||
sso := sso.Init(config.Center, ctx, configCache)
|
||||
promClients := prom.NewPromClient(ctx)
|
||||
@@ -110,11 +111,14 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
go cron.CleanNotifyRecord(ctx, config.Center.CleanNotifyRecordDay)
|
||||
|
||||
alertrtRouter := alertrt.New(config.HTTP, config.Alert, alertMuteCache, targetCache, busiGroupCache, alertStats, ctx, externalProcessors)
|
||||
centerRouter := centerrt.New(config.HTTP, config.Center, config.Alert, config.Ibex, cconf.Operations, dsCache, notifyConfigCache, promClients, tdengineClients,
|
||||
centerRouter := centerrt.New(config.HTTP, config.Center, config.Alert, config.Ibex,
|
||||
cconf.Operations, dsCache, notifyConfigCache, promClients, tdengineClients,
|
||||
redis, sso, ctx, metas, idents, targetCache, userCache, userGroupCache)
|
||||
pushgwRouter := pushgwrt.New(config.HTTP, config.Pushgw, config.Alert, targetCache, busiGroupCache, idents, metas, writers, ctx)
|
||||
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP)
|
||||
go models.MigrateBg(ctx, pushgwRouter.Pushgw.BusiGroupLabelKey)
|
||||
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP, configCvalCache.PrintBodyPaths, configCvalCache.PrintAccessLog)
|
||||
|
||||
centerRouter.Config(r)
|
||||
alertrtRouter.Config(r)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/ccfos/nightingale/v6/conf"
|
||||
_ "github.com/ccfos/nightingale/v6/front/statik"
|
||||
"github.com/ccfos/nightingale/v6/memsto"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/aop"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/httpx"
|
||||
@@ -51,9 +52,14 @@ type Router struct {
|
||||
UserGroupCache *memsto.UserGroupCacheType
|
||||
Ctx *ctx.Context
|
||||
HeartbeatHook HeartbeatHookFunc
|
||||
TargetDeleteHook models.TargetDeleteHookFunc
|
||||
}
|
||||
|
||||
func New(httpConfig httpx.Config, center cconf.Center, alert aconf.Alert, ibex conf.Ibex, operations cconf.Operation, ds *memsto.DatasourceCacheType, ncc *memsto.NotifyConfigCacheType, pc *prom.PromClientMap, tdendgineClients *tdengine.TdengineClientMap, redis storage.Redis, sso *sso.SsoClient, ctx *ctx.Context, metaSet *metas.Set, idents *idents.Set, tc *memsto.TargetCacheType, uc *memsto.UserCacheType, ugc *memsto.UserGroupCacheType) *Router {
|
||||
func New(httpConfig httpx.Config, center cconf.Center, alert aconf.Alert, ibex conf.Ibex,
|
||||
operations cconf.Operation, ds *memsto.DatasourceCacheType, ncc *memsto.NotifyConfigCacheType,
|
||||
pc *prom.PromClientMap, tdendgineClients *tdengine.TdengineClientMap, redis storage.Redis,
|
||||
sso *sso.SsoClient, ctx *ctx.Context, metaSet *metas.Set, idents *idents.Set,
|
||||
tc *memsto.TargetCacheType, uc *memsto.UserCacheType, ugc *memsto.UserGroupCacheType) *Router {
|
||||
return &Router{
|
||||
HTTP: httpConfig,
|
||||
Center: center,
|
||||
@@ -73,9 +79,14 @@ func New(httpConfig httpx.Config, center cconf.Center, alert aconf.Alert, ibex c
|
||||
UserGroupCache: ugc,
|
||||
Ctx: ctx,
|
||||
HeartbeatHook: func(ident string) map[string]interface{} { return nil },
|
||||
TargetDeleteHook: emptyDeleteHook,
|
||||
}
|
||||
}
|
||||
|
||||
func emptyDeleteHook(ctx *ctx.Context, idents []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func stat() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
@@ -276,7 +287,7 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
pages.POST("/targets/tags", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetBindTagsByFE)
|
||||
pages.DELETE("/targets/tags", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetUnbindTagsByFE)
|
||||
pages.PUT("/targets/note", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetUpdateNote)
|
||||
pages.PUT("/targets/bgid", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetUpdateBgid)
|
||||
pages.PUT("/targets/bgids", rt.auth(), rt.user(), rt.perm("/targets/put"), rt.targetBindBgids)
|
||||
|
||||
pages.POST("/builtin-cate-favorite", rt.auth(), rt.user(), rt.builtinCateFavoriteAdd)
|
||||
pages.DELETE("/builtin-cate-favorite/:name", rt.auth(), rt.user(), rt.builtinCateFavoriteDel)
|
||||
@@ -297,6 +308,7 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
pages.POST("/busi-group/:id/board/:bid/clone", rt.auth(), rt.user(), rt.perm("/dashboards/add"), rt.bgrw(), rt.boardClone)
|
||||
pages.POST("/busi-groups/boards/clones", rt.auth(), rt.user(), rt.perm("/dashboards/add"), rt.boardBatchClone)
|
||||
|
||||
pages.GET("/boards", rt.auth(), rt.user(), rt.boardGetsByBids)
|
||||
pages.GET("/board/:bid", rt.boardGet)
|
||||
pages.GET("/board/:bid/pure", rt.boardPureGet)
|
||||
pages.PUT("/board/:bid", rt.auth(), rt.user(), rt.perm("/dashboards/put"), rt.boardPut)
|
||||
@@ -463,6 +475,7 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
pages.GET("/builtin-payload/:id", rt.auth(), rt.user(), rt.perm("/built-in-components"), rt.builtinPayloadGet)
|
||||
pages.PUT("/builtin-payloads", rt.auth(), rt.user(), rt.perm("/built-in-components/put"), rt.builtinPayloadsPut)
|
||||
pages.DELETE("/builtin-payloads", rt.auth(), rt.user(), rt.perm("/built-in-components/del"), rt.builtinPayloadsDel)
|
||||
pages.GET("/builtin-payload", rt.auth(), rt.user(), rt.builtinPayloadsGetByUUIDOrID)
|
||||
}
|
||||
|
||||
r.GET("/api/n9e/versions", func(c *gin.Context) {
|
||||
@@ -539,6 +552,7 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
service.GET("/config/:id", rt.configGet)
|
||||
service.GET("/configs", rt.configsGet)
|
||||
service.GET("/config", rt.configGetByKey)
|
||||
service.GET("/all-configs", rt.configGetAll)
|
||||
service.PUT("/configs", rt.configsPut)
|
||||
service.POST("/configs", rt.configsPost)
|
||||
service.DELETE("/configs", rt.configsDel)
|
||||
@@ -559,6 +573,8 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
service.POST("/notify-record", rt.notificationRecordAdd)
|
||||
|
||||
service.GET("/alert-cur-events-del-by-hash", rt.alertCurEventDelByHash)
|
||||
|
||||
service.POST("/center/heartbeat", rt.heartbeat)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,8 @@ func (rt *Router) alertCurEventsCard(c *gin.Context) {
|
||||
ginx.Dangerous(err)
|
||||
|
||||
// 最多获取50000个,获取太多也没啥意义
|
||||
list, err := models.AlertCurEventGets(rt.Ctx, prods, bgids, stime, etime, severity, dsIds, cates, query, 50000, 0)
|
||||
list, err := models.AlertCurEventsGet(rt.Ctx, prods, bgids, stime, etime, severity, dsIds,
|
||||
cates, 0, query, 50000, 0)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
cardmap := make(map[string]*AlertCard)
|
||||
@@ -162,13 +163,17 @@ func (rt *Router) alertCurEventsList(c *gin.Context) {
|
||||
cates = strings.Split(cate, ",")
|
||||
}
|
||||
|
||||
ruleId := ginx.QueryInt64(c, "rid", 0)
|
||||
|
||||
bgids, err := GetBusinessGroupIds(c, rt.Ctx, rt.Center.EventHistoryGroupView)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
total, err := models.AlertCurEventTotal(rt.Ctx, prods, bgids, stime, etime, severity, dsIds, cates, query)
|
||||
total, err := models.AlertCurEventTotal(rt.Ctx, prods, bgids, stime, etime, severity, dsIds,
|
||||
cates, ruleId, query)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
list, err := models.AlertCurEventGets(rt.Ctx, prods, bgids, stime, etime, severity, dsIds, cates, query, limit, ginx.Offset(c, limit))
|
||||
list, err := models.AlertCurEventsGet(rt.Ctx, prods, bgids, stime, etime, severity, dsIds,
|
||||
cates, ruleId, query, limit, ginx.Offset(c, limit))
|
||||
ginx.Dangerous(err)
|
||||
|
||||
cache := make(map[int64]*models.UserGroup)
|
||||
@@ -201,7 +206,9 @@ func (rt *Router) checkCurEventBusiGroupRWPermission(c *gin.Context, ids []int64
|
||||
for i := 0; i < len(ids); i++ {
|
||||
event, err := models.AlertCurEventGetById(rt.Ctx, ids[i])
|
||||
ginx.Dangerous(err)
|
||||
|
||||
if event == nil {
|
||||
continue
|
||||
}
|
||||
if _, has := set[event.GroupId]; !has {
|
||||
rt.bgrwCheck(c, event.GroupId)
|
||||
set[event.GroupId] = struct{}{}
|
||||
|
||||
@@ -54,13 +54,17 @@ func (rt *Router) alertHisEventsList(c *gin.Context) {
|
||||
cates = strings.Split(cate, ",")
|
||||
}
|
||||
|
||||
ruleId := ginx.QueryInt64(c, "rid", 0)
|
||||
|
||||
bgids, err := GetBusinessGroupIds(c, rt.Ctx, rt.Center.EventHistoryGroupView)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
total, err := models.AlertHisEventTotal(rt.Ctx, prods, bgids, stime, etime, severity, recovered, dsIds, cates, query)
|
||||
total, err := models.AlertHisEventTotal(rt.Ctx, prods, bgids, stime, etime, severity,
|
||||
recovered, dsIds, cates, ruleId, query)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
list, err := models.AlertHisEventGets(rt.Ctx, prods, bgids, stime, etime, severity, recovered, dsIds, cates, query, limit, ginx.Offset(c, limit))
|
||||
list, err := models.AlertHisEventGets(rt.Ctx, prods, bgids, stime, etime, severity, recovered,
|
||||
dsIds, cates, ruleId, query, limit, ginx.Offset(c, limit))
|
||||
ginx.Dangerous(err)
|
||||
|
||||
cache := make(map[int64]*models.UserGroup)
|
||||
|
||||
@@ -37,6 +37,18 @@ func (rt *Router) alertRuleGets(c *gin.Context) {
|
||||
ginx.NewRender(c).Data(ars, err)
|
||||
}
|
||||
|
||||
func getAlertCueEventTimeRange(c *gin.Context) (stime, etime int64) {
|
||||
stime = ginx.QueryInt64(c, "stime", 0)
|
||||
etime = ginx.QueryInt64(c, "etime", 0)
|
||||
if etime == 0 {
|
||||
etime = time.Now().Unix()
|
||||
}
|
||||
if stime == 0 || stime >= etime {
|
||||
stime = etime - 30*24*int64(time.Hour.Seconds())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (rt *Router) alertRuleGetsByGids(c *gin.Context) {
|
||||
gids := str.IdsInt64(ginx.QueryStr(c, "gids", ""), ",")
|
||||
if len(gids) > 0 {
|
||||
@@ -60,9 +72,30 @@ func (rt *Router) alertRuleGetsByGids(c *gin.Context) {
|
||||
ars, err := models.AlertRuleGetsByBGIds(rt.Ctx, gids)
|
||||
if err == nil {
|
||||
cache := make(map[int64]*models.UserGroup)
|
||||
rids := make([]int64, 0, len(ars))
|
||||
names := make([]string, 0, len(ars))
|
||||
for i := 0; i < len(ars); i++ {
|
||||
ars[i].FillNotifyGroups(rt.Ctx, cache)
|
||||
ars[i].FillSeverities()
|
||||
rids = append(rids, ars[i].Id)
|
||||
names = append(names, ars[i].UpdateBy)
|
||||
}
|
||||
|
||||
stime, etime := getAlertCueEventTimeRange(c)
|
||||
cnt := models.AlertCurEventCountByRuleId(rt.Ctx, rids, stime, etime)
|
||||
if cnt != nil {
|
||||
for i := 0; i < len(ars); i++ {
|
||||
ars[i].CurEventCount = cnt[ars[i].Id]
|
||||
}
|
||||
}
|
||||
|
||||
users := models.UserMapGet(rt.Ctx, "username in (?)", names)
|
||||
if users != nil {
|
||||
for i := 0; i < len(ars); i++ {
|
||||
if user, exist := users[ars[i].UpdateBy]; exist {
|
||||
ars[i].UpdateByNickname = user.Nickname
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ginx.NewRender(c).Data(ars, err)
|
||||
@@ -492,7 +525,7 @@ func (rt *Router) relabelTest(c *gin.Context) {
|
||||
|
||||
labels := make([]prompb.Label, len(f.Tags))
|
||||
for i, tag := range f.Tags {
|
||||
label := strings.Split(tag, "=")
|
||||
label := strings.SplitN(tag, "=", 2)
|
||||
if len(label) != 2 {
|
||||
ginx.Bomb(http.StatusBadRequest, "tag:%s format error", tag)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,14 @@ func (rt *Router) boardGet(c *gin.Context) {
|
||||
ginx.NewRender(c).Data(board, nil)
|
||||
}
|
||||
|
||||
// 根据 bids 参数,获取多个 board
|
||||
func (rt *Router) boardGetsByBids(c *gin.Context) {
|
||||
bids := str.IdsInt64(ginx.QueryStr(c, "bids", ""), ",")
|
||||
boards, err := models.BoardGetsByBids(rt.Ctx, bids)
|
||||
ginx.Dangerous(err)
|
||||
ginx.NewRender(c).Data(boards, err)
|
||||
}
|
||||
|
||||
func (rt *Router) boardPureGet(c *gin.Context) {
|
||||
board, err := models.BoardGetByID(rt.Ctx, ginx.UrlParamInt64(c, "bid"))
|
||||
ginx.Dangerous(err)
|
||||
|
||||
@@ -260,3 +260,15 @@ func (rt *Router) builtinPayloadsDel(c *gin.Context) {
|
||||
|
||||
ginx.NewRender(c).Message(models.BuiltinPayloadDels(rt.Ctx, req.Ids))
|
||||
}
|
||||
|
||||
func (rt *Router) builtinPayloadsGetByUUIDOrID(c *gin.Context) {
|
||||
uuid := ginx.QueryInt64(c, "uuid", 0)
|
||||
// 优先以 uuid 为准
|
||||
if uuid != 0 {
|
||||
ginx.NewRender(c).Data(models.BuiltinPayloadGet(rt.Ctx, "uuid = ?", uuid))
|
||||
return
|
||||
}
|
||||
|
||||
id := ginx.QueryInt64(c, "id", 0)
|
||||
ginx.NewRender(c).Data(models.BuiltinPayloadGet(rt.Ctx, "id = ?", id))
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ func (rt *Router) configGet(c *gin.Context) {
|
||||
ginx.NewRender(c).Data(configs, err)
|
||||
}
|
||||
|
||||
func (rt *Router) configGetAll(c *gin.Context) {
|
||||
config, err := models.ConfigsGetAll(rt.Ctx)
|
||||
ginx.NewRender(c).Data(config, err)
|
||||
}
|
||||
|
||||
func (rt *Router) configGetByKey(c *gin.Context) {
|
||||
config, err := models.ConfigsGet(rt.Ctx, ginx.QueryStr(c, "key"))
|
||||
ginx.NewRender(c).Data(config, err)
|
||||
|
||||
@@ -92,10 +92,12 @@ func (rt *Router) datasourceUpsert(c *gin.Context) {
|
||||
var err error
|
||||
var count int64
|
||||
|
||||
err = DatasourceCheck(req)
|
||||
if err != nil {
|
||||
Dangerous(c, err)
|
||||
return
|
||||
if !req.ForceSave {
|
||||
err = DatasourceCheck(req)
|
||||
if err != nil {
|
||||
Dangerous(c, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if req.Id == 0 {
|
||||
|
||||
@@ -45,6 +45,10 @@ func (rt *Router) statistic(c *gin.Context) {
|
||||
statistics, err = models.ConfigsUserVariableStatistics(rt.Ctx)
|
||||
ginx.NewRender(c).Data(statistics, err)
|
||||
return
|
||||
case "cval":
|
||||
statistics, err = models.ConfigCvalStatistics(rt.Ctx)
|
||||
ginx.NewRender(c).Data(statistics, err)
|
||||
return
|
||||
default:
|
||||
ginx.Bomb(http.StatusBadRequest, "invalid name")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -80,16 +81,48 @@ func HandleHeartbeat(c *gin.Context, ctx *ctx.Context, engineName string, metaSe
|
||||
identSet.MSet(items)
|
||||
|
||||
if target, has := targetCache.Get(req.Hostname); has && target != nil {
|
||||
gid := ginx.QueryInt64(c, "gid", 0)
|
||||
gidsStr := ginx.QueryStr(c, "gid", "")
|
||||
overwriteGids := ginx.QueryBool(c, "overwrite_gids", false)
|
||||
hostIp := strings.TrimSpace(req.HostIp)
|
||||
gids := strings.Split(gidsStr, ",")
|
||||
|
||||
if overwriteGids {
|
||||
groupIds := make([]int64, 0)
|
||||
for i := range gids {
|
||||
if gids[i] == "" {
|
||||
continue
|
||||
}
|
||||
groupId, err := strconv.ParseInt(gids[i], 10, 64)
|
||||
if err != nil {
|
||||
logger.Warningf("update target:%s group ids failed, err: %v", req.Hostname, err)
|
||||
continue
|
||||
}
|
||||
groupIds = append(groupIds, groupId)
|
||||
}
|
||||
|
||||
err := models.TargetOverrideBgids(ctx, []string{target.Ident}, groupIds)
|
||||
if err != nil {
|
||||
logger.Warningf("update target:%s group ids failed, err: %v", target.Ident, err)
|
||||
}
|
||||
} else if gidsStr != "" {
|
||||
for i := range gids {
|
||||
groupId, err := strconv.ParseInt(gids[i], 10, 64)
|
||||
if err != nil {
|
||||
logger.Warningf("update target:%s group ids failed, err: %v", req.Hostname, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !target.MatchGroupId(groupId) {
|
||||
err := models.TargetBindBgids(ctx, []string{target.Ident}, []int64{groupId})
|
||||
if err != nil {
|
||||
logger.Warningf("update target:%s group ids failed, err: %v", target.Ident, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newTarget := models.Target{}
|
||||
targetNeedUpdate := false
|
||||
if gid != 0 && gid != target.GroupId {
|
||||
newTarget.GroupId = gid
|
||||
targetNeedUpdate = true
|
||||
}
|
||||
|
||||
if hostIp != "" && hostIp != target.HostIp {
|
||||
newTarget.HostIp = hostIp
|
||||
targetNeedUpdate = true
|
||||
|
||||
@@ -50,7 +50,8 @@ func (rt *Router) alertMuteGets(c *gin.Context) {
|
||||
prods := strings.Fields(ginx.QueryStr(c, "prods", ""))
|
||||
bgid := ginx.QueryInt64(c, "bgid", -1)
|
||||
query := ginx.QueryStr(c, "query", "")
|
||||
lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, query)
|
||||
disabled := ginx.QueryInt(c, "disabled", -1)
|
||||
lst, err := models.AlertMuteGets(rt.Ctx, prods, bgid, disabled, query)
|
||||
|
||||
ginx.NewRender(c).Data(lst, err)
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func (rt *Router) notifyTplPreview(c *gin.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
arr := strings.Split(pair, "=")
|
||||
arr := strings.SplitN(pair, "=", 2)
|
||||
if len(arr) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ func (rt *Router) targetGets(c *gin.Context) {
|
||||
bgids = append(bgids, 0)
|
||||
}
|
||||
}
|
||||
|
||||
options := []models.BuildTargetWhereOption{
|
||||
models.BuildTargetWhereWithBgids(bgids),
|
||||
models.BuildTargetWhereWithDsIds(dsIds),
|
||||
@@ -81,6 +82,13 @@ func (rt *Router) targetGets(c *gin.Context) {
|
||||
ginx.Offset(c, limit), order, desc, options...)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
tgs, err := models.TargetBusiGroupsGetAll(rt.Ctx)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
for _, t := range list {
|
||||
t.GroupIds = tgs[t.Ident]
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
now := time.Now()
|
||||
cache := make(map[int64]*models.BusiGroup)
|
||||
@@ -385,8 +393,15 @@ type targetBgidForm struct {
|
||||
Bgid int64 `json:"bgid"`
|
||||
}
|
||||
|
||||
func (rt *Router) targetUpdateBgid(c *gin.Context) {
|
||||
var f targetBgidForm
|
||||
type targetBgidsForm struct {
|
||||
Idents []string `json:"idents" binding:"required_without=HostIps"`
|
||||
HostIps []string `json:"host_ips" binding:"required_without=Idents"`
|
||||
Bgids []int64 `json:"bgids"`
|
||||
Action string `json:"action"` // add del reset
|
||||
}
|
||||
|
||||
func (rt *Router) targetBindBgids(c *gin.Context) {
|
||||
var f targetBgidsForm
|
||||
var err error
|
||||
var failedResults = make(map[string]string)
|
||||
ginx.BindJSON(c, &f)
|
||||
@@ -402,35 +417,24 @@ func (rt *Router) targetUpdateBgid(c *gin.Context) {
|
||||
}
|
||||
|
||||
user := c.MustGet("user").(*models.User)
|
||||
if user.IsAdmin() {
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetUpdateBgid(rt.Ctx, f.Idents, f.Bgid, false))
|
||||
return
|
||||
}
|
||||
|
||||
if f.Bgid > 0 {
|
||||
// 把要操作的机器分成两部分,一部分是bgid为0,需要管理员分配,另一部分bgid>0,说明是业务组内部想调整
|
||||
// 比如原来分配给didiyun的机器,didiyun的管理员想把部分机器调整到didiyun-ceph下
|
||||
// 对于调整的这种情况,当前登录用户要对这批机器有操作权限,同时还要对目标BG有操作权限
|
||||
orphans, err := models.IdentsFilter(rt.Ctx, f.Idents, "group_id = ?", 0)
|
||||
if !user.IsAdmin() {
|
||||
// 普通用户,检查用户是否有权限操作所有请求的业务组
|
||||
existing, _, err := models.SeparateTargetIdents(rt.Ctx, f.Idents)
|
||||
ginx.Dangerous(err)
|
||||
rt.checkTargetPerm(c, existing)
|
||||
|
||||
// 机器里边存在未归组的,登录用户就需要是admin
|
||||
if len(orphans) > 0 && !user.IsAdmin() {
|
||||
can, err := user.CheckPerm(rt.Ctx, "/targets/bind")
|
||||
var groupIds []int64
|
||||
if f.Action == "reset" {
|
||||
// 如果是复写,则需要检查用户是否有权限操作机器之前的业务组
|
||||
bgids, err := models.TargetGroupIdsGetByIdents(rt.Ctx, f.Idents)
|
||||
ginx.Dangerous(err)
|
||||
if !can {
|
||||
ginx.Bomb(http.StatusForbidden, "No permission. Only admin can assign BG")
|
||||
}
|
||||
|
||||
groupIds = append(groupIds, bgids...)
|
||||
}
|
||||
groupIds = append(groupIds, f.Bgids...)
|
||||
|
||||
reBelongs, err := models.IdentsFilter(rt.Ctx, f.Idents, "group_id > ?", 0)
|
||||
ginx.Dangerous(err)
|
||||
|
||||
if len(reBelongs) > 0 {
|
||||
// 对于这些要重新分配的机器,操作者要对这些机器本身有权限,同时要对目标bgid有权限
|
||||
rt.checkTargetPerm(c, f.Idents)
|
||||
|
||||
bg := BusiGroup(rt.Ctx, f.Bgid)
|
||||
for _, bgid := range groupIds {
|
||||
bg := BusiGroup(rt.Ctx, bgid)
|
||||
can, err := user.CanDoBusiGroup(rt.Ctx, bg, "rw")
|
||||
ginx.Dangerous(err)
|
||||
|
||||
@@ -438,14 +442,24 @@ func (rt *Router) targetUpdateBgid(c *gin.Context) {
|
||||
ginx.Bomb(http.StatusForbidden, "No permission. You are not admin of BG(%s)", bg.Name)
|
||||
}
|
||||
}
|
||||
} else if f.Bgid == 0 {
|
||||
// 退还机器
|
||||
rt.checkTargetPerm(c, f.Idents)
|
||||
} else {
|
||||
ginx.Bomb(http.StatusBadRequest, "invalid bgid")
|
||||
|
||||
can, err := user.CheckPerm(rt.Ctx, "/targets/bind")
|
||||
ginx.Dangerous(err)
|
||||
if !can {
|
||||
ginx.Bomb(http.StatusForbidden, "No permission. Only admin can assign BG")
|
||||
}
|
||||
}
|
||||
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetUpdateBgid(rt.Ctx, f.Idents, f.Bgid, false))
|
||||
switch f.Action {
|
||||
case "add":
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetBindBgids(rt.Ctx, f.Idents, f.Bgids))
|
||||
case "del":
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetUnbindBgids(rt.Ctx, f.Idents, f.Bgids))
|
||||
case "reset":
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetOverrideBgids(rt.Ctx, f.Idents, f.Bgids))
|
||||
default:
|
||||
ginx.Bomb(http.StatusBadRequest, "invalid action")
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *Router) targetUpdateBgidByService(c *gin.Context) {
|
||||
@@ -464,7 +478,7 @@ func (rt *Router) targetUpdateBgidByService(c *gin.Context) {
|
||||
ginx.Bomb(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetUpdateBgid(rt.Ctx, f.Idents, f.Bgid, false))
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetOverrideBgids(rt.Ctx, f.Idents, []int64{f.Bgid}))
|
||||
}
|
||||
|
||||
type identsForm struct {
|
||||
@@ -488,7 +502,7 @@ func (rt *Router) targetDel(c *gin.Context) {
|
||||
ginx.Bomb(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetDel(rt.Ctx, f.Idents))
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetDel(rt.Ctx, f.Idents, rt.TargetDeleteHook))
|
||||
}
|
||||
|
||||
func (rt *Router) targetDelByService(c *gin.Context) {
|
||||
@@ -507,7 +521,7 @@ func (rt *Router) targetDelByService(c *gin.Context) {
|
||||
ginx.Bomb(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetDel(rt.Ctx, f.Idents))
|
||||
ginx.NewRender(c).Data(failedResults, models.TargetDel(rt.Ctx, f.Idents, rt.TargetDeleteHook))
|
||||
}
|
||||
|
||||
func (rt *Router) checkTargetPerm(c *gin.Context, idents []string) {
|
||||
|
||||
@@ -52,11 +52,13 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
|
||||
targetCache := memsto.NewTargetCache(ctx, syncStats, redis)
|
||||
busiGroupCache := memsto.NewBusiGroupCache(ctx, syncStats)
|
||||
configCvalCache := memsto.NewCvalCache(ctx, syncStats)
|
||||
idents := idents.New(ctx, redis)
|
||||
metas := metas.New(redis)
|
||||
writers := writer.NewWriters(config.Pushgw)
|
||||
pushgwRouter := pushgwrt.New(config.HTTP, config.Pushgw, config.Alert, targetCache, busiGroupCache, idents, metas, writers, ctx)
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP)
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP, configCvalCache.PrintBodyPaths, configCvalCache.PrintAccessLog)
|
||||
|
||||
pushgwRouter.Config(r)
|
||||
|
||||
if !config.Alert.Disable {
|
||||
|
||||
@@ -363,6 +363,7 @@ CREATE TABLE `target` (
|
||||
`ident` varchar(191) not null comment 'target id',
|
||||
`note` varchar(255) not null default '' comment 'append to alert event as field',
|
||||
`tags` varchar(512) not null default '' comment 'append to series data as tags, split by space, append external space at suffix',
|
||||
`host_tags` varchar(512) not null default '' comment 'append to series data as tags, split by space, append external space at suffix',
|
||||
`host_ip` varchar(15) default '' COMMENT 'IPv4 string',
|
||||
`agent_version` varchar(255) default '' COMMENT 'agent version',
|
||||
`engine_name` varchar(255) default '' COMMENT 'engine_name',
|
||||
@@ -725,6 +726,15 @@ CREATE TABLE `metric_filter` (
|
||||
KEY `idx_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `target_busi_group` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`target_ident` varchar(191) NOT NULL,
|
||||
`group_id` bigint NOT NULL,
|
||||
`update_at` bigint NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_target_group` (`target_ident`,`group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE `task_meta`
|
||||
(
|
||||
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||
|
||||
@@ -105,5 +105,15 @@ CREATE TABLE notification_record (
|
||||
/* v7.3.0 2024-08-26 */
|
||||
ALTER TABLE `target` ADD COLUMN `host_tags` TEXT COMMENT 'global labels set in conf file';
|
||||
|
||||
/* v7.4.0 2024-08-28 */
|
||||
ALTER TABLE `builtin_payloads` ADD COLUMN `component_id` bigint(20) NOT NULL DEFAULT 0 COMMENT 'component_id';
|
||||
/* v7.3.4 2024-08-28 */
|
||||
ALTER TABLE `builtin_payloads` ADD COLUMN `component_id` bigint(20) NOT NULL DEFAULT 0 COMMENT 'component_id';
|
||||
|
||||
/* v7.4.0 2024-09-20 */
|
||||
CREATE TABLE `target_busi_group` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`target_ident` varchar(191) NOT NULL,
|
||||
`group_id` bigint NOT NULL,
|
||||
`update_at` bigint NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `idx_target_group` (`target_ident`,`group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
2
go.mod
2
go.mod
@@ -33,7 +33,7 @@ require (
|
||||
github.com/redis/go-redis/v9 v9.0.2
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/tidwall/gjson v1.14.0
|
||||
github.com/toolkits/pkg v1.3.6
|
||||
github.com/toolkits/pkg v1.3.8
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
|
||||
4
go.sum
4
go.sum
@@ -294,8 +294,8 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/toolkits/pkg v1.3.6 h1:47e1amsY6mJmcnF3Y2lIpkJXfoYY2RmgI09PtwdAEMU=
|
||||
github.com/toolkits/pkg v1.3.6/go.mod h1:M9ecwFGW1vxCTUFM9sr2ZjXSKb04N+1sTQ6SA3RNAIU=
|
||||
github.com/toolkits/pkg v1.3.8 h1:2yamC20c5mHRtbcGiLY99Lm/2mVitFn6onE8KKvMT1o=
|
||||
github.com/toolkits/pkg v1.3.8/go.mod h1:M9ecwFGW1vxCTUFM9sr2ZjXSKb04N+1sTQ6SA3RNAIU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -584,7 +584,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "下钻",
|
||||
"url": "/dashboards/automq-group-metrics?TSDB=${DS_PROMETHEUS}\u0026cluster_id=${cluster_id}\u0026group_id=${__field.labels.consumer_group}\u0026partition=all\u0026topic=${__field.labels.topic}"
|
||||
"url": "/built-in-components/dashboard/detail?__uuid__=1717556327172992000&TSDB=${DS_PROMETHEUS}\u0026cluster_id=${cluster_id}\u0026group_id=${__field.labels.consumer_group}\u0026partition=all\u0026topic=${__field.labels.topic}"
|
||||
}
|
||||
],
|
||||
"showHeader": true
|
||||
@@ -669,7 +669,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "下钻",
|
||||
"url": "/dashboards/automq-topic-metrics?TSDB=${DS_PROMETHEUS}\u0026cluster_id=${cluster_id}\u0026topic=${__field.labels.topic}"
|
||||
"url": "/built-in-components/dashboard/detail?__uuid__=1717556327174664000&TSDB=${DS_PROMETHEUS}\u0026cluster_id=${cluster_id}\u0026topic=${__field.labels.topic}"
|
||||
}
|
||||
],
|
||||
"showHeader": true
|
||||
@@ -781,7 +781,7 @@
|
||||
"links": [
|
||||
{
|
||||
"title": "下钻",
|
||||
"url": "/dashboards/automq-broker-metrics?DS_PROMETHEUS=${DS_PROMETHEUS}\u0026cluster_id=${cluster_id}\u0026node_id=${__field.labels.instance}"
|
||||
"url": "/built-in-components/dashboard/detail?__uuid__=1717556327159415000&DS_PROMETHEUS=${DS_PROMETHEUS}\u0026cluster_id=${cluster_id}\u0026node_id=${__field.labels.instance}"
|
||||
}
|
||||
],
|
||||
"showHeader": true
|
||||
|
||||
463
integrations/IPMI/dashboards/IPMI_by_prometheus.json
Normal file
463
integrations/IPMI/dashboards/IPMI_by_prometheus.json
Normal file
@@ -0,0 +1,463 @@
|
||||
{
|
||||
"name": "IPMI for Prometheus",
|
||||
"ident": "",
|
||||
"configs": {
|
||||
"version": "2.0.0",
|
||||
"links": [],
|
||||
"var": [
|
||||
{
|
||||
"name": "node",
|
||||
"type": "query",
|
||||
"datasource": {
|
||||
"cate": "prometheus"
|
||||
},
|
||||
"definition": "label_values(ipmi_bmc_info, ident)",
|
||||
"reg": "",
|
||||
"multi": false
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"type": "gauge",
|
||||
"id": "f975fded-f57e-4a6e-80b4-50d5be6dd84c",
|
||||
"layout": {
|
||||
"h": 7,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"i": "f975fded-f57e-4a6e-80b4-50d5be6dd84c",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_temperature_celsius{ident='$node'}",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Temperatures",
|
||||
"links": [],
|
||||
"custom": {
|
||||
"textMode": "valueAndName",
|
||||
"calc": "avg"
|
||||
},
|
||||
"options": {
|
||||
"valueMappings": [],
|
||||
"standardOptions": {
|
||||
"util": "none"
|
||||
},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"id": "681f1191-4777-4377-8b77-404d9f036406",
|
||||
"layout": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 7,
|
||||
"i": "681f1191-4777-4377-8b77-404d9f036406",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_power_watts{ident='$node'}",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Power",
|
||||
"links": [],
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "#634CD9",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"spanNulls": false,
|
||||
"lineWidth": 1,
|
||||
"fillOpacity": 0.5,
|
||||
"gradientMode": "none",
|
||||
"stack": "off",
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"id": "feede24c-8296-4127-982e-08cfc4151933",
|
||||
"layout": {
|
||||
"h": 5,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 7,
|
||||
"i": "feede24c-8296-4127-982e-08cfc4151933",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_power_watts{ident='$node'} * 30 * 24 ",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Power usage 30d",
|
||||
"links": [],
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "all",
|
||||
"sort": "none"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "hidden"
|
||||
},
|
||||
"standardOptions": {},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "#634CD9",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "smooth",
|
||||
"spanNulls": false,
|
||||
"lineWidth": 1,
|
||||
"fillOpacity": 0.5,
|
||||
"gradientMode": "none",
|
||||
"stack": "off",
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"id": "9e11e7f5-ed3c-49eb-8a72-ee76c8700c24",
|
||||
"layout": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 12,
|
||||
"i": "9e11e7f5-ed3c-49eb-8a72-ee76c8700c24",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_temperature_celsius{ident='$node'}",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Temperatures",
|
||||
"links": [],
|
||||
"description": "",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"standardOptions": {
|
||||
"util": "none"
|
||||
},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "linear",
|
||||
"spanNulls": false,
|
||||
"lineWidth": 1,
|
||||
"fillOpacity": 0.5,
|
||||
"gradientMode": "none",
|
||||
"stack": "off",
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"id": "95c734f7-26cb-41a7-8376-49332cc220c2",
|
||||
"layout": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 12,
|
||||
"i": "95c734f7-26cb-41a7-8376-49332cc220c2",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_power_watts{ident='$node'}",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Power",
|
||||
"links": [],
|
||||
"description": "",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"standardOptions": {
|
||||
"util": "none"
|
||||
},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "linear",
|
||||
"spanNulls": false,
|
||||
"lineWidth": 1,
|
||||
"fillOpacity": 0.01,
|
||||
"gradientMode": "none",
|
||||
"stack": "off",
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"id": "0313f34f-afcf-41e9-8f69-9a3dbd4b2e56",
|
||||
"layout": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 19,
|
||||
"i": "0313f34f-afcf-41e9-8f69-9a3dbd4b2e56",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_fan_speed_rpm{ident='$node'}",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Fans",
|
||||
"links": [],
|
||||
"description": "",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"standardOptions": {
|
||||
"util": "none"
|
||||
},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "linear",
|
||||
"spanNulls": false,
|
||||
"lineWidth": 1,
|
||||
"fillOpacity": 0.5,
|
||||
"gradientMode": "none",
|
||||
"stack": "off",
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"id": "29ee004d-a95c-405d-97d1-d715fab4e1de",
|
||||
"layout": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 19,
|
||||
"i": "29ee004d-a95c-405d-97d1-d715fab4e1de",
|
||||
"isResizable": true
|
||||
},
|
||||
"version": "2.0.0",
|
||||
"datasourceCate": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "ipmi_voltage_volts{ident='$node',name!~\"Voltage 1|Voltage 2\"}",
|
||||
"legend": "{{name}}"
|
||||
}
|
||||
],
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
],
|
||||
"name": "Voltages",
|
||||
"links": [],
|
||||
"description": "",
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "multi"
|
||||
},
|
||||
"legend": {
|
||||
"displayMode": "list",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"standardOptions": {
|
||||
"util": "none"
|
||||
},
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null,
|
||||
"type": "base"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"custom": {
|
||||
"drawStyle": "lines",
|
||||
"lineInterpolation": "linear",
|
||||
"spanNulls": false,
|
||||
"lineWidth": 1,
|
||||
"fillOpacity": 0.5,
|
||||
"gradientMode": "none",
|
||||
"stack": "off",
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"uuid": 1727587308068775200
|
||||
}
|
||||
1706
integrations/Kubernetes/dashboards/DeploymentContainer.json
Normal file
1706
integrations/Kubernetes/dashboards/DeploymentContainer.json
Normal file
File diff suppressed because it is too large
Load Diff
1706
integrations/Kubernetes/dashboards/StatefulsetContainer.json
Normal file
1706
integrations/Kubernetes/dashboards/StatefulsetContainer.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1425,7 +1425,7 @@
|
||||
"rule_config": {
|
||||
"queries": [
|
||||
{
|
||||
"prom_ql": "kernel_vmstat_oom_kill != 0",
|
||||
"prom_ql": "increase(kernel_vmstat_oom_kill[2m]) > 0",
|
||||
"severity": 2
|
||||
}
|
||||
]
|
||||
@@ -2139,4 +2139,4 @@
|
||||
"update_by": "",
|
||||
"uuid": 1717556327737117000
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
{
|
||||
"id": 0,
|
||||
"group_id": 0,
|
||||
"name": "机器台账表格视图",
|
||||
"ident": "",
|
||||
"tags": "",
|
||||
"create_at": 0,
|
||||
"create_by": "",
|
||||
"update_at": 0,
|
||||
"update_by": "",
|
||||
"configs": {
|
||||
"links": [
|
||||
{
|
||||
@@ -28,7 +21,7 @@
|
||||
"colorRange": [
|
||||
"thresholds"
|
||||
],
|
||||
"detailUrl": "/dashboards-built-in/detail?__built-in-cate=Linux\u0026__built-in-name=Linux%20Host%20by%20Categraf%20v2\u0026ident=${__field.labels.ident}",
|
||||
"detailUrl": "/built-in-components/dashboard/detail?__uuid__=1717556327744505000&ident=${__field.labels.ident}",
|
||||
"textMode": "valueAndName",
|
||||
"valueField": "Value"
|
||||
},
|
||||
@@ -98,7 +91,7 @@
|
||||
"colorRange": [
|
||||
"thresholds"
|
||||
],
|
||||
"detailUrl": "/dashboards-built-in/detail?__built-in-cate=Linux\u0026__built-in-name=Linux%20Host%20by%20Categraf%20v2\u0026ident=${__field.labels.ident}",
|
||||
"detailUrl": "/built-in-components/dashboard/detail?__uuid__=1717556327744505000&ident=${__field.labels.ident}",
|
||||
"textMode": "valueAndName",
|
||||
"valueField": "Value"
|
||||
},
|
||||
@@ -171,13 +164,16 @@
|
||||
"linkMode": "appendLinkColumn",
|
||||
"links": [
|
||||
{
|
||||
"targetBlank": true,
|
||||
"title": "详情",
|
||||
"url": "/dashboards-built-in/detail?__built-in-cate=Linux\u0026__built-in-name=Linux%20Host%20by%20Categraf%20v2\u0026ident=${__field.labels.ident}"
|
||||
"url": "/built-in-components/dashboard/detail?__uuid__=1717556327744505000&ident=${__field.labels.ident}"
|
||||
}
|
||||
],
|
||||
"nowrap": false,
|
||||
"showHeader": true,
|
||||
"sortColumn": "ident",
|
||||
"sortOrder": "ascend"
|
||||
"sortOrder": "ascend",
|
||||
"tableLayout": "fixed"
|
||||
},
|
||||
"datasourceCate": "prometheus",
|
||||
"datasourceValue": "${prom}",
|
||||
@@ -385,10 +381,5 @@
|
||||
],
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"public": 0,
|
||||
"public_cate": 0,
|
||||
"bgids": null,
|
||||
"built_in": 0,
|
||||
"hide": 0,
|
||||
"uuid": 1717556327742611000
|
||||
}
|
||||
@@ -259,11 +259,11 @@
|
||||
"uuid": 1717556327796195000,
|
||||
"collector": "Categraf",
|
||||
"typ": "Linux",
|
||||
"name": "OOM 次数统计",
|
||||
"name": "1分钟内 OOM 次数统计",
|
||||
"unit": "none",
|
||||
"note": "取自 `/proc/vmstat`,需要较高版本的内核,没记错的话应该是 4.13 以上版本",
|
||||
"lang": "zh_CN",
|
||||
"expression": "kernel_vmstat_oom_kill",
|
||||
"expression": "increase(kernel_vmstat_oom_kill[1m])",
|
||||
"created_at": 0,
|
||||
"created_by": "",
|
||||
"updated_at": 0,
|
||||
@@ -1334,4 +1334,4 @@
|
||||
"updated_at": 0,
|
||||
"updated_by": ""
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
150
memsto/config_cval_cache.go
Normal file
150
memsto/config_cval_cache.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package memsto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/dumper"
|
||||
"github.com/ccfos/nightingale/v6/models"
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/toolkits/pkg/logger"
|
||||
)
|
||||
|
||||
type CvalCache struct {
|
||||
statTotal int64
|
||||
statLastUpdated int64
|
||||
ctx *ctx.Context
|
||||
stats *Stats
|
||||
|
||||
mu sync.RWMutex
|
||||
cvals map[string]string
|
||||
}
|
||||
|
||||
func NewCvalCache(ctx *ctx.Context, stats *Stats) *CvalCache {
|
||||
cvalCache := &CvalCache{
|
||||
statTotal: -1,
|
||||
statLastUpdated: -1,
|
||||
ctx: ctx,
|
||||
stats: stats,
|
||||
cvals: make(map[string]string),
|
||||
}
|
||||
cvalCache.initSyncConfigs()
|
||||
return cvalCache
|
||||
}
|
||||
|
||||
func (c *CvalCache) initSyncConfigs() {
|
||||
err := c.syncConfigs()
|
||||
if err != nil {
|
||||
log.Fatalln("failed to sync configs:", err)
|
||||
}
|
||||
|
||||
go c.loopSyncConfigs()
|
||||
}
|
||||
|
||||
func (c *CvalCache) loopSyncConfigs() {
|
||||
duration := time.Duration(9000) * time.Millisecond
|
||||
for {
|
||||
time.Sleep(duration)
|
||||
if err := c.syncConfigs(); err != nil {
|
||||
logger.Warning("failed to sync configs:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CvalCache) syncConfigs() error {
|
||||
start := time.Now()
|
||||
|
||||
stat, err := models.ConfigCvalStatistics(c.ctx)
|
||||
if err != nil {
|
||||
dumper.PutSyncRecord("cvals", start.Unix(), -1, -1, "failed to query statistics: "+err.Error())
|
||||
return errors.WithMessage(err, "failed to call ConfigCvalStatistics")
|
||||
}
|
||||
|
||||
if !c.statChanged(stat.Total, stat.LastUpdated) {
|
||||
c.stats.GaugeCronDuration.WithLabelValues("sync_cvals").Set(0)
|
||||
c.stats.GaugeSyncNumber.WithLabelValues("sync_cvals").Set(0)
|
||||
dumper.PutSyncRecord("cvals", start.Unix(), -1, -1, "not changed")
|
||||
return nil
|
||||
}
|
||||
|
||||
cvals, err := models.ConfigsGetAll(c.ctx)
|
||||
if err != nil {
|
||||
dumper.PutSyncRecord("cvals", start.Unix(), -1, -1, "failed to query records: "+err.Error())
|
||||
return errors.WithMessage(err, "failed to call ConfigsGet")
|
||||
}
|
||||
|
||||
c.Set(cvals, stat.Total, stat.LastUpdated)
|
||||
|
||||
ms := time.Since(start).Milliseconds()
|
||||
c.stats.GaugeCronDuration.WithLabelValues("sync_cvals").Set(float64(ms))
|
||||
c.stats.GaugeSyncNumber.WithLabelValues("sync_cvals").Set(float64(len(c.cvals)))
|
||||
|
||||
logger.Infof("timer: sync cvals done, cost: %dms", ms)
|
||||
dumper.PutSyncRecord("cvals", start.Unix(), ms, len(c.cvals), "success")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CvalCache) statChanged(total int64, updated int64) bool {
|
||||
if c.statTotal == total && c.statLastUpdated == updated {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *CvalCache) Set(cvals []*models.Configs, total int64, updated int64) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.statTotal = total
|
||||
c.statLastUpdated = updated
|
||||
for _, cfg := range cvals {
|
||||
c.cvals[cfg.Ckey] = cfg.Cval
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CvalCache) Get(ckey string) string {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.cvals[ckey]
|
||||
}
|
||||
|
||||
func (c *CvalCache) GetLastUpdateTime() int64 {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
return c.statLastUpdated
|
||||
}
|
||||
|
||||
type SiteInfo struct {
|
||||
PrintBodyPaths []string `json:"print_body_paths"`
|
||||
PrintAccessLog bool `json:"print_access_log"`
|
||||
}
|
||||
|
||||
func (c *CvalCache) GetSiteInfo() *SiteInfo {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
|
||||
si := SiteInfo{}
|
||||
if siteInfoStr := c.Get("site_info"); siteInfoStr != "" {
|
||||
if err := json.Unmarshal([]byte(siteInfoStr), &si); err != nil {
|
||||
logger.Errorf("Failed to unmarshal site info: %v", err)
|
||||
}
|
||||
}
|
||||
return &si
|
||||
}
|
||||
|
||||
func (c *CvalCache) PrintBodyPaths() map[string]struct{} {
|
||||
printBodyPaths := c.GetSiteInfo().PrintBodyPaths
|
||||
pbp := make(map[string]struct{}, len(printBodyPaths))
|
||||
for _, p := range printBodyPaths {
|
||||
pbp[p] = struct{}{}
|
||||
}
|
||||
return pbp
|
||||
}
|
||||
|
||||
func (c *CvalCache) PrintAccessLog() bool {
|
||||
return c.GetSiteInfo().PrintAccessLog
|
||||
}
|
||||
@@ -160,8 +160,9 @@ func (tc *TargetCacheType) syncTargets() error {
|
||||
}
|
||||
|
||||
m := make(map[string]*models.Target)
|
||||
if tc.ctx.IsCenter {
|
||||
metaMap := tc.GetHostMetas(lst)
|
||||
|
||||
metaMap := tc.GetHostMetas(lst)
|
||||
if len(metaMap) > 0 {
|
||||
for i := 0; i < len(lst); i++ {
|
||||
if meta, ok := metaMap[lst[i].Ident]; ok {
|
||||
lst[i].FillMeta(meta)
|
||||
|
||||
@@ -68,6 +68,7 @@ type AlertCurEvent struct {
|
||||
SubRuleId int64 `json:"sub_rule_id" gorm:"-"`
|
||||
ExtraInfo []string `json:"extra_info" gorm:"-"`
|
||||
Target *Target `json:"target" gorm:"-"`
|
||||
RecoverConfig RecoverConfig `json:"recover_config" gorm:"-"`
|
||||
}
|
||||
|
||||
func (e *AlertCurEvent) TableName() string {
|
||||
@@ -342,7 +343,7 @@ func (e *AlertCurEvent) DB2Mem() {
|
||||
continue
|
||||
}
|
||||
|
||||
arr := strings.Split(pair, "=")
|
||||
arr := strings.SplitN(pair, "=", 2)
|
||||
if len(arr) != 2 {
|
||||
continue
|
||||
}
|
||||
@@ -419,7 +420,8 @@ func (e *AlertCurEvent) FillNotifyGroups(ctx *ctx.Context, cache map[int64]*User
|
||||
return nil
|
||||
}
|
||||
|
||||
func AlertCurEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64, severity int, dsIds []int64, cates []string, query string) (int64, error) {
|
||||
func AlertCurEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64,
|
||||
severity int, dsIds []int64, cates []string, ruleId int64, query string) (int64, error) {
|
||||
session := DB(ctx).Model(&AlertCurEvent{})
|
||||
if stime != 0 && etime != 0 {
|
||||
session = session.Where("trigger_time between ? and ?", stime, etime)
|
||||
@@ -444,6 +446,10 @@ func AlertCurEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime,
|
||||
session = session.Where("cate in ?", cates)
|
||||
}
|
||||
|
||||
if ruleId > 0 {
|
||||
session = session.Where("rule_id = ?", ruleId)
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
@@ -455,7 +461,9 @@ func AlertCurEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime,
|
||||
return Count(session)
|
||||
}
|
||||
|
||||
func AlertCurEventGets(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64, severity int, dsIds []int64, cates []string, query string, limit, offset int) ([]AlertCurEvent, error) {
|
||||
func AlertCurEventsGet(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64,
|
||||
severity int, dsIds []int64, cates []string, ruleId int64, query string, limit, offset int) (
|
||||
[]AlertCurEvent, error) {
|
||||
session := DB(ctx).Model(&AlertCurEvent{})
|
||||
if stime != 0 && etime != 0 {
|
||||
session = session.Where("trigger_time between ? and ?", stime, etime)
|
||||
@@ -480,6 +488,10 @@ func AlertCurEventGets(ctx *ctx.Context, prods []string, bgids []int64, stime, e
|
||||
session = session.Where("cate in ?", cates)
|
||||
}
|
||||
|
||||
if ruleId > 0 {
|
||||
session = session.Where("rule_id = ?", ruleId)
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
@@ -500,6 +512,26 @@ func AlertCurEventGets(ctx *ctx.Context, prods []string, bgids []int64, stime, e
|
||||
return lst, err
|
||||
}
|
||||
|
||||
func AlertCurEventCountByRuleId(ctx *ctx.Context, rids []int64, stime, etime int64) map[int64]int64 {
|
||||
type Row struct {
|
||||
RuleId int64
|
||||
Cnt int64
|
||||
}
|
||||
var rows []Row
|
||||
err := DB(ctx).Model(&AlertCurEvent{}).Select("rule_id, count(*) as cnt").
|
||||
Where("trigger_time between ? and ?", stime, etime).Group("rule_id").Find(&rows).Error
|
||||
if err != nil {
|
||||
logger.Errorf("Failed to count group by rule_id: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
curEventTotalByRid := make(map[int64]int64, len(rids))
|
||||
for _, r := range rows {
|
||||
curEventTotalByRid[r.RuleId] = r.Cnt
|
||||
}
|
||||
return curEventTotalByRid
|
||||
}
|
||||
|
||||
func AlertCurEventDel(ctx *ctx.Context, ids []int64) error {
|
||||
if len(ids) == 0 {
|
||||
return nil
|
||||
|
||||
@@ -121,7 +121,9 @@ func (e *AlertHisEvent) FillNotifyGroups(ctx *ctx.Context, cache map[int64]*User
|
||||
|
||||
// }
|
||||
|
||||
func AlertHisEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64, severity int, recovered int, dsIds []int64, cates []string, query string) (int64, error) {
|
||||
func AlertHisEventTotal(
|
||||
ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64, severity int,
|
||||
recovered int, dsIds []int64, cates []string, ruleId int64, query string) (int64, error) {
|
||||
session := DB(ctx).Model(&AlertHisEvent{}).Where("last_eval_time between ? and ?", stime, etime)
|
||||
|
||||
if len(prods) > 0 {
|
||||
@@ -148,6 +150,10 @@ func AlertHisEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime,
|
||||
session = session.Where("cate in ?", cates)
|
||||
}
|
||||
|
||||
if ruleId > 0 {
|
||||
session = session.Where("rule_id = ?", ruleId)
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
@@ -159,7 +165,9 @@ func AlertHisEventTotal(ctx *ctx.Context, prods []string, bgids []int64, stime,
|
||||
return Count(session)
|
||||
}
|
||||
|
||||
func AlertHisEventGets(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64, severity int, recovered int, dsIds []int64, cates []string, query string, limit, offset int) ([]AlertHisEvent, error) {
|
||||
func AlertHisEventGets(ctx *ctx.Context, prods []string, bgids []int64, stime, etime int64,
|
||||
severity int, recovered int, dsIds []int64, cates []string, ruleId int64, query string,
|
||||
limit, offset int) ([]AlertHisEvent, error) {
|
||||
session := DB(ctx).Where("last_eval_time between ? and ?", stime, etime)
|
||||
|
||||
if len(prods) != 0 {
|
||||
@@ -186,6 +194,10 @@ func AlertHisEventGets(ctx *ctx.Context, prods []string, bgids []int64, stime, e
|
||||
session = session.Where("cate in ?", cates)
|
||||
}
|
||||
|
||||
if ruleId > 0 {
|
||||
session = session.Where("rule_id = ?", ruleId)
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
|
||||
@@ -108,7 +108,7 @@ func AlertMuteGet(ctx *ctx.Context, where string, args ...interface{}) (*AlertMu
|
||||
return lst[0], err
|
||||
}
|
||||
|
||||
func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, query string) (lst []AlertMute, err error) {
|
||||
func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, disabled int, query string) (lst []AlertMute, err error) {
|
||||
session := DB(ctx)
|
||||
|
||||
if bgid != -1 {
|
||||
@@ -119,6 +119,14 @@ func AlertMuteGets(ctx *ctx.Context, prods []string, bgid int64, query string) (
|
||||
session = session.Where("prod in (?)", prods)
|
||||
}
|
||||
|
||||
if disabled != -1 {
|
||||
if disabled == 0 {
|
||||
session = session.Where("disabled = 0")
|
||||
} else {
|
||||
session = session.Where("disabled = 1")
|
||||
}
|
||||
}
|
||||
|
||||
if query != "" {
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
@@ -287,16 +295,9 @@ func AlertMuteStatistics(ctx *ctx.Context) (*Statistics, error) {
|
||||
return s, err
|
||||
}
|
||||
|
||||
// clean expired first
|
||||
buf := int64(30)
|
||||
err := DB(ctx).Where("etime < ? and mute_time_type = 0", time.Now().Unix()-buf).Delete(new(AlertMute)).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := DB(ctx).Model(&AlertMute{}).Select("count(*) as total", "max(update_at) as last_updated")
|
||||
|
||||
err = session.Find(&stats).Error
|
||||
err := session.Find(&stats).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -308,7 +309,7 @@ func AlertMuteGetsAll(ctx *ctx.Context) ([]*AlertMute, error) {
|
||||
// get my cluster's mutes
|
||||
var lst []*AlertMute
|
||||
if !ctx.IsCenter {
|
||||
lst, err := poster.GetByUrls[[]*AlertMute](ctx, "/v1/n9e/alert-mutes")
|
||||
lst, err := poster.GetByUrls[[]*AlertMute](ctx, "/v1/n9e/alert-mutes?disabled=0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -318,7 +319,7 @@ func AlertMuteGetsAll(ctx *ctx.Context) ([]*AlertMute, error) {
|
||||
return lst, err
|
||||
}
|
||||
|
||||
session := DB(ctx).Model(&AlertMute{})
|
||||
session := DB(ctx).Model(&AlertMute{}).Where("disabled = 0")
|
||||
|
||||
err := session.Find(&lst).Error
|
||||
if err != nil {
|
||||
|
||||
@@ -98,6 +98,8 @@ type AlertRule struct {
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
UpdateBy string `json:"update_by"`
|
||||
UUID int64 `json:"uuid" gorm:"-"` // tpl identifier
|
||||
CurEventCount int64 `json:"cur_event_count" gorm:"-"`
|
||||
UpdateByNickname string `json:"update_by_nickname" gorm:"-"` // for fe
|
||||
}
|
||||
|
||||
type Tpl struct {
|
||||
@@ -126,6 +128,19 @@ type PromRuleConfig struct {
|
||||
AlgoParams interface{} `json:"algo_params"`
|
||||
}
|
||||
|
||||
type RecoverJudge int
|
||||
|
||||
const (
|
||||
Origin RecoverJudge = 0
|
||||
RecoverWithoutData RecoverJudge = 1
|
||||
RecoverOnCondition RecoverJudge = 2
|
||||
)
|
||||
|
||||
type RecoverConfig struct {
|
||||
JudgeType RecoverJudge `json:"judge_type"`
|
||||
RecoverExp string `json:"recover_exp"`
|
||||
}
|
||||
|
||||
type HostRuleConfig struct {
|
||||
Queries []HostQuery `json:"queries"`
|
||||
Triggers []HostTrigger `json:"triggers"`
|
||||
@@ -133,8 +148,9 @@ type HostRuleConfig struct {
|
||||
}
|
||||
|
||||
type PromQuery struct {
|
||||
PromQl string `json:"prom_ql"`
|
||||
Severity int `json:"severity"`
|
||||
PromQl string `json:"prom_ql"`
|
||||
Severity int `json:"severity"`
|
||||
RecoverConfig RecoverConfig `json:"recover_config"`
|
||||
}
|
||||
|
||||
type HostTrigger struct {
|
||||
@@ -161,10 +177,13 @@ type Trigger struct {
|
||||
Duration int `json:"duration,omitempty"`
|
||||
Percent int `json:"percent,omitempty"`
|
||||
Joins []Join `json:"joins"`
|
||||
JoinRef string `json:"join_ref"`
|
||||
RecoverConfig RecoverConfig `json:"recover_config"`
|
||||
}
|
||||
|
||||
type Join struct {
|
||||
JoinType string `json:"join_type"`
|
||||
Ref string `json:"ref"`
|
||||
On []string `json:"on"`
|
||||
}
|
||||
|
||||
@@ -176,9 +195,10 @@ func GetHostsQuery(queries []HostQuery) []map[string]interface{} {
|
||||
case "group_ids":
|
||||
ids := ParseInt64(q.Values)
|
||||
if q.Op == "==" {
|
||||
m["group_id in (?)"] = ids
|
||||
m["target_busi_group.group_id in (?)"] = ids
|
||||
} else {
|
||||
m["group_id not in (?)"] = ids
|
||||
m["target.ident not in (select target_ident "+
|
||||
"from target_busi_group where group_id in (?))"] = ids
|
||||
}
|
||||
case "tags":
|
||||
lst := []string{}
|
||||
@@ -818,7 +838,8 @@ func AlertRuleGetsAll(ctx *ctx.Context) ([]*AlertRule, error) {
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
func AlertRulesGetsBy(ctx *ctx.Context, prods []string, query, algorithm, cluster string, cates []string, disabled int) ([]*AlertRule, error) {
|
||||
func AlertRulesGetsBy(ctx *ctx.Context, prods []string, query, algorithm, cluster string,
|
||||
cates []string, disabled int) ([]*AlertRule, error) {
|
||||
session := DB(ctx)
|
||||
|
||||
if len(prods) > 0 {
|
||||
|
||||
@@ -286,3 +286,53 @@ func BoardSetHide(ctx *ctx.Context, ids []int64) error {
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func BoardGetsByBids(ctx *ctx.Context, bids []int64) ([]map[string]interface{}, error) {
|
||||
var boards []Board
|
||||
err := DB(ctx).Where("id IN ?", bids).Find(&boards).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 收集所有唯一的 group_id
|
||||
groupIDs := make([]int64, 0)
|
||||
groupIDSet := make(map[int64]struct{})
|
||||
for _, board := range boards {
|
||||
if _, exists := groupIDSet[board.GroupId]; !exists {
|
||||
groupIDs = append(groupIDs, board.GroupId)
|
||||
groupIDSet[board.GroupId] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// 一次性查询所有需要的 BusiGroup
|
||||
var busiGroups []BusiGroup
|
||||
err = DB(ctx).Where("id IN ?", groupIDs).Find(&busiGroups).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建 group_id 到 BusiGroup 的映射
|
||||
groupMap := make(map[int64]BusiGroup)
|
||||
for _, bg := range busiGroups {
|
||||
groupMap[bg.Id] = bg
|
||||
}
|
||||
|
||||
result := make([]map[string]interface{}, 0, len(boards))
|
||||
for _, board := range boards {
|
||||
busiGroup, exists := groupMap[board.GroupId]
|
||||
if !exists {
|
||||
// 处理找不到对应 BusiGroup 的情况
|
||||
continue
|
||||
}
|
||||
|
||||
item := map[string]interface{}{
|
||||
"busi_group_name": busiGroup.Name,
|
||||
"busi_group_id": busiGroup.Id,
|
||||
"board_id": board.Id,
|
||||
"board_name": board.Name,
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ func (bg *BusiGroup) Del(ctx *ctx.Context) error {
|
||||
return errors.New("Some alert subscribes still in the BusiGroup")
|
||||
}
|
||||
|
||||
has, err = Exists(DB(ctx).Model(&Target{}).Where("group_id=?", bg.Id))
|
||||
has, err = Exists(DB(ctx).Model(&TargetBusiGroup{}).Where("group_id=?", bg.Id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -106,10 +106,8 @@ func InitRSAPassWord(ctx *ctx.Context) (string, error) {
|
||||
|
||||
func ConfigsGet(ctx *ctx.Context, ckey string) (string, error) { //select built-in type configs
|
||||
if !ctx.IsCenter {
|
||||
if !ctx.IsCenter {
|
||||
s, err := poster.GetByUrls[string](ctx, "/v1/n9e/config?key="+ckey)
|
||||
return s, err
|
||||
}
|
||||
s, err := poster.GetByUrls[string](ctx, "/v1/n9e/config?key="+ckey)
|
||||
return s, err
|
||||
}
|
||||
|
||||
var lst []string
|
||||
@@ -125,6 +123,22 @@ func ConfigsGet(ctx *ctx.Context, ckey string) (string, error) { //select built-
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func ConfigsGetAll(ctx *ctx.Context) ([]*Configs, error) { // select built-in type configs
|
||||
if !ctx.IsCenter {
|
||||
lst, err := poster.GetByUrls[[]*Configs](ctx, "/v1/n9e/all-configs")
|
||||
return lst, err
|
||||
}
|
||||
|
||||
var lst []*Configs
|
||||
err := DB(ctx).Model(&Configs{}).Select("ckey, cval").
|
||||
Where("ckey!='' and external=? ", 0).Find(&lst).Error
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to query configs")
|
||||
}
|
||||
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
func ConfigsSet(ctx *ctx.Context, ckey, cval string) error {
|
||||
return ConfigsSetWithUname(ctx, ckey, cval, "default")
|
||||
}
|
||||
@@ -355,3 +369,19 @@ func ConfigUserVariableGetDecryptMap(context *ctx.Context, privateKey []byte, pa
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func ConfigCvalStatistics(context *ctx.Context) (*Statistics, error) {
|
||||
if !context.IsCenter {
|
||||
return poster.GetByUrls[*Statistics](context, "/v1/n9e/statistic?name=cval")
|
||||
}
|
||||
|
||||
session := DB(context).Model(&Configs{}).Select("count(*) as total",
|
||||
"max(update_at) as last_updated").Where("ckey!='' and external=? ", 0) // built-in config
|
||||
|
||||
var stats []*Statistics
|
||||
err := session.Find(&stats).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stats[0], nil
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type Datasource struct {
|
||||
UpdatedBy string `json:"updated_by"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
Transport *http.Transport `json:"-" gorm:"-"`
|
||||
ForceSave bool `json:"force_save" gorm:"-"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
|
||||
@@ -28,7 +28,7 @@ func MigrateIbexTables(db *gorm.DB) {
|
||||
db = db.Set("gorm:table_options", tableOptions)
|
||||
}
|
||||
|
||||
dts := []interface{}{&imodels.TaskMeta{}, &imodels.TaskScheduler{}, &imodels.TaskSchedulerHealth{}, &imodels.TaskHostDoing{}, &imodels.TaskAction{}}
|
||||
dts := []interface{}{&imodels.TaskMeta{}, &imodels.TaskScheduler{}, &imodels.TaskSchedulerHealth{}, &TaskHostDoing{}, &imodels.TaskAction{}}
|
||||
for _, dt := range dts {
|
||||
err := db.AutoMigrate(dt)
|
||||
if err != nil {
|
||||
@@ -58,7 +58,8 @@ func MigrateTables(db *gorm.DB) error {
|
||||
dts := []interface{}{&RecordingRule{}, &AlertRule{}, &AlertSubscribe{}, &AlertMute{},
|
||||
&TaskRecord{}, &ChartShare{}, &Target{}, &Configs{}, &Datasource{}, &NotifyTpl{},
|
||||
&Board{}, &BoardBusigroup{}, &Users{}, &SsoConfig{}, &models.BuiltinMetric{},
|
||||
&models.MetricFilter{}, &models.BuiltinComponent{}, &models.NotificaitonRecord{}}
|
||||
&models.MetricFilter{}, &models.BuiltinComponent{}, &models.NotificaitonRecord{},
|
||||
&models.TargetBusiGroup{}}
|
||||
|
||||
if !columnHasIndex(db, &AlertHisEvent{}, "original_tags") ||
|
||||
!columnHasIndex(db, &AlertCurEvent{}, "original_tags") {
|
||||
@@ -88,7 +89,7 @@ func MigrateTables(db *gorm.DB) error {
|
||||
for _, dt := range dts {
|
||||
err := db.AutoMigrate(dt)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to migrate table: %v", err)
|
||||
logger.Errorf("failed to migrate table:%v %v", dt, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,3 +280,15 @@ type BuiltinPayloads struct {
|
||||
UUID int64 `json:"uuid" gorm:"type:bigint;not null;index:idx_uuid;comment:'uuid of payload'"`
|
||||
ComponentID int64 `json:"component_id" gorm:"type:bigint;index:idx_component,sort:asc;not null;default:0;comment:'component_id of payload'"`
|
||||
}
|
||||
|
||||
type TaskHostDoing struct {
|
||||
Id int64 `gorm:"column:id;index;primaryKey:false"`
|
||||
Host string `gorm:"column:host;size:128;not null;index"`
|
||||
Clock int64 `gorm:"column:clock;not null;default:0"`
|
||||
Action string `gorm:"column:action;size:16;not null"`
|
||||
AlertTriggered bool `gorm:"-"`
|
||||
}
|
||||
|
||||
func (TaskHostDoing) TableName() string {
|
||||
return "task_host_doing"
|
||||
}
|
||||
|
||||
186
models/target.go
186
models/target.go
@@ -1,12 +1,14 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
"github.com/ccfos/nightingale/v6/pkg/poster"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/toolkits/pkg/container/set"
|
||||
@@ -14,10 +16,12 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TargetDeleteHookFunc func(ctx *ctx.Context, idents []string) error
|
||||
|
||||
type Target struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey"`
|
||||
GroupId int64 `json:"group_id"`
|
||||
GroupObj *BusiGroup `json:"group_obj" gorm:"-"`
|
||||
GroupObjs []*BusiGroup `json:"group_objs" gorm:"-"`
|
||||
Ident string `json:"ident"`
|
||||
Note string `json:"note"`
|
||||
Tags string `json:"-"` // user tags
|
||||
@@ -38,6 +42,7 @@ type Target struct {
|
||||
CpuUtil float64 `json:"cpu_util" gorm:"-"`
|
||||
Arch string `json:"arch" gorm:"-"`
|
||||
RemoteAddr string `json:"remote_addr" gorm:"-"`
|
||||
GroupIds []int64 `json:"group_ids" gorm:"-"`
|
||||
}
|
||||
|
||||
func (t *Target) TableName() string {
|
||||
@@ -45,26 +50,49 @@ func (t *Target) TableName() string {
|
||||
}
|
||||
|
||||
func (t *Target) FillGroup(ctx *ctx.Context, cache map[int64]*BusiGroup) error {
|
||||
if t.GroupId <= 0 {
|
||||
return nil
|
||||
var err error
|
||||
if len(t.GroupIds) == 0 {
|
||||
t.GroupIds, err = TargetGroupIdsGetByIdent(ctx, t.Ident)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get target gids")
|
||||
}
|
||||
t.GroupObjs = make([]*BusiGroup, 0, len(t.GroupIds))
|
||||
}
|
||||
|
||||
bg, has := cache[t.GroupId]
|
||||
if has {
|
||||
t.GroupObj = bg
|
||||
return nil
|
||||
for _, gid := range t.GroupIds {
|
||||
bg, has := cache[gid]
|
||||
if has && bg != nil {
|
||||
t.GroupObjs = append(t.GroupObjs, bg)
|
||||
continue
|
||||
}
|
||||
|
||||
bg, err := BusiGroupGetById(ctx, gid)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get busi group")
|
||||
}
|
||||
|
||||
if bg == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
t.GroupObjs = append(t.GroupObjs, bg)
|
||||
cache[gid] = bg
|
||||
}
|
||||
|
||||
bg, err := BusiGroupGetById(ctx, t.GroupId)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed to get busi group")
|
||||
}
|
||||
|
||||
t.GroupObj = bg
|
||||
cache[t.GroupId] = bg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Target) MatchGroupId(gid ...int64) bool {
|
||||
for _, tgId := range t.GroupIds {
|
||||
for _, id := range gid {
|
||||
if tgId == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Target) AfterFind(tx *gorm.DB) (err error) {
|
||||
delta := time.Now().Unix() - t.UpdateAt
|
||||
if delta < 60 {
|
||||
@@ -91,19 +119,43 @@ func TargetStatistics(ctx *ctx.Context) (*Statistics, error) {
|
||||
return stats[0], nil
|
||||
}
|
||||
|
||||
func TargetDel(ctx *ctx.Context, idents []string) error {
|
||||
func TargetDel(ctx *ctx.Context, idents []string, deleteHook TargetDeleteHookFunc) error {
|
||||
if len(idents) == 0 {
|
||||
panic("idents empty")
|
||||
}
|
||||
return DB(ctx).Where("ident in ?", idents).Delete(new(Target)).Error
|
||||
|
||||
return DB(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
txErr := tx.Where("ident in ?", idents).Delete(new(Target)).Error
|
||||
if txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
txErr = deleteHook(ctx, idents)
|
||||
if txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
txErr = TargetDeleteBgids(ctx, idents)
|
||||
if txErr != nil {
|
||||
return txErr
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type BuildTargetWhereOption func(session *gorm.DB) *gorm.DB
|
||||
|
||||
func BuildTargetWhereWithBgids(bgids []int64) BuildTargetWhereOption {
|
||||
return func(session *gorm.DB) *gorm.DB {
|
||||
if len(bgids) > 0 {
|
||||
session = session.Where("group_id in (?)", bgids)
|
||||
if len(bgids) == 1 && bgids[0] == 0 {
|
||||
session = session.Joins("left join target_busi_group on target.ident = " +
|
||||
"target_busi_group.target_ident").Where("target_busi_group.target_ident is null")
|
||||
} else if len(bgids) > 0 {
|
||||
if slices.Contains(bgids, 0) {
|
||||
session = session.Joins("left join target_busi_group on target.ident = target_busi_group.target_ident").
|
||||
Where("target_busi_group.target_ident is null OR target_busi_group.group_id in (?)", bgids)
|
||||
} else {
|
||||
session = session.Joins("join target_busi_group on target.ident = "+
|
||||
"target_busi_group.target_ident").Where("target_busi_group.group_id in (?)", bgids)
|
||||
}
|
||||
}
|
||||
return session
|
||||
}
|
||||
@@ -133,7 +185,7 @@ func BuildTargetWhereWithQuery(query string) BuildTargetWhereOption {
|
||||
arr := strings.Fields(query)
|
||||
for i := 0; i < len(arr); i++ {
|
||||
q := "%" + arr[i] + "%"
|
||||
session = session.Where("ident like ? or note like ? or tags like ? or host_tags like ? or os like ?", q, q, q, q, q)
|
||||
session = session.Where("ident like ? or host_ip like ? or note like ? or tags like ? or host_tags like ? or os like ?", q, q, q, q, q, q)
|
||||
}
|
||||
}
|
||||
return session
|
||||
@@ -143,18 +195,18 @@ func BuildTargetWhereWithQuery(query string) BuildTargetWhereOption {
|
||||
func BuildTargetWhereWithDowntime(downtime int64) BuildTargetWhereOption {
|
||||
return func(session *gorm.DB) *gorm.DB {
|
||||
if downtime > 0 {
|
||||
session = session.Where("update_at < ?", time.Now().Unix()-downtime)
|
||||
session = session.Where("target.update_at < ?", time.Now().Unix()-downtime)
|
||||
}
|
||||
return session
|
||||
}
|
||||
}
|
||||
|
||||
func buildTargetWhere(ctx *ctx.Context, options ...BuildTargetWhereOption) *gorm.DB {
|
||||
session := DB(ctx).Model(&Target{})
|
||||
sub := DB(ctx).Model(&Target{}).Distinct("target.ident")
|
||||
for _, opt := range options {
|
||||
session = opt(session)
|
||||
sub = opt(sub)
|
||||
}
|
||||
return session
|
||||
return DB(ctx).Model(&Target{}).Where("ident in (?)", sub)
|
||||
}
|
||||
|
||||
func TargetTotal(ctx *ctx.Context, options ...BuildTargetWhereOption) (int64, error) {
|
||||
@@ -212,15 +264,18 @@ func MissTargetCountByFilter(ctx *ctx.Context, query []map[string]interface{}, t
|
||||
}
|
||||
|
||||
func TargetFilterQueryBuild(ctx *ctx.Context, query []map[string]interface{}, limit, offset int) *gorm.DB {
|
||||
session := DB(ctx).Model(&Target{})
|
||||
sub := DB(ctx).Model(&Target{}).Distinct("target.ident").Joins("left join " +
|
||||
"target_busi_group on target.ident = target_busi_group.target_ident")
|
||||
for _, q := range query {
|
||||
tx := DB(ctx).Model(&Target{})
|
||||
for k, v := range q {
|
||||
tx = tx.Or(k, v)
|
||||
}
|
||||
session = session.Where(tx)
|
||||
sub = sub.Where(tx)
|
||||
}
|
||||
|
||||
session := DB(ctx).Model(&Target{}).Where("ident in (?)", sub)
|
||||
|
||||
if limit > 0 {
|
||||
session = session.Limit(limit).Offset(offset)
|
||||
}
|
||||
@@ -236,9 +291,20 @@ func TargetGetsAll(ctx *ctx.Context) ([]*Target, error) {
|
||||
|
||||
var lst []*Target
|
||||
err := DB(ctx).Model(&Target{}).Find(&lst).Error
|
||||
if err != nil {
|
||||
return lst, err
|
||||
}
|
||||
|
||||
tgs, err := TargetBusiGroupsGetAll(ctx)
|
||||
if err != nil {
|
||||
return lst, err
|
||||
}
|
||||
|
||||
for i := 0; i < len(lst); i++ {
|
||||
lst[i].FillTagsMap()
|
||||
lst[i].GroupIds = tgs[lst[i].Ident]
|
||||
}
|
||||
|
||||
return lst, err
|
||||
}
|
||||
|
||||
@@ -496,3 +562,73 @@ func IdentsFilter(ctx *ctx.Context, idents []string, where string, args ...inter
|
||||
func (m *Target) UpdateFieldsMap(ctx *ctx.Context, fields map[string]interface{}) error {
|
||||
return DB(ctx).Model(m).Updates(fields).Error
|
||||
}
|
||||
|
||||
func MigrateBg(ctx *ctx.Context, bgLabelKey string) {
|
||||
// 1. 判断是否已经完成迁移
|
||||
var maxGroupId int64
|
||||
if err := DB(ctx).Model(&Target{}).Select("MAX(group_id)").Scan(&maxGroupId).Error; err != nil {
|
||||
log.Println("failed to get max group_id from target table, err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if maxGroupId == 0 {
|
||||
log.Println("migration bgid has been completed.")
|
||||
return
|
||||
}
|
||||
|
||||
err := DoMigrateBg(ctx, bgLabelKey)
|
||||
if err != nil {
|
||||
log.Println("failed to migrate bgid, err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("migration bgid has been completed")
|
||||
}
|
||||
|
||||
func DoMigrateBg(ctx *ctx.Context, bgLabelKey string) error {
|
||||
// 2. 获取全量 target
|
||||
targets, err := TargetGetsAll(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 获取全量 busi_group
|
||||
bgs, err := BusiGroupGetAll(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bgById := make(map[int64]*BusiGroup, len(bgs))
|
||||
for _, bg := range bgs {
|
||||
bgById[bg.Id] = bg
|
||||
}
|
||||
|
||||
// 4. 如果某 busi_group 有 label,将其存至对应的 target tags 中
|
||||
for _, t := range targets {
|
||||
if t.GroupId == 0 {
|
||||
continue
|
||||
}
|
||||
err := DB(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 4.1 将 group_id 迁移至关联表
|
||||
if err := TargetBindBgids(ctx, []string{t.Ident}, []int64{t.GroupId}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := TargetUpdateBgid(ctx, []string{t.Ident}, 0, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4.2 判断该机器是否需要新增 tag
|
||||
if bg, ok := bgById[t.GroupId]; !ok || bg.LabelEnable == 0 ||
|
||||
strings.Contains(t.Tags, bgLabelKey+"=") {
|
||||
return nil
|
||||
} else {
|
||||
return t.AddTags(ctx, []string{bgLabelKey + "=" + bg.LabelValue})
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("failed to migrate %v bg, err: %v\n", t.Ident, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
158
models/target_busi_group.go
Normal file
158
models/target_busi_group.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ccfos/nightingale/v6/pkg/ctx"
|
||||
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
type TargetBusiGroup struct {
|
||||
Id int64 `json:"id" gorm:"primaryKey;type:bigint;autoIncrement"`
|
||||
TargetIdent string `json:"target_ident" gorm:"type:varchar(191);not null;index:idx_target_group,unique,priority:1"`
|
||||
GroupId int64 `json:"group_id" gorm:"type:bigint;not null;index:idx_target_group,unique,priority:2"`
|
||||
UpdateAt int64 `json:"update_at" gorm:"type:bigint;not null"`
|
||||
}
|
||||
|
||||
func (t *TargetBusiGroup) TableName() string {
|
||||
return "target_busi_group"
|
||||
}
|
||||
|
||||
func TargetBusiGroupsGetAll(ctx *ctx.Context) (map[string][]int64, error) {
|
||||
var lst []*TargetBusiGroup
|
||||
err := DB(ctx).Find(&lst).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tgs := make(map[string][]int64)
|
||||
for _, tg := range lst {
|
||||
tgs[tg.TargetIdent] = append(tgs[tg.TargetIdent], tg.GroupId)
|
||||
}
|
||||
return tgs, nil
|
||||
}
|
||||
|
||||
func TargetGroupIdsGetByIdent(ctx *ctx.Context, ident string) ([]int64, error) {
|
||||
var lst []*TargetBusiGroup
|
||||
err := DB(ctx).Where("target_ident = ?", ident).Find(&lst).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupIds := make([]int64, 0, len(lst))
|
||||
for _, tg := range lst {
|
||||
groupIds = append(groupIds, tg.GroupId)
|
||||
}
|
||||
return groupIds, nil
|
||||
}
|
||||
|
||||
func TargetGroupIdsGetByIdents(ctx *ctx.Context, idents []string) ([]int64, error) {
|
||||
var groupIds []int64
|
||||
err := DB(ctx).Model(&TargetBusiGroup{}).
|
||||
Where("target_ident IN ?", idents).
|
||||
Distinct().
|
||||
Pluck("group_id", &groupIds).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return groupIds, nil
|
||||
}
|
||||
|
||||
func TargetBindBgids(ctx *ctx.Context, idents []string, bgids []int64) error {
|
||||
lst := make([]TargetBusiGroup, 0, len(bgids)*len(idents))
|
||||
updateAt := time.Now().Unix()
|
||||
for _, bgid := range bgids {
|
||||
for _, ident := range idents {
|
||||
cur := TargetBusiGroup{
|
||||
TargetIdent: ident,
|
||||
GroupId: bgid,
|
||||
UpdateAt: updateAt,
|
||||
}
|
||||
lst = append(lst, cur)
|
||||
}
|
||||
}
|
||||
|
||||
var cl clause.Expression = clause.Insert{Modifier: "ignore"}
|
||||
switch DB(ctx).Dialector.Name() {
|
||||
case "sqlite":
|
||||
cl = clause.Insert{Modifier: "or ignore"}
|
||||
case "postgres":
|
||||
cl = clause.OnConflict{DoNothing: true}
|
||||
}
|
||||
return DB(ctx).Clauses(cl).CreateInBatches(&lst, 10).Error
|
||||
}
|
||||
|
||||
func TargetUnbindBgids(ctx *ctx.Context, idents []string, bgids []int64) error {
|
||||
return DB(ctx).Where("target_ident in ? and group_id in ?",
|
||||
idents, bgids).Delete(&TargetBusiGroup{}).Error
|
||||
}
|
||||
|
||||
func TargetDeleteBgids(ctx *ctx.Context, idents []string) error {
|
||||
return DB(ctx).Where("target_ident in ?", idents).Delete(&TargetBusiGroup{}).Error
|
||||
}
|
||||
|
||||
func TargetOverrideBgids(ctx *ctx.Context, idents []string, bgids []int64) error {
|
||||
return DB(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 先删除旧的关联
|
||||
if err := tx.Where("target_ident IN ?", idents).Delete(&TargetBusiGroup{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 准备新的关联数据
|
||||
lst := make([]TargetBusiGroup, 0, len(bgids)*len(idents))
|
||||
updateAt := time.Now().Unix()
|
||||
for _, ident := range idents {
|
||||
for _, bgid := range bgids {
|
||||
cur := TargetBusiGroup{
|
||||
TargetIdent: ident,
|
||||
GroupId: bgid,
|
||||
UpdateAt: updateAt,
|
||||
}
|
||||
lst = append(lst, cur)
|
||||
}
|
||||
}
|
||||
|
||||
if len(lst) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 添加新的关联
|
||||
var cl clause.Expression = clause.Insert{Modifier: "ignore"}
|
||||
switch tx.Dialector.Name() {
|
||||
case "sqlite":
|
||||
cl = clause.Insert{Modifier: "or ignore"}
|
||||
case "postgres":
|
||||
cl = clause.OnConflict{DoNothing: true}
|
||||
}
|
||||
return tx.Clauses(cl).CreateInBatches(&lst, 10).Error
|
||||
})
|
||||
}
|
||||
|
||||
func SeparateTargetIdents(ctx *ctx.Context, idents []string) (existing, nonExisting []string, err error) {
|
||||
existingMap := make(map[string]bool)
|
||||
|
||||
// 查询已存在的 idents 并直接填充 map
|
||||
err = DB(ctx).Model(&TargetBusiGroup{}).
|
||||
Where("target_ident IN ?", idents).
|
||||
Distinct().
|
||||
Pluck("target_ident", &existing).
|
||||
Error
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, ident := range existing {
|
||||
existingMap[ident] = true
|
||||
}
|
||||
|
||||
// 分离不存在的 idents
|
||||
for _, ident := range idents {
|
||||
if !existingMap[ident] {
|
||||
nonExisting = append(nonExisting, ident)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -296,6 +296,34 @@ func UserGet(ctx *ctx.Context, where string, args ...interface{}) (*User, error)
|
||||
return lst[0], nil
|
||||
}
|
||||
|
||||
func UsersGet(ctx *ctx.Context, where string, args ...interface{}) ([]*User, error) {
|
||||
var lst []*User
|
||||
err := DB(ctx).Where(where, args...).Find(&lst).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, user := range lst {
|
||||
user.RolesLst = strings.Fields(user.Roles)
|
||||
user.Admin = user.IsAdmin()
|
||||
}
|
||||
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
func UserMapGet(ctx *ctx.Context, where string, args ...interface{}) map[string]*User {
|
||||
lst, err := UsersGet(ctx, where, args...)
|
||||
if err != nil {
|
||||
logger.Errorf("UsersGet err: %v", err)
|
||||
return nil
|
||||
}
|
||||
um := make(map[string]*User, len(lst))
|
||||
for _, user := range lst {
|
||||
um[user.Username] = user
|
||||
}
|
||||
return um
|
||||
}
|
||||
|
||||
func UserGetByUsername(ctx *ctx.Context, username string) (*User, error) {
|
||||
return UserGet(ctx, "username=?", username)
|
||||
}
|
||||
@@ -704,7 +732,10 @@ func (u *User) NopriIdents(ctx *ctx.Context, idents []string) ([]string, error)
|
||||
}
|
||||
|
||||
var allowedIdents []string
|
||||
err = DB(ctx).Model(&Target{}).Where("group_id in ?", bgids).Pluck("ident", &allowedIdents).Error
|
||||
sub := DB(ctx).Model(&Target{}).Distinct("target.ident").
|
||||
Joins("join target_busi_group on target.ident = target_busi_group.target_ident").
|
||||
Where("target_busi_group.group_id in (?)", bgids)
|
||||
err = DB(ctx).Model(&Target{}).Where("ident in (?)", sub).Pluck("ident", &allowedIdents).Error
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
@@ -736,7 +767,11 @@ func (u *User) BusiGroups(ctx *ctx.Context, limit int, query string, all ...bool
|
||||
return lst, nil
|
||||
}
|
||||
|
||||
err = DB(ctx).Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error
|
||||
t.GroupIds, err = TargetGroupIdsGetByIdent(ctx, t.Ident)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = DB(ctx).Order("name").Limit(limit).Where("id in ?", t.GroupIds).Find(&lst).Error
|
||||
}
|
||||
|
||||
return lst, err
|
||||
@@ -768,8 +803,12 @@ func (u *User) BusiGroups(ctx *ctx.Context, limit int, query string, all ...bool
|
||||
return lst, err
|
||||
}
|
||||
|
||||
if t != nil && slice.ContainsInt64(busiGroupIds, t.GroupId) {
|
||||
err = DB(ctx).Order("name").Limit(limit).Where("id=?", t.GroupId).Find(&lst).Error
|
||||
t.GroupIds, err = TargetGroupIdsGetByIdent(ctx, t.Ident)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t != nil && t.MatchGroupId(busiGroupIds...) {
|
||||
err = DB(ctx).Order("name").Limit(limit).Where("id in ?", t.GroupIds).Find(&lst).Error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -41,14 +42,21 @@ type LoggerConfig struct {
|
||||
|
||||
// Output is a writer where logs are written.
|
||||
// Optional. Default value is gin.DefaultWriter.
|
||||
Output io.Writer
|
||||
PrintBody bool
|
||||
Output io.Writer
|
||||
PrintAccessLog func() bool
|
||||
PrintBodyPaths func() map[string]struct{}
|
||||
|
||||
// SkipPaths is a url path array which logs are not written.
|
||||
// Optional.
|
||||
SkipPaths []string
|
||||
}
|
||||
|
||||
func (c *LoggerConfig) ContainsPath(path string) bool {
|
||||
path = strings.Split(path, "?")[0]
|
||||
_, exist := c.PrintBodyPaths()[path]
|
||||
return exist
|
||||
}
|
||||
|
||||
// LogFormatter gives the signature of the formatter function passed to LoggerWithFormatter
|
||||
type LogFormatter func(params LogFormatterParams) string
|
||||
|
||||
@@ -255,6 +263,11 @@ func LoggerWithConfig(conf LoggerConfig) gin.HandlerFunc {
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
if !conf.PrintAccessLog() {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// Start timer
|
||||
start := time.Now()
|
||||
path := c.Request.URL.Path
|
||||
@@ -271,7 +284,7 @@ func LoggerWithConfig(conf LoggerConfig) gin.HandlerFunc {
|
||||
}
|
||||
c.Writer = bodyWriter
|
||||
|
||||
if conf.PrintBody {
|
||||
if conf.ContainsPath(c.Request.RequestURI) {
|
||||
buf, _ := io.ReadAll(c.Request.Body)
|
||||
rdr1 = io.NopCloser(bytes.NewBuffer(buf))
|
||||
rdr2 = io.NopCloser(bytes.NewBuffer(buf))
|
||||
@@ -309,7 +322,7 @@ func LoggerWithConfig(conf LoggerConfig) gin.HandlerFunc {
|
||||
|
||||
// fmt.Fprint(out, formatter(param))
|
||||
logger.Info(formatter(param))
|
||||
if conf.PrintBody {
|
||||
if conf.ContainsPath(c.Request.RequestURI) {
|
||||
respBody := readBody(bytes.NewReader(bodyWriter.body.Bytes()), c.Writer.Header().Get("Content-Encoding"))
|
||||
reqBody := readBody(rdr1, c.Request.Header.Get("Content-Encoding"))
|
||||
logger.Debugf("path:%s req body:%s resp:%s", path, reqBody, respBody)
|
||||
|
||||
@@ -70,10 +70,12 @@ type JWTAuth struct {
|
||||
RedisKeyPrefix string
|
||||
}
|
||||
|
||||
func GinEngine(mode string, cfg Config) *gin.Engine {
|
||||
func GinEngine(mode string, cfg Config, printBodyPaths func() map[string]struct{},
|
||||
printAccessLog func() bool) *gin.Engine {
|
||||
gin.SetMode(mode)
|
||||
|
||||
loggerMid := aop.Logger(aop.LoggerConfig{PrintBody: cfg.PrintBody})
|
||||
loggerMid := aop.Logger(aop.LoggerConfig{PrintAccessLog: printAccessLog,
|
||||
PrintBodyPaths: printBodyPaths})
|
||||
recoveryMid := aop.Recovery()
|
||||
|
||||
if strings.ToLower(mode) == "release" {
|
||||
@@ -84,10 +86,7 @@ func GinEngine(mode string, cfg Config) *gin.Engine {
|
||||
|
||||
r.Use(recoveryMid)
|
||||
|
||||
// whether print access log
|
||||
if cfg.PrintAccessLog {
|
||||
r.Use(loggerMid)
|
||||
}
|
||||
r.Use(loggerMid)
|
||||
|
||||
if cfg.PProf {
|
||||
pprof.Register(r, "/api/debug/pprof")
|
||||
|
||||
@@ -104,6 +104,56 @@ var I18N = `
|
||||
"builtin metric already exists":"内置指标已存在",
|
||||
"AlertRule already exists":"告警规则已存在",
|
||||
"This functionality has not been enabled. Please contact the system administrator to activate it.":"此功能尚未启用。请联系系统管理员启用"
|
||||
},
|
||||
"ja_JP": {
|
||||
"Username or password invalid": "ユーザー名またはパスワードが無効です",
|
||||
"incorrect verification code": "認証コードが正しくありません",
|
||||
"roles empty": "役割を空にすることはできません",
|
||||
"Username already exists": "このユーザー名は既に存在します。別のユーザー名を使用してください",
|
||||
"failed to count user-groups": "データの検証に失敗しました。もう一度お試しください",
|
||||
"UserGroup already exists": "グループ名は既に存在します。別の名前を使用してください",
|
||||
"members empty": "メンバーを空にすることはできません",
|
||||
"At least one team have rw permission": "少なくとも1つのチームに読み書き権限が必要です",
|
||||
"Failed to create BusiGroup(%s)": "[%s]の作成に失敗しました。もう一度お試しください",
|
||||
"business group id invalid": "ビジネスグループIDが正しくありません",
|
||||
"idents empty": "監視対象を空にすることはできません",
|
||||
"invalid tag(%s)": "タグ[%s]が無効です",
|
||||
"invalid tagkey(%s): cannot contains . ": "タグキー[%s]にドット(.)を含めることはできません",
|
||||
"invalid tagkey(%s): cannot contains _ ": "タグキー[%s]にアンダースコア(_)を含めることはできません",
|
||||
"invalid tagkey(%s)": "タグキー[%s]が無効です",
|
||||
"duplicate tagkey(%s)": "タグキー(%s)が重複しています",
|
||||
"name is empty": "名前を空にすることはできません",
|
||||
"Ident duplicate": "ダッシュボードの一意の識別子が既に存在します",
|
||||
"No such dashboard": "ダッシュボードが存在しません",
|
||||
"Name has invalid characters": "名前に無効な文字が含まれています",
|
||||
"Name is blank": "名前を空白にすることはできません",
|
||||
"forbidden": "権限がありません",
|
||||
"builtin alerts is empty, file: %s": "ビルトインアラートテンプレートが空です %s",
|
||||
"input json is empty": "提出内容を空にすることはできません",
|
||||
"fields empty": "選択フィールドを空にすることはできません",
|
||||
"No such AlertRule": "そのようなアラートルールはありません",
|
||||
"GroupId(%d) invalid": "ビジネスグループIDが無効です",
|
||||
"No such recording rule": "そのような記録ルールはありません",
|
||||
"tags is blank": "タグを空白にすることはできません",
|
||||
"oops... etime(%d) <= btime(%d)": "開始時間は終了時間より大きくすることはできません",
|
||||
"group_id invalid": "ビジネスグループが無効です",
|
||||
"No such AlertMute": "そのようなアラートミュートルールはありません",
|
||||
"rule_id and tags are both blank": "アラートルールとタグを同時に空にすることはできません",
|
||||
"rule is blank": "ルールを空にすることはできません",
|
||||
"rule invalid": "ルールが無効です。正しいかどうか確認してください",
|
||||
"unsupported field: %s": "フィールド %s はサポートされていません",
|
||||
"arg(batch) should be nonnegative": "batchは負の数にできません",
|
||||
"arg(tolerance) should be nonnegative": "toleranceは負の数にできません",
|
||||
"arg(timeout) should be nonnegative": "timeoutは負の数にできません",
|
||||
"arg(timeout) longer than five days": "timeoutは5日を超えることはできません",
|
||||
"arg(title) is required": "titleは必須項目です",
|
||||
"created task.id is zero": "作成されたタスクIDがゼロです",
|
||||
"invalid ibex address: %s": "ibex %s のアドレスが無効です",
|
||||
"url path invalid": "URLパスが無効です",
|
||||
"no such server": "そのようなインスタンスはありません",
|
||||
"admin role can not be modified": "管理者ロールは変更できません",
|
||||
"builtin payload already exists": "ビルトインテンプレートは既に存在します",
|
||||
"This functionality has not been enabled. Please contact the system administrator to activate it.": "この機能はまだ有効になっていません。システム管理者に連絡して有効にしてください"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -43,6 +43,7 @@ type SsoClient struct {
|
||||
Host string
|
||||
Port int
|
||||
BaseDn string
|
||||
BaseDns []string
|
||||
BindUser string
|
||||
BindPass string
|
||||
SyncAdd bool
|
||||
@@ -131,6 +132,8 @@ func (s *SsoClient) Reload(cf Config) {
|
||||
if s.SyncInterval > 0 {
|
||||
s.Ticker.Reset(s.SyncInterval * time.Second)
|
||||
}
|
||||
|
||||
s.BaseDns = strings.Split(s.BaseDn, "|")
|
||||
}
|
||||
|
||||
func (s *SsoClient) Copy() *SsoClient {
|
||||
@@ -158,27 +161,40 @@ func (s *SsoClient) LoginCheck(user, pass string) (*ldap.SearchResult, error) {
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sr, err := lc.ldapReq(conn, lc.AuthFilter, user)
|
||||
srs, err := lc.ldapReq(conn, lc.AuthFilter, user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ldap.error: ldap search fail: %v", err)
|
||||
}
|
||||
|
||||
if len(sr.Entries) == 0 {
|
||||
var sr *ldap.SearchResult
|
||||
|
||||
for i := range srs {
|
||||
if srs[i] == nil || len(srs[i].Entries) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 多个 dn 中,账号的唯一性由 LDAP 保证
|
||||
if len(srs[i].Entries) > 1 {
|
||||
return nil, fmt.Errorf("ldap.error: search user(%s), multi entries found", user)
|
||||
}
|
||||
|
||||
sr = srs[i]
|
||||
|
||||
if err := conn.Bind(srs[i].Entries[0].DN, pass); err != nil {
|
||||
return nil, fmt.Errorf("username or password invalid")
|
||||
}
|
||||
|
||||
for _, info := range srs[i].Entries[0].Attributes {
|
||||
logger.Infof("ldap.info: user(%s) info: %+v", user, info)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if sr == nil {
|
||||
return nil, fmt.Errorf("username or password invalid")
|
||||
}
|
||||
|
||||
if len(sr.Entries) > 1 {
|
||||
return nil, fmt.Errorf("ldap.error: search user(%s), multi entries found", user)
|
||||
}
|
||||
|
||||
if err := conn.Bind(sr.Entries[0].DN, pass); err != nil {
|
||||
return nil, fmt.Errorf("username or password invalid")
|
||||
}
|
||||
|
||||
for _, info := range sr.Entries[0].Attributes {
|
||||
logger.Infof("ldap.info: user(%s) info: %+v", user, info)
|
||||
}
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
@@ -218,21 +234,26 @@ func (s *SsoClient) newLdapConn() (*ldap.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (s *SsoClient) ldapReq(conn *ldap.Conn, filter string, values ...interface{}) (*ldap.SearchResult, error) {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
s.BaseDn, // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf(filter, values...), // The filter to apply
|
||||
s.genLdapAttributeSearchList(), // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
func (s *SsoClient) ldapReq(conn *ldap.Conn, filter string, values ...interface{}) ([]*ldap.SearchResult, error) {
|
||||
srs := make([]*ldap.SearchResult, 0, len(s.BaseDns))
|
||||
|
||||
sr, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ldap.error: ldap search fail: %v", err)
|
||||
for i := range s.BaseDns {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
strings.TrimSpace(s.BaseDns[i]), // The base dn to search
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf(filter, values...), // The filter to apply
|
||||
s.genLdapAttributeSearchList(), // A list attributes to retrieve
|
||||
nil,
|
||||
)
|
||||
sr, err := conn.Search(searchRequest)
|
||||
if err != nil {
|
||||
logger.Errorf("ldap.error: ldap search fail: %v", err)
|
||||
continue
|
||||
}
|
||||
srs = append(srs, sr)
|
||||
}
|
||||
|
||||
return sr, nil
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
// GetUserRolesAndTeams Gets the roles and teams of the user
|
||||
@@ -302,6 +323,7 @@ func LdapLogin(ctx *ctx.Context, username, pass string, defaultRoles []string, d
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy attributes from ldap
|
||||
ldap.RLock()
|
||||
attrs := ldap.Attributes
|
||||
|
||||
@@ -82,29 +82,35 @@ func (s *SsoClient) UserGetAll() (map[string]*models.User, error) {
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sr, err := lc.ldapReq(conn, lc.UserFilter)
|
||||
srs, err := lc.ldapReq(conn, lc.UserFilter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ldap.error: ldap search fail: %v", err)
|
||||
}
|
||||
|
||||
res := make(map[string]*models.User, len(sr.Entries))
|
||||
for _, entry := range sr.Entries {
|
||||
attrs := lc.Attributes
|
||||
username := entry.GetAttributeValue(attrs.Username)
|
||||
nickname := entry.GetAttributeValue(attrs.Nickname)
|
||||
email := entry.GetAttributeValue(attrs.Email)
|
||||
phone := entry.GetAttributeValue(attrs.Phone)
|
||||
res := make(map[string]*models.User)
|
||||
|
||||
// Gets the roles and teams for this entry
|
||||
roleTeamMapping := lc.GetUserRolesAndTeams(entry)
|
||||
if len(roleTeamMapping.Roles) == 0 {
|
||||
// No role mapping is configured, the configured default role is used
|
||||
roleTeamMapping.Roles = lc.DefaultRoles
|
||||
for i := range srs {
|
||||
if srs[i] == nil {
|
||||
continue
|
||||
}
|
||||
user := new(models.User)
|
||||
user.FullSsoFieldsWithTeams("ldap", username, nickname, phone, email, roleTeamMapping.Roles, roleTeamMapping.Teams)
|
||||
for _, entry := range srs[i].Entries {
|
||||
attrs := lc.Attributes
|
||||
username := entry.GetAttributeValue(attrs.Username)
|
||||
nickname := entry.GetAttributeValue(attrs.Nickname)
|
||||
email := entry.GetAttributeValue(attrs.Email)
|
||||
phone := entry.GetAttributeValue(attrs.Phone)
|
||||
|
||||
res[entry.GetAttributeValue(attrs.Username)] = user
|
||||
// Gets the roles and teams for this entry
|
||||
roleTeamMapping := lc.GetUserRolesAndTeams(entry)
|
||||
if len(roleTeamMapping.Roles) == 0 {
|
||||
// No role mapping is configured, the configured default role is used
|
||||
roleTeamMapping.Roles = lc.DefaultRoles
|
||||
}
|
||||
user := new(models.User)
|
||||
user.FullSsoFieldsWithTeams("ldap", username, nickname, phone, email, roleTeamMapping.Roles, roleTeamMapping.Teams)
|
||||
|
||||
res[entry.GetAttributeValue(attrs.Username)] = user
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
@@ -172,13 +178,20 @@ func (s *SsoClient) UserExist(username string) (bool, error) {
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sr, err := lc.ldapReq(conn, "(&(%s=%s))", lc.Attributes.Username, username)
|
||||
srs, err := lc.ldapReq(conn, "(&(%s=%s))", lc.Attributes.Username, username)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(sr.Entries) > 0 {
|
||||
return true, nil
|
||||
for i := range srs {
|
||||
if srs[i] == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(srs[i].Entries) > 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Dir string
|
||||
Level string
|
||||
Output string
|
||||
KeepHours uint
|
||||
RotateNum int
|
||||
RotateSize uint64
|
||||
Dir string
|
||||
Level string
|
||||
Output string
|
||||
KeepHours uint
|
||||
RotateNum int
|
||||
RotateSize uint64
|
||||
OutputToOneFile bool
|
||||
}
|
||||
|
||||
func Init(c Config) (func(), error) {
|
||||
@@ -35,6 +36,7 @@ func Init(c Config) (func(), error) {
|
||||
} else {
|
||||
return nil, errors.New("KeepHours and Rotatenum both are 0")
|
||||
}
|
||||
lb.OutputToOneFile(c.OutputToOneFile)
|
||||
|
||||
logger.SetLogging(c.Level, lb)
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func (s *SsoClient) exchangeUser(code string) (*CallbackOutput, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to exchange token: %s", err)
|
||||
}
|
||||
userInfo, err := s.getUserInfo(s.UserInfoAddr, oauth2Token.AccessToken, s.TranTokenMethod)
|
||||
userInfo, err := s.getUserInfo(s.Config.ClientID, s.UserInfoAddr, oauth2Token.AccessToken, s.TranTokenMethod)
|
||||
if err != nil {
|
||||
logger.Errorf("failed to get user info: %s", err)
|
||||
return nil, fmt.Errorf("failed to get user info: %s", err)
|
||||
@@ -223,10 +223,10 @@ func (s *SsoClient) exchangeUser(code string) (*CallbackOutput, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SsoClient) getUserInfo(UserInfoAddr, accessToken string, TranTokenMethod string) ([]byte, error) {
|
||||
func (s *SsoClient) getUserInfo(ClientId, UserInfoAddr, accessToken string, TranTokenMethod string) ([]byte, error) {
|
||||
var req *http.Request
|
||||
if TranTokenMethod == "formdata" {
|
||||
body := bytes.NewBuffer([]byte("access_token=" + accessToken))
|
||||
body := bytes.NewBuffer([]byte("access_token=" + accessToken + "&client_id=" + ClientId))
|
||||
r, err := http.NewRequest("POST", UserInfoAddr, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -234,7 +234,7 @@ func (s *SsoClient) getUserInfo(UserInfoAddr, accessToken string, TranTokenMetho
|
||||
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req = r
|
||||
} else if TranTokenMethod == "querystring" {
|
||||
r, err := http.NewRequest("GET", UserInfoAddr+"?access_token="+accessToken, nil)
|
||||
r, err := http.NewRequest("GET", UserInfoAddr+"?access_token="+accessToken+"&client_id="+ClientId, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -246,6 +246,7 @@ func (s *SsoClient) getUserInfo(UserInfoAddr, accessToken string, TranTokenMetho
|
||||
return nil, err
|
||||
}
|
||||
r.Header.Add("Authorization", "Bearer "+accessToken)
|
||||
r.Header.Add("client_id", ClientId)
|
||||
req = r
|
||||
}
|
||||
|
||||
|
||||
@@ -697,6 +697,18 @@ func (h *httpAPI) LabelValues(ctx context.Context, label string, matchs []string
|
||||
}
|
||||
|
||||
func (h *httpAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, Warnings, error) {
|
||||
for i := 0; i < 3; i++ {
|
||||
value, warnings, err := h.query(ctx, query, ts)
|
||||
if err == nil {
|
||||
return value, warnings, nil
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
return nil, nil, errors.New("query failed")
|
||||
}
|
||||
|
||||
func (h *httpAPI) query(ctx context.Context, query string, ts time.Time) (model.Value, Warnings, error) {
|
||||
u := h.client.URL(epQuery, nil)
|
||||
q := u.Query()
|
||||
|
||||
|
||||
@@ -48,10 +48,11 @@ func Initialize(configDir string, cryptoKey string) (func(), error) {
|
||||
|
||||
busiGroupCache := memsto.NewBusiGroupCache(ctx, stats)
|
||||
targetCache := memsto.NewTargetCache(ctx, stats, nil)
|
||||
configCvalCache := memsto.NewCvalCache(ctx, stats)
|
||||
|
||||
writers := writer.NewWriters(config.Pushgw)
|
||||
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP)
|
||||
r := httpx.GinEngine(config.Global.RunMode, config.HTTP, configCvalCache.PrintBodyPaths, configCvalCache.PrintAccessLog)
|
||||
rt := router.New(config.HTTP, config.Pushgw, config.Alert, targetCache, busiGroupCache, idents, metas, writers, ctx)
|
||||
rt.Config(r)
|
||||
|
||||
|
||||
@@ -48,6 +48,12 @@ func New(httpConfig httpx.Config, pushgw pconf.Pushgw, aconf aconf.Alert, tc *me
|
||||
}
|
||||
|
||||
func (rt *Router) Config(r *gin.Engine) {
|
||||
service := r.Group("/v1/n9e")
|
||||
if len(rt.HTTP.APIForService.BasicAuth) > 0 {
|
||||
service.Use(gin.BasicAuth(rt.HTTP.APIForService.BasicAuth))
|
||||
}
|
||||
service.POST("/target-update", rt.targetUpdate)
|
||||
|
||||
if !rt.HTTP.APIForAgent.Enable {
|
||||
return
|
||||
}
|
||||
@@ -69,7 +75,6 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
r.POST("/opentsdb/put", auth, rt.openTSDBPut)
|
||||
r.POST("/openfalcon/push", auth, rt.falconPush)
|
||||
r.POST("/prometheus/v1/write", auth, rt.remoteWrite)
|
||||
r.POST("/v1/n9e/target-update", auth, rt.targetUpdate)
|
||||
r.POST("/v1/n9e/edge/heartbeat", auth, rt.heartbeat)
|
||||
|
||||
if len(rt.Ctx.CenterApi.Addrs) > 0 {
|
||||
@@ -80,7 +85,6 @@ func (rt *Router) Config(r *gin.Engine) {
|
||||
r.POST("/opentsdb/put", rt.openTSDBPut)
|
||||
r.POST("/openfalcon/push", rt.falconPush)
|
||||
r.POST("/prometheus/v1/write", rt.remoteWrite)
|
||||
r.POST("/v1/n9e/target-update", rt.targetUpdate)
|
||||
r.POST("/v1/n9e/edge/heartbeat", rt.heartbeat)
|
||||
|
||||
if len(rt.Ctx.CenterApi.Addrs) > 0 {
|
||||
|
||||
@@ -3,6 +3,7 @@ package router
|
||||
import (
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
@@ -17,17 +18,18 @@ import (
|
||||
// heartbeat Forward heartbeat request to the center.
|
||||
func (rt *Router) heartbeat(c *gin.Context) {
|
||||
gid := ginx.QueryStr(c, "gid", "")
|
||||
overwriteGids := ginx.QueryBool(c, "overwrite_gids", false)
|
||||
req, err := HandleHeartbeat(c, rt.Aconf.Heartbeat.EngineName, rt.MetaSet)
|
||||
if err != nil {
|
||||
logger.Warningf("req:%v heartbeat failed to handle heartbeat err:%v", req, err)
|
||||
ginx.Dangerous(err)
|
||||
}
|
||||
api := "/v1/n9e/heartbeat"
|
||||
api := "/v1/n9e/center/heartbeat"
|
||||
if rt.HeartbeartApi != "" {
|
||||
api = rt.HeartbeartApi
|
||||
}
|
||||
|
||||
ret, err := poster.PostByUrlsWithResp[map[string]interface{}](rt.Ctx, api+"?gid="+gid, req)
|
||||
ret, err := poster.PostByUrlsWithResp[map[string]interface{}](rt.Ctx, fmt.Sprintf("%s?gid=%s&overwrite_gids=%t", api, gid, overwriteGids), req)
|
||||
ginx.NewRender(c).Data(ret, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ type RedisConfig struct {
|
||||
Username string
|
||||
Password string
|
||||
DB int
|
||||
UseTLS bool
|
||||
tlsx.ClientConfig
|
||||
RedisType string
|
||||
MasterName string
|
||||
|
||||
Reference in New Issue
Block a user