* Fix querying for Zabbix v7.2+ * Update check from 7.2 to 7.0 * Fix also select acknowledges key * Remove unsused methods * release commit 4.6.0 --------- Co-authored-by: yesoreyeram <153843+yesoreyeram@users.noreply.github.com>
164 lines
4.6 KiB
Go
164 lines
4.6 KiB
Go
package zabbix
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/alexanderzobnin/grafana-zabbix/pkg/metrics"
|
|
"github.com/alexanderzobnin/grafana-zabbix/pkg/settings"
|
|
"github.com/alexanderzobnin/grafana-zabbix/pkg/zabbixapi"
|
|
"github.com/bitly/go-simplejson"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
|
)
|
|
|
|
// Zabbix is a wrapper for Zabbix API. It wraps Zabbix API queries and performs authentication, adds caching,
|
|
// deduplication and other performance optimizations.
|
|
type Zabbix struct {
|
|
api *zabbixapi.ZabbixAPI
|
|
dsInfo *backend.DataSourceInstanceSettings
|
|
settings *settings.ZabbixDatasourceSettings
|
|
cache *ZabbixCache
|
|
version int
|
|
logger log.Logger
|
|
}
|
|
|
|
// New returns new instance of Zabbix client.
|
|
func New(dsInfo *backend.DataSourceInstanceSettings, zabbixSettings *settings.ZabbixDatasourceSettings, zabbixAPI *zabbixapi.ZabbixAPI) (*Zabbix, error) {
|
|
logger := log.New()
|
|
zabbixCache := NewZabbixCache(zabbixSettings.CacheTTL, 10*time.Minute)
|
|
|
|
return &Zabbix{
|
|
api: zabbixAPI,
|
|
dsInfo: dsInfo,
|
|
settings: zabbixSettings,
|
|
cache: zabbixCache,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
func (zabbix *Zabbix) GetAPI() *zabbixapi.ZabbixAPI {
|
|
return zabbix.api
|
|
}
|
|
|
|
// Request wraps request with cache
|
|
func (ds *Zabbix) Request(ctx context.Context, apiReq *ZabbixAPIRequest) (*simplejson.Json, error) {
|
|
var resultJson *simplejson.Json
|
|
var err error
|
|
|
|
if ds.version == 0 {
|
|
version, err := ds.GetVersion(ctx)
|
|
if err != nil {
|
|
ds.logger.Error("Error querying Zabbix version", "error", err)
|
|
ds.version = -1
|
|
} else {
|
|
ds.logger.Debug("Got Zabbix version", "version", version)
|
|
ds.version = version
|
|
}
|
|
}
|
|
|
|
cachedResult, queryExistInCache := ds.cache.GetAPIRequest(apiReq)
|
|
if !queryExistInCache {
|
|
resultJson, err = ds.request(ctx, apiReq.Method, apiReq.Params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if IsCachedRequest(apiReq.Method) {
|
|
ds.logger.Debug("Writing result to cache", "method", apiReq.Method)
|
|
ds.cache.SetAPIRequest(apiReq, resultJson)
|
|
}
|
|
} else {
|
|
metrics.CacheHitTotal.WithLabelValues(apiReq.Method).Inc()
|
|
var ok bool
|
|
resultJson, ok = cachedResult.(*simplejson.Json)
|
|
if !ok {
|
|
resultJson = simplejson.New()
|
|
}
|
|
}
|
|
|
|
return resultJson, nil
|
|
}
|
|
|
|
// request checks authentication and makes a request to the Zabbix API.
|
|
func (zabbix *Zabbix) request(ctx context.Context, method string, params ZabbixAPIParams) (*simplejson.Json, error) {
|
|
zabbix.logger.Debug("Zabbix request", "method", method)
|
|
|
|
// Skip auth for methods that are not required it
|
|
if method == "apiinfo.version" {
|
|
return zabbix.api.RequestUnauthenticated(ctx, method, params, zabbix.version)
|
|
}
|
|
|
|
result, err := zabbix.api.Request(ctx, method, params, zabbix.version)
|
|
notAuthorized := isNotAuthorized(err)
|
|
isTokenAuth := zabbix.settings.AuthType == settings.AuthTypeToken
|
|
if err == zabbixapi.ErrNotAuthenticated || (notAuthorized && !isTokenAuth) {
|
|
if notAuthorized {
|
|
zabbix.logger.Debug("Authentication token expired, performing re-login")
|
|
}
|
|
err = zabbix.Authenticate(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return zabbix.request(ctx, method, params)
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (zabbix *Zabbix) Authenticate(ctx context.Context) error {
|
|
jsonData, err := simplejson.NewJson(zabbix.dsInfo.JSONData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
authType := zabbix.settings.AuthType
|
|
if authType == settings.AuthTypeToken {
|
|
token, exists := zabbix.dsInfo.DecryptedSecureJSONData["apiToken"]
|
|
if !exists {
|
|
return errors.New("cannot find Zabbix API token")
|
|
}
|
|
err = zabbix.api.AuthenticateWithToken(ctx, token)
|
|
if err != nil {
|
|
zabbix.logger.Error("Zabbix authentication error", "error", err)
|
|
return err
|
|
}
|
|
zabbix.logger.Debug("Using API token for authentication")
|
|
return nil
|
|
}
|
|
|
|
zabbixLogin := jsonData.Get("username").MustString()
|
|
var zabbixPassword string
|
|
if securePassword, exists := zabbix.dsInfo.DecryptedSecureJSONData["password"]; exists {
|
|
zabbixPassword = securePassword
|
|
} else {
|
|
// Fallback
|
|
zabbixPassword = jsonData.Get("password").MustString()
|
|
}
|
|
|
|
err = zabbix.api.Authenticate(ctx, zabbixLogin, zabbixPassword, zabbix.version)
|
|
if err != nil {
|
|
zabbix.logger.Error("Zabbix authentication error", "error", err)
|
|
return err
|
|
}
|
|
zabbix.logger.Debug("Successfully authenticated", "url", zabbix.api.GetUrl().String(), "user", zabbixLogin)
|
|
|
|
return nil
|
|
}
|
|
|
|
func isNotAuthorized(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
message := err.Error()
|
|
return strings.Contains(message, "Session terminated, re-login, please.") ||
|
|
strings.Contains(message, "Not authorised.") ||
|
|
strings.Contains(message, "Not authorized.")
|
|
}
|