mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-25 06:56:17 +01:00
updates
This commit is contained in:
@@ -28,8 +28,10 @@ import (
|
|||||||
|
|
||||||
// ansiEscapePattern matches ANSI escape sequences (colors, cursor movement, etc.)
|
// ansiEscapePattern matches ANSI escape sequences (colors, cursor movement, etc.)
|
||||||
// This includes CSI sequences like \x1b[...m and simple escapes like \x1b[K
|
// This includes CSI sequences like \x1b[...m and simple escapes like \x1b[K
|
||||||
var ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[@-Z\\-_]`)
|
var (
|
||||||
var dockerContainerIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{12,64}$`)
|
ansiEscapePattern = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[@-Z\\-_]`)
|
||||||
|
dockerContainerIDPattern = regexp.MustCompile(`^[a-fA-F0-9]{12,64}$`)
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Docker API timeout in milliseconds
|
// Docker API timeout in milliseconds
|
||||||
|
|||||||
29
agent/pve.go
29
agent/pve.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -22,24 +23,29 @@ type pveManager struct {
|
|||||||
// Returns stats for all running VMs/LXCs
|
// Returns stats for all running VMs/LXCs
|
||||||
func (pm *pveManager) getPVEStats() ([]*container.Stats, error) {
|
func (pm *pveManager) getPVEStats() ([]*container.Stats, error) {
|
||||||
if pm.client == nil {
|
if pm.client == nil {
|
||||||
|
slog.Info("PVE client not configured")
|
||||||
return nil, errors.New("PVE client not configured")
|
return nil, errors.New("PVE client not configured")
|
||||||
}
|
}
|
||||||
cluster, err := pm.client.Cluster(context.Background())
|
cluster, err := pm.client.Cluster(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
slog.Error("Error getting cluster", "err", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
slog.Info("PVE cluster", "cluster", cluster)
|
||||||
resources, err := cluster.Resources(context.Background(), "vm")
|
resources, err := cluster.Resources(context.Background(), "vm")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
slog.Error("Error getting resources", "err", err, "resources", resources)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
slog.Info("PVE resources", "resources", resources)
|
||||||
containersLength := len(resources)
|
containersLength := len(resources)
|
||||||
|
slog.Info("PVE containers length", "containersLength", containersLength)
|
||||||
containerIds := make(map[string]struct{}, containersLength)
|
containerIds := make(map[string]struct{}, containersLength)
|
||||||
|
|
||||||
// only include running vms and lxcs on selected node
|
// only include running vms and lxcs on selected node
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
if resource.Node == pm.nodeName && resource.Status == "running" {
|
if resource.Node == pm.nodeName && resource.Status == "running" {
|
||||||
|
slog.Info("PVE resource", "resource", resource)
|
||||||
containerIds[resource.ID] = struct{}{}
|
containerIds[resource.ID] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,6 +59,7 @@ func (pm *pveManager) getPVEStats() ([]*container.Stats, error) {
|
|||||||
// populate stats
|
// populate stats
|
||||||
stats := make([]*container.Stats, 0, len(containerIds))
|
stats := make([]*container.Stats, 0, len(containerIds))
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
|
// slog.Info("PVE resource", "resource", resource)
|
||||||
if _, exists := containerIds[resource.ID]; !exists {
|
if _, exists := containerIds[resource.ID]; !exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -71,6 +78,10 @@ func (pm *pveManager) getPVEStats() ([]*container.Stats, error) {
|
|||||||
resourceStats.Id = resource.ID
|
resourceStats.Id = resource.ID
|
||||||
// Store type (e.g. "qemu" or "lxc") in .Image (cbor key 8, json:"-")
|
// Store type (e.g. "qemu" or "lxc") in .Image (cbor key 8, json:"-")
|
||||||
resourceStats.Image = resource.Type
|
resourceStats.Image = resource.Type
|
||||||
|
// PVE limits (cbor-only, for pve_vms table)
|
||||||
|
resourceStats.MaxCPU = resource.MaxCPU
|
||||||
|
resourceStats.MaxMem = resource.MaxMem
|
||||||
|
resourceStats.Uptime = resource.Uptime
|
||||||
// prevent first run from sending all prev sent/recv bytes
|
// prevent first run from sending all prev sent/recv bytes
|
||||||
total_sent := uint64(resource.NetOut)
|
total_sent := uint64(resource.NetOut)
|
||||||
total_recv := uint64(resource.NetIn)
|
total_recv := uint64(resource.NetIn)
|
||||||
@@ -87,14 +98,18 @@ func (pm *pveManager) getPVEStats() ([]*container.Stats, error) {
|
|||||||
resourceStats.Mem = float64(resource.Mem)
|
resourceStats.Mem = float64(resource.Mem)
|
||||||
resourceStats.Bandwidth = [2]uint64{uint64(sent_delta), uint64(recv_delta)}
|
resourceStats.Bandwidth = [2]uint64{uint64(sent_delta), uint64(recv_delta)}
|
||||||
|
|
||||||
|
slog.Info("PVE resource stats", "resourceStats", resourceStats)
|
||||||
|
|
||||||
stats = append(stats, resourceStats)
|
stats = append(stats, resourceStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Info("PVE stats", "stats", stats)
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new PVE manager
|
// Creates a new PVE manager
|
||||||
func newPVEManager(_ *Agent) *pveManager {
|
func newPVEManager(_ *Agent) *pveManager {
|
||||||
|
slog.Info("Creating new PVE manager")
|
||||||
url, exists := GetEnv("PROXMOX_URL")
|
url, exists := GetEnv("PROXMOX_URL")
|
||||||
if !exists {
|
if !exists {
|
||||||
url = "https://localhost:8006/api2/json"
|
url = "https://localhost:8006/api2/json"
|
||||||
@@ -103,6 +118,11 @@ func newPVEManager(_ *Agent) *pveManager {
|
|||||||
tokenID, tokenIDExists := GetEnv("PROXMOX_TOKENID")
|
tokenID, tokenIDExists := GetEnv("PROXMOX_TOKENID")
|
||||||
secret, secretExists := GetEnv("PROXMOX_SECRET")
|
secret, secretExists := GetEnv("PROXMOX_SECRET")
|
||||||
|
|
||||||
|
slog.Info("PROXMOX_URL", "url", url)
|
||||||
|
slog.Info("PROXMOX_NODE", "nodeName", nodeName)
|
||||||
|
slog.Info("PROXMOX_TOKENID", "tokenID", tokenID)
|
||||||
|
slog.Info("PROXMOX_SECRET", "secret", secret)
|
||||||
|
|
||||||
// PROXMOX_INSECURE_TLS defaults to true; set to "false" to enable TLS verification
|
// PROXMOX_INSECURE_TLS defaults to true; set to "false" to enable TLS verification
|
||||||
insecureTLS := true
|
insecureTLS := true
|
||||||
if val, exists := GetEnv("PROXMOX_INSECURE_TLS"); exists {
|
if val, exists := GetEnv("PROXMOX_INSECURE_TLS"); exists {
|
||||||
@@ -123,6 +143,7 @@ func newPVEManager(_ *Agent) *pveManager {
|
|||||||
proxmox.WithAPIToken(tokenID, secret),
|
proxmox.WithAPIToken(tokenID, secret),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
slog.Error("Env variables not set")
|
||||||
client = nil
|
client = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,13 +154,15 @@ func newPVEManager(_ *Agent) *pveManager {
|
|||||||
}
|
}
|
||||||
// Retrieve node cpu count
|
// Retrieve node cpu count
|
||||||
if client != nil {
|
if client != nil {
|
||||||
|
slog.Info("Getting node CPU count", "nodeName", nodeName)
|
||||||
node, err := client.Node(context.Background(), nodeName)
|
node, err := client.Node(context.Background(), nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pveManager.client = nil
|
pveManager.client = nil
|
||||||
|
slog.Error("Error getting node", "err", err)
|
||||||
} else {
|
} else {
|
||||||
pveManager.cpuCount = node.CPUInfo.CPUs
|
pveManager.cpuCount = node.CPUInfo.CPUs
|
||||||
|
slog.Info("Node CPU count", "cpuCount", pveManager.cpuCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pveManager
|
return pveManager
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,9 @@ type Stats struct {
|
|||||||
Status string `json:"-" cbor:"6,keyasint"`
|
Status string `json:"-" cbor:"6,keyasint"`
|
||||||
Id string `json:"-" cbor:"7,keyasint"`
|
Id string `json:"-" cbor:"7,keyasint"`
|
||||||
Image string `json:"-" cbor:"8,keyasint"`
|
Image string `json:"-" cbor:"8,keyasint"`
|
||||||
|
MaxCPU uint64 `json:"-" cbor:"10,keyasint,omitzero"` // PVE: max vCPU count
|
||||||
|
MaxMem uint64 `json:"-" cbor:"11,keyasint,omitzero"` // PVE: max memory bytes
|
||||||
|
Uptime uint64 `json:"-" cbor:"12,keyasint,omitzero"` // PVE: uptime in seconds
|
||||||
// PrevCpu [2]uint64 `json:"-"`
|
// PrevCpu [2]uint64 `json:"-"`
|
||||||
CpuSystem uint64 `json:"-"`
|
CpuSystem uint64 `json:"-"`
|
||||||
CpuContainer uint64 `json:"-"`
|
CpuContainer uint64 `json:"-"`
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
|
"log/slog"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -213,6 +214,7 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
|||||||
if len(data.PVEStats) > 0 {
|
if len(data.PVEStats) > 0 {
|
||||||
if data.PVEStats[0].Id != "" {
|
if data.PVEStats[0].Id != "" {
|
||||||
if err := createPVEVMRecords(txApp, data.PVEStats, sys.Id); err != nil {
|
if err := createPVEVMRecords(txApp, data.PVEStats, sys.Id); err != nil {
|
||||||
|
slog.Error("Error creating PVE VM records", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,6 +227,7 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
|
|||||||
pveStatsRecord.Set("stats", data.PVEStats)
|
pveStatsRecord.Set("stats", data.PVEStats)
|
||||||
pveStatsRecord.Set("type", "1m")
|
pveStatsRecord.Set("type", "1m")
|
||||||
if err := txApp.SaveNoValidate(pveStatsRecord); err != nil {
|
if err := txApp.SaveNoValidate(pveStatsRecord); err != nil {
|
||||||
|
slog.Error("Error creating PVE stats records", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,20 +367,20 @@ func createPVEVMRecords(app core.App, data []*container.Stats, systemId string)
|
|||||||
valueStrings := make([]string, 0, len(data))
|
valueStrings := make([]string, 0, len(data))
|
||||||
for i, vm := range data {
|
for i, vm := range data {
|
||||||
suffix := fmt.Sprintf("%d", i)
|
suffix := fmt.Sprintf("%d", i)
|
||||||
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:type%[1]s}, {:cpu%[1]s}, {:memory%[1]s}, {:net%[1]s}, {:updated})", suffix))
|
valueStrings = append(valueStrings, fmt.Sprintf("({:id%[1]s}, {:system}, {:name%[1]s}, {:type%[1]s}, {:cpu%[1]s}, {:mem%[1]s}, {:net%[1]s}, {:maxcpu%[1]s}, {:maxmem%[1]s}, {:uptime%[1]s}, {:updated})", suffix))
|
||||||
params["id"+suffix] = makeStableHashId(systemId, vm.Id)
|
params["id"+suffix] = makeStableHashId(systemId, vm.Id)
|
||||||
params["name"+suffix] = vm.Name
|
params["name"+suffix] = vm.Name
|
||||||
params["type"+suffix] = vm.Image // "qemu" or "lxc"
|
params["type"+suffix] = vm.Image // "qemu" or "lxc"
|
||||||
params["cpu"+suffix] = vm.Cpu
|
params["cpu"+suffix] = vm.Cpu
|
||||||
params["memory"+suffix] = vm.Mem
|
params["mem"+suffix] = vm.Mem
|
||||||
|
params["maxcpu"+suffix] = vm.MaxCPU
|
||||||
|
params["maxmem"+suffix] = vm.MaxMem
|
||||||
|
params["uptime"+suffix] = vm.Uptime
|
||||||
netBytes := vm.Bandwidth[0] + vm.Bandwidth[1]
|
netBytes := vm.Bandwidth[0] + vm.Bandwidth[1]
|
||||||
if netBytes == 0 {
|
|
||||||
netBytes = uint64((vm.NetworkSent + vm.NetworkRecv) * 1024 * 1024)
|
|
||||||
}
|
|
||||||
params["net"+suffix] = netBytes
|
params["net"+suffix] = netBytes
|
||||||
}
|
}
|
||||||
queryString := fmt.Sprintf(
|
queryString := fmt.Sprintf(
|
||||||
"INSERT INTO pve_vms (id, system, name, type, cpu, memory, net, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system=excluded.system, name=excluded.name, type=excluded.type, cpu=excluded.cpu, memory=excluded.memory, net=excluded.net, updated=excluded.updated",
|
"INSERT INTO pve_vms (id, system, name, type, cpu, mem, net, maxcpu, maxmem, uptime, updated) VALUES %s ON CONFLICT(id) DO UPDATE SET system=excluded.system, name=excluded.name, type=excluded.type, cpu=excluded.cpu, mem=excluded.mem, net=excluded.net, maxcpu=excluded.maxcpu, maxmem=excluded.maxmem, uptime=excluded.uptime, updated=excluded.updated",
|
||||||
strings.Join(valueStrings, ","),
|
strings.Join(valueStrings, ","),
|
||||||
)
|
)
|
||||||
_, err := app.DB().NewQuery(queryString).Bind(params).Execute()
|
_, err := app.DB().NewQuery(queryString).Bind(params).Execute()
|
||||||
|
|||||||
@@ -1691,11 +1691,11 @@ func init() {
|
|||||||
"deleteRule": null,
|
"deleteRule": null,
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"autogeneratePattern": "[a-z0-9]{15}",
|
"autogeneratePattern": "[a-z0-9]{10}",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"id": "text3208210256",
|
"id": "text3208210256",
|
||||||
"max": 15,
|
"max": 10,
|
||||||
"min": 15,
|
"min": 10,
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"pattern": "^[a-z0-9]+$",
|
"pattern": "^[a-z0-9]+$",
|
||||||
"presentable": false,
|
"presentable": false,
|
||||||
@@ -1765,11 +1765,11 @@ func init() {
|
|||||||
"type": "autodate"
|
"type": "autodate"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "pve_stats_col001",
|
"id": "pvestats",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
"CREATE INDEX ` + "`" + `idx_pve_stats_sys_type_created` + "`" + ` ON ` + "`" + `pve_stats` + "`" + ` (\n ` + "`" + `system` + "`" + `,\n ` + "`" + `type` + "`" + `,\n ` + "`" + `created` + "`" + `\n)"
|
"CREATE INDEX ` + "`" + `idx_pve_stats_sys_type_created` + "`" + ` ON ` + "`" + `pve_stats` + "`" + ` (\n ` + "`" + `system` + "`" + `,\n ` + "`" + `type` + "`" + `,\n ` + "`" + `created` + "`" + `\n)"
|
||||||
],
|
],
|
||||||
"listRule": "@request.auth.id != \"\"",
|
"listRule": "@request.auth.id != \"\" && system.users.id ?= @request.auth.id",
|
||||||
"name": "pve_stats",
|
"name": "pve_stats",
|
||||||
"system": false,
|
"system": false,
|
||||||
"type": "base",
|
"type": "base",
|
||||||
@@ -1852,7 +1852,7 @@ func init() {
|
|||||||
"id": "pve_vms_mem001",
|
"id": "pve_vms_mem001",
|
||||||
"max": null,
|
"max": null,
|
||||||
"min": 0,
|
"min": 0,
|
||||||
"name": "memory",
|
"name": "mem",
|
||||||
"onlyInt": false,
|
"onlyInt": false,
|
||||||
"presentable": false,
|
"presentable": false,
|
||||||
"required": false,
|
"required": false,
|
||||||
@@ -1871,6 +1871,42 @@ func init() {
|
|||||||
"system": false,
|
"system": false,
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number1253106325",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "maxcpu",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number1693954525",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "maxmem",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": false,
|
||||||
|
"id": "number1563400775",
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"name": "uptime",
|
||||||
|
"onlyInt": false,
|
||||||
|
"presentable": false,
|
||||||
|
"required": false,
|
||||||
|
"system": false,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"id": "pve_vms_upd001",
|
"id": "pve_vms_upd001",
|
||||||
@@ -1884,7 +1920,7 @@ func init() {
|
|||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"id": "pve_vms_col0001",
|
"id": "pvevms",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
"CREATE INDEX ` + "`" + `idx_pve_vms_updated` + "`" + ` ON ` + "`" + `pve_vms` + "`" + ` (` + "`" + `updated` + "`" + `)",
|
"CREATE INDEX ` + "`" + `idx_pve_vms_updated` + "`" + ` ON ` + "`" + `pve_vms` + "`" + ` (` + "`" + `updated` + "`" + `)",
|
||||||
"CREATE INDEX ` + "`" + `idx_pve_vms_system` + "`" + ` ON ` + "`" + `pve_vms` + "`" + ` (` + "`" + `system` + "`" + `)"
|
"CREATE INDEX ` + "`" + `idx_pve_vms_system` + "`" + ` ON ` + "`" + `pve_vms` + "`" + ` (` + "`" + `system` + "`" + `)"
|
||||||
@@ -1896,6 +1932,7 @@ func init() {
|
|||||||
"updateRule": null,
|
"updateRule": null,
|
||||||
"viewRule": null
|
"viewRule": null
|
||||||
}
|
}
|
||||||
|
|
||||||
]`
|
]`
|
||||||
|
|
||||||
err := app.ImportCollectionsByMarshaledJSON([]byte(jsonData), false)
|
err := app.ImportCollectionsByMarshaledJSON([]byte(jsonData), false)
|
||||||
Reference in New Issue
Block a user