Support item tags, fixes #1258
This commit is contained in:
@@ -75,6 +75,7 @@ type QueryModel struct {
|
|||||||
Group QueryFilter `json:"group"`
|
Group QueryFilter `json:"group"`
|
||||||
Host QueryFilter `json:"host"`
|
Host QueryFilter `json:"host"`
|
||||||
Application QueryFilter `json:"application"`
|
Application QueryFilter `json:"application"`
|
||||||
|
ItemTag QueryFilter `json:"itemTag"`
|
||||||
Item QueryFilter `json:"item"`
|
Item QueryFilter `json:"item"`
|
||||||
|
|
||||||
// Item ID mode
|
// Item ID mode
|
||||||
|
|||||||
@@ -47,9 +47,10 @@ func (ds *ZabbixDatasourceInstance) queryNumericItems(ctx context.Context, query
|
|||||||
groupFilter := query.Group.Filter
|
groupFilter := query.Group.Filter
|
||||||
hostFilter := query.Host.Filter
|
hostFilter := query.Host.Filter
|
||||||
appFilter := query.Application.Filter
|
appFilter := query.Application.Filter
|
||||||
|
itemTagFilter := query.ItemTag.Filter
|
||||||
itemFilter := query.Item.Filter
|
itemFilter := query.Item.Filter
|
||||||
|
|
||||||
items, err := ds.zabbix.GetItems(ctx, groupFilter, hostFilter, appFilter, itemFilter, "num")
|
items, err := ds.zabbix.GetItems(ctx, groupFilter, hostFilter, appFilter, itemTagFilter, itemFilter, "num")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package zabbix
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
)
|
)
|
||||||
@@ -78,7 +80,7 @@ func (ds *Zabbix) getTrend(ctx context.Context, itemids []string, timeRange back
|
|||||||
return trend, err
|
return trend, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *Zabbix) GetItems(ctx context.Context, groupFilter string, hostFilter string, appFilter string, itemFilter string, itemType string) ([]*Item, error) {
|
func (ds *Zabbix) GetItems(ctx context.Context, groupFilter string, hostFilter string, appFilter string, itemTagFilter string, itemFilter string, itemType string) ([]*Item, 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
|
||||||
@@ -90,7 +92,8 @@ func (ds *Zabbix) GetItems(ctx context.Context, groupFilter string, hostFilter s
|
|||||||
|
|
||||||
apps, err := ds.GetApps(ctx, groupFilter, hostFilter, appFilter)
|
apps, err := ds.GetApps(ctx, groupFilter, hostFilter, appFilter)
|
||||||
// Apps not supported in Zabbix 5.4 and higher
|
// Apps not supported in Zabbix 5.4 and higher
|
||||||
if isAppMethodNotFoundError(err) {
|
isZabbix54orHigher := isAppMethodNotFoundError(err)
|
||||||
|
if isZabbix54orHigher {
|
||||||
apps = []Application{}
|
apps = []Application{}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -107,9 +110,50 @@ func (ds *Zabbix) GetItems(ctx context.Context, groupFilter string, hostFilter s
|
|||||||
allItems, err = ds.GetAllItems(ctx, nil, appids, itemType)
|
allItems, err = ds.GetAllItems(ctx, nil, appids, itemType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isZabbix54orHigher && itemTagFilter != "" {
|
||||||
|
allItems, err = filterItemsByTag(allItems, itemTagFilter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return filterItemsByQuery(allItems, itemFilter)
|
return filterItemsByQuery(allItems, itemFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterItemsByTag(items []*Item, filter string) ([]*Item, error) {
|
||||||
|
re, err := parseFilter(filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var filteredItems []*Item
|
||||||
|
for _, i := range items {
|
||||||
|
if len(i.Tags) == 0 && filter == "/.*/" {
|
||||||
|
filteredItems = append(filteredItems, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.Tags) > 0 {
|
||||||
|
var tags []string
|
||||||
|
for _, t := range i.Tags {
|
||||||
|
tags = append(tags, itemTagToString(t))
|
||||||
|
}
|
||||||
|
for _, t := range tags {
|
||||||
|
if re != nil {
|
||||||
|
if re.MatchString(t) {
|
||||||
|
filteredItems = append(filteredItems, i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else if t == filter {
|
||||||
|
filteredItems = append(filteredItems, i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filteredItems, nil
|
||||||
|
}
|
||||||
|
|
||||||
func filterItemsByQuery(items []*Item, filter string) ([]*Item, error) {
|
func filterItemsByQuery(items []*Item, filter string) ([]*Item, error) {
|
||||||
re, err := parseFilter(filter)
|
re, err := parseFilter(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -259,6 +303,10 @@ func (ds *Zabbix) GetAllItems(ctx context.Context, hostids []string, appids []st
|
|||||||
filter["value_type"] = []int{1, 2, 4}
|
filter["value_type"] = []int{1, 2, 4}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ds.version >= 54 {
|
||||||
|
params["selectTags"] = "extend"
|
||||||
|
}
|
||||||
|
|
||||||
result, err := ds.Request(ctx, &ZabbixAPIRequest{Method: "item.get", Params: params})
|
result, err := ds.Request(ctx, &ZabbixAPIRequest{Method: "item.get", Params: params})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -347,6 +395,23 @@ func (ds *Zabbix) GetAllGroups(ctx context.Context) ([]Group, error) {
|
|||||||
return groups, err
|
return groups, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ds *Zabbix) GetVersion(ctx context.Context) (int, error) {
|
||||||
|
result, err := ds.request(ctx, "apiinfo.version", ZabbixAPIParams{})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var version string
|
||||||
|
err = convertTo(result, &version)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
version = strings.Replace(version[0:3], ".", "", 1)
|
||||||
|
versionNum, err := strconv.Atoi(version)
|
||||||
|
return versionNum, err
|
||||||
|
}
|
||||||
|
|
||||||
func isAppMethodNotFoundError(err error) bool {
|
func isAppMethodNotFoundError(err error) bool {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -51,6 +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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ItemHost struct {
|
type ItemHost struct {
|
||||||
@@ -58,6 +59,11 @@ type ItemHost struct {
|
|||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ItemTag struct {
|
||||||
|
Tag string `json:"tag,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Trend []TrendPoint
|
type Trend []TrendPoint
|
||||||
|
|
||||||
type TrendPoint struct {
|
type TrendPoint struct {
|
||||||
|
|||||||
@@ -85,3 +85,11 @@ func parseFilter(filter string) (*regexp.Regexp, error) {
|
|||||||
|
|
||||||
return regexp.Compile(pattern)
|
return regexp.Compile(pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func itemTagToString(tag ItemTag) string {
|
||||||
|
if tag.Value != "" {
|
||||||
|
return fmt.Sprintf("%s: %s", tag.Tag, tag.Value)
|
||||||
|
} else {
|
||||||
|
return tag.Tag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import (
|
|||||||
// Zabbix is a wrapper for Zabbix API. It wraps Zabbix API queries and performs authentication, adds caching,
|
// Zabbix is a wrapper for Zabbix API. It wraps Zabbix API queries and performs authentication, adds caching,
|
||||||
// deduplication and other performance optimizations.
|
// deduplication and other performance optimizations.
|
||||||
type Zabbix struct {
|
type Zabbix struct {
|
||||||
api *zabbixapi.ZabbixAPI
|
api *zabbixapi.ZabbixAPI
|
||||||
dsInfo *backend.DataSourceInstanceSettings
|
dsInfo *backend.DataSourceInstanceSettings
|
||||||
cache *ZabbixCache
|
cache *ZabbixCache
|
||||||
logger log.Logger
|
version int
|
||||||
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns new instance of Zabbix client.
|
// New returns new instance of Zabbix client.
|
||||||
@@ -49,6 +50,16 @@ func (ds *Zabbix) Request(ctx context.Context, apiReq *ZabbixAPIRequest) (*simpl
|
|||||||
var resultJson *simplejson.Json
|
var resultJson *simplejson.Json
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
if ds.version == 0 {
|
||||||
|
version, err := ds.GetVersion(ctx)
|
||||||
|
if err != nil {
|
||||||
|
ds.logger.Error("Error querying Zabbix version", "error", err)
|
||||||
|
} else {
|
||||||
|
ds.logger.Debug("Got Zabbix version", "version", version)
|
||||||
|
ds.version = version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cachedResult, queryExistInCache := ds.cache.GetAPIRequest(apiReq)
|
cachedResult, queryExistInCache := ds.cache.GetAPIRequest(apiReq)
|
||||||
if !queryExistInCache {
|
if !queryExistInCache {
|
||||||
resultJson, err = ds.request(ctx, apiReq.Method, apiReq.Params)
|
resultJson, err = ds.request(ctx, apiReq.Method, apiReq.Params)
|
||||||
|
|||||||
@@ -680,7 +680,7 @@ export class ZabbixDatasource extends DataSourceApi<ZabbixMetricsQuery, ZabbixDS
|
|||||||
resultPromise = this.zabbix.getApps(queryModel.group, queryModel.host, queryModel.application);
|
resultPromise = this.zabbix.getApps(queryModel.group, queryModel.host, queryModel.application);
|
||||||
break;
|
break;
|
||||||
case VariableQueryTypes.Item:
|
case VariableQueryTypes.Item:
|
||||||
resultPromise = this.zabbix.getItems(queryModel.group, queryModel.host, queryModel.application, queryModel.item);
|
resultPromise = this.zabbix.getItems(queryModel.group, queryModel.host, queryModel.application, null, queryModel.item);
|
||||||
break;
|
break;
|
||||||
case VariableQueryTypes.ItemValues:
|
case VariableQueryTypes.ItemValues:
|
||||||
const range = options?.range;
|
const range = options?.range;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export function isGrafana2target(target) {
|
|||||||
if (!target.mode || target.mode === 0 || target.mode === 2) {
|
if (!target.mode || target.mode === 0 || target.mode === 2) {
|
||||||
if ((target.hostFilter || target.itemFilter || target.downsampleFunction ||
|
if ((target.hostFilter || target.itemFilter || target.downsampleFunction ||
|
||||||
(target.host && target.host.host)) &&
|
(target.host && target.host.host)) &&
|
||||||
(target.item.filter === undefined && target.host.filter === undefined)) {
|
(target.item.filter === undefined && target.host.filter === undefined)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -68,6 +68,15 @@ function migrateProblemSort(target) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function migrateApplications(target) {
|
||||||
|
if (!target.itemTag) {
|
||||||
|
target.itemTag = { filter: '' };
|
||||||
|
if (target.application?.filter) {
|
||||||
|
target.itemTag.filter = `Application: ${target.application?.filter}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function migrate(target) {
|
export function migrate(target) {
|
||||||
target.resultFormat = target.resultFormat || 'time_series';
|
target.resultFormat = target.resultFormat || 'time_series';
|
||||||
target = fixTargetGroup(target);
|
target = fixTargetGroup(target);
|
||||||
@@ -78,6 +87,7 @@ export function migrate(target) {
|
|||||||
migrateQueryType(target);
|
migrateQueryType(target);
|
||||||
migrateSLA(target);
|
migrateSLA(target);
|
||||||
migrateProblemSort(target);
|
migrateProblemSort(target);
|
||||||
|
migrateApplications(target);
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +107,7 @@ function convertToRegex(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DS_CONFIG_SCHEMA = 2;
|
export const DS_CONFIG_SCHEMA = 2;
|
||||||
|
|
||||||
export function migrateDSConfig(jsonData) {
|
export function migrateDSConfig(jsonData) {
|
||||||
if (!jsonData) {
|
if (!jsonData) {
|
||||||
jsonData = {};
|
jsonData = {};
|
||||||
|
|||||||
@@ -1,371 +1,396 @@
|
|||||||
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
<query-editor-row query-ctrl="ctrl" can-collapse="false">
|
||||||
|
|
||||||
<div class="gf-form-inline">
|
<div class="gf-form-inline">
|
||||||
<div class="gf-form max-width-20">
|
<div class="gf-form max-width-20">
|
||||||
<label class="gf-form-label width-7">Query Mode</label>
|
<label class="gf-form-label width-7">Query Mode</label>
|
||||||
<div class="gf-form-select-wrapper max-width-20">
|
<div class="gf-form-select-wrapper max-width-20">
|
||||||
<select class="gf-form-input"
|
<select class="gf-form-input"
|
||||||
ng-change="ctrl.switchEditorMode(ctrl.target.queryType)"
|
ng-change="ctrl.switchEditorMode(ctrl.target.queryType)"
|
||||||
ng-model="ctrl.target.queryType"
|
ng-model="ctrl.target.queryType"
|
||||||
ng-options="m.queryType as m.text for m in ctrl.editorModes">
|
ng-options="m.queryType as m.text for m in ctrl.editorModes">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form" ng-show="ctrl.target.queryType == editorMode.TEXT">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Format As</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat"
|
||||||
|
ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form" ng-show="ctrl.target.queryType == editorMode.TEXT">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Format As</label>
|
|
||||||
<div class="gf-form-select-wrapper">
|
|
||||||
<select class="gf-form-input gf-size-auto" ng-model="ctrl.target.resultFormat" ng-options="f.value as f.text for f in ctrl.resultFormats" ng-change="ctrl.refresh()"></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- IT Service editor -->
|
<!-- IT Service editor -->
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.ITSERVICE">
|
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.ITSERVICE">
|
||||||
<div class="gf-form max-width-20">
|
<div class="gf-form max-width-20">
|
||||||
<label class="gf-form-label query-keyword width-7">IT Service</label>
|
<label class="gf-form-label query-keyword width-7">IT Service</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="ctrl.target.itServiceFilter"
|
ng-model="ctrl.target.itServiceFilter"
|
||||||
bs-typeahead="ctrl.getITServices"
|
bs-typeahead="ctrl.getITServices"
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
data-min-length=0
|
data-min-length=0
|
||||||
data-items=100
|
data-items=100
|
||||||
class="gf-form-input"
|
class="gf-form-input"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.itServiceFilter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.itServiceFilter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.itServiceFilter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.itServiceFilter)
|
||||||
}">
|
}">
|
||||||
</input>
|
</input>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Property</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-change="ctrl.onTargetBlur()"
|
||||||
|
ng-model="ctrl.target.slaProperty"
|
||||||
|
ng-options="slaProperty.name for slaProperty in ctrl.slaPropertyList track by slaProperty.name">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Interval</label>
|
||||||
|
<div class="gf-form-select-wrapper">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-change="ctrl.onTargetBlur()"
|
||||||
|
ng-model="ctrl.target.slaInterval"
|
||||||
|
ng-options="i.value as i.text for i in ctrl.slaIntervals">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Property</label>
|
|
||||||
<div class="gf-form-select-wrapper">
|
|
||||||
<select class="gf-form-input"
|
|
||||||
ng-change="ctrl.onTargetBlur()"
|
|
||||||
ng-model="ctrl.target.slaProperty"
|
|
||||||
ng-options="slaProperty.name for slaProperty in ctrl.slaPropertyList track by slaProperty.name">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Interval</label>
|
|
||||||
<div class="gf-form-select-wrapper">
|
|
||||||
<select class="gf-form-input"
|
|
||||||
ng-change="ctrl.onTargetBlur()"
|
|
||||||
ng-model="ctrl.target.slaInterval"
|
|
||||||
ng-options="i.value as i.text for i in ctrl.slaIntervals">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.TEXT || ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
<div class="gf-form-inline"
|
||||||
<!-- Select Group -->
|
ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.TEXT || ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
<div class="gf-form max-width-20">
|
<!-- Select Group -->
|
||||||
<label class="gf-form-label query-keyword width-7">Group</label>
|
<div class="gf-form max-width-20">
|
||||||
<input type="text"
|
<label class="gf-form-label query-keyword width-7">Group</label>
|
||||||
ng-model="ctrl.target.group.filter"
|
<input type="text"
|
||||||
bs-typeahead="ctrl.getGroupNames"
|
ng-model="ctrl.target.group.filter"
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
bs-typeahead="ctrl.getGroupNames"
|
||||||
data-min-length=0
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
data-items=100
|
data-min-length=0
|
||||||
class="gf-form-input"
|
data-items=100
|
||||||
ng-class="{
|
class="gf-form-input"
|
||||||
|
ng-class="{
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.group.filter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.group.filter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.group.filter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.group.filter)
|
||||||
}"></input>
|
}"></input>
|
||||||
</div>
|
</div>
|
||||||
<!-- Select Host -->
|
<!-- Select Host -->
|
||||||
<div class="gf-form max-width-20">
|
<div class="gf-form max-width-20">
|
||||||
<label class="gf-form-label query-keyword width-7">Host</label>
|
<label class="gf-form-label query-keyword width-7">Host</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="ctrl.target.host.filter"
|
ng-model="ctrl.target.host.filter"
|
||||||
bs-typeahead="ctrl.getHostNames"
|
bs-typeahead="ctrl.getHostNames"
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
data-min-length=0
|
data-min-length=0
|
||||||
data-items=100
|
data-items=100
|
||||||
class="gf-form-input"
|
class="gf-form-input"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.host.filter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.host.filter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.host.filter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.host.filter)
|
||||||
}">
|
}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
<label class="gf-form-label query-keyword width-7">Proxy</label>
|
<label class="gf-form-label query-keyword width-7">Proxy</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="ctrl.target.proxy.filter"
|
ng-model="ctrl.target.proxy.filter"
|
||||||
bs-typeahead="ctrl.getProxyNames"
|
bs-typeahead="ctrl.getProxyNames"
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
data-min-length=0
|
data-min-length=0
|
||||||
data-items=100
|
data-items=100
|
||||||
class="gf-form-input width-14"
|
class="gf-form-input width-14"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.proxy.filter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.proxy.filter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.proxy.filter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.proxy.filter)
|
||||||
}">
|
}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form gf-form--grow">
|
<div class="gf-form-inline"
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.TEXT || ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
</div>
|
<!-- Select Application -->
|
||||||
</div>
|
<div class="gf-form max-width-20" ng-show="!ctrl.appFilterDisabled()">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Application</label>
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.TEXT || ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
<input type="text"
|
||||||
<!-- Select Application -->
|
ng-model="ctrl.target.application.filter"
|
||||||
<div class="gf-form max-width-20">
|
bs-typeahead="ctrl.getApplicationNames"
|
||||||
<label class="gf-form-label query-keyword width-7">Application</label>
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
<input type="text"
|
data-min-length=0
|
||||||
ng-model="ctrl.target.application.filter"
|
data-items=100
|
||||||
ng-disabled="ctrl.appFilterDisabled()"
|
class="gf-form-input"
|
||||||
bs-typeahead="ctrl.getApplicationNames"
|
ng-class="{
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
|
||||||
data-min-length=0
|
|
||||||
data-items=100
|
|
||||||
class="gf-form-input"
|
|
||||||
ng-class="{
|
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.application.filter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.application.filter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.application.filter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.application.filter)
|
||||||
}">
|
}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Select Item -->
|
<!-- Select item tags -->
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.TEXT">
|
<div class="gf-form max-width-20" ng-show="ctrl.appFilterDisabled()">
|
||||||
<label class="gf-form-label query-keyword width-7">Item</label>
|
<label class="gf-form-label query-keyword width-7">Item tag</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="ctrl.target.item.filter"
|
ng-model="ctrl.target.itemTag.filter"
|
||||||
bs-typeahead="ctrl.getItemNames"
|
bs-typeahead="ctrl.getItemTags"
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
data-min-length=0
|
data-min-length=0
|
||||||
data-items=100
|
data-items=100
|
||||||
class="gf-form-input"
|
class="gf-form-input"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
|
'zbx-variable': ctrl.isVariable(ctrl.target.itemTag.filter),
|
||||||
|
'zbx-regex': ctrl.isRegex(ctrl.target.itemTag.filter)
|
||||||
|
}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Select Item -->
|
||||||
|
<div class="gf-form max-width-20"
|
||||||
|
ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.TEXT">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Item</label>
|
||||||
|
<input type="text"
|
||||||
|
ng-model="ctrl.target.item.filter"
|
||||||
|
bs-typeahead="ctrl.getItemNames"
|
||||||
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
|
data-min-length=0
|
||||||
|
data-items=100
|
||||||
|
class="gf-form-input"
|
||||||
|
ng-class="{
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.item.filter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.item.filter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.item.filter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.item.filter)
|
||||||
}">
|
}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
<label class="gf-form-label query-keyword width-7">Problem</label>
|
<label class="gf-form-label query-keyword width-7">Problem</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="ctrl.target.trigger.filter"
|
ng-model="ctrl.target.trigger.filter"
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
placeholder="Problem name"
|
placeholder="Problem name"
|
||||||
class="gf-form-input"
|
class="gf-form-input"
|
||||||
ng-style="ctrl.target.trigger.style"
|
ng-style="ctrl.target.trigger.style"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.trigger.filter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.trigger.filter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.trigger.filter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.trigger.filter)
|
||||||
}">
|
}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Tags</label>
|
||||||
|
<input type="text" class="gf-form-input width-14"
|
||||||
|
ng-model="ctrl.target.tags.filter"
|
||||||
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
|
placeholder="tag1:value1, tag2:value2">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
<div class="gf-form-inline"
|
||||||
<label class="gf-form-label query-keyword width-7">Tags</label>
|
ng-show="ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
<input type="text" class="gf-form-input width-14"
|
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
ng-model="ctrl.target.tags.filter"
|
<label class="gf-form-label query-keyword width-7">Show</label>
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
<div class="gf-form-select-wrapper max-width-20">
|
||||||
placeholder="tag1:value1, tag2:value2">
|
<select class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.showProblems"
|
||||||
|
ng-options="v.value as v.text for v in ctrl.showProblemsOptions"
|
||||||
|
ng-change="ctrl.onTargetBlur()">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Min severity</label>
|
||||||
|
<div class="gf-form-select-wrapper max-width-20">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.options.minSeverity"
|
||||||
|
ng-options="v.val as v.text for v in ctrl.severityOptions"
|
||||||
|
ng-change="ctrl.onTargetBlur()">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.TRIGGERS">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Min Severity</label>
|
||||||
|
<div class="gf-form-select-wrapper width-14">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-change="ctrl.onTargetBlur()"
|
||||||
|
ng-model="ctrl.target.triggers.minSeverity"
|
||||||
|
ng-options="s.val as s.text for s in ctrl.severityOptions">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form gf-form--grow">
|
<!-- Text mode options -->
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.TEXT">
|
||||||
</div>
|
<!-- Text metric regex -->
|
||||||
</div>
|
<div class="gf-form max-width-20">
|
||||||
|
<label class="gf-form-label query-keyword width-7">Text filter</label>
|
||||||
|
<input type="text"
|
||||||
|
class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.textFilter"
|
||||||
|
spellcheck='false'
|
||||||
|
placeholder="Text filter (regex)"
|
||||||
|
ng-blur="ctrl.onTargetBlur()">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
<gf-form-switch class="gf-form" label="Use capture groups" checked="ctrl.target.useCaptureGroups"
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
on-change="ctrl.onTargetBlur()">
|
||||||
<label class="gf-form-label query-keyword width-7">Show</label>
|
</gf-form-switch>
|
||||||
<div class="gf-form-select-wrapper max-width-20">
|
<div class="gf-form gf-form--grow">
|
||||||
<select class="gf-form-input"
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
ng-model="ctrl.target.showProblems"
|
</div>
|
||||||
ng-options="v.value as v.text for v in ctrl.showProblemsOptions"
|
|
||||||
ng-change="ctrl.onTargetBlur()">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Min severity</label>
|
|
||||||
<div class="gf-form-select-wrapper max-width-20">
|
|
||||||
<select class="gf-form-input"
|
|
||||||
ng-model="ctrl.target.options.minSeverity"
|
|
||||||
ng-options="v.val as v.text for v in ctrl.severityOptions"
|
|
||||||
ng-change="ctrl.onTargetBlur()">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form max-width-20" ng-show="ctrl.target.queryType == editorMode.TRIGGERS">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Min Severity</label>
|
|
||||||
<div class="gf-form-select-wrapper width-14">
|
|
||||||
<select class="gf-form-input"
|
|
||||||
ng-change="ctrl.onTargetBlur()"
|
|
||||||
ng-model="ctrl.target.triggers.minSeverity"
|
|
||||||
ng-options="s.val as s.text for s in ctrl.severityOptions">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gf-form gf-form--grow">
|
<!-- Item IDs editor mode -->
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.ITEMID">
|
||||||
</div>
|
<div class="gf-form max-width-20">
|
||||||
</div>
|
<label class="gf-form-label query-keyword width-7">Item IDs</label>
|
||||||
|
<input type="text"
|
||||||
<!-- Text mode options -->
|
ng-model="ctrl.target.itemids"
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.TEXT">
|
bs-typeahead="ctrl.getVariables"
|
||||||
<!-- Text metric regex -->
|
ng-blur="ctrl.onTargetBlur()"
|
||||||
<div class="gf-form max-width-20">
|
data-min-length=0
|
||||||
<label class="gf-form-label query-keyword width-7">Text filter</label>
|
data-items=100
|
||||||
<input type="text"
|
class="gf-form-input"
|
||||||
class="gf-form-input"
|
ng-class="{
|
||||||
ng-model="ctrl.target.textFilter"
|
|
||||||
spellcheck='false'
|
|
||||||
placeholder="Text filter (regex)"
|
|
||||||
ng-blur="ctrl.onTargetBlur()">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<gf-form-switch class="gf-form" label="Use capture groups" checked="ctrl.target.useCaptureGroups" on-change="ctrl.onTargetBlur()">
|
|
||||||
</gf-form-switch>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Item IDs editor mode -->
|
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.ITEMID">
|
|
||||||
<div class="gf-form max-width-20">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Item IDs</label>
|
|
||||||
<input type="text"
|
|
||||||
ng-model="ctrl.target.itemids"
|
|
||||||
bs-typeahead="ctrl.getVariables"
|
|
||||||
ng-blur="ctrl.onTargetBlur()"
|
|
||||||
data-min-length=0
|
|
||||||
data-items=100
|
|
||||||
class="gf-form-input"
|
|
||||||
ng-class="{
|
|
||||||
'zbx-variable': ctrl.isVariable(ctrl.target.itServiceFilter),
|
'zbx-variable': ctrl.isVariable(ctrl.target.itServiceFilter),
|
||||||
'zbx-regex': ctrl.isRegex(ctrl.target.itServiceFilter)
|
'zbx-regex': ctrl.isRegex(ctrl.target.itServiceFilter)
|
||||||
}">
|
}">
|
||||||
</input>
|
</input>
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Metric processing functions -->
|
|
||||||
<div class="gf-form-inline" ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.ITEMID || ctrl.target.queryType == editorMode.ITSERVICE">
|
|
||||||
<div class="gf-form">
|
|
||||||
<label class="gf-form-label query-keyword width-7">Functions</label>
|
|
||||||
</div>
|
|
||||||
<div ng-repeat="func in ctrl.target.functions" class="gf-form">
|
|
||||||
<span zabbix-function-editor class="gf-form-label query-part" ng-hide="func.hidden"></span>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form dropdown" add-metric-function>
|
|
||||||
</div>
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<div class="gf-form-label gf-form-label--grow"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form gf-form--grow">
|
|
||||||
<label class="gf-form-label gf-form-label--grow">
|
|
||||||
<a ng-click="ctrl.toggleQueryOptions()">
|
|
||||||
<i class="fa fa-caret-down" ng-show="ctrl.showQueryOptions"></i>
|
|
||||||
<i class="fa fa-caret-right" ng-hide="ctrl.showQueryOptions"></i>
|
|
||||||
{{ctrl.queryOptionsText}}
|
|
||||||
</a>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Query options -->
|
|
||||||
<div class="gf-form-group offset-width-7" ng-if="ctrl.showQueryOptions">
|
|
||||||
<div ng-hide="ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
|
||||||
<gf-form-switch class="gf-form" label-class="width-10"
|
|
||||||
label="Show disabled items"
|
|
||||||
checked="ctrl.target.options.showDisabledItems"
|
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
|
||||||
</gf-form-switch>
|
|
||||||
<gf-form-switch class="gf-form" label-class="width-10"
|
|
||||||
label="Use Zabbix value mapping"
|
|
||||||
checked="ctrl.target.options.useZabbixValueMapping"
|
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
|
||||||
</gf-form-switch>
|
|
||||||
<gf-form-switch class="gf-form" label-class="width-10"
|
|
||||||
label="Disable data alignment"
|
|
||||||
checked="ctrl.target.options.disableDataAlignment"
|
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
|
||||||
</gf-form-switch>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form-group offset-width-7" ng-show="ctrl.target.queryType === editorMode.TEXT && ctrl.target.resultFormat === 'table'">
|
|
||||||
<div class="gf-form">
|
|
||||||
<gf-form-switch class="gf-form" label-class="width-10"
|
|
||||||
label="Skip empty values"
|
|
||||||
checked="ctrl.target.options.skipEmptyValues"
|
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
|
||||||
</gf-form-switch>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="gf-form-group" ng-show="ctrl.target.queryType == editorMode.PROBLEMS || ctrl.target.queryType == editorMode.TRIGGERS">
|
|
||||||
<gf-form-switch class="gf-form" ng-show="ctrl.target.queryType == editorMode.TRIGGERS"
|
|
||||||
label-class="width-9"
|
|
||||||
label="Count"
|
|
||||||
checked="ctrl.target.triggers.count"
|
|
||||||
on-change="ctrl.onTargetBlur()">
|
|
||||||
</gf-form-switch>
|
|
||||||
<div class="gf-form" ng-show="ctrl.target.queryType == editorMode.PROBLEMS || ctrl.target.queryType == editorMode.TRIGGERS">
|
|
||||||
<label class="gf-form-label width-9">Acknowledged</label>
|
|
||||||
<div class="gf-form-select-wrapper width-12">
|
|
||||||
<select class="gf-form-input"
|
|
||||||
ng-change="ctrl.onQueryOptionChange()"
|
|
||||||
ng-model="ctrl.target.options.acknowledged"
|
|
||||||
ng-options="a.value as a.text for a in ctrl.ackFilters">
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="gf-form gf-form--grow">
|
||||||
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
<!-- Metric processing functions -->
|
||||||
|
<div class="gf-form-inline"
|
||||||
|
ng-show="ctrl.target.queryType == editorMode.METRICS || ctrl.target.queryType == editorMode.ITEMID || ctrl.target.queryType == editorMode.ITSERVICE">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<label class="gf-form-label width-9">Sort by</label>
|
<label class="gf-form-label query-keyword width-7">Functions</label>
|
||||||
<div class="gf-form-select-wrapper width-12">
|
|
||||||
<select class="gf-form-input"
|
|
||||||
ng-model="ctrl.target.options.sortProblems"
|
|
||||||
ng-options="f.value as f.text for f in ctrl.sortByFields"
|
|
||||||
ng-change="ctrl.onQueryOptionChange()">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<gf-form-switch class="gf-form"
|
<div ng-repeat="func in ctrl.target.functions" class="gf-form">
|
||||||
label-class="width-9"
|
<span zabbix-function-editor class="gf-form-label query-part" ng-hide="func.hidden"></span>
|
||||||
label="Use time range"
|
</div>
|
||||||
checked="ctrl.target.options.useTimeRange"
|
<div class="gf-form dropdown" add-metric-function>
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
</div>
|
||||||
</gf-form-switch>
|
<div class="gf-form gf-form--grow">
|
||||||
<gf-form-switch class="gf-form"
|
<div class="gf-form-label gf-form-label--grow"></div>
|
||||||
label-class="width-9"
|
</div>
|
||||||
label="Hosts in maintenance"
|
</div>
|
||||||
checked="ctrl.target.options.hostsInMaintenance"
|
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
<div class="gf-form gf-form--grow">
|
||||||
</gf-form-switch>
|
<label class="gf-form-label gf-form-label--grow">
|
||||||
<gf-form-switch class="gf-form"
|
<a ng-click="ctrl.toggleQueryOptions()">
|
||||||
label-class="width-9"
|
<i class="fa fa-caret-down" ng-show="ctrl.showQueryOptions"></i>
|
||||||
label="Host proxy"
|
<i class="fa fa-caret-right" ng-hide="ctrl.showQueryOptions"></i>
|
||||||
checked="ctrl.target.options.hostProxy"
|
{{ctrl.queryOptionsText}}
|
||||||
on-change="ctrl.onQueryOptionChange()">
|
</a>
|
||||||
</gf-form-switch>
|
</label>
|
||||||
<div class="gf-form">
|
</div>
|
||||||
<label class="gf-form-label width-9">Limit triggers</label>
|
|
||||||
<input class="gf-form-input width-5"
|
<!-- Query options -->
|
||||||
type="number" placeholder="100"
|
<div class="gf-form-group offset-width-7" ng-if="ctrl.showQueryOptions">
|
||||||
ng-model="ctrl.target.options.limit"
|
<div ng-hide="ctrl.target.queryType == editorMode.TRIGGERS || ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
ng-model-onblur ng-change="ctrl.onQueryOptionChange()">
|
<gf-form-switch class="gf-form" label-class="width-10"
|
||||||
|
label="Show disabled items"
|
||||||
|
checked="ctrl.target.options.showDisabledItems"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<gf-form-switch class="gf-form" label-class="width-10"
|
||||||
|
label="Use Zabbix value mapping"
|
||||||
|
checked="ctrl.target.options.useZabbixValueMapping"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<gf-form-switch class="gf-form" label-class="width-10"
|
||||||
|
label="Disable data alignment"
|
||||||
|
checked="ctrl.target.options.disableDataAlignment"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-group offset-width-7"
|
||||||
|
ng-show="ctrl.target.queryType === editorMode.TEXT && ctrl.target.resultFormat === 'table'">
|
||||||
|
<div class="gf-form">
|
||||||
|
<gf-form-switch class="gf-form" label-class="width-10"
|
||||||
|
label="Skip empty values"
|
||||||
|
checked="ctrl.target.options.skipEmptyValues"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gf-form-group"
|
||||||
|
ng-show="ctrl.target.queryType == editorMode.PROBLEMS || ctrl.target.queryType == editorMode.TRIGGERS">
|
||||||
|
<gf-form-switch class="gf-form" ng-show="ctrl.target.queryType == editorMode.TRIGGERS"
|
||||||
|
label-class="width-9"
|
||||||
|
label="Count"
|
||||||
|
checked="ctrl.target.triggers.count"
|
||||||
|
on-change="ctrl.onTargetBlur()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<div class="gf-form"
|
||||||
|
ng-show="ctrl.target.queryType == editorMode.PROBLEMS || ctrl.target.queryType == editorMode.TRIGGERS">
|
||||||
|
<label class="gf-form-label width-9">Acknowledged</label>
|
||||||
|
<div class="gf-form-select-wrapper width-12">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-change="ctrl.onQueryOptionChange()"
|
||||||
|
ng-model="ctrl.target.options.acknowledged"
|
||||||
|
ng-options="a.value as a.text for a in ctrl.ackFilters">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-show="ctrl.target.queryType == editorMode.PROBLEMS">
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-9">Sort by</label>
|
||||||
|
<div class="gf-form-select-wrapper width-12">
|
||||||
|
<select class="gf-form-input"
|
||||||
|
ng-model="ctrl.target.options.sortProblems"
|
||||||
|
ng-options="f.value as f.text for f in ctrl.sortByFields"
|
||||||
|
ng-change="ctrl.onQueryOptionChange()">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<gf-form-switch class="gf-form"
|
||||||
|
label-class="width-9"
|
||||||
|
label="Use time range"
|
||||||
|
checked="ctrl.target.options.useTimeRange"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<gf-form-switch class="gf-form"
|
||||||
|
label-class="width-9"
|
||||||
|
label="Hosts in maintenance"
|
||||||
|
checked="ctrl.target.options.hostsInMaintenance"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<gf-form-switch class="gf-form"
|
||||||
|
label-class="width-9"
|
||||||
|
label="Host proxy"
|
||||||
|
checked="ctrl.target.options.hostProxy"
|
||||||
|
on-change="ctrl.onQueryOptionChange()">
|
||||||
|
</gf-form-switch>
|
||||||
|
<div class="gf-form">
|
||||||
|
<label class="gf-form-label width-9">Limit triggers</label>
|
||||||
|
<input class="gf-form-input width-5"
|
||||||
|
type="number" placeholder="100"
|
||||||
|
ng-model="ctrl.target.options.limit"
|
||||||
|
ng-model-onblur ng-change="ctrl.onQueryOptionChange()">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</query-editor-row>
|
</query-editor-row>
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { QueryCtrl } from 'grafana/app/plugins/sdk';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import * as c from './constants';
|
import * as c from './constants';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
|
import { itemTagToString } from './utils';
|
||||||
import * as metricFunctions from './metricFunctions';
|
import * as metricFunctions from './metricFunctions';
|
||||||
import * as migrations from './migrations';
|
import * as migrations from './migrations';
|
||||||
import { ShowProblemTypes } from './types';
|
import { ShowProblemTypes, ZBXItem, ZBXItemTag } from './types';
|
||||||
import { CURRENT_SCHEMA_VERSION } from '../panel-triggers/migrations';
|
import { CURRENT_SCHEMA_VERSION } from '../panel-triggers/migrations';
|
||||||
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ function getTargetDefaults() {
|
|||||||
group: { 'filter': "" },
|
group: { 'filter': "" },
|
||||||
host: { 'filter': "" },
|
host: { 'filter': "" },
|
||||||
application: { 'filter': "" },
|
application: { 'filter': "" },
|
||||||
|
itemTag: { 'filter': "" },
|
||||||
item: { 'filter': "" },
|
item: { 'filter': "" },
|
||||||
functions: [],
|
functions: [],
|
||||||
triggers: {
|
triggers: {
|
||||||
@@ -274,7 +276,11 @@ export class ZabbixQueryController extends QueryCtrl {
|
|||||||
promises.push(this.suggestProxies());
|
promises.push(this.suggestProxies());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises).then(() => {
|
||||||
|
if (this.zabbix.isZabbix54OrHigher()) {
|
||||||
|
this.suggestItemTags();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initDefaultQueryMode(target) {
|
initDefaultQueryMode(target) {
|
||||||
@@ -304,12 +310,23 @@ export class ZabbixQueryController extends QueryCtrl {
|
|||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getItemTags = () => {
|
||||||
|
if (!this.metric?.tagList) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.metric.tagList.map(t => itemTagToString(t));
|
||||||
|
};
|
||||||
|
|
||||||
getTemplateVariables() {
|
getTemplateVariables() {
|
||||||
return _.map(this.templateSrv.getVariables(), variable => {
|
return _.map(this.templateSrv.getVariables(), variable => {
|
||||||
return '$' + variable.name;
|
return '$' + variable.name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isZabbix54OrHigher() {
|
||||||
|
return this.zabbix.isZabbix54OrHigher();
|
||||||
|
}
|
||||||
|
|
||||||
suggestGroups() {
|
suggestGroups() {
|
||||||
return this.zabbix.getAllGroups()
|
return this.zabbix.getAllGroups()
|
||||||
.then(groups => {
|
.then(groups => {
|
||||||
@@ -341,19 +358,34 @@ export class ZabbixQueryController extends QueryCtrl {
|
|||||||
const groupFilter = this.replaceTemplateVars(this.target.group.filter);
|
const groupFilter = this.replaceTemplateVars(this.target.group.filter);
|
||||||
const hostFilter = this.replaceTemplateVars(this.target.host.filter);
|
const hostFilter = this.replaceTemplateVars(this.target.host.filter);
|
||||||
const appFilter = this.replaceTemplateVars(this.target.application.filter);
|
const appFilter = this.replaceTemplateVars(this.target.application.filter);
|
||||||
|
const itemTagFilter = this.replaceTemplateVars(this.target.itemTag.filter);
|
||||||
const options = {
|
const options = {
|
||||||
itemtype: itemtype,
|
itemtype: itemtype,
|
||||||
showDisabledItems: this.target.options.showDisabledItems
|
showDisabledItems: this.target.options.showDisabledItems
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.zabbix
|
return this.zabbix
|
||||||
.getAllItems(groupFilter, hostFilter, appFilter, options)
|
.getAllItems(groupFilter, hostFilter, appFilter, itemTagFilter, options)
|
||||||
.then(items => {
|
.then(items => {
|
||||||
this.metric.itemList = items;
|
this.metric.itemList = items;
|
||||||
return items;
|
return items;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async suggestItemTags() {
|
||||||
|
const groupFilter = this.replaceTemplateVars(this.target.group.filter);
|
||||||
|
const hostFilter = this.replaceTemplateVars(this.target.host.filter);
|
||||||
|
const items = await this.zabbix.getAllItems(groupFilter, hostFilter, null, null, {});
|
||||||
|
const tags: ZBXItemTag[] = _.flatten(items.map((item: ZBXItem) => {
|
||||||
|
if (item.tags) {
|
||||||
|
return item.tags;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
this.metric.tagList = _.uniqBy(tags, t => t.tag + t.value || '');
|
||||||
|
}
|
||||||
|
|
||||||
suggestITServices() {
|
suggestITServices() {
|
||||||
return this.zabbix.getITService()
|
return this.zabbix.getITService()
|
||||||
.then(itservices => {
|
.then(itservices => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SelectableValue, DataQuery, DataSourceJsonData } from "@grafana/data";
|
import { DataQuery, DataSourceJsonData, SelectableValue } from "@grafana/data";
|
||||||
|
|
||||||
export interface ZabbixDSOptions extends DataSourceJsonData {
|
export interface ZabbixDSOptions extends DataSourceJsonData {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -40,6 +40,7 @@ export interface ZabbixMetricsQuery extends DataQuery {
|
|||||||
group: { filter: string; name?: string; };
|
group: { filter: string; name?: string; };
|
||||||
host: { filter: string; name?: string; };
|
host: { filter: string; name?: string; };
|
||||||
application: { filter: string; name?: string; };
|
application: { filter: string; name?: string; };
|
||||||
|
itemTag: { filter: string; name?: string; };
|
||||||
item: { filter: string; name?: string; };
|
item: { filter: string; name?: string; };
|
||||||
textFilter: string;
|
textFilter: string;
|
||||||
mode: number;
|
mode: number;
|
||||||
@@ -87,7 +88,9 @@ export interface TemplateSrv {
|
|||||||
variables: {
|
variables: {
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
highlightVariablesAsHtml(str: any): any;
|
highlightVariablesAsHtml(str: any): any;
|
||||||
|
|
||||||
replace(target: any, scopedVars?: any, format?: any): any;
|
replace(target: any, scopedVars?: any, format?: any): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +296,12 @@ export interface ZBXItem {
|
|||||||
name: string;
|
name: string;
|
||||||
key_: string;
|
key_: string;
|
||||||
lastvalue?: string;
|
lastvalue?: string;
|
||||||
|
tags?: ZBXItemTag[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ZBXItemTag {
|
||||||
|
tag: string;
|
||||||
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ZBXEvent {
|
export interface ZBXEvent {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import * as c from './constants';
|
import * as c from './constants';
|
||||||
import { VariableQuery, VariableQueryTypes } from './types';
|
import { VariableQuery, VariableQueryTypes, ZBXItemTag } from './types';
|
||||||
import { DataFrame, FieldType, getValueFormats, MappingType, rangeUtil, ValueMapping } from '@grafana/data';
|
import { DataFrame, FieldType, getValueFormats, MappingType, rangeUtil, ValueMapping } from '@grafana/data';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -405,6 +405,21 @@ export function parseTags(tagStr: string): any[] {
|
|||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parses string representation of tag into the object
|
||||||
|
export function parseItemTag(tagStr: string): ZBXItemTag {
|
||||||
|
const itemTag: ZBXItemTag = { tag: '', value: '' };
|
||||||
|
const tagParts = tagStr.split(': ');
|
||||||
|
itemTag.tag = tagParts[0];
|
||||||
|
if (tagParts[1]) {
|
||||||
|
itemTag.value = tagParts[1];
|
||||||
|
}
|
||||||
|
return itemTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function itemTagToString(t: ZBXItemTag): string {
|
||||||
|
return t.value ? `${t.tag}: ${t.value}` : t.tag;
|
||||||
|
}
|
||||||
|
|
||||||
export function mustArray(result: any): any[] {
|
export function mustArray(result: any): any[] {
|
||||||
return result || [];
|
return result || [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import _ from 'lodash';
|
|||||||
import semver from 'semver';
|
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 { ZBX_ACK_ACTION_NONE, ZBX_ACK_ACTION_ADD_MESSAGE, MIN_SLA_INTERVAL } from '../../../constants';
|
import { MIN_SLA_INTERVAL, ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_NONE } from '../../../constants';
|
||||||
import { ShowProblemTypes, ZBXProblem } from '../../../types';
|
import { ShowProblemTypes, ZBXProblem } from '../../../types';
|
||||||
import { JSONRPCError, ZBXScript, APIExecuteScriptResponse } from './types';
|
import { APIExecuteScriptResponse, JSONRPCError, ZBXScript } from './types';
|
||||||
import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
|
import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
|
||||||
import { rangeUtil } from '@grafana/data';
|
import { rangeUtil } from '@grafana/data';
|
||||||
|
|
||||||
@@ -106,6 +106,10 @@ export class ZabbixAPIConnector {
|
|||||||
return this.getVersionPromise;
|
return this.getVersionPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isZabbix54OrHigher() {
|
||||||
|
return semver.gte(this.version, '5.4.0');
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
// Zabbix API method wrappers //
|
// Zabbix API method wrappers //
|
||||||
////////////////////////////////
|
////////////////////////////////
|
||||||
@@ -151,7 +155,7 @@ export class ZabbixAPIConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getApps(hostids): Promise<any[]> {
|
async getApps(hostids): Promise<any[]> {
|
||||||
if (semver.gte(this.version, '5.4.0')) {
|
if (this.isZabbix54OrHigher()) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,12 +207,16 @@ export class ZabbixAPIConnector {
|
|||||||
params.filter.value_type = [1, 2, 4];
|
params.filter.value_type = [1, 2, 4];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isZabbix54OrHigher()) {
|
||||||
|
params.selectTags = 'extend';
|
||||||
|
}
|
||||||
|
|
||||||
return this.request('item.get', params)
|
return this.request('item.get', params)
|
||||||
.then(utils.expandItems);
|
.then(utils.expandItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemsByIDs(itemids) {
|
getItemsByIDs(itemids) {
|
||||||
const params = {
|
const params: any = {
|
||||||
itemids: itemids,
|
itemids: itemids,
|
||||||
output: [
|
output: [
|
||||||
'name',
|
'name',
|
||||||
@@ -225,6 +233,10 @@ export class ZabbixAPIConnector {
|
|||||||
selectHosts: ['hostid', 'name']
|
selectHosts: ['hostid', 'name']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.isZabbix54OrHigher()) {
|
||||||
|
params.selectTags = 'extend';
|
||||||
|
}
|
||||||
|
|
||||||
return this.request('item.get', params)
|
return this.request('item.get', params)
|
||||||
.then(items => utils.expandItems(items));
|
.then(items => utils.expandItems(items));
|
||||||
}
|
}
|
||||||
@@ -438,7 +450,7 @@ export class ZabbixAPIConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTriggers(groupids, hostids, applicationids, options) {
|
getTriggers(groupids, hostids, applicationids, options) {
|
||||||
const {showTriggers, maintenance, timeFrom, timeTo} = options;
|
const { showTriggers, maintenance, timeFrom, timeTo } = options;
|
||||||
|
|
||||||
const params: any = {
|
const params: any = {
|
||||||
output: 'extend',
|
output: 'extend',
|
||||||
@@ -605,7 +617,7 @@ export class ZabbixAPIConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHostAlerts(hostids, applicationids, options) {
|
getHostAlerts(hostids, applicationids, options) {
|
||||||
const {minSeverity, acknowledged, count, timeFrom, timeTo} = options;
|
const { minSeverity, acknowledged, count, timeFrom, timeTo } = options;
|
||||||
const params: any = {
|
const params: any = {
|
||||||
output: 'extend',
|
output: 'extend',
|
||||||
hostids: hostids,
|
hostids: hostids,
|
||||||
@@ -709,8 +721,8 @@ function buildSLAIntervals(timeRange, interval) {
|
|||||||
|
|
||||||
for (let i = timeFrom; i <= timeTo - interval; i += interval) {
|
for (let i = timeFrom; i <= timeTo - interval; i += interval) {
|
||||||
intervals.push({
|
intervals.push({
|
||||||
from : i,
|
from: i,
|
||||||
to : (i + interval)
|
to: (i + interval)
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface ZabbixConnector {
|
|||||||
getGroups: (groupFilter?) => any;
|
getGroups: (groupFilter?) => any;
|
||||||
getHosts: (groupFilter?, hostFilter?) => any;
|
getHosts: (groupFilter?, hostFilter?) => any;
|
||||||
getApps: (groupFilter?, hostFilter?, appFilter?) => any;
|
getApps: (groupFilter?, hostFilter?, appFilter?) => any;
|
||||||
getItems: (groupFilter?, hostFilter?, appFilter?, itemFilter?, options?) => any;
|
getItems: (groupFilter?, hostFilter?, appFilter?, itemTagFilter?, itemFilter?, options?) => any;
|
||||||
getSLA: (itservices, timeRange, target, options?) => any;
|
getSLA: (itservices, timeRange, target, options?) => any;
|
||||||
|
|
||||||
supportsApplications: () => boolean;
|
supportsApplications: () => boolean;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import _ from 'lodash';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import * as utils from '../utils';
|
import * as utils from '../utils';
|
||||||
|
import { itemTagToString } from '../utils';
|
||||||
import responseHandler from '../responseHandler';
|
import responseHandler from '../responseHandler';
|
||||||
import { CachingProxy } from './proxy/cachingProxy';
|
import { CachingProxy } from './proxy/cachingProxy';
|
||||||
import { DBConnector } from './connectors/dbConnector';
|
import { DBConnector } from './connectors/dbConnector';
|
||||||
@@ -10,7 +11,7 @@ import { SQLConnector } from './connectors/sql/sqlConnector';
|
|||||||
import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector';
|
import { InfluxDBConnector } from './connectors/influxdb/influxdbConnector';
|
||||||
import { ZabbixConnector } from './types';
|
import { ZabbixConnector } from './types';
|
||||||
import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler';
|
import { joinTriggersWithEvents, joinTriggersWithProblems } from '../problemsHandler';
|
||||||
import { ProblemDTO } from '../types';
|
import { ProblemDTO, ZBXItemTag } from '../types';
|
||||||
|
|
||||||
interface AppsResponse extends Array<any> {
|
interface AppsResponse extends Array<any> {
|
||||||
appFilterEmpty?: boolean;
|
appFilterEmpty?: boolean;
|
||||||
@@ -30,7 +31,7 @@ const REQUESTS_TO_CACHE = [
|
|||||||
const REQUESTS_TO_BIND = [
|
const REQUESTS_TO_BIND = [
|
||||||
'getHistory', 'getTrend', 'getMacros', 'getItemsByIDs', 'getEvents', 'getAlerts', 'getHostAlerts',
|
'getHistory', 'getTrend', 'getMacros', 'getItemsByIDs', 'getEvents', 'getAlerts', 'getHostAlerts',
|
||||||
'getAcknowledges', 'getITService', 'acknowledgeEvent', 'getProxies', 'getEventAlerts',
|
'getAcknowledges', 'getITService', 'acknowledgeEvent', 'getProxies', 'getEventAlerts',
|
||||||
'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings'
|
'getExtendedEventData', 'getScripts', 'executeScript', 'getValueMappings', 'isZabbix54OrHigher'
|
||||||
];
|
];
|
||||||
|
|
||||||
export class Zabbix implements ZabbixConnector {
|
export class Zabbix implements ZabbixConnector {
|
||||||
@@ -56,6 +57,7 @@ export class Zabbix implements ZabbixConnector {
|
|||||||
getExtendedEventData: (eventids) => Promise<any>;
|
getExtendedEventData: (eventids) => Promise<any>;
|
||||||
getMacros: (hostids: any[]) => Promise<any>;
|
getMacros: (hostids: any[]) => Promise<any>;
|
||||||
getValueMappings: () => Promise<any>;
|
getValueMappings: () => Promise<any>;
|
||||||
|
isZabbix54OrHigher: () => boolean;
|
||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
const {
|
const {
|
||||||
@@ -180,7 +182,7 @@ export class Zabbix implements ZabbixConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getItemsFromTarget(target, options) {
|
getItemsFromTarget(target, options) {
|
||||||
const parts = ['group', 'host', 'application', 'item'];
|
const parts = ['group', 'host', 'application', 'itemTag', 'item'];
|
||||||
const filters = _.map(parts, p => target[p].filter);
|
const filters = _.map(parts, p => target[p].filter);
|
||||||
return this.getItems(...filters, options);
|
return this.getItems(...filters, options);
|
||||||
}
|
}
|
||||||
@@ -261,24 +263,36 @@ export class Zabbix implements ZabbixConnector {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllItems(groupFilter, hostFilter, appFilter, options: any = {}) {
|
async getAllItems(groupFilter, hostFilter, appFilter, itemTagFilter, options: any = {}) {
|
||||||
return this.getApps(groupFilter, hostFilter, appFilter)
|
const apps = await this.getApps(groupFilter, hostFilter, appFilter);
|
||||||
.then(apps => {
|
let items: any[];
|
||||||
|
|
||||||
|
if (this.isZabbix54OrHigher()) {
|
||||||
|
items = await this.zabbixAPI.getItems(apps.hostids, undefined, options.itemtype);
|
||||||
|
if (itemTagFilter) {
|
||||||
|
items = items.filter(item => {
|
||||||
|
if (item.tags) {
|
||||||
|
const tags: ZBXItemTag[] = item.tags.map(t => itemTagToString(t));
|
||||||
|
return tags.includes(itemTagFilter);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (apps.appFilterEmpty) {
|
if (apps.appFilterEmpty) {
|
||||||
return this.zabbixAPI.getItems(apps.hostids, undefined, options.itemtype);
|
items = await this.zabbixAPI.getItems(apps.hostids, undefined, options.itemtype);
|
||||||
} else {
|
} else {
|
||||||
const appids = _.map(apps, 'applicationid');
|
const appids = _.map(apps, 'applicationid');
|
||||||
return this.zabbixAPI.getItems(undefined, appids, options.itemtype);
|
items = await this.zabbixAPI.getItems(undefined, appids, options.itemtype);
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(items => {
|
|
||||||
if (!options.showDisabledItems) {
|
|
||||||
items = _.filter(items, { 'status': '0' });
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return items;
|
if (!options.showDisabledItems) {
|
||||||
})
|
items = _.filter(items, { 'status': '0' });
|
||||||
.then(this.expandUserMacro.bind(this));
|
}
|
||||||
|
|
||||||
|
return await this.expandUserMacro(items, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
expandUserMacro(items, isTriggerItem) {
|
expandUserMacro(items, isTriggerItem) {
|
||||||
@@ -298,13 +312,13 @@ export class Zabbix implements ZabbixConnector {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getItems(groupFilter?, hostFilter?, appFilter?, itemFilter?, options = {}) {
|
getItems(groupFilter?, hostFilter?, appFilter?, itemTagFilter?, itemFilter?, options = {}) {
|
||||||
return this.getAllItems(groupFilter, hostFilter, appFilter, options)
|
return this.getAllItems(groupFilter, hostFilter, appFilter, itemTagFilter, options)
|
||||||
.then(items => filterByQuery(items, itemFilter));
|
.then(items => filterByQuery(items, itemFilter));
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemValues(groupFilter?, hostFilter?, appFilter?, itemFilter?, options: any = {}) {
|
getItemValues(groupFilter?, hostFilter?, appFilter?, itemFilter?, options: any = {}) {
|
||||||
return this.getItems(groupFilter, hostFilter, appFilter, itemFilter, options).then(items => {
|
return this.getItems(groupFilter, hostFilter, appFilter, null, itemFilter, options).then(items => {
|
||||||
let timeRange = [moment().subtract(2, 'h').unix(), moment().unix()];
|
let timeRange = [moment().subtract(2, 'h').unix(), moment().unix()];
|
||||||
if (options.range) {
|
if (options.range) {
|
||||||
timeRange = [options.range.from.unix(), options.range.to.unix()];
|
timeRange = [options.range.from.unix(), options.range.to.unix()];
|
||||||
|
|||||||
Reference in New Issue
Block a user