diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4335ea5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/.project +/.settings +/my_tests +/*.tar.gz +.vscode/ +config/ +bin/ +./httpapi_exporter +./passwd_crypt +httpapi_exporter +passwd_encrypt diff --git a/.promu.yml b/.promu.yml new file mode 100644 index 0000000..b4a8b36 --- /dev/null +++ b/.promu.yml @@ -0,0 +1,23 @@ +go: + cgo: false +repository: + path: github.com/peekjef72/httpapi_exporter +build: + binaries: + - name: httpapi_exporter + # path: httpapi_exporter + - name: passwd_encrypt + path: ./cmd/passwd_crypt + flags: -a -tags netgo,static + ldflags: | + -X github.com/prometheus/common/version.Version={{.Version}} + -X github.com/prometheus/common/version.Revision={{.Revision}} + -X github.com/prometheus/common/version.Branch={{.Branch}} + -X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}} + -X github.com/prometheus/common/version.BuildUser={{user}}@{{host}} +tarball: + prefix: . + files: + - LICENSE + - README.md + - contribs/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..89ad7d9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Alin Sinpalean + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..913f5ed --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +# Copyright 2015 The Prometheus Authors +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +GO := go +GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH))) +PROMU := $(GOPATH)/bin/promu +pkgs = $(shell $(GO) list ./... | grep -v /vendor/) + +PREFIX ?= $(shell pwd) +BIN_DIR ?= $(shell pwd) +DOCKER_IMAGE_NAME ?= httpapi_exporter +DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD)) + + +all: promu build test + +style: + @echo ">> checking code style" + @! gofmt -d $(shell find . -path ./vendor -prune -o -name '*.go' -print) | grep '^' + +test: + @echo ">> running tests" + @$(GO) test -short -race $(pkgs) + +format: + @echo ">> formatting code" + @$(GO) fmt $(pkgs) + +vet: + @echo ">> vetting code" + @$(GO) vet $(pkgs) + +build: promu + @echo ">> building binaries" + @$(PROMU) build --prefix $(PREFIX) + +tarball: promu + @echo ">> building release tarball" + @$(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR) + +docker: + @echo ">> building docker image" + @docker build -t "$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" . + +promu: + @GOOS=$(shell uname -s | tr A-Z a-z) \ + GOARCH=$(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m))) \ + $(GO) install github.com/prometheus/promu@latest + + +.PHONY: all style format build test vet tarball docker promu diff --git a/README.md b/README.md new file mode 100644 index 0000000..b73e093 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Prometheus HTTPAPI Exporter + +This exporter wants to be a generic JSON REST API exporter. That's mean it can login, then makes requests to collect metrics and performs transformations on values and finally returns metrics in prometheus format. + +Nothing is hard coded in the exporter. That why it is a generic exporter. + +As examples 3 configurations for exporters are provided (see contribs): +- hp3par_exporter +- veeam_exporter +- netscaler_exporter + diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..9325c3c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.3.0 \ No newline at end of file diff --git a/actions_action.go b/actions_action.go new file mode 100644 index 0000000..b077a18 --- /dev/null +++ b/actions_action.go @@ -0,0 +1,169 @@ +package main + +import ( + //"bytes" + "fmt" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// actions (block / loop ) +// *************************************************************************************** +// *************************************************************************************** + +type ActionsAction struct { + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + + Actions []Action `yaml:"actions"` + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +func (a *ActionsAction) Type() int { + return actions_action +} + +func (a *ActionsAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +func (a *ActionsAction) GetNameField() *Field { + return a.Name +} +func (a *ActionsAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *ActionsAction) GetWidh() []any { + return a.With +} +func (a *ActionsAction) SetWidth(with []any) { + a.With = with +} + +func (a *ActionsAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *ActionsAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *ActionsAction) GetLoopVar() string { + return a.LoopVar +} +func (a *ActionsAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *ActionsAction) GetVars() map[string]any { + return a.Vars +} +func (a *ActionsAction) SetVars(vars map[string]any) { + a.Vars = vars +} + +func (a *ActionsAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *ActionsAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +// func (a *ActionsAction) GetBaseAction() *BaseAction { +// return nil +// } + +func (a *ActionsAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *ActionsAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// WARNING: only the first MetricPrefix in actionsTree if supported +func (a *ActionsAction) GetMetrics() []*GetMetricsRes { + var ( + final_res []*GetMetricsRes + ) + for _, cur_act := range a.Actions { + res := cur_act.GetMetrics() + if len(res) > 0 { + final_res = append(final_res, res...) + } + } + return final_res +} + +// only for MetricAction +func (a *ActionsAction) GetMetric() *MetricConfig { + return nil +} +func (a *ActionsAction) SetMetricFamily(*MetricFamily) { +} + +// only for PlayAction +func (a *ActionsAction) SetPlayAction(script map[string]*YAMLScript) error { + for _, a := range a.Actions { + if a.Type() == play_script_action || a.Type() == actions_action { + if err := a.SetPlayAction(script); err != nil { + return err + } + } + } + return nil +} + +// specific behavior for the ActionsAction +func (a *ActionsAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: ActionsAction] Name: %s - %d Actions to play", a.GetName(symtab, logger), len(a.Actions))) + for _, cur_act := range a.Actions { + // fmt.Printf("\tadd to symbols table: %s = %v\n", key, val) + if err := PlayBaseAction(script, symtab, logger, cur_act, cur_act.CustomAction); err != nil { + return err + } + + } + return nil +} + +func (a *ActionsAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + + for _, cur_act := range a.Actions { + err := cur_act.AddCustomTemplate(customTemplate) + if err != nil { + return err + } + } + + return nil +} + +// *************************************************************************************** diff --git a/client.go b/client.go new file mode 100644 index 0000000..09d2ec5 --- /dev/null +++ b/client.go @@ -0,0 +1,1155 @@ +package main + +import ( + "encoding/json" + "fmt" + "net" + "strconv" + "strings" + + // "sync" + "time" + + "crypto/tls" + "net/http" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/go-resty/resty/v2" + + "github.com/imdario/mergo" + "github.com/mitchellh/copystructure" + "github.com/peekjef72/httpapi_exporter/encrypt" + "golang.org/x/exp/slices" +) + +var ErrInvalidLogin = fmt.Errorf("invalid_login") + +// Query wraps a sql.Stmt and all the metrics populated from it. It helps extract keys and values from result rows. +type Client struct { + client *resty.Client + + logContext []interface{} + logger log.Logger + sc map[string]*YAMLScript + + // maybe better to use target symtab with a mutex.lock + symtab map[string]any + invalid_auth_code []int + + // mutex to hold condition for global client to try to login() + // wait_mutex sync.Mutex + // wake_cond sync.Cond + + // // to protect the data during exchange + // content_mutex sync.Mutex + // msg string +} + +func newClient(target *TargetConfig, sc map[string]*YAMLScript, logger log.Logger, gc *GlobalConfig) *Client { + + cl := &Client{ + logContext: []interface{}{}, + logger: logger, + sc: sc, + symtab: map[string]any{}, + invalid_auth_code: gc.invalid_auth_code, + } + + params := &ClientInitParams{ + Scheme: target.Scheme, + Host: target.Host, + Port: target.Port, + BaseUrl: target.BaseUrl, + AuthConfig: target.AuthConfig, + ProxyUrl: target.ProxyUrl, + VerifySSL: bool(target.VerifySSL), + ConnectionTimeout: time.Duration(target.ConnectionTimeout), + QueryRetry: target.QueryRetry, + } + + cl.Init(params) + + return cl +} + +// ********************* +func TimeoutDialer(cTimeout time.Duration) func(net, addr string) (c net.Conn, err error) { + return func(netw, addr string) (net.Conn, error) { + conn, err := net.DialTimeout(netw, addr, cTimeout) + if err != nil { + return nil, err + } + return conn, nil + } +} + +// *********************** +func (c *Client) Clone() *Client { + //sync.Mutex{} + cl := &Client{ + logContext: []interface{}{}, + logger: c.logger, + sc: c.sc, + // wait_mutex: sync.Mutex{}, + // content_mutex: sync.Mutex{}, + invalid_auth_code: c.invalid_auth_code, + } + + var err error + var tmp any + + tmp = c.symtab + if tmp, err = copystructure.Copy(c.symtab); err != nil { + level.Error(c.logger).Log("msg", "can't clone symbols table for new client") + return nil + } + if val, ok := tmp.(map[string]any); ok { + cl.symtab = val + } else { + cl.symtab = make(map[string]any) + } + + // c.wake_cond = *sync.NewCond(&c.wait_mutex) + + verifySSL, _ := GetMapValueBool(cl.symtab, "verifySSL") + timeout := time.Duration(cl.symtab["connectionTimeout"].(time.Duration)) + query_retry, _ := GetMapValueInt(cl.symtab, "queryRetry") + + params := &ClientInitParams{ + Scheme: GetMapValueString(cl.symtab, "scheme"), + Host: GetMapValueString(cl.symtab, "host"), + Port: GetMapValueString(cl.symtab, "port"), + BaseUrl: GetMapValueString(cl.symtab, "base_url"), + AuthConfig: AuthConfig{ + Mode: GetMapValueString(cl.symtab, "auth_mode"), + Username: GetMapValueString(cl.symtab, "user"), + Password: Secret(GetMapValueString(cl.symtab, "password")), + Token: Secret(GetMapValueString(cl.symtab, "auth_token")), + authKey: GetMapValueString(cl.symtab, "auth_key"), + }, + // BasicAuth: auth_mode, + // Username: GetMapValueString(cl.symtab, "user"), + // Password: Secret(GetMapValueString(cl.symtab, "password")), + ProxyUrl: GetMapValueString(cl.symtab, "proxyUrl"), + VerifySSL: verifySSL, + ConnectionTimeout: timeout, + QueryRetry: query_retry, + } + cl.Init(params) + + for header, values := range c.client.Header { + cl.client.SetHeader(header, values[0]) + } + + auth_set, _ := GetMapValueBool(c.symtab, "auth_set") + if auth_set { + cl.client.UserInfo = &resty.User{ + Username: c.client.UserInfo.Username, + Password: c.client.UserInfo.Password, + } + + cl.symtab["auth_set"] = true + } + return cl +} + +// set the url for client +func (c *Client) SetUrl(url string) string { + if _, ok := c.symtab["APIEndPoint"]; !ok { + err := fmt.Errorf("http base uri not found") + level.Error(c.logger).Log("errmsg", err) + return "" + } + base := c.symtab["APIEndPoint"].(string) + + uri := fmt.Sprintf("%s/%s", base, strings.TrimPrefix(url, "/")) + c.symtab["uri"] = uri + + level.Debug(c.logger).Log("uri", uri) + return uri +} + +// func (c *Client) Synchronise(src *Client) error { +// c.content_mutex.Lock() +// src.content_mutex.Lock() + +// defer func(){ +// c.content_mutex.Unlock() +// src.content_mutex.Unlock() +// }() + +// return nil +// } +// HTTP GET encapsulation +// func (c *Client) Get( +// uri string, +// params map[string]string, +// with_retry bool) ( +// *resty.Response, +// any, +// error) { +// if c.auth_token == "" { +// err := c.Login() +// if err != nil { +// return nil, nil, err +// } +// } +// return c.Execute("GET", uri, params, nil, with_retry) +// } + +// // Post PowerMax HTTP POST encapsulation +// func (c *Client) Post( +// uri string, +// body interface{}, +// with_retry bool) ( +// *resty.Response, +// any, +// error) { + +// if c.auth_token == "" { +// err := c.Login() +// if err != nil { +// return nil, nil, err +// } +// } +// return c.Execute("POST", uri, nil, body, with_retry) +// } + +// parse a response to a json map[string]interface{} +func (c *Client) getJSONResponse(resp *resty.Response) any { + var err error + // var data map[string]interface{} + // var data_map map[string]interface{} + var data any + + body := resp.Body() + if len(body) > 0 { + content_type := resp.Header().Get("content-type") + if strings.Contains(content_type, "application/json") { + // tmp := make([]byte, len(body)) + // copy(tmp, body) + err = json.Unmarshal(body, &data) + if err != nil { + level.Error(c.logger).Log("errmsg", fmt.Sprintf("Fail to decode json results %v", err)) + } + } + } + return data +} + +// sent HTTP Method to uri with params or body and get the reponse and the json obj +func (c *Client) Execute( + method, uri string, + params map[string]string, + body interface{}) ( + // check_invalid_auth bool) ( + *resty.Response, + any, + error) { + + var err error + var data any + var query_retry int + var ok bool + + // lock client until current request is performed + // c.mutex.Lock() + // defer c.mutex.Unlock() + + url := c.SetUrl(uri) + level.Debug(c.logger).Log("msg", "querying httpapi", "method", method, "url", url) + if body != nil { + level.Debug(c.logger).Log("msg", "querying httpapi", "method", method, "url", url, "body", fmt.Sprintf("%+v", body)) + } + if len(params) > 0 { + level.Debug(c.logger).Log("msg", "querying httpapi", "method", method, "url", url, "params", params) + } + + if query_retry, ok = GetMapValueInt(c.symtab, "queryRetry"); !ok { + query_retry = 1 + } + var resp *resty.Response + + req := c.client.NewRequest() + if body != nil { + req.SetBody(body) + } + if len(params) > 0 { + req.SetQueryParams(params) + } + + for i := 0; i <= query_retry; i++ { + resp, err = req.Execute(method, url) + if err == nil { + // check if retry and invalid auth to replat Ping() script + code := resp.StatusCode() + // if (i+1 < query_retry) && check_invalid_auth && slices.Contains(c.invalid_auth_code, code) { + if (i+1 < query_retry) && slices.Contains(c.invalid_auth_code, code) { + level.Debug(c.logger).Log("msg", "received invalid auth. start Ping()/Login()") + if r_val, ok := c.symtab["__coll_channel"]; ok { + if coll_channel, ok := r_val.(chan<- int); ok { + coll_channel <- MsgLogin + c.symtab["logged"] = false + } + } + // if wake_cond, ok := c.symtab["__wake_cond"].(*sync.Cond); !ok { + // wake_cond.Signal() + // } + + return resp, data, ErrInvalidLogin + + // c.Clear() + // // c.auth_token = "" + // // c.client.Header.Del("x-hp3par-wsapi-sessionkey") + // status := false + // var err error + // var tmp any + // original_symtab := c.symtab + // if tmp, err = copystructure.Copy(c.symtab); err != nil { + // err = fmt.Errorf("can't clone symbols table for new client: %s", err) + // return resp, data, err + // } + // if val, ok := tmp.(map[string]any); ok { + // c.symtab = val + // } else { + // c.symtab = make(map[string]any) + // } + // // ** launch a login sequence with temporary symtab + // status, err = c.Login() + + // resp = nil + // // ** reset original symtab for client + // c.symtab = original_symtab + // // ping/login is unsuccessfull : leave the loop + // if err != nil || !status { + // i = query_retry + 1 + // if err == nil { + // err = fmt.Errorf("Ping() is unsuccessful: query stopped") + // return resp, data, err + // } + // } else { + // level.Debug(c.logger).Log("msg", fmt.Sprintf("Ping()/Login() successfull: retrying (%d)", i+1)) + // } + } else { + data = c.getJSONResponse(resp) + i = query_retry + 1 + } + c.symtab["response_headers"] = resp.Header() + } else { + level.Debug(c.logger).Log("msg", fmt.Sprintf("query unsuccessfull: retrying (%d)", i+1)) + delete(c.symtab, "response_headers") + } + } + // something wrong with retry... + if resp == nil { + err = fmt.Errorf("empty response") + } + if r_val, ok := c.symtab["__coll_channel"]; ok { + if coll_channel, ok := r_val.(chan<- int); ok { + coll_channel <- MsgDone + level.Debug(c.logger).Log("msg", "MsgDone sent to channel.") + } + } + return resp, data, err +} + +// add headers to client +func (cl *Client) proceedHeaders() error { + + if r_headers, ok := cl.symtab["headers"]; ok { + // format: "header" "value" + var head_name, head_value, action string + var headers map[any]any + var err error + var ok bool + if headers, ok = r_headers.(map[any]any); ok { + for r_header, r_value := range headers { + // ** get header name + switch header := r_header.(type) { + case *Field: + if head_name, err = header.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + head_name = header + } + + switch value := r_value.(type) { + case *Field: + if head_value, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + head_value = value + } + if head_value == "__delete__" || head_value == "__remove__" { + cl.client.Header.Del(head_name) + } else { + cl.client.SetHeader(head_name, head_value) + } + } + } else if headers_list, ok := r_headers.([]any); ok { + // format: "- name:header"\n value: "header_value" mode: add|delete + for _, map_header := range headers_list { + if headers, ok = map_header.(map[any]any); ok { + action = "add" + head_name = "" + head_value = "" + for r_key, r_value := range headers { + var key_name string + switch key_val := r_key.(type) { + case *Field: + if key_name, err = key_val.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + key_name = key_val + } + if key_name == "name" { + switch value := r_value.(type) { + case *Field: + if head_name, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + head_name = value + } + + } + // get value + if key_name == "value" { + switch value := r_value.(type) { + case *Field: + if head_value, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + head_value = value + } + + } + + if key_name == "action" { + switch value := r_value.(type) { + case *Field: + if action, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + action = value + } + } + } + if head_name != "" && head_value != "" { + if action == "add" { + cl.client.SetHeader(head_name, head_value) + } else if action == "delete" || action == "remove" { + cl.client.Header.Del(head_name) + } + } + } + } + } + } + return nil +} + +func DeleteCookie(cookies []*http.Cookie, cookie_name string) []*http.Cookie { + for index, http_cookie := range cookies { + if http_cookie.Name == cookie_name { + cookies = append(cookies[:index], cookies[index+1:]...) + } + } + return cookies +} + +// add cookie to client +func (cl *Client) proceedCookies() error { + + if r_cookies, ok := cl.symtab["cookies"]; ok { + // format: name: "header" value: "value" path: + var ( + cookie_name, cookie_value, cookie_path, cookie_domain, action string + cookie_max_age int + headers map[any]any + err error + ok bool + ) + if headers, ok = r_cookies.(map[any]any); ok { + for r_header, r_value := range headers { + // ** get header name + switch header := r_header.(type) { + case *Field: + if cookie_name, err = header.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + cookie_name = header + } + + switch value := r_value.(type) { + case *Field: + if cookie_value, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + cookie_value = value + } + if cookie_value == "__delete__" || cookie_value == "__remove__" { + cl.client.Cookies = DeleteCookie(cl.client.Cookies, cookie_name) + } else { + cookie := &http.Cookie{ + Name: cookie_name, + Value: cookie_value, + } + cl.client.SetCookie(cookie) + } + } + } else if cookies_list, ok := r_cookies.([]any); ok { + // format: "- name:header"\n value: "header_value" mode: add|delete + for _, map_cookie := range cookies_list { + if headers, ok = map_cookie.(map[any]any); ok { + action = "add" + cookie_name = "" + cookie_value = "" + cookie_path = "" + cookie_domain = "" + cookie_max_age = -1 + for r_key, r_value := range headers { + var key_name string + switch key_val := r_key.(type) { + case *Field: + if key_name, err = key_val.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + key_name = key_val + } + if key_name == "name" { + switch value := r_value.(type) { + case *Field: + if cookie_name, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + cookie_name = value + } + + } + // get value + if key_name == "value" { + switch value := r_value.(type) { + case *Field: + if cookie_value, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + cookie_value = value + } + + } + // get domain + if key_name == "domain" { + switch value := r_value.(type) { + case *Field: + if cookie_domain, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + cookie_domain = value + } + } + // get path + if key_name == "path" { + switch value := r_value.(type) { + case *Field: + if cookie_path, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + cookie_path = value + } + } + + if key_name == "action" { + switch value := r_value.(type) { + case *Field: + if action, err = value.GetValueString(cl.symtab, nil, false); err != nil { + return err + } + case string: + action = value + } + } + } + if cookie_name != "" && cookie_value != "" { + if action == "add" { + cookie := &http.Cookie{ + Name: cookie_name, + Value: cookie_value, + } + if cookie_path != "" { + cookie.Path = cookie_path + } + if cookie_domain != "" { + cookie.Domain = cookie_domain + } + if cookie_max_age != -1 { + cookie.MaxAge = cookie_max_age + } + cl.client.SetCookie(cookie) + } else if action == "delete" || action == "remove" { + cl.client.Cookies = DeleteCookie(cl.client.Cookies, cookie_name) + } + } + } + } + } + } + return nil +} + +type CallClientExecuteParams struct { + Payload string + Method string + Url string + Debug bool + VarName string + OkStatus []int + AuthMode string + Username string + Password string + Token string + Timeout time.Duration + // Check_invalid_Auth bool +} + +func (c *Client) callClientExecute(params *CallClientExecuteParams, symtab map[string]any) error { + // payload := fmt.Sprintf("{ \"user\":\"%s\",\"password\":\"%s\", \"sessionType\": 1}", c.user, c.password) + + // if _, ok := symtab["data"]; !ok { + // err := fmt.Errorf("http data not found") + // level.Error(c.logger).Log("errmsg", err) + // return err + // } + var ( + payload any + ) + // payload_raw := symtab["data"].(string) + // if payload_raw == "" { + // payload = nil + // } else { + // payload = payload_raw + // } + if params.Payload == "" { + payload = nil + } else { + payload = params.Payload + } + + // if _, ok := symtab["method"]; !ok { + // err := fmt.Errorf("http method not found") + // level.Error(c.logger).Log("errmsg", err) + // return err + // } + if params.Method == "" { + err := fmt.Errorf("http method not found") + level.Error(c.logger).Log("errmsg", err) + return err + } + method := strings.ToUpper(params.Method) + + // if _, ok := symtab["url"]; !ok { + // err := fmt.Errorf("http url not found") + // level.Error(c.logger).Log("errmsg", err) + // return err + // } + // url := symtab["url"].(string) + if params.Url == "" { + err := fmt.Errorf("http url not found") + level.Error(c.logger).Log("errmsg", err) + return err + } + url := params.Url + old_values := make(map[string]string, 4) + + auth_mode := GetMapValueString(symtab, "auth_mode") + if params.AuthMode != "" { + old_values["auth_mode"] = auth_mode + auth_mode = params.AuthMode + symtab["auth_mode"] = auth_mode + } + + if params.Timeout != 0 { + old_values["timeout"] = fmt.Sprintf("%d", c.client.GetClient().Timeout) + c.client.SetTimeout(params.Timeout) + } + + auth_set, _ := GetMapValueBool(symtab, "auth_set") + if !auth_set { + if auth_mode == "basic" { + passwd := GetMapValueString(symtab, "password") + if params.Password != "" { + old_values["password"] = passwd + passwd = params.Password + symtab["password"] = passwd + } + if strings.Contains(passwd, "/encrypted/") { + ciphertext := passwd[len("/encrypted/"):] + level.Debug(c.logger).Log("ciphertext", ciphertext) + + user := GetMapValueString(symtab, "user") + if params.Username != "" { + old_values["user"] = user + user = params.Username + symtab["user"] = user + } + auth_key := GetMapValueString(symtab, "auth_key") + level.Debug(c.logger).Log("auth_key", auth_key) + cipher, err := encrypt.NewAESCipher(auth_key) + if err != nil { + err := fmt.Errorf("can't obtain cipher to decrypt") + // level.Error(c.logger).Log("errmsg", err) + return err + } + passwd, err = cipher.Decrypt(ciphertext, true) + if err != nil { + err := fmt.Errorf("invalid key provided to decrypt") + // level.Error(c.logger).Log("errmsg", err) + return err + } + c.client.SetBasicAuth(user, passwd) + passwd = "" + symtab["auth_set"] = true + delete(symtab, "auth_key") + } + } else if auth_mode == "token" { + auth_token := GetMapValueString(symtab, "auth_token") + if params.Token != "" { + old_values["auth_token"] = auth_token + auth_token = params.Token + symtab["auth_token"] = auth_token + } + if auth_token != "" { + c.client.SetAuthToken(auth_token) + } + } + } + + // * check if returned status is valid or not: present in valid_status list + // if _, ok := symtab["ok_status"]; !ok { + // err := fmt.Errorf("ok_status not found") + // level.Error(c.logger).Log("errmsg", err) + // return err + // } + if len(params.OkStatus) <= 0 { + err := fmt.Errorf("ok_status not found") + level.Error(c.logger).Log("errmsg", err) + return err + } + valid_status := params.OkStatus + + var_name := params.VarName + + //****************** + //* play the request + // resp, data, err := c.Execute(method, url, nil, payload, params.Check_invalid_Auth) + resp, data, err := c.Execute(method, url, nil, payload) + if err != nil { + level.Error(c.logger).Log("errmsg", err) + return err + } + if params.Debug { + level.Info(c.logger).Log("msg", "launch query debug", + "url", symtab["uri"].(string), "results", string(resp.Body())) + } + // * get returned status + code := resp.StatusCode() + // * set it to symbols table so user can access it + symtab["results_status"] = code + + if !slices.Contains(valid_status, code) { + symtab["query_status"] = false + level.Info(c.logger).Log("msg", fmt.Sprintf("invalid response status: (%d not in %v)", code, valid_status)) + } else { + if data == nil { + err = fmt.Errorf("fail to decode json results: %s", err) + // level.Error(c.logger).Log("errmsg", err) + return err + } else { + if var_name != "" && var_name != "_" { + symtab[var_name] = data + } else if var_name == "_root" { + opts := mergo.WithOverride + if err := mergo.Merge(&symtab, data, opts); err != nil { + level.Error(c.logger).Log("msg", "merging results into symbols table", "errmsg", err) + return err + } + } + symtab["query_status"] = true + + err = nil + } + } + // reset local auth param from client + if auth_mode, ok := old_values["auth_mode"]; ok { + symtab["auth_mode"] = auth_mode + if params.AuthMode == "basic" && params.AuthMode != auth_mode { + c.client.UserInfo = nil + } + } + + if user, ok := old_values["user"]; ok { + symtab["user"] = user + } + + if passwd, ok := old_values["passwd"]; ok { + symtab["passwd"] = passwd + } + + if auth_token, ok := old_values["auth_token"]; ok { + symtab["auth_token"] = auth_token + if params.Token != auth_token { + c.client.Token = "" + } + } + + if timeout_str, ok := old_values["timeout"]; ok { + var i_value int64 + if i_value, err = strconv.ParseInt(timeout_str, 10, 0); err != nil { + i_value = 0 + } + c.client.SetTimeout(time.Duration(i_value)) + } + + return err +} + +func GetMapValueString(symtab map[string]any, key string) string { + var value string + if value_raw, ok := symtab[key]; ok { + switch value_val := value_raw.(type) { + case string: + value = value_val + case int: + value = fmt.Sprintf("%d", value_val) + default: + value = "" + } + } + return value +} + +func GetMapValueInt(symtab map[string]any, key string) (int, bool) { + var value int + found := false + if value_raw, ok := symtab[key]; ok { + found = true + switch value_val := value_raw.(type) { + case string: + var i_value int64 + var err error + if i_value, err = strconv.ParseInt(value_val, 10, 0); err != nil { + i_value = 0 + } + value = int(i_value) + case int: + value = value_val + default: + value = 0 + found = false + } + } + return value, found +} + +func GetMapValueBool(symtab map[string]any, key string) (bool, bool) { + var value bool + + found := false + if value_raw, ok := symtab[key]; ok { + found = true + switch value_val := value_raw.(type) { + case bool: + value = value_val + case string: + asString := strings.ToLower(value_val) + if asString == "1" || asString == "true" || asString == "yes" || asString == "on" { + value = true + } else if asString == "0" || asString == "false" || asString == "no" || asString == "off" { + value = false + } + default: + value = false + found = false + } + } + return value, found +} + +// **************************************************************** +// user HTTP connections script steps +// init(): to initialize http request +// login(): to login to the http API and proceed result (token bearer) +// logout(): to logout and reset parameters +// ping(): to check the auth/cnx is still active +type ClientInitParams struct { + Scheme string + Host string + Port string + BaseUrl string + AuthConfig AuthConfig + // BasicAuth bool + // Username string + // Password Secret + ProxyUrl string + VerifySSL bool + ConnectionTimeout time.Duration + QueryRetry int +} + +func (cl *Client) Init(params *ClientInitParams) error { + + // ** get the init script definition from config if one is defined + // ** set default config for all targets + if script, ok := cl.sc["init"]; ok && script != nil { + // cl.symtab["__client"] = cl.client + cl.symtab["__method"] = cl.callClientExecute + err := script.Play(cl.symtab, false, cl.logger) + delete(cl.symtab, "__method") + + if err != nil { + return err + } + } + var base_url, scheme, port string + var verifySSL bool + var query_retry int + + base_url = GetMapValueString(cl.symtab, "base_url") + scheme = GetMapValueString(cl.symtab, "scheme") + port = GetMapValueString(cl.symtab, "port") + verifySSL, _ = GetMapValueBool(cl.symtab, "verifySSL") + query_retry, _ = GetMapValueInt(cl.symtab, "queryRetry") + + // ** update default parameters with target parameters + if params.BaseUrl != "" { + base_url = params.BaseUrl + } + if params.Scheme != "" { + scheme = params.Scheme + } + if params.Port != "" { + port = params.Port + } + if verifySSL != params.VerifySSL { + verifySSL = params.VerifySSL + } + if query_retry != params.QueryRetry { + query_retry = params.QueryRetry + } + apiendpoint := fmt.Sprintf("%s://%s:%s", scheme, params.Host, port) + baseurl := strings.TrimPrefix(base_url, "/") + if baseurl != "" { + apiendpoint += "/" + baseurl + } + + cl.symtab["APIEndPoint"] = apiendpoint + cl.symtab["scheme"] = scheme + cl.symtab["host"] = params.Host + cl.symtab["port"] = port + cl.symtab["base_url"] = base_url + cl.symtab["auth_mode"] = params.AuthConfig.Mode + cl.symtab["user"] = params.AuthConfig.Username + cl.symtab["password"] = string(params.AuthConfig.Password) + cl.symtab["auth_token"] = string(params.AuthConfig.Token) + cl.symtab["auth_key"] = string(params.AuthConfig.authKey) + cl.symtab["auth_set"] = false + cl.symtab["verifySSL"] = verifySSL + cl.symtab["proxyUrl"] = params.ProxyUrl + cl.symtab["connectionTimeout"] = params.ConnectionTimeout + cl.symtab["queryRetry"] = query_retry + + if scheme == "https" { + cl.client = resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: !verifySSL}) + } else if scheme == "http" { + cl.client = resty.New() + } else { + level.Error(cl.logger).Log("msg", fmt.Sprintf("invalid scheme for url '%s'", scheme)) + return nil + } + timeout := time.Duration(cl.symtab["connectionTimeout"].(time.Duration)) + // cl.client.SetTransport( + // &http.Transport{ + // DialContext: (&net.Dialer{ + // Timeout: timeout, + // }).DialContext, + // }, + // ) + cl.client.SetTimeout(timeout) + + if err := cl.proceedHeaders(); err != nil { + return err + } + if err := cl.proceedCookies(); err != nil { + return err + } + + if params.AuthConfig.Mode == "basic" { + passwd := string(params.AuthConfig.Password) + if params.AuthConfig.Username != "" && passwd != "" && + !strings.Contains(passwd, "/encrypted/") { + cl.client.SetBasicAuth(params.AuthConfig.Username, passwd) + } + } else if params.AuthConfig.Mode == "token" && params.AuthConfig.Token != "" { + token := GetMapValueString(cl.symtab, "auth_token") + cl.client.SetAuthToken(token) + } + if params.ProxyUrl != "" { + cl.client.SetProxy(params.ProxyUrl) + } + + return nil +} + +// login to target +func (cl *Client) Login() (bool, error) { + + // ** init the connection status func and symbol table + status := false + cl.symtab["logged"] = false + + // ** get the login script definition from config if one is defined + if script, ok := cl.sc["login"]; ok && script != nil { + // cl.symtab["__client"] = cl.client + cl.symtab["__method"] = cl.callClientExecute + err := script.Play(cl.symtab, false, cl.logger) + delete(cl.symtab, "__method") + + if err != nil { + return false, err + } + if err := cl.proceedHeaders(); err != nil { + return false, err + } + if err := cl.proceedCookies(); err != nil { + return false, err + } + // check is user has set a token + token := GetMapValueString(cl.symtab, "auth_token") + if cl.client.Token != token { + cl.client.SetAuthToken(token) + } + } else { + // * no user script has been defined: logged is equivalent to "ping()" query_status + cl.symtab["logged"] = cl.symtab["query_status"] + } + + if logged, ok := GetMapValueBool(cl.symtab, "logged"); ok { + status = logged + } + + return status, nil +} + +// logout from target +func (cl *Client) Logout() error { + // ** get the login script definition from config if one is defined + if script, ok := cl.sc["logout"]; ok && script != nil { + // cl.symtab["__client"] = cl.client + cl.symtab["__method"] = cl.callClientExecute + err := script.Play(cl.symtab, false, cl.logger) + delete(cl.symtab, "__method") + + if err != nil { + return err + } + + if err := cl.proceedHeaders(); err != nil { + return err + } + if err := cl.proceedCookies(); err != nil { + return err + } + } else { + // ** no user script found: equivalent to Clear() + return cl.Clear() + } + return nil +} + +// clear auth info for target +func (cl *Client) Clear() error { + // ** get the clear script definition from config if one is defined + if script, ok := cl.sc["clear"]; ok && script != nil { + // cl.symtab["__client"] = cl.client + cl.symtab["__method"] = cl.callClientExecute + err := script.Play(cl.symtab, false, cl.logger) + delete(cl.symtab, "__method") + + if err != nil { + return err + } + } else { + cl.symtab["logged"] = false + cl.symtab["auth_set"] = false + delete(cl.symtab, "auth_token") + cl.client.SetAuthToken("") + } + + if err := cl.proceedHeaders(); err != nil { + return err + } + if err := cl.proceedCookies(); err != nil { + return err + } + return nil +} + +// ping the target +func (cl *Client) Ping() (bool, error) { + + // ** init the connection status func and symbol table + status := false + cl.symtab["query_status"] = false + + // ** get the ping script definition from config if one is defined + if script, ok := cl.sc["ping"]; ok && script != nil { + level.Debug(cl.logger).Log("msg", fmt.Sprintf("starting script '%s'", script.name)) + // cl.symtab["__client"] = cl.client + cl.symtab["__method"] = cl.callClientExecute + // cl.symtab["check_invalid_auth"] = false + err := script.Play(cl.symtab, false, cl.logger) + delete(cl.symtab, "__method") + // delete(cl.symtab, "check_invalid_auth") + + if err != nil { + return false, err + } + } else { + err := fmt.Errorf("no ping script found... can't connect") + level.Error(cl.logger).Log("msg", err) + return false, err + } + + if query_status, ok := GetMapValueBool(cl.symtab, "query_status"); ok { + status = query_status + } + + if err := cl.proceedHeaders(); err != nil { + return status, err + } + if err := cl.proceedCookies(); err != nil { + return status, err + } + + // check is user has set a token + token := GetMapValueString(cl.symtab, "auth_token") + if cl.client.Token != token { + cl.client.SetAuthToken(token) + } + + return status, nil + +} diff --git a/cmd/passwd_crypt/main.go b/cmd/passwd_crypt/main.go new file mode 100644 index 0000000..dcdec33 --- /dev/null +++ b/cmd/passwd_crypt/main.go @@ -0,0 +1,85 @@ +package main + +import ( + /* rsa + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "fmt" + + // "crypto/ed25519" + "crypto/x509" + */ + /* cipher + */ + "bufio" + "fmt" + "os" + "strings" + + // "golang.org/x/crypto/ssh/terminal" + kingpin "github.com/alecthomas/kingpin/v2" + "github.com/peekjef72/httpapi_exporter/encrypt" + "github.com/prometheus/common/version" +) + +func main() { + + app := kingpin.New("passwd_crypt", "encrypt password with a shared key.") + var ( + decrypt = app.Flag("decrypt", "Decrypt the provided password with key.").Short('d').Default("false").Bool() + hexa = app.Flag("hexa", "Encode password in hexastring.(default base64).").Short('x').Default("false").Bool() + ) + app.HelpFlag.Short('h') + app.Version(version.Print("passwd_crypt")).VersionFlag.Short('V') + kingpin.MustParse(app.Parse(os.Args[1:])) + // kingpin.Parse() + + fmt.Println("give the key: must be 16 24 or 32 bytes long") + key := credentials("enter key: ") + + cipher, err := encrypt.NewAESCipher(key) + if err != nil { + fmt.Printf("%s\n", err) + os.Exit(1) + } + + if !*decrypt { + passwd := credentials("enter password: ") + + fmt.Println("Encrypting...") + msg := []byte(passwd) + ciphertext := cipher.Encrypt(msg, !*hexa) + fmt.Printf("Encrypted message hex: %s\n", ciphertext) + } else { + passwd := credentials("enter encrypted password: ") + + fmt.Println("Decrypting...") + plaintext, err := cipher.Decrypt(passwd, !*hexa) + if err != nil { + // Don't display this message to the end-user, as it could potentially + // give an attacker useful information. Just tell them something like "Failed to decrypt." + fmt.Printf("Error decryping message: %s\n", err.Error()) + os.Exit(1) + } + fmt.Printf("Decrypted message: %s\n", string(plaintext)) + } +} + +func credentials(prompt string) string { + reader := bufio.NewReader(os.Stdin) + + fmt.Print(prompt) + res, _ := reader.ReadString('\n') + + // fmt.Print("Enter Password: ") + // bytePassword, err := terminal.ReadPassword(0) + // if err == nil { + // fmt.Println("\nPassword typed: " + string(bytePassword)) + // } + // password := string(bytePassword) + + return strings.TrimSpace(res) +} diff --git a/collector.go b/collector.go new file mode 100644 index 0000000..281a95f --- /dev/null +++ b/collector.go @@ -0,0 +1,261 @@ +package main + +import ( + "context" + "fmt" + + // "sync" + "time" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + dto "github.com/prometheus/client_model/go" +) + +// Collector is a self-contained group of http queries and metric families to collect from results. It is +// conceptually similar to a prometheus.Collector. +type Collector interface { + // Collect is the equivalent of prometheus.Collector.Collect() but takes a context to run in and a database to run on. + Collect(context.Context, chan<- Metric, chan<- int) + SetClient(*Client) + GetClient() *Client +} + +// collector implements Collector. It wraps a collection of queries, metrics and the database to collect them from. +type collector struct { + config *CollectorConfig + client *Client + // queries []*Query + logContext []interface{} + collect_script []*YAMLScript + // metricFamilies []*MetricFamily + logger log.Logger +} + +// NewCollector returns a new Collector with the given configuration and database. The metrics it creates will all have +// the provided const labels applied. +func NewCollector( + logContext []interface{}, + logger log.Logger, + cc *CollectorConfig, + constLabels []*dto.LabelPair, + collect_script []*YAMLScript) (Collector, error) { + + // var mfs []*MetricFamily + + logContext = append(logContext, "collector", cc.Name) + // mfs := make([]*MetricFamily,) + for _, scr := range collect_script { + for _, ma := range scr.metricsActions { + for _, act := range ma.Actions { + if act.Type() == metric_action { + mc := act.GetMetric() + if mc == nil { + return nil, fmt.Errorf("MetricAction nil received") + } + mf, err := NewMetricFamily(logContext, mc, constLabels, cc.customTemplate) + if err != nil { + return nil, err + } + // ma.metricFamilies = append(ma.metricFamilies, mf) + // mfs = append(mfs, mf) + act.SetMetricFamily(mf) + } + } + // for _, mc := range ma.GetMetrics() { + // } + } + } + // Instantiate metric families. + // for _, mc := range cc.Metrics { + // mf, err := NewMetricFamily(logContext, mc, constLabels, cc.customTemplate) + // if err != nil { + // return nil, err + // } + // mfs, found := queryMFs[mc.Query()] + // if !found { + // mfs = make([]*MetricFamily, 0, 2) + // } + // queryMFs[mc.Query()] = append(mfs, mf) + // } + + // Instantiate queries. + // queries := make([]*Query, 0, len(cc.Metrics)) + // for qc, mfs := range queryMFs { + // q, err := NewQuery(logContext, logger, qc, mfs...) + // if err != nil { + // return nil, err + // } + // queries = append(queries, q) + // } + + c := collector{ + config: cc, + // queries: queries, + logContext: logContext, + logger: logger, + // metricFamilies: mfs, + collect_script: collect_script, + } + + if c.config.MinInterval > 0 { + var logCtx []interface{} + + logCtx = append(logCtx, logContext...) + logCtx = append(logCtx, "msg", fmt.Sprintf("NewCollector(): Non-zero min_interval (%s), using cached collector.", c.config.MinInterval)) + level.Debug(logger).Log(logCtx...) + return newCachingCollector(&c), nil + } + return &c, nil +} + +// SetClient implement SetClient for Client +func (c *collector) GetClient() (client *Client) { + return c.client +} + +// SetClient implement SetClient for Client +func (c *collector) SetClient(client *Client) { + c.client = client +} + +// type CollectContext struct { +// method func(*CallClientExecuteParams, map[string]any) error +// // ctx context.Context +// metric_ch chan<- Metric +// metricfamilies []*MetricFamily +// wake_cond *sync.Cond +// // logcontext []any +// } + +// Collect implements Collector. +func (c *collector) Collect(ctx context.Context, ch chan<- Metric, coll_ch chan<- int) { + // var wg sync.WaitGroup + // wg.Add(len(c.queries)) + // for _, q := range c.queries { + // go func(q *Query) { + // defer wg.Done() + // q.Collect(ctx, client, ch) + // }(q) + // } + // // Only return once all queries have been processed + // wg.Wait() + // use a collect context object + + // cctx := &CollectContext{ + // method: c.client.callClientExecute, + // // ctx: ctx, + // metric_ch: ch, + // metricfamilies: c.metricFamilies, + // wake_cond: wake_cond, + // // logcontext: c.logContext, + // } + + c.client.symtab["__method"] = c.client.callClientExecute + // c.client.symtab["__context"] = ctx + c.client.symtab["__channel"] = ch + c.client.symtab["__coll_channel"] = coll_ch + // c.client.symtab["__metricfamilies"] = c.metricFamilies + // c.client.symtab["__wake_cond"] = wake_cond + // c.client.symtab["__logcontext"] = c.logContext + + // c.client.symtab["__collect_context"] = cctx + + for _, scr := range c.collect_script { + level.Debug(c.logger).Log("msg", fmt.Sprintf("starting script '%s'", scr.name)) + if err := scr.Play(c.client.symtab, false, c.logger); err != nil { + if err != ErrInvalidLogin { + level.Warn(c.logger).Log("script", scr.name, "errmsg", err) + coll_ch <- MsgQuit + } + } + } + delete(c.client.symtab, "__channel") + delete(c.client.symtab, "__coll_channel") + // delete(c.client.symtab, "__metricfamilies") +} + +// newCachingCollector returns a new Collector wrapping the provided raw Collector. +func newCachingCollector(rawColl *collector) Collector { + cc := &cachingCollector{ + rawColl: rawColl, + minInterval: time.Duration(rawColl.config.MinInterval), + cacheSem: make(chan time.Time, 1), + } + cc.cacheSem <- time.Time{} + return cc +} + +// Collector with a cache for collected metrics. Only used when min_interval is non-zero. +type cachingCollector struct { + // Underlying collector, which is being cached. + rawColl *collector + // Convenience copy of rawColl.config.MinInterval. + minInterval time.Duration + + // Used as a non=blocking semaphore protecting the cache. The value in the channel is the time of the cached metrics. + cacheSem chan time.Time + // Metrics saved from the last Collect() call. + cache []Metric +} + +// SetClient implement SetClient()for Client +func (cc *cachingCollector) SetClient(client *Client) { + cc.rawColl.client = client +} + +// SetClient implement SetClient for Client +func (cc *cachingCollector) GetClient() (client *Client) { + return cc.rawColl.client +} + +// Collect implements Collector. +func (cc *cachingCollector) Collect(ctx context.Context, ch chan<- Metric, coll_ch chan<- int) { + if ctx.Err() != nil { + ch <- NewInvalidMetric(cc.rawColl.logContext, ctx.Err()) + return + } + + collTime := time.Now() + select { + case cacheTime := <-cc.cacheSem: + // Have the lock. + if age := collTime.Sub(cacheTime); age > cc.minInterval { + // Cache contents are older than minInterval, collect fresh metrics, cache them and pipe them through. + var logCtx []interface{} + + logCtx = append(logCtx, cc.rawColl.logContext...) + logCtx = append(logCtx, "msg", fmt.Sprintf("Collecting fresh metrics: min_interval=%.3fs cache_age=%.3fs", + cc.minInterval.Seconds(), age.Seconds())) + level.Debug(cc.rawColl.logger).Log(logCtx...) + cacheChan := make(chan Metric, capMetricChan) + cc.cache = make([]Metric, 0, len(cc.cache)) + go func() { + cc.rawColl.Collect(ctx, cacheChan, coll_ch) + close(cacheChan) + }() + for metric := range cacheChan { + cc.cache = append(cc.cache, metric) + ch <- metric + } + cacheTime = collTime + } else { + var logCtx []interface{} + + logCtx = append(logCtx, cc.rawColl.logContext...) + logCtx = append(logCtx, "msg", fmt.Sprintf("Returning cached metrics: min_interval=%.3fs cache_age=%.3fs", + cc.minInterval.Seconds(), age.Seconds())) + level.Debug(cc.rawColl.logger).Log(logCtx...) + for _, metric := range cc.cache { + ch <- metric + } + } + // Always replace the value in the semaphore channel. + cc.cacheSem <- cacheTime + + case <-ctx.Done(): + // Context closed, record an error and return + // TODO: increment an error counter + ch <- NewInvalidMetric(cc.rawColl.logContext, ctx.Err()) + } +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..f2d3210 --- /dev/null +++ b/config.go @@ -0,0 +1,958 @@ +package main + +import ( + "fmt" + "reflect" + "regexp" + + // "html/template" + "os" + "path/filepath" + "strings" + "text/template" + "time" + + // "github.com/Masterminds/sprig/v3" + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/model" + "gopkg.in/yaml.v3" +) + +// Load attempts to parse the given config file and return a Config object. +func Load(configFile string, logger log.Logger, collectorName string) (*Config, error) { + level.Info(logger).Log("msg", fmt.Sprintf("Loading configuration from %s", configFile)) + buf, err := os.ReadFile(configFile) + if err != nil { + return nil, err + } + + c := Config{ + configFile: configFile, + logger: logger, + collectorName: collectorName, + } + + err = yaml.Unmarshal(buf, &c) + if err != nil { + return nil, err + } + + return &c, nil +} + +// +// Top-level config +// + +// Config is a collection of targets and collectors. +type Config struct { + Globals *GlobalConfig `yaml:"global"` + CollectorFiles []string `yaml:"collector_files,omitempty"` + Targets []*TargetConfig `yaml:"targets,omitempty"` + Collectors []*CollectorConfig `yaml:"collectors,omitempty"` + HttpAPIConfig map[string]*YAMLScript `yaml:"httpapi_config"` + + configFile string + logger log.Logger + collectorName string + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for Config. +func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain Config + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + if len(c.Targets) == 0 { + return fmt.Errorf("at least one target in `targets` must be defined") + } + + // Load any externally defined collectors. + if err := c.loadCollectorFiles(); err != nil { + return err + } + + if len(c.Collectors) == 0 { + return fmt.Errorf("at least one collector in `collectors` must be defined") + } + + // Populate collector references for the target/jobs. + colls := make(map[string]*CollectorConfig) + for _, coll := range c.Collectors { + // Set the min interval to the global default if not explicitly set. + if coll.MinInterval < 0 { + coll.MinInterval = c.Globals.MinInterval + } + if _, found := colls[coll.Name]; found { + return fmt.Errorf("duplicate collector name: %s", coll.Name) + } + colls[coll.Name] = coll + // set metric prefix + var prefix string + for _, cs := range coll.CollectScripts { + for _, a := range cs.Actions { + reslist := a.GetMetrics() + for _, res := range reslist { + for _, metric := range res.mc { + // for _, metric := range coll.Metrics { + // add metric prefix to all metrics name + + if res.maprefix != "" { + prefix = res.maprefix + } else if coll.MetricPrefix != "" { + prefix = coll.MetricPrefix + } else if c.Globals.MetricPrefix != "" { + prefix = c.Globals.MetricPrefix + } + metric.Name = prefix + "_" + metric.Name + } + } + } + } + } + + // read the target config with a TargetsFiles specfied + for _, t := range c.Targets { + if len(t.TargetsFiles) > 0 { + err := c.loadTargetsFiles(t.TargetsFiles) + if err != nil { + return err + } + } + } + targets := c.Targets + c.Targets = nil + // remove pseudo targets with a TargetsFiles + for _, t := range targets { + if len(t.TargetsFiles) == 0 { + c.Targets = append(c.Targets, t) + } + } + + if len(c.Targets) == 0 { + return fmt.Errorf("at least one target in `targets` must be defined") + } + + for _, t := range c.Targets { + // substitute the collector names list set in config by the value forced in command line argument + if c.collectorName != "" { + t.CollectorRefs = nil + t.CollectorRefs = append(t.CollectorRefs, c.collectorName) + } + cs, err := resolveCollectorRefs(t.CollectorRefs, colls, fmt.Sprintf("target %q", t.Name)) + if err != nil { + return err + } + t.collectors = cs + } + + // Check for empty/duplicate target names/data source names + tnames := make(map[string]interface{}) + for _, t := range c.Targets { + if len(t.TargetsFiles) > 0 { + continue + } + if t.Name == "" { + return fmt.Errorf("empty target name in static config %+v", t) + } + if _, ok := tnames[t.Name]; ok { + return fmt.Errorf("duplicate target name %q in target %+v", t.Name, t) + } + tnames[t.Name] = nil + + if t.ConnectionTimeout == 0 { + t.ConnectionTimeout = c.Globals.ConnectionTimeout + } + + if t.QueryRetry == -1 { + t.QueryRetry = c.Globals.QueryRetry + } + } + + // check HttpAPIConfig script: + for name, sc := range c.HttpAPIConfig { + if sc != nil { + sc.name = name + // have to set the action to play for play_script_action + for _, a := range sc.Actions { + if a.Type() == play_script_action || a.Type() == actions_action { + if err := a.SetPlayAction(c.HttpAPIConfig); err != nil { + return err + } + } + } + } + } + + return checkOverflow(c.XXX, "config") +} + +func GetScriptsDef(map_src map[string]*YAMLScript) map[string]ActionsList { + var val ActionsList + scdef := make(map[string]ActionsList, len(map_src)+1) + for name, scr := range map_src { + val = (ActionsList)(nil) + if scr != nil { + val = (ActionsList)(scr.Actions) + } + scdef[name] = val + } + return scdef +} + +type dumpConfig struct { + Globals *GlobalConfig `yaml:"global"` + CollectorFiles []string `yaml:"collector_files,omitempty"` + Collectors []*dumpCollectorConfig `yaml:"collectors,omitempty"` + // HttpAPIConfig map[string]*ActionsList `yaml:"httpapi_config"` + HttpAPIConfig map[string]ActionsList `yaml:"httpapi_config"` +} + +// YAML marshals the config into YAML format. +func (c *Config) YAML() ([]byte, error) { + dc := &dumpConfig{ + Globals: c.Globals, + CollectorFiles: c.CollectorFiles, + Collectors: GetCollectorsDef(c.Collectors), + HttpAPIConfig: GetScriptsDef(c.HttpAPIConfig), + } + return yaml.Marshal(dc) +} + +// loadCollectorFiles resolves all collector file globs to files and loads the collectors they define. +func (c *Config) loadCollectorFiles() error { + baseDir := filepath.Dir(c.configFile) + for _, cfglob := range c.CollectorFiles { + // Resolve relative paths by joining them to the configuration file's directory. + if len(cfglob) > 0 && !filepath.IsAbs(cfglob) { + cfglob = filepath.Join(baseDir, cfglob) + } + + // Resolve the glob to actual filenames. + cfs, err := filepath.Glob(cfglob) + level.Debug(c.logger).Log("msg", fmt.Sprintf("Checking collectors from %s", cfglob)) + if err != nil { + // The only error can be a bad pattern. + return fmt.Errorf("error parsing collector files for %s: %s", cfglob, err) + } + + // And load the CollectorConfig defined in each file. + for _, cf := range cfs { + level.Debug(c.logger).Log("msg", fmt.Sprintf("Loading collectors from %s", cf)) + buf, err := os.ReadFile(cf) + if err != nil { + return fmt.Errorf("reading collectors file %s: %s", cf, err) + } + + cc := CollectorConfig{ + symtab: map[string]any{}, + } + err = yaml.Unmarshal(buf, &cc) + if err != nil { + return fmt.Errorf("reading %s: %s", cf, err) + } + c.Collectors = append(c.Collectors, &cc) + level.Info(c.logger).Log("msg", fmt.Sprintf("Loaded collector %s from %s", cc.Name, cf)) + } + } + + return nil +} + +// loadTargetsFiles resolves all targets file globs to files and loads the targets they define. +func (c *Config) loadTargetsFiles(targetFilepath []string) error { + baseDir := filepath.Dir(c.configFile) + for _, tfglob := range targetFilepath { + // Resolve relative paths by joining them to the configuration file's directory. + if len(tfglob) > 0 && !filepath.IsAbs(tfglob) { + tfglob = filepath.Join(baseDir, tfglob) + } + + // Resolve the glob to actual filenames. + tfs, err := filepath.Glob(tfglob) + level.Debug(c.logger).Log("msg", fmt.Sprintf("Checking targets from %s", tfglob)) + if err != nil { + // The only error can be a bad pattern. + return fmt.Errorf("error resolving targets_files files for %s: %s", tfglob, err) + } + + // And load the CollectorConfig defined in each file. + for _, tf := range tfs { + level.Debug(c.logger).Log("msg", fmt.Sprintf("Loading targets from %s", tf)) + buf, err := os.ReadFile(tf) + if err != nil { + return fmt.Errorf("reading targets_files for %s: %s", tf, err) + } + + target := TargetConfig{} + err = yaml.Unmarshal(buf, &target) + if err != nil { + return fmt.Errorf("parsing targets_files for %s: %s", tf, err) + } + target.setFromFile(tf) + c.Targets = append(c.Targets, &target) + level.Info(c.logger).Log("msg", fmt.Sprintf("Loaded target '%q' from %s", target.Name, tf)) + } + } + + return nil +} + +// GlobalConfig contains globally applicable defaults. +type GlobalConfig struct { + MinInterval model.Duration `yaml:"min_interval"` // minimum interval between query executions, default is 0 + ConnectionTimeout model.Duration `yaml:"connection_timeout"` // connection timeout, target + ScrapeTimeout model.Duration `yaml:"scrape_timeout"` // per-scrape timeout, global + TimeoutOffset model.Duration `yaml:"scrape_timeout_offset"` // offset to subtract from timeout in seconds + MetricPrefix string `yaml:"metric_prefix"` // a prefix to ad dto all metric name; may be redefined in collector files + QueryRetry int `yaml:"query_retry,omitempty"` // target specific number of times to retry a query + InvalidHttpCode any `yaml:"invalid_auth_code,omitempty"` + ExporterName string `yaml:"exporter_name,omitempty"` + + invalid_auth_code []int + // query_retry int + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for GlobalConfig. +func (g *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + // Default to running the queries on every scrape. + g.MinInterval = model.Duration(0) + // Default to 2 seconds, to connect to a target. + g.ConnectionTimeout = model.Duration(2 * time.Second) + // Default to 10 seconds, since Prometheus has a 10 second scrape timeout default. + g.ScrapeTimeout = model.Duration(10 * time.Second) + // Default to .5 seconds. + g.TimeoutOffset = model.Duration(500 * time.Millisecond) + g.ExporterName = exporter_name + + // Default tp 3 + g.QueryRetry = 3 + + // Default to hp3par + g.MetricPrefix = "hp3par" + + type plain GlobalConfig + if err := unmarshal((*plain)(g)); err != nil { + return err + } + + if g.TimeoutOffset <= 0 { + return fmt.Errorf("global.scrape_timeout_offset must be strictly positive, have %s", g.TimeoutOffset) + } + if g.ConnectionTimeout <= 0 { + return fmt.Errorf("global.connection_timeout must be strictly positive, have %s", g.ConnectionTimeout) + } + + if g.InvalidHttpCode == nil { + g.invalid_auth_code = []int{401, 403} + } else { + g.invalid_auth_code = buildStatus(g.InvalidHttpCode) + } + + return checkOverflow(g.XXX, "global") +} + +// +// Targets +// + +// TargetConfig defines a url and a set of collectors to be executed on it. +type TargetConfig struct { + Name string `yaml:"name"` // target name to connect to from prometheus + Scheme string `yaml:"scheme"` + Host string `yaml:"host"` + Port string `yaml:"port,omitempty"` + BaseUrl string `yaml:"baseUrl,omitempty"` + AuthConfig AuthConfig `yaml:"auth_mode,omitempty"` + // Username string `yaml:"user,omitempty"` + // Password Secret `yaml:"password,omitempty"` // data source definition to connect to + // BasicAuth ConvertibleBoolean `yaml:"basicAuth"` + ProxyUrl string `yaml:"proxy,omitempty"` + VerifySSL ConvertibleBoolean `yaml:"verifySSL,omitempty"` + ConnectionTimeout model.Duration `yaml:"connection_timeout,omitempty"` // connection timeout, per-target + Labels map[string]string `yaml:"labels,omitempty"` // labels to apply to all metrics collected from the targets + CollectorRefs []string `yaml:"collectors"` // names of collectors to execute on the target + TargetsFiles []string `yaml:"targets_files,omitempty"` // slice of path and pattern for files that contains targets + QueryRetry int `yaml:"query_retry,omitempty"` // target specific number of times to retry a query + + collectors []*CollectorConfig // resolved collector references + fromFile string // filepath if loaded from targets_files pattern + // basicAuth bool + // verifySSL bool + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// Collectors returns the collectors referenced by the target, resolved. +func (t *TargetConfig) Collectors() []*CollectorConfig { + return t.collectors +} + +// set fromFile for target when read from targets_files directive +func (t *TargetConfig) setFromFile(file_path string) { + t.fromFile = file_path +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for TargetConfig. +func (t *TargetConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain TargetConfig + // set default value for target + t.QueryRetry = -1 + t.VerifySSL = true + + if err := unmarshal((*plain)(t)); err != nil { + return err + } + + // Check required fields + if len(t.TargetsFiles) == 0 { + if t.Name == "" { + return fmt.Errorf("empty target name in target %+v", t) + } + + if t.Scheme == "" { + t.Scheme = "https" + } + if t.Port == "" { + t.Port = "443" + } + if t.BaseUrl != "" { + t.BaseUrl = strings.Trim(t.BaseUrl, "/") + } + + if t.Host == "" { + return fmt.Errorf("missing data_source_name for target %+v", t) + } + checkCollectorRefs(t.CollectorRefs, t.Name) + + if len(t.Labels) > 0 { + err := t.checkLabelCollisions() + if err != nil { + return err + } + } + } else { + for _, file := range t.TargetsFiles { + if file == "" { + return fmt.Errorf("missing targets_files pattern") + } + } + } + if t.AuthConfig.Mode == "" { + t.AuthConfig.Mode = "basic" + } + + return checkOverflow(t.XXX, "target") +} + +// checkLabelCollisions checks for label collisions between StaticConfig labels and Metric labels. +func (t *TargetConfig) checkLabelCollisions() error { + sclabels := make(map[string]interface{}) + for _, l := range t.Labels { + sclabels[l] = nil + } + + for _, c := range t.collectors { + // for _, m := range c.Metrics { + for _, cs := range c.CollectScripts { + for _, a := range cs.Actions { + fmt.Printf("action type: %s", reflect.TypeOf(a)) + reslist := a.GetMetrics() + for _, res := range reslist { + for _, m := range res.mc { + if keymap, ok := m.KeyLabels.(map[string]string); ok { + for _, l := range keymap { + if _, ok := sclabels[l]; ok { + return fmt.Errorf( + "label collision in target %q: label %q is defined both by a static_config and by metric %q of collector %q", + t.Name, l, m.Name, c.Name) + } + } + } + } + } + } + } + } + return nil +} + +// +// Collectors +// + +// type mapScript map[string]*YAMLScript + +// func (m mapScript) MarshalText() (text []byte, err error) { +// var res []byte +// for name, sc := range m { +// b, err := yaml.Marshal(sc) +// if err != nil { +// return nil, err +// } +// res = append(res, []byte(name)...) +// res = append(res, []byte(":\n")...) +// b = bytes.Replace(b, []byte("|\n"), []byte(""), 1) +// res = append(res, b...) +// } +// return res, nil +// } + +// CollectorConfig defines a set of metrics and how they are collected. +type CollectorConfig struct { + Name string `yaml:"collector_name"` // name of this collector + MetricPrefix string `yaml:"metric_prefix,omitempty"` // a prefix to ad dto all metric name; may be redefined in collector files + MinInterval model.Duration `yaml:"min_interval,omitempty"` // minimum interval between query executions + // Metrics []*MetricConfig `yaml:"metrics"` // metrics/queries defined by this collector + Templates map[string]string `yaml:"templates,omitempty"` // share custom templates/funcs for results templating + CollectScripts map[string]*YAMLScript `yaml:"scripts,omitempty"` // map of all independent scripts to collect metrics - each script can run in parallem + // CollectScripts mapScript `yaml:"scripts,omitempty"` // map of all independent scripts to collect metrics - each script can run in parallem + symtab map[string]any + + customTemplate *exporterTemplate // to store the custom Templates used by this collector + // Metrics []*MetricConfig // metrics defined by this collector + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for CollectorConfig. +func (c *CollectorConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + // Default to undefined (a negative value) so it can be overridden by the global default when not explicitly set. + c.MinInterval = -1 + c.MetricPrefix = "" + + type plain CollectorConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + + // if len(c.Metrics) == 0 { + // return fmt.Errorf("no metrics defined for collector %q", c.Name) + // } + + // build the default templates/funcs that my be used by all templates + if len(c.Templates) > 0 { + // c.customTemplate = template.New("default").Funcs(sprig.FuncMap()) + c.customTemplate = (*exporterTemplate)(template.New("default").Funcs(mymap())) + if c.customTemplate == nil { + return fmt.Errorf("for collector %s template is invalid", c.Name) + } + for name, tmpl := range c.Templates { + def := "{{- define \"" + name + "\" }}" + strings.ReplaceAll(tmpl, "\n", "") + "{{ end -}}" + ptr := (*template.Template)(c.customTemplate) + if tmp_tpl, err := ptr.Parse(def); err != nil { + return fmt.Errorf("for collector %s template %s is invalid: %s", c.Name, def, err) + } else { + c.customTemplate = (*exporterTemplate)(tmp_tpl) + } + } + } + + if c.CollectScripts != nil { + for collect_script_name, c_script := range c.CollectScripts { + if c_script.name == "" { + c_script.name = collect_script_name + } + if err := c_script.AddCustomTemplate(c.customTemplate); err != nil { + err = fmt.Errorf("script %s: error with custom template: %s", c_script.name, err) + return err + } + } + } + + return checkOverflow(c.XXX, "collector") +} + +type dumpCollectorConfig struct { + Name string `yaml:"collector_name"` // name of this collector + MetricPrefix string `yaml:"metric_prefix,omitempty"` // a prefix to ad dto all metric name; may be redefined in collector files + MinInterval model.Duration `yaml:"min_interval,omitempty"` // minimum interval between query executions + Templates map[string]string `yaml:"templates,omitempty"` // share custom templates/funcs for results templating + CollectScripts map[string]ActionsList `yaml:"scripts,omitempty"` // map of all independent scripts to collect metrics - each script can run in parallem +} + +func GetCollectorsDef(src_colls []*CollectorConfig) []*dumpCollectorConfig { + colls := make([]*dumpCollectorConfig, len(src_colls)) + for idx, coll := range src_colls { + colls[idx] = &dumpCollectorConfig{ + Name: coll.Name, + MetricPrefix: coll.MetricPrefix, + MinInterval: coll.MinInterval, + Templates: coll.Templates, + CollectScripts: GetScriptsDef(coll.CollectScripts), + } + } + return colls +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for CollectorConfig. +// func (c *CollectorConfig) MarshalText() (text []byte, err error) { +// var res []byte +// // res = append(res, []byte(name)...) +// // res = append(res, []byte(":\n")...) +// // b = bytes.Replace(b, []byte("|\n"), []byte(""), 1) +// // res = append(res, b...) +// if b, err := yaml.Marshal(c.Name); err != nil { +// return nil, err +// } else { +// res = append(res, []byte("collector_name: ")...) +// res = append(res, b...) +// } + +// if b, err := yaml.Marshal(c.MetricPrefix); err != nil { +// return nil, err +// } else { +// // res = append(res, []byte(name)...) +// res = append(res, []byte("metric_prefix: ")...) +// res = append(res, b...) +// } + +// if b, err := yaml.Marshal(c.MinInterval); err != nil { +// return nil, err +// } else { +// // res = append(res, []byte(name)...) +// res = append(res, []byte("min_interval: ")...) +// res = append(res, b...) +// } + +// if len(c.Templates) > 0 { +// if b, err := yaml.Marshal(c.Templates); err != nil { +// return nil, err +// } else { +// res = append(res, []byte("templates:\n")...) +// res = append(res, b...) +// } +// } +// if b, err := yaml.Marshal(c.CollectScripts); err != nil { +// return nil, err +// } else { +// res = append(res, []byte("scripts:\n")...) +// b = bytes.Replace(b, []byte("|\n"), []byte(""), 1) +// res = append(res, b...) +// } +// return res, nil +// } + +// MetricConfig defines a Prometheus metric, the SQL query to populate it and the mapping of columns to metric +// keys/values. +type MetricConfig struct { + Name string `yaml:"metric_name"` // the Prometheus metric name + TypeString string `yaml:"type"` // the Prometheus metric type + Help string `yaml:"help"` // the Prometheus metric help text + // KeyLabels map[string]string `yaml:"key_labels,omitempty"` // expose these atributes as labels from JSON object: format name: value with name and value that should be template + KeyLabels any `yaml:"key_labels,omitempty"` // expose these atributes as labels from JSON object: format name: value with name and value that should be template + // Labels string `yaml:"labels,omitempty"` // expose these atributes as labels like key_labels but should be a variable template: format name: value with name and value that should be template + StaticLabels map[string]string `yaml:"static_labels,omitempty"` // fixed key/value pairs as static labels + ValueLabel string `yaml:"value_label,omitempty"` // with multiple value columns, map their names under this label + Values map[string]string `yaml:"values"` // expose each of these columns as a value, keyed by column name + // ResultFields []string `yaml:"results,omitempty"` // field name in JSON where to find a list of results + Scope string `yaml:"scope,omitempty"` // var path where to collect data: shortcut for {{ .scope.path.var }} + + valueType prometheus.ValueType // TypeString converted to prometheus.ValueType + key_labels_map map[string]string + key_labels *Field + // name *Field + // help *Field + // metric_type *Field + // labels *Field + // Catches all undefined fields and must be empty after parsing. + // XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// ValueType returns the metric type, converted to a prometheus.ValueType. +func (m *MetricConfig) ValueType() prometheus.ValueType { + return m.valueType +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for MetricConfig. +func (m *MetricConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain MetricConfig + if err := unmarshal((*plain)(m)); err != nil { + return err + } + + // Check required fields + if m.Name == "" { + return fmt.Errorf("missing name for metric %+v", m) + } + // if val, err := NewField(m.Name, nil); err == nil { + // m.name = val + // } else { + // return err + // } + + if m.TypeString == "" { + return fmt.Errorf("missing type for metric %q", m.Name) + } + // if val, err := NewField(m.TypeString, nil); err == nil { + // m.metric_type = val + // } else { + // return err + // } + + // help is not mandatory: so empty is valid + // if m.Help == "" { + // return fmt.Errorf("missing help for metric %q", m.Name) + // } + + // if val, err := NewField(m.Help, nil); err == nil { + // m.help = val + // } else { + // return err + // } + // if m.Labels != "" { + // if val, err := NewField(m.Help, nil); err == nil { + // m.help = val + // } else { + // return err + // } + // } + + switch strings.ToLower(m.TypeString) { + case "counter": + m.valueType = prometheus.CounterValue + case "gauge": + m.valueType = prometheus.GaugeValue + default: + return fmt.Errorf("unsupported metric type: %s", m.TypeString) + } + + // m.keyLabels := make(map[Label]*Label,0 len(m.KeyLabels)) + // Check for duplicate key labels + if m.KeyLabels != nil { + switch ktype := m.KeyLabels.(type) { + case map[string]string: + for key := range ktype { + checkLabel(key, "metric", m.Name) + } + m.key_labels_map = ktype + case map[string]any: + m.key_labels_map = make(map[string]string, len(ktype)) + for key, val_raw := range ktype { + checkLabel(key, "metric", m.Name) + if val, ok := val_raw.(string); ok { + m.key_labels_map[key] = val + } + } + case string: + if ktype != "" { + if val, err := NewField(ktype, nil); err == nil { + m.key_labels = val + } else { + return err + } + } + default: + return fmt.Errorf("key_labels should be a map[string][string] or Template(string) that will contain a map[string][string] for metric %q", m.Name) + } + } + + if len(m.Values) == 0 { + return fmt.Errorf("no values defined for metric %q", m.Name) + } + + if len(m.Values) > 1 { + // Multiple value columns but no value label to identify them + if m.ValueLabel == "" { + return fmt.Errorf("value_label must be defined for metric with multiple values %q", m.Name) + } + checkLabel(m.ValueLabel, "value_label for metric", m.Name) + } + + // return checkOverflow(m.XXX, "metric") + return nil +} + +// Secret special type for storing secrets. +type Secret string + +// UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets. +func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain Secret + return unmarshal((*plain)(s)) +} + +// MarshalYAML implements the yaml.Marshaler interface for Secrets. +func (s Secret) MarshalYAML() (interface{}, error) { + if s != "" { + return "", nil + } + return nil, nil +} + +// ConvertibleBoolean special type to retrive 1 yes true to boolean true +type ConvertibleBoolean bool + +func (bit *ConvertibleBoolean) UnmarshalJSON(data []byte) error { + asString := strings.ToLower(string(data)) + if asString == "1" || asString == "true" || asString == "yes" || asString == "on" { + *bit = true + } else if asString == "0" || asString == "false" || asString == "no" || asString == "off" { + *bit = false + } else { + return fmt.Errorf("boolean unmarshal error: invalid input %s", asString) + } + return nil +} + +type AuthConfig struct { + Mode string `yaml:"mode,omitempty"` // basic, encrypted, bearer + Username string `yaml:"user,omitempty"` + Password Secret `yaml:"password,omitempty"` + Token Secret `yaml:"token,omitempty"` + authKey string +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for authConfig +func (auth *AuthConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain AuthConfig + if err := unmarshal((*plain)(auth)); err != nil { + return err + } + + // Check required fields + if auth.Mode == "" { + auth.Mode = "basic" + } else { + auth.Mode = strings.ToLower(auth.Mode) + mode := make(map[string]int) + for _, val := range []string{"basic", "token", "script"} { + mode[val] = 1 + } + if _, err := mode[auth.Mode]; !err { + return fmt.Errorf("invalid mode auth %s", auth.Mode) + } + } + if auth.Mode == "token" && auth.Token == "" { + return fmt.Errorf("token not set with auth mode 'token'") + } + + return nil +} + +// MarshalYAML implements the yaml.Marshaler interface for AuthConfig. +// func (auth AuthConfig) MarshalYAML() (interface{}, error) { +// var ( +// ymlname string +// val string +// buffer bytes.Buffer +// ) +// nptype := reflect.TypeOf(auth) +// values := reflect.ValueOf(auth) +// for i := 0; i < nptype.NumField(); i++ { +// // prints empty line if there is no json tag for the field +// field := nptype.Field(i) +// val = "" +// t_name := field.Type.Name() +// if t_name == "string" || t_name == "Secret" { +// val = values.Field(i).String() +// if val == "" { +// continue +// } +// } +// tags := field.Tag.Get("yaml") +// elmts := strings.Split(tags, ",") +// if len(elmts) > 0 { +// ymlname = elmts[0] +// } else { +// ymlname = "" +// } +// if field.Type.Name() == "string" { +// // val = values.Field(i) +// buffer.WriteString(ymlname) +// buffer.WriteString(": ") +// buffer.WriteString(val) +// buffer.WriteString("\n") +// } else { +// indent := " " +// buffer.WriteString(ymlname) +// buffer.WriteString(":\n") +// value := values.Field(i) +// b, err := yaml.Marshal(value.Interface()) +// if err == nil { +// b = bytes.TrimRight(b, "\n") +// res := bytes.SplitAfter(b, []byte("\n")) +// if len(res) > 1 { +// for _, val := range res { +// buffer.WriteString(indent) +// buffer.Write(val) +// // buffer.WriteString("\n") +// } +// } +// } else { +// return nil, err +// } +// } +// } +// return buffer.String(), nil +// } + +// ************************************************************************************************* +func checkCollectorRefs(collectorRefs []string, ctx string) error { + // At least one collector, no duplicates + if len(collectorRefs) == 0 { + return fmt.Errorf("no collectors defined for %s", ctx) + } + for i, ci := range collectorRefs { + for _, cj := range collectorRefs[i+1:] { + if ci == cj { + return fmt.Errorf("duplicate collector reference %q in %s", ci, ctx) + } + } + } + return nil +} + +func resolveCollectorRefs( + collectorRefs []string, collectors map[string]*CollectorConfig, ctx string) ([]*CollectorConfig, error) { + resolved := make([]*CollectorConfig, 0, len(collectorRefs)) + for _, cref := range collectorRefs { + // check if cref(a collector name) is a pattern or not + if strings.HasPrefix(cref, "~") { + pat := regexp.MustCompile(cref[1:]) + for c_name, c := range collectors { + if pat.MatchString(c_name) { + resolved = append(resolved, c) + } + } + } else { + c, found := collectors[cref] + if !found { + return nil, fmt.Errorf("unknown collector %q referenced in %s", cref, ctx) + } + resolved = append(resolved, c) + } + } + return resolved, nil +} + +func checkLabel(label string, ctx ...string) error { + if label == "" { + return fmt.Errorf("empty label defined in %s", strings.Join(ctx, " ")) + } + if label == "job" || label == "instance" { + return fmt.Errorf("reserved label %q redefined in %s", label, strings.Join(ctx, " ")) + } + return nil +} + +func checkOverflow(m map[string]interface{}, ctx string) error { + if len(m) > 0 { + var keys []string + for k := range m { + keys = append(keys, k) + } + return fmt.Errorf("unknown fields '%s' in '%s'", strings.Join(keys, ", "), ctx) + } + return nil +} diff --git a/content.go b/content.go new file mode 100644 index 0000000..fc9ed63 --- /dev/null +++ b/content.go @@ -0,0 +1,237 @@ +package main + +import ( + "fmt" + "html/template" + "net/http" + "runtime" + + "github.com/prometheus/common/version" + "gopkg.in/yaml.v3" +) + +const ( + docsUrl = "https://github.com/peekjef72/httpapi_exporter#readme" + templates = ` + {{ define "page" -}} + + + Prometheus {{ .ExporterName }} + + + + + {{template "content" .}} + + + {{- end }} + + {{ define "content.home" -}} +

This is a Prometheus {{ .ExporterName }} instance. + You are probably looking for its metrics handler.

+ {{- end }} + + {{ define "content.config" -}} +

Configuration

+
{{ .Config }}
+ {{- end }} + + {{ define "content.targets" -}} +

Targets

+
{{ .Targets }}
+ {{- end }} + + {{ define "content.status" -}} +

Build Information

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Version{{ .Version.Version }}
Revision{{ .Version.Revision }}
Branch{{ .Version.Branch }}
BuildUser{{ .Version.BuildUser }}
BuildDate{{ .Version.BuildDate }}
GoVersion{{ .Version.GoVersion }}
+ {{- end }} + + {{ define "content.error" -}} +

Error

+
{{ .Err }}
+ {{- end }} + ` +) + +type versionInfo struct { + Version string + Revision string + Branch string + BuildUser string + BuildDate string + GoVersion string +} +type tdata struct { + ExporterName string + MetricsPath string + DocsUrl string + + // `/config` only + Config string + + // `:targets` only + Targets string + + // status + Version versionInfo + // `/error` only + Err error +} + +var ( + allTemplates = template.Must(template.New("").Parse(templates)) + homeTemplate = pageTemplate("home") + configTemplate = pageTemplate("config") + targetsTemplate = pageTemplate("targets") + statusTemplate = pageTemplate("status") + errorTemplate = pageTemplate("error") +) + +func pageTemplate(name string) *template.Template { + pageTemplate := fmt.Sprintf(`{{define "content"}}{{template "content.%s" .}}{{end}}{{template "page" .}}`, name) + return template.Must(template.Must(allTemplates.Clone()).Parse(pageTemplate)) +} + +// HomeHandlerFunc is the HTTP handler for the home page (`/`). +func HomeHandlerFunc(metricsPath string, exporter Exporter) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + homeTemplate.Execute(w, &tdata{ + ExporterName: exporter.Config().Globals.ExporterName, + MetricsPath: metricsPath, + DocsUrl: docsUrl, + }) + } +} + +// ConfigHandlerFunc is the HTTP handler for the `/config` page. It outputs the configuration marshaled in YAML format. +func ConfigHandlerFunc(metricsPath string, exporter Exporter) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + config, err := exporter.Config().YAML() + if err != nil { + HandleError(0, err, metricsPath, exporter, w, r) + return + } + configTemplate.Execute(w, &tdata{ + ExporterName: exporter.Config().Globals.ExporterName, + MetricsPath: metricsPath, + DocsUrl: docsUrl, + Config: string(config), + }) + } +} + +// ConfigHandlerFunc is the HTTP handler for the `/config` page. It outputs the configuration marshaled in YAML format. +func StatusHandlerFunc(metricsPath string, exporter Exporter) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + + vinfos := versionInfo{ + Version: version.Version, + Revision: version.Revision, + Branch: version.Branch, + BuildUser: version.BuildUser, + BuildDate: version.BuildDate, + GoVersion: runtime.Version(), + } + + statusTemplate.Execute(w, &tdata{ + ExporterName: exporter.Config().Globals.ExporterName, + MetricsPath: metricsPath, + DocsUrl: docsUrl, + Version: vinfos, + }) + } +} + +// TargetsHandlerFunc is the HTTP handler for the `/target` page. It outputs the targets configuration marshaled in YAML format. +func TargetsHandlerFunc(metricsPath string, exporter Exporter) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + var targets_cfg []byte + var err error + c := exporter.Config() + // for _, t := range c.Targets { + targets_cfg, err = yaml.Marshal(c.Targets) + if err != nil { + // content = nil + HandleError(0, err, metricsPath, exporter, w, r) + return + } + // targets_cfg = append(targets_cfg, content...) + // } + targetsTemplate.Execute(w, &tdata{ + ExporterName: exporter.Config().Globals.ExporterName, + MetricsPath: metricsPath, + DocsUrl: docsUrl, + Targets: string(targets_cfg), + }) + } +} + +// HandleError is an error handler that other handlers defer to in case of error. It is important to not have written +// anything to w before calling HandleError(), or the 500 status code won't be set (and the content might be mixed up). +func HandleError(status int, err error, metricsPath string, exporter Exporter, w http.ResponseWriter, r *http.Request) { + if status == 0 { + status = http.StatusInternalServerError + } + w.WriteHeader(status) + errorTemplate.Execute(w, &tdata{ + ExporterName: exporter.Config().Globals.ExporterName, + MetricsPath: metricsPath, + DocsUrl: docsUrl, + Err: err, + }) +} diff --git a/contribs/dashboards/CITRIX ADC Netscaler Stats.json b/contribs/dashboards/CITRIX ADC Netscaler Stats.json new file mode 100644 index 0000000..6a4000c --- /dev/null +++ b/contribs/dashboards/CITRIX ADC Netscaler Stats.json @@ -0,0 +1,6410 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.16" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 1, + "id": null, + "iteration": 1695549945022, + "links": [], + "panels": [ + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 59, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 0, + "y": 1 + }, + "id": 57, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^edition$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.11", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_bandwidth_licensed{instance=~\"$instance\"}", + "format": "table", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Edition", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 15, + "x": 9, + "y": 1 + }, + "id": 56, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^version$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.11", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_bandwidth_licensed{instance=~\"$instance\"}", + "format": "table", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "version", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 4 + }, + "id": 62, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^status$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.11", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ha_available{instance=~\"$instance\"}", + "format": "table", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "HA Available", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "Mbits" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 4 + }, + "id": 730, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.11", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_bandwidth_licensed{instance=~\"$instance\"}", + "format": "table", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Licensed Bandwidth", + "type": "stat" + } + ], + "title": "Configuration", + "type": "row" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 61, + "panels": [], + "title": "High Availability", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "Down (n/a)", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "dark-green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 2 + }, + "id": 63, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ha_state{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "State", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Other", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Primary", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 3, + "text": "Secondary", + "to": "", + "type": 1, + "value": "2" + } + ], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "semi-dark-red", + "value": null + }, + { + "color": "semi-dark-green", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 2 + }, + "id": 64, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ha_node_state{instance=~\"$instance\"}", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Node Status", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "dateTimeAsIso" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 12, + "x": 12, + "y": 2 + }, + "id": 65, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ha_node_start_timestamp{instance=~\"$instance\"}*1000", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Last transition", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 5 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 5 + }, + "id": 66, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ha_node_propagation_timeout_total{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "legendFormat": "count", + "refId": "A" + } + ], + "title": "Propagation timeout Count", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 5 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 5 + }, + "id": 67, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ha_node_sync_failure_total{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "legendFormat": "count", + "refId": "A" + } + ], + "title": "Sync Failure", + "type": "stat" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 8 + }, + "id": 18, + "panels": [], + "title": "System", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "Down", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + }, + { + "color": "dark-green", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 9, + "x": 0, + "y": 9 + }, + "id": 16, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_probe_success{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "State", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 8, + "x": 9, + "y": 9 + }, + "id": 48, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_cpu_number_total{instance=~\"$instance\"}", + "format": "table", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "CPU ", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + } + ], + "noValue": "N/A", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 17, + "y": 9 + }, + "id": 173, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_memory_total_available_bytes{instance=~\"$instance\"}", + "format": "table", + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Memory", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "percentunit" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 12 + }, + "hiddenSeries": false, + "id": 13, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:7745", + "alias": "Total", + "hiddenSeries": true + } + ], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "( citrixadc_ns_rx_mbits_rate {instance=~\"$instance\"} / sum(citrixadc_bandwidth_licensed{instance=~\"$instance\"}) without(edition,version) )", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "received", + "refId": "A" + }, + { + "exemplar": true, + "expr": "( citrixadc_ns_tx_mbits_rate {instance=~\"$instance\"} / sum(citrixadc_bandwidth_licensed{instance=~\"$instance\"}) without(edition,version) )", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "transmitted", + "refId": "B" + }, + { + "exemplar": true, + "expr": "( (citrixadc_ns_rx_mbits_rate {instance=~\"$instance\"} + citrixadc_ns_tx_mbits_rate {instance=~\"$instance\"}) / sum(citrixadc_bandwidth_licensed{instance=~\"$instance\"}) without(edition,version) )", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Total", + "refId": "C" + } + ], + "thresholds": [ + { + "$$hashKey": "object:7758", + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 1, + "yaxis": "left" + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Licensed Throughput percent (stacked)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:82", + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:83", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "Mbits" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 12 + }, + "hiddenSeries": false, + "id": 1179, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:5767", + "alias": "transmitted", + "stack": "B" + }, + { + "$$hashKey": "object:5774", + "alias": "received", + "stack": "B" + }, + { + "$$hashKey": "object:5871", + "alias": "Licensed Bandwidth", + "color": "#C4162A", + "fill": 0 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ns_rx_mbits_rate {instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "received", + "refId": "A" + }, + { + "exemplar": true, + "expr": "citrixadc_ns_tx_mbits_rate {instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "transmitted", + "refId": "B" + }, + { + "exemplar": true, + "expr": "citrixadc_bandwidth_licensed{instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Licensed Bandwidth", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Licensed Throughput rate (stacked) ", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:82", + "format": "Mbits", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:83", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 23 + }, + "hiddenSeries": false, + "id": 3, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_packet_cpu_usage_percent{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "cpu usage", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Utilization Percentage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:113", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:114", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 23 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_memory_usage_percent{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "mem % used", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory Utilization Percentage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 6, + "y": 32 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_var_partition_used_percent{instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "/var", + "refId": "A" + }, + { + "exemplar": true, + "expr": "citrixadc_flash_partition_used_percent{instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "/flash", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Disk Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 35, + "panels": [], + "title": "Interfaces Stats", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "unit": "bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 42 + }, + "hiddenSeries": false, + "id": 36, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:369", + "alias": "/^Out.*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_interface_tot_rx_bytes_total{instance=~\"$instance\"}[2m]) * 8", + "interval": "", + "legendFormat": "In {{ citrixadc_interface_id }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_interface_tot_tx_bytes_total{instance=~\"$instance\"}[2m]) * 8", + "hide": false, + "interval": "", + "legendFormat": "Out {{ citrixadc_interface_id }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Average In/out Bits by second by interface", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 42 + }, + "hiddenSeries": false, + "id": 1453, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:369", + "alias": "/^Out.*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_interface_rx_bytes_rate{instance=~\"$instance\"} * 8", + "hide": false, + "interval": "", + "legendFormat": "In {{ citrixadc_interface_id }}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "citrixadc_interface_tx_bytes_rate{instance=~\"$instance\"} * 8", + "hide": false, + "interval": "", + "legendFormat": "Out {{ citrixadc_interface_id }}", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Instant In/out Bits by second by interface", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 52 + }, + "hiddenSeries": false, + "id": 38, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_interface_tot_multicast_packets_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "Multicast pkt {{ citrixadc_interface_id }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Multicast Packet by second by interface", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 52 + }, + "hiddenSeries": false, + "id": 37, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:635", + "alias": "/^Out.*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_interface_err_dropped_rx_packets_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "In {{ citrixadc_interface_id }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_interface_err_dropped_tx_packets_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "Out {{ citrixadc_interface_id }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In/out Dropped Pkt by second by Interface", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 60 + }, + "id": 29, + "panels": [], + "title": "IP stats", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 61 + }, + "hiddenSeries": false, + "id": 32, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:583", + "alias": "Out", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_rx_bytes_total{instance=~\"$instance\"}[2m]) * 8", + "interval": "", + "legendFormat": "In", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tx_bytes_total{instance=~\"$instance\"}[2m]) *8 ", + "hide": false, + "interval": "", + "legendFormat": "Out", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In/out Bits by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 61 + }, + "hiddenSeries": false, + "id": 30, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:547", + "alias": "Out", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_rx_bytes_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "In", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tx_bytes_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "Out", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In/out Packets by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "cps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 69 + }, + "hiddenSeries": false, + "id": 31, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_routed_packets_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "routed", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Packet routed by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "cps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "cps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 69 + }, + "hiddenSeries": false, + "id": 33, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_fragments_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "fragmented", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_successful_assembly_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "assembly", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_address_lookup_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "lookup", + "refId": "C" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_address_lookup_fail_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "Failed lookup", + "refId": "D" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_udp_fragments_forwarded_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "udp fragments fwd", + "refId": "E" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_tcp_fragments_forwarded_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "tcp fragments fwd", + "refId": "F" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ip_tot_bad_checksums_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "bad checksum", + "refId": "G" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "packet stats", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "cps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 77 + }, + "id": 313, + "panels": [], + "title": "UDP stats", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "decbits" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 78 + }, + "hiddenSeries": false, + "id": 447, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:547", + "alias": "/^Out .*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_udp_tot_rx_bytes_total {instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "In {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_udp_tot_tx_bytes_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "Out {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In/out bits by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "decbits", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 78 + }, + "hiddenSeries": false, + "id": 453, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:1966", + "alias": "/^Out /", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_udp_tot_rx_packets_total{instance=~\"$instance\"}[2m]) * 8", + "interval": "", + "legendFormat": "In {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_udp_tot_tx_packets_total{instance=~\"$instance\"}[2m]) *8 ", + "hide": false, + "interval": "", + "legendFormat": "Out {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In / Out packets by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 6, + "y": 86 + }, + "hiddenSeries": false, + "id": 448, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "show": true, + "total": true, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_udp_tot_unknown_service_packets_total {instance=~\"$instance\"}[2m]) * 8", + "interval": "", + "legendFormat": "Unknown {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_udp_tot_bad_checksum_packets_total {instance=~\"$instance\"}[2m]) *8 ", + "hide": false, + "interval": "", + "legendFormat": "bad checksum {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Unknown - bad checksum packets by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 94 + }, + "id": 451, + "panels": [], + "title": "TCP Stats", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 95 + }, + "hiddenSeries": false, + "id": 449, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:547", + "alias": "/^Out .*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_rx_bytes_total {instance=~\"$instance\"}[2m]) * 8 ", + "interval": "", + "legendFormat": "In {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_tx_bytes_total{instance=~\"$instance\"}[2m]) * 8", + "hide": false, + "interval": "", + "legendFormat": "Out {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In/out bits by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 95 + }, + "hiddenSeries": false, + "id": 724, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:547", + "alias": "/^Out .*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_rx_packets_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "In {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_tx_packets_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "Out {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "In/out packets by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 103 + }, + "hiddenSeries": false, + "id": 455, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_client_connections_opened_total {instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "client opened cnx {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_server_connections_opened_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "server opened cnx {{ instance }}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_syn_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "SYN received {{ instance }}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_client_fin_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "client FIN received {{ instance }}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_server_fin_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "server FIN received {{ instance }}", + "refId": "E" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_tot_syn_probe_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "PROBE {{ instance }}", + "refId": "F" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TCP metrics by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 11, + "w": 12, + "x": 12, + "y": 103 + }, + "hiddenSeries": false, + "id": 1727, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_full_retransmit_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "full retransmitted {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_badchecksum_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "bad chksum {{ instance }}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_reset_threshold_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "RESET {{ instance }}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_bad_connection_state_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "BAD state {{ instance }}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_out_of_window_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "out of window {{ instance }}", + "refId": "E" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_syn_dropped_congestion_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "SYN dropped congestion {{ instance }}", + "refId": "F" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_tcp_err_any_port_fail_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "any port fail {{ instance }}", + "refId": "G" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TCP \"errors\" packets by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 114 + }, + "id": 590, + "panels": [], + "title": "HTTP Stats", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 115 + }, + "hiddenSeries": false, + "id": 454, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:547", + "alias": "/^Responses /", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_requests_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "Requests {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_responses_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "Responses {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Request, Response by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 115 + }, + "hiddenSeries": false, + "id": 728, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:547", + "alias": "/^Responses /", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_http_requests_rate{instance=~\"$instance\"}", + "interval": "", + "legendFormat": "Requests {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "citrixadc_http_responses_rate{instance=~\"$instance\"}", + "hide": false, + "interval": "", + "legendFormat": "Responses {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Request, Response instant value by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 123 + }, + "hiddenSeries": false, + "id": 726, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:2495", + "alias": "/.*response.*/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_10_requests_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "HTTP 1.0 req {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_11_requests_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "HTTP 1.1 req {{ instance }}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_10_responses_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "HTTP 1.0 response {{ instance }}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_10_responses_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "HTTP 1.1 response {{ instance }}", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP request/response by version by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 123 + }, + "hiddenSeries": false, + "id": 725, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_gets_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "GET {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_posts_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "POST {{ instance }}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_others_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "others {{ instance }}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP methods by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 131 + }, + "hiddenSeries": false, + "id": 727, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_chunked_requests_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "req {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_tot_chunked_responses_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "response {{ instance }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP chunked by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 131 + }, + "hiddenSeries": false, + "id": 729, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_http_err_tot_incomplete_header_packets_total {instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "incomplete header {{ instance }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_err_tot_server_responses_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "error response {{ instance }}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_err_tot_large_body_packets_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "too large body {{ instance }}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_err_tot_large_chunk_requests_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "too large chunked size {{ instance }}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_http_err_tot_large_content_requests_total {instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "too large content size {{ instance }}", + "refId": "E" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP \"errors\" by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 139 + }, + "id": 40, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 140 + }, + "hiddenSeries": false, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_lb_hits_total{instance=~\"$instance\", citrixadc_lb_state=\"UP\"}[2m])", + "interval": "", + "legendFormat": "{{ citrixadc_lb_name }} {{ citrixadc_lb_type }} In", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total Hits/s by LB Service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 154 + }, + "hiddenSeries": false, + "id": 896, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum by(citrixadc_lb_name) (citrixadc_lb_members_up_total{instance=~\"$instance\"})", + "interval": "", + "legendFormat": "client connection {{ citrixadc_lb_name }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "percent of members UP for service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Load Balacing", + "type": "row" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 140 + }, + "id": 46, + "panels": [], + "repeat": "service", + "title": "LB service Stats $service", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "reqps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 141 + }, + "hiddenSeries": false, + "id": 893, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:2923", + "alias": "/Response/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_requests_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "interval": "", + "legendFormat": "requests {{ citrixadc_lb_name}}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_responses_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Responses {{ citrixadc_lb_name}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "requests & responses by second for $service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "cps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 141 + }, + "hiddenSeries": false, + "id": 44, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(citrixadc_lb_hits_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "interval": "", + "legendFormat": "Hits", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_responses_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "hide": true, + "interval": "", + "legendFormat": "Responses", + "refId": "B" + }, + { + "exemplar": true, + "expr": "sum(rate(citrixadc_lb_surge_count_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Surge", + "refId": "C" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_tolerable_transactions_count_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Tolerable Transactions", + "refId": "D" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_ttlb_calculated_transactions_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Calculated Transactions", + "refId": "E" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_deffered_requests_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "deffered req", + "refId": "F" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_spillover_count_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Spillover", + "refId": "G" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_invalid_response_request_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "invalid req", + "refId": "H" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_invalid_response_request_dropped_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "dropped req", + "refId": "I" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_busy_error_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "busy error", + "refId": "J" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Stats by second for $service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "cps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "unit": "Bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 150 + }, + "hiddenSeries": false, + "id": 864, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:3086", + "alias": "/^Out/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(rate(citrixadc_lb_request_bytes_received_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "interval": "", + "legendFormat": "In", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_response_bytes_received_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Out", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Bytes In/Out by sec for $service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "unit": "pps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 150 + }, + "hiddenSeries": false, + "id": 43, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:3048", + "alias": "/^Out/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum( rate(citrixadc_lb_packets_received_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m]) ) without( citrixadc_lb_state )", + "interval": "", + "legendFormat": "In", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum(rate(citrixadc_lb_packets_sent_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}[2m])) without( citrixadc_lb_state )", + "hide": false, + "interval": "", + "legendFormat": "Out", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Packet In/Out by sec for $service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "unit": "short" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 159 + }, + "hiddenSeries": false, + "id": 894, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(citrixadc_lb_current_client_connection_count{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}) without(citrixadc_lb_state)", + "interval": "", + "legendFormat": "client connection {{ citrixadc_lb_name }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "current client connection for $service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 159 + }, + "hiddenSeries": false, + "id": 895, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(citrixadc_lb_members_up_total{instance=~\"$instance\", citrixadc_lb_name=~\"$service\"}) without(citrixadc_lb_state)", + "interval": "", + "legendFormat": "client connection {{ citrixadc_lb_name }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "percent of members UP for $service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1198", + "format": "percent", + "label": null, + "logBase": 1, + "max": "100", + "min": null, + "show": true + }, + { + "$$hashKey": "object:1199", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 168 + }, + "id": 20, + "panels": [], + "title": "SSL", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 169 + }, + "hiddenSeries": false, + "id": 22, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ssl_tot_tlsv11_sessions_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "TLSv1 session", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_ssl_tot_v2_sessions_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "SSLv2 session", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "SSL V2 session by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 169 + }, + "hiddenSeries": false, + "id": 23, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ssl_tot_sessions_total{instance=~\"$instance\"}[2m])", + "interval": "", + "legendFormat": "num", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total SSL session by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 177 + }, + "hiddenSeries": false, + "id": 24, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ssl_tot_encode_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "num", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total SSL encode by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 177 + }, + "hiddenSeries": false, + "id": 25, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_ssl_crypto_utilization_stat_total{instance=~\"$instance\"}[2m])", + "hide": false, + "interval": "", + "legendFormat": "num", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total SSL crypto usage by second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:305", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:306", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Expiration in Days" + }, + "properties": [ + { + "id": "unit", + "value": "d" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "#EAB839", + "value": 45 + }, + { + "color": "green", + "value": 80 + } + ] + } + }, + { + "id": "custom.displayMode", + "value": "color-background" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Expiration Date" + }, + "properties": [ + { + "id": "unit", + "value": "dateTimeAsLocal" + } + ] + } + ] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 185 + }, + "id": 27, + "options": { + "showHeader": true + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_ssl_cert_days_to_expire{instance=~\"$instance\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "(time() + (citrixadc_ssl_cert_days_to_expire{instance=~\"$instance\"} * 86400)) * 1000", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Certificate expiration", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "citrixadc_cert_key", + "Value #A", + "Value #B" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value #A": "Expiration in Days", + "Value #B": "Expiration Date" + } + } + } + ], + "type": "table" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 192 + }, + "id": 175, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "Bps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 224 + }, + "hiddenSeries": false, + "id": 176, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "scopedVars": { + "ssl_service": { + "selected": false, + "text": "svctxsgdh.exterieur.d-a.com", + "value": "svctxsgdh.exterieur.d-a.com" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_sslvserver_decrypt_bytes_total{instance=~\"$instance\", citrixadc_sslvserver_name=~\"$ssl_service\"}[2m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "decrypt {{ citrixadc_sslvserver_name }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_sslvserver_encrypt_bytes_total{instance=~\"$instance\", citrixadc_sslvserver_name=~\"$ssl_service\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "encrypt {{ citrixadc_sslvserver_name }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "SSL Encrypt/Decrypt bytes/s $ssl_service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "cps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 224 + }, + "hiddenSeries": false, + "id": 177, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "scopedVars": { + "ssl_service": { + "selected": false, + "text": "svctxsgdh.exterieur.d-a.com", + "value": "svctxsgdh.exterieur.d-a.com" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_sslvserver_session_new_total{instance=~\"$instance\", citrixadc_sslvserver_name=~\"$ssl_service\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "new session {{ citrixadc_sslvserver_name }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_sslvserver_session_hits_total{instance=~\"$instance\", citrixadc_sslvserver_name=~\"$ssl_service\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "hits {{ citrixadc_sslvserver_name }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "SSL new session and hits /s for $ssl_service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "cps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "cps" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 233 + }, + "hiddenSeries": false, + "id": 178, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "scopedVars": { + "ssl_service": { + "selected": false, + "text": "svctxsgdh.exterieur.d-a.com", + "value": "svctxsgdh.exterieur.d-a.com" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_sslvserver_auth_success_total{instance=~\"$instance\", citrixadc_sslvserver_name=~\"$ssl_service\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "auth success {{ citrixadc_sslvserver_name }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_sslvserver_auth_failure_total{instance=~\"$instance\", citrixadc_sslvserver_name=~\"$ssl_service\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "auth failure {{ citrixadc_sslvserver_name }}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "SSL Encrypt/Decrypt bytes/s $ssl_service", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "cps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "repeat": "ssl_service", + "title": "SSL service Stats $ssl_service", + "type": "row" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 197 + }, + "id": 52, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 209 + }, + "hiddenSeries": false, + "id": 49, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_aaa_auth_success_total{instance=~\"$instance\"}[2m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "success auth", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_aaa_auth_fail_total {instance=~\"$instance\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "failed auth", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Auth", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 209 + }, + "hiddenSeries": false, + "id": 53, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(citrixadc_aaa_tot_sessions_total{instance=~\"$instance\"}[2m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "Total session", + "refId": "A" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_aaa_tot_sessiontimeout_total{instance=~\"$instance\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Timeout session", + "refId": "B" + }, + { + "exemplar": true, + "expr": "rate(citrixadc_aaa_tot_tm_sessions_total {instance=~\"$instance\"}[2m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "TM? session", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Session", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "Count of current Basic ICA only connections.\nCount of current SmartAccess ICA connections.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 218 + }, + "hiddenSeries": false, + "id": 1170, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "citrixadc_aaa_cur_ica_conn {instance=~\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "SmartAccess ICA", + "refId": "A" + }, + { + "exemplar": true, + "expr": "citrixadc_aaa_cur_ica_only_conn {instance=~\"$instance\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Basic ICA only conn", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "ICA", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:144", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:145", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "AAA Stats", + "type": "row" + } + ], + "refresh": "1m", + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(citrixadc_probe_success, instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Hostname", + "multi": false, + "name": "instance", + "options": [], + "query": { + "query": "label_values(citrixadc_probe_success, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(citrixadc_lb_packets_received_total{instance=~\"$instance\",citrixadc_lb_state=\"UP\"}, citrixadc_lb_name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "LBC service", + "multi": true, + "name": "service", + "options": [], + "query": { + "query": "label_values(citrixadc_lb_packets_received_total{instance=~\"$instance\",citrixadc_lb_state=\"UP\"}, citrixadc_lb_name)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(citrixadc_sslvserver_health{instance=~\"$instance\"}, citrixadc_sslvserver_name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "SSL service", + "multi": true, + "name": "ssl_service", + "options": [], + "query": { + "query": "label_values(citrixadc_sslvserver_health{instance=~\"$instance\"}, citrixadc_sslvserver_name)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "1s", + "2s", + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "CITRIX ADC Netscaler Stats", + "uid": "NT3Z0MJkk", + "version": 56 +} \ No newline at end of file diff --git a/contribs/dashboards/HP3PAR System.json b/contribs/dashboards/HP3PAR System.json new file mode 100644 index 0000000..a903c93 --- /dev/null +++ b/contribs/dashboards/HP3PAR System.json @@ -0,0 +1,3093 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.16" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1695551204099, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "system", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^model$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_system_id{host=~\"$host\"} ", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Model", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^version$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_system_id{host=~\"$host\"} ", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Version", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^patches$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_system_id{host=~\"$host\"} ", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Patches", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 12, + "y": 1 + }, + "id": 9, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^serial$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_system_id{host=~\"$host\"} ", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Serial", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 16, + "y": 1 + }, + "id": 10, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_system_nodes_total{host=~\"$host\"} ", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Total Nodes", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 2 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 2, + "w": 4, + "x": 20, + "y": 1 + }, + "id": 18, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_system_nodes_active{host=~\"$host\"} ", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Active Nodes", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 9, + "x": 0, + "y": 3 + }, + "id": 19, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_capacity_total_bytes{host=~\"$host\"} ", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Total Capacity by Disk Type", + "type": "stat" + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 15, + "x": 9, + "y": 3 + }, + "id": 20, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "lastNotNull", + "mean" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_capacity_free_bytes{host=~\"$host\"} ", + "interval": "", + "legendFormat": "{{ type}}", + "refId": "A" + } + ], + "title": "Total free Capacity by disk type", + "type": "timeseries" + }, + { + "datasource": null, + "description": "average cluster cpu usage ( over all cpu and all cluster's nodes)", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 4, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "lastNotNull", + "mean" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "avg by(mode) (hp3par_cpu_usage_percent{ mode != \"idle\"})", + "interval": "", + "legendFormat": "{{ mode }}", + "refId": "A" + } + ], + "title": "cpu usage", + "type": "timeseries" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "description": "Hits divided accesses displayed in percentage. (hitIO / accessIO percent) over 5 minutes", + "fieldConfig": { + "defaults": { + "unit": "percent" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 10 + }, + "hiddenSeries": false, + "id": 45, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:1090", + "alias": "/.*read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_memory_hit_percent{host=~\"$host\"}", + "interval": "", + "legendFormat": "node: {{ node }} - {{ mode }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory IO cache hit percent", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1012", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1013", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 18 + }, + "id": 46, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "lastNotNull", + "mean" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_memory_page_stats{host=~\"$host\"}", + "interval": "", + "legendFormat": "node: {{ node }} - pages {{ mode }}", + "refId": "A" + } + ], + "title": "Memory Page details", + "type": "timeseries" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 32, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "% Used" + }, + "properties": [ + { + "id": "unit", + "value": "percentunit" + }, + { + "id": "custom.displayMode", + "value": "gradient-gauge" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 0.85 + }, + { + "color": "red", + "value": 0.97 + } + ] + } + }, + { + "id": "custom.width", + "value": 234 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Avail Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 314 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "custom.width", + "value": 521 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "host" + }, + "properties": [ + { + "id": "custom.width", + "value": 237 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Snapshots Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 261 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Total Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 261 + } + ] + } + ] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 33, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "sum by(host, name) (hp3par_cpg_total_bytes{host=~\"$host\"}) != 0", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum by(host, name) (hp3par_cpg_available_bytes{host=~\"$host\"}) != 0", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "( sum by(host, name) (hp3par_cpg_total_bytes{host=~\"$host\"}) - sum by(host, name) (hp3par_cpg_available_bytes{host=~\"$host\"}) )/ (sum by(host, name) (hp3par_cpg_total_bytes{host=~\"$host\"}) +1 ) != 0", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + }, + { + "exemplar": true, + "expr": "sum by(host, name) (hp3par_cpg_snapshot_used_bytes{host=~\"$host\"}) != 0", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "D" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Common Provisionning Groups", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "host": true + }, + "indexByName": { + "Time": 0, + "Value #A": 4, + "Value #B": 3, + "Value #C": 5, + "host": 1, + "name": 2 + }, + "renameByName": { + "Value #A": "Total Size", + "Value #B": "Avail Size", + "Value #C": "% Used", + "Value #D": "Snapshots Size" + } + } + } + ], + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "percentunit" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 32 + }, + "hiddenSeries": false, + "id": 35, + "legend": { + "avg": false, + "current": false, + "hideZero": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "( sum by(host, name) (hp3par_cpg_total_bytes{host=~\"$host\"}) - sum by(host, name) (hp3par_cpg_available_bytes{host=~\"$host\"}) )/ (sum by(host, name) (hp3par_cpg_total_bytes{host=~\"$host\"}) +1 ) ", + "interval": "", + "legendFormat": "{{ name }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPG Used %", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:368", + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:369", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "decbytes" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 32 + }, + "hiddenSeries": false, + "id": 36, + "legend": { + "avg": false, + "current": false, + "hideZero": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "( sum by(host, name) (hp3par_cpg_snapshot_used_bytes{host=~\"$host\"})) != 0", + "interval": "", + "legendFormat": "{{ name }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPG Snapshots Used Bytes", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:368", + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:369", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "CPG", + "type": "row" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 28 + }, + "id": 38, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Used %" + }, + "properties": [ + { + "id": "unit", + "value": "percentunit" + }, + { + "id": "custom.displayMode", + "value": "gradient-gauge" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Compaction Ratio" + }, + "properties": [ + { + "id": "unit", + "value": "short" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Deduplication Ration" + }, + "properties": [ + { + "id": "unit", + "value": "short" + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 29 + }, + "id": 40, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Deduplication Ration" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "sum by(host, name, provisionningtype) (hp3par_volume_total_bytes{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum by(host, name, provisionningtype) (hp3par_volume_used_bytes{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "sum by(host, name, provisionningtype) (hp3par_volume_used_bytes{host=~\"$host\"}) / sum by(host, name, provisionningtype) (hp3par_volume_total_bytes{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + }, + { + "exemplar": true, + "expr": "sum by(host, name, provisionningtype) (hp3par_volume_compaction_ratio{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "D" + }, + { + "exemplar": true, + "expr": "sum by(host, name, provisionningtype) (hp3par_volume_deduplication_ratio{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "E" + } + ], + "title": "Volumes Infos", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "host": true + }, + "indexByName": {}, + "renameByName": { + "Value #A": "Total Size", + "Value #B": "Used Size", + "Value #C": "Used %", + "Value #D": "Compaction Ratio", + "Value #E": "Deduplication Ration", + "Value #Total Size": "Total Size", + "Value #Used Percent": "Used Percent", + "Value #Used Size": "Used Size" + } + } + } + ], + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 38 + }, + "hiddenSeries": false, + "id": 41, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:76", + "alias": "/read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_volume_read_io_per_second{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ name }}: read", + "refId": "A" + }, + { + "exemplar": true, + "expr": "hp3par_volume_write_io_per_second{host=~\"$host\"}", + "hide": false, + "interval": "", + "legendFormat": "{{ name }}: write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Volumes IO operations by sec over 5 min", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:734", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:735", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "decbytes" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 38 + }, + "hiddenSeries": false, + "id": 42, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:76", + "alias": "/read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_volume_read_bytes_per_second{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ name }}: read", + "refId": "A" + }, + { + "exemplar": true, + "expr": "hp3par_volume_write_bytes_per_second{host=~\"$host\"}", + "hide": false, + "interval": "", + "legendFormat": "{{ name }}: write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Volumes IO Bytes by sec over 5 min", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:790", + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:791", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "s" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 46 + }, + "hiddenSeries": false, + "id": 43, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:76", + "alias": "/read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_volume_read_latency_second{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ name }}: read", + "refId": "A" + }, + { + "exemplar": true, + "expr": "hp3par_volume_write_latency_second{host=~\"$host\"}", + "hide": false, + "interval": "", + "legendFormat": "{{ name }}: write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Volumes IO latency over 5 min", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:896", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:897", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 46 + }, + "id": 44, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "mean", + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_volume_busy_percent{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ name }}", + "refId": "A" + } + ], + "title": "Volumes Busy % over 5 min", + "type": "timeseries" + } + ], + "title": "Volumes", + "type": "row" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 29 + }, + "id": 12, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": ".* Size" + }, + "properties": [ + { + "id": "unit", + "value": "decbytes" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Free %" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "gradient-gauge" + }, + { + "id": "unit", + "value": "percent" + }, + { + "id": "custom.width", + "value": 291 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cageID" + }, + "properties": [ + { + "id": "custom.width", + "value": 63 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "cageSide" + }, + "properties": [ + { + "id": "custom.width", + "value": 76 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "magazine" + }, + "properties": [ + { + "id": "custom.width", + "value": 81 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "diskPos" + }, + "properties": [ + { + "id": "custom.width", + "value": 70 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "id" + }, + "properties": [ + { + "id": "custom.width", + "value": 33 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "type" + }, + "properties": [ + { + "id": "custom.width", + "value": 51 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Total Size" + }, + "properties": [ + { + "id": "custom.width", + "value": 167 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Left Life %" + }, + "properties": [ + { + "id": "custom.width", + "value": 251 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 14, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "avg by( cageID, cageSide,magazine,diskPos) (hp3par_physical_disk_total_bytes{host=~\"$host\"})", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{ id }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "avg by( cageID, cageSide,magazine,diskPos) (hp3par_physical_disk_failed_bytes{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "{{ id }}", + "refId": "C" + }, + { + "exemplar": true, + "expr": "avg by( cageID, cageSide,magazine,diskPos) (hp3par_physical_disk_free_bytes{host=~\"$host\"} )", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "{{ id }}", + "refId": "D" + }, + { + "exemplar": true, + "expr": "hp3par_physical_disk_free_bytes{host=~\"$host\"} / hp3par_physical_disk_total_bytes{host=~\"$host\"} * 100", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "E" + }, + { + "exemplar": true, + "expr": "avg by( cageID, cageSide,magazine,diskPos) (hp3par_physical_disk_life_left_percent{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Physical disks", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "daapplication": true, + "dacomposant": true, + "daversion": true, + "environment": true, + "host": true, + "instance": true, + "job": true, + "segment": true + }, + "indexByName": { + "Time": 0, + "Value #A": 15, + "Value #B": 18, + "Value #C": 19, + "Value #D": 16, + "Value #E": 17, + "cageID": 1, + "cageSide": 2, + "daapplication": 3, + "dacomposant": 4, + "daversion": 5, + "diskPos": 7, + "environment": 8, + "host": 9, + "id": 10, + "instance": 11, + "job": 12, + "magazine": 6, + "segment": 13, + "type": 14 + }, + "renameByName": { + "Value #A": "Total Size", + "Value #B": "Left Life %", + "Value #C": "Failed Size", + "Value #D": "Free Size", + "Value #E": "Free %" + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "celsius" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 38 + }, + "id": 16, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "mean", + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_physical_disk_temperature{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ id }}", + "refId": "A" + } + ], + "title": "Disk Temperature in Celsius", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 38 + }, + "id": 17, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "mean", + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_physical_disk_total_bytes{host=~\"$host\"} - hp3par_physical_disk_free_bytes{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ id }}", + "refId": "A" + } + ], + "title": "Disk usage", + "type": "timeseries" + } + ], + "title": "Physical disks", + "type": "row" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 22, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "CONFIG_WAIT", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 2, + "text": "ALPA_WAIT", + "to": "", + "type": 1, + "value": "2" + }, + { + "from": "", + "id": 3, + "text": "LOGIN_WAIT", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 4, + "text": "READY", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 5, + "text": "LOSS_SYNC", + "to": "", + "type": 1, + "value": "5" + }, + { + "from": "", + "id": 6, + "text": "ERROR_STATE", + "to": "", + "type": 1, + "value": "6" + }, + { + "from": "", + "id": 7, + "text": "XXX", + "to": "", + "type": 1, + "value": "7" + }, + { + "from": "", + "id": 8, + "text": "NONPARTICIPATE", + "to": "", + "type": 1, + "value": "8" + }, + { + "from": "", + "id": 9, + "text": "COREDUMP", + "to": "", + "type": 1, + "value": "9" + }, + { + "from": "", + "id": 10, + "text": "OFFLINE", + "to": "", + "type": 1, + "value": "10" + } + ] + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "orange", + "value": null + }, + { + "color": "green", + "value": 4 + }, + { + "color": "red", + "value": 5 + } + ] + } + }, + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "custom.align", + "value": "center" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "FailOver State" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "On", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 2, + "text": "Off", + "to": "", + "type": 1, + "value": "0" + } + ] + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(130, 135, 130)", + "value": null + }, + { + "color": "dark-green", + "value": 1 + } + ] + } + }, + { + "id": "custom.displayMode", + "value": "color-background" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 31 + }, + "id": 24, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "source" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "sum by(source, target, label, porttype, protocol) (hp3par_port_status{host=~\"$host\"})", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum by(source, target, label, porttype, protocol) (hp3par_port_failover_state{host=~\"$host\"})", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + } + ], + "title": "Port Status", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true + }, + "indexByName": { + "Time": 0, + "Value #A": 6, + "Value #B": 7, + "label": 3, + "porttype": 4, + "protocol": 5, + "source": 1, + "target": 2 + }, + "renameByName": { + "Value #A": "Status", + "Value #B": "FailOver State" + } + } + } + ], + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 39 + }, + "hiddenSeries": false, + "id": 26, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:76", + "alias": "/read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_port_read_io_per_second{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ source }} {{ porttype}}: read", + "refId": "A" + }, + { + "exemplar": true, + "expr": "hp3par_port_write_io_per_second{host=~\"$host\"}", + "hide": false, + "interval": "", + "legendFormat": "{{ source }} {{ porttype}}: write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Port IO operations by sec over 5 min", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "decbytes" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 39 + }, + "hiddenSeries": false, + "id": 27, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "sort": "max", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:76", + "alias": "/read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_port_read_bytes_per_second{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ source }} {{ porttype}}: read", + "refId": "A" + }, + { + "exemplar": true, + "expr": "hp3par_port_write_bytes_per_second{host=~\"$host\"}", + "hide": false, + "interval": "", + "legendFormat": "{{ source }} {{ porttype}}: write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Port IO Bytes by sec over 5 min", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "s" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 47 + }, + "hiddenSeries": false, + "id": 28, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "$$hashKey": "object:76", + "alias": "/read$/", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "hp3par_port_read_latency_second{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ source }} {{ porttype}}: read", + "refId": "A" + }, + { + "exemplar": true, + "expr": "hp3par_port_write_latency_second{host=~\"$host\"}", + "hide": false, + "interval": "", + "legendFormat": "{{ source }} {{ porttype}}: write", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Port IO latency over 5 min", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 47 + }, + "id": 30, + "options": { + "graph": {}, + "legend": { + "calcs": [ + "min", + "max", + "mean", + "last" + ], + "displayMode": "table", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "hp3par_port_busy_percent{host=~\"$host\"}", + "interval": "", + "legendFormat": "{{ source }} {{ porttype }}", + "refId": "A" + } + ], + "title": "Port Busy % over 5 min", + "type": "timeseries" + } + ], + "title": "Physical ports", + "type": "row" + } + ], + "refresh": false, + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(hp3par_up, host)", + "description": "", + "error": null, + "hide": 0, + "includeAll": false, + "label": null, + "multi": false, + "name": "host", + "options": [], + "query": { + "query": "label_values(hp3par_up, host)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "HP3PAR System", + "uid": "fK_lN3D4z", + "version": 17 +} \ No newline at end of file diff --git a/contribs/dashboards/Veeam Enterprise Manager.json b/contribs/dashboards/Veeam Enterprise Manager.json new file mode 100644 index 0000000..df641f6 --- /dev/null +++ b/contribs/dashboards/Veeam Enterprise Manager.json @@ -0,0 +1,4834 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.16" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "panel", + "id": "piechart", + "name": "Pie chart v2", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "iteration": 1695551238041, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Enterprise Manager - Summary", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "backup" + }, + "properties": [ + { + "id": "displayName", + "value": "Backup Servers" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "proxy" + }, + "properties": [ + { + "id": "displayName", + "value": "Proxy Servers" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "repository" + }, + "properties": [ + { + "id": "displayName", + "value": "Repository Servers" + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"server\", type=\"backup\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Backup Servers", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"server\", type=\"repository\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Repository Servers", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"server\", type=\"proxy\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Proxy Servers", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 12, + "y": 1 + }, + "id": 7, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"tasks\", type=\"scheduled_jobs\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Scueduled Jobs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 16, + "y": 1 + }, + "id": 12, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_vms_total_bytes{instance=~\"$enterprisemanager\", type=\"source_vms\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Total Source VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 20, + "y": 1 + }, + "id": 31, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_vms_total_bytes{instance=~\"$enterprisemanager\", type=\"full_backup_points\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Full backup VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 0, + "y": 6 + }, + "id": 8, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"tasks\", type=\"successful_vms\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Successful VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-orange", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 4, + "y": 6 + }, + "id": 10, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"tasks\", type=\"warning_vms\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Warning VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "dark-red", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 8, + "y": 6 + }, + "id": 9, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_element_count{instance=~\"$enterprisemanager\", count_type=\"tasks\", type=\"failed_vms\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Failed VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 12, + "y": 6 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_vms_count{instance=~\"$enterprisemanager\", type=\"protected\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Protected VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 16, + "y": 6 + }, + "id": 30, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_vms_total_bytes{instance=~\"$enterprisemanager\", type=\"incremental_backup_points\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Incremental backup VMs", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(228, 232, 228)", + "value": null + } + ] + }, + "unit": "decbytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 20, + "y": 6 + }, + "id": 32, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "value" + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_vms_total_bytes{instance=~\"$enterprisemanager\", type=\"replica_restore_points\"}", + "interval": "", + "legendFormat": "{{ type }}", + "refId": "A" + } + ], + "title": "Replica Restore VMs", + "type": "stat" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 157, + "panels": [ + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 70 + }, + { + "color": "red", + "value": 90 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "% Used" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "lcd-gauge" + }, + { + "id": "thresholds", + "value": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 70 + }, + { + "color": "red", + "value": 90 + } + ] + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Total bytes" + }, + "properties": [ + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.width", + "value": 138 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Free Bytes" + }, + "properties": [ + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.width", + "value": 140 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "type" + }, + "properties": [ + { + "id": "custom.width", + "value": 181 + } + ] + } + ] + }, + "gridPos": { + "h": 12, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 14, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "% Used" + } + ] + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "1 - (sum(veeam_em_overview_repositories_capacity_free_bytes{}) by (name, type) / sum(veeam_em_overview_repositories_capacity_total_bytes{}) by (name, type) )", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "{{ name }}", + "refId": "A" + }, + { + "exemplar": true, + "expr": "sum(veeam_em_overview_repositories_capacity_total_bytes{}) by (name, type)", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "sum(veeam_em_overview_repositories_capacity_free_bytes{}) by (name, type)", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + } + ], + "title": "Backup Repository - Usage Capacity", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "name", + "type", + "Value #A", + "Value #B", + "Value #C" + ] + } + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 4, + "Value #B": 2, + "Value #C": 3, + "name": 0, + "type": 1 + }, + "renameByName": { + "Value": "Usage", + "Value #A": "% Used", + "Value #B": "Total bytes", + "Value #C": "Free Bytes" + } + } + } + ], + "type": "table" + } + ], + "title": "Repositories", + "type": "row" + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 12 + }, + "id": 36, + "panels": [], + "title": "Job Historical Performance and Duration", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Warning" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "rgba(224, 218, 224, 0.58)", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/(Idle)|(Working)/" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 13 + }, + "id": 161, + "options": { + "displayLabels": [ + "value", + "name" + ], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 1) or vector(0)", + "instant": true, + "interval": "", + "legendFormat": "Success", + "refId": "A" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 2) or vector(0)", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "Warning", + "refId": "B" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 3) or vector(0)", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "Failed", + "refId": "C" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 4) or vector(0)", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "Idle", + "refId": "D" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 5) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Working", + "refId": "E" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Backup Job State (last 24 hours)", + "type": "piechart" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Warning" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "rgba(224, 218, 224, 0.58)", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/(Idle)|(Working)/" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 13 + }, + "id": 162, + "options": { + "displayLabels": [ + "value", + "name" + ], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\"} == 1) or vector(0)", + "instant": false, + "interval": "", + "legendFormat": "Success", + "refId": "A" + }, + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\"} == 2) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Warning", + "refId": "B" + }, + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\"} == 3) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Failed", + "refId": "C" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 4) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Idle", + "refId": "D" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"} == 5) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Working", + "refId": "E" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Backup VM Jobs State (last 24 hours)", + "type": "piechart" + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Success", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 2, + "text": "Warning", + "to": "", + "type": 1, + "value": "2" + }, + { + "from": "", + "id": 3, + "text": "Failed", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 4, + "text": "Idle", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 6, + "text": "Working", + "to": "", + "type": 1, + "value": "5" + }, + { + "from": "", + "id": 7, + "text": "!! undefined !!", + "to": "", + "type": 1, + "value": "0" + } + ] + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(106, 107, 105)", + "value": null + }, + { + "color": "green", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + }, + { + "color": "red", + "value": 3 + }, + { + "color": "rgb(164, 168, 168)", + "value": 4 + } + ] + } + }, + { + "id": "custom.width", + "value": 86 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Duration" + }, + "properties": [ + { + "id": "custom.width", + "value": 99 + }, + { + "id": "unit", + "value": "s" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "type" + }, + "properties": [ + { + "id": "custom.width", + "value": 146 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "startdate" + }, + "properties": [ + { + "id": "custom.width", + "value": 193 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backupserver" + }, + "properties": [ + { + "id": "custom.width", + "value": 197 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobname" + }, + "properties": [ + { + "id": "custom.width", + "value": 308 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobretries" + }, + "properties": [ + { + "id": "custom.width", + "value": 75 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "custom.width", + "value": 602 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Retries" + }, + "properties": [ + { + "id": "custom.width", + "value": 64 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 22 + }, + "id": 87, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Status" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "veeam_em_jobs_sessions_duration{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "veeam_em_jobs_sessions_retries{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + } + ], + "title": "Backup Job Status", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backupserver", + "jobname", + "name", + "Value #A", + "Value #B", + "Value #C" + ] + } + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 3, + "Value #B": 4, + "backupserver": 0, + "jobname": 1, + "jobretries": 5, + "name": 2 + }, + "renameByName": { + "Value #A": "Status", + "Value #B": "Duration", + "Value #C": "Retries" + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Success", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 2, + "text": "Warning", + "to": "", + "type": 1, + "value": "2" + }, + { + "from": "", + "id": 3, + "text": "Failed", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 4, + "text": "Idle", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 5, + "text": "!! Undefined !!", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 6, + "text": "Working", + "to": "", + "type": 1, + "value": "5" + } + ] + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(106, 107, 105)", + "value": null + }, + { + "color": "green", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + }, + { + "color": "red", + "value": 3 + }, + { + "color": "rgb(164, 168, 168)", + "value": 4 + } + ] + } + }, + { + "id": "custom.width", + "value": 89 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Duration" + }, + "properties": [ + { + "id": "custom.width", + "value": 99 + }, + { + "id": "unit", + "value": "s" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backupserver" + }, + "properties": [ + { + "id": "custom.width", + "value": 197 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobname" + }, + "properties": [ + { + "id": "custom.width", + "value": 443 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Retries" + }, + "properties": [ + { + "id": "custom.width", + "value": 65 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vmname" + }, + "properties": [ + { + "id": "custom.width", + "value": 148 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "VM Size" + }, + "properties": [ + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.width", + "value": 91 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "taskname" + }, + "properties": [ + { + "id": "custom.width", + "value": 311 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 30 + }, + "id": 160, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "vmname" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_duration{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_retries{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": true, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + }, + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_total_bytes{instance=~\"$enterprisemanager\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "D" + } + ], + "title": "Backup VMJob Status", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backupserver", + "jobname", + "taskname", + "vmname", + "Value #A", + "Value #B", + "Value #D" + ] + } + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 4, + "Value #B": 5, + "Value #D": 6, + "backupserver": 0, + "jobname": 1, + "taskname": 3, + "vmname": 2 + }, + "renameByName": { + "Value #A": "Status", + "Value #B": "Duration", + "Value #C": "Retries", + "Value #D": "VM Size" + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Undefined", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Failed", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 3, + "text": "Pending", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 4, + "text": "Working", + "to": "", + "type": 1, + "value": "5" + }, + { + "from": "", + "id": 5, + "text": "Warning", + "to": "", + "type": 1, + "value": "2" + } + ] + }, + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "rgb(131, 135, 135)", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + }, + { + "color": "red", + "value": 3 + }, + { + "color": "dark-purple", + "value": 4 + } + ] + } + }, + { + "id": "custom.width", + "value": 108 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vmname" + }, + "properties": [ + { + "id": "custom.width", + "value": 108 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "message" + }, + "properties": [ + { + "id": "custom.width", + "value": 507 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "taskname" + }, + "properties": [ + { + "id": "custom.width", + "value": 283 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 39 + }, + "id": 169, + "options": { + "showHeader": true, + "sortBy": [] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_error{instance=~\"$enterprisemanager\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backup VMJob Errors", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backupserver", + "jobname", + "message", + "taskname", + "vmname", + "Value" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 4, + "backupserver": 0, + "jobname": 1, + "message": 5, + "taskname": 3, + "vmname": 2 + }, + "renameByName": { + "Value": "Status", + "Value #A": "Status" + } + } + } + ], + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "s" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 47 + }, + "hiddenSeries": false, + "id": 34, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_overview_jobs_max_duration{instance=~\"$enterprisemanager\"}", + "interval": "", + "legendFormat": "MAX {{ jobname }} ( {{ type }} )", + "refId": "A" + }, + { + "exemplar": true, + "expr": "avg(veeam_em_jobs_sessions_duration{instance=~\"$enterprisemanager\"}) by(backupserver)", + "hide": false, + "interval": "", + "legendFormat": "avg {{ backupserver }} ", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Job Duration (All type)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:341", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:342", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": null, + "fieldConfig": { + "defaults": { + "unit": "percentunit" + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 47 + }, + "hiddenSeries": false, + "id": 133, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.16", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "1 - (count (veeam_em_jobs_sessions_retries{instance=~\"$enterprisemanager\"} != 0 ) by(instance, backupserver)/ count(veeam_em_jobs_sessions_retries{instance=~\"$enterprisemanager\"} ) by(instance, backupserver) )", + "interval": "", + "legendFormat": "{{ backupserver }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Job Reries percent (All type)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:341", + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:342", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 55 + }, + "id": 135, + "panels": [], + "repeat": "backupserver", + "title": "backup Jobs for [$backupserver]", + "type": "row" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Warning" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Working" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 56 + }, + "id": 136, + "options": { + "displayLabels": [ + "value", + "name" + ], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 1) or vector(0)", + "instant": false, + "interval": "", + "legendFormat": "Success", + "refId": "A" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 2) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Warning", + "refId": "B" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 3) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Failed", + "refId": "C" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 4) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Idle", + "refId": "D" + }, + { + "exemplar": true, + "expr": "count(veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 5) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Working", + "refId": "E" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Job State", + "type": "piechart" + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Success", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 2, + "text": "Warning", + "to": "", + "type": 1, + "value": "2" + }, + { + "from": "", + "id": 3, + "text": "Failed", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 4, + "text": "Idle", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 5, + "text": "!! Undefined !!", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 6, + "text": "Working", + "to": "", + "type": 1, + "value": "5" + } + ] + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(106, 107, 105)", + "value": null + }, + { + "color": "green", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + }, + { + "color": "red", + "value": 3 + }, + { + "color": "rgb(164, 168, 168)", + "value": 4 + } + ] + } + }, + { + "id": "custom.width", + "value": 86 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Duration" + }, + "properties": [ + { + "id": "custom.width", + "value": 99 + }, + { + "id": "unit", + "value": "s" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "type" + }, + "properties": [ + { + "id": "custom.width", + "value": 146 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "startdate" + }, + "properties": [ + { + "id": "custom.width", + "value": 193 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "backupserver" + }, + "properties": [ + { + "id": "custom.width", + "value": 197 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobname" + }, + "properties": [ + { + "id": "custom.width", + "value": 256 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobretries" + }, + "properties": [ + { + "id": "custom.width", + "value": 75 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "name" + }, + "properties": [ + { + "id": "custom.width", + "value": 369 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Retries" + }, + "properties": [ + { + "id": "custom.width", + "value": 54 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobtype" + }, + "properties": [ + { + "id": "custom.width", + "value": 86 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 16, + "x": 8, + "y": 56 + }, + "id": 155, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Status" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_jobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "veeam_em_jobs_sessions_duration{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "veeam_em_jobs_sessions_retries{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + } + ], + "title": "Backup Job Status", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "jobname", + "name", + "Value #A", + "Value #B", + "Value #C", + "jobtype" + ] + } + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 1, + "Value #B": 3, + "Value #C": 4, + "jobname": 0, + "name": 2 + }, + "renameByName": { + "Value #A": "Status", + "Value #B": "Duration", + "Value #C": "Retries" + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Warning" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Success" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Idle" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Working" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 65 + }, + "id": 158, + "options": { + "displayLabels": [ + "value", + "name" + ], + "legend": { + "displayMode": "table", + "placement": "right", + "values": [ + "value" + ] + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "text": {} + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 1) or vector(0)", + "instant": false, + "interval": "", + "legendFormat": "Success", + "refId": "A" + }, + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 2) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Warning", + "refId": "B" + }, + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 3) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Failed", + "refId": "C" + }, + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 4) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Idle", + "refId": "D" + }, + { + "exemplar": true, + "expr": "count(veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"} == 5) or vector(0)", + "hide": false, + "interval": "", + "legendFormat": "Working", + "refId": "E" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "VM Jobs State", + "type": "piechart" + }, + { + "datasource": null, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Success", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 2, + "text": "Warning", + "to": "", + "type": 1, + "value": "2" + }, + { + "from": "", + "id": 3, + "text": "Failed", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 4, + "text": "Idle", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 5, + "text": "!! Undefined !!", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 6, + "text": "Working", + "to": "", + "type": 1, + "value": "5" + } + ] + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(106, 107, 105)", + "value": null + }, + { + "color": "green", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + }, + { + "color": "red", + "value": 3 + }, + { + "color": "rgb(164, 168, 168)", + "value": 4 + } + ] + } + }, + { + "id": "custom.width", + "value": 86 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Duration" + }, + "properties": [ + { + "id": "custom.width", + "value": 99 + }, + { + "id": "unit", + "value": "s" + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "jobname" + }, + "properties": [ + { + "id": "custom.width", + "value": 240 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Retries" + }, + "properties": [ + { + "id": "custom.width", + "value": 65 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vmname" + }, + "properties": [ + { + "id": "custom.width", + "value": 131 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "VM Size" + }, + "properties": [ + { + "id": "unit", + "value": "decbytes" + }, + { + "id": "custom.width", + "value": 91 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "taskname" + }, + "properties": [ + { + "id": "custom.width", + "value": 271 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 16, + "x": 8, + "y": 65 + }, + "id": 159, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Status" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_state{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + }, + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_duration{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "B" + }, + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_retries{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "C" + }, + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_total_bytes{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "hide": false, + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "D" + } + ], + "title": "Backup VMJob Status", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "jobname", + "taskname", + "vmname", + "Value #A", + "Value #B", + "Value #D" + ] + } + } + }, + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 3, + "Value #B": 4, + "Value #C": 5, + "jobname": 1, + "taskname": 2, + "vmname": 0 + }, + "renameByName": { + "Value #A": "Status", + "Value #B": "Duration", + "Value #C": "Retries", + "Value #D": "VM Size" + } + } + } + ], + "type": "table" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Undefined", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Failed", + "to": "", + "type": 1, + "value": "3" + }, + { + "from": "", + "id": 3, + "text": "Pending", + "to": "", + "type": 1, + "value": "4" + }, + { + "from": "", + "id": 4, + "text": "Working", + "to": "", + "type": 1, + "value": "5" + }, + { + "from": "", + "id": 5, + "text": "Warning", + "to": "", + "type": 1, + "value": "2" + } + ] + }, + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "rgb(131, 135, 135)", + "value": 1 + }, + { + "color": "#EAB839", + "value": 2 + }, + { + "color": "red", + "value": 3 + }, + { + "color": "dark-purple", + "value": 4 + } + ] + } + }, + { + "id": "custom.width", + "value": 108 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "vmname" + }, + "properties": [ + { + "id": "custom.width", + "value": 108 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "message" + }, + "properties": [ + { + "id": "custom.width", + "value": 507 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "taskname" + }, + "properties": [ + { + "id": "custom.width", + "value": 283 + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 74 + }, + "id": 170, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Status" + } + ] + }, + "pluginVersion": "7.5.16", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_vmjobs_sessions_error{instance=~\"$enterprisemanager\", backupserver=~\"$backupserver\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "VM Backup Errors", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "backupserver", + "jobname", + "message", + "taskname", + "vmname", + "Value" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": { + "Value #A": 4, + "backupserver": 0, + "jobname": 1, + "message": 5, + "taskname": 3, + "vmname": 2 + }, + "renameByName": { + "Value": "Status", + "Value #A": "Status" + } + } + } + ], + "type": "table" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 325 + }, + "id": 38, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": null, + "filterable": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Status" + }, + "properties": [ + { + "id": "mappings", + "value": [ + { + "from": "", + "id": 1, + "text": "Unknown", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "Online", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 3, + "text": "Offline", + "to": "", + "type": 1, + "value": "2" + } + ] + }, + { + "id": "custom.displayMode", + "value": "color-background" + }, + { + "id": "thresholds", + "value": { + "mode": "absolute", + "steps": [ + { + "color": "rgb(142, 143, 142)", + "value": null + }, + { + "color": "green", + "value": 1 + }, + { + "color": "red", + "value": 2 + } + ] + } + }, + { + "id": "custom.width", + "value": 103 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Agent OS Version" + }, + "properties": [ + { + "id": "custom.width", + "value": 310 + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "Agent Version" + }, + "properties": [ + { + "id": "custom.width", + "value": 126 + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 24 + }, + "id": 40, + "options": { + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Status" + } + ] + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_agents_status{instance=~\"$enterprisemanager\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Backup Agent Status", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "name", + "osversion", + "version", + "Value", + "backupserver" + ] + } + } + }, + { + "id": "organize", + "options": { + "excludeByName": {}, + "indexByName": {}, + "renameByName": { + "Value": "Status", + "name": "Agent Host", + "osversion": "Agent OS Version", + "version": "Agent Version" + } + } + } + ], + "type": "table" + } + ], + "repeat": null, + "title": "Veeam Agent Overview", + "type": "row" + }, + { + "collapsed": true, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 326 + }, + "id": 16, + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 0, + "y": 149 + }, + "id": 164, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^description$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_backup_servers_config{instance=~\"$enterprisemanager\", name=~\"$backupserver\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Description", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 7, + "y": 149 + }, + "id": 166, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^version$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_backup_servers_config{instance=~\"$enterprisemanager\", name=~\"$backupserver\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Description", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "blue", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 7, + "x": 14, + "y": 149 + }, + "id": 165, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "/^port$/", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.5", + "targets": [ + { + "exemplar": true, + "expr": "veeam_em_backup_servers_config{instance=~\"$enterprisemanager\", name=~\"$backupserver\"}", + "format": "table", + "instant": true, + "interval": "", + "legendFormat": "", + "refId": "A" + } + ], + "title": "Port", + "type": "stat" + }, + { + "aliasColors": { + "idle": "#0A50A1" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "unit": "percentunit" + }, + "overrides": [] + }, + "fill": 10, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 152 + }, + "hiddenSeries": false, + "hideTimeOverride": false, + "id": 25, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 0, + "links": [], + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "backupserver", + "scopedVars": { + "backupserver": { + "selected": true, + "text": "", + "value": "" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "application": { + "filter": "" + }, + "exemplar": true, + "expr": "1 - (sum by (host) (rate(windows_cpu_time_total{host=\"$backupserver\", mode=\"idle\"}[5m])) / count by (host) (windows_cpu_core_frequency_mhz{host=\"$backupserver\"}) )", + "format": "time_series", + "functions": [], + "group": { + "filter": "" + }, + "hide": false, + "host": { + "filter": "" + }, + "interval": "", + "intervalFactor": 1, + "item": { + "filter": "" + }, + "legendFormat": "", + "metric": "mysql_global_status_questions", + "mode": 0, + "options": { + "showDisabledItems": false + }, + "refId": "A", + "step": 20 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Usage / $backupserver", + "tooltip": { + "shared": false, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:104", + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:105", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 168 + }, + "hiddenSeries": false, + "hideTimeOverride": false, + "id": 23, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "backupserver", + "scopedVars": { + "backupserver": { + "selected": true, + "text": "", + "value": "" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "application": { + "filter": "" + }, + "exemplar": true, + "expr": "windows_os_virtual_memory_bytes{host=~\"$backupserver\"}", + "format": "time_series", + "functions": [], + "group": { + "filter": "" + }, + "hide": false, + "host": { + "filter": "" + }, + "interval": "", + "intervalFactor": 1, + "item": { + "filter": "" + }, + "legendFormat": "Virtual memory {{ host }}", + "metric": "mysql_global_status_questions", + "mode": 0, + "options": { + "showDisabledItems": false + }, + "refId": "A", + "step": 5 + }, + { + "application": { + "filter": "" + }, + "exemplar": true, + "expr": "windows_cs_physical_memory_bytes{host=~\"$backupserver\"}", + "format": "time_series", + "functions": [], + "group": { + "filter": "" + }, + "hide": false, + "host": { + "filter": "" + }, + "interval": "", + "intervalFactor": 1, + "item": { + "filter": "" + }, + "legendFormat": "Physical memory {{ host }}", + "metric": "mysql_global_status_questions", + "mode": 0, + "options": { + "showDisabledItems": false + }, + "refId": "B", + "step": 5 + }, + { + "application": { + "filter": "" + }, + "exemplar": true, + "expr": "windows_os_physical_memory_free_bytes{host=~\"$backupserver\"}", + "format": "time_series", + "functions": [], + "group": { + "filter": "" + }, + "hide": false, + "host": { + "filter": "" + }, + "interval": "", + "intervalFactor": 1, + "item": { + "filter": "" + }, + "legendFormat": "Free physical memory {{ host }}", + "metric": "mysql_global_status_questions", + "mode": 0, + "options": { + "showDisabledItems": false + }, + "refId": "C", + "step": 5 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Memory / $backupserver", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:271", + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:272", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "from": "", + "id": 0, + "text": "Up", + "to": "", + "type": 1, + "value": "1" + }, + { + "from": "", + "id": 1, + "text": "Down", + "to": "", + "type": 1, + "value": "0" + }, + { + "from": "", + "id": 2, + "text": "No Data", + "to": "", + "type": 1, + "value": "null" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "semi-dark-red", + "value": null + }, + { + "color": "semi-dark-green", + "value": 0 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 24, + "x": 0, + "y": 184 + }, + "id": 21, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "text": {}, + "textMode": "auto" + }, + "pluginVersion": "7.5.5", + "repeat": "backupserver", + "scopedVars": { + "backupserver": { + "selected": true, + "text": "", + "value": "" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "up{host=~\"$backupserver\"}", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{ host }}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Satus $backupserver", + "type": "stat" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 192 + }, + "id": 27, + "options": { + "displayMode": "lcd", + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "last" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "text": {} + }, + "pluginVersion": "7.5.5", + "repeat": "backupserver", + "scopedVars": { + "backupserver": { + "selected": true, + "text": "", + "value": "" + } + }, + "targets": [ + { + "exemplar": true, + "expr": "100 - (windows_logical_disk_free_bytes{host=~\"$backupserver\"} / windows_logical_disk_size_bytes{host=~\"$backupserver\"})*100", + "interval": "", + "legendFormat": "{{ volume }}", + "refId": "A" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Disks $backupserver", + "type": "bargauge" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 206 + }, + "hiddenSeries": false, + "hideTimeOverride": false, + "id": 29, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.5", + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "backupserver", + "scopedVars": { + "backupserver": { + "selected": true, + "text": "", + "value": "" + } + }, + "seriesOverrides": [ + { + "$$hashKey": "object:305", + "alias": "A", + "transform": "negative-Y" + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "application": { + "filter": "" + }, + "exemplar": true, + "expr": "rate(windows_net_bytes_sent_total{host=~\"$backupserver\"}[5m]) >0", + "format": "time_series", + "functions": [], + "group": { + "filter": "" + }, + "hide": false, + "host": { + "filter": "" + }, + "interval": "", + "intervalFactor": 1, + "item": { + "filter": "" + }, + "legendFormat": "Sent {{nic}} {{ host }}", + "metric": "mysql_global_status_questions", + "mode": 0, + "options": { + "showDisabledItems": false + }, + "refId": "B", + "step": 10 + }, + { + "application": { + "filter": "" + }, + "exemplar": true, + "expr": "rate(windows_net_bytes_received_total{host=~\"$backupserver\"}[5m]) <0", + "format": "time_series", + "functions": [], + "group": { + "filter": "" + }, + "hide": false, + "host": { + "filter": "" + }, + "interval": "", + "intervalFactor": 1, + "item": { + "filter": "" + }, + "legendFormat": "Received {{nic}} {{ host }}", + "metric": "mysql_global_status_questions", + "mode": 0, + "options": { + "showDisabledItems": false + }, + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Network $backupserver", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:439", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:440", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "repeat": null, + "title": "Backup Server Performance [ $backupserver ( $backupserver_version )] (node_exporter)", + "type": "row" + } + ], + "refresh": "5m", + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(veeam_em_up, instance)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Enterprise Manager", + "multi": true, + "name": "enterprisemanager", + "options": [], + "query": { + "query": "label_values(veeam_em_up, instance)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(veeam_em_backup_servers_config, name)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "backupserver", + "options": [], + "query": { + "query": "label_values(veeam_em_backup_servers_config, name)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(veeam_em_backup_servers_config{name=~\"$backupserver\"},version)", + "description": null, + "error": null, + "hide": 2, + "includeAll": false, + "label": "BackupServer Version", + "multi": false, + "name": "backupserver_version", + "options": [], + "query": { + "query": "label_values(veeam_em_backup_servers_config{name=~\"$backupserver\"},version)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "Warning", + "value": "2" + }, + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "VM BackupJob Status", + "multi": false, + "name": "vm_status", + "options": [ + { + "selected": false, + "text": "Undefined", + "value": "0" + }, + { + "selected": false, + "text": "Success", + "value": "1" + }, + { + "selected": true, + "text": "Warning", + "value": "2" + }, + { + "selected": false, + "text": "Failed", + "value": "3" + }, + { + "selected": false, + "text": "Pending -Idle", + "value": "4" + }, + { + "selected": false, + "text": "Working-In Progress", + "value": "5" + } + ], + "query": "Undefined : 0, Success : 1, Warning : 2, Failed : 3, Pending -Idle : 4 , Working-In Progress : 5", + "queryValue": "", + "skipUrlSync": false, + "type": "custom" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Veeam Enterprise Manager", + "uid": "bpHLHK6Mk", + "version": 28 +} \ No newline at end of file diff --git a/contribs/prometheus/hp3par_node_example.yml b/contribs/prometheus/hp3par_node_example.yml new file mode 100644 index 0000000..f0fdc79 --- /dev/null +++ b/contribs/prometheus/hp3par_node_example.yml @@ -0,0 +1,8 @@ +- targets: [ "hp3par_node_1" ] + labels: + __tmp_source_host: "hp3par_exporter_host.domain.name:9321" + # if you have activated password encrypted passphrass + __auth_key: __shared__auth_passphrase__ + host: " hp3par_node_1_fullqualified.domain.name" + #custom labels… + environment: "DEV" diff --git a/contribs/prometheus/prometheus_jobs.yml b/contribs/prometheus/prometheus_jobs.yml new file mode 100644 index 0000000..ee9ca98 --- /dev/null +++ b/contribs/prometheus/prometheus_jobs.yml @@ -0,0 +1,16 @@ +#--------- Start prometheus hp3par exporter ---------# + - job_name: "hp3par" + metrics_path: /metrics + file_sd_configs: + - files: [ "/etc/prometheus/hp3par_nodes/*.yml" ] + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + # if you use a shared passphrase between prometheus and exporter + - source_labels: [__auth_key] + target_label: __param_auth_key + # use the value set in __tmp_source_host as exporter host name + - source_labels: [__tmp_source_host] + target_label: __address__ + +#--------- End prometheus hp3par exporter ---------# diff --git a/contribs/systemd/system/hp3par_exporter.service b/contribs/systemd/system/hp3par_exporter.service new file mode 100644 index 0000000..1120422 --- /dev/null +++ b/contribs/systemd/system/hp3par_exporter.service @@ -0,0 +1,23 @@ +[Unit] +Description=hp3par_exporter (httpapi_exporter) for prometheus +Wants=network-online.target +After=network-online.target +StartLimitBurst=4 +StartLimitIntervalSec=30 + +[Service] +User=node_exporter +Group=node_exporter +WorkingDirectory=/etc/httpapi_exporter/hp3par/ +Restart=always +RestartSec=2 +Type=simple + +ExecStart=/opt/httpapi_exporter/hp3par_exporter \ + --config.file=/etc/httpapi_exporter/hp3par/config.yml \ + --log.level=warn \ + --web.listen-address=dal-v-survdadc.dassault-avion.val:9321 +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target diff --git a/contribs/systemd/system/veeam_exporter.service b/contribs/systemd/system/veeam_exporter.service new file mode 100644 index 0000000..d1d1efd --- /dev/null +++ b/contribs/systemd/system/veeam_exporter.service @@ -0,0 +1,23 @@ +[Unit] +Description= veeam_exporter (httpapi_exporter) for prometheus +Wants=network-online.target +After=network-online.target +StartLimitBurst=4 +StartLimitIntervalSec=30 + +[Service] +User=node_exporter +Group=node_exporter +WorkingDirectory=/etc/httpapi_exporter/veeam/ +Restart=always +RestartSec=2 +Type=simple + +ExecStart=/opt/httpapi_exporter/veeam_exporter \ + --config.file=/etc/httpapi_exporter/veeam/config.yml \ + --log.level=warn \ + --web.listen-address=dal-v-survdadc.dassault-avion.val:9247 +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target diff --git a/debug_action.go b/debug_action.go new file mode 100644 index 0000000..72265e0 --- /dev/null +++ b/debug_action.go @@ -0,0 +1,182 @@ +package main + +import ( + //"bytes" + "fmt" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// debug action +// *************************************************************************************** +// *************************************************************************************** + +// **************************** + +type DebugActionConfig struct { + MsgVal string `yaml:"msg"` + + msg *Field + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for DebugActionConfig. +func (dc *DebugActionConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain DebugActionConfig + var err error + if err := unmarshal((*plain)(dc)); err != nil { + return err + } + // Check required fields + dc.msg, err = NewField(dc.MsgVal, nil) + if err != nil { + return fmt.Errorf("invalid template for debug message %q: %s", dc.MsgVal, err) + } + + return checkOverflow(dc.XXX, "debug action") +} + +// **************************** +type DebugAction struct { + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + + Debug *DebugActionConfig `yaml:"debug"` +} + +func (a *DebugAction) Type() int { + return debug_action +} + +func (a *DebugAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +func (a *DebugAction) GetNameField() *Field { + return a.Name +} +func (a *DebugAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *DebugAction) GetWidh() []any { + return a.With +} +func (a *DebugAction) SetWidth(with []any) { + a.With = with +} + +func (a *DebugAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *DebugAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *DebugAction) GetLoopVar() string { + return a.LoopVar +} +func (a *DebugAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *DebugAction) GetVars() map[string]any { + return a.Vars +} +func (a *DebugAction) SetVars(vars map[string]any) { + a.Vars = vars +} + +func (a *DebugAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *DebugAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +// func (a *DebugAction) GetBaseAction() *BaseAction { +// return nil +// } + +func (a *DebugAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *DebugAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// only for MetricsAction +func (a *DebugAction) GetMetrics() []*GetMetricsRes { + return nil +} + +// only for MetricAction +func (a *DebugAction) GetMetric() *MetricConfig { + return nil +} +func (a *DebugAction) SetMetricFamily(*MetricFamily) { +} + +// only for PlayAction +func (a *DebugAction) SetPlayAction(scripts map[string]*YAMLScript) error { + return nil +} + +// specific behavior for the DebugAction +func (a *DebugAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: DebugAction] Name: %s", Name(a.Name, symtab, logger))) + + str, err := a.Debug.msg.GetValueString(symtab, nil, false) + if err != nil { + str = a.Debug.MsgVal + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for debug message '%s': %v", str, err)) + } + + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf(" message: %s", str)) + + return nil +} + +func (a *DebugAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + if a.Debug.msg != nil { + if err := a.Debug.msg.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + return nil +} + +// *************************************************************************************** diff --git a/doc/session.drawio b/doc/session.drawio new file mode 100644 index 0000000..e3fe808 --- /dev/null +++ b/doc/session.drawio @@ -0,0 +1,269 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/session.png b/doc/session.png new file mode 100644 index 0000000..3b5343c Binary files /dev/null and b/doc/session.png differ diff --git a/encrypt/encrypt.go b/encrypt/encrypt.go new file mode 100644 index 0000000..ffcb375 --- /dev/null +++ b/encrypt/encrypt.go @@ -0,0 +1,79 @@ +package encrypt + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "encoding/hex" + "fmt" +) + +type AESCipher struct { + GCM cipher.AEAD + nonceSize int +} + +// Initilze AES/GCM for both encrypting and decrypting. +func NewAESCipher(key_str string) (*AESCipher, error) { + + block, err := aes.NewCipher([]byte(key_str)) + if err != nil { + return nil, fmt.Errorf("error reading key: %s", err.Error()) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("error initializing AEAD: %s", err.Error()) + } + + res := AESCipher{ + GCM: gcm, + nonceSize: gcm.NonceSize(), + } + return &res, nil +} + +func randBytes(length int) []byte { + b := make([]byte, length) + rand.Read(b) + return b +} + +func (ci *AESCipher) Encrypt(plaintext []byte, base64_encoded bool) (cipherstring string) { + nonce := randBytes(ci.nonceSize) + ciphertext := ci.GCM.Seal(nonce, nonce, plaintext, nil) + + if base64_encoded { + cipherstring = base64.StdEncoding.EncodeToString(ciphertext) + } else { + cipherstring = hex.EncodeToString(ciphertext) + } + return cipherstring +} + +func (ci *AESCipher) Decrypt(cipherstring string, base64_encoded bool) (plainstring string, err error) { + var ciphertext, plaintext []byte + + if base64_encoded { + ciphertext, err = base64.StdEncoding.DecodeString(cipherstring) + } else { + ciphertext, err = hex.DecodeString(cipherstring) + } + if err != nil { + return "", err + } + + if len(ciphertext) < ci.nonceSize { + return "", fmt.Errorf("ciphertext too short") + } + nonce := ciphertext[0:ci.nonceSize] + msg := ciphertext[ci.nonceSize:] + plaintext, err = ci.GCM.Open(nil, nonce, msg, nil) + if err != nil { + return "", err + } + plainstring = string(plaintext) + + return plainstring, nil +} diff --git a/exporter.go b/exporter.go new file mode 100644 index 0000000..8815baf --- /dev/null +++ b/exporter.go @@ -0,0 +1,198 @@ +package main + +import ( + "context" + "fmt" + "sync" + + "google.golang.org/protobuf/proto" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" +) + +// Exporter is a prometheus.Gatherer that gathers SQL metrics from targets and merges them with the default registry. +type Exporter interface { + prometheus.Gatherer + + // WithContext returns a (single use) copy of the Exporter, which will use the provided context for Gather() calls. + WithContext(context.Context, Target) Exporter + // Config returns the Exporter's underlying Config object. + Config() *Config + Targets() []Target + Logger() log.Logger + FindTarget(string) (Target, error) + GetFirstTarget() (Target, error) +} + +type exporter struct { + config *Config + targets []Target + + cur_target Target + ctx context.Context + logger log.Logger +} + +// NewExporter returns a new Exporter with the provided config. +func NewExporter(configFile string, logger log.Logger, collectorName string) (Exporter, error) { + c, err := Load(configFile, logger, collectorName) + if err != nil { + return nil, err + } + + var targets []Target + var logContext []interface{} + if len(c.Targets) > 1 { + targets = make([]Target, 0, len(c.Targets)*3) + } + for _, t := range c.Targets { + if len(t.TargetsFiles) > 0 { + continue + } + target, err := NewTarget(logContext, t, t.Collectors(), nil, c.Globals, c.HttpAPIConfig, logger) + if err != nil { + return nil, err + } + if len(c.Targets) > 1 { + targets = append(targets, target) + } else { + targets = []Target{target} + } + } + + return &exporter{ + config: c, + targets: targets, + ctx: context.Background(), + logger: logger, + }, nil +} + +func (e *exporter) WithContext(ctx context.Context, t Target) Exporter { + return &exporter{ + config: e.config, + targets: e.targets, + cur_target: t, + ctx: ctx, + logger: e.logger, + } +} + +// Gather implements prometheus.Gatherer. +func (e *exporter) Gather() ([]*dto.MetricFamily, error) { + var ( + metricChan = make(chan Metric, capMetricChan) + errs prometheus.MultiError + ) + + var wg sync.WaitGroup + // wg.Add(len(e.targets)) + // for _, t := range e.targets { + // go func(target Target) { + // defer wg.Done() + // target.Collect(e.ctx, metricChan) + // }(t) + // } + // add only cur target + wg.Add(1) + go func(target Target) { + defer wg.Done() + target.Collect(e.ctx, metricChan) + }(e.cur_target) + + // Wait for all collectors to complete, then close the channel. + go func() { + wg.Wait() + close(metricChan) + }() + + // Drain metricChan in case of premature return. + defer func() { + for range metricChan { + } + }() + + level.Debug(e.logger).Log("msg", fmt.Sprintf("exporter.Gather(): **** Target launch is OVER :'%s' ****", e.cur_target.Name())) + // Gather. + dtoMetricFamilies := make(map[string]*dto.MetricFamily, 10) + // level.Debug(e.logger).Log("msg", "exporter.Gather(): just before for chan") + for metric := range metricChan { + // level.Debug(e.logger).Log("msg", "exporter.Gather(): in for chan") + dtoMetric := &dto.Metric{} + if err := metric.Write(dtoMetric); err != nil { + errs = append(errs, err) + continue + } + metricDesc := metric.Desc() + dtoMetricFamily, ok := dtoMetricFamilies[metricDesc.Name()] + if !ok { + dtoMetricFamily = &dto.MetricFamily{} + dtoMetricFamily.Name = proto.String(metricDesc.Name()) + dtoMetricFamily.Help = proto.String(metricDesc.Help()) + switch { + case dtoMetric.Gauge != nil: + dtoMetricFamily.Type = dto.MetricType_GAUGE.Enum() + case dtoMetric.Counter != nil: + dtoMetricFamily.Type = dto.MetricType_COUNTER.Enum() + default: + errs = append(errs, fmt.Errorf("don't know how to handle metric %v", dtoMetric)) + continue + } + dtoMetricFamilies[metricDesc.Name()] = dtoMetricFamily + } + dtoMetricFamily.Metric = append(dtoMetricFamily.Metric, dtoMetric) + } + level.Debug(e.logger).Log("msg", "exporter.Gather(): **** Target channel analysis is OVER ****") + + // No need to sort metric families, prometheus.Gatherers will do that for us when merging. + result := make([]*dto.MetricFamily, 0, len(dtoMetricFamilies)) + for _, mf := range dtoMetricFamilies { + result = append(result, mf) + } + return result, errs +} + +// Config implements Exporter. +func (e *exporter) Config() *Config { + return e.config +} + +// Targets implements Exporter. +func (e *exporter) Targets() []Target { + return e.targets +} + +// Logger implements Exporter. +func (e *exporter) Logger() log.Logger { + return e.logger +} + +// FindTarget implements Exporter. +func (e *exporter) FindTarget(tname string) (Target, error) { + var t_found Target + found := false + for _, t := range e.targets { + if tname == t.Name() { + t_found = t + found = true + } + } + if !found { + return t_found, fmt.Errorf("target '%s' not found", tname) + } + return t_found, nil +} + +// GetFirstTarget implements Exporter. +func (e *exporter) GetFirstTarget() (Target, error) { + var t_found Target + if len(e.targets) == 0 { + return t_found, fmt.Errorf("no target found") + } else { + t_found = e.targets[0] + } + return t_found, nil +} diff --git a/field.go b/field.go new file mode 100644 index 0000000..49e5769 --- /dev/null +++ b/field.go @@ -0,0 +1,340 @@ +package main + +import ( + "encoding/json" + "fmt" + "html" + + // "regexp" + + "strconv" + ttemplate "text/template" + + "strings" +) + +// var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) + +type exporterTemplate ttemplate.Template + +func (tmpl *exporterTemplate) MarshalText() (text []byte, err error) { + + return []byte(tmpl.Tree.Root.String()), nil +} + +type Field struct { + raw string + tmpl *exporterTemplate +} + +// create a new key or value Field that can be a GO template +func NewField(name string, customTemplate *exporterTemplate) (*Field, error) { + var tmpl *ttemplate.Template + var err error + + if strings.Contains(name, "{") { + if customTemplate != nil { + ptr := (*ttemplate.Template)(customTemplate) + tmpl, err = ptr.Clone() + if err != nil { + return nil, fmt.Errorf("field template clone for %s is invalid: %s", name, err) + } + } else { + // tmpl = template.New("field").Funcs(sprig.FuncMap()) + tmpl = ttemplate.New("field").Funcs(mymap()) + } + tmpl, err = tmpl.Parse(name) + if err != nil { + return nil, fmt.Errorf("field template %s is invalid: %s", name, err) + } + } + return &Field{ + raw: name, + tmpl: (*exporterTemplate)(tmpl), + }, nil +} + +// obtain float64 from a var of any type +func RawGetValueFloat(curval any) float64 { + var f_value float64 + var err error + if curval == nil { + return 0.0 + } + + // it is a raw value not a template look in "item" + switch curval := curval.(type) { + case int64: + f_value = float64(curval) + case float64: + f_value = curval + case string: + if f_value, err = strconv.ParseFloat(strings.Trim(curval, "\r\n "), 64); err != nil { + f_value = 0 + } + default: + f_value = 0 + // value := row[v].(float64) + } + return f_value +} + +// obtain string from a var of any type +func RawGetValueString(curval any) string { + res := "" + if curval == nil { + return res + } + + switch curval := curval.(type) { + case string: + res = curval + // case map[any]any: + // bytes_res, err := json.MarshalIndent(curval, "", "") + // if err != nil { + // fmt.Printf("error: %s\n", string(err.Error())) + // } + // res = string(bytes_res) + default: + res = fmt.Sprintf("%v", curval) + } + return strings.Trim(res, "\r\n ") +} + +// obtain a final string value from Field +// use template if one is defined using item to symbols table +// else +// check if value must be sustituted using provided sub map +// if check_item set to true, check if the resulting value exists in item symbols table +// else return raw value (simple string) +func (f *Field) GetValueString( + item map[string]interface{}, + sub map[string]string, + check_item bool) (string, error) { + if f == nil { + return "", nil + } + + if f.tmpl != nil { + tmp_res := new(strings.Builder) + err := ((*ttemplate.Template)(f.tmpl)).Execute(tmp_res, &item) + if err != nil { + return "", err + } + // obtain final string from builder + tmp := tmp_res.String() + // remove before and after blank chars + tmp = strings.Trim(tmp, "\r\n ") + // unescape string + return html.UnescapeString(tmp), nil + } else { + val := f.raw + // check if there is a transformation value in sub[stitution] map + if sub != nil { + if _, ok := sub[val]; ok { + val = sub[val] + } + } + if check_item { + if curval, ok := item[val]; ok { + return RawGetValueString(curval), nil + } + } + return RawGetValueString(val), nil + } +} + +// obtain a final float64 value from Field +// use template if one is defined using item to symbols table +// else if the resulting value exists in item symbols table return it +// else return raw value (simple float64 constant) +func (f *Field) GetValueFloat( + item map[string]interface{}) (float64, error) { + var str_value any + + if f == nil { + return 0, nil + } + + if f.tmpl != nil { + tmp_res := new(strings.Builder) + // tmpl, err := template.New("field").Funcs(sprig.FuncMap()).Parse("{{ mulf .result.totalMiB 1024 1024 }}") + // if err == nil { + // tmpl.Execute(tmp_res, &item) + // if err != nil { + // return 0, err + // } + // str_value = tmp_res.String() + // str_value = html.UnescapeString(str_value.(string)) + // fmt.Printf("tmp: %s\n", str_value) + // var data any + // err = json.Unmarshal([]byte(str_value.(string)), &data) + // if err == nil { + // res, err := json.MarshalIndent(data, "", " ") + // if err == nil { + // fmt.Printf("res: %s\n", string(res)) + // } + // } + // } + // tmp_res = new(strings.Builder) + err := ((*ttemplate.Template)(f.tmpl)).Execute(tmp_res, &item) + if err != nil { + return 0, err + } + str_value = html.UnescapeString(tmp_res.String()) + } else { + val := f.raw + // check if value exists in symbol table + if curval, ok := item[val]; ok { + str_value = curval + } else { + str_value = val + } + } + return RawGetValueFloat(str_value), nil +} + +func (f *Field) GetValueObject( + // item map[string]interface{}) ([]any, error) { + item any) (any, error) { + res_slice := make([]any, 0) + + if f == nil { + return res_slice, nil + } + + if f.tmpl != nil { + tmp_res := new(strings.Builder) + err := ((*ttemplate.Template)(f.tmpl)).Execute(tmp_res, &item) + if err != nil { + return res_slice, err + } + var data any + json_obj := tmp_res.String() + if json_obj != "" { + // json_obj = strings.ReplaceAll(json_obj, """, "\"") + json_obj = html.UnescapeString(json_obj) + // json_obj = strings.TrimSuffix(json_obj, "\n") + // fmt.Println(json_obj) + err = json.Unmarshal([]byte(json_obj), &data) + if err != nil { + // if _, ok := err.(*json.UnmarshalTypeError); ok { + + // } + return res_slice, err + } + } + return data, nil + } else { + datas := &ResultElement{ + raw: item, + } + if data, found := datas.GetSlice(f.raw); found { + return data, nil + } else { + return nil, nil + } + } +} + +func (f *Field) String() string { + if f == nil { + return "" + } + + if f.tmpl != nil { + // f.tmpl. + return f.tmpl.Tree.Root.String() + } else { + return f.raw + } +} + +func (f *Field) MarshalText() (text []byte, err error) { + + return []byte(f.String()), nil +} + +func (f *Field) AddDefaultTemplate(customTemplate *exporterTemplate) error { + if f.tmpl != nil && customTemplate != nil { + if tmpl, err := AddDefaultTemplate(f.tmpl, customTemplate); err != nil { + f.tmpl = tmpl + } else { + return err + } + } + return nil +} + +func AddDefaultTemplate(dest_tmpl *exporterTemplate, customTemplate *exporterTemplate) (*exporterTemplate, error) { + if dest_tmpl != nil && customTemplate != nil { + cc_tmpl, err := ((*ttemplate.Template)(customTemplate)).Clone() + if err != nil { + return nil, fmt.Errorf("field template clone for %s is invalid: %s", ((*ttemplate.Template)(customTemplate)).Name(), err) + } + for _, tmpl := range cc_tmpl.Templates() { + name := tmpl.Name() + if name == "default" { + continue + } + + _, err = ((*ttemplate.Template)(dest_tmpl)).AddParseTree(tmpl.Name(), tmpl.Tree) + if err != nil { + return nil, fmt.Errorf("field template %s is invalid: %s", tmpl.Name(), err) + } + } + } + return dest_tmpl, nil +} + +type ResultElement struct { + raw any +} + +func (r *ResultElement) GetSlice(field string) ([]any, bool) { + var myslice []any + var found bool + if r_type, ok := r.raw.(map[string]any); ok { + + if myvar, ok := r_type[field]; ok { + switch curval := myvar.(type) { + case []any: + myslice = curval + found = true + case map[string]any: + n_slice := make([]any, 1) + n_slice[0] = myvar + myslice = n_slice + found = true + + } + // myvart := reflect.ValueOf(myvar).Kind() + // if myvart == reflect.Slice { + // myslice = myvar.([]interface{}) + // // for i, myvar := range myslice { + // // myvart := reflect.ValueOf(myvar).Kind() + // // if myvart == reflect.Map { + // // mymap := make(map[string]interface{}) + // // for k, v := range myvar.(map[string]interface{}) { + // // fmt.Printf("k: %s, v %+v\n", k, v) + // // } + // // } + // // } + // } else if myvart == reflect.Map { + // n_slice := make([]interface{}, 1) + // n_slice[0] = myvar + // myslice = n_slice + // } + } + } + return myslice, found +} + +// func (r *ResultElement) GetMap(field string) map[string]interface{} { +// var mymap map[string]interface{} +// myvart := reflect.ValueOf(r.raw).Kind() +// if myvart != reflect.Map { +// mymap = nil +// } +// return mymap +// } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..22a9774 --- /dev/null +++ b/go.mod @@ -0,0 +1,42 @@ +module github.com/peekjef72/httpapi_exporter + +go 1.18 + +require ( + github.com/go-kit/log v0.2.1 + github.com/mitchellh/copystructure v1.2.0 + github.com/prometheus/client_golang v1.16.0 + github.com/prometheus/client_model v0.4.0 + github.com/prometheus/common v0.44.0 + google.golang.org/protobuf v1.31.0 +) + +require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.15 + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/shopspring/decimal v1.3.1 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/net v0.15.0 // indirect +) + +require ( + github.com/Masterminds/sprig/v3 v3.2.3 + github.com/alecthomas/kingpin/v2 v2.3.2 + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-resty/resty/v2 v2.8.0 + github.com/golang/protobuf v1.5.3 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 + golang.org/x/sys v0.12.0 // indirect + gopkg.in/yaml.v3 v3.0.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..cb8ef90 --- /dev/null +++ b/go.sum @@ -0,0 +1,165 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWrKI6ocU= +github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-resty/resty/v2 v2.8.0 h1:J29d0JFWwSWrDCysnOK/YjsPMLQTx0TvgJEHVGvf2L8= +github.com/go-resty/resty/v2 v2.8.0/go.mod h1:UCui0cMHekLrSntoMyofdSTaPpinlRHFtPpizuyDW2w= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.43.0 h1:iq+BVjvYLei5f27wiuNiB1DN6DYQkp1c8Bx0Vykh5us= +github.com/prometheus/common v0.43.0/go.mod h1:NCvr5cQIh3Y/gy73/RdVtC9r8xxrxwJnB+2lB3BxrFc= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o= +golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/httpapi_exporter.go b/httpapi_exporter.go new file mode 100644 index 0000000..ddf9bbc --- /dev/null +++ b/httpapi_exporter.go @@ -0,0 +1,137 @@ +package main + +import ( + "context" + "fmt" + "net/http" + "os" + "time" + + _ "net/http/pprof" + + kingpin "github.com/alecthomas/kingpin/v2" + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/prometheus/common/expfmt" + "github.com/prometheus/common/promlog" + "github.com/prometheus/common/promlog/flag" + "github.com/prometheus/common/version" +) + +const ( + // Constant values + metricsPublishingPort = ":9321" + exporter_name = "httpapi_exporter" +) + +var ( + listenAddress = kingpin.Flag("web.listen-address", "The address to listen on for HTTP requests.").Default(metricsPublishingPort).String() + metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose collector's internal metrics.").Default("/metrics").String() + configFile = kingpin.Flag("config.file", "Exporter configuration file.").Short('c').Default("config/config.yml").String() + //debug_flag = kingpin.Flag("debug", "debug connection checks.").Short('d').Default("false").Bool() + dry_run = kingpin.Flag("dry-run", "Only check exporter configuration file and exit.").Short('n').Default("false").Bool() + // alsologtostderr = kingpin.Flag("alsologtostderr", "log to standard error as well as files.").Default("true").Bool() + target_name = kingpin.Flag("target", "In dry-run mode specify the target name, else ignored.").Short('t').String() + collector_name = kingpin.Flag("metric", "Specify the collector name restriction to collect, replace the collector_names set for each target.").Short('m').String() +) + +func init() { + prometheus.MustRegister(version.NewCollector(exporter_name)) +} + +func main() { + + logConfig := promlog.Config{} + flag.AddFlags(kingpin.CommandLine, &logConfig) + kingpin.Version(version.Print(exporter_name)).VersionFlag.Short('V') + kingpin.HelpFlag.Short('h') + kingpin.Parse() + + logger := promlog.New(&logConfig) + level.Info(logger).Log("msg", fmt.Sprintf("Starting %s", exporter_name), "version", version.Info()) + level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) + + exporter, err := NewExporter(*configFile, logger, *collector_name) + if err != nil { + level.Error(logger).Log("msg", fmt.Sprintf("Error creating exporter: %s", err)) + os.Exit(1) + } + + if *dry_run { + level.Info(logger).Log("msg", "configuration OK.") + // get the target if defined + var t Target + var err error + if *target_name != "" { + t, err = exporter.FindTarget(*target_name) + } else { + t, err = exporter.GetFirstTarget() + } + if err != nil { + level.Error(logger).Log("errmsg", err) + os.Exit(1) + } + level.Info(logger).Log("msg", fmt.Sprintf("try to collect target %s.", t.Name())) + timeout := time.Duration(0) + configTimeout := time.Duration(exporter.Config().Globals.ScrapeTimeout) + + // If the configured scrape timeout is more restrictive, use that instead. + if configTimeout > 0 && (timeout <= 0 || configTimeout < timeout) { + timeout = configTimeout + } + var ctx context.Context + var cancel context.CancelFunc + if timeout <= 0 { + ctx = context.Background() + cancel = func() {} + } else { + ctx, cancel = context.WithTimeout(context.Background(), timeout) + } + defer cancel() + + gatherer := prometheus.Gatherers{exporter.WithContext(ctx, t)} + mfs, err := gatherer.Gather() + if err != nil { + level.Error(logger).Log("errmsg", fmt.Sprintf("Error gathering metrics: %v", err)) + if len(mfs) == 0 { + os.Exit(1) + } + } else { + level.Info(logger).Log("msg", "collect is OK. Dumping result to stdout.") + } + + //dump metric to stdout + enc := expfmt.NewEncoder(os.Stdout, expfmt.FmtText) + + for _, mf := range mfs { + err := enc.Encode(mf) + if err != nil { + level.Error(logger).Log("Errmsg", err) + break + } + } + if closer, ok := enc.(expfmt.Closer); ok { + // This in particular takes care of the final "# EOF\n" line for OpenMetrics. + closer.Close() + } + level.Info(logger).Log("msg", "dry-run is over. Exiting.") + os.Exit(0) + } + + // Setup and start webserver. + http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { http.Error(w, "OK", http.StatusOK) }) + http.HandleFunc("/", HomeHandlerFunc(*metricsPath, exporter)) + http.HandleFunc("/config", ConfigHandlerFunc(*metricsPath, exporter)) + http.HandleFunc("/status", StatusHandlerFunc(*metricsPath, exporter)) + http.HandleFunc("/targets", TargetsHandlerFunc(*metricsPath, exporter)) + http.Handle(*metricsPath, ExporterHandlerFor(exporter)) + // Expose exporter metrics separately, for debugging purposes. + http.Handle("/httpapi_exporter_metrics", promhttp.Handler()) + + level.Info(logger).Log("msg", "Listening on address", "address", *listenAddress) + if err := http.ListenAndServe(*listenAddress, nil); err != nil { + level.Error(logger).Log("msg", "Error starting HTTP server", "errmsg", err) + os.Exit(1) + } +} diff --git a/metric.go b/metric.go new file mode 100644 index 0000000..f201648 --- /dev/null +++ b/metric.go @@ -0,0 +1,490 @@ +package main + +import ( + "fmt" + "sort" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/proto" +) + +// MetricDesc is a descriptor for a family of metrics, sharing the same name, help, labes, type. +type MetricDesc interface { + Name() string + Help() string + ValueType() prometheus.ValueType + ConstLabels() []*dto.LabelPair + // Labels() []string + LogContext() []interface{} +} + +// +// MetricFamily +// + +// MetricFamily implements MetricDesc for SQL metrics, with logic for populating its labels and values from sql.Rows. +type MetricFamily struct { + config *MetricConfig + constLabels []*dto.LabelPair + // labels []string + labels []*Label // raw string or template + valueslabels []*Label // raw string or template for key and value + // resultsFields []*Field // raw string or template too + logContext []interface{} +} + +// NewMetricFamily creates a new MetricFamily with the given metric config and const labels (e.g. job and instance). +func NewMetricFamily( + logContext []interface{}, + mc *MetricConfig, + constLabels []*dto.LabelPair, + customTemplate *exporterTemplate) (*MetricFamily, error) { + + var ( + err error + labels []*Label + ) + + logContext = append(logContext, "metric", mc.Name) + + if len(mc.Values) == 0 { + logContext = append(logContext, "errmsg", "NewMetricFamily(): multiple values but no value label") + return nil, fmt.Errorf("%s", logContext...) + } + if len(mc.Values) > 1 && len(mc.ValueLabel) == 0 { + logContext = append(logContext, "errmsg", "NewMetricFamily(): multiple values but no value label") + } + + // labels := make(map[Label]*Label, len(mc.KeyLabels)+1) + // valueslabels := make(map[Label]*Label, len(mc.Values)+1) + + // all labels are stored in variable 'labels': size of slice if size of KeyLabels + 1 if size of Values is greater than 1 + if len(mc.key_labels_map) > 0 { + label_len := len(mc.key_labels_map) + if len(mc.Values) > 1 { + label_len++ + } + labels = make([]*Label, label_len) + + // labels = append(labels, mc.KeyLabels...) + + i := 0 + for key, val := range mc.key_labels_map { + labels[i], err = NewLabel(key, val, mc.Name, "key_label", customTemplate) + if err != nil { + return nil, err + } + i++ + } + // add an element in labels for value_label (the name of the label); value will be set later + if len(mc.Values) > 1 { + labels[i], err = NewLabel(mc.ValueLabel, "", mc.Name, "value_label", customTemplate) + if err != nil { + return nil, err + } + } + } + // values are stored in variable 'valueslabels': it is meanfull only when values are greater than 1. + // original config struct is a map of [value label] : [value template]; + valueslabels := make([]*Label, len(mc.Values)) + + i := 0 + for key, val := range mc.Values { + valueslabels[i], err = NewLabel(key, val, mc.Name, "'values'", customTemplate) + if err != nil { + return nil, err + } + i++ + // valueslabels[*keylabel] = valuelabel + } + + // Create a copy of original slice to avoid modifying constLabels + sortedLabels := append(constLabels[:0:0], constLabels...) + + for k, v := range mc.StaticLabels { + sortedLabels = append(sortedLabels, &dto.LabelPair{ + Name: proto.String(k), + Value: proto.String(v), + }) + } + sort.Sort(labelPairSorter(sortedLabels)) + + // results := make([]*Field, len(mc.ResultFields)) + // for i, res_field := range mc.ResultFields { + // res, err := NewField(res_field, customTemplate) + // if err != nil { + // return nil, fmt.Errorf("NewMetricFamily(): result field invalid metric{name:'%s', result: '%s'}: %s", mc.Name, res_field, err) + // } + // results[i] = res + // } + + return &MetricFamily{ + config: mc, + constLabels: sortedLabels, + labels: labels, + valueslabels: valueslabels, + // resultsFields: results, + logContext: logContext, + }, nil +} + +// Collect is the equivalent of prometheus.Collector.Collect() but takes a Query output map to populate values from. +func (mf MetricFamily) Collect(rawdatas any, logger log.Logger, ch chan<- Metric) { + var set_root bool = false + // reset logcontxt for MetricFamily: remove previous errors if any + mf.logContext = make([]any, 2) + mf.logContext[0] = "metric" + mf.logContext[1] = mf.config.Name + + item, ok := rawdatas.(map[string]any) + if !ok { + ch <- NewInvalidMetric(mf.logContext, fmt.Errorf("metrtic %s invalid type", mf.config.Name)) + } + // set default scope to "loop_var" entry: item[item["loop_var"]] + if mf.config.Scope == "" { + if loop_var, ok := item["loop_var"].(string); ok { + if loop_var == "empty_item" { + loop_var = "item" + } + if datas, ok := item[loop_var].(map[string]any); ok { + item = datas + item["root"] = rawdatas + set_root = true + } + } + } else if mf.config.Scope != "none" { + var err error + item, err = SetScope(mf.config.Scope, item) + if err != nil { + ch <- NewInvalidMetric(mf.logContext, err) + } + item["root"] = rawdatas + set_root = true + } + + // build the labels family with the content of the var(*Field) + if len(mf.labels) == 0 && mf.config.key_labels != nil { + if labelsmap_raw, err := ValorizeValue(item, mf.config.key_labels, logger, 0); err == nil { + if key_labels_map, ok := labelsmap_raw.(map[string]any); ok { + label_len := len(key_labels_map) + if len(mf.config.Values) > 1 { + label_len++ + } + mf.labels = make([]*Label, label_len) + + i := 0 + for key, val_raw := range key_labels_map { + if val, ok := val_raw.(string); ok { + mf.labels[i], err = NewLabel(key, val, mf.config.Name, "key_label", nil) + if err != nil { + level.Warn(logger).Log("errmsg", fmt.Sprintf("invalid template for key_values for metric %s: %s (maybe use |toRawJson.)", mf.config.Name, err)) + continue + } + } + i++ + } + // add an element in labels for value_label (the name of the label); value will be set later + if len(mf.config.Values) > 1 { + mf.labels[i], _ = NewLabel(mf.config.ValueLabel, "", mf.config.Name, "value_label", nil) + if err != nil { + level.Warn(logger).Log("errmsg", fmt.Sprintf("invalid templatefor value_label for metric %s: %s (maybe use |toRawJson.)", mf.config.Name, err)) + // return nil, err + } + } + } + } else { + level.Warn(logger).Log("errmsg", fmt.Sprintf("invalid template for key_values for metric %s: %s (maybe use |toRawJson.)", mf.config.Name, err)) + } + } + + labelNames := make([]string, len(mf.labels)) + labelValues := make([]string, len(mf.labels)) + + for i, label := range mf.labels { + var err error + // last label may be null if metric has no value label, because it has only one value + if label == nil { + continue + } + if item != nil { + if label.Key != nil { + labelNames[i], err = label.Key.GetValueString(item, nil, false) + if err != nil { + ch <- NewInvalidMetric(mf.logContext, fmt.Errorf("invalid template label name metric{ name: %s, key: %s} : %s", mf.config.Name, label.Key.tmpl.Tree.Root.String(), err)) + } + } + + if label.Value != nil { + sub := make(map[string]string) + sub["_"] = labelNames[i] + labelValues[i], err = label.Value.GetValueString(item, sub, true) + if err != nil { + ch <- NewInvalidMetric(mf.logContext, fmt.Errorf("invalid template label value metric{ name: %s, value: %s} : %s", mf.config.Name, label.Value.tmpl.Tree.Root.String(), err)) + continue + } + } + } else { + labelNames[i] = fmt.Sprintf("undef_%d", i) + labelValues[i] = "" + } + i++ + } + + for _, label := range mf.valueslabels { + var f_value float64 + var err error + + // fill the label value for value_label with the current name of the value if there is a label ! + if len(mf.config.Values) > 1 { + labelValues[len(labelValues)-1], err = label.Key.GetValueString(item, nil, false) + if err != nil { + ch <- NewInvalidMetric(mf.logContext, fmt.Errorf("invalid template label name metric{ name: %s, key: %s} : %s", mf.config.Name, label.Key.tmpl.Tree.Root.String(), err)) + continue + } + } + f_value, err = label.Value.GetValueFloat(item) + if err != nil { + ch <- NewInvalidMetric(mf.logContext, fmt.Errorf("invalid template label value metric{ name: %s, value: %s} : %s", mf.config.Name, label.Value.tmpl.Tree.Root.String(), err)) + } + + ch <- NewMetric(&mf, f_value, labelNames, labelValues) + } + if set_root { + delete(item, "root") + } +} + +// Name implements MetricDesc. +func (mf MetricFamily) Name() string { + return mf.config.Name +} + +// Help implements MetricDesc. +func (mf MetricFamily) Help() string { + return mf.config.Help +} + +// ValueType implements MetricDesc. +func (mf MetricFamily) ValueType() prometheus.ValueType { + return mf.config.ValueType() +} + +// ConstLabels implements MetricDesc. +func (mf MetricFamily) ConstLabels() []*dto.LabelPair { + return mf.constLabels +} + +// Labels implements MetricDesc. +// func (mf MetricFamily) Labels() []string { +// return mf.labels +// } + +// LogContext implements MetricDesc. +func (mf MetricFamily) LogContext() []interface{} { + return mf.logContext +} + +// +// automaticMetricDesc +// + +// automaticMetric is a MetricDesc for automatically generated metrics (e.g. `up` and `scrape_duration`). +type automaticMetricDesc struct { + name string + help string + valueType prometheus.ValueType + labels []string + constLabels []*dto.LabelPair + logContext []interface{} +} + +// NewAutomaticMetricDesc creates a MetricDesc for automatically generated metrics. +func NewAutomaticMetricDesc( + logContext []interface{}, name, help string, valueType prometheus.ValueType, constLabels []*dto.LabelPair, labels ...string) MetricDesc { + return &automaticMetricDesc{ + name: name, + help: help, + valueType: valueType, + constLabels: constLabels, + labels: labels, + logContext: logContext, + } +} + +// Name implements MetricDesc. +func (a automaticMetricDesc) Name() string { + return a.name +} + +// Help implements MetricDesc. +func (a automaticMetricDesc) Help() string { + return a.help +} + +// ValueType implements MetricDesc. +func (a automaticMetricDesc) ValueType() prometheus.ValueType { + return a.valueType +} + +// ConstLabels implements MetricDesc. +func (a automaticMetricDesc) ConstLabels() []*dto.LabelPair { + return a.constLabels +} + +// Labels implements MetricDesc. +func (a automaticMetricDesc) Labels() []string { + return a.labels +} + +// LogContext implements MetricDesc. +func (a automaticMetricDesc) LogContext() []interface{} { + return a.logContext +} + +// +// Metric +// + +// A Metric models a single sample value with its meta data being exported to Prometheus. +type Metric interface { + Desc() MetricDesc + Write(out *dto.Metric) error +} + +// NewMetric returns a metric with one fixed value that cannot be changed. +// +// NewMetric panics if the length of labelValues is not consistent with desc.labels(). +func NewMetric(desc MetricDesc, value float64, labelNames []string, labelValues []string) Metric { + if len(labelNames) != len(labelValues) { + panic(fmt.Sprintf("[%s] expected %d labels, got %d", desc.LogContext(), len(labelNames), len(labelValues))) + } + return &constMetric{ + desc: desc, + val: value, + labelPairs: makeLabelPairs(desc, labelNames, labelValues), + } + // fmt.Printf("met: %+v\n", tmp) + // fmt.Printf("\tmet: %s k[%d]:%v v[%d]: %v\n", tmp.desc.Name(), len(labelNames), labelNames, len(labelValues), labelValues) + // return tmp +} + +// constMetric is a metric with one fixed value that cannot be changed. +type constMetric struct { + desc MetricDesc + val float64 + labelPairs []*dto.LabelPair +} + +// Desc implements Metric. +func (m *constMetric) Desc() MetricDesc { + return m.desc +} + +// Write implements Metric. +func (m *constMetric) Write(out *dto.Metric) error { + out.Label = m.labelPairs + switch t := m.desc.ValueType(); t { + case prometheus.CounterValue: + out.Counter = &dto.Counter{Value: proto.Float64(m.val)} + case prometheus.GaugeValue: + out.Gauge = &dto.Gauge{Value: proto.Float64(m.val)} + default: + var logContext []interface{} + logContext = append(logContext, m.desc.LogContext()...) + logContext = append(logContext, "errmsg", fmt.Sprintf("encountered unknown type %v", t)) + return fmt.Errorf("%s", logContext...) + } + return nil +} + +func makeLabelPairs(desc MetricDesc, labelNames []string, labelValues []string) []*dto.LabelPair { + labels := labelNames + constLabels := desc.ConstLabels() + + totalLen := len(labels) + len(constLabels) + if totalLen == 0 { + // Super fast path. + return nil + } + if len(labels) == 0 { + // Moderately fast path. + return constLabels + } + labelPairs := make([]*dto.LabelPair, 0, totalLen) + for i, label := range labels { + labelPairs = append(labelPairs, &dto.LabelPair{ + Name: proto.String(label), + Value: proto.String(labelValues[i]), + }) + } + labelPairs = append(labelPairs, constLabels...) + sort.Sort(labelPairSorter(labelPairs)) + return labelPairs +} + +// labelPairSorter implements sort.Interface. +// It provides a sortable version of a slice of dto.LabelPair pointers. + +type labelPairSorter []*dto.LabelPair + +func (s labelPairSorter) Len() int { + return len(s) +} + +func (s labelPairSorter) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s labelPairSorter) Less(i, j int) bool { + return s[i].GetName() < s[j].GetName() +} + +type invalidMetric struct { + logContext []interface{} + err error +} + +// NewInvalidMetric returns a metric whose Write method always returns the provided error. +func NewInvalidMetric(logContext []interface{}, err error) Metric { + return invalidMetric{ + logContext: logContext, + err: err, + } +} + +func (m invalidMetric) Desc() MetricDesc { return nil } + +func (m invalidMetric) Write(*dto.Metric) error { + return m.err +} + +type Label struct { + Key *Field + Value *Field +} + +func NewLabel(key string, value string, mName string, errStr string, customTemplate *exporterTemplate) (*Label, error) { + var ( + keyField, valueField *Field + err error + ) + + keyField, err = NewField(key, customTemplate) + if err != nil { + return nil, fmt.Errorf("NewMetricFamily(): name of %s for metric %q: %s", errStr, mName, err) + } + if value != "" { + valueField, err = NewField(value, customTemplate) + if err != nil { + return nil, fmt.Errorf("NewMetricFamily(): value of %s for metric %q: %s", errStr, mName, err) + } + } + + return &Label{ + Key: keyField, + Value: valueField, + }, nil +} diff --git a/metric_action.go b/metric_action.go new file mode 100644 index 0000000..8c31d44 --- /dev/null +++ b/metric_action.go @@ -0,0 +1,164 @@ +package main + +import ( + //"bytes" + "fmt" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// metric fact +// *************************************************************************************** +// *************************************************************************************** + +type MetricAction struct { + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + + mc *MetricConfig + + metricFamily *MetricFamily + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +func (a *MetricAction) Type() int { + return metric_action +} + +func (a *MetricAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +func (a *MetricAction) GetNameField() *Field { + return a.Name +} +func (a *MetricAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *MetricAction) GetWidh() []any { + return a.With +} +func (a *MetricAction) SetWidth(with []any) { + a.With = with +} + +func (a *MetricAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *MetricAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *MetricAction) GetLoopVar() string { + return a.LoopVar +} +func (a *MetricAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *MetricAction) GetVars() map[string]any { + return a.Vars +} +func (a *MetricAction) SetVars(vars map[string]any) { + a.Vars = vars +} + +func (a *MetricAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *MetricAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +func (a *MetricAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *MetricAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// only for MetricsAction +func (a *MetricAction) GetMetrics() []*GetMetricsRes { + return nil +} + +// only for MetricAction +func (a *MetricAction) GetMetric() *MetricConfig { + return a.mc +} + +func (a *MetricAction) SetMetricFamily(mf *MetricFamily) { + a.metricFamily = mf +} + +// only for PlayAction +func (a *MetricAction) SetPlayAction(scripts map[string]*YAMLScript) error { + return nil +} + +// specific behavior for the MetricAction + +func (a *MetricAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + var ( + metric_channel chan<- Metric + // mfs []*MetricFamily + ) + loop_var_idx := "" + if raw_loop_var_idx, ok := symtab["loop_var_idx"].(int); ok { + if raw_loop_var_idx > 0 { + loop_var_idx = fmt.Sprintf(" %d", raw_loop_var_idx) + } + } + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: MetricAction] Name: %s%s", a.GetName(symtab, logger), loop_var_idx)) + + if r_val, ok := symtab["__channel"]; ok { + if metric_channel, ok = r_val.(chan<- Metric); !ok { + panic("invalid context (channel)") + } + } else { + panic("invalid context (channel)") + } + + // for _, mf := range mfs { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf(" metric_name: %s", a.metricFamily.Name())) + a.metricFamily.Collect(symtab, logger, metric_channel) + // } + + return nil +} + +func (a *MetricAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + + return nil +} diff --git a/metrics_action.go b/metrics_action.go new file mode 100644 index 0000000..1b14272 --- /dev/null +++ b/metrics_action.go @@ -0,0 +1,230 @@ +package main + +import ( + //"bytes" + "fmt" + "strings" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// metrics action (block / loop ) +// *************************************************************************************** +// *************************************************************************************** + +type MetricsAction struct { + // BaseAction + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + + Metrics []*MetricConfig `yaml:"metrics"` // metrics defined by this collector + Scope string `yaml:"scope,omitempty"` // var path where to collect data: shortcut for {{ .scope.path.var }} + MetricPrefix string `yaml:"metric_prefix,omitempty"` // var to alert metric name + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` + Actions []Action `yaml:"-"` +} + +func (a *MetricsAction) Type() int { + return metrics_action +} + +func (a *MetricsAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +func (a *MetricsAction) GetNameField() *Field { + return a.Name +} +func (a *MetricsAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *MetricsAction) GetWidh() []any { + return a.With +} +func (a *MetricsAction) SetWidth(with []any) { + a.With = with +} + +func (a *MetricsAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *MetricsAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *MetricsAction) GetLoopVar() string { + return a.LoopVar +} +func (a *MetricsAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *MetricsAction) GetVars() map[string]any { + return a.Vars +} +func (a *MetricsAction) SetVars(vars map[string]any) { + a.Vars = vars +} + +func (a *MetricsAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *MetricsAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +func (a *MetricsAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *MetricsAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// only for MetricsAction +func (a *MetricsAction) GetMetrics() []*GetMetricsRes { + res := make([]*GetMetricsRes, 1) + res[0] = &GetMetricsRes{ + mc: a.Metrics, + maprefix: a.MetricPrefix, + } + return res +} + +// only for MetricAction +func (a *MetricsAction) GetMetric() *MetricConfig { + return nil +} +func (a *MetricsAction) SetMetricFamily(*MetricFamily) { +} + +// only for PlayAction +func (a *MetricsAction) SetPlayAction(scripts map[string]*YAMLScript) error { + return nil +} + +// *************************************************************************************** +// specific behavior for the MetricsAction +func SetScope(scope string, symtab map[string]any) (map[string]any, error) { + var err error + + tmp_symtab := symtab + // split the scope string into parts: attr1.attr[0].attr + if scope[0] == '.' { + scope = scope[1:] + } + vars := strings.Split(scope, ".") + // last_elmt := len(vars) -1 + for _, var_name := range vars { + if raw_value, ok := tmp_symtab[var_name]; ok { + switch cur_value := raw_value.(type) { + case map[string]any: + tmp_symtab = cur_value + default: + err = fmt.Errorf("can't set scope: '%s' has invalid type", var_name) + } + // } + } else { + err = fmt.Errorf("can't set scope: '%s' not found", var_name) + } + } + return tmp_symtab, err +} + +// *************************************************************************************** +func (a *MetricsAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + + var ( + metric_channel chan<- Metric + // mfs []*MetricFamily + ) + // var logContext []any + + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: MetricsAction] Name: %s - %d metrics_name to set", a.GetName(symtab, logger), len(a.Metrics))) + + query_status, ok := GetMapValueBool(symtab, "query_status") + if !ok || (ok && !query_status) { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: MetricsAction] Name: %s - previous query has invalid status skipping", a.GetName(symtab, logger))) + return nil + } + + if r_val, ok := symtab["__channel"]; ok { + if metric_channel, ok = r_val.(chan<- Metric); !ok { + panic("invalid context (channel)") + } + } else { + panic("invalid context (channel)") + } + + // check if user has specified a + tmp_symtab := symtab + // check if user has specified a scope for result : change the symtab access to that scope + if a.Scope != "" && a.Scope != "none" { + var err error + tmp_symtab, err = SetScope(a.Scope, tmp_symtab) + if err != nil { + level.Warn(logger).Log("errmsg", err) + } + + tmp_symtab["__name__"] = symtab["__name__"] + } + for _, cur_act := range a.Actions { + tmp_symtab["__channel"] = metric_channel + // tmp_symtab["__metricfamilies"] = mfs + // fmt.Printf("\tadd to symbols table: %s = %v\n", key, val) + if err := PlayBaseAction(script, tmp_symtab, logger, cur_act, cur_act.CustomAction); err != nil { + return err + } + + } + if a.Scope != "" && a.Scope != "none" && tmp_symtab != nil { + delete(tmp_symtab, "__name__") + } + + return nil +} + +// *************************************************************************************** +func (a *MetricsAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + + for _, cur_act := range a.Actions { + err := cur_act.AddCustomTemplate(customTemplate) + if err != nil { + return err + } + } + + return nil +} + +// *************************************************************************************** diff --git a/play_script_action.go b/play_script_action.go new file mode 100644 index 0000000..dad11f4 --- /dev/null +++ b/play_script_action.go @@ -0,0 +1,141 @@ +package main + +import ( + //"bytes" + "fmt" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// play_script_fact +// *************************************************************************************** +// *************************************************************************************** + +type PlayScriptAction struct { + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + PlayScriptActionName string `yaml:"play_script"` + + playScriptAction *YAMLScript + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +func (a *PlayScriptAction) Type() int { + return play_script_action +} + +func (a *PlayScriptAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +func (a *PlayScriptAction) GetNameField() *Field { + return a.Name +} +func (a *PlayScriptAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *PlayScriptAction) GetWidh() []any { + return a.With +} +func (a *PlayScriptAction) SetWidth(with []any) { + a.With = with +} + +func (a *PlayScriptAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *PlayScriptAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *PlayScriptAction) GetLoopVar() string { + return a.LoopVar +} +func (a *PlayScriptAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *PlayScriptAction) GetVars() map[string]any { + return a.Vars +} +func (a *PlayScriptAction) SetVars(vars map[string]any) { + a.Vars = vars +} + +func (a *PlayScriptAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *PlayScriptAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +func (a *PlayScriptAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *PlayScriptAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// only for MetricsAction +func (a *PlayScriptAction) GetMetrics() []*GetMetricsRes { + return nil +} + +// only for MetricAction +func (a *PlayScriptAction) GetMetric() *MetricConfig { + return nil +} +func (a *PlayScriptAction) SetMetricFamily(*MetricFamily) { +} + +// only for PlayAction +func (a *PlayScriptAction) SetPlayAction(scripts map[string]*YAMLScript) error { + if script, ok := scripts[a.PlayScriptActionName]; ok && script != nil { + a.playScriptAction = script + return nil + } + return fmt.Errorf("scriptname not found in play_script action %s", a.PlayScriptActionName) +} + +// specific behavior for the PlayScriptAction +func (a *PlayScriptAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + // var err error + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: PlayScriptAction] Name: %s", a.GetName(symtab, logger))) + + return a.playScriptAction.Play(symtab, false, logger) +} + +func (a *PlayScriptAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + return a.playScriptAction.AddCustomTemplate(customTemplate) +} + +// *************************************************************************************** diff --git a/promhttp.go b/promhttp.go new file mode 100644 index 0000000..46521bb --- /dev/null +++ b/promhttp.go @@ -0,0 +1,154 @@ +package main + +import ( + "bytes" + "compress/gzip" + "context" + "fmt" + "io" + "net/http" + "strconv" + "strings" + "sync" + "time" + + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/expfmt" +) + +const ( + contentTypeHeader = "Content-Type" + contentLengthHeader = "Content-Length" + contentEncodingHeader = "Content-Encoding" + acceptEncodingHeader = "Accept-Encoding" +) + +// ExporterHandlerFor returns an http.Handler for the provided Exporter. +func ExporterHandlerFor(exporter Exporter) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + ctx, cancel := contextFor(req, exporter) + defer cancel() + + params := req.URL.Query() + tname := params.Get("target") + if tname == "" { + err := fmt.Errorf("Target parameter is missing") + HandleError(http.StatusBadRequest, err, *metricsPath, exporter, w, req) + return + } + + t, err := exporter.FindTarget(tname) + if err != nil { + HandleError(http.StatusNotFound, err, *metricsPath, exporter, w, req) + return + } + + auth_key := params.Get("auth_key") + if auth_key != "" { + t.SetSymbol("auth_key", auth_key) + } + + // Go through prometheus.Gatherers to sanitize and sort metrics. + gatherer := prometheus.Gatherers{exporter.WithContext(ctx, t)} + mfs, err := gatherer.Gather() + if err != nil { + level.Error(exporter.Logger()).Log("msg", fmt.Sprintf("Error gathering metrics for '%s': %s", tname, err)) + if len(mfs) == 0 { + http.Error(w, "No metrics gathered, "+err.Error(), http.StatusInternalServerError) + return + } + } + + contentType := expfmt.Negotiate(req.Header) + buf := getBuf() + defer giveBuf(buf) + writer, encoding := decorateWriter(req, buf) + enc := expfmt.NewEncoder(writer, contentType) + var errs prometheus.MultiError + for _, mf := range mfs { + if err := enc.Encode(mf); err != nil { + errs = append(errs, err) + level.Info(exporter.Logger()).Log("msg", fmt.Sprintf("Error encoding metric family %q: %s", mf.GetName(), err)) + } + } + if closer, ok := writer.(io.Closer); ok { + closer.Close() + } + if errs.MaybeUnwrap() != nil && buf.Len() == 0 { + err = fmt.Errorf("no metrics encoded: %s, ", errs.Error()) + HandleError(http.StatusInternalServerError, err, *metricsPath, exporter, w, req) + return + } + header := w.Header() + header.Set(contentTypeHeader, string(contentType)) + header.Set(contentLengthHeader, fmt.Sprint(buf.Len())) + if encoding != "" { + header.Set(contentEncodingHeader, encoding) + } + w.Write(buf.Bytes()) + }) +} + +func contextFor(req *http.Request, exporter Exporter) (context.Context, context.CancelFunc) { + timeout := time.Duration(0) + configTimeout := time.Duration(exporter.Config().Globals.ScrapeTimeout) + // If a timeout is provided in the Prometheus header, use it. + if v := req.Header.Get("X-Prometheus-Scrape-Timeout-Seconds"); v != "" { + timeoutSeconds, err := strconv.ParseFloat(v, 64) + if err != nil { + level.Error(exporter.Logger()).Log("msg", fmt.Sprintf("Failed to parse timeout (`%s`) from Prometheus header: %s", v, err)) + } else { + timeout = time.Duration(timeoutSeconds * float64(time.Second)) + + // Subtract the timeout offset, unless the result would be negative or zero. + timeoutOffset := time.Duration(exporter.Config().Globals.TimeoutOffset) + if timeoutOffset > timeout { + level.Error(exporter.Logger()).Log("msg", fmt.Sprintf("global.scrape_timeout_offset (`%s`) is greater than Prometheus' scraping timeout (`%s`), ignoring", + timeoutOffset, timeout)) + } else { + timeout -= timeoutOffset + } + } + } + + // If the configured scrape timeout is more restrictive, use that instead. + if configTimeout > 0 && (timeout <= 0 || configTimeout < timeout) { + timeout = configTimeout + } + + if timeout <= 0 { + return context.Background(), func() {} + } + return context.WithTimeout(context.Background(), timeout) +} + +var bufPool sync.Pool + +func getBuf() *bytes.Buffer { + buf := bufPool.Get() + if buf == nil { + return &bytes.Buffer{} + } + return buf.(*bytes.Buffer) +} + +func giveBuf(buf *bytes.Buffer) { + buf.Reset() + bufPool.Put(buf) +} + +// decorateWriter wraps a writer to handle gzip compression if requested. It +// returns the decorated writer and the appropriate "Content-Encoding" header +// (which is empty if no compression is enabled). +func decorateWriter(request *http.Request, writer io.Writer) (w io.Writer, encoding string) { + header := request.Header.Get(acceptEncodingHeader) + parts := strings.Split(header, ",") + for _, part := range parts { + part := strings.TrimSpace(part) + if part == "gzip" || strings.HasPrefix(part, "gzip;") { + return gzip.NewWriter(writer), "gzip" + } + } + return writer, "" +} diff --git a/query_action.go b/query_action.go new file mode 100644 index 0000000..d4f2811 --- /dev/null +++ b/query_action.go @@ -0,0 +1,462 @@ +package main + +import ( + //"bytes" + "fmt" + "strconv" + "strings" + "time" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// query +// *************************************************************************************** +// *************************************************************************************** +type QueryActionConfig struct { + Query string `yaml:"url"` + Method string `yaml:"method,omitempty"` + Data string `yaml:"data,omitempty"` + Debug ConvertibleBoolean `yaml:"debug,omitempty"` + VarName string `yaml:"var_name,omitempty"` + OkStatus any `yaml:"ok_status,omitempty"` + AuthConfig *AuthConfig `yaml:"auth_mode,omitempty"` + Timeout int `yaml:"timeout,omitempty"` + + query *Field + method *Field + data *Field + var_name *Field + + auth_mode *Field + user *Field + passwd *Field + token *Field + + ok_status []int + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for QueryActionConfig. +func (qc *QueryActionConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + type plain QueryActionConfig + var err error + if err := unmarshal((*plain)(qc)); err != nil { + return err + } + // Check required fields + qc.query, err = NewField(qc.Query, nil) + if err != nil { + return fmt.Errorf("invalid template for query %q: %s", qc.Query, err) + } + + if qc.Method == "" { + qc.Method = "GET" + } + qc.method, err = NewField(qc.Method, nil) + if err != nil { + return fmt.Errorf("invalid template for method %q: %s", qc.Method, err) + } + if qc.Data != "" { + qc.data, err = NewField(qc.Data, nil) + if err != nil { + return fmt.Errorf("invalid template for data %q: %s", qc.Data, err) + } + } else { + qc.data = nil + } + + if qc.VarName == "" { + qc.VarName = "_root" + } + qc.var_name, err = NewField(qc.VarName, nil) + if err != nil { + return fmt.Errorf("invalid template for var_name %q: %s", qc.VarName, err) + } + + if qc.OkStatus != nil { + qc.ok_status = buildStatus(qc.OkStatus) + } + // set default if something was wrong or not set + if qc.ok_status == nil { + qc.ok_status = []int{200} + } + + if qc.AuthConfig != (*AuthConfig)(nil) { + if qc.AuthConfig.Mode != "" { + qc.auth_mode, err = NewField(qc.AuthConfig.Mode, nil) + if err != nil { + return fmt.Errorf("invalid template for query auth_mode %q: %s", qc.AuthConfig.Mode, err) + } + } + if qc.AuthConfig.Username != "" { + qc.user, err = NewField(qc.AuthConfig.Username, nil) + if err != nil { + return fmt.Errorf("invalid template for query username %q: %s", qc.AuthConfig.Username, err) + } + } + if qc.AuthConfig.Password != "" { + qc.passwd, err = NewField(string(qc.AuthConfig.Password), nil) + if err != nil { + return fmt.Errorf("invalid template for query password %q: %s", qc.AuthConfig.Password, err) + } + } + if qc.AuthConfig.Token != "" { + qc.token, err = NewField(string(qc.AuthConfig.Token), nil) + if err != nil { + return fmt.Errorf("invalid template for query auth_token %q: %s", qc.AuthConfig.Token, err) + } + } + } + + // switch curval := qc.OkStatus.(type) { + // case int: + // qc.ok_status = make([]int, 1) + // qc.ok_status[0] = curval + // case []int: + // qc.ok_status = curval + // case []string: + // var i_value int64 + // var err error + // qc.ok_status = make([]int, len(curval)) + // for idx, status := range curval { + // if i_value, err = strconv.ParseInt(strings.Trim(status, "\r\n "), 10, 0); err != nil { + // i_value = 0 + // } + // qc.ok_status[idx] = int(i_value) + // } + + // } + + return checkOverflow(qc.XXX, "query action") +} + +func buildStatus(raw_status any) []int { + var status []int + switch curval := raw_status.(type) { + case int: + status = make([]int, 1) + status[0] = curval + case []any: + status = make([]int, len(curval)) + for idx, subval := range curval { + switch sub_val := subval.(type) { + case int: + status[idx] = sub_val + case string: + var i_value int64 + var err error + if i_value, err = strconv.ParseInt(strings.Trim(sub_val, "\r\n "), 10, 0); err != nil { + i_value = 0 + } + status[idx] = int(i_value) + } + } + } + return status +} + +type QueryAction struct { + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + Query *QueryActionConfig + + // Catches all undefined fields and must be empty after parsing. + XXX map[string]interface{} `yaml:",inline" json:"-"` +} + +// ***************** +func (a *QueryAction) Type() int { + return query_action +} + +func (a *QueryAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} +func (a *QueryAction) GetNameField() *Field { + return a.Name +} +func (a *QueryAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *QueryAction) GetWidh() []any { + return a.With +} +func (a *QueryAction) SetWidth(with []any) { + a.With = with +} + +func (a *QueryAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *QueryAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *QueryAction) GetLoopVar() string { + return a.LoopVar +} +func (a *QueryAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *QueryAction) GetVars() map[string]any { + return a.Vars +} +func (a *QueryAction) SetVars(vars map[string]any) { + a.Vars = vars +} + +func (a *QueryAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *QueryAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +func (a *QueryAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *QueryAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// only for MetricsAction +func (a *QueryAction) GetMetrics() []*GetMetricsRes { + return nil +} + +// only for MetricAction +func (a *QueryAction) GetMetric() *MetricConfig { + return nil +} +func (a *QueryAction) SetMetricFamily(*MetricFamily) { +} + +// only for PlayAction +func (a *QueryAction) SetPlayAction(scripts map[string]*YAMLScript) error { + return nil +} + +func (a *QueryAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + + if a.Query.query != nil { + if err := a.Query.query.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + if a.Query.method != nil { + if err := a.Query.method.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + if a.Query.data != nil { + if err := a.Query.data.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + if a.Query.var_name != nil { + if err := a.Query.var_name.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + if a.Query.auth_mode != nil { + if err := a.Query.auth_mode.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + if a.Query.user != nil { + if err := a.Query.user.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + if a.Query.passwd != nil { + if err := a.Query.passwd.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + if a.Query.token != nil { + if err := a.Query.token.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + + return nil +} + +func (a *QueryAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + var ( + err error + payload, query, method, var_name string + auth_mode, user, passwd, auth_token string + ) + + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: QueryAction] Name: %s", a.GetName(symtab, logger))) + + query, err = a.Query.query.GetValueString(symtab, nil, false) + if err != nil { + query = a.Query.Query + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for query '%s': %v", a.Query.Query, err)) + } + // symtab["url"] = query + + if a.Query.data != nil { + // auth_key, ok := symtab["auth_key"] + // if ok { + // level.Debug(logger).Log("authkey", auth_key) + // } + + payload, err = a.Query.data.GetValueString(symtab, nil, false) + if err != nil { + payload = a.Query.Data + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for data '%s': %v", a.Query.Data, err)) + } + } else { + payload = "" + } + // symtab["data"] = payload + + method, err = a.Query.method.GetValueString(symtab, nil, false) + if err != nil { + method = strings.ToUpper(a.Query.Method) + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for method '%s': %v", a.Query.Method, err)) + } + // symtab["method"] = method + + // maybe a good idea to check template var in ok_status too + // ok_status, err = a.Query.method.GetValueString(symtab, nil, false) + // if err != nil { + // method = strings.ToUpper(method) + // level.Warn(logger).Log( + // "script", a.ScriptName(symtab, logger), + // "msg", fmt.Sprintf("invalid template for method '%s': %v", method, err)) + // } + // symtab["ok_status"] = a.Query.ok_status + + var_name, err = a.Query.var_name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for var_name '%s': %v", a.Query.VarName, err)) + } + // check_invalid_auth, found := GetMapValueBool(symtab, "check_invalid_auth") + // if !found { + // check_invalid_auth = true + // } + // symtab["var_name"] = var_name + + auth_mode, err = a.Query.auth_mode.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for auth_mode.mode '%s': %v", a.Query.AuthConfig.Mode, err)) + } + + user, err = a.Query.user.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for auth_mode.user '%s': %v", a.Query.AuthConfig.Username, err)) + } + + passwd, err = a.Query.passwd.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for auth_mode.user '%s': %v", a.Query.AuthConfig.Password, err)) + } + + auth_token, err = a.Query.token.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("invalid template for auth_mode.token '%s': %v", a.Query.AuthConfig.Token, err)) + } + + // reset special var headers from th symbols table + delete(symtab, "headers") + params := &CallClientExecuteParams{ + Payload: payload, + Method: method, + Url: query, + Debug: bool(a.Query.Debug), + VarName: var_name, + OkStatus: a.Query.ok_status, + AuthMode: auth_mode, + Username: user, + Password: passwd, + Token: auth_token, + Timeout: time.Duration(a.Query.Timeout) * time.Second, + // Check_invalid_Auth: check_invalid_auth, + } + + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf(" query: '%s' - method: '%s' - target_var: '%s'", query, method, a.Query.VarName)) + + if raw_func, ok := symtab["__method"]; ok { + if Func, ok := raw_func.(func(*CallClientExecuteParams, map[string]any) error); ok { + if err = Func(params, symtab); err != nil { + if err != ErrInvalidLogin { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("internal method returns error: '%v'", err)) + } + } + } + // delete(symtab, "url") + // delete(symtab, "data") + // delete(symtab, "method") + // delete(symtab, "ok_status") + // delete(symtab, "var_name") + + } else { + level.Warn(logger).Log( + "script", ScriptName(symtab, logger), + "msg", "internal method to play not found") + } + return err +} + +// *************************************************************************************** diff --git a/set_fact_action.go b/set_fact_action.go new file mode 100644 index 0000000..882c013 --- /dev/null +++ b/set_fact_action.go @@ -0,0 +1,257 @@ +package main + +import ( + //"bytes" + "fmt" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +// *************************************************************************************** +// *************************************************************************************** +// set_fact +// *************************************************************************************** +// *************************************************************************************** + +type SetFactAction struct { + Name *Field `yaml:"name,omitempty"` + With []any `yaml:"with,omitempty"` + When []*exporterTemplate `yaml:"when,omitempty"` + LoopVar string `yaml:"loop_var,omitempty"` + Vars map[string]any `yaml:"vars,omitempty"` + Until []*exporterTemplate `yaml:"until,omitempty"` + + SetFact map[string]any `yaml:"set_fact"` + + setFact [][]any +} + +func (a *SetFactAction) Type() int { + return set_fact_action +} + +func (a *SetFactAction) GetName(symtab map[string]any, logger log.Logger) string { + str, err := a.Name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +func (a *SetFactAction) GetNameField() *Field { + return a.Name +} +func (a *SetFactAction) SetNameField(name *Field) { + a.Name = name +} + +func (a *SetFactAction) GetWidh() []any { + return a.With +} +func (a *SetFactAction) SetWidth(with []any) { + a.With = with +} + +func (a *SetFactAction) GetWhen() []*exporterTemplate { + return a.When + +} +func (a *SetFactAction) SetWhen(when []*exporterTemplate) { + a.When = when +} + +func (a *SetFactAction) GetLoopVar() string { + return a.LoopVar +} +func (a *SetFactAction) SetLoopVar(loopvar string) { + a.LoopVar = loopvar +} + +func (a *SetFactAction) GetVars() map[string]any { + return a.Vars +} +func (a *SetFactAction) SetVars(vars map[string]any) { + a.Vars = vars +} +func (a *SetFactAction) GetUntil() []*exporterTemplate { + return a.Until +} +func (a *SetFactAction) SetUntil(until []*exporterTemplate) { + a.Until = until +} + +func (a *SetFactAction) setBasicElement( + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + return setBasicElement(a, nameField, vars, with, loopVar, when, until) +} + +func (a *SetFactAction) PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + + return PlayBaseAction(script, symtab, logger, a, a.CustomAction) +} + +// only for MetricsAction +func (a *SetFactAction) GetMetrics() []*GetMetricsRes { + return nil +} + +// only for MetricAction +func (a *SetFactAction) GetMetric() *MetricConfig { + return nil +} +func (a *SetFactAction) SetMetricFamily(*MetricFamily) { +} + +// only for PlayAction +func (a *SetFactAction) SetPlayAction(scripts map[string]*YAMLScript) error { + return nil +} + +// specific behavior for the DebugAction +func (a *SetFactAction) CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error { + var key_name string + var err error + var value_name any + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("[Type: SetFactAction] Name: %s", Name(a.Name, symtab, logger))) + for _, pair := range a.setFact { + // tmp_map := map[string]any{} + if pair == nil { + return fmt.Errorf("set_fact: invalid key value") + } + if key, ok := pair[0].(*Field); ok { + key_name, err = key.GetValueString(symtab, nil, false) + if err == nil { + if value_name, err = getValue(symtab, pair[1]); err != nil { + return err + } + // switch value := pair[1].(type) { + // case *Field: + // if value_name, err = value.GetValueString(symtab, nil, false); err != nil { + // return err + // } + // default: + // // need to call a func to obtain a value from any type but with all content valorized + // // => list: try to valorize each element + // // => map: try to valorize key and value + // value_name = value + // } + if value_name == nil { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf(" remove from symbols table: %s", key_name)) + delete(symtab, key_name) + } else { + if key_name != "_" { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf(" add to symbols table: %s = '%v'", key_name, value_name)) + symtab[key_name] = value_name + } else { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", " result discard (key >'_')") + + } + } + } + } + } + + return nil +} + +func (a *SetFactAction) AddCustomTemplate(customTemplate *exporterTemplate) error { + + if err := AddCustomTemplate(a, customTemplate); err != nil { + return err + } + + for _, pair := range a.setFact { + // tmp_map := map[string]any{} + if pair == nil { + return fmt.Errorf("set_fact: invalid key value") + } + if key, ok := pair[0].(*Field); ok { + if key != nil { + if err := key.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + if pair[1] != nil { + if err := AddCustomTemplateElement(pair[1], customTemplate); err != nil { + return fmt.Errorf("error in set_fact value: %s", err) + } + } + } + } + + return nil +} + +// *************************************************************************************** +// *************************************************************************************** + +func getMapValue(symtab map[string]any, raw_maps map[any]any) (map[any]any, error) { + var err error + final_res := make(map[any]any) + for raw_key, raw_value := range raw_maps { + key, err := getValue(symtab, raw_key) + if err != nil { + return nil, fmt.Errorf("invalid template for var key %s: %s", raw_key, err) + } + value, err := getValue(symtab, raw_value) + if err != nil { + return nil, fmt.Errorf("invalid template for var value %s: %s", raw_value, err) + } + final_res[key] = value + } + return final_res, err +} + +func getSliceValue(symtab map[string]any, raw_slice []any) (any, error) { + var err error + final_res := make([]any, len(raw_slice)) + for idx, r_value := range raw_slice { + res, err := getValue(symtab, r_value) + if err != nil { + return nil, fmt.Errorf("invalid template for var key %q: %s", res, err) + } + final_res[idx] = res + + } + return final_res, err +} + +func getValue(symtab map[string]any, raw_value any) (value_name any, err error) { + switch value := raw_value.(type) { + case *Field: + if value_name, err = value.GetValueString(symtab, nil, false); err != nil { + return nil, err + } + case []any: + if value_name, err = getSliceValue(symtab, value); err != nil { + return nil, err + } + case map[any]any: + if value_name, err = getMapValue(symtab, value); err != nil { + return nil, err + } + default: + // need to call a func to obtain a value from any type but with all content valorized + // => list: try to valorize each element + // => map: try to valorize key and value + value_name = value + } + return value_name, err +} + +// *************************************************************************************** diff --git a/target.go b/target.go new file mode 100644 index 0000000..7d8c94c --- /dev/null +++ b/target.go @@ -0,0 +1,524 @@ +package main + +import ( + "context" + "fmt" + "sort" + "sync" + "time" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "google.golang.org/protobuf/proto" +) + +const ( + // Capacity for the channel to collect metrics. + capMetricChan = 1000 + + // Capacity for the channel to collect control message from collectors. + capCollectChan = 100 + + upMetricName = "up" + upMetricHelp = "1 if the target is reachable, or 0 if the scrape failed" + scrapeDurationName = "scrape_duration_seconds" + scrapeDurationHelp = "How long it took to scrape the target in seconds" +) + +// Target collects SQL metrics from a single sql.DB instance. It aggregates one or more Collectors and it looks much +// like a prometheus.Collector, except its Collect() method takes a Context to run in. +type Target interface { + // Collect is the equivalent of prometheus.Collector.Collect(), but takes a context to run in. + Collect(ctx context.Context, ch chan<- Metric) + Name() string + SetSymbol(string, any) error + // YAML() ([]byte, error) +} + +// target implements Target. It wraps a httpAPI, which is initially nil but never changes once instantianted. +type target struct { + name string + client *Client + collectors []Collector + constLabels prometheus.Labels + globalConfig *GlobalConfig + httpAPIScript map[string]*YAMLScript + upDesc MetricDesc + scrapeDurationDesc MetricDesc + logContext []interface{} + + logger log.Logger + + // symbols table + // tsymtab map[string]any + + // mutex to hold condition for global client to try to login() + // wait_mutex sync.Mutex + // wake_cond sync.Cond + + // to protect the data during exchange + // content_mutex sync.Mutex + // msg int +} + +const ( + // one collector needs a ping action + MsgPing = iota + // one collector needs a login action + MsgLogin = iota + // start-restart collect for all collectors + MsgCollect = iota + // wait for all collectors to reply + MsgWait = iota + // collecting is over + MsgQuit = iota + // one collect is over + MsgDone = iota +) + +// NewTarget returns a new Target with the given instance name, data source name, collectors and constant labels. +// An empty target name means the exporter is running in single target mode: no synthetic metrics will be exported. +func NewTarget( + logContext []interface{}, + tpar *TargetConfig, + ccs []*CollectorConfig, + constLabels prometheus.Labels, + gc *GlobalConfig, + http_script map[string]*YAMLScript, + logger log.Logger) (Target, error) { + + if tpar.Name != "" { + logContext = append(logContext, "target", tpar.Name) + } + + constLabelPairs := make([]*dto.LabelPair, 0, len(constLabels)) + for n, v := range constLabels { + constLabelPairs = append(constLabelPairs, &dto.LabelPair{ + Name: proto.String(n), + Value: proto.String(v), + }) + } + sort.Sort(labelPairSorter(constLabelPairs)) + + collectors := make([]Collector, 0, len(ccs)) + for _, cc := range ccs { + cscrl := make([]*YAMLScript, len(cc.CollectScripts)) + i := 0 + for _, cs := range cc.CollectScripts { + cscrl[i] = cs + i++ + } + c, err := NewCollector(logContext, logger, cc, constLabelPairs, cscrl) + if err != nil { + return nil, err + } + collectors = append(collectors, c) + } + + upDesc := NewAutomaticMetricDesc(logContext, gc.MetricPrefix+"_"+upMetricName, upMetricHelp, prometheus.GaugeValue, constLabelPairs) + scrapeDurationDesc := + NewAutomaticMetricDesc(logContext, gc.MetricPrefix+"_"+scrapeDurationName, scrapeDurationHelp, prometheus.GaugeValue, constLabelPairs) + + t := target{ + name: tpar.Name, + client: newClient(tpar, http_script, logger, gc), + collectors: collectors, + constLabels: constLabels, + globalConfig: gc, + httpAPIScript: http_script, + upDesc: upDesc, + scrapeDurationDesc: scrapeDurationDesc, + logContext: logContext, + logger: logger, + // wait_mutex: sync.Mutex{}, + // content_mutex: sync.Mutex{}, + } + if t.client == nil { + return nil, fmt.Errorf("internal http client undefined") + } + // t.wake_cond = *sync.NewCond(&t.wait_mutex) + + // init the symbols tab + // t.symtab = make(map[string]any) + + return &t, nil +} + +// Name implement Target.Name +// to obtain target name from interface +func (t *target) Name() string { + return t.name +} + +// SetSymbol implement Target.SetSymbol +func (t *target) SetSymbol(key string, value any) error { + + t.client.symtab[key] = value + return nil +} + +// func (t *target) startCollect(wg *sync.WaitGroup, ctx context.Context, ch chan<- Metric) { +// wg.Add(len(t.collectors)) +// for _, c := range t.collectors { +// // have to build a new client copy to allow multi connection to target +// c_client := t.client.Clone() +// c.SetClient(c_client) + +// // If using a single target connection, collectors will likely run sequentially anyway. But we might have more. +// go func(collector Collector) { +// defer wg.Done() +// collector.Collect(ctx, ch) +// }(c) +// } +// } + +// Collect implements Target. +func (t *target) Collect(ctx context.Context, ch chan<- Metric) { + + // chan to receive order from collector if something wrong with authentication + collectChan := make(chan int, capCollectChan) + + var ( + scrapeStart = time.Now() + wg_coll sync.WaitGroup + targetUp bool + err error + ) + + // wait for all collectors are over + defer func() { + wg_coll.Wait() + }() + + // to store is we already have tried to login + has_logged := false + + // t.client.client.httpClient.Transport.TLSClientConfig.InsecureSkipVerify + // try to connect to target + collectChan <- MsgPing + + leave_loop := false + for msg := range collectChan { + // t.content_mutex.Lock() + // msg := t.msg + // t.content_mutex.Unlock() + switch msg { + + case MsgLogin: + level.Debug(t.logger).Log("msg", "target: received MsgLogin") + if msg == MsgLogin && !has_logged { + t.client.Clear() + if status, err := t.client.Login(); err != nil { + collectChan <- MsgQuit + } else { + if status { + has_logged = true + collectChan <- MsgPing + } else { + collectChan <- MsgQuit + } + } + } + case MsgPing: + level.Debug(t.logger).Log("msg", "target: received MsgPing") + // If using a single target connection, collectors will likely run sequentially anyway. But we might have more. + wg_coll.Add(1) + go func(t *target, ch chan<- Metric, coll_ch chan<- int) { + defer wg_coll.Done() + // collector.Collect(ctx, met_ch, &t.wake_cond) + targetUp, err = t.ping(ctx, ch, collectChan) + }(t, ch, collectChan) + level.Debug(t.logger).Log("msg", "target: ping send MsgWait") + collectChan <- MsgWait + + case MsgQuit: + level.Debug(t.logger).Log("msg", "target: ping received MsgQuit") + leave_loop = true + + case MsgWait: + level.Debug(t.logger).Log("msg", "start waiting for ping is over") + wg_coll.Wait() + level.Debug(t.logger).Log("msg", "after waiting for ping is over") + need_login := false + submsg := <-collectChan + + switch submsg { + case MsgLogin: + level.Debug(t.logger).Log("msg", "target ping wait: received MsgLogin") + need_login = true + case MsgDone: + level.Debug(t.logger).Log("msg", "target ping wait: received MsgDone") + default: + level.Debug(t.logger).Log("msg", fmt.Sprintf("target ping wait: received msg [%d]", submsg)) + } + if need_login { + collectChan <- MsgLogin + } else { + collectChan <- MsgQuit + } + } + // leave collectChan loop + if leave_loop { + break + } + } + + // targetUp, err := t.ping(ctx) + if err != nil { + ch <- NewInvalidMetric(t.logContext, err) + targetUp = false + } + if t.name != "" { + // Export the target's `up` metric as early as we know what it should be. + ch <- NewMetric(t.upDesc, boolToFloat64(targetUp), nil, nil) + } + + // Don't bother with the collectors if target is down. + if targetUp { + // var wg sync.WaitGroup + + // mutex to hold condition for global client to try to login() + // wait_mutex := sync.Mutex{} + // wake_cond := *sync.NewCond(&t.wait_mutex) + + // wg.Add(1) + // go func(t *target, ctx context.Context, met_ch chan<- Metric) { + + // ok := true + has_logged := false + // check if we have already logged to target ? + // if not then set msg to directly try to login + if logged, ok := GetMapValueBool(t.client.symtab, "logged"); ok && logged { + has_logged = logged + } + + if has_logged { + // if already logged only start to collect metrics + collectChan <- MsgCollect + } else { + // else not already logged so start to login in + collectChan <- MsgLogin + } + + for msg := range collectChan { + // t.content_mutex.Lock() + // msg := t.msg + // t.content_mutex.Unlock() + switch msg { + + case MsgLogin: + level.Debug(t.logger).Log("msg", "target: received MsgLogin") + // check if we have already logged to target ? + // if not then set msg to directly try to login + // if logged, ok := GetMapValueBool(t.client.symtab, "logged"); ok && logged { + // // t.content_mutex.Lock() + // // t.msg = MsgLogin + // // t.content_mutex.Unlock() + // has_logged = logged + // } + level.Debug(t.logger).Log("msg", fmt.Sprintf("target: MsgLogin check has_logged: %v", has_logged)) + if msg == MsgLogin && !has_logged { + // + // wait until all collectors have finished + wg_coll.Wait() + t.client.Clear() + t.client.symtab["__coll_channel"] = collectChan + + if status, err := t.client.Login(); err != nil { + collectChan <- MsgQuit + } else { + if status { + has_logged = true + collectChan <- MsgCollect + // t.msg = MsgEmpty + // msg = MsgEmpty + } else { + collectChan <- MsgQuit + + // t.msg = MsgQuit + // msg = MsgQuit + } + } + delete(t.client.symtab, "__coll_channel") + } + case MsgCollect: + level.Debug(t.logger).Log("msg", "target: received MsgCollect") + wg_coll.Add(len(t.collectors)) + level.Debug(t.logger).Log("msg", fmt.Sprintf("target: send %d collector(s)", len(t.collectors))) + for _, c := range t.collectors { + // have to build a new client copy to allow multi connection to target + c_client := t.client.Clone() + c.SetClient(c_client) + // c.SetClient(t.client) + + // If using a single target connection, collectors will likely run sequentially anyway. But we might have more. + go func(collector Collector) { + defer wg_coll.Done() + // collector.Collect(ctx, met_ch, &t.wake_cond) + collector.Collect(ctx, ch, collectChan) + }(c) + } + level.Debug(t.logger).Log("msg", "target: send MsgWait") + collectChan <- MsgWait + + case MsgQuit: + level.Debug(t.logger).Log("msg", "target: received MsgQuit") + close(collectChan) + + case MsgWait: + level.Debug(t.logger).Log("msg", "start waiting for all collectors are over") + + wg_coll.Wait() + level.Debug(t.logger).Log("msg", "after waiting for all collectors is over") + need_login := false + // we have received len(t.collectors) msgs from collectors + for i := 0; i < len(t.collectors); i++ { + level.Debug(t.logger).Log("msg", fmt.Sprintf("target wait: read msg from collector %d", i)) + submsg := <-collectChan + // for submsg := range collectChan { + + switch submsg { + case MsgLogin: + level.Debug(t.logger).Log("msg", "target wait: received MsgLogin") + need_login = true + default: + level.Debug(t.logger).Log("msg", fmt.Sprintf("target wait: received msg [%d] for collector %d", submsg, i)) + } + } + if need_login { + collectChan <- MsgLogin + t.client.symtab["logged"] = false + has_logged = false + if logged, ok := GetMapValueBool(t.client.symtab, "logged"); ok && logged { + level.Debug(t.logger).Log("msg", fmt.Sprintf("target: MsgLogin check has_logged: %v", logged)) + } + } else { + collectChan <- MsgQuit + } + } + // level.Debug(t.logger).Log("msg", "goroutine login() is waiting") + // t.wait_mutex.Lock() + // t.wake_cond.Wait() + } + level.Debug(t.logger).Log("msg", "goroutine target collector controler is over") + // }(t, ctx, ch) + + // wait_mutex.Lock() + // wake_cond.Wait() + // Wait for all collectors to complete, then close the channel. + // go func() { + // wg.Wait() + // close(collectChan) + // }() + + // Drain collectChan in case of premature return. + defer func() { + for range collectChan { + } + }() + } + + // Wait for all collectors (if any) to complete. + // wg.Wait() + level.Debug(t.logger).Log("msg", "collectors have stopped") + // tell the "waiting login" func to stop + // t.content_mutex.Lock() + // t.msg = MsgQuit + // t.content_mutex.Unlock() + // t.wake_cond.Signal() + + if t.name != "" { + // And export a `scrape duration` metric once we're done scraping. + ch <- NewMetric(t.scrapeDurationDesc, float64(time.Since(scrapeStart))*1e-9, nil, nil) + } +} + +// YAML marshals the config into YAML format. +// type tconfig struct { +// Name string `yaml:"name"` // target name to connect to from prometheus +// Scheme string `yaml:"scheme"` +// Host string `yaml:"host"` +// Port string `yaml:"port"` +// BaseUrl string `yaml:"baseUrl"` +// AuthConfig AuthConfig `yaml:"auth_mode,omitempty"` +// ProxyUrl string `yaml:"proxy"` +// VerifySSL ConvertibleBoolean `yaml:"verifySSL"` +// ConnectionTimeout model.Duration `yaml:"connection_timeout"` // connection timeout, per-target +// Labels map[string]string `yaml:"labels,omitempty"` // labels to apply to all metrics collected from the targets +// CollectorRefs []string `yaml:"collectors"` // names of collectors to execute on the target +// TargetsFiles []string `yaml:"targets_files,omitempty"` // slice of path and pattern for files that contains targets +// QueryRetry string `yaml:"query_retry,omitempty"` // target specific number of times to retry a query +// } + +// func (t *target) YAML() ([]byte, error) { +// tcfg := &tconfig{ +// Name: t.Name(), +// Scheme: t.Sc, +// Host: "", +// Port: "", +// BaseUrl: "", +// AuthConfig: AuthConfig{}, +// ProxyUrl: "", +// VerifySSL: false, +// ConnectionTimeout: t, +// Labels: map[string]string{}, +// CollectorRefs: []string{}, +// TargetsFiles: []string{}, +// QueryRetry: "", +// } +// return yaml.Marshal(tcfg) +// } + +// ping implement ping for target +func (t *target) ping(ctx context.Context, ch chan<- Metric, coll_ch chan<- int) (bool, error) { + t.client.symtab["__coll_channel"] = coll_ch + status, err := t.client.Ping() + delete(t.client.symtab, "__coll_channel") + return status, err +} + +// boolToFloat64 converts a boolean flag to a float64 value (0.0 or 1.0). +func boolToFloat64(value bool) float64 { + if value { + return 1.0 + } + return 0.0 +} + +// type targets []target + +// func (ts targets) YAML() ([]byte, error) { +// var full, content []byte +// var err error + +// for t := range ts { +// content, err = yaml.Marshal(t) +// if err != nil { +// content = nil +// return content, err +// } +// full = append(full, content...) +// } +// return full, err +// } + +// type Targets interface { +// YAML() ([]byte, error) +// } + +// func (ts *Targets) YAML() ([]byte, error) { +// var full, content []byte +// var err error + +// for t := range *ts { +// content, err = yaml.Marshal(t) +// if err != nil { +// content = nil +// return content, err +// } +// full = append(full, content...) +// } +// return full, err +// } diff --git a/template.go b/template.go new file mode 100644 index 0000000..0498cb0 --- /dev/null +++ b/template.go @@ -0,0 +1,250 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strconv" + + // "strconv" + "strings" + ttemplate "text/template" + + "github.com/Masterminds/sprig/v3" + "github.com/peekjef72/httpapi_exporter/encrypt" +) + +func convertToBytes(curval any, unit string) (int64, error) { + var i_value int64 + var err error + if curval == nil { + return 0, nil + } + + // it is a raw value not a template look in "item" + switch curval := curval.(type) { + case int: + i_value = int64(curval) + case int64: + i_value = curval + case float32: + i_value = int64(curval) + case float64: + i_value = int64(curval) + case string: + if i_value, err = strconv.ParseInt(strings.Trim(curval, "\r\n "), 10, 64); err != nil { + i_value = 0 + } + default: + i_value = 0 + // value := row[v].(float64) + } + switch unit { + case "kilobyte", "Kb": + i_value = i_value * 1024 + case "megabyte", "Mb": + i_value = i_value * 1024 * 1024 + case "gigabyte", "Gb": + i_value = i_value * 1024 * 1024 * 1024 + + } + return i_value, nil +} + +// allow to retrive string header from response's headers +func getHeader(headers http.Header, header string) (string, error) { + return headers.Get(header), nil +} + +// function for template: custom dict hasKey() key that allow to query key from dict of map[any]any type instead of map[string]any +func exporterHasKey(dict any, lookup_key string) (bool, error) { + res := false + + switch maptype := dict.(type) { + case map[string]any: + if _, ok := maptype[lookup_key]; ok { + res = true + } + case map[any]any: + if _, ok := maptype[lookup_key]; ok { + res = true + } + } + + return res, nil +} + +// function for template: custom dict get() key that allow to query key from dict of map[any]any type instead of map[string]any +func exporterGet(dict any, lookup_key string) (any, error) { + var val any + + switch maptype := dict.(type) { + case map[string]any: + if raw_val, ok := maptype[lookup_key]; ok { + val = raw_val + } + case map[any]any: + if raw_val, ok := maptype[lookup_key]; ok { + val = raw_val + } + default: + val = "" + } + return val, nil +} + +// function for template: custom dict set() key with value that allow to set key of dict map[any]any type instead of map[string]any +func exporterSet(dict any, lookup_key string, val any) (any, error) { + + switch maptype := dict.(type) { + case map[string]any: + maptype[lookup_key] = val + case map[any]any: + maptype[lookup_key] = val + } + + return dict, nil +} + +// function for template: custom dict keys() that allow to obtain keys slide from dict map[any]any type instead of map[string]any +func exporterKeys(dict any) ([]any, error) { + var res []any + + switch maptype := dict.(type) { + case map[string]any: + res = make([]any, len(maptype)) + i := 0 + for raw_key := range maptype { + res[i] = raw_key + i++ + } + case map[any]any: + res = make([]any, len(maptype)) + i := 0 + for raw_key := range maptype { + res[i] = raw_key + i++ + } + } + + return res, nil +} + +// function for template: custom dict values() that allow to obtain values slide from map[any]any type instead of map[string]any +func exporterValues(dict any) ([]any, error) { + var res []any + + switch maptype := dict.(type) { + case map[string]any: + res = make([]any, len(maptype)) + i := 0 + for _, raw_value := range maptype { + res[i] = raw_value + i++ + } + case map[any]any: + res = make([]any, len(maptype)) + i := 0 + for _, raw_value := range maptype { + res[i] = raw_value + i++ + } + } + + return res, nil +} + +// function for template: obtain json marshal representation of obj +func exporterToRawJson(in any) (string, error) { + var ( + err error + ) + buf := new(bytes.Buffer) + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + + switch raw_v := in.(type) { + case []any: + buf.WriteString("[") + llen := len(raw_v) + for i, raw_v2 := range raw_v { + err = enc.Encode(&raw_v2) + if i+1 < llen { + buf.WriteString(",") + } + } + buf.WriteString("]") + case map[any]any: + buf.WriteString("{") + mlen := len(raw_v) + i := 0 + for raw_k, raw_v2 := range raw_v { + str, err2 := exporterToRawJson(raw_k) + if err2 != nil { + return "", err2 + } + buf.WriteString(str) + buf.WriteString(":") + str, err2 = exporterToRawJson(raw_v2) + if err2 != nil { + return "", err2 + } + buf.WriteString(str) + i++ + if i < mlen { + buf.WriteString(",") + } + } + buf.WriteString("}") + // case string: + default: + err = enc.Encode(&raw_v) + } + + if err != nil { + return "", err + } + return strings.TrimSuffix(buf.String(), "\n"), nil + +} + +// function to decrypt password from shared key sent by caller +func exporterDecryptPass(passwd string, auth_key string) (string, error) { + if strings.Contains(passwd, "/encrypted/") { + ciphertext := passwd[len("/encrypted/"):] + cipher, err := encrypt.NewAESCipher(auth_key) + if err != nil { + err := fmt.Errorf("can't obtain cipher to decrypt: %s", err) + // level.Error(c.logger).Log("errmsg", err) + return passwd, err + } + passwd, err = cipher.Decrypt(ciphertext, true) + if err != nil { + err := fmt.Errorf("invalid key provided to decrypt: %s", err) + // level.Error(c.logger).Log("errmsg", err) + return passwd, err + } + } + + return passwd, nil +} +func mymap() ttemplate.FuncMap { + sprig_map := sprig.FuncMap() + // my_map := make(map[string]interface{}, len(sprig_map)+1) + // for k, v := range sprig_map { + // my_map[k] = v + // } + // my_map["convertToBytes"] = convertToBytes + sprig_map["convertToBytes"] = convertToBytes + sprig_map["getHeader"] = getHeader + sprig_map["exporterDecryptPass"] = exporterDecryptPass + sprig_map["exporterHasKey"] = exporterHasKey + sprig_map["exporterGet"] = exporterGet + sprig_map["exporterSet"] = exporterSet + sprig_map["exporterKeys"] = exporterKeys + sprig_map["exporterValues"] = exporterValues + sprig_map["exporterToRawJson"] = exporterToRawJson + + return sprig_map +} diff --git a/veeam_exporter b/veeam_exporter new file mode 120000 index 0000000..b13eec0 --- /dev/null +++ b/veeam_exporter @@ -0,0 +1 @@ +httpapi_exporter \ No newline at end of file diff --git a/yaml_script.go b/yaml_script.go new file mode 100644 index 0000000..384401f --- /dev/null +++ b/yaml_script.go @@ -0,0 +1,993 @@ +package main + +import ( + //"bytes" + "fmt" + "reflect" + "strings" + "text/template" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "gopkg.in/yaml.v3" +) + +type YAMLScript struct { + // name string `yaml:"name"` + Actions ActionsList `yaml:"actions"` + UntilLimit int `yaml:"until_limit"` + + name string + customTemplate *template.Template + metricsActions []*MetricsAction +} + +type DumpYAMLScript struct { + Actions ActionsList `yaml:"actions"` + UntilLimit int `yaml:"until_limit"` +} + +//****************************************************************** +//** +//****************************************************************** + +const ( + base_action = iota + set_fact_action = iota + actions_action = iota + debug_action = iota + query_action = iota + metrics_action = iota + metric_action = iota + play_script_action = iota +) + +type GetMetricsRes struct { + mc []*MetricConfig + maprefix string +} + +type Action interface { + BaseAction + // GetName(symtab map[string]any, logger log.Logger) string + Type() int + // GetBaseAction() *BaseAction + setBasicElement(nameField *Field, vars map[string]any, with []any, loopVar string, when []*exporterTemplate, until []*exporterTemplate) error + PlayAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error + CustomAction(script *YAMLScript, symtab map[string]any, logger log.Logger) error + // to dump config + // String() string + + // only for MetricsAction + GetMetrics() []*GetMetricsRes + // only for MetricAction + GetMetric() *MetricConfig + SetMetricFamily(*MetricFamily) + // only for PlayAction + SetPlayAction(scripts map[string]*YAMLScript) error + AddCustomTemplate(customTemplate *exporterTemplate) error +} + +type ActionsList []Action + +func (sc *YAMLScript) Play(symtab map[string]any, ignore_errors bool, logger log.Logger) error { + symtab["__name__"] = sc.name + for _, ac := range sc.Actions { + err := ac.PlayAction(sc, symtab, logger) + if !ignore_errors && err != nil { + return err + } + } + return nil +} + +func (sc *YAMLScript) AddCustomTemplate(customTemplate *exporterTemplate) error { + for _, ac := range sc.Actions { + err := ac.AddCustomTemplate(customTemplate) + if err != nil { + return err + } + } + return nil +} + +func ScriptName(symtab map[string]any, logger log.Logger) string { + raw_name, ok := symtab["__name__"] + if !ok { + level.Warn(logger).Log("msg", "invalid script name") + return "" + } + str, ok := raw_name.(string) + if !ok { + level.Warn(logger).Log("msg", "invalid script name type") + return "" + + } + return str +} + +func Name(name *Field, symtab map[string]any, logger log.Logger) string { + str, err := name.GetValueString(symtab, nil, false) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("invalid action name: %v", err)) + return "" + } + return str +} + +type BaseAction interface { + GetName(symtab map[string]any, logger log.Logger) string + + GetNameField() *Field + SetNameField(*Field) + GetWidh() []any + SetWidth([]any) + GetWhen() []*exporterTemplate + SetWhen([]*exporterTemplate) + GetLoopVar() string + SetLoopVar(string) + GetVars() map[string]any + SetVars(map[string]any) + GetUntil() []*exporterTemplate + SetUntil([]*exporterTemplate) +} + +func setBasicElement( + ba BaseAction, + nameField *Field, + vars map[string]any, + with []any, + loopVar string, + when []*exporterTemplate, + until []*exporterTemplate) error { + + if nameField != nil { + ba.SetNameField(nameField) + } + if len(vars) > 0 { + ba.SetVars(vars) + } + if len(with) > 0 { + // have to check every element in the slide + // build a Field for each. + baWith := make([]any, len(with)) + for idx, elmt := range with { + switch curval := elmt.(type) { + case string: + tt := strings.LastIndex(curval, "}}") + if tt != -1 { + curval = curval[:tt] + " | toRawJson " + curval[tt:] + } + field, err := NewField(curval, nil) + if err != nil { + return err + } + baWith[idx] = field + case map[any]any: + baWith[idx] = elmt + case int: + tmp := fmt.Sprintf("%d", curval) + field, err := NewField(tmp, nil) + if err != nil { + return err + } + baWith[idx] = field + + case *Field: + baWith[idx] = curval + + default: + return fmt.Errorf("with_items: invalid type: '%s'", reflect.TypeOf(elmt)) + } + } + ba.SetWidth(baWith) + if loopVar != "" { + ba.SetLoopVar(loopVar) + } + // ba.With = with + } + if len(when) > 0 { + ba.SetWhen(when) + } + if len(until) > 0 { + ba.SetUntil(until) + } + return nil +} + +func AddCustomTemplateElement(item any, customTemplate *exporterTemplate) error { + switch curval := item.(type) { + case *Field: + if curval != nil { + if err := curval.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + case map[any]any: + if curval == nil { + return nil + } + for r_key, r_value := range curval { + if err := AddCustomTemplateElement(r_key, customTemplate); err != nil { + return err + } + if err := AddCustomTemplateElement(r_value, customTemplate); err != nil { + return err + } + } + case []any: + if curval == nil { + return nil + } + for _, r_value := range curval { + if err := AddCustomTemplateElement(r_value, customTemplate); err != nil { + return err + } + } + default: + return fmt.Errorf("invalid type for with_items: %v", item) + } + return nil +} + +func AddCustomTemplate(ba BaseAction, customTemplate *exporterTemplate) error { + name := ba.GetNameField() + if name != nil { + if err := name.AddDefaultTemplate(customTemplate); err != nil { + return err + } + } + baWith := ba.GetWidh() + for idx, with := range baWith { + if err := AddCustomTemplateElement(with, customTemplate); err != nil { + return fmt.Errorf("error in with[%d]: %s", idx, err) + } + } + baUntil := ba.GetUntil() + for idx, until_tmpl := range baUntil { + if tmpl, err := AddDefaultTemplate(until_tmpl, customTemplate); err != nil { + return fmt.Errorf("error in until[%d]: %s", idx, err) + } else { + baUntil[idx] = tmpl + } + } + + baWhen := ba.GetWhen() + for idx, when_tmpl := range baWhen { + if tmpl, err := AddDefaultTemplate(when_tmpl, customTemplate); err != nil { + return fmt.Errorf("error in when[%d]: %s", idx, err) + } else { + baWhen[idx] = tmpl + } + } + + return nil +} + +// ******************************************************************************** +func ValorizeValue(symtab map[string]any, item any, logger log.Logger, cur_level int) (any, error) { + var data any + var err error + // fmt.Println("item = ", reflect.TypeOf(item)) + switch curval := item.(type) { + case *Field: + check_value := false + if cur_level == 0 { + // this is a template populate list with var from symtab + data, err = curval.GetValueObject(symtab) + if err != nil { + return data, err + } + if r_data, ok := data.([]any); ok { + if r_data == nil { + check_value = true + } + } + // if r_data, ok := data.([]any); ok { + // if len(r_data) == 0 { + // check_value = true + // } + // } + } else { + check_value = true + } + if check_value { + data, err = curval.GetValueString(symtab, nil, false) + } + return data, err + case map[any]any: + ldata := make(map[string]any, len(curval)) + + for r_key, r_value := range curval { + key, err := ValorizeValue(symtab, r_key, logger, cur_level+1) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("error building map key: %v", r_key), "errmsg", err) + continue + } + key_val := "" + if r_key_val, ok := key.(string); ok { + key_val = r_key_val + } else { + continue + } + value, err := ValorizeValue(symtab, r_value, logger, cur_level+1) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("error building map value for key '%s'", key_val), "errmsg", err) + continue + } + ldata[key_val] = value + } + if len(ldata) > 0 { + data = ldata + } + case []any: + ldata := make([]any, len(curval)) + for idx, r_value := range curval { + values, err := ValorizeValue(symtab, r_value, logger, cur_level+1) + if err != nil { + level.Warn(logger).Log("msg", fmt.Sprintf("error building list value for index: %d", idx), "errmsg", err) + continue + } + ldata[idx] = values + } + if len(ldata) > 0 { + data = ldata + } + default: + level.Warn(logger).Log("msg", fmt.Sprintf("invalid type for with_items: %s", item)) + } + return data, nil +} + +func preserve_sym_tab(symtab map[string]any, old_values map[string]any, key string, val any) { + if old_val, ok := symtab[key]; ok { + old_values[key] = old_val + } else { + old_values[key] = "_" + } + symtab[key] = val +} + +func PlayBaseAction(script *YAMLScript, symtab map[string]any, logger log.Logger, ba Action, customAction func(*YAMLScript, map[string]any, log.Logger) error) error { + + // to preverse values from symtab + old_values := make(map[string]any) + + defer func() { + for key, val := range old_values { + if val == "_" { + delete(symtab, key) + } else { + symtab[key] = val + } + } + }() + + // add the local vars from the action to symbols table + baVars := ba.GetVars() + if len(baVars) > 0 { + for key, val := range baVars { + preserve_sym_tab(symtab, old_values, key, val) + } + } + var items []any + var loop_var string + do_loop := false + set_loops_var := false + + final_items := make([]any, 0) + baWith := ba.GetWidh() + if len(baWith) > 0 { + // build a list of element from ba.With list of Field + items = baWith + for _, item := range items { + + data, err := ValorizeValue(symtab, item, logger, 0) + if err == nil { + if l_data, ok := data.([]any); ok { + final_items = append(final_items, l_data...) + } else { + final_items = append(final_items, data) + } + } else { + level.Warn(logger).Log("msg", fmt.Sprintf("no data found for with_items: %s", err)) + } + } + items = final_items + baLoopVar := ba.GetLoopVar() + if baLoopVar != "" { + loop_var = baLoopVar + } else { + loop_var = "item" + } + set_loops_var = true + } else if len(ba.GetUntil()) > 0 { + do_loop = true + } else { + items = make([]any, 1) + items[0] = 0 + } + + if !do_loop { + // loop on items + for idx, item := range items { + if set_loops_var { + preserve_sym_tab(symtab, old_values, loop_var, item) + preserve_sym_tab(symtab, old_values, "loop_var_idx", idx) + preserve_sym_tab(symtab, old_values, "loop_var", loop_var) + } + // check if there are condition on the "item" loop; + // if one is false break item the loop on next. + baWhen := ba.GetWhen() + if len(baWhen) > 0 { + + valid_value := true + + for i, condTpl := range baWhen { + tmp_res := new(strings.Builder) + // ptr := (*template.Template)(condTpl) + err := ((*template.Template)(condTpl)).Execute(tmp_res, &symtab) + if err != nil { + return fmt.Errorf("invalid template value for 'when' %s: %s", baWhen[i].Tree.Root.String(), err) + } + if tmp_res.String() != "true" { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("Name: '%s' skipped : when condition false: '%s'", ba.GetName(symtab, logger), baWhen[i].Tree.Root.String())) + + valid_value = false + break + } + } + if !valid_value { + continue + } + } + err := customAction(script, symtab, logger) + if err != nil { + return err + } + } + } else { + idx := 0 + for { + if set_loops_var { + preserve_sym_tab(symtab, old_values, "loop_var_idx", idx) + // symtab["loop_var_idx"] = idx + } + valid_value := true + + baUntil := ba.GetUntil() + for i, condTpl := range baUntil { + tmp_res := new(strings.Builder) + err := ((*template.Template)(condTpl)).Execute(tmp_res, &symtab) + if err != nil { + err := fmt.Errorf("invalid template value for 'until' %s: %s", baUntil[i].Tree.Root.String(), err) + level.Warn(logger).Log("msg", err) + return err + } + if tmp_res.String() != "true" { + level.Debug(logger).Log( + "script", ScriptName(symtab, logger), + "msg", fmt.Sprintf("Name: '%s' until limit cond reached : '%s'", ba.GetName(symtab, logger), baUntil[i].Tree.Root.String())) + + valid_value = false + break + } + } + if !valid_value || idx >= script.UntilLimit { + if idx >= script.UntilLimit { + level.Warn(logger).Log("msg", fmt.Sprintf("max iteration reached for until action (%d)", script.UntilLimit)) + } + break + } + idx += 1 + + err := customAction(script, symtab, logger) + if err != nil { + return err + } + } + } + return nil +} + +// *************************************************************************************** +// *************************************************************************************** +// yaml script parser +// *************************************************************************************** +// *************************************************************************************** + +// parse all possible actions in a yaml script. +// +// * debug: msg: test{{ go template}} +// +// * set_fact: vars to set in symbols table +// +// * query: play url to the target and set resulting json obj the sybols table +// +// * metrics: define a list of metric (metric_name) to generate for the exporter +// +// * actions: define a list of subaction to play: query, metrics... +// +// * play_script: play the script +// +// * metric_name: a metric definition +type tmpActions []map[string]yaml.Node + +func build_WithItems(raw yaml.Node) ([]any, error) { + var listElmt []any + if raw.Tag == "!!str" { + with_field, err := NewField(raw.Value, nil) + if err != nil { + return nil, fmt.Errorf("invalid template for var name (set_fact) %q: %s", raw.Value, err) + } + listElmt = make([]any, 1) + listElmt[0] = with_field + } else if raw.Tag == "!!map" { + var ( + raw_mapElmt map[string]any + mapElmt map[any]any + err error + ) + + if err = raw.Decode(&raw_mapElmt); err != nil { + return nil, err + } + if mapElmt, err = buildMapField(raw_mapElmt); err != nil { + return nil, err + } + listElmt = make([]any, 1) + listElmt[0] = mapElmt + } else if raw.Tag == "!!seq" { + var ( + str_listElmt []any + err error + ) + + if err = raw.Decode(&str_listElmt); err != nil { + return nil, err + } + if listElmt, err = buildSliceField(str_listElmt); err != nil { + return nil, err + } + } else { + listElmt = make([]any, 0) + } + return listElmt, nil +} + +func build_Cond(script *YAMLScript, raw yaml.Node) ([]*exporterTemplate, error) { + var listElmt []string + var when []*exporterTemplate + + if raw.Tag == "!!str" { + listElmt = make([]string, 1) + listElmt[0] = raw.Value + } else if raw.Tag == "!!seq" { + if err := raw.Decode(&listElmt); err != nil { + return nil, err + } + } else { + listElmt = make([]string, 0) + } + if len(listElmt) > 0 { + when = make([]*exporterTemplate, len(listElmt)) + for i, cond := range listElmt { + var tmpl *template.Template + var err error + + if !strings.Contains(cond, "{{") { + cond = "{{ " + cond + " }}" + } + // alter the when conditions to add custom templates/funcs if exist + if script.customTemplate != nil { + tmpl, err = script.customTemplate.Clone() + if err != nil { + return nil, fmt.Errorf("for script %s template clone error: %s", script.name, err) + } + } else { + tmpl = template.New("cond").Funcs(mymap()) + } + tmpl, err = tmpl.Parse(cond) + if err != nil { + return nil, fmt.Errorf("for script %s template %s is invalid: %s", script.name, cond, err) + } + when[i] = (*exporterTemplate)(tmpl) + } + } + return when, nil +} + +func buildMapField(raw_maps map[string]any) (map[any]any, error) { + var err error + final_res := make(map[any]any) + for key, r_value := range raw_maps { + res, err := buildFields(key, r_value) + if err != nil { + return nil, fmt.Errorf("invalid template for var key %s: %s", key, err) + } + for key, val := range res { + final_res[key] = val + } + } + return final_res, err +} + +func buildSliceField(raw_slice []any) ([]any, error) { + var err error + final_res := make([]any, len(raw_slice)) + for idx, r_value := range raw_slice { + res, err := buildValueField(r_value) + if err != nil { + return nil, fmt.Errorf("invalid template for var key %q: %s", res, err) + } + final_res[idx] = res + + } + return final_res, err +} + +func buildFields(key string, val any) (map[any]any, error) { + var err error + var key_field *Field + var value_field any + // Check required fields + res := make(map[any]any) + + key_field, err = NewField(key, nil) + if err != nil { + return nil, fmt.Errorf("invalid template for var name (set_fact) %q: %s", key, err) + } + value_field, err = buildValueField(val) + if err != nil { + return nil, fmt.Errorf("invalid template for var name (set_fact) %q: %s", key, err) + } + res[key_field] = value_field + + return res, nil +} + +func buildValueField(val any) (any, error) { + + switch curval := val.(type) { + case string: + value_field, err := NewField(curval, nil) + if err != nil { + return nil, fmt.Errorf("invalid template for var value (set_fact) %q: %s", curval, err) + } + return value_field, nil + + // value is a map + case map[string]any: + tmp, err := buildMapField(curval) + if err != nil { + return nil, fmt.Errorf("invalid template for map value (set_fact) %q: %s", curval, err) + } + return tmp, nil + // value is a slice + case []any: + tmp, err := buildSliceField(curval) + if err != nil { + return nil, fmt.Errorf("invalid template for map value (set_fact) %q: %s", curval, err) + } + return tmp, nil + + // a value int float... what else ? + default: + return curval, nil + } +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface for YAMLScript. +func (script *YAMLScript) UnmarshalYAML(value *yaml.Node) error { + var tmp tmpActions + if err := value.Decode(&tmp); err != nil { + return err + } + actions, err := ActionsListDecode(script, make(ActionsList, 0, len(tmp)), tmp, value) + if err != nil { + return err + } + script.Actions = actions + script.UntilLimit = 20 + return nil +} + +func ActionsListDecode(script *YAMLScript, actions ActionsList, tmp tmpActions, parentNode *yaml.Node) (ActionsList, error) { + main_checker := map[string]bool{ + "name": true, + "loop": true, + "loop_var": true, + "vars": true, + "until": true, + "when": true, + "with_items": true, + } + + for i := range tmp { + var name *Field + var nameVal, loopVar string + var with_items []any + var until, when []*exporterTemplate + var vars map[string]any + var err error + checker := main_checker + skip_checker := false + cur_act := tmp[i] + + // parse name + if raw, ok := cur_act["name"]; ok { + nameVal = raw.Value + name, err = NewField(nameVal, nil) + if err != nil { + return nil, fmt.Errorf("name for action invalid %q: %s", raw.Value, err) + } + } + // parse vars + if raw, ok := cur_act["vars"]; ok { + if raw.Tag == "!!map" { + if err := raw.Decode(&vars); err != nil { + return nil, err + } + } + } + + // parse with_items + if raw, ok := cur_act["with_items"]; ok { + listElmt, err := build_WithItems(raw) + if err != nil { + return nil, err + } + with_items = listElmt + } else if raw, ok := cur_act["loop"]; ok { + listElmt, err := build_WithItems(raw) + if err != nil { + return nil, err + } + with_items = listElmt + } + if with_items != nil { + // parse loop_var + if raw, ok := cur_act["loop_var"]; ok { + loopVar = raw.Value + } + } + + // parse when + if raw, ok := cur_act["when"]; ok { + cond, err := build_Cond(script, raw) + if err != nil { + return nil, err + } + when = cond + } + + // parse when + if raw, ok := cur_act["until"]; ok { + cond, err := build_Cond(script, raw) + if err != nil { + return nil, err + } + until = cond + } + + // *********************************************** + // *********************************************** + // ** parse the action keyword + // *********************************************** + // *********************************************** + + // *********************************************** + // debug + if raw, ok := cur_act["debug"]; ok { + checker["debug"] = true + da := &DebugActionConfig{} + if err := raw.Decode(da); err != nil { + err = fmt.Errorf("%v: for action '%s'", err, name.String()) + return nil, err + } + a := &DebugAction{} + a.Debug = da + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + } else if raw, ok := cur_act["set_fact"]; ok { + // *********************************************** + // set_fact + checker["set_fact"] = true + sfa := make(map[string]interface{}) + if err := raw.Decode(&sfa); err != nil { + err = fmt.Errorf("%v: for action '%s'", err, name.String()) + return nil, err + } + a := &SetFactAction{} + if len(sfa) > 0 { + a.SetFact = sfa + a.setFact = make([][]any, len(a.SetFact)) + + idx := 0 + for key, val := range a.SetFact { + // Check required fields + a.setFact[idx] = make([]any, 2) + if new_map, err := buildFields(key, val); err != nil { + return nil, err + } else { + for key, val := range new_map { + a.setFact[idx][0] = key + a.setFact[idx][1] = val + } + } + idx++ + } + } + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + } else if raw, ok := cur_act["query"]; ok { + // *********************************************** + // url/query + checker["query"] = true + qa := &QueryActionConfig{} + if err := raw.Decode(qa); err != nil { + err = fmt.Errorf("%v: for action '%s'", err, name.String()) + return nil, err + } + a := &QueryAction{} + a.Query = qa + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + } else if raw, ok := cur_act["play_script"]; ok { + // *********************************************** + // play_script + checker["play_script"] = true + script_name := "" + if err := raw.Decode(&script_name); err != nil { + err = fmt.Errorf("%v: for action '%s'", err, name.String()) + return nil, err + } + a := &PlayScriptAction{ + PlayScriptActionName: script_name, + } + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + } else if _, ok := cur_act["metric_name"]; ok { + // *********************************************** + // play_script + checker["metric_name"] = true + mc := &MetricConfig{} + if err := parentNode.Content[i].Decode(mc); err != nil { + return nil, err + } + // MAYBE mc.Name should be a Field so that the name could be a template !! + a := &MetricAction{ + mc: mc, + } + name, err = NewField(mc.Name, nil) + if err != nil { + return nil, fmt.Errorf("name for action invalid %q: %s", mc.Name, err) + } + + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + skip_checker = true + } else if raw, ok := cur_act["actions"]; ok { + // *********************************************** + // actions + checker["actions"] = true + if raw.Tag == "!!seq" { + var tmp_sub tmpActions + if err := raw.Decode(&tmp_sub); err != nil { + err = fmt.Errorf("%v: for action '%s'", err, name.String()) + return nil, err + } + + acta, err := ActionsListDecode(script, make(ActionsList, 0, len(tmp_sub)), tmp_sub, &raw) + if err != nil { + return nil, err + } + a := &ActionsAction{} + a.Actions = acta + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + } + } else if raw, ok := cur_act["metrics"]; ok { + // *********************************************** + // metric name + checker["metrics"] = true + checker["scope"] = true + checker["metric_prefix"] = true + + if raw.Tag == "!!seq" { + var tmp_sub tmpActions + if err := raw.Decode(&tmp_sub); err != nil { + err = fmt.Errorf("%v: for action '%s'", err, name.String()) + return nil, err + } + + acta, err := ActionsListDecode(script, make(ActionsList, 0, len(tmp_sub)), tmp_sub, &raw) + if err != nil { + return nil, err + } + a := &MetricsAction{} + a.Actions = acta + if err = a.setBasicElement(name, vars, with_items, loopVar, when, until); err != nil { + return nil, err + } + actions = append(actions, a) + + //*** append current metrics list to the global list + script.metricsActions = append(script.metricsActions, a) + + mcl := make([]*MetricConfig, len(raw.Content)) + idx := 0 + for _, act := range acta { + if act.Type() == metric_action { + mcl[idx] = act.GetMetric() + idx++ + } + } + a.Metrics = mcl + + if raw, ok := cur_act["scope"]; ok { + if raw.Tag == "!!str" { + scope := "" + if err := raw.Decode(&scope); err != nil { + return nil, err + } + a.Scope = scope + } + // # propagate scope == "none" to all metrics + if a.Scope == "none" { + for _, mcl := range a.Metrics { + if mcl.Scope == "" { + mcl.Scope = a.Scope + } + } + } + } + + if raw, ok := cur_act["metric_prefix"]; ok { + if raw.Tag == "!!str" { + metric_prefix := "" + if err := raw.Decode(&metric_prefix); err != nil { + return nil, err + } + a.MetricPrefix = metric_prefix + } + } + } + } else { + // we haven't found any label in action that we should understand + // display first key of the map and context (line, column) + for key, val := range cur_act { + err := fmt.Errorf("unknown action type: '%s': '%v', arround line %d column: %d", key, val.Value, val.Line, val.Column) + return nil, err + } + // *********************************************** + // return nil, fmt.Errorf("unknown action type: +%v", cur_act) + } + + if !skip_checker { + for name, raw := range cur_act { + if _, ok := checker[name]; !ok { + return nil, fmt.Errorf("unknown attribute '%s' for action '%v' on line: %d column: %d", name, reflect.TypeOf(actions[len(actions)-1]), raw.Line, raw.Column) + + } + } + } + } + return actions, nil +} + +// ***************************************************************************************