468 lines
12 KiB
Go
468 lines
12 KiB
Go
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)
|
|
}
|