feat(inputs.proxmox): Allow to add VM-id and status as tag (#16066)
This commit is contained in:
parent
237dd3d582
commit
b613db37e1
|
|
@ -19,7 +19,8 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
```toml @sample.conf
|
```toml @sample.conf
|
||||||
# Provides metrics from Proxmox nodes (Proxmox Virtual Environment > 6.2).
|
# Provides metrics from Proxmox nodes (Proxmox Virtual Environment > 6.2).
|
||||||
[[inputs.proxmox]]
|
[[inputs.proxmox]]
|
||||||
## API connection configuration. The API token was introduced in Proxmox v6.2. Required permissions for user and token: PVEAuditor role on /.
|
## API connection configuration. The API token was introduced in Proxmox v6.2.
|
||||||
|
## Required permissions for user and token: PVEAuditor role on /.
|
||||||
base_url = "https://localhost:8006/api2/json"
|
base_url = "https://localhost:8006/api2/json"
|
||||||
api_token = "USER@REALM!TOKENID=UUID"
|
api_token = "USER@REALM!TOKENID=UUID"
|
||||||
|
|
||||||
|
|
@ -29,15 +30,19 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
## leaving this empty will often lead to a "search domain is not set" error.
|
## leaving this empty will often lead to a "search domain is not set" error.
|
||||||
# node_name = ""
|
# node_name = ""
|
||||||
|
|
||||||
|
## Additional tags of the VM stats data to add as a tag
|
||||||
|
## Supported values are "vmid" and "status"
|
||||||
|
# additional_vmstats_tags = []
|
||||||
|
|
||||||
## Optional TLS Config
|
## Optional TLS Config
|
||||||
# tls_ca = "/etc/telegraf/ca.pem"
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
# tls_cert = "/etc/telegraf/cert.pem"
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
# tls_key = "/etc/telegraf/key.pem"
|
# tls_key = "/etc/telegraf/key.pem"
|
||||||
## Use TLS but skip chain & host verification
|
## Use TLS but skip chain & host verification
|
||||||
insecure_skip_verify = false
|
# insecure_skip_verify = false
|
||||||
|
|
||||||
# HTTP response timeout (default: 5s)
|
## HTTP response timeout (default: 5s)
|
||||||
response_timeout = "5s"
|
# response_timeout = "5s"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Permissions
|
### Permissions
|
||||||
|
|
@ -88,9 +93,10 @@ See this [Proxmox docs example][1] for further details.
|
||||||
- vm_name - Name of the VM/container
|
- vm_name - Name of the VM/container
|
||||||
- vm_fqdn - FQDN of the VM/container
|
- vm_fqdn - FQDN of the VM/container
|
||||||
- vm_type - Type of the VM/container (lxc, qemu)
|
- vm_type - Type of the VM/container (lxc, qemu)
|
||||||
|
- vm_id - ID of the VM/container
|
||||||
|
|
||||||
## Example Output
|
## Example Output
|
||||||
|
|
||||||
```text
|
```text
|
||||||
proxmox,host=pxnode,node_fqdn=pxnode.example.com,vm_fqdn=vm1.example.com,vm_name=vm1,vm_type=lxc cpuload=0.147998116735236,disk_free=4461129728i,disk_total=5217320960i,disk_used=756191232i,disk_used_percentage=14,mem_free=1046827008i,mem_total=1073741824i,mem_used=26914816i,mem_used_percentage=2,status="running",swap_free=536698880i,swap_total=536870912i,swap_used=172032i,swap_used_percentage=0,uptime=1643793i 1595457277000000000
|
proxmox,host=pxnode,node_fqdn=pxnode.example.com,vm_fqdn=vm1.example.com,vm_id=112,vm_name=vm1,vm_type=lxc cpuload=0.147998116735236,disk_free=4461129728i,disk_total=5217320960i,disk_used=756191232i,disk_used_percentage=14,mem_free=1046827008i,mem_total=1073741824i,mem_used=26914816i,mem_used_percentage=2,status="running",swap_free=536698880i,swap_total=536870912i,swap_used=172032i,swap_used_percentage=0,uptime=1643793i 1595457277000000000
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,54 @@ import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/config"
|
||||||
|
"github.com/influxdata/telegraf/plugins/common/tls"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
|
type Proxmox struct {
|
||||||
|
BaseURL string `toml:"base_url"`
|
||||||
|
APIToken string `toml:"api_token"`
|
||||||
|
ResponseTimeout config.Duration `toml:"response_timeout"`
|
||||||
|
NodeName string `toml:"node_name"`
|
||||||
|
AdditionalVmstatsTags []string `toml:"additional_vmstats_tags"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
tls.ClientConfig
|
||||||
|
|
||||||
|
httpClient *http.Client
|
||||||
|
nodeSearchDomain string
|
||||||
|
|
||||||
|
requestFunction func(apiUrl string, method string, data url.Values) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
func (*Proxmox) SampleConfig() string {
|
func (*Proxmox) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (px *Proxmox) Init() error {
|
func (px *Proxmox) Init() error {
|
||||||
|
// Check parameters
|
||||||
|
for _, v := range px.AdditionalVmstatsTags {
|
||||||
|
switch v {
|
||||||
|
case "vmid", "status":
|
||||||
|
// Do nothing as those are valid values
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid additional vmstats tag %q", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set hostname as default node name for backwards compatibility
|
// Set hostname as default node name for backwards compatibility
|
||||||
if px.NodeName == "" {
|
if px.NodeName == "" {
|
||||||
//nolint:errcheck // best attempt setting of NodeName
|
//nolint:errcheck // best attempt setting of NodeName
|
||||||
|
|
@ -42,24 +71,25 @@ func (px *Proxmox) Init() error {
|
||||||
Timeout: time.Duration(px.ResponseTimeout),
|
Timeout: time.Duration(px.ResponseTimeout),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
px.requestFunction = px.performRequest
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (px *Proxmox) Gather(acc telegraf.Accumulator) error {
|
func (px *Proxmox) Gather(acc telegraf.Accumulator) error {
|
||||||
err := getNodeSearchDomain(px)
|
if err := px.getNodeSearchDomain(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gatherLxcData(px, acc)
|
px.gatherVMData(acc, lxc)
|
||||||
gatherQemuData(px, acc)
|
px.gatherVMData(acc, qemu)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNodeSearchDomain(px *Proxmox) error {
|
func (px *Proxmox) getNodeSearchDomain() error {
|
||||||
apiURL := "/nodes/" + px.NodeName + "/dns"
|
apiURL := "/nodes/" + px.NodeName + "/dns"
|
||||||
jsonData, err := px.requestFunction(px, apiURL, http.MethodGet, nil)
|
jsonData, err := px.requestFunction(apiURL, http.MethodGet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +108,7 @@ func getNodeSearchDomain(px *Proxmox) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func performRequest(px *Proxmox, apiURL, method string, data url.Values) ([]byte, error) {
|
func (px *Proxmox) performRequest(apiURL, method string, data url.Values) ([]byte, error) {
|
||||||
request, err := http.NewRequest(method, px.BaseURL+apiURL, strings.NewReader(data.Encode()))
|
request, err := http.NewRequest(method, px.BaseURL+apiURL, strings.NewReader(data.Encode()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -99,24 +129,15 @@ func performRequest(px *Proxmox, apiURL, method string, data url.Values) ([]byte
|
||||||
return responseBody, nil
|
return responseBody, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatherLxcData(px *Proxmox, acc telegraf.Accumulator) {
|
func (px *Proxmox) gatherVMData(acc telegraf.Accumulator, rt resourceType) {
|
||||||
gatherVMData(px, acc, lxc)
|
vmStats, err := px.getVMStats(rt)
|
||||||
}
|
|
||||||
|
|
||||||
func gatherQemuData(px *Proxmox, acc telegraf.Accumulator) {
|
|
||||||
gatherVMData(px, acc, qemu)
|
|
||||||
}
|
|
||||||
|
|
||||||
func gatherVMData(px *Proxmox, acc telegraf.Accumulator, rt resourceType) {
|
|
||||||
vmStats, err := getVMStats(px, rt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
px.Log.Errorf("Error getting VM stats: %v", err)
|
px.Log.Errorf("Error getting VM stats: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each VM add metrics to Accumulator
|
|
||||||
for _, vmStat := range vmStats.Data {
|
for _, vmStat := range vmStats.Data {
|
||||||
vmConfig, err := getVMConfig(px, vmStat.ID, rt)
|
vmConfig, err := px.getVMConfig(vmStat.ID, rt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
px.Log.Errorf("Error getting VM config: %v", err)
|
px.Log.Errorf("Error getting VM config: %v", err)
|
||||||
return
|
return
|
||||||
|
|
@ -127,76 +148,43 @@ func gatherVMData(px *Proxmox, acc telegraf.Accumulator, rt resourceType) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
tags := getTags(px, vmStat.Name, vmConfig, rt)
|
currentVMStatus, err := px.getCurrentVMStatus(rt, vmStat.ID)
|
||||||
currentVMStatus, err := getCurrentVMStatus(px, rt, vmStat.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
px.Log.Errorf("Error getting VM current VM status: %v", err)
|
px.Log.Errorf("Error getting VM current VM status: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fields := getFields(currentVMStatus)
|
hostname := vmConfig.Data.Hostname
|
||||||
acc.AddFields("proxmox", fields, tags)
|
if hostname == "" {
|
||||||
|
hostname = vmStat.Name
|
||||||
}
|
}
|
||||||
|
domain := vmConfig.Data.Searchdomain
|
||||||
|
if domain == "" {
|
||||||
|
domain = px.nodeSearchDomain
|
||||||
|
}
|
||||||
|
fqdn := hostname + "." + domain
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"node_fqdn": px.NodeName + "." + px.nodeSearchDomain,
|
||||||
|
"vm_name": vmStat.Name,
|
||||||
|
"vm_fqdn": fqdn,
|
||||||
|
"vm_type": string(rt),
|
||||||
|
}
|
||||||
|
if slices.Contains(px.AdditionalVmstatsTags, "vmid") {
|
||||||
|
tags["vm_id"] = vmStat.ID.String()
|
||||||
|
}
|
||||||
|
if slices.Contains(px.AdditionalVmstatsTags, "status") {
|
||||||
|
tags["status"] = currentVMStatus.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentVMStatus(px *Proxmox, rt resourceType, id json.Number) (vmStat, error) {
|
memMetrics := getByteMetrics(currentVMStatus.TotalMem, currentVMStatus.UsedMem)
|
||||||
apiURL := "/nodes/" + px.NodeName + "/" + string(rt) + "/" + string(id) + "/status/current"
|
swapMetrics := getByteMetrics(currentVMStatus.TotalSwap, currentVMStatus.UsedSwap)
|
||||||
|
diskMetrics := getByteMetrics(currentVMStatus.TotalDisk, currentVMStatus.UsedDisk)
|
||||||
|
|
||||||
jsonData, err := px.requestFunction(px, apiURL, http.MethodGet, nil)
|
fields := map[string]interface{}{
|
||||||
if err != nil {
|
"status": currentVMStatus.Status,
|
||||||
return vmStat{}, err
|
"uptime": jsonNumberToInt64(currentVMStatus.Uptime),
|
||||||
}
|
"cpuload": jsonNumberToFloat64(currentVMStatus.CPULoad),
|
||||||
|
|
||||||
var currentVMStatus vmCurrentStats
|
|
||||||
err = json.Unmarshal(jsonData, ¤tVMStatus)
|
|
||||||
if err != nil {
|
|
||||||
return vmStat{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentVMStatus.Data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVMStats(px *Proxmox, rt resourceType) (vmStats, error) {
|
|
||||||
apiURL := "/nodes/" + px.NodeName + "/" + string(rt)
|
|
||||||
jsonData, err := px.requestFunction(px, apiURL, http.MethodGet, nil)
|
|
||||||
if err != nil {
|
|
||||||
return vmStats{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var vmStatistics vmStats
|
|
||||||
err = json.Unmarshal(jsonData, &vmStatistics)
|
|
||||||
if err != nil {
|
|
||||||
return vmStats{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return vmStatistics, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getVMConfig(px *Proxmox, vmID json.Number, rt resourceType) (vmConfig, error) {
|
|
||||||
apiURL := "/nodes/" + px.NodeName + "/" + string(rt) + "/" + string(vmID) + "/config"
|
|
||||||
jsonData, err := px.requestFunction(px, apiURL, http.MethodGet, nil)
|
|
||||||
if err != nil {
|
|
||||||
return vmConfig{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var vmCfg vmConfig
|
|
||||||
err = json.Unmarshal(jsonData, &vmCfg)
|
|
||||||
if err != nil {
|
|
||||||
return vmConfig{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return vmCfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFields(vmStat vmStat) map[string]interface{} {
|
|
||||||
memMetrics := getByteMetrics(vmStat.TotalMem, vmStat.UsedMem)
|
|
||||||
swapMetrics := getByteMetrics(vmStat.TotalSwap, vmStat.UsedSwap)
|
|
||||||
diskMetrics := getByteMetrics(vmStat.TotalDisk, vmStat.UsedDisk)
|
|
||||||
|
|
||||||
return map[string]interface{}{
|
|
||||||
"status": vmStat.Status,
|
|
||||||
"uptime": jsonNumberToInt64(vmStat.Uptime),
|
|
||||||
"cpuload": jsonNumberToFloat64(vmStat.CPULoad),
|
|
||||||
"mem_used": memMetrics.used,
|
"mem_used": memMetrics.used,
|
||||||
"mem_total": memMetrics.total,
|
"mem_total": memMetrics.total,
|
||||||
"mem_free": memMetrics.free,
|
"mem_free": memMetrics.free,
|
||||||
|
|
@ -210,6 +198,57 @@ func getFields(vmStat vmStat) map[string]interface{} {
|
||||||
"disk_free": diskMetrics.free,
|
"disk_free": diskMetrics.free,
|
||||||
"disk_used_percentage": diskMetrics.usedPercentage,
|
"disk_used_percentage": diskMetrics.usedPercentage,
|
||||||
}
|
}
|
||||||
|
acc.AddFields("proxmox", fields, tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (px *Proxmox) getCurrentVMStatus(rt resourceType, id json.Number) (vmStat, error) {
|
||||||
|
apiURL := "/nodes/" + px.NodeName + "/" + string(rt) + "/" + string(id) + "/status/current"
|
||||||
|
|
||||||
|
jsonData, err := px.requestFunction(apiURL, http.MethodGet, nil)
|
||||||
|
if err != nil {
|
||||||
|
return vmStat{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentVMStatus vmCurrentStats
|
||||||
|
err = json.Unmarshal(jsonData, ¤tVMStatus)
|
||||||
|
if err != nil {
|
||||||
|
return vmStat{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentVMStatus.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (px *Proxmox) getVMStats(rt resourceType) (vmStats, error) {
|
||||||
|
apiURL := "/nodes/" + px.NodeName + "/" + string(rt)
|
||||||
|
jsonData, err := px.requestFunction(apiURL, http.MethodGet, nil)
|
||||||
|
if err != nil {
|
||||||
|
return vmStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vmStatistics vmStats
|
||||||
|
err = json.Unmarshal(jsonData, &vmStatistics)
|
||||||
|
if err != nil {
|
||||||
|
return vmStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmStatistics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (px *Proxmox) getVMConfig(vmID json.Number, rt resourceType) (vmConfig, error) {
|
||||||
|
apiURL := "/nodes/" + px.NodeName + "/" + string(rt) + "/" + string(vmID) + "/config"
|
||||||
|
jsonData, err := px.requestFunction(apiURL, http.MethodGet, nil)
|
||||||
|
if err != nil {
|
||||||
|
return vmConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vmCfg vmConfig
|
||||||
|
err = json.Unmarshal(jsonData, &vmCfg)
|
||||||
|
if err != nil {
|
||||||
|
return vmConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getByteMetrics(total, used json.Number) metrics {
|
func getByteMetrics(total, used json.Number) metrics {
|
||||||
|
|
@ -247,30 +286,8 @@ func jsonNumberToFloat64(value json.Number) float64 {
|
||||||
return float64Value
|
return float64Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTags(px *Proxmox, name string, vmConfig vmConfig, rt resourceType) map[string]string {
|
|
||||||
domain := vmConfig.Data.Searchdomain
|
|
||||||
if len(domain) == 0 {
|
|
||||||
domain = px.nodeSearchDomain
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname := vmConfig.Data.Hostname
|
|
||||||
if len(hostname) == 0 {
|
|
||||||
hostname = name
|
|
||||||
}
|
|
||||||
fqdn := hostname + "." + domain
|
|
||||||
|
|
||||||
return map[string]string{
|
|
||||||
"node_fqdn": px.NodeName + "." + px.nodeSearchDomain,
|
|
||||||
"vm_name": name,
|
|
||||||
"vm_fqdn": fqdn,
|
|
||||||
"vm_type": string(rt),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
inputs.Add("proxmox", func() telegraf.Input {
|
inputs.Add("proxmox", func() telegraf.Input {
|
||||||
return &Proxmox{
|
return &Proxmox{}
|
||||||
requestFunction: performRequest,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,12 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -24,7 +27,7 @@ var lxcCurrentStatusTestData = `{"data":{"vmid":"111","type":"lxc","uptime":2078
|
||||||
var qemuCurrentStatusTestData = `{"data":{"name":"qemu1","status":"running","maxdisk":10737418240,"cpu":0.029336643550795,"vmid":"113",` +
|
var qemuCurrentStatusTestData = `{"data":{"name":"qemu1","status":"running","maxdisk":10737418240,"cpu":0.029336643550795,"vmid":"113",` +
|
||||||
`"uptime":2159739,"disk":0,"maxmem":2147483648,"mem":1722451796}}`
|
`"uptime":2159739,"disk":0,"maxmem":2147483648,"mem":1722451796}}`
|
||||||
|
|
||||||
func performTestRequest(_ *Proxmox, apiURL, _ string, _ url.Values) ([]byte, error) {
|
func performTestRequest(apiURL, _ string, _ url.Values) ([]byte, error) {
|
||||||
var bytedata = []byte("")
|
var bytedata = []byte("")
|
||||||
|
|
||||||
if strings.HasSuffix(apiURL, "dns") {
|
if strings.HasSuffix(apiURL, "dns") {
|
||||||
|
|
@ -46,37 +49,40 @@ func performTestRequest(_ *Proxmox, apiURL, _ string, _ url.Values) ([]byte, err
|
||||||
return bytedata, nil
|
return bytedata, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setUp(t *testing.T) *Proxmox {
|
|
||||||
px := &Proxmox{
|
|
||||||
requestFunction: performTestRequest,
|
|
||||||
NodeName: "testnode",
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, px.Init())
|
|
||||||
|
|
||||||
// Override logger for test
|
|
||||||
px.Log = testutil.Logger{}
|
|
||||||
return px
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetNodeSearchDomain(t *testing.T) {
|
func TestGetNodeSearchDomain(t *testing.T) {
|
||||||
px := setUp(t)
|
px := &Proxmox{
|
||||||
|
NodeName: "testnode",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
}
|
||||||
|
require.NoError(t, px.Init())
|
||||||
|
px.requestFunction = performTestRequest
|
||||||
|
|
||||||
err := getNodeSearchDomain(px)
|
require.NoError(t, px.getNodeSearchDomain())
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "test.example.com", px.nodeSearchDomain)
|
require.Equal(t, "test.example.com", px.nodeSearchDomain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGatherLxcData(t *testing.T) {
|
func TestGatherLxcData(t *testing.T) {
|
||||||
px := setUp(t)
|
px := &Proxmox{
|
||||||
px.nodeSearchDomain = "test.example.com"
|
NodeName: "testnode",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
nodeSearchDomain: "test.example.com",
|
||||||
|
}
|
||||||
|
require.NoError(t, px.Init())
|
||||||
|
px.requestFunction = performTestRequest
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
var acc testutil.Accumulator
|
||||||
gatherLxcData(px, acc)
|
px.gatherVMData(&acc, lxc)
|
||||||
|
|
||||||
require.Equal(t, 15, acc.NFields())
|
expected := []telegraf.Metric{
|
||||||
testFields := map[string]interface{}{
|
metric.New(
|
||||||
|
"proxmox",
|
||||||
|
map[string]string{
|
||||||
|
"node_fqdn": "testnode.test.example.com",
|
||||||
|
"vm_name": "container1",
|
||||||
|
"vm_fqdn": "container1.test.example.com",
|
||||||
|
"vm_type": "lxc",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"uptime": int64(2078164),
|
"uptime": int64(2078164),
|
||||||
"cpuload": float64(0.00371567669193613),
|
"cpuload": float64(0.00371567669193613),
|
||||||
|
|
@ -92,25 +98,35 @@ func TestGatherLxcData(t *testing.T) {
|
||||||
"disk_total": int64(5217320960),
|
"disk_total": int64(5217320960),
|
||||||
"disk_free": int64(4473131008),
|
"disk_free": int64(4473131008),
|
||||||
"disk_used_percentage": float64(14.26383306117322),
|
"disk_used_percentage": float64(14.26383306117322),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
testTags := map[string]string{
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||||
"node_fqdn": "testnode.test.example.com",
|
|
||||||
"vm_name": "container1",
|
|
||||||
"vm_fqdn": "container1.test.example.com",
|
|
||||||
"vm_type": "lxc",
|
|
||||||
}
|
|
||||||
acc.AssertContainsTaggedFields(t, "proxmox", testFields, testTags)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGatherQemuData(t *testing.T) {
|
func TestGatherQemuData(t *testing.T) {
|
||||||
px := setUp(t)
|
px := &Proxmox{
|
||||||
px.nodeSearchDomain = "test.example.com"
|
NodeName: "testnode",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
nodeSearchDomain: "test.example.com",
|
||||||
|
}
|
||||||
|
require.NoError(t, px.Init())
|
||||||
|
px.requestFunction = performTestRequest
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
var acc testutil.Accumulator
|
||||||
gatherQemuData(px, acc)
|
px.gatherVMData(&acc, qemu)
|
||||||
|
|
||||||
require.Equal(t, 15, acc.NFields())
|
expected := []telegraf.Metric{
|
||||||
testFields := map[string]interface{}{
|
metric.New(
|
||||||
|
"proxmox",
|
||||||
|
map[string]string{
|
||||||
|
"node_fqdn": "testnode.test.example.com",
|
||||||
|
"vm_name": "qemu1",
|
||||||
|
"vm_fqdn": "qemu1.test.example.com",
|
||||||
|
"vm_type": "qemu",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"uptime": int64(2159739),
|
"uptime": int64(2159739),
|
||||||
"cpuload": float64(0.029336643550795),
|
"cpuload": float64(0.029336643550795),
|
||||||
|
|
@ -126,23 +142,115 @@ func TestGatherQemuData(t *testing.T) {
|
||||||
"disk_total": int64(10737418240),
|
"disk_total": int64(10737418240),
|
||||||
"disk_free": int64(10737418240),
|
"disk_free": int64(10737418240),
|
||||||
"disk_used_percentage": float64(0),
|
"disk_used_percentage": float64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
testTags := map[string]string{
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatherLxcDataWithID(t *testing.T) {
|
||||||
|
px := &Proxmox{
|
||||||
|
NodeName: "testnode",
|
||||||
|
AdditionalVmstatsTags: []string{"vmid"},
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
nodeSearchDomain: "test.example.com",
|
||||||
|
}
|
||||||
|
require.NoError(t, px.Init())
|
||||||
|
px.requestFunction = performTestRequest
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
px.gatherVMData(&acc, lxc)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"proxmox",
|
||||||
|
map[string]string{
|
||||||
|
"node_fqdn": "testnode.test.example.com",
|
||||||
|
"vm_name": "container1",
|
||||||
|
"vm_fqdn": "container1.test.example.com",
|
||||||
|
"vm_type": "lxc",
|
||||||
|
"vm_id": "111",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"status": "running",
|
||||||
|
"uptime": int64(2078164),
|
||||||
|
"cpuload": float64(0.00371567669193613),
|
||||||
|
"mem_used": int64(98500608),
|
||||||
|
"mem_total": int64(536870912),
|
||||||
|
"mem_free": int64(438370304),
|
||||||
|
"mem_used_percentage": float64(18.34716796875),
|
||||||
|
"swap_used": int64(9412608),
|
||||||
|
"swap_total": int64(536870912),
|
||||||
|
"swap_free": int64(527458304),
|
||||||
|
"swap_used_percentage": float64(1.75323486328125),
|
||||||
|
"disk_used": int64(744189952),
|
||||||
|
"disk_total": int64(5217320960),
|
||||||
|
"disk_free": int64(4473131008),
|
||||||
|
"disk_used_percentage": float64(14.26383306117322),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatherQemuDataWithID(t *testing.T) {
|
||||||
|
px := &Proxmox{
|
||||||
|
NodeName: "testnode",
|
||||||
|
AdditionalVmstatsTags: []string{"vmid"},
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
nodeSearchDomain: "test.example.com",
|
||||||
|
}
|
||||||
|
require.NoError(t, px.Init())
|
||||||
|
px.requestFunction = performTestRequest
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
px.gatherVMData(&acc, qemu)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"proxmox",
|
||||||
|
map[string]string{
|
||||||
"node_fqdn": "testnode.test.example.com",
|
"node_fqdn": "testnode.test.example.com",
|
||||||
"vm_name": "qemu1",
|
"vm_name": "qemu1",
|
||||||
"vm_fqdn": "qemu1.test.example.com",
|
"vm_fqdn": "qemu1.test.example.com",
|
||||||
"vm_type": "qemu",
|
"vm_type": "qemu",
|
||||||
|
"vm_id": "113",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"status": "running",
|
||||||
|
"uptime": int64(2159739),
|
||||||
|
"cpuload": float64(0.029336643550795),
|
||||||
|
"mem_used": int64(1722451796),
|
||||||
|
"mem_total": int64(2147483648),
|
||||||
|
"mem_free": int64(425031852),
|
||||||
|
"mem_used_percentage": float64(80.20791206508875),
|
||||||
|
"swap_used": int64(0),
|
||||||
|
"swap_total": int64(0),
|
||||||
|
"swap_free": int64(0),
|
||||||
|
"swap_used_percentage": float64(0),
|
||||||
|
"disk_used": int64(0),
|
||||||
|
"disk_total": int64(10737418240),
|
||||||
|
"disk_free": int64(10737418240),
|
||||||
|
"disk_used_percentage": float64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
acc.AssertContainsTaggedFields(t, "proxmox", testFields, testTags)
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGather(t *testing.T) {
|
func TestGather(t *testing.T) {
|
||||||
px := setUp(t)
|
px := &Proxmox{
|
||||||
px.nodeSearchDomain = "test.example.com"
|
NodeName: "testnode",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
}
|
||||||
|
require.NoError(t, px.Init())
|
||||||
|
px.requestFunction = performTestRequest
|
||||||
|
|
||||||
acc := &testutil.Accumulator{}
|
var acc testutil.Accumulator
|
||||||
err := px.Gather(acc)
|
require.NoError(t, px.Gather(&acc))
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Results from both tests above
|
// Results from both tests above
|
||||||
require.Equal(t, 30, acc.NFields())
|
require.Equal(t, 30, acc.NFields())
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Provides metrics from Proxmox nodes (Proxmox Virtual Environment > 6.2).
|
# Provides metrics from Proxmox nodes (Proxmox Virtual Environment > 6.2).
|
||||||
[[inputs.proxmox]]
|
[[inputs.proxmox]]
|
||||||
## API connection configuration. The API token was introduced in Proxmox v6.2. Required permissions for user and token: PVEAuditor role on /.
|
## API connection configuration. The API token was introduced in Proxmox v6.2.
|
||||||
|
## Required permissions for user and token: PVEAuditor role on /.
|
||||||
base_url = "https://localhost:8006/api2/json"
|
base_url = "https://localhost:8006/api2/json"
|
||||||
api_token = "USER@REALM!TOKENID=UUID"
|
api_token = "USER@REALM!TOKENID=UUID"
|
||||||
|
|
||||||
|
|
@ -10,12 +11,16 @@
|
||||||
## leaving this empty will often lead to a "search domain is not set" error.
|
## leaving this empty will often lead to a "search domain is not set" error.
|
||||||
# node_name = ""
|
# node_name = ""
|
||||||
|
|
||||||
|
## Additional tags of the VM stats data to add as a tag
|
||||||
|
## Supported values are "vmid" and "status"
|
||||||
|
# additional_vmstats_tags = []
|
||||||
|
|
||||||
## Optional TLS Config
|
## Optional TLS Config
|
||||||
# tls_ca = "/etc/telegraf/ca.pem"
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
# tls_cert = "/etc/telegraf/cert.pem"
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
# tls_key = "/etc/telegraf/key.pem"
|
# tls_key = "/etc/telegraf/key.pem"
|
||||||
## Use TLS but skip chain & host verification
|
## Use TLS but skip chain & host verification
|
||||||
insecure_skip_verify = false
|
# insecure_skip_verify = false
|
||||||
|
|
||||||
# HTTP response timeout (default: 5s)
|
## HTTP response timeout (default: 5s)
|
||||||
response_timeout = "5s"
|
# response_timeout = "5s"
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,6 @@ package proxmox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
|
||||||
"github.com/influxdata/telegraf/config"
|
|
||||||
"github.com/influxdata/telegraf/plugins/common/tls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -15,21 +9,6 @@ var (
|
||||||
lxc resourceType = "lxc"
|
lxc resourceType = "lxc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Proxmox struct {
|
|
||||||
BaseURL string `toml:"base_url"`
|
|
||||||
APIToken string `toml:"api_token"`
|
|
||||||
ResponseTimeout config.Duration `toml:"response_timeout"`
|
|
||||||
NodeName string `toml:"node_name"`
|
|
||||||
tls.ClientConfig
|
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
|
||||||
|
|
||||||
httpClient *http.Client
|
|
||||||
|
|
||||||
nodeSearchDomain string
|
|
||||||
requestFunction func(px *Proxmox, apiUrl string, method string, data url.Values) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type resourceType string
|
type resourceType string
|
||||||
|
|
||||||
type vmStats struct {
|
type vmStats struct {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue