feat(inputs.win_wmi): Add support for remote queries (#14973)

This commit is contained in:
Sven Rebhan 2024-03-20 20:36:39 +01:00 committed by GitHub
parent 60adc92014
commit f8905b270a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 65 additions and 7 deletions

View File

@ -19,12 +19,27 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
## Secret-store support
This plugin supports secrets from secret-stores for the `username` and
`password` option.
See the [secret-store documentation][SECRETSTORE] for more details on how
to use them.
[SECRETSTORE]: ../../../docs/CONFIGURATION.md#secret-store-secrets
## Configuration
```toml @sample.conf
# Input plugin to query Windows Management Instrumentation
# This plugin ONLY supports Windows
[[inputs.win_wmi]]
## Hostname or IP for remote connections, by default the local machine is queried
# host = ""
## Credentials for the connection, by default no credentials are used
# username = ""
# password = ""
[[inputs.win_wmi.query]]
# a string representing the WMI namespace to be queried
namespace = "root\\cimv2"

View File

@ -10,7 +10,9 @@ import (
"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"
)
@ -23,11 +25,13 @@ type Query struct {
Filter string `toml:"filter"`
TagPropertiesInclude []string `toml:"tag_properties"`
tagFilter filter.Filter
query string
host string
query string
connectionParams []interface{}
tagFilter filter.Filter
}
func (q *Query) prepare() error {
func (q *Query) prepare(host string, username, password config.Secret) error {
// Compile the filter
f, err := filter.Compile(q.TagPropertiesInclude)
if err != nil {
@ -35,6 +39,30 @@ func (q *Query) prepare() error {
}
q.tagFilter = f
q.host = host
if q.host != "" {
q.connectionParams = append(q.connectionParams, q.host)
} else {
q.connectionParams = append(q.connectionParams, nil)
}
q.connectionParams = append(q.connectionParams, q.Namespace)
if !username.Empty() {
u, err := username.Get()
if err != nil {
return fmt.Errorf("getting username secret failed: %w", err)
}
q.connectionParams = append(q.connectionParams, u.String())
username.Destroy()
}
if !password.Empty() {
p, err := password.Get()
if err != nil {
return fmt.Errorf("getting password secret failed: %w", err)
}
q.connectionParams = append(q.connectionParams, p.String())
password.Destroy()
}
// Construct the overall query from the given parts
wql := fmt.Sprintf("SELECT %s FROM %s", strings.Join(q.Properties, ", "), q.ClassName)
if len(q.Filter) > 0 {
@ -78,7 +106,8 @@ func (q *Query) execute(acc telegraf.Accumulator) error {
defer wmi.Release()
// service is a SWbemServices
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", nil, q.Namespace)
serviceRaw, err := oleutil.CallMethod(wmi, "ConnectServer", q.connectionParams...)
if err != nil {
return fmt.Errorf("failed calling method ConnectServer: %w", err)
}
@ -116,6 +145,10 @@ func (q *Query) execute(acc telegraf.Accumulator) error {
func (q *Query) extractProperties(acc telegraf.Accumulator, itemRaw *ole.VARIANT) error {
tags, fields := map[string]string{}, map[string]interface{}{}
if q.host != "" {
tags["source"] = q.host
}
item := itemRaw.ToIDispatch()
defer item.Release()

View File

@ -1,6 +1,12 @@
# Input plugin to query Windows Management Instrumentation
# This plugin ONLY supports Windows
[[inputs.win_wmi]]
## Hostname or IP for remote connections, by default the local machine is queried
# host = ""
## Credentials for the connection, by default no credentials are used
# username = ""
# password = ""
[[inputs.win_wmi.query]]
# a string representing the WMI namespace to be queried
namespace = "root\\cimv2"

View File

@ -9,6 +9,7 @@ import (
"sync"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/plugins/inputs"
)
@ -17,8 +18,11 @@ var sampleConfig string
// Wmi struct
type Wmi struct {
Queries []Query `toml:"query"`
Log telegraf.Logger `toml:"-"`
Host string `toml:"host"`
Username config.Secret `toml:"username"`
Password config.Secret `toml:"password"`
Queries []Query `toml:"query"`
Log telegraf.Logger `toml:"-"`
}
// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
@ -28,7 +32,7 @@ const sFalse = 0x00000001
func (w *Wmi) Init() error {
for i := range w.Queries {
q := &w.Queries[i]
if err := q.prepare(); err != nil {
if err := q.prepare(w.Host, w.Username, w.Password); err != nil {
return fmt.Errorf("preparing query %q failed: %w", q.ClassName, err)
}
}