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" ) // ZabbixAPIQuery handles query requests to Zabbix API func (ds *ZabbixDatasourceInstance) ZabbixAPIQuery(ctx context.Context, apiReq *zabbix.ZabbixAPIRequest) (*ZabbixAPIResourceResponse, error) { resultJson, err := ds.zabbix.Request(ctx, apiReq) if err != nil { return nil, err } result := resultJson.Interface() return BuildAPIResponse(&result) } func BuildAPIResponse(responseData *interface{}) (*ZabbixAPIResourceResponse, error) { return &ZabbixAPIResourceResponse{ Result: *responseData, }, nil } // TestConnection checks authentication and version of the Zabbix API and returns that info func (ds *ZabbixDatasourceInstance) TestConnection(ctx context.Context) (string, error) { _, err := ds.zabbix.GetAllGroups(ctx) if err != nil { return "", err } response, err := ds.zabbix.Request(ctx, &zabbix.ZabbixAPIRequest{Method: "apiinfo.version"}) if err != nil { return "", err } resultByte, _ := response.MarshalJSON() return string(resultByte), nil } func (ds *ZabbixDatasourceInstance) queryNumericItems(ctx context.Context, query *QueryModel) (*data.Frame, error) { groupFilter := query.Group.Filter hostFilter := query.Host.Filter appFilter := query.Application.Filter itemFilter := query.Item.Filter items, err := ds.zabbix.GetItems(ctx, groupFilter, hostFilter, appFilter, itemFilter, "num") 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.Items) (*data.Frame, error) { valueType := ds.getTrendValueType(query) consolidateBy := ds.getConsolidateBy(query) if consolidateBy == "" { consolidateBy = valueType } history, err := ds.getHistotyOrTrend(ctx, query, items) if err != nil { return nil, err } frame := convertHistoryToDataFrame(history, items) return frame, nil } func (ds *ZabbixDatasourceInstance) getTrendValueType(query *QueryModel) string { trendValue := "avg" for _, fn := range query.Functions { if fn.Def.Name == "trendValue" && len(fn.Params) > 0 { trendValue = fn.Params[0] } } return trendValue } func (ds *ZabbixDatasourceInstance) getConsolidateBy(query *QueryModel) string { consolidateBy := "avg" for _, fn := range query.Functions { if fn.Def.Name == "consolidateBy" && len(fn.Params) > 0 { consolidateBy = fn.Params[0] } } return consolidateBy } func (ds *ZabbixDatasourceInstance) getHistotyOrTrend(ctx context.Context, query *QueryModel, items zabbix.Items) (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 := 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 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 allHistory, nil } func (ds *ZabbixDatasourceInstance) isUseTrend(timeRange backend.TimeRange) bool { if !ds.Settings.Trends { return false } trendsFrom := ds.Settings.TrendsFrom trendsRange := ds.Settings.TrendsRange fromSec := timeRange.From.Unix() toSec := timeRange.To.Unix() rangeSec := float64(toSec - fromSec) if (fromSec < time.Now().Add(-trendsFrom).Unix()) || (rangeSec > trendsRange.Seconds()) { return true } return false }