implement node endpoint

This commit is contained in:
Arthur Lu 2024-12-27 19:59:44 +00:00
parent 6b41ca984c
commit c91f54f506
5 changed files with 197 additions and 34 deletions

View File

@ -1,3 +1,5 @@
.PHONY: build test clean
build: clean
CGO_ENABLED=0 go build -ldflags="-s -w" -o dist/ .

View File

@ -1,8 +1,9 @@
package app
import (
"context"
"encoding/gob"
"flag"
"fmt"
"log"
"net/http"
"strconv"
@ -13,21 +14,24 @@ import (
const APIVersion string = "0.0.1"
var client *proxmox.Client = nil
var client ProxmoxClient
func Run() {
gob.Register(proxmox.Client{})
configPath := flag.String("config", "config.json", "path to config.json file")
flag.Parse()
config := GetConfig(*configPath)
log.Println("Initialized config from " + *configPath)
client = NewClient(config.PVE.Token.ID, config.PVE.Token.Secret)
token := fmt.Sprintf(`%s@%s!%s`, config.PVE.Token.USER, config.PVE.Token.REALM, config.PVE.Token.ID)
client = NewClient(token, config.PVE.Token.Secret)
router := gin.Default()
router.GET("/version", func(c *gin.Context) {
PVEVersion, err := client.Version(context.Background())
PVEVersion, err := client.Version()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
} else {
@ -35,6 +39,14 @@ func Run() {
}
})
router.Run("0.0.0.0:" + strconv.Itoa(config.ListenPort))
router.GET("/nodes/:node", func(c *gin.Context) {
Node, err := client.Node(c.Param("node"))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusOK, gin.H{"node": Node})
}
})
router.Run("0.0.0.0:" + strconv.Itoa(config.ListenPort))
}

View File

@ -1,18 +1,127 @@
package app
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"strings"
"github.com/luthermonson/go-proxmox"
)
func NewClient(tokenID string, secret string) *proxmox.Client {
HTTPClient := http.Client{}
type ProxmoxClient struct {
client *proxmox.Client
}
func NewClient(token string, secret string) ProxmoxClient {
HTTPClient := http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
println(token, secret)
client := proxmox.NewClient("https://pve.tronnet.net/api2/json",
proxmox.WithHTTPClient(&HTTPClient),
proxmox.WithAPIToken(tokenID, secret),
proxmox.WithAPIToken(token, secret),
)
return client
return ProxmoxClient{client: client}
}
// Gets and returns the PVE API version
func (pve ProxmoxClient) Version() (proxmox.Version, error) {
version, err := pve.client.Version(context.Background())
if err != nil {
return *version, err
}
return *version, err
}
// Gets and returns a Node's CPU, memory, swap, and Hardware (PCI) resources
func (pve ProxmoxClient) Node(nodeName string) (Host, error) {
host := Host{}
host.Hardware = make(map[string]PVEDevice)
node, err := pve.client.Node(context.Background(), nodeName)
if err != nil {
return host, err
}
devices := []PVEDevice{}
err = pve.client.Get(context.Background(), fmt.Sprintf("/nodes/%s/hardware/pci", nodeName), &devices)
if err != nil {
return host, err
}
vms, err := node.VirtualMachines(context.Background())
if err != nil {
return host, err
}
cts, err := node.Containers(context.Background())
if err != nil {
return host, err
}
// temporary helper which maps supersystem devices to each contained subsystem
// eg 0000:00:05 -> [0000:00:05.0, 0000:00:05.1, 0000:00:05.2, 0000:00:05.3, ...]
DeviceSubsystemMap := make(map[string][]string)
for _, device := range devices {
host.Hardware[device.BusID] = device
SupersystemID := strings.Split(device.BusID, ".")[0]
DeviceSubsystemMap[SupersystemID] = append(DeviceSubsystemMap[SupersystemID], device.BusID)
}
host.Name = node.Name
host.Cores.Total = int64(node.CPUInfo.CPUs)
host.Memory.Total = int64(node.Memory.Total)
host.Swap.Total = int64(node.Swap.Total)
for _, vm := range vms {
vm, err := node.VirtualMachine(context.Background(), int(vm.VMID))
if err != nil {
return host, err
}
host.Cores.Reserved += int64(vm.VirtualMachineConfig.Cores)
host.Memory.Reserved += int64(vm.VirtualMachineConfig.Memory * MiB)
MarshallVirtualMachineConfig(vm.VirtualMachineConfig)
for _, v := range vm.VirtualMachineConfig.HostPCIs {
HostPCIBusID := strings.Split(v, ",")[0]
if device, ok := host.Hardware[HostPCIBusID]; ok { // is a specific subsystem of device
device.Reserved = true
host.Hardware[HostPCIBusID] = device
} else if SubsystemBusIDs, ok := DeviceSubsystemMap[HostPCIBusID]; ok { // is a supersystem device containing multiple subsystems
for _, SubsystemBusID := range SubsystemBusIDs {
device := host.Hardware[SubsystemBusID]
device.Reserved = true
host.Hardware[SubsystemBusID] = device
}
}
}
}
for _, ct := range cts {
ct, err := node.Container(context.Background(), int(ct.VMID))
if err != nil {
return host, err
}
host.Cores.Reserved += int64(ct.ContainerConfig.Cores)
host.Memory.Reserved += int64(ct.ContainerConfig.Memory * MiB)
host.Swap.Reserved += int64(ct.ContainerConfig.Swap * MiB)
}
host.Cores.Free = host.Cores.Total - host.Cores.Reserved
host.Memory.Free = host.Memory.Total - host.Memory.Reserved
host.Swap.Free = host.Swap.Total - host.Swap.Reserved
return host, err
}

View File

@ -1,37 +1,57 @@
package app
type PVEBus int
type Resource struct { // number of virtual cores (usually threads)
Reserved int64
Free int64
Total int64
}
const (
IDE PVEBus = iota
SATA
)
type Host struct {
Name string
Cores Resource
Memory Resource
Swap Resource
Storage map[string]Storage
Hardware map[string]Device
}
type PVEDrive struct{}
type PVEDisk struct{}
type PVENet struct{}
type PVEDevice struct{}
type Storage struct{}
type QEMUInstance struct {
Name string
Proctype string
Cores int16
Memory int32
Drive map[int]PVEDrive
Disk map[int]PVEDisk
Net map[int]PVENet
Device map[int]PVEDevice
Cores Resource
Memory Resource
Drive map[int]Volume
Disk map[int]Volume
Net map[int]Net
Device map[int]Device
}
type LXCInstance struct {
Name string
Cores int16
Memory int32
Swap int32
RootDisk PVEDrive
MP map[int]PVEDisk
Net map[int]PVENet
Cores Resource
Memory Resource
Swap Resource
RootDisk Volume
MP map[int]Volume
Net map[int]Net
}
type Volume struct {
Format string
Path string
Size string
Used string
}
type Net struct{}
type Device struct {
BusID string `json:"id"`
DeviceName string `json:"device_name"`
VendorName string `json:"vendor_name"`
SubsystemDeviceName string `json:"subsystem_device_name"`
SubsystemVendorName string `json:"subsystem_vendor_name"`
Reserved bool
}

View File

@ -4,14 +4,20 @@ import (
"encoding/json"
"log"
"os"
"github.com/luthermonson/go-proxmox"
)
const MiB = 1024 * 1024
type Config struct {
ListenPort int `json:"listenPort"`
PVE struct {
Token struct {
USER string `json:"user"`
REALM string `json:"realm"`
ID string `json:"id"`
Secret string `json:"secret"`
Secret string `json:"uuid"`
}
}
}
@ -28,3 +34,17 @@ func GetConfig(configPath string) Config {
}
return config
}
func MarshallVirtualMachineConfig(v *proxmox.VirtualMachineConfig) {
v.HostPCIs = make(map[string]string)
v.HostPCIs["hostpci0"] = v.HostPCI0
v.HostPCIs["hostpci1"] = v.HostPCI1
v.HostPCIs["hostpci2"] = v.HostPCI2
v.HostPCIs["hostpci3"] = v.HostPCI3
v.HostPCIs["hostpci4"] = v.HostPCI4
v.HostPCIs["hostpci5"] = v.HostPCI5
v.HostPCIs["hostpci6"] = v.HostPCI6
v.HostPCIs["hostpci7"] = v.HostPCI7
v.HostPCIs["hostpci8"] = v.HostPCI8
v.HostPCIs["hostpci9"] = v.HostPCI9
}