feat: переход на pvesh API для получения данных о нодах и ресурсах
- Заменен ping на pvesh API для определения статуса нод - Добавлена функция getNodesFromPvesh для получения списка нод через pvesh get /nodes - Добавлена функция getNodeStatusFromPvesh для получения статуса ноды через pvesh get /nodes/{node}/status - Улучшена функция getNodeResources - теперь использует pvesh API с fallback на /proc - Улучшена функция getNodeHardwareInfo - теперь использует pvesh API с fallback на /proc - Добавлен сбор реальных данных о CPU usage, memory usage, load average через pvesh - Улучшена точность данных о ресурсах нод Автор: Сергей Антропов, сайт: https://devops.org.ru
This commit is contained in:
parent
7f2b25e94d
commit
b229c8fcdd
@ -521,14 +521,8 @@ func parseSizeToGB(sizeStr string) (float64, error) {
|
||||
func collectDetailedNodesInfo(ctx context.Context, clusterName, clusterUUID string) ([]map[string]any, error) {
|
||||
var nodes []map[string]any
|
||||
|
||||
// Получаем данные из pvecm nodes (имена нод)
|
||||
nodesData := parsePvecmNodes(ctx)
|
||||
|
||||
// Получаем данные из pvecm status (IP адреса)
|
||||
statusData := parsePvecmStatus(ctx)
|
||||
|
||||
// Объединяем данные
|
||||
combinedNodes := combineNodeInfo(nodesData, statusData)
|
||||
// Получаем данные о нодах через pvesh API
|
||||
combinedNodes := getNodesFromPvesh(ctx)
|
||||
|
||||
// Если не удалось получить данные через pvecm, создаем информацию о текущей ноде
|
||||
if len(combinedNodes) == 0 {
|
||||
@ -605,13 +599,8 @@ func collectDetailedNodesInfo(ctx context.Context, clusterName, clusterUUID stri
|
||||
}
|
||||
}
|
||||
|
||||
// Определяем статус ноды
|
||||
isOnline := true // По умолчанию считаем ноду онлайн
|
||||
|
||||
// Если это не локальная нода, проверяем доступность через ping
|
||||
if !isLocal {
|
||||
isOnline = checkNodeOnline(ctx, nodeIP)
|
||||
}
|
||||
// Определяем статус ноды из pvesh данных
|
||||
isOnline := getNodeStatusFromPvesh(ctx, nodeName)
|
||||
|
||||
// Создаем структуру ноды с правильным порядком полей
|
||||
node := map[string]any{
|
||||
@ -704,6 +693,106 @@ func collectDetailedNodesInfo(ctx context.Context, clusterName, clusterUUID stri
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
// getNodesFromPvesh получает информацию о нодах через pvesh API
|
||||
func getNodesFromPvesh(ctx context.Context) []NodeInfo {
|
||||
var nodes []NodeInfo
|
||||
|
||||
// Проверяем наличие pvesh
|
||||
if _, err := exec.LookPath("pvesh"); err != nil {
|
||||
return nodes
|
||||
}
|
||||
|
||||
// Получаем список нод
|
||||
cmd := exec.CommandContext(ctx, "pvesh", "get", "/nodes", "--output-format", "json")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nodes
|
||||
}
|
||||
|
||||
var nodesData []map[string]any
|
||||
if err := json.Unmarshal(out, &nodesData); err != nil {
|
||||
return nodes
|
||||
}
|
||||
|
||||
// Преобразуем данные в структуру NodeInfo
|
||||
for i, nodeData := range nodesData {
|
||||
nodeID := i + 1
|
||||
name := ""
|
||||
status := "offline"
|
||||
|
||||
if n, ok := nodeData["node"].(string); ok {
|
||||
name = n
|
||||
}
|
||||
if s, ok := nodeData["status"].(string); ok {
|
||||
status = s
|
||||
}
|
||||
|
||||
// Получаем IP адрес ноды
|
||||
var nodeIP string
|
||||
if name != "" {
|
||||
// Получаем детальную информацию о ноде
|
||||
detailCmd := exec.CommandContext(ctx, "pvesh", "get", fmt.Sprintf("/nodes/%s/status", name), "--output-format", "json")
|
||||
detailOut, err := detailCmd.Output()
|
||||
if err == nil {
|
||||
var detailData map[string]any
|
||||
if err := json.Unmarshal(detailOut, &detailData); err == nil {
|
||||
if ip, ok := detailData["ip"].(string); ok {
|
||||
nodeIP = ip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Определяем votes (обычно 1 для каждой ноды)
|
||||
votes := 1
|
||||
if status == "online" {
|
||||
votes = 1
|
||||
} else {
|
||||
votes = 0
|
||||
}
|
||||
|
||||
nodes = append(nodes, NodeInfo{
|
||||
NodeID: nodeID,
|
||||
Votes: votes,
|
||||
Name: name,
|
||||
IP: nodeIP,
|
||||
})
|
||||
}
|
||||
|
||||
return nodes
|
||||
}
|
||||
|
||||
// getNodeStatusFromPvesh получает статус ноды через pvesh API
|
||||
func getNodeStatusFromPvesh(ctx context.Context, nodeName string) bool {
|
||||
if nodeName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Проверяем наличие pvesh
|
||||
if _, err := exec.LookPath("pvesh"); err != nil {
|
||||
return true // Если pvesh недоступен, считаем ноду онлайн
|
||||
}
|
||||
|
||||
// Получаем статус ноды
|
||||
cmd := exec.CommandContext(ctx, "pvesh", "get", fmt.Sprintf("/nodes/%s/status", nodeName), "--output-format", "json")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var statusData map[string]any
|
||||
if err := json.Unmarshal(out, &statusData); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Проверяем статус
|
||||
if status, ok := statusData["status"].(string); ok {
|
||||
return status == "online"
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeInfo структура для хранения информации о ноде
|
||||
type NodeInfo struct {
|
||||
NodeID int
|
||||
@ -1030,9 +1119,41 @@ func getNodeOSInfo(ctx context.Context) (map[string]any, error) {
|
||||
}
|
||||
|
||||
func getNodeHardwareInfo(ctx context.Context) (map[string]any, error) {
|
||||
result := map[string]any{}
|
||||
result := map[string]any{
|
||||
"cpu_model": "",
|
||||
"cpu_cores": 0,
|
||||
"sockets": 0,
|
||||
"threads": 0,
|
||||
"memory_total_mb": 0,
|
||||
}
|
||||
|
||||
// CPU информация
|
||||
// Пробуем получить данные через pvesh API
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
if _, err := exec.LookPath("pvesh"); err == nil {
|
||||
// Получаем информацию о ноде через pvesh
|
||||
cmd := exec.CommandContext(ctx, "pvesh", "get", fmt.Sprintf("/nodes/%s/status", hostname), "--output-format", "json")
|
||||
out, err := cmd.Output()
|
||||
if err == nil {
|
||||
var statusData map[string]any
|
||||
if err := json.Unmarshal(out, &statusData); err == nil {
|
||||
// CPU cores
|
||||
if maxcpu, ok := statusData["maxcpu"].(float64); ok {
|
||||
result["cpu_cores"] = int(maxcpu)
|
||||
result["threads"] = int(maxcpu)
|
||||
}
|
||||
|
||||
// Memory total
|
||||
if maxmem, ok := statusData["maxmem"].(float64); ok {
|
||||
result["memory_total_mb"] = int(maxmem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: получаем данные из /proc если pvesh недоступен
|
||||
if result["cpu_cores"].(int) == 0 {
|
||||
// CPU информация из /proc/cpuinfo
|
||||
if data, err := os.ReadFile("/proc/cpuinfo"); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
var cpuModel string
|
||||
@ -1070,10 +1191,12 @@ func getNodeHardwareInfo(ctx context.Context) (map[string]any, error) {
|
||||
result["cpu_model"] = cpuModel
|
||||
result["cpu_cores"] = cores
|
||||
result["sockets"] = sockets
|
||||
result["threads"] = cores // В упрощенном виде
|
||||
result["threads"] = cores
|
||||
}
|
||||
}
|
||||
|
||||
// Memory
|
||||
if result["memory_total_mb"].(int) == 0 {
|
||||
// Memory информация из /proc/meminfo
|
||||
if data, err := os.ReadFile("/proc/meminfo"); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
@ -1087,17 +1210,84 @@ func getNodeHardwareInfo(ctx context.Context) (map[string]any, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func getNodeResources(ctx context.Context) (map[string]any, error) {
|
||||
result := map[string]any{}
|
||||
result := map[string]any{
|
||||
"cpu_usage_percent": 0.0,
|
||||
"memory_used_mb": 0,
|
||||
"swap_used_mb": 0,
|
||||
"loadavg": []float64{0, 0, 0},
|
||||
}
|
||||
|
||||
// CPU usage (упрощенная версия)
|
||||
result["cpu_usage_percent"] = 0.0
|
||||
// Пробуем получить данные через pvesh API
|
||||
if hostname, err := os.Hostname(); err == nil {
|
||||
if _, err := exec.LookPath("pvesh"); err == nil {
|
||||
// Получаем ресурсы через pvesh
|
||||
cmd := exec.CommandContext(ctx, "pvesh", "get", fmt.Sprintf("/nodes/%s/status", hostname), "--output-format", "json")
|
||||
out, err := cmd.Output()
|
||||
if err == nil {
|
||||
var statusData map[string]any
|
||||
if err := json.Unmarshal(out, &statusData); err == nil {
|
||||
// CPU usage
|
||||
if cpu, ok := statusData["cpu"].(float64); ok {
|
||||
result["cpu_usage_percent"] = cpu * 100
|
||||
}
|
||||
|
||||
// Memory usage
|
||||
if mem, ok := statusData["memory"].(float64); ok {
|
||||
result["memory_used_mb"] = int(mem)
|
||||
}
|
||||
|
||||
// Load average
|
||||
if loadavg, ok := statusData["loadavg"].([]interface{}); ok && len(loadavg) >= 3 {
|
||||
var load []float64
|
||||
for i := 0; i < 3; i++ {
|
||||
if val, ok := loadavg[i].(float64); ok {
|
||||
load = append(load, val)
|
||||
} else {
|
||||
load = append(load, 0)
|
||||
}
|
||||
}
|
||||
result["loadavg"] = load
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: получаем данные из /proc если pvesh недоступен
|
||||
if result["cpu_usage_percent"].(float64) == 0.0 {
|
||||
// CPU usage из /proc/stat
|
||||
if data, err := os.ReadFile("/proc/stat"); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
if len(lines) > 0 {
|
||||
fields := strings.Fields(lines[0])
|
||||
if len(fields) >= 8 {
|
||||
// Простой расчет CPU usage
|
||||
var total, idle uint64
|
||||
for i := 1; i < len(fields); i++ {
|
||||
if val, err := strconv.ParseUint(fields[i], 10, 64); err == nil {
|
||||
total += val
|
||||
if i == 4 { // idle time
|
||||
idle = val
|
||||
}
|
||||
}
|
||||
}
|
||||
if total > 0 {
|
||||
usage := float64(total-idle) / float64(total) * 100
|
||||
result["cpu_usage_percent"] = usage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result["memory_used_mb"].(int) == 0 {
|
||||
// Memory usage из /proc/meminfo
|
||||
if data, err := os.ReadFile("/proc/meminfo"); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
var total, free, buffers, cached uint64
|
||||
@ -1121,11 +1311,10 @@ func getNodeResources(ctx context.Context) (map[string]any, error) {
|
||||
used := total - free - buffers - cached
|
||||
result["memory_used_mb"] = int(used / 1024)
|
||||
}
|
||||
}
|
||||
|
||||
// Swap
|
||||
result["swap_used_mb"] = 0
|
||||
|
||||
// Load average
|
||||
if len(result["loadavg"].([]float64)) == 0 || result["loadavg"].([]float64)[0] == 0 {
|
||||
// Load average из /proc/loadavg
|
||||
if data, err := os.ReadFile("/proc/loadavg"); err == nil {
|
||||
fields := strings.Fields(string(data))
|
||||
if len(fields) >= 3 {
|
||||
@ -1133,11 +1322,14 @@ func getNodeResources(ctx context.Context) (map[string]any, error) {
|
||||
for i := 0; i < 3; i++ {
|
||||
if val, err := strconv.ParseFloat(fields[i], 64); err == nil {
|
||||
loadavg = append(loadavg, val)
|
||||
} else {
|
||||
loadavg = append(loadavg, 0)
|
||||
}
|
||||
}
|
||||
result["loadavg"] = loadavg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user