feat(inputs.win_wmi): Allow to invoke methods (#15300)
This commit is contained in:
parent
bf0c8e879f
commit
e7703ae9c0
|
|
@ -40,43 +40,114 @@ to use them.
|
||||||
# username = ""
|
# username = ""
|
||||||
# password = ""
|
# password = ""
|
||||||
|
|
||||||
|
## WMI query to execute, multiple methods are possible
|
||||||
[[inputs.win_wmi.query]]
|
[[inputs.win_wmi.query]]
|
||||||
# a string representing the WMI namespace to be queried
|
## Namespace, class and a list of properties to use in the WMI query
|
||||||
namespace = "root\\cimv2"
|
namespace = "root\\cimv2"
|
||||||
# a string representing the WMI class to be queried
|
|
||||||
class_name = "Win32_Volume"
|
class_name = "Win32_Volume"
|
||||||
# an array of strings representing the properties of the WMI class to be queried
|
|
||||||
properties = ["Name", "Capacity", "FreeSpace"]
|
properties = ["Name", "Capacity", "FreeSpace"]
|
||||||
# a string specifying a WHERE clause to use as a filter for the WQL
|
## Optional WHERE clause for the WQL query
|
||||||
filter = 'NOT Name LIKE "\\\\?\\%"'
|
# filter = 'NOT Name LIKE "\\\\?\\%"'
|
||||||
# WMI class properties which should be considered tags instead of fields
|
## Returned properties to use as tags instead of fields
|
||||||
tag_properties = ["Name"]
|
# tag_properties = ["Name"]
|
||||||
|
|
||||||
|
# ## WMI method to invoke, multiple methods are possible
|
||||||
|
# [[inputs.win_wmi.method]]
|
||||||
|
# ## WMI namespace, class and method to use
|
||||||
|
# namespace = 'root\default'
|
||||||
|
# class_name = "StdRegProv"
|
||||||
|
# method = "GetStringValue"
|
||||||
|
# ## Returned WMI method values to use as tags instead of fields
|
||||||
|
# # tag_properties = ["ReturnValue"]
|
||||||
|
# ## Named arguments for the method call
|
||||||
|
# [inputs.win_wmi.method.arguments]
|
||||||
|
# hDefKey = '2147483650'
|
||||||
|
# sSubKeyName = 'Software\Microsoft\windows NT\CurrentVersion'
|
||||||
|
# sValueName = 'ProductName'
|
||||||
|
# ## Mapping of the name of the returned property to a field-name
|
||||||
|
# [inputs.win_wmi.method.fields]
|
||||||
|
# sValue = "product_name"
|
||||||
```
|
```
|
||||||
|
|
||||||
### namespace
|
### Remote execution
|
||||||
|
|
||||||
A string representing the WMI namespace to be queried. For example,
|
This plugin allows to execute queries and methods on a remote host. To do so,
|
||||||
`root\\cimv2`.
|
you need to provide the `host` as a hostname or IP-address as well as the
|
||||||
|
credentials to execute the query or method as.
|
||||||
|
|
||||||
### class_name
|
Please note, the remote machine must be configured to allow remote execution and
|
||||||
|
the user needs to have sufficient permission to execute the query or method!
|
||||||
|
Check the [Microsoft guide][remotedoc] for how to do this and test the
|
||||||
|
connection with the `Get-WmiObject` method first.
|
||||||
|
|
||||||
A string representing the WMI class to be queried. For example,
|
[remotedoc]: https://learn.microsoft.com/en-us/windows/win32/wmisdk/connecting-to-wmi-on-a-remote-computer#configuring-a-computer-for-a-remote-connection
|
||||||
`Win32_Processor`.
|
|
||||||
|
|
||||||
### properties
|
### Query settings
|
||||||
|
|
||||||
An array of strings representing the properties of the WMI class to be queried.
|
To issue a query you need to provide the `namespace` (e.g. `root\cimv2`) and the
|
||||||
|
`class_name` (e.g. `Win32_Processor`) for the WMI query. Furthermore, you need
|
||||||
|
to define which `properties` to output. An asterix (`*`) will output all values
|
||||||
|
provided by the query.
|
||||||
|
|
||||||
### filter
|
The `filter` setting specifies a WHERE clause passed to the query in the
|
||||||
|
WMI Query Language (WQL). See [WHERE Clause][WHERE] for more information.
|
||||||
|
|
||||||
A string specifying a WHERE clause to use as a filter for the WMI Query
|
The `tag_properties` allows to provide a list of returned properties that should
|
||||||
Language (WQL). See [WHERE Clause][WHERE] for more information.
|
be provided as tags instead of fields in the metric.
|
||||||
|
|
||||||
[WHERE]: https://learn.microsoft.com/en-us/windows/win32/wmisdk/where-clause?source=recommendations
|
[WHERE]: https://learn.microsoft.com/en-us/windows/win32/wmisdk/where-clause?source=recommendations
|
||||||
|
|
||||||
### tag_properties
|
As an example
|
||||||
|
|
||||||
Properties which should be considered tags instead of fields.
|
```toml
|
||||||
|
[[inputs.win_wmi]]
|
||||||
|
[[inputs.win_wmi.query]]
|
||||||
|
namespace = "root\\cimv2"
|
||||||
|
class_name = "Win32_Processor"
|
||||||
|
properties = ["Name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
corresponds to executing
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Get-WmiObject -Namespace "root\cimv2" -Class "Win32_Processor" -Property "Name"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method settings
|
||||||
|
|
||||||
|
To invoke a method you need to provide the `namespace` (e.g. `root\default`),
|
||||||
|
the `class_name` (e.g. `StdRegProv`) and the `method` name
|
||||||
|
(e.g. `GetStringValue`)for the method to invoke. Furthermore, you may need to
|
||||||
|
provide `arguments` as key-value pair(s) to the method. The number and type of
|
||||||
|
arguments depends on the method specified above.
|
||||||
|
|
||||||
|
Check the [WMI reference][wmireferenc] for available methods and their
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
The `tag_properties` allows to provide a list of returned properties that should
|
||||||
|
be provided as tags instead of fields in the metric.
|
||||||
|
|
||||||
|
[wmireferenc]: https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-reference
|
||||||
|
|
||||||
|
As an example
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[inputs.win_wmi]]
|
||||||
|
[[inputs.win_wmi.method]]
|
||||||
|
namespace = 'root\default'
|
||||||
|
class_name = "StdRegProv"
|
||||||
|
method = "GetStringValue"
|
||||||
|
[inputs.win_wmi.method.arguments]
|
||||||
|
hDefKey = '2147483650'
|
||||||
|
sSubKeyName = 'Software\Microsoft\windows NT\CurrentVersion'
|
||||||
|
sValueName = 'ProductName'
|
||||||
|
```
|
||||||
|
|
||||||
|
corresponds to executing
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Invoke-WmiMethod -Namespace "root\default" -Class "StdRegProv" -Name "GetStringValue" @(2147483650,"Software\Microsoft\windows NT\CurrentVersion", "ProductName")
|
||||||
|
```
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,228 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package win_wmi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/go-ole/go-ole"
|
||||||
|
"github.com/go-ole/go-ole/oleutil"
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/config"
|
||||||
|
"github.com/influxdata/telegraf/filter"
|
||||||
|
"github.com/influxdata/telegraf/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Method struct
|
||||||
|
type Method struct {
|
||||||
|
Namespace string `toml:"namespace"`
|
||||||
|
ClassName string `toml:"class_name"`
|
||||||
|
Method string `toml:"method"`
|
||||||
|
Arguments map[string]interface{} `toml:"arguments"`
|
||||||
|
FieldMapping map[string]string `toml:"fields"`
|
||||||
|
Filter string `toml:"filter"`
|
||||||
|
TagPropertiesInclude []string `toml:"tag_properties"`
|
||||||
|
|
||||||
|
host string
|
||||||
|
connectionParams []interface{}
|
||||||
|
tagFilter filter.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Method) prepare(host string, username, password config.Secret) error {
|
||||||
|
// Compile the filter
|
||||||
|
f, err := filter.Compile(m.TagPropertiesInclude)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("compiling tag-filter failed: %w", err)
|
||||||
|
}
|
||||||
|
m.tagFilter = f
|
||||||
|
|
||||||
|
// Setup the connection parameters
|
||||||
|
m.host = host
|
||||||
|
if m.host != "" {
|
||||||
|
m.connectionParams = append(m.connectionParams, m.host)
|
||||||
|
} else {
|
||||||
|
m.connectionParams = append(m.connectionParams, nil)
|
||||||
|
}
|
||||||
|
m.connectionParams = append(m.connectionParams, m.Namespace)
|
||||||
|
if !username.Empty() {
|
||||||
|
u, err := username.Get()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getting username secret failed: %w", err)
|
||||||
|
}
|
||||||
|
m.connectionParams = append(m.connectionParams, u.String())
|
||||||
|
username.Destroy()
|
||||||
|
}
|
||||||
|
if !password.Empty() {
|
||||||
|
p, err := password.Get()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getting password secret failed: %w", err)
|
||||||
|
}
|
||||||
|
m.connectionParams = append(m.connectionParams, p.String())
|
||||||
|
password.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Method) execute(acc telegraf.Accumulator) error {
|
||||||
|
// The only way to run WMI queries in parallel while being thread-safe is to
|
||||||
|
// ensure the CoInitialize[Ex]() call is bound to its current OS thread.
|
||||||
|
// Otherwise, attempting to initialize and run parallel queries across
|
||||||
|
// goroutines will result in protected memory errors.
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
// Init the COM client
|
||||||
|
if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil {
|
||||||
|
var oleCode *ole.OleError
|
||||||
|
if errors.As(err, &oleCode) && oleCode.Code() != ole.S_OK && oleCode.Code() != sFalse {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer ole.CoUninitialize()
|
||||||
|
|
||||||
|
// Initialize the WMI service
|
||||||
|
locator, err := oleutil.CreateObject("WbemScripting.SWbemLocator")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if locator == nil {
|
||||||
|
return errors.New("failed to create WbemScripting.SWbemLocator, maybe WMI is broken")
|
||||||
|
}
|
||||||
|
defer locator.Release()
|
||||||
|
wmi, err := locator.QueryInterface(ole.IID_IDispatch)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to query interface: %w", err)
|
||||||
|
}
|
||||||
|
defer wmi.Release()
|
||||||
|
|
||||||
|
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", m.connectionParams...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed calling method ConnectServer: %w", err)
|
||||||
|
}
|
||||||
|
service := serviceRaw.ToIDispatch()
|
||||||
|
defer serviceRaw.Clear()
|
||||||
|
|
||||||
|
// Get the specified class-method
|
||||||
|
classRaw, err := oleutil.CallMethod(service, "Get", m.ClassName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get class %s: %w", m.ClassName, err)
|
||||||
|
}
|
||||||
|
class := classRaw.ToIDispatch()
|
||||||
|
defer classRaw.Clear()
|
||||||
|
|
||||||
|
classMethodsRaw, err := class.GetProperty("Methods_")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to call method %s: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
classMethods := classMethodsRaw.ToIDispatch()
|
||||||
|
defer classMethodsRaw.Clear()
|
||||||
|
|
||||||
|
methodRaw, err := classMethods.CallMethod("Item", m.Method)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to call method %s: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
method := methodRaw.ToIDispatch()
|
||||||
|
defer methodRaw.Clear()
|
||||||
|
|
||||||
|
// Fill the input parameters of the method
|
||||||
|
inputParamsRaw, err := oleutil.GetProperty(method, "InParameters")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get input parameters for %s: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
inputParams := inputParamsRaw.ToIDispatch()
|
||||||
|
defer inputParamsRaw.Clear()
|
||||||
|
for k, v := range m.Arguments {
|
||||||
|
if _, err := inputParams.PutProperty(k, v); err != nil {
|
||||||
|
return fmt.Errorf("setting param %q for method %q failed: %w", k, m.Method, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the output parameters of the method
|
||||||
|
outputParamsRaw, err := oleutil.GetProperty(method, "OutParameters")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get output parameters for %s: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
outputParams := outputParamsRaw.ToIDispatch()
|
||||||
|
defer outputParamsRaw.Clear()
|
||||||
|
|
||||||
|
// Execute the method
|
||||||
|
outputRaw, err := service.CallMethod("ExecMethod", "StdRegProv", m.Method, inputParamsRaw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to execute method %s: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
output := outputRaw.ToIDispatch()
|
||||||
|
defer outputRaw.Clear()
|
||||||
|
|
||||||
|
outputPropertiesRaw, err := oleutil.GetProperty(outputParams, "Properties_")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get output properties for method %s: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
outputProperties := outputPropertiesRaw.ToIDispatch()
|
||||||
|
defer outputPropertiesRaw.Clear()
|
||||||
|
|
||||||
|
// Convert the results to fields and tags
|
||||||
|
tags, fields := map[string]string{}, map[string]interface{}{}
|
||||||
|
|
||||||
|
// Add a source tag if we use remote queries
|
||||||
|
if m.host != "" {
|
||||||
|
tags["source"] = m.host
|
||||||
|
}
|
||||||
|
|
||||||
|
err = oleutil.ForEach(outputProperties, func(p *ole.VARIANT) error {
|
||||||
|
// Name of the returned result item
|
||||||
|
nameProperty, err := p.ToIDispatch().GetProperty("Name")
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("cannot get output property name")
|
||||||
|
}
|
||||||
|
name := nameProperty.ToString()
|
||||||
|
defer nameProperty.Clear()
|
||||||
|
|
||||||
|
// Value of the returned result item
|
||||||
|
property, err := output.GetProperty(name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get value for output property %s: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the fieldname if provided
|
||||||
|
if n, found := m.FieldMapping[name]; found {
|
||||||
|
name = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// We might get either scalar values or an array of values...
|
||||||
|
if value := property.Value(); value != nil {
|
||||||
|
if m.tagFilter != nil && m.tagFilter.Match(name) {
|
||||||
|
if s, err := internal.ToString(value); err == nil && s != "" {
|
||||||
|
tags[name] = s
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fields[name] = value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if array := property.ToArray(); array != nil {
|
||||||
|
if m.tagFilter != nil && m.tagFilter.Match(name) {
|
||||||
|
for i, v := range array.ToValueArray() {
|
||||||
|
if s, err := internal.ToString(v); err == nil && s != "" {
|
||||||
|
tags[fmt.Sprintf("%s_%d", name, i)] = s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i, v := range array.ToValueArray() {
|
||||||
|
fields[fmt.Sprintf("%s_%d", name, i)] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot handle property %q with value %v", name, property)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot iterate the output properties: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AddFields(m.ClassName, fields, tags)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -39,6 +39,7 @@ func (q *Query) prepare(host string, username, password config.Secret) error {
|
||||||
}
|
}
|
||||||
q.tagFilter = f
|
q.tagFilter = f
|
||||||
|
|
||||||
|
// Setup the connection parameters
|
||||||
q.host = host
|
q.host = host
|
||||||
if q.host != "" {
|
if q.host != "" {
|
||||||
q.connectionParams = append(q.connectionParams, q.host)
|
q.connectionParams = append(q.connectionParams, q.host)
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,30 @@
|
||||||
# username = ""
|
# username = ""
|
||||||
# password = ""
|
# password = ""
|
||||||
|
|
||||||
|
## WMI query to execute, multiple methods are possible
|
||||||
[[inputs.win_wmi.query]]
|
[[inputs.win_wmi.query]]
|
||||||
# a string representing the WMI namespace to be queried
|
## Namespace, class and a list of properties to use in the WMI query
|
||||||
namespace = "root\\cimv2"
|
namespace = "root\\cimv2"
|
||||||
# a string representing the WMI class to be queried
|
|
||||||
class_name = "Win32_Volume"
|
class_name = "Win32_Volume"
|
||||||
# an array of strings representing the properties of the WMI class to be queried
|
|
||||||
properties = ["Name", "Capacity", "FreeSpace"]
|
properties = ["Name", "Capacity", "FreeSpace"]
|
||||||
# a string specifying a WHERE clause to use as a filter for the WQL
|
## Optional WHERE clause for the WQL query
|
||||||
filter = 'NOT Name LIKE "\\\\?\\%"'
|
# filter = 'NOT Name LIKE "\\\\?\\%"'
|
||||||
# WMI class properties which should be considered tags instead of fields
|
## Returned properties to use as tags instead of fields
|
||||||
tag_properties = ["Name"]
|
# tag_properties = ["Name"]
|
||||||
|
|
||||||
|
# ## WMI method to invoke, multiple methods are possible
|
||||||
|
# [[inputs.win_wmi.method]]
|
||||||
|
# ## WMI namespace, class and method to use
|
||||||
|
# namespace = 'root\default'
|
||||||
|
# class_name = "StdRegProv"
|
||||||
|
# method = "GetStringValue"
|
||||||
|
# ## Returned WMI method values to use as tags instead of fields
|
||||||
|
# # tag_properties = ["ReturnValue"]
|
||||||
|
# ## Named arguments for the method call
|
||||||
|
# [inputs.win_wmi.method.arguments]
|
||||||
|
# hDefKey = '2147483650'
|
||||||
|
# sSubKeyName = 'Software\Microsoft\windows NT\CurrentVersion'
|
||||||
|
# sValueName = 'ProductName'
|
||||||
|
# ## Mapping of the name of the returned property to a field-name
|
||||||
|
# [inputs.win_wmi.method.fields]
|
||||||
|
# sValue = "product_name"
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ type Wmi struct {
|
||||||
Username config.Secret `toml:"username"`
|
Username config.Secret `toml:"username"`
|
||||||
Password config.Secret `toml:"password"`
|
Password config.Secret `toml:"password"`
|
||||||
Queries []Query `toml:"query"`
|
Queries []Query `toml:"query"`
|
||||||
|
Methods []Method `toml:"method"`
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,6 +38,13 @@ func (w *Wmi) Init() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := range w.Methods {
|
||||||
|
m := &w.Methods[i]
|
||||||
|
if err := m.prepare(w.Host, w.Username, w.Password); err != nil {
|
||||||
|
return fmt.Errorf("preparing method %q failed: %w", m.Method, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +63,15 @@ func (w *Wmi) Gather(acc telegraf.Accumulator) error {
|
||||||
acc.AddError(q.execute(acc))
|
acc.AddError(q.execute(acc))
|
||||||
}(query)
|
}(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, method := range w.Methods {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(m Method) {
|
||||||
|
defer wg.Done()
|
||||||
|
acc.AddError(m.execute(acc))
|
||||||
|
}(method)
|
||||||
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
//go:build windows
|
//go:build windows
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package win_wmi
|
package win_wmi
|
||||||
|
|
||||||
|
|
@ -8,7 +7,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -45,7 +47,7 @@ func TestInit(t *testing.T) {
|
||||||
require.NoError(t, plugin.Init())
|
require.NoError(t, plugin.Init())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestQuery(t *testing.T) {
|
func TestQueryIntegration(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping integration test in short mode")
|
t.Skip("Skipping integration test in short mode")
|
||||||
}
|
}
|
||||||
|
|
@ -75,3 +77,42 @@ func TestQuery(t *testing.T) {
|
||||||
// FreeSpace property was collected as a field
|
// FreeSpace property was collected as a field
|
||||||
require.NotEmpty(t, acc.Metrics[0].Fields["FreeSpace"])
|
require.NotEmpty(t, acc.Metrics[0].Fields["FreeSpace"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMethodIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin := &Wmi{
|
||||||
|
Methods: []Method{
|
||||||
|
{
|
||||||
|
Namespace: "ROOT\\default",
|
||||||
|
ClassName: "StdRegProv",
|
||||||
|
Method: "GetStringValue",
|
||||||
|
Arguments: map[string]interface{}{
|
||||||
|
"hDefKey": `2147483650`,
|
||||||
|
"sSubKeyName": `software\microsoft\windows nt\currentversion`,
|
||||||
|
"sValueName": `ProductName`,
|
||||||
|
},
|
||||||
|
TagPropertiesInclude: []string{"ReturnValue"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"StdRegProv",
|
||||||
|
map[string]string{"ReturnValue": "0"},
|
||||||
|
map[string]interface{}{"sValue": "Windows ..."},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
require.Empty(t, acc.Errors)
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsStructureEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue