## Sumary When dealing with multiple hosts, it can be hard for customers filter through and figure out which host to query metric data from. This PR aims to make this easier by adding support for host tags so that there is another layer of filtering / grouping applied for hosts. ## Detailed explanation - Adds new UI components to allow adding one or more host tag filter, and a switch to choose between `AND/OR` and `OR` operators when using more than one filter following Zabbix's UI: https://github.com/user-attachments/assets/c971f5eb-7e93-4238-bd6b-902cc657c014 https://github.com/user-attachments/assets/5f8996de-684e-4ffa-b98e-8e205c4fc1df - Modifies the existing `getHosts` function to make a call to the backend with a few additional parameters to `extend` (essentially extract) the host tags for a given selected group. No backend changes were required for this. ## Why To make it easier for customers to query metric data when dealing with multiple hosts. ## How to test - Go to explore or a dashboard and create a Zabbix query where the query type is `Metrics` - The easiest way to test is by selecting `/.*/` for Groups, checking the returned `Hosts` they should all be there - Add a host tag filter and change the keys and operators as well as switching from `AND/OR` to `OR` you should see how the values returned for `Host` changes ## Future work Adding variable support for host tags once this is completed. Fixes: https://github.com/orgs/grafana/projects/457/views/40?pane=issue&itemId=3609900134&issue=grafana%7Coss-big-tent-squad%7C126 and https://github.com/grafana/grafana-zabbix/issues/927 --------- Co-authored-by: ismail simsek <ismailsimsek09@gmail.com>
428 lines
12 KiB
Go
428 lines
12 KiB
Go
package zabbix
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGetFullVersion(t *testing.T) {
|
|
zabbixClient, err := MockZabbixClient(BasicDatasourceInfo, `{"result":"5.0.12"}`, 200)
|
|
require.NoError(t, err)
|
|
|
|
version, err := zabbixClient.GetFullVersion(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "5.0.12", version)
|
|
}
|
|
|
|
func TestGetFullVersionInvalidPayload(t *testing.T) {
|
|
zabbixClient, err := MockZabbixClient(BasicDatasourceInfo, `{"result":{"version":"5.0.12"}}`, 200)
|
|
require.NoError(t, err)
|
|
|
|
version, err := zabbixClient.GetFullVersion(context.Background())
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "", version)
|
|
}
|
|
|
|
func TestGetVersion(t *testing.T) {
|
|
zabbixClient, err := MockZabbixClient(BasicDatasourceInfo, `{"result":"5.4.3"}`, 200)
|
|
require.NoError(t, err)
|
|
|
|
version, err := zabbixClient.GetVersion(context.Background())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 54, version)
|
|
}
|
|
|
|
func TestGetVersionReturnsError(t *testing.T) {
|
|
zabbixClient, err := MockZabbixClient(BasicDatasourceInfo, `{"result":"a.b.c"}`, 200)
|
|
require.NoError(t, err)
|
|
|
|
version, err := zabbixClient.GetVersion(context.Background())
|
|
assert.Error(t, err)
|
|
assert.Equal(t, 0, version)
|
|
}
|
|
|
|
func TestGetHistory(t *testing.T) {
|
|
var historyCalls []int
|
|
client := NewZabbixClientWithHandler(t, func(payload ApiRequestPayload) string {
|
|
if payload.Method == "history.get" {
|
|
historyCalls = append(historyCalls, int(payload.Params["history"].(float64)))
|
|
return `{"result":[{"itemid":"1","clock":"1","value":"1.2","ns":"0"}]}`
|
|
}
|
|
if payload.Method == "apiinfo.version" {
|
|
return `{"result":"6.4.0"}`
|
|
}
|
|
return `{"result":null}`
|
|
})
|
|
|
|
items := []*Item{
|
|
{ID: "10", ValueType: 0},
|
|
{ID: "20", ValueType: 3},
|
|
}
|
|
tr := backend.TimeRange{
|
|
From: time.Unix(0, 0),
|
|
To: time.Unix(10, 0),
|
|
}
|
|
|
|
history, err := client.GetHistory(context.Background(), items, tr)
|
|
require.NoError(t, err)
|
|
assert.Len(t, history, 2)
|
|
assert.ElementsMatch(t, []int{0, 3}, historyCalls)
|
|
}
|
|
|
|
func TestGetTrend(t *testing.T) {
|
|
var capturedIDs []string
|
|
client := NewZabbixClientWithHandler(t, func(payload ApiRequestPayload) string {
|
|
if payload.Method == "trend.get" {
|
|
for _, raw := range payload.Params["itemids"].([]interface{}) {
|
|
capturedIDs = append(capturedIDs, raw.(string))
|
|
}
|
|
return `{"result":[{"itemid":"1","clock":"1","value_min":"0","value_avg":"1","value_max":"2"}]}`
|
|
}
|
|
|
|
if payload.Method == "apiinfo.version" {
|
|
return `{"result":"6.4.0"}`
|
|
}
|
|
return `{"result":null}`
|
|
})
|
|
|
|
items := []*Item{{ID: "100"}, {ID: "200"}}
|
|
tr := backend.TimeRange{
|
|
From: time.Unix(0, 0),
|
|
To: time.Unix(10, 0),
|
|
}
|
|
|
|
trend, err := client.GetTrend(context.Background(), items, tr)
|
|
require.NoError(t, err)
|
|
assert.Len(t, trend, 1)
|
|
assert.ElementsMatch(t, []string{"100", "200"}, capturedIDs)
|
|
}
|
|
|
|
func TestGetItems(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"}]}`,
|
|
"host.get": `{"result":[{"hostid":"10","name":"web01"}]}`,
|
|
"item.get": `{
|
|
"result":[
|
|
{"itemid":"100","name":"CPU usage"},
|
|
{"itemid":"200","name":"Memory usage"}
|
|
]
|
|
}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
items, err := client.GetItems(context.Background(), "Servers", "web01", "", "/CPU/", "num", false)
|
|
require.NoError(t, err)
|
|
if assert.Len(t, items, 1) {
|
|
assert.Equal(t, "100", items[0].ID)
|
|
}
|
|
}
|
|
|
|
func TestGetItemsBefore54(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"}]}`,
|
|
"host.get": `{"result":[{"hostid":"10","name":"web01"}]}`,
|
|
"application.get": `{
|
|
"result":[
|
|
{"applicationid":"50","name":"Databases"},
|
|
{"applicationid":"60","name":"Apps"}
|
|
]
|
|
}`,
|
|
"item.get": `{
|
|
"result":[
|
|
{"itemid":"500","name":"DB Size"},
|
|
{"itemid":"600","name":"API latency"}
|
|
]
|
|
}`,
|
|
"apiinfo.version": `{"result":"5.0.0"}`,
|
|
})
|
|
|
|
items, err := client.GetItemsBefore54(context.Background(), "Servers", "web01", "Databases", "/DB/", "num", false)
|
|
require.NoError(t, err)
|
|
if assert.Len(t, items, 1) {
|
|
assert.Equal(t, "500", items[0].ID)
|
|
}
|
|
}
|
|
|
|
func TestFilterItemsByQuery(t *testing.T) {
|
|
items := []*Item{
|
|
{Name: "CPU usage"},
|
|
{Name: "Memory usage"},
|
|
}
|
|
|
|
filtered, err := filterItemsByQuery(items, "/CPU/")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "CPU usage", filtered[0].Name)
|
|
|
|
filtered, err = filterItemsByQuery(items, "Memory usage")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "Memory usage", filtered[0].Name)
|
|
}
|
|
|
|
func TestGetApps(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"}]}`,
|
|
"host.get": `{"result":[{"hostid":"10","name":"web01"}]}`,
|
|
"application.get": `{
|
|
"result":[
|
|
{"applicationid":"50","name":"API"},
|
|
{"applicationid":"60","name":"DB"}
|
|
]
|
|
}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
apps, err := client.GetApps(context.Background(), "Servers", "web01", "/^API/")
|
|
require.NoError(t, err)
|
|
if assert.Len(t, apps, 1) {
|
|
assert.Equal(t, "50", apps[0].ID)
|
|
}
|
|
}
|
|
|
|
func TestFilterAppsByQuery(t *testing.T) {
|
|
apps := []Application{
|
|
{Name: "API"},
|
|
{Name: "DB"},
|
|
}
|
|
|
|
filtered, err := filterAppsByQuery(apps, "/^A/")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "API", filtered[0].Name)
|
|
|
|
filtered, err = filterAppsByQuery(apps, "DB")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "DB", filtered[0].Name)
|
|
}
|
|
|
|
func TestGetItemTags(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"}]}`,
|
|
"host.get": `{"result":[{"hostid":"10","name":"web01"}]}`,
|
|
"item.get": `{
|
|
"result":[
|
|
{"itemid":"1","name":"CPU","tags":[{"tag":"Env","value":"prod"},{"tag":"Application","value":"api"}]},
|
|
{"itemid":"2","name":"Mem","tags":[{"tag":"Env","value":"stage"},{"tag":"Env","value":"prod"}]}
|
|
]
|
|
}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
tags, err := client.GetItemTags(context.Background(), "Servers", "web01", "/^Env/")
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, []Tag{
|
|
{Tag: "Env", Value: "prod"},
|
|
{Tag: "Env", Value: "stage"},
|
|
}, tags)
|
|
}
|
|
|
|
func TestFilterTags(t *testing.T) {
|
|
tags := []Tag{
|
|
{Tag: "Env", Value: "prod"},
|
|
{Tag: "Application", Value: "api"},
|
|
}
|
|
|
|
filtered, err := filterTags(tags, "/^Env/")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "Env", filtered[0].Tag)
|
|
|
|
filtered, err = filterTags(tags, "Application: api")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "Application", filtered[0].Tag)
|
|
}
|
|
|
|
func TestGetHosts(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"}]}`,
|
|
"host.get": `{
|
|
"result":[
|
|
{"hostid":"10","name":"web01"},
|
|
{"hostid":"20","name":"db01"}
|
|
]
|
|
}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
hosts, err := client.GetHosts(context.Background(), "Servers", "/web/")
|
|
require.NoError(t, err)
|
|
if assert.Len(t, hosts, 1) {
|
|
assert.Equal(t, "web01", hosts[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestFilterHostsByQuery(t *testing.T) {
|
|
hosts := []Host{
|
|
{Name: "web01"},
|
|
{Name: "db01"},
|
|
}
|
|
|
|
filtered, err := filterHostsByQuery(hosts, "/^web/")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "web01", filtered[0].Name)
|
|
|
|
filtered, err = filterHostsByQuery(hosts, "db01")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "db01", filtered[0].Name)
|
|
}
|
|
|
|
func TestGetGroups(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"},{"groupid":"2","name":"Apps"}]}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
groups, err := client.GetGroups(context.Background(), "/Apps/")
|
|
require.NoError(t, err)
|
|
if assert.Len(t, groups, 1) {
|
|
assert.Equal(t, "Apps", groups[0].Name)
|
|
}
|
|
}
|
|
|
|
func TestFilterGroupsByQuery(t *testing.T) {
|
|
groups := []Group{
|
|
{Name: "Servers"},
|
|
{Name: "Apps"},
|
|
}
|
|
|
|
filtered, err := filterGroupsByQuery(groups, "/Apps/")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "Apps", filtered[0].Name)
|
|
|
|
filtered, err = filterGroupsByQuery(groups, "Servers")
|
|
require.NoError(t, err)
|
|
assert.Len(t, filtered, 1)
|
|
assert.Equal(t, "Servers", filtered[0].Name)
|
|
}
|
|
|
|
func TestGetAllItemsBuildsParams(t *testing.T) {
|
|
var lastRequest ApiRequestPayload
|
|
client := NewZabbixClientWithHandler(t, func(payload ApiRequestPayload) string {
|
|
if payload.Method == "item.get" {
|
|
lastRequest = payload
|
|
return `{"result":[{"itemid":"1","name":"CPU $1","key_":"system.cpu[user]","value_type":"0","hosts":[{"hostid":"10","name":"web"}]}]}`
|
|
}
|
|
|
|
if payload.Method == "apiinfo.version" {
|
|
return `{"result":"6.4.0"}`
|
|
}
|
|
|
|
return `{"result":null}`
|
|
})
|
|
|
|
items, err := client.GetAllItems(
|
|
context.Background(),
|
|
[]string{"10"},
|
|
nil,
|
|
"num",
|
|
false,
|
|
"Env: prod, Application: api",
|
|
)
|
|
require.NoError(t, err)
|
|
if assert.Len(t, items, 1) {
|
|
assert.Equal(t, "CPU user", items[0].Name)
|
|
}
|
|
|
|
require.Equal(t, "item.get", lastRequest.Method)
|
|
params := lastRequest.Params
|
|
assert.Equal(t, []interface{}{"10"}, params["hostids"])
|
|
assert.Equal(t, true, params["monitored"])
|
|
filter := params["filter"].(map[string]interface{})
|
|
assert.ElementsMatch(t, []interface{}{float64(0), float64(3)}, filter["value_type"].([]interface{}))
|
|
tags := params["tags"].([]interface{})
|
|
if assert.Len(t, tags, 2) {
|
|
first := tags[0].(map[string]interface{})
|
|
second := tags[1].(map[string]interface{})
|
|
assert.Equal(t, "Application", first["tag"])
|
|
assert.Equal(t, "Env", second["tag"])
|
|
}
|
|
}
|
|
|
|
func TestGetItemsByIDs(t *testing.T) {
|
|
var lastRequest ApiRequestPayload
|
|
client := NewZabbixClientWithHandler(t, func(payload ApiRequestPayload) string {
|
|
if payload.Method == "item.get" {
|
|
lastRequest = payload
|
|
return `{"result":[{"itemid":"1","name":"CPU"}]}`
|
|
}
|
|
|
|
if payload.Method == "apiinfo.version" {
|
|
return `{"result":"6.4.0"}`
|
|
}
|
|
|
|
return `{"result":null}`
|
|
})
|
|
|
|
items, err := client.GetItemsByIDs(context.Background(), []string{"1"})
|
|
require.NoError(t, err)
|
|
assert.Len(t, items, 1)
|
|
assert.Equal(t, []interface{}{"1"}, lastRequest.Params["itemids"].([]interface{}))
|
|
}
|
|
|
|
func TestGetAllApps(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"application.get": `{"result":[{"applicationid":"10","name":"API"}]}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
apps, err := client.GetAllApps(context.Background(), []string{"1"})
|
|
require.NoError(t, err)
|
|
assert.Len(t, apps, 1)
|
|
assert.Equal(t, "API", apps[0].Name)
|
|
}
|
|
|
|
func TestGetAllHosts(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"host.get": `{"result":[{"hostid":"10","name":"web01"}]}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
hosts, err := client.GetAllHosts(context.Background(), []string{"1"})
|
|
require.NoError(t, err)
|
|
assert.Len(t, hosts, 1)
|
|
assert.Equal(t, "web01", hosts[0].Name)
|
|
}
|
|
|
|
func TestGetAllGroups(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"hostgroup.get": `{"result":[{"groupid":"1","name":"Servers"}]}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
groups, err := client.GetAllGroups(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Len(t, groups, 1)
|
|
assert.Equal(t, "Servers", groups[0].Name)
|
|
}
|
|
|
|
func TestGetValueMappings(t *testing.T) {
|
|
client := NewZabbixClientWithResponses(t, map[string]string{
|
|
"valuemap.get": `{
|
|
"result":[
|
|
{
|
|
"valuemapid":"1",
|
|
"name":"Status",
|
|
"mappings":[{"value":"0","newvalue":"down"}]
|
|
}
|
|
]
|
|
}`,
|
|
"apiinfo.version": `{"result":"6.4.0"}`,
|
|
})
|
|
|
|
valueMaps, err := client.GetValueMappings(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Len(t, valueMaps, 1)
|
|
assert.Equal(t, "Status", valueMaps[0].Name)
|
|
}
|