From e7a386cf8a99a809741990a4f8e2930a446f9b45 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Mon, 24 May 2021 15:31:56 +0300 Subject: [PATCH] Refactor history/trend queries --- pkg/datasource/response_handler.go | 29 +++++++++++- pkg/datasource/zabbix.go | 57 +++-------------------- pkg/zabbix/methods.go | 74 ++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 51 deletions(-) diff --git a/pkg/datasource/response_handler.go b/pkg/datasource/response_handler.go index 120e8b0..576fe1f 100644 --- a/pkg/datasource/response_handler.go +++ b/pkg/datasource/response_handler.go @@ -2,6 +2,7 @@ package datasource import ( "fmt" + "strconv" "time" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix" @@ -9,7 +10,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/data" ) -func convertHistoryToDataFrame(history History, items zabbix.Items) *data.Frame { +func convertHistoryToDataFrame(history zabbix.History, items zabbix.Items) *data.Frame { timeFileld := data.NewFieldFromFieldType(data.FieldTypeTime, 0) timeFileld.Name = "time" frame := data.NewFrame("History", timeFileld) @@ -48,3 +49,29 @@ func convertHistoryToDataFrame(history History, items zabbix.Items) *data.Frame } return wideFrame } + +func convertTrendToHistory(trend zabbix.Trend, valueType string) (zabbix.History, error) { + history := make([]zabbix.HistoryPoint, 0) + for _, point := range trend { + valueStr := point.ValueAvg + switch valueType { + case "min": + valueStr = point.ValueMin + case "max": + valueStr = point.ValueMax + } + + value, err := strconv.ParseFloat(valueStr, 64) + if err != nil { + return nil, fmt.Errorf("error parsing trend value: %s", err) + } + + history = append(history, zabbix.HistoryPoint{ + ItemID: point.ItemID, + Clock: point.Clock, + Value: value, + }) + } + + return history, nil +} diff --git a/pkg/datasource/zabbix.go b/pkg/datasource/zabbix.go index b987fb1..74ba844 100644 --- a/pkg/datasource/zabbix.go +++ b/pkg/datasource/zabbix.go @@ -1,12 +1,9 @@ package datasource import ( - "encoding/json" - "fmt" "time" "github.com/alexanderzobnin/grafana-zabbix/pkg/zabbix" - simplejson "github.com/bitly/go-simplejson" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" "golang.org/x/net/context" @@ -71,7 +68,7 @@ func (ds *ZabbixDatasourceInstance) queryNumericDataForItems(ctx context.Context consolidateBy = valueType } - history, err := ds.getHistotyOrTrend(ctx, query, items) + history, err := ds.getHistotyOrTrend(ctx, query, items, valueType) if err != nil { return nil, err } @@ -103,59 +100,19 @@ func (ds *ZabbixDatasourceInstance) getConsolidateBy(query *QueryModel) string { return consolidateBy } -func (ds *ZabbixDatasourceInstance) getHistotyOrTrend(ctx context.Context, query *QueryModel, items zabbix.Items) (History, error) { +func (ds *ZabbixDatasourceInstance) getHistotyOrTrend(ctx context.Context, query *QueryModel, items zabbix.Items, trendValueType string) (zabbix.History, error) { timeRange := query.TimeRange useTrend := ds.isUseTrend(timeRange) - allHistory := History{} - - groupedItems := map[int]zabbix.Items{} - - for _, j := range items { - groupedItems[j.ValueType] = append(groupedItems[j.ValueType], j) - } - - for k, l := range groupedItems { - var itemids []string - for _, m := range l { - itemids = append(itemids, m.ID) - } - - params := zabbix.ZabbixAPIParams{ - "output": "extend", - "sortfield": "clock", - "sortorder": "ASC", - "itemids": itemids, - "time_from": timeRange.From.Unix(), - "time_till": timeRange.To.Unix(), - } - - var response *simplejson.Json - var err error - if useTrend { - response, err = ds.zabbix.Request(ctx, &zabbix.ZabbixAPIRequest{Method: "trend.get", Params: params}) - } else { - params["history"] = &k - response, err = ds.zabbix.Request(ctx, &zabbix.ZabbixAPIRequest{Method: "history.get", Params: params}) - } + if useTrend { + result, err := ds.zabbix.GetTrend(ctx, items, timeRange) if err != nil { return nil, err } - - pointJSON, err := response.MarshalJSON() - if err != nil { - return nil, fmt.Errorf("Internal error parsing response JSON: %w", err) - } - - history := History{} - err = json.Unmarshal(pointJSON, &history) - if err != nil { - ds.logger.Error("Error handling history response", "error", err.Error()) - } else { - allHistory = append(allHistory, history...) - } + return convertTrendToHistory(result, trendValueType) } - return allHistory, nil + + return ds.zabbix.GetHistory(ctx, items, timeRange) } func (ds *ZabbixDatasourceInstance) isUseTrend(timeRange backend.TimeRange) bool { diff --git a/pkg/zabbix/methods.go b/pkg/zabbix/methods.go index d2356be..2dea862 100644 --- a/pkg/zabbix/methods.go +++ b/pkg/zabbix/methods.go @@ -2,8 +2,82 @@ package zabbix import ( "context" + + "github.com/grafana/grafana-plugin-sdk-go/backend" ) +func (ds *Zabbix) GetHistory(ctx context.Context, items []Item, timeRange backend.TimeRange) (History, error) { + history := History{} + // Zabbix stores history in different tables and `history` param required for query. So in one query it's only + // possible to get history for items with one type. In order to get history for items with multple types (numeric unsigned and numeric float), + // items should be grouped by the `value_type` field. + groupedItemids := make(map[int][]string, 0) + for _, item := range items { + groupedItemids[item.ValueType] = append(groupedItemids[item.ValueType], item.ID) + } + + for historyType, itemids := range groupedItemids { + result, err := ds.getHistory(ctx, itemids, historyType, timeRange) + if err != nil { + return nil, err + } + + history = append(history, result...) + } + + return history, nil +} + +func (ds *Zabbix) getHistory(ctx context.Context, itemids []string, historyType int, timeRange backend.TimeRange) (History, error) { + params := ZabbixAPIParams{ + "output": "extend", + "itemids": itemids, + "history": historyType, + "time_from": timeRange.From.Unix(), + "time_till": timeRange.To.Unix(), + "sortfield": "clock", + "sortorder": "ASC", + } + + result, err := ds.Request(ctx, &ZabbixAPIRequest{Method: "history.get", Params: params}) + if err != nil { + return nil, err + } + + var history History + err = convertTo(result, &history) + return history, err +} + +func (ds *Zabbix) GetTrend(ctx context.Context, items []Item, timeRange backend.TimeRange) (Trend, error) { + itemids := make([]string, 0) + for _, item := range items { + itemids = append(itemids, item.ID) + } + + return ds.getTrend(ctx, itemids, timeRange) +} + +func (ds *Zabbix) getTrend(ctx context.Context, itemids []string, timeRange backend.TimeRange) (Trend, error) { + params := ZabbixAPIParams{ + "output": "extend", + "itemids": itemids, + "time_from": timeRange.From.Unix(), + "time_till": timeRange.To.Unix(), + "sortfield": "clock", + "sortorder": "ASC", + } + + result, err := ds.Request(ctx, &ZabbixAPIRequest{Method: "trend.get", Params: params}) + if err != nil { + return nil, err + } + + var trend Trend + err = convertTo(result, &trend) + return trend, err +} + func (ds *Zabbix) GetItems(ctx context.Context, groupFilter string, hostFilter string, appFilter string, itemFilter string, itemType string) ([]Item, error) { hosts, err := ds.GetHosts(ctx, groupFilter, hostFilter) if err != nil {