feat: process group tag for groundwork output plugin (#10499)
Co-authored-by: Pavlo Sumkin <pavlo@bluesunrise.com>
This commit is contained in:
parent
a888d0233b
commit
e4f040a2df
|
|
@ -26,10 +26,15 @@ This plugin writes to a [GroundWork Monitor][1] instance. Plugin only supports G
|
||||||
|
|
||||||
## The name of the tag that contains the hostname.
|
## The name of the tag that contains the hostname.
|
||||||
# resource_tag = "host"
|
# resource_tag = "host"
|
||||||
|
|
||||||
|
## The name of the tag that contains the host group name.
|
||||||
|
# group_tag = "group"
|
||||||
```
|
```
|
||||||
|
|
||||||
## List of tags used by the plugin
|
## List of tags used by the plugin
|
||||||
|
|
||||||
|
* group - to define the name of the group you want to monitor, can be changed with config.
|
||||||
|
* host - to define the name of the host you want to monitor, can be changed with config.
|
||||||
* service - to define the name of the service you want to monitor.
|
* service - to define the name of the service you want to monitor.
|
||||||
* status - to define the status of the service. Supported statuses: "SERVICE_OK", "SERVICE_WARNING", "SERVICE_UNSCHEDULED_CRITICAL", "SERVICE_PENDING", "SERVICE_SCHEDULED_CRITICAL", "SERVICE_UNKNOWN".
|
* status - to define the status of the service. Supported statuses: "SERVICE_OK", "SERVICE_WARNING", "SERVICE_UNSCHEDULED_CRITICAL", "SERVICE_PENDING", "SERVICE_SCHEDULED_CRITICAL", "SERVICE_UNKNOWN".
|
||||||
* message - to provide any message you want.
|
* message - to provide any message you want.
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,16 @@ const sampleConfig = `
|
||||||
|
|
||||||
## The name of the tag that contains the hostname.
|
## The name of the tag that contains the hostname.
|
||||||
# resource_tag = "host"
|
# resource_tag = "host"
|
||||||
|
|
||||||
|
## The name of the tag that contains the host group name.
|
||||||
|
# group_tag = "group"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
type metricMeta struct {
|
||||||
|
group string
|
||||||
|
resource string
|
||||||
|
}
|
||||||
|
|
||||||
type Groundwork struct {
|
type Groundwork struct {
|
||||||
Server string `toml:"url"`
|
Server string `toml:"url"`
|
||||||
AgentID string `toml:"agent_id"`
|
AgentID string `toml:"agent_id"`
|
||||||
|
|
@ -45,6 +53,7 @@ type Groundwork struct {
|
||||||
Password string `toml:"password"`
|
Password string `toml:"password"`
|
||||||
DefaultHost string `toml:"default_host"`
|
DefaultHost string `toml:"default_host"`
|
||||||
DefaultServiceState string `toml:"default_service_state"`
|
DefaultServiceState string `toml:"default_service_state"`
|
||||||
|
GroupTag string `toml:"group_tag"`
|
||||||
ResourceTag string `toml:"resource_tag"`
|
ResourceTag string `toml:"resource_tag"`
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
client clients.GWClient
|
client clients.GWClient
|
||||||
|
|
@ -123,14 +132,39 @@ func (g *Groundwork) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Groundwork) Write(metrics []telegraf.Metric) error {
|
func (g *Groundwork) Write(metrics []telegraf.Metric) error {
|
||||||
|
groupMap := make(map[string][]transit.ResourceRef)
|
||||||
resourceToServicesMap := make(map[string][]transit.MonitoredService)
|
resourceToServicesMap := make(map[string][]transit.MonitoredService)
|
||||||
for _, metric := range metrics {
|
for _, metric := range metrics {
|
||||||
resource, service, err := g.parseMetric(metric)
|
meta, service, err := g.parseMetric(metric)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log.Errorf("%v", err)
|
g.Log.Errorf("%v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
resource := meta.resource
|
||||||
resourceToServicesMap[resource] = append(resourceToServicesMap[resource], *service)
|
resourceToServicesMap[resource] = append(resourceToServicesMap[resource], *service)
|
||||||
|
|
||||||
|
group := meta.group
|
||||||
|
if len(group) != 0 {
|
||||||
|
resRef := transit.ResourceRef{
|
||||||
|
Name: resource,
|
||||||
|
Type: transit.ResourceTypeHost,
|
||||||
|
}
|
||||||
|
if refs, ok := groupMap[group]; ok {
|
||||||
|
refs = append(refs, resRef)
|
||||||
|
groupMap[group] = refs
|
||||||
|
} else {
|
||||||
|
groupMap[group] = []transit.ResourceRef{resRef}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := make([]transit.ResourceGroup, 0, len(groupMap))
|
||||||
|
for groupName, refs := range groupMap {
|
||||||
|
groups = append(groups, transit.ResourceGroup{
|
||||||
|
GroupName: groupName,
|
||||||
|
Resources: refs,
|
||||||
|
Type: transit.HostGroup,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var resources []transit.MonitoredResource
|
var resources []transit.MonitoredResource
|
||||||
|
|
@ -163,7 +197,7 @@ func (g *Groundwork) Write(metrics []telegraf.Metric) error {
|
||||||
Version: transit.ModelVersion,
|
Version: transit.ModelVersion,
|
||||||
},
|
},
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
Groups: nil,
|
Groups: groups,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -185,6 +219,7 @@ func (g *Groundwork) Description() string {
|
||||||
func init() {
|
func init() {
|
||||||
outputs.Add("groundwork", func() telegraf.Output {
|
outputs.Add("groundwork", func() telegraf.Output {
|
||||||
return &Groundwork{
|
return &Groundwork{
|
||||||
|
GroupTag: "group",
|
||||||
ResourceTag: "host",
|
ResourceTag: "host",
|
||||||
DefaultHost: "telegraf",
|
DefaultHost: "telegraf",
|
||||||
DefaultServiceState: string(transit.ServiceOk),
|
DefaultServiceState: string(transit.ServiceOk),
|
||||||
|
|
@ -192,7 +227,9 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Groundwork) parseMetric(metric telegraf.Metric) (string, *transit.MonitoredService, error) {
|
func (g *Groundwork) parseMetric(metric telegraf.Metric) (metricMeta, *transit.MonitoredService, error) {
|
||||||
|
group, _ := metric.GetTag(g.GroupTag)
|
||||||
|
|
||||||
resource := g.DefaultHost
|
resource := g.DefaultHost
|
||||||
if value, present := metric.GetTag(g.ResourceTag); present {
|
if value, present := metric.GetTag(g.ResourceTag); present {
|
||||||
resource = value
|
resource = value
|
||||||
|
|
@ -302,7 +339,7 @@ func (g *Groundwork) parseMetric(metric telegraf.Metric) (string, *transit.Monit
|
||||||
serviceObject.Status = serviceStatus
|
serviceObject.Status = serviceStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
return resource, &serviceObject, nil
|
return metricMeta{resource: resource, group: group}, &serviceObject, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validStatus(status string) bool {
|
func validStatus(status string) bool {
|
||||||
|
|
|
||||||
|
|
@ -20,33 +20,26 @@ const (
|
||||||
defaultHost = "telegraf"
|
defaultHost = "telegraf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWrite(t *testing.T) {
|
func TestWriteWithDefaults(t *testing.T) {
|
||||||
// Generate test metric with default name to test Write logic
|
// Generate test metric with default name to test Write logic
|
||||||
floatMetric := testutil.TestMetric(1.0, "Float")
|
intMetric := testutil.TestMetric(42, "IntMetric")
|
||||||
|
|
||||||
// Simulate Groundwork server that should receive custom metrics
|
// Simulate Groundwork server that should receive custom metrics
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Decode body to use in assertations below
|
// Decode body to use in assertions below
|
||||||
var obj groundworkObject
|
var obj groundworkObject
|
||||||
err = json.Unmarshal(body, &obj)
|
err = json.Unmarshal(body, &obj)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Check if server gets valid metrics object
|
// Check if server gets valid metrics object
|
||||||
require.Equal(t, obj.Context.AgentID, defaultTestAgentID)
|
require.Equal(t, defaultTestAgentID, obj.Context.AgentID)
|
||||||
require.Equal(t, obj.Resources[0].Name, defaultHost)
|
require.Equal(t, defaultHost, obj.Resources[0].Name)
|
||||||
require.Equal(
|
require.Equal(t, "IntMetric", obj.Resources[0].Services[0].Name)
|
||||||
t,
|
require.Equal(t, int64(42), obj.Resources[0].Services[0].Metrics[0].Value.IntegerValue)
|
||||||
obj.Resources[0].Services[0].Name,
|
require.Equal(t, 0, len(obj.Groups))
|
||||||
"Float",
|
|
||||||
)
|
|
||||||
require.Equal(
|
|
||||||
t,
|
|
||||||
obj.Resources[0].Services[0].Metrics[0].Value.DoubleValue,
|
|
||||||
1.0,
|
|
||||||
)
|
|
||||||
|
|
||||||
_, err = fmt.Fprintln(w, `OK`)
|
_, err = fmt.Fprintln(w, `OK`)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
@ -55,7 +48,56 @@ func TestWrite(t *testing.T) {
|
||||||
i := Groundwork{
|
i := Groundwork{
|
||||||
Server: server.URL,
|
Server: server.URL,
|
||||||
AgentID: defaultTestAgentID,
|
AgentID: defaultTestAgentID,
|
||||||
DefaultHost: "telegraf",
|
DefaultHost: defaultHost,
|
||||||
|
client: clients.GWClient{
|
||||||
|
AppName: "telegraf",
|
||||||
|
AppType: "TELEGRAF",
|
||||||
|
GWConnection: &clients.GWConnection{
|
||||||
|
HostName: server.URL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := i.Write([]telegraf.Metric{intMetric})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteWithTags(t *testing.T) {
|
||||||
|
// Generate test metric with tags to test Write logic
|
||||||
|
floatMetric := testutil.TestMetric(1.0, "FloatMetric")
|
||||||
|
floatMetric.AddTag("host", "Host01")
|
||||||
|
floatMetric.AddTag("group", "Group01")
|
||||||
|
|
||||||
|
// Simulate Groundwork server that should receive custom metrics
|
||||||
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Decode body to use in assertions below
|
||||||
|
var obj groundworkObject
|
||||||
|
err = json.Unmarshal(body, &obj)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Check if server gets valid metrics object
|
||||||
|
require.Equal(t, defaultTestAgentID, obj.Context.AgentID)
|
||||||
|
require.Equal(t, "Host01", obj.Resources[0].Name)
|
||||||
|
require.Equal(t, "FloatMetric", obj.Resources[0].Services[0].Name)
|
||||||
|
require.Equal(t, 1.0, obj.Resources[0].Services[0].Metrics[0].Value.DoubleValue)
|
||||||
|
require.Equal(t, "Group01", obj.Groups[0].GroupName)
|
||||||
|
require.Equal(t, "Host01", obj.Groups[0].Resources[0].Name)
|
||||||
|
|
||||||
|
_, err = fmt.Fprintln(w, `OK`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}))
|
||||||
|
|
||||||
|
i := Groundwork{
|
||||||
|
Server: server.URL,
|
||||||
|
AgentID: defaultTestAgentID,
|
||||||
|
DefaultHost: defaultHost,
|
||||||
|
GroupTag: "group",
|
||||||
|
ResourceTag: "host",
|
||||||
client: clients.GWClient{
|
client: clients.GWClient{
|
||||||
AppName: "telegraf",
|
AppName: "telegraf",
|
||||||
AppType: "TELEGRAF",
|
AppType: "TELEGRAF",
|
||||||
|
|
@ -81,10 +123,18 @@ type groundworkObject struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Metrics []struct {
|
Metrics []struct {
|
||||||
Value struct {
|
Value struct {
|
||||||
StringValue string `json:"stringValue"`
|
DoubleValue float64 `json:"doubleValue"`
|
||||||
DoubleValue float64 `json:"doubleValue"`
|
IntegerValue int64 `json:"integerValue"`
|
||||||
} `json:"value"`
|
} `json:"value"`
|
||||||
}
|
}
|
||||||
} `json:"services"`
|
} `json:"services"`
|
||||||
} `json:"resources"`
|
} `json:"resources"`
|
||||||
|
Groups []struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
GroupName string `json:"groupName"`
|
||||||
|
Resources []struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
} `json:"resources"`
|
||||||
|
} `json:"groups"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue