Initial commit
This commit is contained in:
257
proxmox/api_client.go
Normal file
257
proxmox/api_client.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/patrickmn/go-cache"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Multipoint API HTTP client will make requests in round-robin fashion on all endpoints which are reachable.
|
||||
// The client will periodically check endpoints for liveness.
|
||||
type ApiClient struct {
|
||||
endpoints []*ApiEndpoint // API endpoints array.
|
||||
tokenId string // API token ID.
|
||||
secret string // API token secret.
|
||||
httpClient *http.Client // Internal HTTP client.
|
||||
checkInterval time.Duration // Check interval how often will APi client look for endpoints liveness.
|
||||
stop chan struct{} // Stop channel which is used in ticker.
|
||||
mutex sync.Mutex // Stop mutex to prevent requests making requests on unitialized API client instance.
|
||||
stopped bool // state of API client.
|
||||
index int // Round robin next index.
|
||||
aliveCount int // Count of alive endpoints.
|
||||
cache *cache.Cache // Cache instance.
|
||||
}
|
||||
|
||||
// PVE API endpoint.
|
||||
type ApiEndpoint struct {
|
||||
host string // Endpoint address.
|
||||
alive bool // Endpoint liveness state.
|
||||
}
|
||||
|
||||
// PVE API response.
|
||||
type ApiResponse struct {
|
||||
Data json.RawMessage `json:"data"` // Data message from PVE API.
|
||||
}
|
||||
|
||||
// Create new instance of API HTTP client.
|
||||
func NewApiClient(endpoints []string, tokenId string, secret string, checkInterval time.Duration) *ApiClient {
|
||||
instance := ApiClient{
|
||||
checkInterval: checkInterval,
|
||||
tokenId: tokenId,
|
||||
secret: secret,
|
||||
}
|
||||
|
||||
// Cache initialization.
|
||||
instance.cache = cache.New(30*time.Second, 5*time.Second)
|
||||
|
||||
// Prepare API endpoints.
|
||||
for _, endpoint := range endpoints {
|
||||
apiEndpoint := ApiEndpoint{
|
||||
host: endpoint,
|
||||
alive: false,
|
||||
}
|
||||
instance.endpoints = append(instance.endpoints, &apiEndpoint)
|
||||
}
|
||||
|
||||
// Configure HTTP transport.
|
||||
transport := &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 60 * time.Second,
|
||||
DualStack: true,
|
||||
}).Dial,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
DisableCompression: false,
|
||||
MaxIdleConns: 4,
|
||||
MaxIdleConnsPerHost: 4,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
// Create instance of HTTP client with modified HTTP transport.
|
||||
instance.httpClient = &http.Client{
|
||||
Timeout: time.Second * 10,
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
return &instance
|
||||
}
|
||||
|
||||
// Check endpoint liveness state.
|
||||
func (instance *ApiClient) checkEndpointsLiveness() {
|
||||
// We want to make sure other routines won't make any requests until we have checked for alive connections.
|
||||
instance.mutex.Lock()
|
||||
defer instance.mutex.Unlock()
|
||||
|
||||
prevAliveCount := instance.aliveCount
|
||||
instance.aliveCount = 0
|
||||
|
||||
for _, endpoint := range instance.endpoints {
|
||||
endpoint.alive = false
|
||||
|
||||
url := fmt.Sprintf("%s%s", endpoint.host, "api2/json/")
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Errorf("Liveness check of host %s failed. Error: %s. ", endpoint.host, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Authentication token.
|
||||
req.Header.Set("Authorization", fmt.Sprintf("PVEAPIToken=%s=%s", instance.tokenId, instance.secret))
|
||||
|
||||
resp, err := instance.httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Debugf("Liveness check of host %s failed. Error: %s. ", endpoint.host, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode == 401 {
|
||||
log.Errorf("Liveness check of host %s failed. Error: Authentication failed.", endpoint.host)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
log.Errorf("Liveness check of host %s failed. Status code: %d. ", endpoint.host, resp.StatusCode)
|
||||
continue
|
||||
}
|
||||
|
||||
instance.aliveCount++
|
||||
endpoint.alive = true
|
||||
}
|
||||
|
||||
if prevAliveCount != instance.aliveCount {
|
||||
log.Infof("Checked Proxmox API hosts. Status: (%d/%d) hosts are UP.", instance.aliveCount, len(instance.endpoints))
|
||||
}
|
||||
}
|
||||
|
||||
// Return one alive endpoint Round-Robin.
|
||||
func (instance *ApiClient) getAliveEndpoint() (*ApiEndpoint, error) {
|
||||
var alive []*ApiEndpoint
|
||||
|
||||
// Prepare only alive endpoints.
|
||||
for _, endpoint := range instance.endpoints {
|
||||
if endpoint.alive {
|
||||
alive = append(alive, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
length := len(alive)
|
||||
|
||||
// If there are no alive endpoints then return error.
|
||||
if length == 0 {
|
||||
return nil, errors.New("All API endpoints are unreachable.")
|
||||
}
|
||||
|
||||
next := alive[instance.index%length]
|
||||
instance.index = (instance.index + 1) % length
|
||||
return next, nil
|
||||
}
|
||||
|
||||
// Check if API client is stopped.
|
||||
func (instance *ApiClient) PerformRequest(method string, path string, params map[string]string) (*ApiResponse, error) {
|
||||
instance.mutex.Lock()
|
||||
defer instance.mutex.Unlock()
|
||||
|
||||
response, found := instance.cache.Get(method + path)
|
||||
if found {
|
||||
r := response.(ApiResponse)
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
endpoint, err := instance.getAliveEndpoint()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%sapi2/json%s", endpoint.host, path)
|
||||
req, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Authentication token.
|
||||
req.Header.Set("Authorization", fmt.Sprintf("PVEAPIToken=%s=%s", instance.tokenId, instance.secret))
|
||||
|
||||
q := req.URL.Query()
|
||||
for key, element := range params {
|
||||
q.Add(key, element)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
req.Header.Set("Connection", "Keep-Alive")
|
||||
req.Header.Set("User-Agent", "Proxmox exporter")
|
||||
|
||||
var maxAttempts int = 3
|
||||
var attempts int = 0
|
||||
var resp *http.Response
|
||||
for attempts <= maxAttempts {
|
||||
resp, err = instance.httpClient.Do(req)
|
||||
// If request is sucessful, then break the loop.
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
// In case the request failed it will be attempted again in another loop iteration after second + attempts made.
|
||||
if err != nil && attempts < maxAttempts {
|
||||
log.Warnf("Request '%s' failed (%s). Attempting again in %d seconds.", url, err.Error(), attempts+1)
|
||||
time.Sleep(time.Duration(attempts+1) * time.Second)
|
||||
continue
|
||||
}
|
||||
// If all attempts fail then return the error.
|
||||
if err != nil && attempts == maxAttempts {
|
||||
return nil, err
|
||||
}
|
||||
attempts++
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Decode JSON to ApiResponse struct.
|
||||
result := ApiResponse{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
instance.cache.Set(method+path, result, time.Second*5)
|
||||
return &result, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check if API client is stopped.
|
||||
func (instance *ApiClient) IsStopped() bool {
|
||||
instance.mutex.Lock()
|
||||
defer instance.mutex.Unlock()
|
||||
return instance.stopped
|
||||
}
|
||||
|
||||
// Start the API client.
|
||||
func (instance *ApiClient) Start() {
|
||||
ticker := time.NewTicker(instance.checkInterval)
|
||||
instance.checkEndpointsLiveness()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
instance.checkEndpointsLiveness()
|
||||
case <-instance.stop:
|
||||
instance.mutex.Lock()
|
||||
defer instance.mutex.Unlock()
|
||||
instance.stopped = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Close connections and stop the API client.
|
||||
func (instance *ApiClient) Stop() {
|
||||
instance.httpClient.CloseIdleConnections()
|
||||
close(instance.stop)
|
||||
}
|
||||
494
proxmox/model.go
Normal file
494
proxmox/model.go
Normal file
@@ -0,0 +1,494 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PveVersion represents the version information of a PVE (Proxmox Virtual Environment).
|
||||
type PveVersion struct {
|
||||
Version string `json:"version"` // PVE version number.
|
||||
RepoID string `json:"repoid"` // Repository ID of the PVE.
|
||||
Release string `json:"release"` // Release version of the PVE.
|
||||
}
|
||||
|
||||
// PveNodeStatus represents the status of a PVE node.
|
||||
type PveNodeStatus struct {
|
||||
IP string `json:"ip"` // IP address of the node.
|
||||
Name string `json:"name"` // Name of the node.
|
||||
Online uint8 `json:"online"` // Online status of the node (1 = online, 0 = offline).
|
||||
Local uint8 `json:"local"` // Local status of the node (1 = local, 0 = not local).
|
||||
NodeID int `json:"nodeid"` // Unique identifier for the node.
|
||||
Type string `json:"type"` // Type of the resource (e.g., "node").
|
||||
Level string `json:"level"` // Subscription level of the node.
|
||||
ID string `json:"id"` // Unique identifier for the node resource.
|
||||
}
|
||||
|
||||
// PveClusterStatus represents the status of a PVE cluster.
|
||||
type PveClusterStatus struct {
|
||||
Name string `json:"name"` // Name of the cluster.
|
||||
ID string `json:"id"` // Unique identifier for the cluster resource.
|
||||
Type string `json:"type"` // Type of the resource (e.g., "cluster").
|
||||
Version uint16 `json:"version"` // Version of the cluster.
|
||||
Quorate uint8 `json:"quorate"` // Quorate status of the cluster (1 = quorate, 0 = not quorate).
|
||||
Nodes uint16 `json:"nodes"` // Number of nodes in the cluster.
|
||||
NodeStatuses []PveNodeStatus `json:"-"` // Cluster nodes statuses.
|
||||
}
|
||||
|
||||
// PveResource represents a generic PVE resource object.
|
||||
type PveResource struct {
|
||||
Type string `mapstructure:"type"` // Type of resource (e.g., "lxc", "qemu", "node", "storage", "sdn").
|
||||
Node string `mapstructure:"node"` // Node where the resource is located.
|
||||
Status string `mapstructure:"status"` // Status of the resource (e.g., "running", "stopped", "online", "available").
|
||||
ID string `mapstructure:"id"` // Unique identifier for the resource.
|
||||
}
|
||||
|
||||
// PveLxcResource represents a PVE LXC container resource.
|
||||
type PveLxcResource struct {
|
||||
PveResource
|
||||
DiskRead uint64 `mapstructure:"diskread"` // Disk read bytes.
|
||||
MaxDisk uint64 `mapstructure:"maxdisk"` // Maximum disk size in bytes.
|
||||
CPU float64 `mapstructure:"cpu"` // CPU usage.
|
||||
NetOut uint64 `mapstructure:"netout"` // Network output bytes.
|
||||
Uptime uint32 `mapstructure:"uptime"` // Uptime in seconds.
|
||||
NetIn uint64 `mapstructure:"netin"` // Network input bytes.
|
||||
MaxCPU uint16 `mapstructure:"maxcpu"` // Maximum number of CPUs.
|
||||
Disk uint64 `mapstructure:"disk"` // Disk usage in bytes.
|
||||
Tags string `mapstructure:"tags"` // Tags associated with the container.
|
||||
VMID uint16 `mapstructure:"vmid"` // Virtual Machine ID.
|
||||
DiskWrite uint64 `mapstructure:"diskwrite"` // Disk write bytes.
|
||||
Name string `mapstructure:"name"` // Name of the container.
|
||||
Template uint8 `mapstructure:"template"` // Template status (0 = not a template, 1 = template).
|
||||
MaxMem uint64 `mapstructure:"maxmem"` // Maximum memory in bytes.
|
||||
ID string `mapstructure:"id"` // Unique identifier for the resource.
|
||||
}
|
||||
|
||||
// PveQemuResource represents a PVE QEMU virtual machine resource.
|
||||
type PveQemuResource struct {
|
||||
PveResource
|
||||
DiskRead uint64 `mapstructure:"diskread"` // Disk read bytes.
|
||||
MaxDisk uint64 `mapstructure:"maxdisk"` // Maximum disk size in bytes.
|
||||
NetOut uint64 `mapstructure:"netout"` // Network output bytes.
|
||||
Uptime uint32 `mapstructure:"uptime"` // Uptime in seconds.
|
||||
NetIn uint64 `mapstructure:"netin"` // Network input bytes.
|
||||
MaxCPU uint16 `mapstructure:"maxcpu"` // Maximum number of CPUs.
|
||||
Disk uint64 `mapstructure:"disk"` // Disk usage in bytes.
|
||||
Mem uint64 `mapstructure:"mem"` // Memory usage in bytes.
|
||||
Name string `mapstructure:"name"` // Name of the virtual machine.
|
||||
Template uint8 `mapstructure:"template"` // Template status (0 = not a template, 1 = template).
|
||||
MaxMem uint64 `mapstructure:"maxmem"` // Maximum memory in bytes.
|
||||
}
|
||||
|
||||
// PveNodeResource represents a PVE node resource.
|
||||
type PveNodeResource struct {
|
||||
PveResource
|
||||
MaxMem uint64 `mapstructure:"maxmem"` // Maximum memory in bytes.
|
||||
MaxCPU uint16 `mapstructure:"maxcpu"` // Maximum number of CPUs.
|
||||
Level string `mapstructure:"level"` // Subscription level.
|
||||
CPU float64 `mapstructure:"cpu"` // CPU usage.
|
||||
Uptime uint32 `mapstructure:"uptime"` // Uptime in seconds.
|
||||
Disk uint64 `mapstructure:"disk"` // Disk usage in bytes.
|
||||
Mem uint64 `mapstructure:"mem"` // Memory usage in bytes.
|
||||
CGroupMode uint8 `mapstructure:"cgroup-mode"` // Control group mode.
|
||||
}
|
||||
|
||||
// PveStorageResource represents a PVE storage resource.
|
||||
type PveStorageResource struct {
|
||||
PveResource
|
||||
Storage string `mapstructure:"storage"` // Name of the storage.
|
||||
MaxDisk uint64 `mapstructure:"maxdisk"` // Maximum disk size in bytes.
|
||||
Shared uint64 `mapstructure:"shared"` // Shared storage status (0 = not shared, 1 = shared).
|
||||
PluginType string `mapstructure:"plugintype"` // Type of storage plugin.
|
||||
Status string `mapstructure:"status"` // Status of the storage (e.g., "available").
|
||||
Content string `mapstructure:"content"` // Types of content stored (e.g., "rootdir,images").
|
||||
}
|
||||
|
||||
// PveSdnResource represents a PVE software-defined network (SDN) resource.
|
||||
type PveSdnResource struct {
|
||||
PveResource
|
||||
SDN string `mapstructure:"sdn"` // Name of the SDN.
|
||||
}
|
||||
|
||||
// PVE resources.
|
||||
type PveResources struct {
|
||||
CTs []PveLxcResource // LXC container resources.
|
||||
VMs []PveQemuResource // Virtual machine container resources.
|
||||
Nodes []PveNodeResource // Node resources.
|
||||
Storages []PveStorageResource // Storage resources.
|
||||
SDNs []PveSdnResource // Software defiend network resources.
|
||||
}
|
||||
|
||||
// PveNodeRootfs represents root filesystem stats.
|
||||
type PveNodeRootfs struct {
|
||||
Free uint64 `json:"free"` // Free space in bytes.
|
||||
Total uint64 `json:"total"` // Total space in bytes.
|
||||
Used uint64 `json:"used"` // Used space in bytes.
|
||||
Avail uint64 `json:"avail"` // Available space in bytes.
|
||||
}
|
||||
|
||||
// PveNodeMemory represents memory stats.
|
||||
type PveNodeMemory struct {
|
||||
Total uint64 `json:"total"` // Total memory in bytes.
|
||||
Free uint64 `json:"free"` // Free memory in bytes.
|
||||
Used uint64 `json:"used"` // Used memory in bytes.
|
||||
}
|
||||
|
||||
// PveNodeBootInfo represents boot information.
|
||||
type PveNodeBootInfo struct {
|
||||
Mode string `json:"mode"` // Boot mode (e.g., "efi").
|
||||
Secureboot int `json:"secureboot"` // Secure boot status (0 = disabled, 1 = enabled).
|
||||
}
|
||||
|
||||
// PveNodeKsm represents Kernel Same-page Merging (KSM) info.
|
||||
type PveNodeKsm struct {
|
||||
Shared int64 `json:"shared"` // Amount of shared memory in bytes.
|
||||
}
|
||||
|
||||
// PveNodeCurrentKernel represents current kernel info.
|
||||
type PveNodeCurrentKernel struct {
|
||||
Machine string `json:"machine"` // Machine architecture (e.g., "x86_64").
|
||||
Sysname string `json:"sysname"` // Operating system name (e.g., "Linux").
|
||||
Release string `json:"release"` // Kernel release version.
|
||||
Version string `json:"version"` // Detailed kernel version info.
|
||||
}
|
||||
|
||||
// PveNodeSwap represents swap memory stats.
|
||||
type PveNodeSwap struct {
|
||||
Used uint64 `json:"used"` // Used swap space in bytes.
|
||||
Free uint64 `json:"free"` // Free swap space in bytes.
|
||||
Total uint64 `json:"total"` // Total swap space in bytes.
|
||||
}
|
||||
|
||||
// PveNodeCPUInfo represents CPU information.
|
||||
type PveNodeCPUInfo struct {
|
||||
Flags string `json:"flags"` // CPU flags.
|
||||
Cores int `json:"cores"` // Number of CPU cores.
|
||||
MHz string `json:"mhz"` // CPU frequency in MHz.
|
||||
Model string `json:"model"` // CPU model.
|
||||
Sockets int `json:"sockets"` // Number of CPU sockets.
|
||||
UserHZ float64 `json:"user_hz"` // User HZ value.
|
||||
CPUs int `json:"cpus"` // Number of logical CPUs.
|
||||
HVM string `json:"hvm"` // Hardware virtualization support.
|
||||
}
|
||||
|
||||
// PveNodeStatusDetail represents detailed status of a PVE node.
|
||||
type PveNodeStatusDetail struct {
|
||||
Rootfs PveNodeRootfs `json:"rootfs"` // Root filesystem stats.
|
||||
Wait float64 `json:"wait"` // CPU wait time.
|
||||
Memory PveNodeMemory `json:"memory"` // Memory stats.
|
||||
BootInfo PveNodeBootInfo `json:"boot-info"` // Boot information.
|
||||
Ksm PveNodeKsm `json:"ksm"` // Kernel Same-page Merging (KSM) info.
|
||||
Kversion string `json:"kversion"` // Kernel version.
|
||||
LoadAvg []string `json:"loadavg,string"` // Load average values.
|
||||
CPUInfo PveNodeCPUInfo `json:"cpuinfo"` // CPU information.
|
||||
PveVersion string `json:"pveversion"` // Proxmox VE version.
|
||||
Uptime uint64 `json:"uptime"` // System uptime in seconds.
|
||||
Idle uint64 `json:"idle"` // Idle time in seconds.
|
||||
CurrentKernel PveNodeCurrentKernel `json:"current-kernel"` // Current kernel information.
|
||||
CPU float64 `json:"cpu"` // CPU usage.
|
||||
Swap PveNodeSwap `json:"swap"` // Swap memory stats.
|
||||
}
|
||||
|
||||
// PveSubscription represents PVE node subscription info.
|
||||
type PveSubscription struct {
|
||||
NextDueDate string `json:"nextduedate"` // Next due date for subscription (format: YYYY-MM-DD).
|
||||
ProductName string `json:"productname"` // Name of product associated with subscription.
|
||||
ServerID string `json:"serverid"` // Unique identifier of server.
|
||||
Registration string `json:"regdate"` // Registration date of subscription (format: YYYY-MM-DD HH:MM:SS).
|
||||
Sockets uint8 `json:"sockets"` // Number of CPU sockets covered by subscription.
|
||||
CheckTime uint `json:"checktime"` // Unix timestamp of last check time.
|
||||
URL string `json:"url"` // URL with more information about subscription.
|
||||
Level string `json:"level"` // Subscription level (e.g., "c" for community).
|
||||
Key string `json:"key"` // Subscription key.
|
||||
Status string `json:"status"` // Status of subscription (e.g., "active").
|
||||
}
|
||||
|
||||
// PVE node disk.
|
||||
type PveDisk struct {
|
||||
WWN string `json:"wwn"` // WWN (World Wide Name) of the drive.
|
||||
Type string `json:"type"` // Type of drive (e.g., ssd, hdd).
|
||||
Model string `json:"model"` // Model of the drive.
|
||||
Serial string `json:"serial"` // Serial number of the drive.
|
||||
Size int64 `json:"size"` // Size of the drive in bytes.
|
||||
Vendor string `json:"vendor"` // Vendor of the drive.
|
||||
OSDID int `json:"osdid"` // OSD (Object Storage Device) ID of the drive.
|
||||
DevPath string `json:"devpath"` // Device path of the drive.
|
||||
OSDIDList []int `json:"osdid-list"` // List of OSD IDs associated with the drive.
|
||||
ByIDLink string `json:"by_id_link"` // Symbolic link to the drive in /dev/disk/by-id directory.
|
||||
GPT int `json:"gpt"` // Indicates whether the drive uses GPT partitioning (1 for true, 0 for false).
|
||||
Health string `json:"health"` // Health status of the drive (e.g., PASSED, FAILED, OK).
|
||||
WearOut interface{} `json:"wearout,omitempty"` // Wear out percentage of the drive.
|
||||
Used string `json:"used,omitempty"` // How the drive is used (optional field).
|
||||
}
|
||||
|
||||
// PVE node time.
|
||||
type PveNodeTime struct {
|
||||
Time uint64 `json:"time"` // Unix timestamp in UTC.
|
||||
LocalTime uint64 `json:"localtime"` // Unix timestamp in local time.
|
||||
Timezone string `json:"timezone"` // Timezone information.
|
||||
}
|
||||
|
||||
// PVE storage information.
|
||||
type PveStorage struct {
|
||||
Shared int `json:"shared"` // Indicates if the storage is shared between multiple nodes.
|
||||
Enabled int `json:"enabled"` // Indicates if the storage is enabled.
|
||||
Storage string `json:"storage"` // Name of the storage.
|
||||
Total int64 `json:"total"` // Total storage capacity in bytes.
|
||||
Content string `json:"content"` // Type of content stored (e.g., backup, ISO, vztmpl).
|
||||
Avail int64 `json:"avail"` // Available storage capacity in bytes.
|
||||
Active int `json:"active"` // Indicates if the storage is active.
|
||||
Used int64 `json:"used"` // Used storage capacity in bytes.
|
||||
UsedFraction float64 `json:"used_fraction"` // Fraction of used storage.
|
||||
Type string `json:"type"` // Type of storage (e.g., pbs, zfspool, rbd, lvm).
|
||||
}
|
||||
|
||||
// PveLXCStatus represents the status information of a PVE LXC container.
|
||||
type PveLxcStatus struct {
|
||||
Status string `json:"status"` // Status of the LXC container (e.g., "running", "stopped").
|
||||
DiskRead uint64 `json:"diskread"` // Amount of disk read by the LXC container.
|
||||
DiskWrite uint64 `json:"diskwrite"` // Amount of disk write by the LXC container.
|
||||
VMID string `json:"vmid"` // Virtual Machine ID of the LXC container.
|
||||
MaxSwap uint64 `json:"maxswap"` // Maximum swap space allocated for the LXC container.
|
||||
PID uint32 `json:"pid"` // Process ID of the LXC container.
|
||||
Disk uint64 `json:"disk"` // Disk usage of the LXC container.
|
||||
Type string `json:"type"` // Type of the container (e.g., "lxc").
|
||||
MaxMem uint64 `json:"maxmem"` // Maximum memory allocated for the LXC container.
|
||||
Name string `json:"name"` // Name of the LXC container.
|
||||
NetIn uint64 `json:"netin"` // Network incoming traffic of the LXC container.
|
||||
CPU float64 `json:"cpu"` // CPU usage of the LXC container.
|
||||
Uptime uint32 `json:"uptime"` // Uptime of the LXC container in seconds.
|
||||
NetOut uint64 `json:"netout"` // Network outgoing traffic of the LXC container.
|
||||
Ha PveHaStatus `json:"ha"` // High Availability configuration for the LXC container.
|
||||
CPUs int `json:"cpus"` // Number of CPUs allocated for the LXC container.
|
||||
Swap uint64 `json:"swap"` // Swap usage of the LXC container.
|
||||
Mem uint64 `json:"mem"` // Memory usage of the LXC container.
|
||||
MaxDisk uint64 `json:"maxdisk"` // Maximum disk space allocated for the LXC container.
|
||||
Template int `json:"template"` // Template status (0 = not a template, 1 = template).
|
||||
}
|
||||
|
||||
// PveQemuStatus represents the status of a Proxmox virtual machine.
|
||||
type PveQemuStatus struct {
|
||||
BalloonMin uint64 `json:"balloon_min"` // Minimum balloon memory in bytes.
|
||||
Pid int `json:"pid"` // Process ID.
|
||||
Disk uint64 `json:"disk"` // Disk usage in bytes.
|
||||
Status string `json:"status"` // Status of the virtual machine (e.g., "running").
|
||||
DiskRead uint64 `json:"diskread"` // Disk read bytes.
|
||||
Tags string `json:"tags"` // Tags for the container.
|
||||
VMID int `json:"vmid"` // VM ID.
|
||||
DiskWrite uint64 `json:"diskwrite"` // Disk write bytes.
|
||||
CPUs int `json:"cpus"` // Number of CPUs.
|
||||
Shares int `json:"shares"` // CPU shares.
|
||||
MaxDisk uint64 `json:"maxdisk"` // Maximum disk size in bytes.
|
||||
Mem uint64 `json:"mem"` // Memory usage in bytes.
|
||||
Uptime int `json:"uptime"` // Uptime in seconds.
|
||||
NetOut uint64 `json:"netout"` // Network out bytes.
|
||||
Name string `json:"name"` // Name of the virtual machine.
|
||||
NetIn uint64 `json:"netin"` // Network in bytes.
|
||||
CPU float64 `json:"cpu"` // CPU usage.
|
||||
MaxMem uint64 `json:"maxmem"` // Maximum memory in bytes.
|
||||
Template int `json:"template"` // Template status (0 = not a template, 1 = template).
|
||||
}
|
||||
|
||||
// Ha represents the High Availability configuration for a PVE resource.
|
||||
type PveHaStatus struct {
|
||||
Managed int `json:"managed"` // Whether HA is managed for the resource.
|
||||
}
|
||||
|
||||
// PveBalloonInfo represents the balloon memory information.
|
||||
type PveBalloonInfo struct {
|
||||
LastUpdate int64 `json:"last_update"` // Last update time in Unix timestamp.
|
||||
MemSwappedIn uint64 `json:"mem_swapped_in"` // Memory swapped in, in bytes.
|
||||
TotalMem uint64 `json:"total_mem"` // Total memory in bytes.
|
||||
FreeMem uint64 `json:"free_mem"` // Free memory in bytes.
|
||||
MemSwappedOut uint64 `json:"mem_swapped_out"` // Memory swapped out, in bytes.
|
||||
MajorPageFaults uint64 `json:"major_page_faults"` // Number of major page faults.
|
||||
Actual uint64 `json:"actual"` // Actual memory in bytes.
|
||||
MaxMem uint64 `json:"max_mem"` // Maximum memory in bytes.
|
||||
MinorPageFaults uint64 `json:"minor_page_faults"` // Number of minor page faults.
|
||||
}
|
||||
|
||||
// PveNic represents network interface controller statistics.
|
||||
type PveNic struct {
|
||||
NetOut uint64 `json:"netout"` // Network out bytes.
|
||||
NetIn uint64 `json:"netin"` // Network in bytes.
|
||||
}
|
||||
|
||||
// PveBlockStat represents block device statistics.
|
||||
type PveBlockStat struct {
|
||||
ZoneAppendMerged uint64 `json:"zone_append_merged"` // Number of zone append merged operations.
|
||||
RdTotalTimeNs uint64 `json:"rd_total_time_ns"` // Total read time in nanoseconds.
|
||||
UnmapTotalTimeNs uint64 `json:"unmap_total_time_ns"` // Total unmap time in nanoseconds.
|
||||
RdMerged uint64 `json:"rd_merged"` // Number of read merged operations.
|
||||
TimedStats []interface{} `json:"timed_stats"` // Timed statistics (not detailed in sample).
|
||||
FlushOperations uint64 `json:"flush_operations"` // Number of flush operations.
|
||||
ZoneAppendOperations uint64 `json:"zone_append_operations"` // Number of zone append operations.
|
||||
RdOperations uint64 `json:"rd_operations"` // Number of read operations.
|
||||
FailedWrOperations uint64 `json:"failed_wr_operations"` // Number of failed write operations.
|
||||
FailedUnmapOperations uint64 `json:"failed_unmap_operations"` // Number of failed unmap operations.
|
||||
AccountInvalid bool `json:"account_invalid"` // Indicates if the account is invalid.
|
||||
WrTotalTimeNs uint64 `json:"wr_total_time_ns"` // Total write time in nanoseconds.
|
||||
InvalidUnmapOperations uint64 `json:"invalid_unmap_operations"` // Number of invalid unmap operations.
|
||||
WrMerged uint64 `json:"wr_merged"` // Number of write merged operations.
|
||||
AccountFailed bool `json:"account_failed"` // Indicates if the account failed.
|
||||
InvalidZoneAppendOperations uint64 `json:"invalid_zone_append_operations"` // Number of invalid zone append operations.
|
||||
WrHighestOffset uint64 `json:"wr_highest_offset"` // Highest write offset.
|
||||
WrOperations uint64 `json:"wr_operations"` // Number of write operations.
|
||||
UnmapMerged uint64 `json:"unmap_merged"` // Number of unmap merged operations.
|
||||
InvalidFlushOperations uint64 `json:"invalid_flush_operations"` // Number of invalid flush operations.
|
||||
WrBytes uint64 `json:"wr_bytes"` // Write bytes.
|
||||
UnmapBytes uint64 `json:"unmap_bytes"` // Unmap bytes.
|
||||
InvalidRdOperations uint64 `json:"invalid_rd_operations"` // Number of invalid read operations.
|
||||
ZoneAppendTotalTimeNs uint64 `json:"zone_append_total_time_ns"` // Total zone append time in nanoseconds.
|
||||
IdleTimeNs uint64 `json:"idle_time_ns"` // Idle time in nanoseconds.
|
||||
FailedZoneAppendOperations uint64 `json:"failed_zone_append_operations"` // Number of failed zone append operations.
|
||||
UnmapOperations uint64 `json:"unmap_operations"` // Number of unmap operations.
|
||||
FailedFlushOperations uint64 `json:"failed_flush_operations"` // Number of failed flush operations.
|
||||
ZoneAppendBytes uint64 `json:"zone_append_bytes"` // Zone append bytes.
|
||||
RdBytes uint64 `json:"rd_bytes"` // Read bytes.
|
||||
FailedRdOperations uint64 `json:"failed_rd_operations"` // Number of failed read operations.
|
||||
FlushTotalTimeNs uint64 `json:"flush_total_time_ns"` // Total flush time in nanoseconds.
|
||||
InvalidWrOperations uint64 `json:"invalid_wr_operations"` // Number of invalid write operations.
|
||||
}
|
||||
|
||||
// PveProxmoxSupport represents Proxmox support features and versions.
|
||||
type PveProxmoxSupport struct {
|
||||
BackupMaxWorkers bool `json:"backup-max-workers"` // Indicates if backup max workers is supported.
|
||||
PbsMasterkey bool `json:"pbs-masterkey"` // Indicates if PBS master key is supported.
|
||||
PbsDirtyBitmapSavevm bool `json:"pbs-dirty-bitmap-savevm"` // Indicates if PBS dirty bitmap save VM is supported.
|
||||
PbsDirtyBitmapMigration bool `json:"pbs-dirty-bitmap-migration"` // Indicates if PBS dirty bitmap migration is supported.
|
||||
PbsDirtyBitmap bool `json:"pbs-dirty-bitmap"` // Indicates if PBS dirty bitmap is supported.
|
||||
QueryBitmapInfo bool `json:"query-bitmap-info"` // Indicates if querying bitmap info is supported.
|
||||
PbsLibraryVersion string `json:"pbs-library-version"` // PBS library version.
|
||||
}
|
||||
|
||||
// PveQemuStatusDetail represents the detailed status of a Proxmox QEMU virtual machine.
|
||||
type PveQemuStatusDetail struct {
|
||||
VMID int `json:"vmid"` // VM ID.
|
||||
DiskWrite uint64 `json:"diskwrite"` // Disk write bytes.
|
||||
Status string `json:"status"` // Status of the VM (e.g., "running").
|
||||
RunningQemu string `json:"running-qemu"` // Running QEMU version.
|
||||
RunningMachine string `json:"running-machine"` // Running machine type.
|
||||
ProxmoxSupport PveProxmoxSupport `json:"proxmox-support"` // Proxmox support features and versions.
|
||||
Clipboard interface{} `json:"clipboard"` // Clipboard status (could be null).
|
||||
BalloonMin uint64 `json:"balloon_min"` // Minimum balloon memory in bytes.
|
||||
Agent int `json:"agent"` // Agent status.
|
||||
Name string `json:"name"` // Name of the VM.
|
||||
NetIn uint64 `json:"netin"` // Network in bytes.
|
||||
Ha PveHaStatus `json:"ha"` // High availability configuration.
|
||||
BalloonInfo PveBalloonInfo `json:"ballooninfo"` // Balloon memory information.
|
||||
MaxDisk uint64 `json:"maxdisk"` // Maximum disk size in bytes.
|
||||
Tags string `json:"tags"` // Tags for the VM.
|
||||
DiskRead uint64 `json:"diskread"` // Disk read bytes.
|
||||
Nics map[string]PveNic `json:"nics"` // Network interface controllers statistics.
|
||||
BlockStat map[string]PveBlockStat `json:"blockstat"` // Block device statistics.
|
||||
QmpStatus string `json:"qmpstatus"` // QMP status of the VM.
|
||||
Disk uint64 `json:"disk"` // Disk usage in bytes.
|
||||
Pid int `json:"pid"` // Process ID.
|
||||
Balloon uint64 `json:"balloon"` // Balloon memory in bytes.
|
||||
MaxMem uint64 `json:"maxmem"` // Maximum memory in bytes.
|
||||
CPU float64 `json:"cpu"` // CPU usage.
|
||||
NetOut uint64 `json:"netout"` // Network out bytes.
|
||||
Uptime int `json:"uptime"` // Uptime in seconds.
|
||||
FreeMem uint64 `json:"freemem"` // Free memory in bytes.
|
||||
Shares int `json:"shares"` // CPU shares.
|
||||
CPUs int `json:"cpus"` // Number of CPUs.
|
||||
Mem uint64 `json:"mem"` // Memory usage in bytes.
|
||||
Template int `json:"template"` // Template status (0 = not a template, 1 = template).
|
||||
}
|
||||
|
||||
// FindNode searches for a node by its name in the PVE resources.
|
||||
// Returns a pointer to the PveNodeResource if found, otherwise returns an error.
|
||||
func (r *PveResources) FindNode(nodeName string) (*PveNodeResource, error) {
|
||||
for i := range r.Nodes {
|
||||
if r.Nodes[i].Node == nodeName {
|
||||
return &r.Nodes[i], nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Unable to find node '" + nodeName + "' in node resources.")
|
||||
}
|
||||
|
||||
// FindNodeSDN searches for SDN resources associated with a specific node name in the PVE resources.
|
||||
// Returns a slice of PveSdnResource pointers containing all matching SDN resources.
|
||||
func (r *PveResources) FindNodeSDN(nodeName string) *[]PveSdnResource {
|
||||
var sdns []PveSdnResource
|
||||
for _, sdn := range r.SDNs {
|
||||
if sdn.Node == nodeName {
|
||||
sdns = append(sdns, sdn)
|
||||
}
|
||||
}
|
||||
return &sdns
|
||||
}
|
||||
|
||||
// GetStatusNumeric returns the numeric status of an SDN resource.
|
||||
// Returns 1 if the status is "ok", otherwise returns 0.
|
||||
func (r *PveSdnResource) GetStatusNumeric() float64 {
|
||||
if r.Status == "ok" {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetActiveNumeric returns the numeric state of a subscription.
|
||||
// Returns 1 if the subscription status is "active", otherwise returns 0.
|
||||
func (r *PveSubscription) GetActiveNumeric() float64 {
|
||||
if r.Status == "active" {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetSmartPassedState returns the numeric health state of a disk.
|
||||
// Returns 1 if the disk health status is "OK" or "PASSED", otherwise returns 0.
|
||||
func (r *PveDisk) GetSmartPassedState() float64 {
|
||||
switch r.Health {
|
||||
case "OK", "PASSED":
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// GetStatusNumeric returns a numeric representation of the LXC container's status.
|
||||
// If the container status is "running", it returns 1. Otherwise, it returns 0.
|
||||
func (r *PveLxcStatus) GetStatusNumeric() float64 {
|
||||
if r.Status == "running" {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetStatusNumeric returns a numeric representation of the virtual machine status.
|
||||
// If the virtual machine status is "running", it returns 1. Otherwise, it returns 0.
|
||||
func (r *PveQemuStatus) GetStatusNumeric() float64 {
|
||||
if r.Status == "running" {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// IsRunning checks if LXC container is running.
|
||||
// If the LXC container status is "running", it returns true. Otherwise, it returns false.
|
||||
func (r *PveLxcStatus) IsRunning() bool {
|
||||
if r.Status == "running" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRunning checks if virtual machine is running.
|
||||
// If the virtual machine status is "running", it returns true. Otherwise, it returns false.
|
||||
func (r *PveQemuStatus) IsRunning() bool {
|
||||
if r.Status == "running" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetClusterName returns name of cluster for labeling purposes.
|
||||
// If there is no cluster then return standalone node with node name. Otherwise, it returns cluster name.
|
||||
func (r *PveClusterStatus) GetClusterName() string {
|
||||
if r.Name == "" {
|
||||
return fmt.Sprintf("Standalone node - %s", r.NodeStatuses[0].Name)
|
||||
}
|
||||
return r.Name
|
||||
}
|
||||
303
proxmox/pve_api_client.go
Normal file
303
proxmox/pve_api_client.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// PVE API client.
|
||||
type PveApiClient struct {
|
||||
apiClient *ApiClient // Instance of API client.
|
||||
}
|
||||
|
||||
// Create new instance of PVE API client.
|
||||
func NewPveApiClient(endpoints []string, tokenId string, secret string) *PveApiClient {
|
||||
client := PveApiClient{}
|
||||
client.apiClient = NewApiClient(endpoints, tokenId, secret, 5*time.Second)
|
||||
client.apiClient.Start()
|
||||
return &client
|
||||
}
|
||||
|
||||
// Get PVE version.
|
||||
func (instance *PveApiClient) GetVersion() (*PveVersion, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/version", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
version := PveVersion{}
|
||||
err = json.Unmarshal(res.Data, &version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &version, nil
|
||||
}
|
||||
|
||||
// Get PVE cluster status.
|
||||
func (instance *PveApiClient) GetClusterStatus() (*PveClusterStatus, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/cluster/status", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var objects []map[string]interface{}
|
||||
err = json.Unmarshal(res.Data, &objects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If data returned is empty array then there is possible issue with permissions.
|
||||
if len(objects) == 0 {
|
||||
return nil, errors.New("Unable to fetch cluster status from API. Please make sure you have valid token and 'PVEAuditor' permission on token and user.")
|
||||
}
|
||||
|
||||
var cluster PveClusterStatus
|
||||
|
||||
for _, obj := range objects {
|
||||
switch obj["type"] {
|
||||
case "cluster":
|
||||
if err := mapstructure.Decode(obj, &cluster); err != nil {
|
||||
log.Errorf("Unable to decode cluster status object. Error:", err)
|
||||
continue
|
||||
}
|
||||
case "node":
|
||||
var node PveNodeStatus
|
||||
if err := mapstructure.Decode(obj, &node); err != nil {
|
||||
log.Errorf("Unable to decode node status object. Error:", err)
|
||||
continue
|
||||
}
|
||||
cluster.NodeStatuses = append(cluster.NodeStatuses, node)
|
||||
default:
|
||||
log.Errorf("Unable to decode cluster status object. Unknown type:", obj["type"])
|
||||
}
|
||||
}
|
||||
|
||||
return &cluster, nil
|
||||
}
|
||||
|
||||
// Get PVE cluster resources.
|
||||
func (instance *PveApiClient) GetClusterResources() (*PveResources, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/cluster/resources", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var objects []map[string]interface{}
|
||||
err = json.Unmarshal(res.Data, &objects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If data returned is empty array then there is possible issue with permissions.
|
||||
if len(objects) == 0 {
|
||||
return nil, errors.New("Unable to fetch cluster status from API. Please make sure you have valid token and 'PVEAuditor' permission on token and user.")
|
||||
}
|
||||
|
||||
resources := PveResources{}
|
||||
for _, obj := range objects {
|
||||
switch obj["type"] {
|
||||
case "lxc":
|
||||
var resource PveLxcResource
|
||||
if err := mapstructure.Decode(obj, &resource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
if err := mapstructure.Decode(obj, &resource.PveResource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
resources.CTs = append(resources.CTs, resource)
|
||||
case "qemu":
|
||||
var resource PveQemuResource
|
||||
if err := mapstructure.Decode(obj, &resource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
if err := mapstructure.Decode(obj, &resource.PveResource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
resources.VMs = append(resources.VMs, resource)
|
||||
case "node":
|
||||
var resource PveNodeResource
|
||||
if err := mapstructure.Decode(obj, &resource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
if err := mapstructure.Decode(obj, &resource.PveResource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
resources.Nodes = append(resources.Nodes, resource)
|
||||
case "storage":
|
||||
var resource PveStorageResource
|
||||
if err := mapstructure.Decode(obj, &resource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
if err := mapstructure.Decode(obj, &resource.PveResource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
resources.Storages = append(resources.Storages, resource)
|
||||
case "sdn":
|
||||
var resource PveSdnResource
|
||||
if err := mapstructure.Decode(obj, &resource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
if err := mapstructure.Decode(obj, &resource.PveResource); err != nil {
|
||||
log.Errorf("Unable to decode cluster resource object. Error:", err)
|
||||
continue
|
||||
}
|
||||
resources.SDNs = append(resources.SDNs, resource)
|
||||
default:
|
||||
log.Errorf("Unable to decode cluster resource object. Unknown type:", obj["type"])
|
||||
}
|
||||
}
|
||||
|
||||
return &resources, nil
|
||||
}
|
||||
|
||||
// Get PVE cluster node stats.
|
||||
func (instance *PveApiClient) GetNodeStatusDetail(node string) (*PveNodeStatusDetail, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/nodes/"+node+"/status", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var status PveNodeStatusDetail
|
||||
err = json.Unmarshal(res.Data, &status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// Get PVE cluster node subscription info.
|
||||
func (instance *PveApiClient) GetNodeSubscription(node string) (*PveSubscription, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/nodes/"+node+"/subscription", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var subscription PveSubscription
|
||||
err = json.Unmarshal(res.Data, &subscription)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &subscription, nil
|
||||
}
|
||||
|
||||
// Get PVE cluster node disks info.
|
||||
func (instance *PveApiClient) GetNodeDisksList(node string) (*[]PveDisk, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/nodes/"+node+"/disks/list", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var disks []PveDisk
|
||||
err = json.Unmarshal(res.Data, &disks)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &disks, nil
|
||||
}
|
||||
|
||||
// Get PVE node time info.
|
||||
func (instance *PveApiClient) GetNodeTime(node string) (*PveNodeTime, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/nodes/"+node+"/time", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var time PveNodeTime
|
||||
err = json.Unmarshal(res.Data, &time)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &time, nil
|
||||
}
|
||||
|
||||
// Get PVE node storage time info.
|
||||
func (instance *PveApiClient) GetNodeStorages(node string) (*[]PveStorage, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", "/nodes/"+node+"/storage", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var storages []PveStorage
|
||||
err = json.Unmarshal(res.Data, &storages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &storages, nil
|
||||
}
|
||||
|
||||
// Get PVE node LXC containers.
|
||||
func (instance *PveApiClient) GetNodeContainerList(node string) (*[]PveLxcStatus, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", fmt.Sprintf("/nodes/%s/lxc/", node), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var status []PveLxcStatus
|
||||
err = json.Unmarshal(res.Data, &status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// Get PVE LXC container status.
|
||||
func (instance *PveApiClient) GetContainerStatus(node string, vmid int) (*PveLxcStatus, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", fmt.Sprintf("/nodes/%s/lxc/%d/status/current", node, vmid), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var status PveLxcStatus
|
||||
err = json.Unmarshal(res.Data, &status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// Get PVE node VM list.
|
||||
func (instance *PveApiClient) GetNodeQemuList(node string) (*[]PveQemuStatus, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", fmt.Sprintf("/nodes/%s/qemu/", node), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var status []PveQemuStatus
|
||||
err = json.Unmarshal(res.Data, &status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &status, nil
|
||||
}
|
||||
|
||||
// Get PVE node VM detail.
|
||||
func (instance *PveApiClient) GetNodeQemu(node string, vmid string) (*PveQemuStatusDetail, error) {
|
||||
res, err := instance.apiClient.PerformRequest("GET", fmt.Sprintf("/nodes/%s/qemu/%s/status/current", node, vmid), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var status PveQemuStatusDetail
|
||||
err = json.Unmarshal(res.Data, &status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &status, nil
|
||||
}
|
||||
Reference in New Issue
Block a user