package v1 import ( "context" "encoding/json" "fmt" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/data" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/controller" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/device" mc "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/model/metric" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/errors" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/internal/pkg/request" "git.inspur.com/sbg-jszt/cfn/cfn-schedule/pkg/log" "github.com/gin-gonic/gin" v1 "github.com/prometheus/client_golang/api/prometheus/v1" "github.com/prometheus/common/model" "strings" "time" ) type QueryBody struct { Rids string `json:"rids"` OrgId string `json:"orgId"` AssetType string `json:"assetType"` RuleNos string `json:"ruleNo"` Start string `json:"start"` End string `json:"end"` Step time.Duration `json:"step"` } type Metric struct { RuleNo string `json:"ruleNo"` RuleName string `json:"ruleName"` Data interface{} `json:"data"` } type Pair struct { Time model.Time `json:"time"` Value interface{} `json:"value"` } func QueryMetric(c *gin.Context) { body := request.GetBody(c) var b QueryBody err := json.Unmarshal(body, &b) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "请检查参数(body)是否符合要求!") return } var end time.Time if b.End != "" { end, err = time.Parse(time.RFC3339, b.End) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "invalid end") return } } else { end = time.Now() } var start time.Time if b.Start != "" { start, err = time.Parse(time.RFC3339, b.Start) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "invalid start") return } } else if b.End == "" { start = end } if end.Before(start) { controller.FailCode(c, errors.InvalidParameter, err, "invalid start and end") return } if b.Step == 0 { duration := end.Sub(start) durationSeconds := duration.Seconds() b.Step = time.Duration(durationSeconds/6) * time.Second if b.Step == 0 { b.Step = time.Second } } ruleNos := strings.Split(b.RuleNos, ",") if len(ruleNos) == 0 { controller.Success(c, nil) return } rids := strings.Split(b.Rids, ",") if len(rids) == 0 { controller.Success(c, nil) return } if data.V1api == nil { log.Info("prometheus client is nil") controller.Success(c, nil) return } device := &device.DeviceInfo{} devices, err := device.GetBySerialNos(rids) if len(devices) == 0 { controller.FailCode(c, errors.InvalidParameter, err, "rids are not exist") return } osType := make(map[string]string, len(devices)) for _, info := range devices { osType[info.SerialNo] = info.OsType } rule := mc.MetricsRule{} rules, err := rule.GetByRuleNo(ruleNos) if err != nil { controller.FailCode(c, errors.ServerError, err, "数据库错误!") return } if len(rules) == 0 { controller.Success(c, nil) return } result := make(map[string][]Metric, len(rids)) for _, rid := range rids { var isWindows bool if osType[rid] == "" || strings.ToLower(osType[rid]) == "windows" { isWindows = true } else { isWindows = false } result[rid] = []Metric{} for _, metricsRule := range rules { var ruleQl string if isWindows { ruleQl = metricsRule.RuleQlWindows } else { ruleQl = metricsRule.RuleQl } promQl := strings.ReplaceAll(ruleQl, "$rid", rid) queryRange, _, err := data.V1api.QueryRange(context.TODO(), promQl, v1.Range{ Start: start, End: end, Step: b.Step, }) if err != nil { log.Info("failed to query range, err: ", err.Error()) continue } var data interface{} resultType := queryRange.Type() switch resultType { case model.ValScalar: scalar := queryRange.(*model.Scalar) data = []Pair{ { scalar.Timestamp, scalar.Value, }, } case model.ValMatrix: matrix := queryRange.(model.Matrix) size := len(matrix) if size > 1 { temp := make(map[string][]Pair) for _, stream := range matrix { var pairs []Pair for _, value := range stream.Values { pairs = append(pairs, Pair{ value.Timestamp, value.Value, }) } key, _ := json.Marshal(stream.Metric) temp[string(key)] = pairs } data = temp } else if size > 0 { var pairs []Pair values := matrix[0].Values for i := range values { pairs = append(pairs, Pair{ values[i].Timestamp, values[i].Value, }) } data = pairs } case model.ValString: str := queryRange.(*model.String) data = []Pair{ { str.Timestamp, str.Value, }, } case model.ValVector: vector := queryRange.(model.Vector) size := len(vector) if size > 1 { temp := make(map[string][]Pair) for _, sample := range vector { key, _ := json.Marshal(sample.Metric.String()) temp[string(key)] = []Pair{{ sample.Timestamp, sample.Value}, } } data = temp } else if size > 0 { data = []Pair{ { vector[0].Timestamp, vector[0].Value, }, } } default: continue } result[rid] = append(result[rid], Metric{ metricsRule.RuleNo, metricsRule.RuleName, data, }) } } controller.Success(c, result) } type ProcessMetricQueryBody struct { Rid string `json:"rid"` PName string `json:"pName"` Start string `json:"start"` End string `json:"end"` Step time.Duration `json:"step"` } func QueryProcessMetric(c *gin.Context) { body := request.GetBody(c) var b ProcessMetricQueryBody err := json.Unmarshal(body, &b) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "请检查参数(body)是否符合要求!") return } if b.Rid == "" || b.PName == "" { controller.FailCode(c, errors.InvalidParameter, err, "请检查参数(body)是否符合要求!") } var end time.Time if b.End != "" { end, err = time.Parse(time.RFC3339, b.End) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "invalid end") return } } else { end = time.Now() } var start time.Time if b.Start != "" { start, err = time.Parse(time.RFC3339, b.Start) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "invalid start") return } } else if b.End == "" { start = end } if end.Before(start) { controller.FailCode(c, errors.InvalidParameter, err, "invalid start and end") return } if b.Step == 0 { duration := end.Sub(start) durationSeconds := duration.Seconds() b.Step = time.Duration(durationSeconds/6) * time.Second if b.Step == 0 { b.Step = time.Second } } d := &device.DeviceInfo{} r, err := d.GetBySerialNo(b.Rid) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "rid are not exist") return } isWindows := strings.ToLower(r.OsType) == "windows" promQLs := make(map[string]string) if isWindows { promQLs["read"] = fmt.Sprintf( `rate(windows_process_io_bytes_total{rid="%s", process="%s", mode="read"}[5m])`, b.Rid, b.PName) promQLs["write"] = fmt.Sprintf( `rate(windows_process_io_bytes_total{rid="%s", process="%s", mode="write"}[5m])`, b.Rid, b.PName) promQLs["cpu"] = fmt.Sprintf( `sum(rate(windows_process_cpu_time_total{rid="%s", process="%s"}[5m]))`, b.Rid, b.PName) promQLs["memory"] = fmt.Sprintf( `sum(windows_process_page_file_bytes{rid="%s", process="%s"})`, b.Rid, b.PName) } else { promQLs["read"] = fmt.Sprintf( `rate(namedprocess_namegroup_read_bytes_total{rid="%s",groupname="%s"}[5m])`, b.Rid, b.PName) promQLs["write"] = fmt.Sprintf( `rate(namedprocess_namegroup_write_bytes_total{rid="%s",groupname="%s"}[5m])`, b.Rid, b.PName) promQLs["cpu"] = fmt.Sprintf( `sum(rate(namedprocess_namegroup_cpu_seconds_total{rid="%s",groupname="%s"}[5m]))`, b.Rid, b.PName) promQLs["memory"] = fmt.Sprintf( `sum(namedprocess_namegroup_memory_bytes{rid="%s",groupname="%s"})`, b.Rid, b.PName) } result := make(map[string][]Pair) for key, promQL := range promQLs { queryRange, _, err := data.V1api.QueryRange(context.TODO(), promQL, v1.Range{ Start: start, End: end, Step: b.Step, }) if err != nil { log.Info("failed to query range, err: ", err.Error()) continue } var pairs []Pair resultType := queryRange.Type() switch resultType { case model.ValScalar: scalar := queryRange.(*model.Scalar) pairs = append(pairs, Pair{ scalar.Timestamp, scalar.Value, }) case model.ValMatrix: matrix := queryRange.(model.Matrix) if len(matrix) == 0 { continue } values := matrix[0].Values for i := range values { pairs = append(pairs, Pair{ values[i].Timestamp, values[i].Value, }) } case model.ValString: str := queryRange.(*model.String) pairs = append(pairs, Pair{ str.Timestamp, str.Value, }) case model.ValVector: vector := queryRange.(model.Vector) for i := range vector { pairs = append(pairs, Pair{ vector[i].Timestamp, vector[i].Value, }) } default: continue } result[key] = pairs } controller.Success(c, result) } func QueryProcessListMetric(c *gin.Context) { rid := c.Param("rid") d := &device.DeviceInfo{} r, err := d.GetBySerialNo(rid) if err != nil { controller.FailCode(c, errors.InvalidParameter, err, "rid are not exist") return } isWindows := strings.ToLower(r.OsType) == "windows" promQLs := make(map[string]string) if isWindows { promQLs["pName"] = fmt.Sprintf( `sum(windows_process_start_time{rid="%s"}) by (process)`, rid) promQLs["state"] = fmt.Sprintf( `sum(windows_process_threads{rid="%s"}) by (process) > 0`, rid) promQLs["cpu"] = fmt.Sprintf( `sum(rate(windows_process_cpu_time_total{rid="%s"}[5m])) by (process)`, rid) promQLs["memory"] = fmt.Sprintf( `sum(windows_process_page_file_bytes{rid="%s"}) by (process)`, rid) promQLs["diskIO"] = fmt.Sprintf( `sum(rate(windows_process_io_bytes_total{rid="%s", mode="read"}[5m])) by (process)`, rid) } else { promQLs["pName"] = fmt.Sprintf( `namedprocess_namegroup_num_procs{rid="%s"} == 1`, rid) promQLs["state"] = fmt.Sprintf( `sum(namedprocess_namegroup_states{rid="%s", state=~"Other|Zombie"}) by (groupname) == 0`, rid) promQLs["cpu"] = fmt.Sprintf( `sum(rate(namedprocess_namegroup_cpu_seconds_total{rid="%s"}[5m])) by (groupname)`, rid) promQLs["memory"] = fmt.Sprintf( `sum(namedprocess_namegroup_memory_bytes{rid="%s"}) by (groupname)`, rid) promQLs["diskIO"] = fmt.Sprintf( `rate(namedprocess_namegroup_read_bytes_total{rid="%s"}[5m]) + rate(namedprocess_namegroup_write_bytes_total{rid="%s"}[5m])`, rid, rid) } temp := make(map[string][]*model.Sample) time := time.Now() for key, promQL := range promQLs { queryRange, _, err := data.V1api.Query(context.TODO(), promQL, time) if err != nil { log.Info("failed to query range, err: ", err.Error()) continue } if queryRange.Type() == model.ValVector { temp[key] = queryRange.(model.Vector) } } samples := temp["pName"] result := make(map[string]map[string]interface{}, len(samples)) var processName model.LabelName if isWindows { processName = "process" } else { processName = "groupname" } for _, sample := range samples { result[string(sample.Metric[processName])] = make(map[string]interface{}, 3) } delete(temp, "pName") samples = temp["state"] for _, sample := range samples { pName := string(sample.Metric[processName]) if _, ok := result[pName]; ok { result[pName]["status"] = true } } delete(temp, "state") for key, value := range temp { for _, sample := range value { pName := string(sample.Metric[processName]) if _, ok := result[pName]; !ok { continue } result[pName][key] = sample.Value } } controller.Success(c, result) }