feat: Xtremio input (#9697)
This commit is contained in:
parent
d61f2e435a
commit
6dc8e9d8b9
|
|
@ -211,6 +211,7 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/wireguard"
|
_ "github.com/influxdata/telegraf/plugins/inputs/wireguard"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/wireless"
|
_ "github.com/influxdata/telegraf/plugins/inputs/wireless"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/x509_cert"
|
_ "github.com/influxdata/telegraf/plugins/inputs/x509_cert"
|
||||||
|
_ "github.com/influxdata/telegraf/plugins/inputs/xtremio"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/zfs"
|
_ "github.com/influxdata/telegraf/plugins/inputs/zfs"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/zipkin"
|
_ "github.com/influxdata/telegraf/plugins/inputs/zipkin"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/zookeeper"
|
_ "github.com/influxdata/telegraf/plugins/inputs/zookeeper"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
# XtremIO Input Plugin
|
||||||
|
|
||||||
|
The `xtremio` plugin gathers metrics from a Dell EMC XtremIO Storage Array's V3 Rest API. Documentation can be found [here](https://dl.dell.com/content/docu96624_xtremio-storage-array-x1-and-x2-cluster-types-with-xms-6-3-0-to-6-3-3-and-xios-4-0-15-to-4-0-31-and-6-0-0-to-6-3-3-restful-api-3-x-guide.pdf?language=en_us)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[inputs.xtremio]]
|
||||||
|
## XtremIO User Interface Endpoint
|
||||||
|
url = "https://xtremio.example.com/" # required
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
username = "user1"
|
||||||
|
password = "pass123"
|
||||||
|
|
||||||
|
## Metrics to collect from the XtremIO
|
||||||
|
# collectors = ["bbus","clusters","ssds","volumes","xms"]
|
||||||
|
|
||||||
|
## Optional TLS Config
|
||||||
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
|
# tls_key = "/etc/telegraf/key.pem"
|
||||||
|
## Use SSL but skip chain & host verification
|
||||||
|
# insecure_skip_verify = false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
- bbus
|
||||||
|
- tags:
|
||||||
|
- serial_number
|
||||||
|
- guid
|
||||||
|
- power_feed
|
||||||
|
- name
|
||||||
|
- model_name
|
||||||
|
- fields:
|
||||||
|
- bbus_power
|
||||||
|
- bbus_average_daily_temp
|
||||||
|
- bbus_enabled
|
||||||
|
- bbus_ups_need_battery_replacement
|
||||||
|
- bbus_ups_low_battery_no_input
|
||||||
|
|
||||||
|
- clusters
|
||||||
|
- tags:
|
||||||
|
- hardware_platform
|
||||||
|
- license_id
|
||||||
|
- guid
|
||||||
|
- name
|
||||||
|
- sys_psnt_serial_number
|
||||||
|
- fields:
|
||||||
|
- clusters_compression_factor
|
||||||
|
- clusters_percent_memory_in_use
|
||||||
|
- clusters_read_iops
|
||||||
|
- clusters_write_iops
|
||||||
|
- clusters_number_of_volumes
|
||||||
|
- clusters_free_ssd_space_in_percent
|
||||||
|
- clusters_ssd_num
|
||||||
|
- clusters_data_reduction_ratio
|
||||||
|
|
||||||
|
- ssds
|
||||||
|
- tags:
|
||||||
|
- model_name
|
||||||
|
- firmware_version
|
||||||
|
- ssd_uid
|
||||||
|
- guid
|
||||||
|
- sys_name
|
||||||
|
- serial_number
|
||||||
|
- fields:
|
||||||
|
- ssds_ssd_size
|
||||||
|
- ssds_ssd_space_in_use
|
||||||
|
- ssds_write_iops
|
||||||
|
- ssds_read_iops
|
||||||
|
- ssds_write_bandwidth
|
||||||
|
- ssds_read_bandwidth
|
||||||
|
- ssds_num_bad_sectors
|
||||||
|
|
||||||
|
- volumes
|
||||||
|
- tags:
|
||||||
|
- guid
|
||||||
|
- sys_name
|
||||||
|
- name
|
||||||
|
- fields:
|
||||||
|
- volumes_read_iops
|
||||||
|
- volumes_write_iops
|
||||||
|
- volumes_read_latency
|
||||||
|
- volumes_write_latency
|
||||||
|
- volumes_data_reduction_ratio
|
||||||
|
- volumes_provisioned_space
|
||||||
|
- volumes_used_space
|
||||||
|
|
||||||
|
- xms
|
||||||
|
- tags:
|
||||||
|
- guid
|
||||||
|
- name
|
||||||
|
- version
|
||||||
|
- xms_ip
|
||||||
|
- fields:
|
||||||
|
- xms_write_iops
|
||||||
|
- xms_read_iops
|
||||||
|
- xms_overall_efficiency_ratio
|
||||||
|
- xms_ssd_space_in_use
|
||||||
|
- xms_ram_in_use
|
||||||
|
- xms_ram_total
|
||||||
|
- xms_cpu_usage_total
|
||||||
|
- xms_write_latency
|
||||||
|
- xms_read_latency
|
||||||
|
- xms_user_accounts_count
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
> xio,guid=abcdefghifklmnopqrstuvwxyz111111,host=HOSTNAME,model_name=Eaton\ 5P\ 1550,name=X2-BBU,power_feed=PWR-B,serial_number=SER1234567890 bbus_average_daily_temp=22i,bbus_enabled=1i,bbus_power=286i,bbus_ups_low_battery_no_input=0i,bbus_ups_need_battery_replacement=0i 1638295340000000000
|
||||||
|
> xio,guid=abcdefghifklmnopqrstuvwxyz222222,host=HOSTNAME,model_name=Eaton\ 5P\ 1550,name=X1-BBU,power_feed=PWR-A,serial_number=SER1234567891 bbus_average_daily_temp=22i,bbus_enabled=1i,bbus_power=246i,bbus_ups_low_battery_no_input=0i,bbus_ups_need_battery_replacement=0i 1638295340000000000
|
||||||
|
> xio,guid=abcdefghifklmnopqrstuvwxyz333333,hardware_platform=X1,host=HOSTNAME,license_id=LIC123456789,name=SERVER01,sys_psnt_serial_number=FNM01234567890 clusters_compression_factor=1.5160012465000001,clusters_data_reduction_ratio=2.1613617899,clusters_free_ssd_space_in_percent=34i,clusters_number_of_volumes=36i,clusters_percent_memory_in_use=29i,clusters_read_iops=331i,clusters_ssd_num=50i,clusters_write_iops=4649i 1638295341000000000
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"content": {
|
||||||
|
"is-low-battery-has-input": "false",
|
||||||
|
"serial-number": "A123B45678",
|
||||||
|
"guid": "987654321abcdef",
|
||||||
|
"brick-name": "X1",
|
||||||
|
"ups-battery-charge-in-percent": 100,
|
||||||
|
"power": 244,
|
||||||
|
"avg-daily-temp": 23,
|
||||||
|
"fw-version": "01.02.0034",
|
||||||
|
"sys-name": "ABCXIO001",
|
||||||
|
"power-feed": "PWR-A",
|
||||||
|
"ups-load-in-percent": 21,
|
||||||
|
"name": "X1-BBU",
|
||||||
|
"enabled-state": "enabled",
|
||||||
|
"is-low-battery-no-input": "false",
|
||||||
|
"ups-need-battery-replacement": "false",
|
||||||
|
"model-name": "Eaton Model Name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"bbus": [
|
||||||
|
{
|
||||||
|
"href": "https://127.0.0.1/api/json/v3/types/bbus/987654321abcdef",
|
||||||
|
"name": "X1-BBU",
|
||||||
|
"sys-name": "ABCXIO001"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "https://127.0.0.1/api/json/v3/types/bbus/",
|
||||||
|
"rel": "self"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,402 @@
|
||||||
|
package xtremio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/internal/choice"
|
||||||
|
"github.com/influxdata/telegraf/plugins/common/tls"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XtremIO struct {
|
||||||
|
Username string `toml:"username"`
|
||||||
|
Password string `toml:"password"`
|
||||||
|
URL string `toml:"url"`
|
||||||
|
Collectors []string `toml:"collectors"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
tls.ClientConfig
|
||||||
|
|
||||||
|
cookie *http.Cookie
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
const sampleConfig = `
|
||||||
|
## XtremIO User Interface Endpoint
|
||||||
|
url = "https://xtremio.example.com/" # required
|
||||||
|
|
||||||
|
## Credentials
|
||||||
|
username = "user1"
|
||||||
|
password = "pass123"
|
||||||
|
|
||||||
|
## Metrics to collect from the XtremIO
|
||||||
|
# collectors = ["bbus","clusters","ssds","volumes","xms"]
|
||||||
|
|
||||||
|
## Optional TLS Config
|
||||||
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
|
# tls_key = "/etc/telegraf/key.pem"
|
||||||
|
## Use SSL but skip chain & host verification
|
||||||
|
# insecure_skip_verify = false
|
||||||
|
`
|
||||||
|
|
||||||
|
// Description will appear directly above the plugin definition in the config file
|
||||||
|
func (xio *XtremIO) Description() string {
|
||||||
|
return `Gathers Metrics From a Dell EMC XtremIO Storage Array's V3 API`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SampleConfig will populate the sample configuration portion of the plugin's configuration
|
||||||
|
func (xio *XtremIO) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) Init() error {
|
||||||
|
if xio.Username == "" {
|
||||||
|
return errors.New("username cannot be empty")
|
||||||
|
}
|
||||||
|
if xio.Password == "" {
|
||||||
|
return errors.New("password cannot be empty")
|
||||||
|
}
|
||||||
|
if xio.URL == "" {
|
||||||
|
return errors.New("url cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
availableCollectors := []string{"bbus", "clusters", "ssds", "volumes", "xms"}
|
||||||
|
if len(xio.Collectors) == 0 {
|
||||||
|
xio.Collectors = availableCollectors
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, collector := range xio.Collectors {
|
||||||
|
if !choice.Contains(collector, availableCollectors) {
|
||||||
|
return fmt.Errorf("specified collector %q isn't supported", collector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsCfg, err := xio.ClientConfig.TLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
xio.client = &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: tlsCfg,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) Gather(acc telegraf.Accumulator) error {
|
||||||
|
if err := xio.authenticate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if xio.cookie == nil {
|
||||||
|
return errors.New("no authentication cookie set")
|
||||||
|
}
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for _, collector := range xio.Collectors {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(collector string) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
resp, err := xio.call(collector)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := CollectorResponse{}
|
||||||
|
err = json.Unmarshal([]byte(resp), &data)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr []HREF
|
||||||
|
switch collector {
|
||||||
|
case "bbus":
|
||||||
|
arr = data.BBUs
|
||||||
|
case "clusters":
|
||||||
|
arr = data.Clusters
|
||||||
|
case "ssds":
|
||||||
|
arr = data.SSDs
|
||||||
|
case "volumes":
|
||||||
|
arr = data.Volumes
|
||||||
|
case "xms":
|
||||||
|
arr = data.XMS
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range arr {
|
||||||
|
itemSplit := strings.Split(item.Href, "/")
|
||||||
|
if len(itemSplit) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
url := collector + "/" + itemSplit[len(itemSplit)-1]
|
||||||
|
|
||||||
|
// Each collector is ran in a goroutine so they can be run in parallel.
|
||||||
|
// Each collector does an initial query to build out the subqueries it
|
||||||
|
// needs to run, which are started here in nested goroutines. A future
|
||||||
|
// refactor opportunity would be for the intial collector goroutines to
|
||||||
|
// return the results while exiting the goroutine, and then a series of
|
||||||
|
// goroutines can be kicked off for the subqueries. That way there is no
|
||||||
|
// nesting of goroutines.
|
||||||
|
switch collector {
|
||||||
|
case "bbus":
|
||||||
|
wg.Add(1)
|
||||||
|
go xio.gatherBBUs(acc, url, &wg)
|
||||||
|
case "clusters":
|
||||||
|
wg.Add(1)
|
||||||
|
go xio.gatherClusters(acc, url, &wg)
|
||||||
|
case "ssds":
|
||||||
|
wg.Add(1)
|
||||||
|
go xio.gatherSSDs(acc, url, &wg)
|
||||||
|
case "volumes":
|
||||||
|
wg.Add(1)
|
||||||
|
go xio.gatherVolumes(acc, url, &wg)
|
||||||
|
case "xms":
|
||||||
|
wg.Add(1)
|
||||||
|
go xio.gatherXMS(acc, url, &wg)
|
||||||
|
default:
|
||||||
|
acc.AddError(fmt.Errorf("specified collector %q isn't supported", collector))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(collector)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// At the beginning of every collection, we re-authenticate.
|
||||||
|
// We reset this cookie so we don't accidentally use an
|
||||||
|
// expired cookie, we can just check if it's nil and know
|
||||||
|
// that we either need to re-authenticate or that the
|
||||||
|
// authentication failed to set the cookie.
|
||||||
|
xio.cookie = nil
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) gatherBBUs(acc telegraf.Accumulator, url string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
resp, err := xio.call(url)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := BBU{}
|
||||||
|
err = json.Unmarshal([]byte(resp), &data)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"serial_number": data.Content.Serial,
|
||||||
|
"guid": data.Content.GUID,
|
||||||
|
"power_feed": data.Content.PowerFeed,
|
||||||
|
"name": data.Content.Name,
|
||||||
|
"model_name": data.Content.ModelName,
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"bbus_power": data.Content.BBUPower,
|
||||||
|
"bbus_average_daily_temp": data.Content.BBUDailyTemp,
|
||||||
|
"bbus_enabled": (data.Content.BBUEnabled == "enabled"),
|
||||||
|
"bbus_ups_need_battery_replacement": data.Content.BBUNeedBat,
|
||||||
|
"bbus_ups_low_battery_no_input": data.Content.BBULowBat,
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AddFields("xio", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) gatherClusters(acc telegraf.Accumulator, url string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
resp, err := xio.call(url)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := Clusters{}
|
||||||
|
err = json.Unmarshal([]byte(resp), &data)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"hardware_platform": data.Content.HardwarePlatform,
|
||||||
|
"license_id": data.Content.LicenseID,
|
||||||
|
"guid": data.Content.GUID,
|
||||||
|
"name": data.Content.Name,
|
||||||
|
"sys_psnt_serial_number": data.Content.SerialNumber,
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"clusters_compression_factor": data.Content.CompressionFactor,
|
||||||
|
"clusters_percent_memory_in_use": data.Content.MemoryUsed,
|
||||||
|
"clusters_read_iops": data.Content.ReadIops,
|
||||||
|
"clusters_write_iops": data.Content.WriteIops,
|
||||||
|
"clusters_number_of_volumes": data.Content.NumVolumes,
|
||||||
|
"clusters_free_ssd_space_in_percent": data.Content.FreeSSDSpace,
|
||||||
|
"clusters_ssd_num": data.Content.NumSSDs,
|
||||||
|
"clusters_data_reduction_ratio": data.Content.DataReductionRatio,
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AddFields("xio", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) gatherSSDs(acc telegraf.Accumulator, url string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
resp, err := xio.call(url)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := SSD{}
|
||||||
|
err = json.Unmarshal([]byte(resp), &data)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"model_name": data.Content.ModelName,
|
||||||
|
"firmware_version": data.Content.FirmwareVersion,
|
||||||
|
"ssd_uid": data.Content.SSDuid,
|
||||||
|
"guid": data.Content.GUID,
|
||||||
|
"sys_name": data.Content.SysName,
|
||||||
|
"serial_number": data.Content.SerialNumber,
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"ssds_ssd_size": data.Content.Size,
|
||||||
|
"ssds_ssd_space_in_use": data.Content.SpaceUsed,
|
||||||
|
"ssds_write_iops": data.Content.WriteIops,
|
||||||
|
"ssds_read_iops": data.Content.ReadIops,
|
||||||
|
"ssds_write_bandwidth": data.Content.WriteBandwidth,
|
||||||
|
"ssds_read_bandwidth": data.Content.ReadBandwidth,
|
||||||
|
"ssds_num_bad_sectors": data.Content.NumBadSectors,
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AddFields("xio", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) gatherVolumes(acc telegraf.Accumulator, url string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
resp, err := xio.call(url)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := Volumes{}
|
||||||
|
err = json.Unmarshal([]byte(resp), &data)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"guid": data.Content.GUID,
|
||||||
|
"sys_name": data.Content.SysName,
|
||||||
|
"name": data.Content.Name,
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"volumes_read_iops": data.Content.ReadIops,
|
||||||
|
"volumes_write_iops": data.Content.WriteIops,
|
||||||
|
"volumes_read_latency": data.Content.ReadLatency,
|
||||||
|
"volumes_write_latency": data.Content.WriteLatency,
|
||||||
|
"volumes_data_reduction_ratio": data.Content.DataReductionRatio,
|
||||||
|
"volumes_provisioned_space": data.Content.ProvisionedSpace,
|
||||||
|
"volumes_used_space": data.Content.UsedSpace,
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AddFields("xio", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) gatherXMS(acc telegraf.Accumulator, url string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
resp, err := xio.call(url)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := XMS{}
|
||||||
|
err = json.Unmarshal([]byte(resp), &data)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags := map[string]string{
|
||||||
|
"guid": data.Content.GUID,
|
||||||
|
"name": data.Content.Name,
|
||||||
|
"version": data.Content.Version,
|
||||||
|
"xms_ip": data.Content.IP,
|
||||||
|
}
|
||||||
|
fields := map[string]interface{}{
|
||||||
|
"xms_write_iops": data.Content.WriteIops,
|
||||||
|
"xms_read_iops": data.Content.ReadIops,
|
||||||
|
"xms_overall_efficiency_ratio": data.Content.EfficiencyRatio,
|
||||||
|
"xms_ssd_space_in_use": data.Content.SpaceUsed,
|
||||||
|
"xms_ram_in_use": data.Content.RAMUsage,
|
||||||
|
"xms_ram_total": data.Content.RAMTotal,
|
||||||
|
"xms_cpu_usage_total": data.Content.CPUUsage,
|
||||||
|
"xms_write_latency": data.Content.WriteLatency,
|
||||||
|
"xms_read_latency": data.Content.ReadLatency,
|
||||||
|
"xms_user_accounts_count": data.Content.NumAccounts,
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AddFields("xio", fields, tags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) call(endpoint string) (string, error) {
|
||||||
|
req, err := http.NewRequest("GET", xio.URL+"/api/json/v3/types/"+endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
req.AddCookie(xio.cookie)
|
||||||
|
resp, err := xio.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xio *XtremIO) authenticate() error {
|
||||||
|
req, err := http.NewRequest("GET", xio.URL+"/api/json/v3/commands/login", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.SetBasicAuth(xio.Username, xio.Password)
|
||||||
|
resp, err := xio.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
for _, cookie := range resp.Cookies() {
|
||||||
|
if cookie.Name == "sessid" {
|
||||||
|
xio.cookie = cookie
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inputs.Add("xtremio", func() telegraf.Input {
|
||||||
|
return &XtremIO{}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
package xtremio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testdataDir = getTestdataDir()
|
||||||
|
|
||||||
|
func TestInitDefault(t *testing.T) {
|
||||||
|
// This test should succeed with the default initialization.
|
||||||
|
plugin := &XtremIO{
|
||||||
|
Username: "testuser",
|
||||||
|
Password: "testpass",
|
||||||
|
URL: "http://example.com",
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the initialization succeeds
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Also test that default values are set correctly
|
||||||
|
require.Equal(t, "testuser", plugin.Username)
|
||||||
|
require.Equal(t, "testpass", plugin.Password)
|
||||||
|
require.Equal(t, "http://example.com", plugin.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitFail(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
plugin *XtremIO
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all empty",
|
||||||
|
plugin: &XtremIO{},
|
||||||
|
expected: "username cannot be empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no username",
|
||||||
|
plugin: &XtremIO{Password: "testpass", URL: "http://example.com"},
|
||||||
|
expected: "username cannot be empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no password",
|
||||||
|
plugin: &XtremIO{Username: "testuser", URL: "http://example.com"},
|
||||||
|
expected: "password cannot be empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no url",
|
||||||
|
plugin: &XtremIO{Username: "testuser", Password: "testpass"},
|
||||||
|
expected: "url cannot be empty",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.plugin.Log = testutil.Logger{}
|
||||||
|
err := tt.plugin.Init()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.EqualError(t, err, tt.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFixedValue(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(
|
||||||
|
http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path == "/api/json/v3/commands/login" {
|
||||||
|
cookie := &http.Cookie{Name: "sessid", Value: "cookie:123456789"}
|
||||||
|
http.SetCookie(w, cookie)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err := fmt.Fprintln(w, "authentication succeeded")
|
||||||
|
require.NoError(t, err)
|
||||||
|
} else if r.URL.Path == "/api/json/v3/types/bbus" {
|
||||||
|
sampleGetBBUsResponse, err := ioutil.ReadFile(filepath.Join(testdataDir, "sample_get_bbu_response.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err = fmt.Fprintln(w, string(sampleGetBBUsResponse))
|
||||||
|
require.NoError(t, err)
|
||||||
|
} else if r.URL.Path == "/api/json/v3/types/bbus/987654321abcdef" {
|
||||||
|
sampleBBUResponseOne, err := ioutil.ReadFile(filepath.Join(testdataDir, "sample_bbu_response.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err = fmt.Fprintln(w, string(sampleBBUResponseOne))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
plugin *XtremIO
|
||||||
|
expected []telegraf.Metric
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "gather bbus only",
|
||||||
|
plugin: &XtremIO{
|
||||||
|
Username: "testuser",
|
||||||
|
Password: "testpass",
|
||||||
|
URL: ts.URL,
|
||||||
|
Collectors: []string{"bbus"},
|
||||||
|
},
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"xio",
|
||||||
|
map[string]string{
|
||||||
|
"serial_number": "A123B45678",
|
||||||
|
"guid": "987654321abcdef",
|
||||||
|
"power_feed": "PWR-A",
|
||||||
|
"name": "X1-BBU",
|
||||||
|
"model_name": "Eaton Model Name",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"bbus_power": 244,
|
||||||
|
"bbus_average_daily_temp": 23,
|
||||||
|
"bbus_enabled": true,
|
||||||
|
"bbus_ups_need_battery_replacement": false,
|
||||||
|
"bbus_ups_low_battery_no_input": false,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
tt.plugin.Log = testutil.Logger{}
|
||||||
|
require.NoError(t, tt.plugin.Init())
|
||||||
|
require.NoError(t, tt.plugin.Gather(&acc))
|
||||||
|
require.Len(t, acc.Errors, 0, "found errors accumulated by acc.AddError()")
|
||||||
|
acc.Wait(len(tt.expected))
|
||||||
|
testutil.RequireMetricsEqual(t, tt.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthenticationFailed(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(
|
||||||
|
http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
_, err := fmt.Fprintln(w, "bad request")
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer ts.Close()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
plugin *XtremIO
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "authentication failed",
|
||||||
|
plugin: &XtremIO{
|
||||||
|
Username: "usertest",
|
||||||
|
Password: "userpass",
|
||||||
|
URL: ts.URL,
|
||||||
|
},
|
||||||
|
expected: "no authentication cookie set",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
|
||||||
|
tt.plugin.Log = testutil.Logger{}
|
||||||
|
require.NoError(t, tt.plugin.Init())
|
||||||
|
|
||||||
|
err := tt.plugin.Gather(&acc)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.EqualError(t, err, tt.expected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTestdataDir() string {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
// if we cannot even establish the test directory, further progress is meaningless
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(dir, "testdata")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
package xtremio
|
||||||
|
|
||||||
|
type BBU struct {
|
||||||
|
Content struct {
|
||||||
|
Serial string `json:"serial-number"`
|
||||||
|
GUID string `json:"guid"`
|
||||||
|
PowerFeed string `json:"power-feed"`
|
||||||
|
Name string `json:"Name"`
|
||||||
|
ModelName string `json:"model-name"`
|
||||||
|
BBUPower int `json:"power"`
|
||||||
|
BBUDailyTemp int `json:"avg-daily-temp"`
|
||||||
|
BBUEnabled string `json:"enabled-state"`
|
||||||
|
BBUNeedBat bool `json:"ups-need-battery-replacement,string"`
|
||||||
|
BBULowBat bool `json:"is-low-battery-no-input,string"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Clusters struct {
|
||||||
|
Content struct {
|
||||||
|
HardwarePlatform string `json:"hardware-platform"`
|
||||||
|
LicenseID string `json:"license-id"`
|
||||||
|
GUID string `json:"guid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SerialNumber string `json:"sys-psnt-serial-number"`
|
||||||
|
CompressionFactor float64 `json:"compression-factor"`
|
||||||
|
MemoryUsed int `json:"total-memory-in-use-in-percent"`
|
||||||
|
ReadIops int `json:"rd-iops,string"`
|
||||||
|
WriteIops int `json:"wr-iops,string"`
|
||||||
|
NumVolumes int `json:"num-of-vols"`
|
||||||
|
FreeSSDSpace int `json:"free-ud-ssd-space-in-percent"`
|
||||||
|
NumSSDs int `json:"num-of-ssds"`
|
||||||
|
DataReductionRatio float64 `json:"data-reduction-ratio"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SSD struct {
|
||||||
|
Content struct {
|
||||||
|
ModelName string `json:"model-name"`
|
||||||
|
FirmwareVersion string `json:"fw-version"`
|
||||||
|
SSDuid string `json:"ssd-uid"`
|
||||||
|
GUID string `json:"guid"`
|
||||||
|
SysName string `json:"sys-name"`
|
||||||
|
SerialNumber string `json:"serial-number"`
|
||||||
|
Size int `json:"ssd-size,string"`
|
||||||
|
SpaceUsed int `json:"ssd-space-in-use,string"`
|
||||||
|
WriteIops int `json:"wr-iops,string"`
|
||||||
|
ReadIops int `json:"rd-iops,string"`
|
||||||
|
WriteBandwidth int `json:"wr-bw,string"`
|
||||||
|
ReadBandwidth int `json:"rd-bw,string"`
|
||||||
|
NumBadSectors int `json:"num-bad-sectors"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Volumes struct {
|
||||||
|
Content struct {
|
||||||
|
GUID string `json:"guid"`
|
||||||
|
SysName string `json:"sys-name"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ReadIops int `json:"rd-iops,string"`
|
||||||
|
WriteIops int `json:"wr-iops,string"`
|
||||||
|
ReadLatency int `json:"rd-latency,string"`
|
||||||
|
WriteLatency int `json:"wr-latency,string"`
|
||||||
|
DataReductionRatio float64 `json:"data-reduction-ratio,string"`
|
||||||
|
ProvisionedSpace int `json:"vol-size,string"`
|
||||||
|
UsedSpace int `json:"logical-space-in-use,string"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type XMS struct {
|
||||||
|
Content struct {
|
||||||
|
GUID string `json:"guid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
IP string `json:"xms-ip"`
|
||||||
|
WriteIops int `json:"wr-iops,string"`
|
||||||
|
ReadIops int `json:"rd-iops,string"`
|
||||||
|
EfficiencyRatio float64 `json:"overall-efficiency-ratio,string"`
|
||||||
|
SpaceUsed int `json:"ssd-space-in-use,string"`
|
||||||
|
RAMUsage int `json:"ram-usage,string"`
|
||||||
|
RAMTotal int `json:"ram-total,string"`
|
||||||
|
CPUUsage float64 `json:"cpu"`
|
||||||
|
WriteLatency int `json:"wr-latency,string"`
|
||||||
|
ReadLatency int `json:"rd-latency,string"`
|
||||||
|
NumAccounts int `json:"num-of-user-accounts"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type HREF struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CollectorResponse struct {
|
||||||
|
BBUs []HREF `json:"bbus"`
|
||||||
|
Clusters []HREF `json:"clusters"`
|
||||||
|
SSDs []HREF `json:"ssds"`
|
||||||
|
Volumes []HREF `json:"volumes"`
|
||||||
|
XMS []HREF `json:"xmss"`
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue