From 31aaa9514b6de527457cd66881a8cbfb39977083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20silva?= Date: Sun, 24 Mar 2024 18:23:17 +0000 Subject: [PATCH] Accept target and sub_target in module config If specified in the config, the respective value is ignored in the HTTP request parameters. --- README.md | 12 +++++++++-- config/config.go | 2 ++ modbus.yml | 9 ++++++++ modbus_exporter.go | 51 +++++++++++++++++++++++++++++----------------- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index bc4d131..bdf8497 100644 --- a/README.md +++ b/README.md @@ -78,10 +78,18 @@ Flags: --[no-]version Show application version. ``` -Visit http://localhost:9602/modbus?target=1.2.3.4:502&module=fake&sub_target=1 where 1.2.3.4:502 is the IP and port number of the modbus IP device to get metrics from, -while module and sub_target parameters specify which module and subtarget to use from the config file. +Visit http://localhost:9602/modbus?module=fake&target=1.2.3.4:502&sub_target=1 where: + +- the `module` parameter specifies which module to use from the config file; +- the `target` parameter specifies the IP and port number of the modbus IP device to get metrics from, e.g. `1.2.3.4:502`; +- the `sub_target` specifies the slave address/id to use in the modbus request; + If your device doesn't use sub-targets you can usually just set it to 1. +Note `target` and `sub_target` may be provided per-module in the config, +in which case the respective value in the request is ignored. +This is useful e.g. if you only have one device per module and don't want to accept the IP address in the request. + Visit http://localhost:9602/metrics to get the metrics of the exporter itself. ## Configuration File diff --git a/config/config.go b/config/config.go index ae1be44..032e172 100644 --- a/config/config.go +++ b/config/config.go @@ -62,6 +62,8 @@ type ListTargets map[string]*Module type Module struct { Name string `yaml:"name"` Protocol ModbusProtocol `yaml:"protocol"` + Target *string `yaml:"target"` + SubTarget *uint8 `yaml:"subTarget"` Timeout int `yaml:"timeout"` Baudrate int `yaml:"baudrate"` Databits int `yaml:"databits"` diff --git a/modbus.yml b/modbus.yml index b9b33f1..722f0a7 100644 --- a/modbus.yml +++ b/modbus.yml @@ -3,6 +3,15 @@ modules: # Module name, needs to be passed as parameter by Prometheus. - name: "fake" protocol: 'tcp/ip' + + # IP and port number of the modbus IP device to get metrics from. + # Optinal. If specified, this value is used and the target HTTP request parameter is ignored. + # target: "1.2.3.4:502" + + # Modbus slave address/id to use + # Optional. If specified, this value is used and the sub_target HTTP request parameter is ignored. + # subTarget: 0 + # Certain modbus devices need special timing workarounds workarounds: # Sleep a certain time after the TCP connection is established diff --git a/modbus_exporter.go b/modbus_exporter.go index e24d733..ce25d39 100644 --- a/modbus_exporter.go +++ b/modbus_exporter.go @@ -88,32 +88,45 @@ func scrapeHandler(e *modbus.Exporter, w http.ResponseWriter, r *http.Request, l http.Error(w, "'module' parameter must be specified", http.StatusBadRequest) return } - - if !e.GetConfig().HasModule(moduleName) { + module := e.GetConfig().GetModule(moduleName) + if module == nil { http.Error(w, fmt.Sprintf("module '%v' not defined in configuration file", moduleName), http.StatusBadRequest) return } - target := r.URL.Query().Get("target") - if target == "" { - http.Error(w, "'target' parameter must be specified", http.StatusBadRequest) - return + var target string + if module.Target == nil { + target = r.URL.Query().Get("target") + if target == "" { + http.Error(w, "'target' parameter must be specified", http.StatusBadRequest) + return + } + } else { + target = *module.Target } - sT := r.URL.Query().Get("sub_target") - if sT == "" { - http.Error(w, "'sub_target' parameter must be specified", http.StatusBadRequest) - return - } + var subTarget uint8 + if module.SubTarget == nil { + sT := r.URL.Query().Get("sub_target") + if sT == "" { + http.Error(w, "'sub_target' parameter must be specified", http.StatusBadRequest) + return + } - subTarget, err := strconv.ParseUint(sT, 10, 32) - if err != nil { - http.Error(w, fmt.Sprintf("'sub_target' parameter must be a valid integer: %v", err), http.StatusBadRequest) - return - } - if subTarget > 255 { - http.Error(w, fmt.Sprintf("'sub_target' parameter must be from 0 to 255. Invalid value: %d", subTarget), http.StatusBadRequest) - return + parsedSubTarget, err := strconv.ParseUint(sT, 10, 32) + if err != nil { + http.Error(w, fmt.Sprintf("'sub_target' parameter must be a valid integer: %v", err), http.StatusBadRequest) + return + } + + if parsedSubTarget > 255 { + http.Error(w, fmt.Sprintf("'sub_target' parameter must be from 0 to 255. Invalid value: %d", parsedSubTarget), http.StatusBadRequest) + return + } + + subTarget = byte(parsedSubTarget) + } else { + subTarget = *module.SubTarget } level.Info(logger).Log("msg", "got scrape request", "module", moduleName, "target", target, "sub_target", subTarget)