Compare commits
23 Commits
3ed2b845e0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f83a26ff6b | |||
| 612f2a159f | |||
| 9be70e5900 | |||
| 1225439b4a | |||
| 692b348994 | |||
| eac682236b | |||
| b092b506be | |||
| d887444e41 | |||
| e0fc7253ac | |||
| 3ed570f60a | |||
| 81815bab82 | |||
| b2360500f2 | |||
| cd12365336 | |||
| 8e73db22b7 | |||
| a1b4353b06 | |||
| a07a0a5e98 | |||
| 4ef3f76589 | |||
| 6a65ca2021 | |||
| 6a1d13538d | |||
| 2265a8e580 | |||
| cc35e38455 | |||
| d957198eaf | |||
| 5dbb87d772 |
6
Makefile
6
Makefile
@@ -1,11 +1,13 @@
|
|||||||
.PHONY: build test clean
|
.PHONY: build test clean
|
||||||
|
|
||||||
build: clean
|
build: clean
|
||||||
CGO_ENABLED=0 go build -ldflags="-s -w" -o dist/ .
|
@echo "======================== Building Binary ======================="
|
||||||
|
CGO_ENABLED=0 go build -ldflags="-s -w" -v -o dist/ .
|
||||||
|
|
||||||
test: clean
|
test: clean
|
||||||
go run .
|
go run .
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@echo "======================== Cleaning Project ======================"
|
||||||
go clean
|
go clean
|
||||||
rm -f dist/*
|
rm -rf dist/*
|
||||||
84
app/app.go
84
app/app.go
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/luthermonson/go-proxmox"
|
"github.com/luthermonson/go-proxmox"
|
||||||
)
|
)
|
||||||
|
|
||||||
const APIVersion string = "0.0.2"
|
const APIVersion string = "1.0.0"
|
||||||
|
|
||||||
var client ProxmoxClient
|
var client ProxmoxClient
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ func Run() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
config := GetConfig(*configPath)
|
config := GetConfig(*configPath)
|
||||||
log.Println("Initialized config from " + *configPath)
|
log.Printf("Initialized config from %s", *configPath)
|
||||||
|
|
||||||
token := fmt.Sprintf(`%s@%s!%s`, config.PVE.Token.USER, config.PVE.Token.REALM, config.PVE.Token.ID)
|
token := fmt.Sprintf(`%s@%s!%s`, config.PVE.Token.USER, config.PVE.Token.REALM, config.PVE.Token.ID)
|
||||||
client = NewClient(config.PVE.URL, token, config.PVE.Token.Secret)
|
client = NewClient(config.PVE.URL, token, config.PVE.Token.Secret)
|
||||||
@@ -120,28 +120,28 @@ func Run() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.POST("/sync", func(c *gin.Context) {
|
router.POST("/sync", func(c *gin.Context) {
|
||||||
go func() {
|
//go func() {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Printf("Starting cluster sync\n")
|
log.Printf("Starting cluster sync\n")
|
||||||
cluster.Sync()
|
cluster.Sync()
|
||||||
log.Printf("Synced cluster in %fs\n", time.Since(start).Seconds())
|
log.Printf("Synced cluster in %fs\n", time.Since(start).Seconds())
|
||||||
}()
|
//}()
|
||||||
})
|
})
|
||||||
|
|
||||||
router.POST("/nodes/:node/sync", func(c *gin.Context) {
|
router.POST("/nodes/:node/sync", func(c *gin.Context) {
|
||||||
nodeid := c.Param("node")
|
nodeid := c.Param("node")
|
||||||
go func() {
|
//go func() {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Printf("Starting %s sync\n", nodeid)
|
log.Printf("Starting %s sync\n", nodeid)
|
||||||
err := cluster.RebuildHost(nodeid)
|
err := cluster.RebuildHost(nodeid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to sync %s: %s", nodeid, err.Error())
|
log.Printf("Failed to sync %s: %s", nodeid, err.Error())
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Synced %s in %fs\n", nodeid, time.Since(start).Seconds())
|
log.Printf("Synced %s in %fs\n", nodeid, time.Since(start).Seconds())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
//}()
|
||||||
})
|
})
|
||||||
|
|
||||||
router.POST("/nodes/:node/instances/:vmid/sync", func(c *gin.Context) {
|
router.POST("/nodes/:node/instances/:vmid/sync", func(c *gin.Context) {
|
||||||
@@ -152,31 +152,31 @@ func Run() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
//go func() {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Printf("Starting %s.%d sync\n", nodeid, vmid)
|
log.Printf("Starting %s.%d sync\n", nodeid, vmid)
|
||||||
|
|
||||||
node, err := cluster.GetNode(nodeid)
|
node, err := cluster.GetNode(nodeid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to sync %s.%d: %s", nodeid, vmid, err.Error())
|
log.Printf("Failed to sync %s.%d: %s", nodeid, vmid, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
instance, err := node.GetInstance(uint(vmid))
|
instance, err := node.GetInstance(uint(vmid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to sync %s.%d: %s", nodeid, vmid, err.Error())
|
log.Printf("Failed to sync %s.%d: %s", nodeid, vmid, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = node.RebuildInstance(instance.Type, uint(vmid))
|
err = node.RebuildInstance(instance.Type, uint(vmid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to sync %s.%d: %s", nodeid, vmid, err.Error())
|
log.Printf("Failed to sync %s.%d: %s", nodeid, vmid, err.Error())
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Synced %s.%d in %fs\n", nodeid, vmid, time.Since(start).Seconds())
|
log.Printf("Synced %s.%d in %fs\n", nodeid, vmid, time.Since(start).Seconds())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
//}()
|
||||||
})
|
})
|
||||||
|
|
||||||
router.Run("0.0.0.0:" + strconv.Itoa(config.ListenPort))
|
router.Run("0.0.0.0:" + strconv.Itoa(config.ListenPort))
|
||||||
|
|||||||
91
app/model.go
91
app/model.go
@@ -2,7 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,8 +26,9 @@ func (cluster *Cluster) Sync() error {
|
|||||||
for _, hostName := range nodes {
|
for _, hostName := range nodes {
|
||||||
// rebuild node
|
// rebuild node
|
||||||
err := cluster.RebuildHost(hostName)
|
err := cluster.RebuildHost(hostName)
|
||||||
if err != nil {
|
if err != nil { // if an error was encountered, continue and log the error
|
||||||
return err
|
log.Print(err.Error())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +67,8 @@ func (cluster *Cluster) GetNode(hostName string) (*Node, error) {
|
|||||||
|
|
||||||
func (cluster *Cluster) RebuildHost(hostName string) error {
|
func (cluster *Cluster) RebuildHost(hostName string) error {
|
||||||
host, err := cluster.pve.Node(hostName)
|
host, err := cluster.pve.Node(hostName)
|
||||||
if err != nil {
|
if err != nil { // host is probably down or otherwise unreachable
|
||||||
return err
|
return fmt.Errorf("error retrieving %s: %s, possibly down?", hostName, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// aquire lock on host, release on return
|
// aquire lock on host, release on return
|
||||||
@@ -84,8 +85,9 @@ func (cluster *Cluster) RebuildHost(hostName string) error {
|
|||||||
}
|
}
|
||||||
for _, vmid := range vms {
|
for _, vmid := range vms {
|
||||||
err := host.RebuildInstance(VM, vmid)
|
err := host.RebuildInstance(VM, vmid)
|
||||||
if err != nil {
|
if err != nil { // if an error was encountered, continue and log the error
|
||||||
return err
|
log.Print(err.Error())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,13 +149,13 @@ func (host *Node) RebuildInstance(instancetype InstanceType, vmid uint) error {
|
|||||||
var err error
|
var err error
|
||||||
instance, err = host.VirtualMachine(vmid)
|
instance, err = host.VirtualMachine(vmid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error retrieving %d: %s, possibly down?", vmid, err.Error())
|
||||||
}
|
}
|
||||||
} else if instancetype == CT {
|
} else if instancetype == CT {
|
||||||
var err error
|
var err error
|
||||||
instance, err = host.Container(vmid)
|
instance, err = host.Container(vmid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("error retrieving %d: %s, possibly down?", vmid, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -176,6 +178,10 @@ func (host *Node) RebuildInstance(instancetype InstanceType, vmid uint) error {
|
|||||||
instance.RebuildDevice(host, deviceid)
|
instance.RebuildDevice(host, deviceid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if instance.Type == VM {
|
||||||
|
instance.RebuildBoot()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +193,9 @@ func (instance *Instance) RebuildVolume(host *Node, volid string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
voltype := AnyPrefixes(volid, VolumeTypes)
|
||||||
|
volume.Type = voltype
|
||||||
|
volume.Volume_ID = VolumeID(volid)
|
||||||
instance.Volumes[VolumeID(volid)] = volume
|
instance.Volumes[VolumeID(volid)] = volume
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -194,17 +203,14 @@ func (instance *Instance) RebuildVolume(host *Node, volid string) error {
|
|||||||
|
|
||||||
func (instance *Instance) RebuildNet(netid string) error {
|
func (instance *Instance) RebuildNet(netid string) error {
|
||||||
net := instance.configNets[netid]
|
net := instance.configNets[netid]
|
||||||
idnum, err := strconv.ParseUint(strings.TrimPrefix(netid, "net"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
netinfo, err := GetNetInfo(net)
|
netinfo, err := GetNetInfo(net)
|
||||||
|
netinfo.Net_ID = NetID(netid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.Nets[NetID(idnum)] = netinfo
|
instance.Nets[NetID(netid)] = netinfo
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -216,21 +222,62 @@ func (instance *Instance) RebuildDevice(host *Node, deviceid string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hostDeviceBusID := DeviceID(strings.Split(instanceDevice, ",")[0])
|
hostDeviceBusID := DeviceID(strings.Split(instanceDevice, ",")[0])
|
||||||
|
instanceDeviceBusID := DeviceID(deviceid)
|
||||||
idbid, err := strconv.ParseUint(strings.TrimPrefix(deviceid, "hostpci"), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
instanceDeviceBusID := InstanceDeviceID(idbid)
|
|
||||||
|
|
||||||
if DeviceBusIDIsSuperDevice(hostDeviceBusID) {
|
if DeviceBusIDIsSuperDevice(hostDeviceBusID) {
|
||||||
instance.Devices[InstanceDeviceID(instanceDeviceBusID)] = host.Devices[DeviceID(hostDeviceBusID)]
|
instance.Devices[DeviceID(instanceDeviceBusID)] = host.Devices[DeviceBus(hostDeviceBusID)]
|
||||||
for _, function := range instance.Devices[InstanceDeviceID(instanceDeviceBusID)].Functions {
|
for _, function := range instance.Devices[DeviceID(instanceDeviceBusID)].Functions {
|
||||||
function.Reserved = true
|
function.Reserved = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// sub function assignment not supported yet
|
// sub function assignment not supported yet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance.Devices[DeviceID(instanceDeviceBusID)].Device_ID = DeviceID(deviceid)
|
||||||
|
instance.Devices[DeviceID(instanceDeviceBusID)].Value = instanceDevice
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (instance *Instance) RebuildBoot() {
|
||||||
|
instance.Boot = BootOrder{}
|
||||||
|
|
||||||
|
eligibleBoot := map[string]bool{}
|
||||||
|
for k := range instance.Volumes {
|
||||||
|
eligiblePrefix := AnyPrefixes(string(k), []string{"sata", "scsi", "ide"})
|
||||||
|
if eligiblePrefix != "" {
|
||||||
|
eligibleBoot[string(k)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k := range instance.Nets {
|
||||||
|
eligibleBoot[string(k)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
bootOrder := PVEObjectStringToMap(instance.configBoot)["order"]
|
||||||
|
|
||||||
|
if len(bootOrder) != 0 {
|
||||||
|
for bootTarget := range strings.SplitSeq(bootOrder, ";") { // iterate over elements selected for boot, add them to Enabled, and remove them from eligible boot target
|
||||||
|
_, isEligible := eligibleBoot[bootTarget]
|
||||||
|
if val, ok := instance.Volumes[VolumeID(bootTarget)]; ok && isEligible { // if the item is eligible and is in volumes
|
||||||
|
instance.Boot.Enabled = append(instance.Boot.Enabled, val)
|
||||||
|
delete(eligibleBoot, bootTarget)
|
||||||
|
} else if val, ok := instance.Nets[NetID(bootTarget)]; ok && isEligible { // if the item is eligible and is in nets
|
||||||
|
instance.Boot.Enabled = append(instance.Boot.Enabled, val)
|
||||||
|
delete(eligibleBoot, bootTarget)
|
||||||
|
} else { // item is not eligible for boot but is included in the boot order
|
||||||
|
log.Printf("Encountered enabled but non-eligible boot target %s in instance %s\n", bootTarget, instance.Name)
|
||||||
|
delete(eligibleBoot, bootTarget)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for bootTarget, isEligible := range eligibleBoot { // iterate over remaining items, add them to Disabled
|
||||||
|
if val, ok := instance.Volumes[VolumeID(bootTarget)]; ok && isEligible { // if the item is eligible and is in volumes
|
||||||
|
instance.Boot.Disabled = append(instance.Boot.Disabled, val)
|
||||||
|
} else if val, ok := instance.Nets[NetID(bootTarget)]; ok && isEligible { // if the item is eligible and is in nets
|
||||||
|
instance.Boot.Disabled = append(instance.Boot.Disabled, val)
|
||||||
|
} else { // item is not eligible and is not already in the boot order, skip adding to model
|
||||||
|
log.Printf("Encountered disabled and non-eligible boot target %s in instance %s\n", bootTarget, instance.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
115
app/proxmox.go
115
app/proxmox.go
@@ -15,6 +15,20 @@ type ProxmoxClient struct {
|
|||||||
client *proxmox.Client
|
client *proxmox.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PVEDevice struct { // used only for requests to PVE
|
||||||
|
ID string `json:"id"`
|
||||||
|
Device_Name string `json:"device_name"`
|
||||||
|
Vendor_Name string `json:"vendor_name"`
|
||||||
|
Subsystem_Device_Name string `json:"subsystem_device_name"`
|
||||||
|
Subsystem_Vendor_Name string `json:"subsystem_vendor_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PVEProctype struct {
|
||||||
|
Custom int
|
||||||
|
Name string
|
||||||
|
Vendor string
|
||||||
|
}
|
||||||
|
|
||||||
func NewClient(url string, token string, secret string) ProxmoxClient {
|
func NewClient(url string, token string, secret string) ProxmoxClient {
|
||||||
HTTPClient := http.Client{
|
HTTPClient := http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
@@ -59,7 +73,7 @@ func (pve ProxmoxClient) Nodes() ([]string, error) {
|
|||||||
// Gets a Node's resources but does not recursively expand instances
|
// Gets a Node's resources but does not recursively expand instances
|
||||||
func (pve ProxmoxClient) Node(nodeName string) (*Node, error) {
|
func (pve ProxmoxClient) Node(nodeName string) (*Node, error) {
|
||||||
host := Node{}
|
host := Node{}
|
||||||
host.Devices = make(map[DeviceID]*Device)
|
host.Devices = make(map[DeviceBus]*Device)
|
||||||
host.Instances = make(map[InstanceID]*Instance)
|
host.Instances = make(map[InstanceID]*Instance)
|
||||||
|
|
||||||
node, err := pve.client.Node(context.Background(), nodeName)
|
node, err := pve.client.Node(context.Background(), nodeName)
|
||||||
@@ -78,24 +92,33 @@ func (pve ProxmoxClient) Node(nodeName string) (*Node, error) {
|
|||||||
if len(x) != 2 { // this should always be true, but skip if not
|
if len(x) != 2 { // this should always be true, but skip if not
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
deviceid := DeviceID(x[0])
|
deviceid := DeviceBus(x[0])
|
||||||
functionid := FunctionID(x[1])
|
functionid := FunctionID(x[1])
|
||||||
if _, ok := host.Devices[deviceid]; !ok {
|
if _, ok := host.Devices[deviceid]; !ok {
|
||||||
host.Devices[deviceid] = &Device{
|
host.Devices[deviceid] = &Device{
|
||||||
DeviceID: deviceid,
|
Device_Bus: deviceid,
|
||||||
DeviceName: device.DeviceName,
|
Device_Name: device.Device_Name,
|
||||||
VendorName: device.VendorName,
|
Vendor_Name: device.Vendor_Name,
|
||||||
Functions: make(map[FunctionID]*Function),
|
Functions: make(map[FunctionID]*Function),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
host.Devices[deviceid].Functions[functionid] = &Function{
|
host.Devices[deviceid].Functions[functionid] = &Function{
|
||||||
FunctionID: functionid,
|
Function_ID: functionid,
|
||||||
FunctionName: device.SubsystemDeviceName,
|
Function_Name: device.Subsystem_Device_Name,
|
||||||
VendorName: device.SubsystemVendorName,
|
Vendor_Name: device.Subsystem_Vendor_Name,
|
||||||
Reserved: false,
|
Reserved: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proctypes := []PVEProctype{}
|
||||||
|
err = pve.client.Get(context.Background(), fmt.Sprintf("/nodes/%s/capabilities/qemu/cpu", nodeName), &proctypes)
|
||||||
|
if err != nil {
|
||||||
|
return &host, err
|
||||||
|
}
|
||||||
|
for _, proctype := range proctypes {
|
||||||
|
host.Proctypes = append(host.Proctypes, proctype.Name)
|
||||||
|
}
|
||||||
|
|
||||||
host.Name = node.Name
|
host.Name = node.Name
|
||||||
host.Cores = uint64(node.CPUInfo.CPUs)
|
host.Cores = uint64(node.CPUInfo.CPUs)
|
||||||
host.Memory = uint64(node.Memory.Total)
|
host.Memory = uint64(node.Memory.Total)
|
||||||
@@ -130,6 +153,7 @@ func (host *Node) VirtualMachine(VMID uint) (*Instance, error) {
|
|||||||
instance.configHostPCIs = config.MergeHostPCIs()
|
instance.configHostPCIs = config.MergeHostPCIs()
|
||||||
instance.configNets = config.MergeNets()
|
instance.configNets = config.MergeNets()
|
||||||
instance.configDisks = MergeVMDisksAndUnused(config)
|
instance.configDisks = MergeVMDisksAndUnused(config)
|
||||||
|
instance.configBoot = config.Boot
|
||||||
|
|
||||||
instance.pveconfig = config
|
instance.pveconfig = config
|
||||||
instance.Type = VM
|
instance.Type = VM
|
||||||
@@ -140,7 +164,7 @@ func (host *Node) VirtualMachine(VMID uint) (*Instance, error) {
|
|||||||
instance.Memory = uint64(vm.VirtualMachineConfig.Memory) * MiB
|
instance.Memory = uint64(vm.VirtualMachineConfig.Memory) * MiB
|
||||||
instance.Volumes = make(map[VolumeID]*Volume)
|
instance.Volumes = make(map[VolumeID]*Volume)
|
||||||
instance.Nets = make(map[NetID]*Net)
|
instance.Nets = make(map[NetID]*Net)
|
||||||
instance.Devices = make(map[InstanceDeviceID]*Device)
|
instance.Devices = make(map[DeviceID]*Device)
|
||||||
|
|
||||||
return &instance, nil
|
return &instance, nil
|
||||||
}
|
}
|
||||||
@@ -208,9 +232,11 @@ func MergeCTDisksAndUnused(cc *proxmox.ContainerConfig) map[string]string {
|
|||||||
func GetVolumeInfo(host *Node, volume string) (*Volume, error) {
|
func GetVolumeInfo(host *Node, volume string) (*Volume, error) {
|
||||||
volumeData := Volume{}
|
volumeData := Volume{}
|
||||||
|
|
||||||
storageID := strings.Split(volume, ":")[0]
|
volumeObj := PVEObjectStringToMap(volume)
|
||||||
volumeID := strings.Split(volume, ",")[0]
|
volumeFile := volumeObj[""]
|
||||||
storage, err := host.pvenode.Storage(context.Background(), storageID)
|
volumeStorage := strings.Split(volumeFile, ":")[0]
|
||||||
|
|
||||||
|
storage, err := host.pvenode.Storage(context.Background(), volumeStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &volumeData, nil
|
return &volumeData, nil
|
||||||
}
|
}
|
||||||
@@ -221,37 +247,58 @@ func GetVolumeInfo(host *Node, volume string) (*Volume, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range content {
|
for _, c := range content {
|
||||||
if c.Volid == volumeID {
|
if c.Volid == volumeFile {
|
||||||
volumeData.Storage = storageID
|
volumeData.Storage = volumeStorage
|
||||||
volumeData.Format = c.Format
|
volumeData.Format = c.Format
|
||||||
volumeData.Size = uint64(c.Size)
|
volumeData.Size = uint64(c.Size)
|
||||||
volumeData.Volid = VolumeID(volumeID)
|
volumeData.File = volumeFile
|
||||||
|
volumeData.MP = volumeObj["mp"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &volumeData, nil
|
return &volumeData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNetInfo(net string) (*Net, error) {
|
func GetNetInfo(netstring string) (*Net, error) {
|
||||||
n := Net{}
|
n := Net{}
|
||||||
|
|
||||||
for _, val := range strings.Split(net, ",") {
|
netobj := PVEObjectStringToMap(netstring)
|
||||||
if strings.HasPrefix(val, "rate=") {
|
|
||||||
rate, err := strconv.ParseUint(strings.TrimPrefix(val, "rate="), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return &n, err
|
|
||||||
}
|
|
||||||
n.Rate = rate
|
|
||||||
} else if strings.HasPrefix(val, "tag=") {
|
|
||||||
vlan, err := strconv.ParseUint(strings.TrimPrefix(val, "tag="), 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return &n, err
|
|
||||||
}
|
|
||||||
n.VLAN = vlan
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Value = net
|
rate, err := strconv.ParseUint(netobj["rate"], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return &n, err
|
||||||
|
}
|
||||||
|
n.Rate = rate
|
||||||
|
|
||||||
|
vlan, err := strconv.ParseUint(netobj["tag"], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return &n, err
|
||||||
|
}
|
||||||
|
n.VLAN = vlan
|
||||||
|
|
||||||
|
n.Value = netstring
|
||||||
|
|
||||||
return &n, nil
|
return &n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// most pve objects (nets, disks, pcie, etc) have the following similar format:
|
||||||
|
// objname: v1,k2=v2,k3=v3,k4=v4 ...
|
||||||
|
// this function maps such strings to a map so that each individual key or value can be found more quickly
|
||||||
|
// in pcie or disks, the first value often does not have a key name, in such cases the key will be empty string ""
|
||||||
|
func PVEObjectStringToMap(objectstring string) map[string]string {
|
||||||
|
objectmap := map[string]string{}
|
||||||
|
for v := range strings.SplitSeq(objectstring, ",") {
|
||||||
|
key := ""
|
||||||
|
val := ""
|
||||||
|
if strings.Contains(v, "=") {
|
||||||
|
x := strings.Split(v, "=")
|
||||||
|
key = x[0]
|
||||||
|
val = x[1]
|
||||||
|
} else {
|
||||||
|
key = ""
|
||||||
|
val = v
|
||||||
|
}
|
||||||
|
objectmap[key] = val
|
||||||
|
}
|
||||||
|
return objectmap
|
||||||
|
}
|
||||||
|
|||||||
89
app/types.go
89
app/types.go
@@ -18,8 +18,9 @@ type Node struct {
|
|||||||
Cores uint64 `json:"cores"`
|
Cores uint64 `json:"cores"`
|
||||||
Memory uint64 `json:"memory"`
|
Memory uint64 `json:"memory"`
|
||||||
Swap uint64 `json:"swap"`
|
Swap uint64 `json:"swap"`
|
||||||
Devices map[DeviceID]*Device `json:"devices"`
|
Devices map[DeviceBus]*Device `json:"devices"`
|
||||||
Instances map[InstanceID]*Instance `json:"instances"`
|
Instances map[InstanceID]*Instance `json:"instances"`
|
||||||
|
Proctypes []string `json:"cpus"`
|
||||||
pvenode *proxmox.Node
|
pvenode *proxmox.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,58 +34,72 @@ const (
|
|||||||
|
|
||||||
type Instance struct {
|
type Instance struct {
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
Type InstanceType `json:"type"`
|
Type InstanceType `json:"type"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Proctype string `json:"cpu"`
|
Proctype string `json:"cpu"`
|
||||||
Cores uint64 `json:"cores"`
|
Cores uint64 `json:"cores"`
|
||||||
Memory uint64 `json:"memory"`
|
Memory uint64 `json:"memory"`
|
||||||
Swap uint64 `json:"swap"`
|
Swap uint64 `json:"swap"`
|
||||||
Volumes map[VolumeID]*Volume `json:"volumes"`
|
Volumes map[VolumeID]*Volume `json:"volumes"`
|
||||||
Nets map[NetID]*Net `json:"nets"`
|
Nets map[NetID]*Net `json:"nets"`
|
||||||
Devices map[InstanceDeviceID]*Device `json:"devices"`
|
Devices map[DeviceID]*Device `json:"devices"`
|
||||||
pveconfig interface{}
|
Boot BootOrder `json:"boot"`
|
||||||
|
pveconfig any
|
||||||
configDisks map[string]string
|
configDisks map[string]string
|
||||||
configNets map[string]string
|
configNets map[string]string
|
||||||
configHostPCIs map[string]string
|
configHostPCIs map[string]string
|
||||||
|
configBoot string
|
||||||
|
}
|
||||||
|
|
||||||
|
var VolumeTypes = []string{
|
||||||
|
"sata",
|
||||||
|
"scsi",
|
||||||
|
"ide",
|
||||||
|
"rootfs",
|
||||||
|
"mp",
|
||||||
|
"unused",
|
||||||
}
|
}
|
||||||
|
|
||||||
type VolumeID string
|
type VolumeID string
|
||||||
type Volume struct {
|
type Volume struct {
|
||||||
Storage string `json:"storage"`
|
Volume_ID VolumeID `json:"volume_id"`
|
||||||
Format string `json:"format"`
|
Type string `json:"type"`
|
||||||
Size uint64 `json:"size"`
|
Storage string `json:"storage"`
|
||||||
Volid VolumeID `json:"volid"`
|
Format string `json:"format"`
|
||||||
|
Size uint64 `json:"size"`
|
||||||
|
File string `json:"file"`
|
||||||
|
MP string `json:"mp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type NetID uint64
|
type NetID string
|
||||||
type Net struct {
|
type Net struct {
|
||||||
Value string `json:"value"`
|
Net_ID NetID `json:"net_id"`
|
||||||
Rate uint64 `json:"rate"`
|
Value string `json:"value"`
|
||||||
VLAN uint64 `json:"vlan"`
|
Rate uint64 `json:"rate"`
|
||||||
}
|
VLAN uint64 `json:"vlan"`
|
||||||
|
|
||||||
type PVEDevice struct {
|
|
||||||
ID 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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceID string
|
type DeviceID string
|
||||||
type InstanceDeviceID uint64
|
type DeviceBus string
|
||||||
type Device struct {
|
type Device struct {
|
||||||
DeviceID DeviceID `json:"device_id"`
|
Device_ID DeviceID `json:"device_id"`
|
||||||
DeviceName string `json:"device_name"`
|
Device_Bus DeviceBus `json:"device_bus"`
|
||||||
VendorName string `json:"vendor_name"`
|
Device_Name string `json:"device_name"`
|
||||||
Functions map[FunctionID]*Function `json:"functions"`
|
Vendor_Name string `json:"vendor_name"`
|
||||||
Reserved bool `json:"reserved"`
|
Functions map[FunctionID]*Function `json:"functions"`
|
||||||
|
Reserved bool `json:"reserved"`
|
||||||
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
type FunctionID string
|
type FunctionID string
|
||||||
type Function struct {
|
type Function struct {
|
||||||
FunctionID FunctionID `json:"function_id"`
|
Function_ID FunctionID `json:"function_id"`
|
||||||
FunctionName string `json:"subsystem_device_name"`
|
Function_Name string `json:"subsystem_device_name"`
|
||||||
VendorName string `json:"subsystem_vendor_name"`
|
Vendor_Name string `json:"subsystem_vendor_name"`
|
||||||
Reserved bool `json:"reserved"`
|
Reserved bool `json:"reserved"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BootOrder struct {
|
||||||
|
Enabled []any `json:"enabled"`
|
||||||
|
Disabled []any `json:"disabled"`
|
||||||
}
|
}
|
||||||
|
|||||||
20
app/utils.go
20
app/utils.go
@@ -36,7 +36,7 @@ func GetConfig(configPath string) Config {
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns if a device pcie bus id is a super device or subsystem device
|
// checks if a device pcie bus id is a super device or subsystem device
|
||||||
//
|
//
|
||||||
// subsystem devices always has the format xxxx:yy.z, whereas super devices have the format xxxx:yy
|
// subsystem devices always has the format xxxx:yy.z, whereas super devices have the format xxxx:yy
|
||||||
//
|
//
|
||||||
@@ -45,13 +45,15 @@ func DeviceBusIDIsSuperDevice(BusID DeviceID) bool {
|
|||||||
return !strings.ContainsRune(string(BusID), '.')
|
return !strings.ContainsRune(string(BusID), '.')
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns if a device pcie bus id is a subdevice of specified super device
|
// checks if string s has one of any prefixes, and returns the prefix or "" if there was no match
|
||||||
//
|
//
|
||||||
// subsystem devices always has the format xxxx:yy.z, whereas super devices have the format xxxx:yy
|
// matches the first prefix match in array order
|
||||||
//
|
func AnyPrefixes(s string, prefixes []string) string {
|
||||||
// returns true if BusID has prefix SuperDeviceBusID and SuperDeviceBusID is a Super Device
|
for _, prefix := range prefixes {
|
||||||
/*
|
if strings.HasPrefix(s, prefix) {
|
||||||
func DeviceBusIDIsSubDevice(BusID string, SuperDeviceBusID string) bool {
|
return prefix
|
||||||
return DeviceBusIDIsSuperDevice(SuperDeviceBusID) && strings.HasPrefix(BusID, SuperDeviceBusID)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|||||||
63
go.mod
63
go.mod
@@ -1,52 +1,55 @@
|
|||||||
module proxmoxaas-fabric
|
module proxmoxaas-fabric
|
||||||
|
|
||||||
go 1.23.6
|
go 1.25.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/luthermonson/go-proxmox v0.2.1
|
github.com/luthermonson/go-proxmox v0.2.3
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/buger/goterm v1.0.4 // indirect
|
github.com/buger/goterm v1.0.4 // indirect
|
||||||
github.com/bytedance/sonic v1.12.8 // indirect
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
github.com/bytedance/sonic v1.14.1 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
github.com/diskfs/go-diskfs v1.4.2 // indirect
|
github.com/diskfs/go-diskfs v1.7.0 // indirect
|
||||||
github.com/djherbis/times v1.6.0 // indirect
|
github.com/djherbis/times v1.6.0 // indirect
|
||||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab // indirect
|
github.com/elliotwutingfeng/asciiset v0.0.0-20250812055617-fb43ac3ba420 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.24.0 // indirect
|
github.com/go-playground/validator/v10 v10.28.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/jinzhu/copier v0.4.0 // indirect
|
github.com/jinzhu/copier v0.4.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
github.com/knz/go-libedit v1.10.1 // indirect
|
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magefile/mage v1.15.0 // indirect
|
github.com/magefile/mage v1.15.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
github.com/pkg/xattr v0.4.9 // indirect
|
github.com/pkg/xattr v0.4.12 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/quic-go/quic-go v0.55.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||||
golang.org/x/arch v0.14.0 // indirect
|
go.uber.org/mock v0.6.0 // indirect
|
||||||
golang.org/x/crypto v0.32.0 // indirect
|
golang.org/x/arch v0.22.0 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/crypto v0.43.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/mod v0.29.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/net v0.46.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
golang.org/x/sync v0.17.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
|
golang.org/x/text v0.30.0 // indirect
|
||||||
|
golang.org/x/tools v0.38.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.10 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user