2024-10-18 04:28:31 +00:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2024-12-27 19:59:44 +00:00
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"fmt"
|
2024-10-18 04:28:31 +00:00
|
|
|
"net/http"
|
2025-01-08 22:42:17 +00:00
|
|
|
"strconv"
|
2024-12-27 19:59:44 +00:00
|
|
|
"strings"
|
2024-10-18 04:28:31 +00:00
|
|
|
|
|
|
|
"github.com/luthermonson/go-proxmox"
|
|
|
|
)
|
|
|
|
|
2024-12-27 19:59:44 +00:00
|
|
|
type ProxmoxClient struct {
|
|
|
|
client *proxmox.Client
|
|
|
|
}
|
|
|
|
|
2025-01-28 00:48:53 +00:00
|
|
|
func NewClient(url string, token string, secret string) ProxmoxClient {
|
2024-12-27 19:59:44 +00:00
|
|
|
HTTPClient := http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2025-01-28 00:48:53 +00:00
|
|
|
client := proxmox.NewClient(url,
|
2024-10-18 04:28:31 +00:00
|
|
|
proxmox.WithHTTPClient(&HTTPClient),
|
2024-12-27 19:59:44 +00:00
|
|
|
proxmox.WithAPIToken(token, secret),
|
2024-10-18 04:28:31 +00:00
|
|
|
)
|
|
|
|
|
2024-12-27 19:59:44 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
// Gets all Nodes names
|
|
|
|
func (pve ProxmoxClient) Nodes() ([]string, error) {
|
|
|
|
nodes, err := pve.client.Nodes(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
names := []string{}
|
|
|
|
for _, node := range nodes {
|
|
|
|
names = append(names, node.Node)
|
|
|
|
}
|
|
|
|
|
|
|
|
return names, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets a Node's resources but does not recursively expand instances
|
2025-01-20 21:42:13 +00:00
|
|
|
func (pve ProxmoxClient) Node(nodeName string) (*Host, error) {
|
2024-12-27 19:59:44 +00:00
|
|
|
host := Host{}
|
2025-01-14 04:44:18 +00:00
|
|
|
host.Devices = make(map[string]*Device)
|
|
|
|
host.Instances = make(map[uint]*Instance)
|
2024-12-27 19:59:44 +00:00
|
|
|
|
|
|
|
node, err := pve.client.Node(context.Background(), nodeName)
|
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &host, err
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
|
|
|
|
2025-01-14 04:44:18 +00:00
|
|
|
devices := []Device{}
|
2024-12-27 19:59:44 +00:00
|
|
|
err = pve.client.Get(context.Background(), fmt.Sprintf("/nodes/%s/hardware/pci", nodeName), &devices)
|
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &host, err
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
for _, device := range devices {
|
2025-01-14 04:44:18 +00:00
|
|
|
host.Devices[device.BusID] = &device
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
host.Name = node.Name
|
2025-01-28 00:48:53 +00:00
|
|
|
host.Cores = uint64(node.CPUInfo.CPUs)
|
|
|
|
host.Memory = uint64(node.Memory.Total)
|
|
|
|
host.Swap = uint64(node.Swap.Total)
|
2025-01-20 21:42:13 +00:00
|
|
|
host.pvenode = node
|
2025-01-08 22:42:17 +00:00
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
return &host, err
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get all VM IDs on specified host
|
2025-01-20 21:42:13 +00:00
|
|
|
func (host *Host) VirtualMachines() ([]uint, error) {
|
|
|
|
vms, err := host.pvenode.VirtualMachines(context.Background())
|
2024-12-27 19:59:44 +00:00
|
|
|
if err != nil {
|
2025-01-08 22:42:17 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ids := []uint{}
|
|
|
|
for _, vm := range vms {
|
|
|
|
ids = append(ids, uint(vm.VMID))
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
2025-01-08 22:42:17 +00:00
|
|
|
return ids, nil
|
|
|
|
}
|
2024-12-27 19:59:44 +00:00
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
// Get a VM's CPU, Memory but does not recursively link Devices, Disks, Drives, Nets
|
2025-01-20 21:42:13 +00:00
|
|
|
func (host *Host) VirtualMachine(VMID uint) (*Instance, error) {
|
2025-01-08 22:42:17 +00:00
|
|
|
instance := Instance{}
|
2025-01-20 21:42:13 +00:00
|
|
|
vm, err := host.pvenode.VirtualMachine(context.Background(), int(VMID))
|
2025-01-08 22:42:17 +00:00
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &instance, err
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
config := vm.VirtualMachineConfig
|
2025-01-10 01:08:44 +00:00
|
|
|
instance.configHostPCIs = config.MergeHostPCIs()
|
2025-01-08 22:42:17 +00:00
|
|
|
instance.configNets = config.MergeNets()
|
|
|
|
instance.configDisks = MergeVMDisksAndUnused(config)
|
2024-12-27 19:59:44 +00:00
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
instance.pveconfig = config
|
2025-01-08 22:42:17 +00:00
|
|
|
instance.Type = VM
|
2024-12-27 19:59:44 +00:00
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
instance.Name = vm.Name
|
|
|
|
instance.Proctype = vm.VirtualMachineConfig.CPU
|
|
|
|
instance.Cores = uint64(vm.VirtualMachineConfig.Cores)
|
|
|
|
instance.Memory = uint64(vm.VirtualMachineConfig.Memory) * MiB
|
2025-01-14 04:44:18 +00:00
|
|
|
instance.Volumes = make(map[string]*Volume)
|
|
|
|
instance.Nets = make(map[uint]*Net)
|
|
|
|
instance.Devices = make(map[uint][]*Device)
|
2025-01-08 22:42:17 +00:00
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
return &instance, nil
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func MergeVMDisksAndUnused(vmc *proxmox.VirtualMachineConfig) map[string]string {
|
|
|
|
mergedDisks := vmc.MergeDisks()
|
|
|
|
for k, v := range vmc.MergeUnuseds() {
|
|
|
|
mergedDisks[k] = v
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
2025-01-08 22:42:17 +00:00
|
|
|
return mergedDisks
|
|
|
|
}
|
2024-12-27 19:59:44 +00:00
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
// Get all CT IDs on specified host
|
2025-01-20 21:42:13 +00:00
|
|
|
func (host *Host) Containers() ([]uint, error) {
|
|
|
|
cts, err := host.pvenode.Containers(context.Background())
|
2025-01-08 22:42:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ids := []uint{}
|
2024-12-27 19:59:44 +00:00
|
|
|
for _, ct := range cts {
|
2025-01-08 22:42:17 +00:00
|
|
|
ids = append(ids, uint(ct.VMID))
|
|
|
|
}
|
|
|
|
return ids, nil
|
|
|
|
}
|
2024-12-27 19:59:44 +00:00
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
// Get a CT's CPU, Memory, Swap but does not recursively link Devices, Disks, Drives, Nets
|
2025-01-20 21:42:13 +00:00
|
|
|
func (host *Host) Container(VMID uint) (*Instance, error) {
|
2025-01-08 22:42:17 +00:00
|
|
|
instance := Instance{}
|
2025-01-20 21:42:13 +00:00
|
|
|
ct, err := host.pvenode.Container(context.Background(), int(VMID))
|
2025-01-08 22:42:17 +00:00
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &instance, err
|
2024-12-27 19:59:44 +00:00
|
|
|
}
|
|
|
|
|
2025-01-08 22:42:17 +00:00
|
|
|
config := ct.ContainerConfig
|
2025-01-10 01:08:44 +00:00
|
|
|
instance.configHostPCIs = make(map[string]string)
|
2025-01-08 22:42:17 +00:00
|
|
|
instance.configNets = config.MergeNets()
|
|
|
|
instance.configDisks = MergeCTDisksAndUnused(config)
|
2024-12-27 19:59:44 +00:00
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
instance.pveconfig = config
|
2025-01-08 22:42:17 +00:00
|
|
|
instance.Type = CT
|
|
|
|
|
|
|
|
instance.Name = ct.Name
|
|
|
|
instance.Cores = uint64(ct.ContainerConfig.Cores)
|
|
|
|
instance.Memory = uint64(ct.ContainerConfig.Memory) * MiB
|
|
|
|
instance.Swap = uint64(ct.ContainerConfig.Swap) * MiB
|
2025-01-14 04:44:18 +00:00
|
|
|
instance.Volumes = make(map[string]*Volume)
|
|
|
|
instance.Nets = make(map[uint]*Net)
|
2025-01-08 22:42:17 +00:00
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
return &instance, nil
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func MergeCTDisksAndUnused(cc *proxmox.ContainerConfig) map[string]string {
|
|
|
|
mergedDisks := make(map[string]string)
|
|
|
|
for k, v := range cc.MergeUnuseds() {
|
|
|
|
mergedDisks[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range cc.MergeMps() {
|
|
|
|
mergedDisks[k] = v
|
|
|
|
}
|
|
|
|
mergedDisks["rootfs"] = cc.RootFS
|
|
|
|
return mergedDisks
|
|
|
|
}
|
|
|
|
|
|
|
|
// get volume fornmat, size, volumeid, and storageid from instance volume data string (eg: local:100/vm-100-disk-0.raw ... )
|
2025-01-20 21:42:13 +00:00
|
|
|
func GetVolumeInfo(host *Host, volume string) (*Volume, string, string, error) {
|
2025-01-08 22:42:17 +00:00
|
|
|
volumeData := Volume{}
|
|
|
|
|
|
|
|
storageID := strings.Split(volume, ":")[0]
|
|
|
|
volumeID := strings.Split(volume, ",")[0]
|
2025-01-20 21:42:13 +00:00
|
|
|
storage, err := host.pvenode.Storage(context.Background(), storageID)
|
2025-01-08 22:42:17 +00:00
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &volumeData, volumeID, storageID, nil
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
content, err := storage.GetContent(context.Background())
|
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &volumeData, volumeID, storageID, nil
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range content {
|
|
|
|
if c.Volid == volumeID {
|
|
|
|
volumeData.Format = c.Format
|
|
|
|
volumeData.Size = uint64(c.Size)
|
|
|
|
volumeData.Volid = volumeID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
return &volumeData, volumeID, storageID, nil
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
func GetNetInfo(net string) (*Net, error) {
|
2025-01-08 22:42:17 +00:00
|
|
|
n := Net{}
|
|
|
|
|
|
|
|
for _, val := range strings.Split(net, ",") {
|
|
|
|
if strings.HasPrefix(val, "rate=") {
|
|
|
|
rate, err := strconv.ParseUint(strings.TrimPrefix(val, "rate="), 10, 64)
|
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &n, err
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
n.Rate = rate
|
|
|
|
} else if strings.HasPrefix(val, "tag=") {
|
|
|
|
vlan, err := strconv.ParseUint(strings.TrimPrefix(val, "tag="), 10, 64)
|
|
|
|
if err != nil {
|
2025-01-20 21:42:13 +00:00
|
|
|
return &n, err
|
2025-01-08 22:42:17 +00:00
|
|
|
}
|
|
|
|
n.VLAN = vlan
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-20 21:42:13 +00:00
|
|
|
return &n, nil
|
2024-10-18 04:28:31 +00:00
|
|
|
}
|