diff --git a/pkg/datasource/datasource.go b/pkg/datasource/datasource.go index abef02e..035703e 100644 --- a/pkg/datasource/datasource.go +++ b/pkg/datasource/datasource.go @@ -122,15 +122,22 @@ func (ds *ZabbixDatasource) QueryData(ctx context.Context, req *backend.QueryDat ds.logger.Debug("DS query", "query", q) if err != nil { res.Error = err - } else if query.Mode != 0 { - res.Error = ErrNonMetricQueryNotSupported - } else { + } else if query.QueryType == MODE_METRICS { frames, err := zabbixDS.queryNumericItems(ctx, &query) if err != nil { res.Error = err } else { res.Frames = append(res.Frames, frames...) } + } else if query.QueryType == MODE_ITEMID { + frames, err := zabbixDS.queryItemIdData(ctx, &query) + if err != nil { + res.Error = err + } else { + res.Frames = append(res.Frames, frames...) + } + } else { + res.Error = ErrNonMetricQueryNotSupported } qdr.Responses[q.RefID] = res } diff --git a/pkg/datasource/models.go b/pkg/datasource/models.go index 9b50cfd..ec84f2d 100644 --- a/pkg/datasource/models.go +++ b/pkg/datasource/models.go @@ -8,6 +8,15 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" ) +const ( + MODE_METRICS = "0" + MODE_ITSERVICE = "1" + MODE_TEXT = "2" + MODE_ITEMID = "3" + MODE_TRIGGERS = "4" + MODE_PROBLEMS = "5" +) + // ZabbixDatasourceSettingsDTO model type ZabbixDatasourceSettingsDTO struct { Trends bool `json:"trends"` @@ -44,13 +53,19 @@ type ZabbixAPIResourceResponse struct { // QueryModel model type QueryModel struct { - Mode int64 `json:"mode"` - Group QueryFilter `json:"group"` - Host QueryFilter `json:"host"` - Application QueryFilter `json:"application"` - Item QueryFilter `json:"item"` - Functions []QueryFunction `json:"functions,omitempty"` - Options QueryOptions `json:"options"` + // Deprecated `mode` field, use QueryType instead + Mode int64 `json:"mode"` + + Group QueryFilter `json:"group"` + Host QueryFilter `json:"host"` + Application QueryFilter `json:"application"` + Item QueryFilter `json:"item"` + + // Item ID mode + ItemIDs string `json:"itemids,omitempty"` + + Functions []QueryFunction `json:"functions,omitempty"` + Options QueryOptions `json:"options"` // Direct from the gRPC interfaces RefID string `json:"-"` diff --git a/pkg/datasource/zabbix.go b/pkg/datasource/zabbix.go index 231c30b..345c5cb 100644 --- a/pkg/datasource/zabbix.go +++ b/pkg/datasource/zabbix.go @@ -1,6 +1,7 @@ package datasource import ( + "strings" "time" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix" @@ -60,6 +61,25 @@ func (ds *ZabbixDatasourceInstance) queryNumericItems(ctx context.Context, query return frames, nil } +func (ds *ZabbixDatasourceInstance) queryItemIdData(ctx context.Context, query *QueryModel) ([]*data.Frame, error) { + itemids := strings.Split(query.ItemIDs, ",") + for i, id := range itemids { + itemids[i] = strings.Trim(id, " ") + } + + items, err := ds.zabbix.GetItemsByIDs(ctx, itemids) + if err != nil { + return nil, err + } + + frames, err := ds.queryNumericDataForItems(ctx, query, items) + if err != nil { + return nil, err + } + + return frames, nil +} + func (ds *ZabbixDatasourceInstance) queryNumericDataForItems(ctx context.Context, query *QueryModel, items []*zabbix.Item) ([]*data.Frame, error) { trendValueType := ds.getTrendValueType(query) consolidateBy := ds.getConsolidateBy(query) diff --git a/pkg/zabbix/methods.go b/pkg/zabbix/methods.go index 9818814..2c465e9 100644 --- a/pkg/zabbix/methods.go +++ b/pkg/zabbix/methods.go @@ -274,6 +274,29 @@ func (ds *Zabbix) GetAllItems(ctx context.Context, hostids []string, appids []st return items, err } +func (ds *Zabbix) GetItemsByIDs(ctx context.Context, itemids []string) ([]*Item, error) { + params := ZabbixAPIParams{ + "itemids": itemids, + "output": []string{"itemid", "name", "key_", "value_type", "hostid", "status", "state", "units", "valuemapid", "delay"}, + "webitems": true, + "selectHosts": []string{"hostid", "name"}, + } + + result, err := ds.Request(ctx, &ZabbixAPIRequest{Method: "item.get", Params: params}) + if err != nil { + return nil, err + } + + var items []*Item + err = convertTo(result, &items) + if err != nil { + return nil, err + } + + items = expandItems(items) + return items, err +} + func (ds *Zabbix) GetAllApps(ctx context.Context, hostids []string) ([]Application, error) { params := ZabbixAPIParams{ "output": "extend", diff --git a/src/datasource-zabbix/constants.ts b/src/datasource-zabbix/constants.ts index 41d3776..164168f 100644 --- a/src/datasource-zabbix/constants.ts +++ b/src/datasource-zabbix/constants.ts @@ -7,12 +7,12 @@ export const DATAPOINT_VALUE = 0; export const DATAPOINT_TS = 1; // Editor modes -export const MODE_METRICS = 0; -export const MODE_ITSERVICE = 1; -export const MODE_TEXT = 2; -export const MODE_ITEMID = 3; -export const MODE_TRIGGERS = 4; -export const MODE_PROBLEMS = 5; +export const MODE_METRICS = '0'; +export const MODE_ITSERVICE = '1'; +export const MODE_TEXT = '2'; +export const MODE_ITEMID = '3'; +export const MODE_TRIGGERS = '4'; +export const MODE_PROBLEMS = '5'; // Triggers severity export const SEV_NOT_CLASSIFIED = 0; diff --git a/src/datasource-zabbix/datasource.ts b/src/datasource-zabbix/datasource.ts index c9042b8..dd942c2 100644 --- a/src/datasource-zabbix/datasource.ts +++ b/src/datasource-zabbix/datasource.ts @@ -100,15 +100,21 @@ export class ZabbixDatasource extends DataSourceApi): Promise | Observable { - const isMetricQuery = request.targets.every(q => q.queryType === c.MODE_METRICS || q.mode === c.MODE_METRICS); - if (isMetricQuery) { + // Migrate old targets + request.targets = request.targets.map(t => { + // Prevent changes of original object + const target = _.cloneDeep(t); + return migrations.migrate(target); + }); + + if (isBackendQuery(request)) { return this.backendQuery(request); } // Create request for each target - const promises = _.map(request.targets, t => { + const promises = _.map(request.targets, target => { // Don't request for hidden targets - if (t.hide) { + if (target.hide) { return []; } @@ -118,11 +124,6 @@ export class ZabbixDatasource extends DataSourceApi { func.params = _.map(func.params, param => { if (typeof param === 'number') { @@ -969,3 +970,10 @@ function getRequestTarget(request: DataQueryRequest, refId: string): any { } return null; } + +function isBackendQuery(request: DataQueryRequest): boolean { + return request.targets.every(q => + q.queryType === c.MODE_METRICS || + q.queryType === c.MODE_ITEMID + ); +} diff --git a/src/datasource-zabbix/migrations.ts b/src/datasource-zabbix/migrations.ts index ecd1846..0425aa0 100644 --- a/src/datasource-zabbix/migrations.ts +++ b/src/datasource-zabbix/migrations.ts @@ -49,6 +49,9 @@ function migrateQueryType(target) { delete target.mode; } } + + // queryType is a string in query model + target.queryType = (target.queryType as number).toString(); } function migrateSLA(target) { diff --git a/src/datasource-zabbix/query.controller.ts b/src/datasource-zabbix/query.controller.ts index 56c64bb..746bad2 100644 --- a/src/datasource-zabbix/query.controller.ts +++ b/src/datasource-zabbix/query.controller.ts @@ -82,7 +82,7 @@ export class ZabbixQueryController extends QueryCtrl { zabbix: any; replaceTemplateVars: any; templateSrv: TemplateSrv; - editorModes: Array<{ value: string; text: string; queryType: number; }>; + editorModes: Array<{ value: string; text: string; queryType: string; }>; slaPropertyList: Array<{ name: string; property: string; }>; slaIntervals: Array<{ text: string; value: string; }>; ackFilters: Array<{ text: string; value: number; }>;