Adds support for host tags (#2140)
## 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>
This commit is contained in:
committed by
GitHub
parent
3d0895c008
commit
0d64736e86
5
.changeset/funny-fans-fry.md
Normal file
5
.changeset/funny-fans-fry.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'grafana-zabbix': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add support for host tags when querying metrics
|
||||||
@@ -233,7 +233,7 @@ func filterAppsByQuery(items []Application, filter string) ([]Application, error
|
|||||||
return filteredItems, nil
|
return filteredItems, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *Zabbix) GetItemTags(ctx context.Context, groupFilter string, hostFilter string, tagFilter string) ([]ItemTag, error) {
|
func (ds *Zabbix) GetItemTags(ctx context.Context, groupFilter string, hostFilter string, tagFilter string) ([]Tag, error) {
|
||||||
hosts, err := ds.GetHosts(ctx, groupFilter, hostFilter)
|
hosts, err := ds.GetHosts(ctx, groupFilter, hostFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -252,8 +252,8 @@ func (ds *Zabbix) GetItemTags(ctx context.Context, groupFilter string, hostFilte
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var allTags []ItemTag
|
var allTags []Tag
|
||||||
tagsMap := make(map[string]ItemTag)
|
tagsMap := make(map[string]Tag)
|
||||||
for _, item := range allItems {
|
for _, item := range allItems {
|
||||||
for _, itemTag := range item.Tags {
|
for _, itemTag := range item.Tags {
|
||||||
tagStr := itemTagToString(itemTag)
|
tagStr := itemTagToString(itemTag)
|
||||||
@@ -267,13 +267,13 @@ func (ds *Zabbix) GetItemTags(ctx context.Context, groupFilter string, hostFilte
|
|||||||
return filterTags(allTags, tagFilter)
|
return filterTags(allTags, tagFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterTags(items []ItemTag, filter string) ([]ItemTag, error) {
|
func filterTags(items []Tag, filter string) ([]Tag, error) {
|
||||||
re, err := parseFilter(filter)
|
re, err := parseFilter(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredItems := make([]ItemTag, 0)
|
filteredItems := make([]Tag, 0)
|
||||||
for _, i := range items {
|
for _, i := range items {
|
||||||
tagStr := itemTagToString(i)
|
tagStr := itemTagToString(i)
|
||||||
if re != nil {
|
if re != nil {
|
||||||
|
|||||||
@@ -217,14 +217,14 @@ func TestGetItemTags(t *testing.T) {
|
|||||||
|
|
||||||
tags, err := client.GetItemTags(context.Background(), "Servers", "web01", "/^Env/")
|
tags, err := client.GetItemTags(context.Background(), "Servers", "web01", "/^Env/")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.ElementsMatch(t, []ItemTag{
|
assert.ElementsMatch(t, []Tag{
|
||||||
{Tag: "Env", Value: "prod"},
|
{Tag: "Env", Value: "prod"},
|
||||||
{Tag: "Env", Value: "stage"},
|
{Tag: "Env", Value: "stage"},
|
||||||
}, tags)
|
}, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFilterTags(t *testing.T) {
|
func TestFilterTags(t *testing.T) {
|
||||||
tags := []ItemTag{
|
tags := []Tag{
|
||||||
{Tag: "Env", Value: "prod"},
|
{Tag: "Env", Value: "prod"},
|
||||||
{Tag: "Application", Value: "api"},
|
{Tag: "Application", Value: "api"},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ type Item struct {
|
|||||||
Delay string `json:"delay,omitempty"`
|
Delay string `json:"delay,omitempty"`
|
||||||
Units string `json:"units,omitempty"`
|
Units string `json:"units,omitempty"`
|
||||||
ValueMapID string `json:"valuemapid,omitempty"`
|
ValueMapID string `json:"valuemapid,omitempty"`
|
||||||
Tags []ItemTag `json:"tags,omitempty"`
|
Tags []Tag `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemHost struct {
|
type ItemHost struct {
|
||||||
@@ -59,7 +59,7 @@ type ItemHost struct {
|
|||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemTag struct {
|
type Tag struct {
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
Value string `json:"value,omitempty"`
|
Value string `json:"value,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -93,6 +93,7 @@ type Host struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
ID string `json:"hostid"`
|
ID string `json:"hostid"`
|
||||||
|
Tags []Tag `json:"tags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Application struct {
|
type Application struct {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func isRegex(filter string) bool {
|
|||||||
return regex.MatchString(filter)
|
return regex.MatchString(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func itemTagToString(tag ItemTag) string {
|
func itemTagToString(tag Tag) string {
|
||||||
if tag.Value != "" {
|
if tag.Value != "" {
|
||||||
return fmt.Sprintf("%s: %s", tag.Tag, tag.Value)
|
return fmt.Sprintf("%s: %s", tag.Tag, tag.Value)
|
||||||
} else {
|
} else {
|
||||||
@@ -140,8 +140,8 @@ func itemTagToString(tag ItemTag) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseItemTag(tagStr string) ItemTag {
|
func parseItemTag(tagStr string) Tag {
|
||||||
tag := ItemTag{}
|
tag := Tag{}
|
||||||
firstIdx := strings.Index(tagStr, ":")
|
firstIdx := strings.Index(tagStr, ":")
|
||||||
if firstIdx > 0 {
|
if firstIdx > 0 {
|
||||||
tag.Tag = strings.TrimSpace(tagStr[:firstIdx])
|
tag.Tag = strings.TrimSpace(tagStr[:firstIdx])
|
||||||
|
|||||||
151
src/datasource/components/QueryEditor/HostTagQueryEditor.tsx
Normal file
151
src/datasource/components/QueryEditor/HostTagQueryEditor.tsx
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
import { Tooltip, Button, Combobox, ComboboxOption, Stack, Input, RadioButtonGroup } from '@grafana/ui';
|
||||||
|
import React, { FormEvent, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { HostTagOperatorLabel, HostTagOperatorValue } from './types';
|
||||||
|
import { HostTagFilter, ZabbixTagEvalType } from 'datasource/types/query';
|
||||||
|
import { getHostTagOptionLabel } from './utils';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
hostTagOptions: ComboboxOption[];
|
||||||
|
hostTagOptionsLoading: boolean;
|
||||||
|
version: string;
|
||||||
|
evalTypeValue?: ZabbixTagEvalType;
|
||||||
|
onHostTagFilterChange?: (hostTags: HostTagFilter[]) => void;
|
||||||
|
onHostTagEvalTypeChange?: (evalType: ZabbixTagEvalType) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HostTagQueryEditor = ({
|
||||||
|
hostTagOptions,
|
||||||
|
hostTagOptionsLoading,
|
||||||
|
version,
|
||||||
|
evalTypeValue,
|
||||||
|
onHostTagFilterChange,
|
||||||
|
onHostTagEvalTypeChange,
|
||||||
|
}: Props) => {
|
||||||
|
const [hostTagFilters, setHostTagFilters] = useState<HostTagFilter[]>([]);
|
||||||
|
const [hostTagValueDrafts, setHostTagValueDrafts] = useState<string[]>([]);
|
||||||
|
const operatorOptions: ComboboxOption[] = [
|
||||||
|
{ value: HostTagOperatorValue.Exists, label: HostTagOperatorLabel.Exists },
|
||||||
|
{ value: HostTagOperatorValue.Equals, label: HostTagOperatorLabel.Equals },
|
||||||
|
{ value: HostTagOperatorValue.Contains, label: HostTagOperatorLabel.Contains },
|
||||||
|
{
|
||||||
|
value: HostTagOperatorValue.DoesNotExist,
|
||||||
|
label: getHostTagOptionLabel(HostTagOperatorValue.DoesNotExist, version),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: HostTagOperatorValue.DoesNotEqual,
|
||||||
|
label: getHostTagOptionLabel(HostTagOperatorValue.DoesNotEqual, version),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: HostTagOperatorValue.DoesNotContain,
|
||||||
|
label: getHostTagOptionLabel(HostTagOperatorValue.DoesNotContain, version),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const onAddHostTagFilter = useCallback(() => {
|
||||||
|
setHostTagFilters((prevFilters) => [
|
||||||
|
...prevFilters,
|
||||||
|
{ tag: '', value: '', operator: HostTagOperatorValue.Contains },
|
||||||
|
]);
|
||||||
|
setHostTagValueDrafts((prevDrafts) => [...prevDrafts, '']);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onRemoveHostTagFilter = useCallback((index: number) => {
|
||||||
|
setHostTagFilters((prevFilters) => prevFilters.filter((_, i) => i !== index));
|
||||||
|
setHostTagValueDrafts((prevDrafts) => prevDrafts.filter((_, i) => i !== index));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setHostTagFilterName = useCallback((index: number, name: string) => {
|
||||||
|
setHostTagFilters((prevFilters) =>
|
||||||
|
prevFilters.map((filter, i) => (i === index ? { ...filter, tag: name } : filter))
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setHostTagFilterValue = useCallback((index: number, value: string) => {
|
||||||
|
if (value !== undefined) {
|
||||||
|
setHostTagFilters((prevFilters) =>
|
||||||
|
prevFilters.map((filter, i) => (i === index ? { ...filter, value: value } : filter))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setHostTagFilterOperator = useCallback((index: number, operator: HostTagOperatorValue) => {
|
||||||
|
setHostTagFilters((prevFilters) =>
|
||||||
|
prevFilters.map((filter, i) => (i === index ? { ...filter, operator } : filter))
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onHostTagFilterChange(hostTagFilters);
|
||||||
|
}, [hostTagFilters]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Stack direction="row">
|
||||||
|
<Tooltip content="Add host tag filter">
|
||||||
|
<Button icon="plus" variant="secondary" aria-label="Add new host tag filter" onClick={onAddHostTagFilter} />
|
||||||
|
</Tooltip>
|
||||||
|
{hostTagFilters.length > 0 && (
|
||||||
|
<RadioButtonGroup
|
||||||
|
options={[
|
||||||
|
{ label: 'AND/OR', value: '0' }, // Default
|
||||||
|
{ label: 'OR', value: '2' },
|
||||||
|
]}
|
||||||
|
onChange={onHostTagEvalTypeChange}
|
||||||
|
value={evalTypeValue ?? '0'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
<Stack direction="column">
|
||||||
|
{hostTagFilters.map((filter, index) => {
|
||||||
|
return (
|
||||||
|
<Stack key={`host-tag-filter-${index}`} direction="row">
|
||||||
|
<Combobox
|
||||||
|
value={filter.tag}
|
||||||
|
onChange={(option: ComboboxOption) => setHostTagFilterName(index, option.value)}
|
||||||
|
options={hostTagOptions ?? []}
|
||||||
|
width={19}
|
||||||
|
loading={hostTagOptionsLoading}
|
||||||
|
/>
|
||||||
|
<Combobox
|
||||||
|
value={filter.operator}
|
||||||
|
onChange={(option: ComboboxOption<HostTagOperatorValue>) =>
|
||||||
|
setHostTagFilterOperator(index, option.value)
|
||||||
|
}
|
||||||
|
options={operatorOptions}
|
||||||
|
width={19}
|
||||||
|
/>
|
||||||
|
{filter.operator !== HostTagOperatorValue.Exists &&
|
||||||
|
filter.operator !== HostTagOperatorValue.DoesNotExist && (
|
||||||
|
<Input
|
||||||
|
value={hostTagValueDrafts[index] ?? filter.value}
|
||||||
|
onChange={(evt: FormEvent<HTMLInputElement>) => {
|
||||||
|
const value = evt?.currentTarget?.value ?? '';
|
||||||
|
setHostTagValueDrafts((prevDrafts) => {
|
||||||
|
const nextDrafts = [...prevDrafts];
|
||||||
|
nextDrafts[index] = value;
|
||||||
|
return nextDrafts;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onBlur={(evt: FormEvent<HTMLInputElement>) =>
|
||||||
|
setHostTagFilterValue(index, evt?.currentTarget?.value)
|
||||||
|
}
|
||||||
|
width={19}
|
||||||
|
placeholder="Host tag value"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Tooltip content="Remove host tag filter">
|
||||||
|
<Button
|
||||||
|
key={`remove-host-tag-${index}`}
|
||||||
|
icon="minus"
|
||||||
|
variant="secondary"
|
||||||
|
aria-label="Remove host tag filter"
|
||||||
|
onClick={() => onRemoveHostTagFilter(index)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Stack>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
import _ from 'lodash';
|
import { flatten, uniqBy } from 'lodash';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import { useAsyncFn } from 'react-use';
|
import { useAsyncFn } from 'react-use';
|
||||||
|
|
||||||
import { InlineField, ComboboxOption } from '@grafana/ui';
|
import { InlineField, ComboboxOption } from '@grafana/ui';
|
||||||
import { QueryEditorRow } from './QueryEditorRow';
|
import { QueryEditorRow } from './QueryEditorRow';
|
||||||
import { MetricPicker } from '../../../components';
|
import { MetricPicker } from '../../../components';
|
||||||
import { getVariableOptions } from './utils';
|
import { getVariableOptions, processHostTags } from './utils';
|
||||||
import { ZabbixDatasource } from '../../datasource';
|
import { ZabbixDatasource } from '../../datasource';
|
||||||
import { ZabbixMetricsQuery } from '../../types/query';
|
import { HostTagFilter, ZabbixMetricsQuery, ZabbixTagEvalType } from '../../types/query';
|
||||||
import { ZBXItem, ZBXItemTag } from '../../types';
|
import { ZBXItem, ZBXItemTag } from '../../types';
|
||||||
import { itemTagToString } from '../../utils';
|
import { itemTagToString } from '../../utils';
|
||||||
|
import { HostTagQueryEditor } from './HostTagQueryEditor';
|
||||||
import { useInterpolatedQuery } from '../../hooks/useInterpolatedQuery';
|
import { useInterpolatedQuery } from '../../hooks/useInterpolatedQuery';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -28,6 +29,9 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
value: group.name,
|
value: group.name,
|
||||||
label: group.name,
|
label: group.name,
|
||||||
}));
|
}));
|
||||||
|
if (options.length > 0) {
|
||||||
|
options.unshift({ value: '/.*/' });
|
||||||
|
}
|
||||||
options.unshift(...getVariableOptions());
|
options.unshift(...getVariableOptions());
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
@@ -37,22 +41,44 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
return options;
|
return options;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadHostOptions = async (group: string) => {
|
const loadHostTagOptions = async (group: string) => {
|
||||||
const hosts = await datasource.zabbix.getAllHosts(group);
|
const hostsWithTags = await datasource.zabbix.getAllHosts(group, true);
|
||||||
|
const hostTags = processHostTags(hostsWithTags ?? []);
|
||||||
|
let options: Array<ComboboxOption<string>> = hostTags?.map((tag) => ({
|
||||||
|
value: tag.tag,
|
||||||
|
label: tag.tag,
|
||||||
|
}));
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadHostOptions = async (group: string, hostTags?: HostTagFilter[], evalType?: ZabbixTagEvalType) => {
|
||||||
|
const hosts = await datasource.zabbix.getAllHosts(group, false, hostTags, evalType);
|
||||||
let options: Array<ComboboxOption<string>> = hosts?.map((host) => ({
|
let options: Array<ComboboxOption<string>> = hosts?.map((host) => ({
|
||||||
value: host.name,
|
value: host.name,
|
||||||
label: host.name,
|
label: host.name,
|
||||||
}));
|
}));
|
||||||
options = _.uniqBy(options, (o) => o.value);
|
options = uniqBy(options, (o) => o.value);
|
||||||
|
if (options.length > 0) {
|
||||||
options.unshift({ value: '/.*/' });
|
options.unshift({ value: '/.*/' });
|
||||||
|
}
|
||||||
options.unshift(...getVariableOptions());
|
options.unshift(...getVariableOptions());
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [{ loading: hostsLoading, value: hostOptions }, fetchHosts] = useAsyncFn(async () => {
|
const [{ loading: hostTagsLoading, value: hostTagsOptions }, fetchHostTags] = useAsyncFn(async () => {
|
||||||
const options = await loadHostOptions(interpolatedQuery.group.filter);
|
const options = await loadHostTagOptions(query.group.filter);
|
||||||
return options;
|
return options;
|
||||||
}, [interpolatedQuery.group.filter]);
|
}, [query.group.filter]);
|
||||||
|
|
||||||
|
const [{ loading: hostsLoading, value: hostOptions }, fetchHosts] = useAsyncFn(async () => {
|
||||||
|
const options = await loadHostOptions(
|
||||||
|
interpolatedQuery.group.filter,
|
||||||
|
interpolatedQuery.hostTags,
|
||||||
|
interpolatedQuery.evaltype
|
||||||
|
);
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}, [interpolatedQuery.group.filter, interpolatedQuery.hostTags, interpolatedQuery.evaltype]);
|
||||||
|
|
||||||
const loadAppOptions = async (group: string, host: string) => {
|
const loadAppOptions = async (group: string, host: string) => {
|
||||||
const apps = await datasource.zabbix.getAllApps(group, host);
|
const apps = await datasource.zabbix.getAllApps(group, host);
|
||||||
@@ -60,7 +86,7 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
value: app.name,
|
value: app.name,
|
||||||
label: app.name,
|
label: app.name,
|
||||||
}));
|
}));
|
||||||
options = _.uniqBy(options, (o) => o.value);
|
options = uniqBy(options, (o) => o.value);
|
||||||
options.unshift(...getVariableOptions());
|
options.unshift(...getVariableOptions());
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
@@ -77,15 +103,15 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
const items = await datasource.zabbix.getAllItems(group, host, null, null, {});
|
const items = await datasource.zabbix.getAllItems(group, host, null, null, {});
|
||||||
const tags: ZBXItemTag[] = _.flatten(items.map((item: ZBXItem) => item.tags || []));
|
const tags: ZBXItemTag[] = flatten(items.map((item: ZBXItem) => item.tags || []));
|
||||||
// const tags: ZBXItemTag[] = await datasource.zabbix.getItemTags(groupFilter, hostFilter, null);
|
// const tags: ZBXItemTag[] = await datasource.zabbix.getItemTags(groupFilter, hostFilter, null);
|
||||||
|
|
||||||
const tagList = _.uniqBy(tags, (t) => t.tag + t.value || '').map((t) => itemTagToString(t));
|
const tagList = uniqBy(tags, (t) => t.tag + t.value || '').map((t) => itemTagToString(t));
|
||||||
let options: Array<ComboboxOption<string>> = tagList?.map((tag) => ({
|
let options: Array<ComboboxOption<string>> = tagList?.map((tag) => ({
|
||||||
value: tag,
|
value: tag,
|
||||||
label: tag,
|
label: tag,
|
||||||
}));
|
}));
|
||||||
options = _.uniqBy(options, (o) => o.value);
|
options = uniqBy(options, (o) => o.value);
|
||||||
options.unshift(...getVariableOptions());
|
options.unshift(...getVariableOptions());
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
@@ -123,7 +149,7 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
value: item.name,
|
value: item.name,
|
||||||
label: item.name,
|
label: item.name,
|
||||||
}));
|
}));
|
||||||
itemOptions = _.uniqBy(itemOptions, (o) => o.value);
|
itemOptions = uniqBy(itemOptions, (o) => o.value);
|
||||||
itemOptions.unshift(...getVariableOptions());
|
itemOptions.unshift(...getVariableOptions());
|
||||||
return itemOptions;
|
return itemOptions;
|
||||||
};
|
};
|
||||||
@@ -147,6 +173,8 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
|
|
||||||
// Update suggestions on every metric change
|
// Update suggestions on every metric change
|
||||||
const groupFilter = interpolatedQuery.group?.filter;
|
const groupFilter = interpolatedQuery.group?.filter;
|
||||||
|
const hostTagFilters = interpolatedQuery.hostTags;
|
||||||
|
const evalType = interpolatedQuery.evaltype;
|
||||||
const hostFilter = interpolatedQuery.host?.filter;
|
const hostFilter = interpolatedQuery.host?.filter;
|
||||||
const appFilter = interpolatedQuery.application?.filter;
|
const appFilter = interpolatedQuery.application?.filter;
|
||||||
const tagFilter = interpolatedQuery.itemTag?.filter;
|
const tagFilter = interpolatedQuery.itemTag?.filter;
|
||||||
@@ -157,9 +185,13 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchHosts();
|
fetchHostTags();
|
||||||
}, [groupFilter]);
|
}, [groupFilter]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchHosts();
|
||||||
|
}, [groupFilter, hostTagFilters, evalType]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchApps();
|
fetchApps();
|
||||||
}, [groupFilter, hostFilter]);
|
}, [groupFilter, hostFilter]);
|
||||||
@@ -180,6 +212,20 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onHostTagFilterChange = useCallback(
|
||||||
|
(hostTags: HostTagFilter[]) => {
|
||||||
|
onChange({ ...query, hostTags: hostTags });
|
||||||
|
},
|
||||||
|
[onChange, query]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onHostTagEvalTypeChange = useCallback(
|
||||||
|
(evalType: ZabbixTagEvalType) => {
|
||||||
|
onChange({ ...query, evaltype: evalType });
|
||||||
|
},
|
||||||
|
[onChange, query]
|
||||||
|
);
|
||||||
|
|
||||||
const supportsApplications = datasource.zabbix.supportsApplications();
|
const supportsApplications = datasource.zabbix.supportsApplications();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -195,6 +241,16 @@ export const MetricsQueryEditor = ({ query, datasource, onChange, onItemCountCha
|
|||||||
placeholder="Group name"
|
placeholder="Group name"
|
||||||
/>
|
/>
|
||||||
</InlineField>
|
</InlineField>
|
||||||
|
<InlineField label="Host tag" labelWidth={12}>
|
||||||
|
<HostTagQueryEditor
|
||||||
|
hostTagOptions={hostTagsOptions}
|
||||||
|
evalTypeValue={query.evaltype}
|
||||||
|
hostTagOptionsLoading={hostTagsLoading}
|
||||||
|
onHostTagFilterChange={onHostTagFilterChange}
|
||||||
|
onHostTagEvalTypeChange={onHostTagEvalTypeChange}
|
||||||
|
version={datasource.zabbix.version}
|
||||||
|
/>
|
||||||
|
</InlineField>
|
||||||
<InlineField label="Host" labelWidth={12}>
|
<InlineField label="Host" labelWidth={12}>
|
||||||
<MetricPicker
|
<MetricPicker
|
||||||
width={24}
|
width={24}
|
||||||
|
|||||||
23
src/datasource/components/QueryEditor/types.ts
Normal file
23
src/datasource/components/QueryEditor/types.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
export enum HostTagOperatorLabel {
|
||||||
|
Exists = 'Exists',
|
||||||
|
Equals = 'Equals',
|
||||||
|
Contains = 'Contains',
|
||||||
|
DoesNotExist = 'Does not exist',
|
||||||
|
DoesNotEqual = 'Does not equal',
|
||||||
|
DoesNotContain = 'Does not contain',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HostTagOperatorValue {
|
||||||
|
Contains = '0', // default
|
||||||
|
Equals = '1',
|
||||||
|
DoesNotContain = '2',
|
||||||
|
DoesNotEqual = '3',
|
||||||
|
Exists = '4',
|
||||||
|
DoesNotExist = '5',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HostTagOperatorLabelBefore70 {
|
||||||
|
NotExist = 'Not exists',
|
||||||
|
NotEqual = 'Not equal',
|
||||||
|
NotLike = 'Not like',
|
||||||
|
}
|
||||||
89
src/datasource/components/QueryEditor/utils.test.ts
Normal file
89
src/datasource/components/QueryEditor/utils.test.ts
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import { getTemplateSrv } from '@grafana/runtime';
|
||||||
|
import { getHostTagOptionLabel, getVariableOptions, processHostTags } from './utils';
|
||||||
|
import { HostTagOperatorLabel, HostTagOperatorLabelBefore70, HostTagOperatorValue } from './types';
|
||||||
|
|
||||||
|
jest.mock(
|
||||||
|
'@grafana/runtime',
|
||||||
|
() => ({
|
||||||
|
getTemplateSrv: jest.fn(),
|
||||||
|
}),
|
||||||
|
{ virtual: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('QueryEditor utils', () => {
|
||||||
|
describe('getVariableOptions', () => {
|
||||||
|
it('returns template variables except datasource and interval types', () => {
|
||||||
|
(getTemplateSrv as jest.Mock).mockReturnValue({
|
||||||
|
getVariables: jest.fn().mockReturnValue([
|
||||||
|
{ name: 'env', type: 'query' },
|
||||||
|
{ name: 'ds', type: 'datasource' },
|
||||||
|
{ name: 'step', type: 'interval' },
|
||||||
|
{ name: 'region', type: 'custom' },
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = getVariableOptions();
|
||||||
|
|
||||||
|
expect(options).toEqual([
|
||||||
|
{ label: '$env', value: '$env' },
|
||||||
|
{ label: '$region', value: '$region' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('processHostTags', () => {
|
||||||
|
it('deduplicates tags by tag key', () => {
|
||||||
|
const tags = processHostTags([
|
||||||
|
{
|
||||||
|
host: 'a',
|
||||||
|
name: 'a',
|
||||||
|
tags: [
|
||||||
|
{ tag: 'env', value: 'prod' },
|
||||||
|
{ tag: 'role', value: 'api' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
host: 'b',
|
||||||
|
name: 'b',
|
||||||
|
tags: [
|
||||||
|
{ tag: 'env', value: 'stage' },
|
||||||
|
{ tag: 'region', value: 'eu' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ host: 'c', name: 'c' },
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(tags).toEqual([
|
||||||
|
{ tag: 'env', value: 'prod' },
|
||||||
|
{ tag: 'role', value: 'api' },
|
||||||
|
{ tag: 'region', value: 'eu' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getHostTagOptionLabel', () => {
|
||||||
|
it('returns pre-7.0 labels for legacy versions', () => {
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.DoesNotExist, '6.4.0')).toBe(
|
||||||
|
HostTagOperatorLabelBefore70.NotExist
|
||||||
|
);
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.DoesNotEqual, '6.0.0')).toBe(
|
||||||
|
HostTagOperatorLabelBefore70.NotEqual
|
||||||
|
);
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.DoesNotContain, '5.0.0')).toBe(
|
||||||
|
HostTagOperatorLabelBefore70.NotLike
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns current labels for 7.0 and newer', () => {
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.DoesNotExist, '7.0.0')).toBe(HostTagOperatorLabel.DoesNotExist);
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.DoesNotEqual, '7.1.0')).toBe(HostTagOperatorLabel.DoesNotEqual);
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.DoesNotContain, '7.2.0')).toBe(
|
||||||
|
HostTagOperatorLabel.DoesNotContain
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns empty string for unsupported values', () => {
|
||||||
|
expect(getHostTagOptionLabel(HostTagOperatorValue.Equals, '7.2.0')).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import { uniqBy } from 'lodash';
|
||||||
import { getTemplateSrv } from '@grafana/runtime';
|
import { getTemplateSrv } from '@grafana/runtime';
|
||||||
|
import { Host, Tag } from 'datasource/zabbix/types';
|
||||||
|
import { HostTagOperatorLabel, HostTagOperatorLabelBefore70, HostTagOperatorValue } from './types';
|
||||||
|
|
||||||
export const getVariableOptions = () => {
|
export const getVariableOptions = () => {
|
||||||
const variables = getTemplateSrv()
|
const variables = getTemplateSrv()
|
||||||
@@ -11,3 +14,28 @@ export const getVariableOptions = () => {
|
|||||||
label: `$${v.name}`,
|
label: `$${v.name}`,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function processHostTags(hosts: Host[]): Tag[] {
|
||||||
|
const hostTags = hosts.map((host) => host.tags || []).flat();
|
||||||
|
// deduplicate tags
|
||||||
|
const uniqueHostTags = uniqBy(hostTags, (tag) => tag.tag);
|
||||||
|
return uniqueHostTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the label for a host tag option
|
||||||
|
* Zabbix changed some of the operator labels in version 7.0.0 but the value equivalents remained the same.
|
||||||
|
* this function helps fetch the right label value for those that are different.
|
||||||
|
*/
|
||||||
|
export function getHostTagOptionLabel(value: HostTagOperatorValue, version: string): string {
|
||||||
|
switch (value) {
|
||||||
|
case HostTagOperatorValue.DoesNotExist:
|
||||||
|
return version < '7.0.0' ? HostTagOperatorLabelBefore70.NotExist : HostTagOperatorLabel.DoesNotExist;
|
||||||
|
case HostTagOperatorValue.DoesNotEqual:
|
||||||
|
return version < '7.0.0' ? HostTagOperatorLabelBefore70.NotEqual : HostTagOperatorLabel.DoesNotEqual;
|
||||||
|
case HostTagOperatorValue.DoesNotContain:
|
||||||
|
return version < '7.0.0' ? HostTagOperatorLabelBefore70.NotLike : HostTagOperatorLabel.DoesNotContain;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DataQuery } from '@grafana/schema';
|
import { DataQuery } from '@grafana/schema';
|
||||||
import * as c from './../constants';
|
import * as c from './../constants';
|
||||||
|
import { HostTagOperatorValue } from 'datasource/components/QueryEditor/types';
|
||||||
|
|
||||||
export type QueryType =
|
export type QueryType =
|
||||||
| typeof c.MODE_METRICS
|
| typeof c.MODE_METRICS
|
||||||
@@ -24,6 +25,7 @@ export type ZabbixMetricsQuery = {
|
|||||||
mode: number;
|
mode: number;
|
||||||
itemids: string;
|
itemids: string;
|
||||||
useCaptureGroups: boolean;
|
useCaptureGroups: boolean;
|
||||||
|
hostTags?: HostTagFilter[];
|
||||||
proxy?: { filter: string };
|
proxy?: { filter: string };
|
||||||
trigger?: { filter: string };
|
trigger?: { filter: string };
|
||||||
itServiceFilter?: string;
|
itServiceFilter?: string;
|
||||||
@@ -108,3 +110,9 @@ export enum ZabbixTagEvalType {
|
|||||||
AndOr = '0',
|
AndOr = '0',
|
||||||
Or = '2',
|
Or = '2',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HostTagFilter {
|
||||||
|
tag: string;
|
||||||
|
value: string;
|
||||||
|
operator: HostTagOperatorValue;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { ZabbixAPIConnector } from './zabbixAPIConnector';
|
import { ZabbixAPIConnector } from './zabbixAPIConnector';
|
||||||
|
import { HostTagOperatorValue } from '../../../components/QueryEditor/types';
|
||||||
|
import { ZabbixTagEvalType } from 'datasource/types/query';
|
||||||
|
|
||||||
describe('Zabbix API connector', () => {
|
describe('Zabbix API connector', () => {
|
||||||
describe('getProxies function', () => {
|
describe('getProxies function', () => {
|
||||||
@@ -154,6 +156,80 @@ describe('Zabbix API connector', () => {
|
|||||||
expect(params.applicationids).toBeUndefined();
|
expect(params.applicationids).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getHosts', () => {
|
||||||
|
it('passes base params and group ids', () => {
|
||||||
|
const zabbixAPIConnector = new ZabbixAPIConnector(true, true, 123);
|
||||||
|
zabbixAPIConnector.request = jest.fn();
|
||||||
|
|
||||||
|
zabbixAPIConnector.getHosts(['1', '2']);
|
||||||
|
|
||||||
|
expect(zabbixAPIConnector.request).toHaveBeenCalledWith('host.get', {
|
||||||
|
output: ['hostid', 'name', 'host'],
|
||||||
|
sortfield: 'name',
|
||||||
|
groupids: ['1', '2'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('requests tags when getHostTags is true', () => {
|
||||||
|
const zabbixAPIConnector = new ZabbixAPIConnector(true, true, 123);
|
||||||
|
zabbixAPIConnector.request = jest.fn();
|
||||||
|
|
||||||
|
zabbixAPIConnector.getHosts(undefined, true);
|
||||||
|
|
||||||
|
expect(zabbixAPIConnector.request).toHaveBeenCalledWith('host.get', {
|
||||||
|
output: ['hostid', 'name', 'host', 'tags'],
|
||||||
|
sortfield: 'name',
|
||||||
|
selectTags: 'extend',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('builds tag filters with numeric operator and evaltype', () => {
|
||||||
|
const zabbixAPIConnector = new ZabbixAPIConnector(true, true, 123);
|
||||||
|
zabbixAPIConnector.request = jest.fn();
|
||||||
|
|
||||||
|
zabbixAPIConnector.getHosts(
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
{ tag: 'role', value: 'api', operator: HostTagOperatorValue.Contains },
|
||||||
|
{ tag: '', value: 'ignore me', operator: HostTagOperatorValue.Equals },
|
||||||
|
],
|
||||||
|
ZabbixTagEvalType.Or
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(zabbixAPIConnector.request).toHaveBeenCalledWith('host.get', {
|
||||||
|
output: ['hostid', 'name', 'host'],
|
||||||
|
sortfield: 'name',
|
||||||
|
selectTags: 'extend',
|
||||||
|
evaltype: 2,
|
||||||
|
tags: [{ tag: 'role', value: 'api', operator: 0 }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('builds tag filters with numeric operator and default evaltype when using unsupported evalType', () => {
|
||||||
|
const zabbixAPIConnector = new ZabbixAPIConnector(true, true, 123);
|
||||||
|
zabbixAPIConnector.request = jest.fn();
|
||||||
|
|
||||||
|
zabbixAPIConnector.getHosts(
|
||||||
|
undefined,
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
{ tag: 'role', value: 'api', operator: HostTagOperatorValue.Contains },
|
||||||
|
{ tag: '', value: 'ignore me', operator: HostTagOperatorValue.Equals },
|
||||||
|
],
|
||||||
|
'3' as ZabbixTagEvalType
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(zabbixAPIConnector.request).toHaveBeenCalledWith('host.get', {
|
||||||
|
output: ['hostid', 'name', 'host'],
|
||||||
|
sortfield: 'name',
|
||||||
|
selectTags: 'extend',
|
||||||
|
evaltype: 0,
|
||||||
|
tags: [{ tag: 'role', value: 'api', operator: 0 }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const triggers = [
|
const triggers = [
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import semver from 'semver';
|
|||||||
import kbn from 'grafana/app/core/utils/kbn';
|
import kbn from 'grafana/app/core/utils/kbn';
|
||||||
import * as utils from '../../../utils';
|
import * as utils from '../../../utils';
|
||||||
import { MIN_SLA_INTERVAL, ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_NONE } from '../../../constants';
|
import { MIN_SLA_INTERVAL, ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_NONE } from '../../../constants';
|
||||||
import { ShowProblemTypes } from '../../../types/query';
|
import { HostTagFilter, ShowProblemTypes, ZabbixTagEvalType } from '../../../types/query';
|
||||||
import { ZBXProblem, ZBXTrigger } from '../../../types';
|
import { ZBXProblem, ZBXTrigger } from '../../../types';
|
||||||
import { APIExecuteScriptResponse, JSONRPCError, ZBXScript } from './types';
|
import { APIExecuteScriptResponse, JSONRPCError, ZBXScript } from './types';
|
||||||
import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
|
import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
|
||||||
@@ -149,7 +149,12 @@ export class ZabbixAPIConnector {
|
|||||||
return this.request('hostgroup.get', params);
|
return this.request('hostgroup.get', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
getHosts(groupids): Promise<any[]> {
|
getHosts(
|
||||||
|
groupids: string[],
|
||||||
|
getHostTags?: boolean,
|
||||||
|
hostTagFilters?: HostTagFilter[],
|
||||||
|
evalType?: ZabbixTagEvalType
|
||||||
|
): Promise<any[]> {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
output: ['hostid', 'name', 'host'],
|
output: ['hostid', 'name', 'host'],
|
||||||
sortfield: 'name',
|
sortfield: 'name',
|
||||||
@@ -158,6 +163,23 @@ export class ZabbixAPIConnector {
|
|||||||
params.groupids = groupids;
|
params.groupids = groupids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getHostTags) {
|
||||||
|
params.output.push('tags');
|
||||||
|
params.selectTags = 'extend';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostTagFilters && hostTagFilters.length > 0) {
|
||||||
|
params.selectTags = 'extend';
|
||||||
|
params.evaltype = evalType === ZabbixTagEvalType.Or || evalType === ZabbixTagEvalType.AndOr ? +evalType : 0;
|
||||||
|
|
||||||
|
// ensure only non empty tag keys are being sent
|
||||||
|
// convert operator to number since that is the expected type in Zabbix.
|
||||||
|
params.tags = hostTagFilters
|
||||||
|
.filter((tagFilter) => tagFilter.tag !== '')
|
||||||
|
.map((tagFilter) => {
|
||||||
|
return { ...tagFilter, operator: +tagFilter.operator };
|
||||||
|
});
|
||||||
|
}
|
||||||
return this.request('host.get', params);
|
return this.request('host.get', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,3 +50,15 @@ export interface ZabbixConnector {
|
|||||||
|
|
||||||
supportsApplications: () => boolean;
|
supportsApplications: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Host {
|
||||||
|
host: string;
|
||||||
|
name: string;
|
||||||
|
hostid?: string;
|
||||||
|
tags?: Tag[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Tag {
|
||||||
|
tag: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import { DBConnector } from './connectors/dbConnector';
|
|||||||
import { ZabbixAPIConnector } from './connectors/zabbix_api/zabbixAPIConnector';
|
import { ZabbixAPIConnector } from './connectors/zabbix_api/zabbixAPIConnector';
|
||||||
import { SQLConnector } from './connectors/sql/sqlConnector';
|
import { SQLConnector } from './connectors/sql/sqlConnector';
|
||||||
import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector';
|
import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector';
|
||||||
import { ZabbixConnector } from './types';
|
import { Host, ZabbixConnector } from './types';
|
||||||
import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler';
|
import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler';
|
||||||
import { ZabbixMetricsQuery } from '../types/query';
|
import { HostTagFilter, ZabbixMetricsQuery, ZabbixTagEvalType } from '../types/query';
|
||||||
import { ProblemDTO, ZBXApp, ZBXHost, ZBXItem, ZBXItemTag, ZBXTrigger } from '../types';
|
import { ProblemDTO, ZBXApp, ZBXHost, ZBXItem, ZBXItemTag, ZBXTrigger } from '../types';
|
||||||
|
|
||||||
interface AppsResponse extends Array<any> {
|
interface AppsResponse extends Array<any> {
|
||||||
@@ -296,6 +296,7 @@ export class Zabbix implements ZabbixConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAllGroups() {
|
getAllGroups() {
|
||||||
|
console.log(this.zabbixAPI.getGroups());
|
||||||
return this.zabbixAPI.getGroups();
|
return this.zabbixAPI.getGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,10 +307,15 @@ export class Zabbix implements ZabbixConnector {
|
|||||||
/**
|
/**
|
||||||
* Get list of host belonging to given groups.
|
* Get list of host belonging to given groups.
|
||||||
*/
|
*/
|
||||||
getAllHosts(groupFilter): Promise<any[]> {
|
getAllHosts(
|
||||||
|
groupFilter: string,
|
||||||
|
getHostTags?: boolean,
|
||||||
|
hostTagFilters?: HostTagFilter[],
|
||||||
|
evalType?: ZabbixTagEvalType
|
||||||
|
): Promise<Host[]> {
|
||||||
return this.getGroups(groupFilter).then((groups) => {
|
return this.getGroups(groupFilter).then((groups) => {
|
||||||
const groupids = _.map(groups, 'groupid');
|
const groupids = _.map(groups, 'groupid');
|
||||||
return this.zabbixAPI.getHosts(groupids);
|
return this.zabbixAPI.getHosts(groupids, getHostTags, hostTagFilters, evalType);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user