commit c2f972220e112946da6a03b534e54e088ca877f2 Author: dustoair <107600816+dustoair@users.noreply.github.com> Date: Sun Nov 19 18:01:59 2023 +0800 metrics data info diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7989761 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.idea +go.sum +*.exe +host.csv +monitor.csv +logs +*.log \ No newline at end of file diff --git a/cpu.go b/cpu.go new file mode 100644 index 0000000..a54cc46 --- /dev/null +++ b/cpu.go @@ -0,0 +1,41 @@ +/* +* +@author: sre +@date: 2023/11/19 0019 +@desc: todo +* +*/ +package main + +import "strings" + +// getCPUInfo 获取cpu核心 使用率 +func getCPUInfo(ip, port string) (int, float64, error) { + var cpuTotal, cpuIdle float64 + var cpuCount int + data, err := getMetrics(ip, port) + if err != nil { + return 0, 0, err + + } + // 解析metrics数据 + lines := strings.Split(data, "\n") + for _, line := range lines { + if strings.HasPrefix(line, "node_cpu_seconds_total") { + fields := strings.Fields(line) + if len(fields) == 2 { + cpuData, err := extractMetricValue(fields[1]) + if err != nil { + //fmt.Println("无法提取指标值: ", err) + return 0, 0, err + } + cpuTotal += cpuData + if strings.Contains(line, "mode=\"idle\"") { + cpuIdle += cpuData + cpuCount += 1 + } + } + } + } + return cpuCount, 100 * (1 - cpuIdle/cpuTotal), nil +} diff --git a/csv.go b/csv.go new file mode 100644 index 0000000..fa905cf --- /dev/null +++ b/csv.go @@ -0,0 +1,50 @@ +/* +* +@author: sre +@date: 2023/11/19 0019 +@desc: todo +* +*/ +package main + +import ( + "github.com/gocarina/gocsv" + "os" +) + +// Monitor for output +type Monitor struct { + IP string `csv:"ip"` + Port string `csv:"port"` + CPUCore int `csv:"cpu_core"` + CPUUsage float64 `csv:"cpu_usage"` + MemTotal float64 `csv:"mem_total"` + MemAvailable float64 `csv:"mem_available"` + NodeLoad1 float64 `csv:"node_load1"` + NodeLoad5 float64 `csv:"node_load5"` + NodeLoad15 float64 `csv:"node_load15"` + ERROR string `csv:"error"` +} + +// Host for input +type Host struct { + IP string `csv:"ip"` + Port string `csv:"port"` +} + +func GetHostListFromCSV() ([]*Host, error) { + hostssFile, err := os.OpenFile(INPUTFILE, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + return nil, err + } + defer hostssFile.Close() + + hosts := []*Host{} + + if err := gocsv.UnmarshalFile(hostssFile, &hosts); err != nil { // Load hosts from file + //if err := gocsv.UnmarshalBytes(CSVDATA, &hosts); err != nil { // Load hosts from file + return nil, err + } + return hosts, nil + +} diff --git a/csv_test.go b/csv_test.go new file mode 100644 index 0000000..847c1d7 --- /dev/null +++ b/csv_test.go @@ -0,0 +1,30 @@ +/* +* +@author: sre +@date: 2023/11/19 0019 +@desc: todo +* +*/ +package main + +import ( + "fmt" + "testing" +) + +func TestGetHostListFromCSV(t *testing.T) { + hosts, err := GetHostListFromCSV() + if err != nil { + fmt.Println(err) + } + for _, host := range hosts { + fmt.Println(host) + } + +} + +func TestStrclean(t *testing.T) { + s1 := "\"30.1 30.251.46,\"," + fmt.Println(strClean(s1)) + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..866511a --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module exporterData + +go 1.21 + +require github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a diff --git a/host1.csv b/host1.csv new file mode 100644 index 0000000..00826e7 --- /dev/null +++ b/host1.csv @@ -0,0 +1,3 @@ +ip,port +119.91.19.44,9100 +119.91.19.44,9100 \ No newline at end of file diff --git a/input_sample.csv b/input_sample.csv new file mode 100644 index 0000000..e9bc013 --- /dev/null +++ b/input_sample.csv @@ -0,0 +1,2 @@ +ip,port +127.0.0.1,19100 \ No newline at end of file diff --git a/load.go b/load.go new file mode 100644 index 0000000..3f95a2c --- /dev/null +++ b/load.go @@ -0,0 +1,58 @@ +/* +* +@author: sre +@date: 2023/11/19 0019 +@desc: todo +* +*/ +package main + +import "strings" + +func getLoadInfo(ip, port string) (float64, float64, float64, error) { + var node_load1, node_load5, node_load15 float64 + data, err := getMetrics(ip, port) + if err != nil { + return 0, 0, 0, err + + } + // 解析metrics数据 + lines := strings.Split(data, "\n") + for _, line := range lines { + //node_load1 + if strings.HasPrefix(line, "node_load1 ") { + fields := strings.Fields(line) + if len(fields) == 2 { + node_load1, err = extractMetricValue(fields[1]) + if err != nil { + return 0, 0, 0, err + } + } + + } + //node_load5 + if strings.HasPrefix(line, "node_load5 ") { + fields := strings.Fields(line) + if len(fields) == 2 { + node_load1, err = extractMetricValue(fields[1]) + if err != nil { + return 0, 0, 0, err + } + } + + } + //node_load15 + if strings.HasPrefix(line, "node_load15 ") { + fields := strings.Fields(line) + if len(fields) == 2 { + node_load1, err = extractMetricValue(fields[1]) + if err != nil { + return 0, 0, 0, err + } + } + + } + } + return node_load1, node_load5, node_load15, nil + +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..05f4967 --- /dev/null +++ b/main.go @@ -0,0 +1,126 @@ +package main + +import ( + "fmt" + "github.com/gocarina/gocsv" + "io" + "log" + "net" + "net/http" + "os" + "strconv" + "strings" + "time" +) + +func main() { + monitorsFile, err := os.OpenFile(OUTPUTFILE, os.O_RDWR|os.O_CREATE, os.ModePerm) + if err != nil { + panic(err) + } + defer monitorsFile.Close() + monitors := []*Monitor{} + + hosts, err := GetHostListFromCSV() + if err != nil { + fmt.Println(err) + } + totalHosts := len(hosts) + count := 0 + log.Println("本次要处理主机数量: ", totalHosts) + for _, host := range hosts { + fmt.Println(host) + var errorflag string + serverIP := strClean(host.IP) + if !isTCPOpen(serverIP, host.Port) { + count += 1 + log.Println(count, "/", totalHosts, "端口不通: ", serverIP, host.Port) + continue + } + cpuCore, cpuUsage, err1 := getCPUInfo(serverIP, host.Port) + memTotal, memAvailable, err2 := getMemoryInfo(serverIP, host.Port) + nodeLoad1, nodeLoad5, nodeLoad15, err3 := getLoadInfo(serverIP, host.Port) + if err1 != nil || err2 != nil || err3 != nil { + fmt.Println("host error: ", err) + errorflag = "error" + } + + monitors = append(monitors, &Monitor{ + IP: serverIP, + Port: host.Port, + CPUCore: cpuCore, + CPUUsage: cpuUsage, + MemTotal: memTotal, + MemAvailable: memAvailable, + NodeLoad1: nodeLoad1, + NodeLoad5: nodeLoad5, + NodeLoad15: nodeLoad15, + ERROR: errorflag, + }) + count += 1 + log.Println(count, "/", totalHosts, " handled: ", serverIP) + } + + err = gocsv.MarshalFile(&monitors, monitorsFile) // Use this to save the CSV back to the file + if err != nil { + panic(err) + } + +} + +func getMetrics(ip, port string) (string, error) { + // 发送HTTP请求获取Node Exporter的metrics数据 + address := "http://" + ip + ":" + port + "/metrics" + response, err := http.Get(address) + if err != nil { + log.Println("无法发送HTTP请求:", err) + //log.Fatal("无法发送HTTP请求:", err) + return "", err + } + defer response.Body.Close() + + // 读取响应内容 + body, err := io.ReadAll(response.Body) + if err != nil { + log.Println("无法读取响应内容:", err) + //log.Fatal("无法读取响应内容:", err) + return "", err + } + return string(body), nil + +} + +// 从指标行中提取指标值,并将其转换为浮点数类型 +func extractMetricValue(value string) (float64, error) { + // 去除指标值的单位(此处假设单位为'count') + value = strings.Split(value, " ")[0] + // 将指标值转换为浮点数类型并返回 + return parseFloat(value) +} + +// 将字符串转换为浮点数类型,并处理可能的错误情况 +func parseFloat(value string) (float64, error) { + f, err := strconv.ParseFloat(value, 64) + if err != nil { + return 0, err + } + return f, nil +} + +func strClean(str string) string { + str = strings.ReplaceAll(str, " ", "") + str = strings.ReplaceAll(str, ",", "") + str = strings.ReplaceAll(str, "\"", "") + return str +} + +func isTCPOpen(ip, port string) bool { + // 尝试在指定端口上建立连接 + address := ip + ":" + port + conn, err := net.DialTimeout("tcp", address, time.Millisecond*10) + if err != nil { + return false + } + defer conn.Close() + return true +} diff --git a/mem.go b/mem.go new file mode 100644 index 0000000..2df25b2 --- /dev/null +++ b/mem.go @@ -0,0 +1,75 @@ +/* +* +@author: sre +@date: 2023/11/19 0019 +@desc: todo +* +*/ +package main + +import ( + "strings" +) + +// getMemoryInfo 返回总内存 可用内存 +func getMemoryInfo(ip, port string) (float64, float64, error) { + var memTotal, memAvailable float64 + data, err := getMetrics(ip, port) + if err != nil { + return 0, 0, err + } + // 解析metrics数据 + lines := strings.Split(data, "\n") + for _, line := range lines { + //memTotal + if strings.HasPrefix(line, "node_memory_MemTotal_bytes ") { + fields := strings.Fields(line) + if len(fields) == 2 { + memTotal, err = extractMetricValue(fields[1]) + if err != nil { + return 0, 0, err + } + } + + } + //memAvailable + if strings.HasPrefix(line, "node_memory_MemAvailable_bytes ") { + fields := strings.Fields(line) + if len(fields) == 2 { + memAvailable, err = extractMetricValue(fields[1]) + if err != nil { + return 0, 0, err + } + } + + } + + } + return memTotal, memAvailable, nil +} + +//// 解析Node Exporter的响应内容,提取内存使用率指标 +//func parseMemoryUsage(response string) float64 { +// // 在响应中找到相关的指标行,例如 "node_memory_MemTotal: 167772160000 149510252888 149510252888" +// lines := strings.Split(response, "\n") +// for _, line := range lines { +// if strings.Contains(line, "node_memory_MemTotal") { +// fields := strings.Fields(line) +// if len(fields) >= 4 { +// // 从指标行中提取内存使用率指标,并将其转换为百分比形式 +// memoryUsage := strings.TrimSuffix(fields[2], " ") + "%" +// return getPercentage(memoryUsage) +// } +// } +// } +// +// // 如果找不到相关的指标行,则返回0% +// return 0.0 +//} +// +//// 从字符串中提取百分比值,并将其转换为浮点数形式 +//func getPercentage(percentage string) float64 { +// // 去掉百分号,并将其转换为浮点数形式 +// percentage = strings.ReplaceAll(percentage, "%", "") +// return parseFloat(percentage) * (100.0 / 100) // 将字符串转换为浮点数,并乘以100转换为百分比形式 +//} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..cc900be --- /dev/null +++ b/readme.md @@ -0,0 +1,16 @@ + + +交叉编译 +```cmd +SET CGO_ENABLED=0 +SET GOOS=linux +SET GOARCH=amd64 +go build +``` + +```cmd +SET CGO_ENABLED=0 +SET GOOS=linux +SET GOARCH=arm64 +go build +``` \ No newline at end of file diff --git a/var.go b/var.go new file mode 100644 index 0000000..01a2a27 --- /dev/null +++ b/var.go @@ -0,0 +1,13 @@ +/* +* +@author: sre +@date: 2023/11/19 0019 +@desc: todo +* +*/ +package main + +const ( + INPUTFILE = "host.csv" + OUTPUTFILE = "monitor.csv" +)