Browse Source

lua metric collection now working

pull/4/head
sberk42 4 years ago
parent
commit
bf7d020b74
  1. 4
      README.md
  2. 32
      fritzbox_lua/lua_client.go
  3. 228
      main.go
  4. 8
      metrics-lua.json

4
README.md

@ -12,7 +12,7 @@ The goal of the fork is:
- [x] move config of metrics to be exported to config file rather then code - [x] move config of metrics to be exported to config file rather then code
- [x] add config for additional metrics to collect (especially from TR-064 API) - [x] add config for additional metrics to collect (especially from TR-064 API)
- [x] create a grafana dashboard consuming the additional metrics - [x] create a grafana dashboard consuming the additional metrics
- [ ] collect metrics from lua APIs not available in UPNP APIs - [x] collect metrics from lua APIs not available in UPNP APIs
Other changes: Other changes:
- replaced digest authentication code with own implementation - replaced digest authentication code with own implementation
@ -49,6 +49,8 @@ Usage:
The password for the FRITZ!Box UPnP service The password for the FRITZ!Box UPnP service
-test -test
print all available SOAP calls and their results (if call possible) to stdout print all available SOAP calls and their results (if call possible) to stdout
-testLua
read luaTest.json file make all contained calls and dump results
-collect -collect
collect metrics once print to stdout and exit collect metrics once print to stdout and exit
-json-out string -json-out string

32
fritzbox_lua/lua_client.go

@ -67,7 +67,7 @@ type LuaMetricValueDefinition struct {
// LuaMetricValue single value retrieved from lua page // LuaMetricValue single value retrieved from lua page
type LuaMetricValue struct { type LuaMetricValue struct {
Name string Name string
Value string Value float64
Labels map[string]string Labels map[string]string
} }
@ -103,15 +103,7 @@ func (lua *LuaSession) doLogin(response string) error {
return nil return nil
} }
func (lmvDef *LuaMetricValueDefinition) createValue(name string, value string) LuaMetricValue { func (lmvDef *LuaMetricValueDefinition) createValue(name string, value float64) LuaMetricValue {
if lmvDef.OkValue != "" {
if value == lmvDef.OkValue {
value = "1"
} else {
value = "0"
}
}
lmv := LuaMetricValue{ lmv := LuaMetricValue{
Name: name, Name: name,
Value: value, Value: value,
@ -208,7 +200,7 @@ func (lua *LuaSession) LoadData(page LuaPage) ([]byte, error) {
} else if resp.StatusCode == http.StatusForbidden && !callDone { } else if resp.StatusCode == http.StatusForbidden && !callDone {
// we assume SID is expired, so retry login // we assume SID is expired, so retry login
} else { } else {
return nil, fmt.Errorf("data.lua failed: %s", resp.Status) return nil, fmt.Errorf("%s failed: %s", page.Path, resp.Status)
} }
} }
@ -314,8 +306,24 @@ VALUE:
path += key path += key
} }
var sVal = toString(valUntyped)
var floatVal float64
if metricDef.OkValue != "" {
if metricDef.OkValue == sVal {
floatVal = 1
} else {
floatVal = 0
}
} else {
// convert value to float
floatVal, err = strconv.ParseFloat(sVal, 64)
if err != nil {
continue VALUE
}
}
// create metric value // create metric value
lmv := metricDef.createValue(path, toString(valUntyped)) lmv := metricDef.createValue(path, floatVal)
// add labels if pathVal is a hash // add labels if pathVal is a hash
valMap, isType := pathVal.(map[string]interface{}) valMap, isType := pathVal.(map[string]interface{})

228
main.go

@ -42,7 +42,7 @@ const serviceLoadRetryTime = 1 * time.Minute
var ( var (
flag_test = flag.Bool("test", false, "print all available metrics to stdout") flag_test = flag.Bool("test", false, "print all available metrics to stdout")
flag_luatest = flag.Bool("testLua", false, "read luaTest.json file make all contained calls ans print results") flag_luatest = flag.Bool("testLua", false, "read luaTest.json file make all contained calls and dump results")
flag_collect = flag.Bool("collect", false, "print configured metrics to stdout and exit") flag_collect = flag.Bool("collect", false, "print configured metrics to stdout and exit")
flag_jsonout = flag.String("json-out", "", "store metrics also to JSON file when running test") flag_jsonout = flag.String("json-out", "", "store metrics also to JSON file when running test")
@ -63,6 +63,12 @@ var (
Help: "Number of collection errors.", Help: "Number of collection errors.",
}) })
) )
var (
lua_collect_errors = prometheus.NewCounter(prometheus.CounterOpts{
Name: "fritzbox_exporter_lua_collect_errors",
Help: "Number of lua collection errors.",
})
)
type JSON_PromDesc struct { type JSON_PromDesc struct {
FqName string `json:"fqName"` FqName string `json:"fqName"`
@ -114,17 +120,19 @@ type LuaMetric struct {
PromType string `json:"promType"` PromType string `json:"promType"`
// initialized at startup // initialized at startup
Desc *prometheus.Desc Desc *prometheus.Desc
MetricType prometheus.ValueType MetricType prometheus.ValueType
LuaPage lua.LuaPage
LuaMetricDef lua.LuaMetricValueDefinition
} }
type LuaMetricsFile struct { type LuaMetricsFile struct {
LabelRenames []LuaLabelRename `json:"labelRenames"` LabelRenames []LuaLabelRename `json:"labelRenames"`
Metrics []LuaMetric `json:"metrics"` Metrics []*LuaMetric `json:"metrics"`
} }
var metrics []*Metric var metrics []*Metric
var luaMetricsFile *LuaMetricsFile var luaMetrics []*LuaMetric
type FritzboxCollector struct { type FritzboxCollector struct {
Url string Url string
@ -132,6 +140,10 @@ type FritzboxCollector struct {
Username string Username string
Password string Password string
// support for lua collector
LuaSession *lua.LuaSession
LabelRenames *[]lua.LabelRename
sync.Mutex // protects Root sync.Mutex // protects Root
Root *upnp.Root Root *upnp.Root
} }
@ -354,6 +366,81 @@ func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
fc.ReportMetric(ch, m, result) fc.ReportMetric(ch, m, result)
} }
// if lua is enabled now also collect metrics
if fc.LuaSession != nil {
fc.collectLua(ch)
}
}
func (fc *FritzboxCollector) collectLua(ch chan<- prometheus.Metric) {
// create a map for caching results
var result_map = make(map[string]map[string]interface{})
for _, lm := range luaMetrics {
key := lm.Path + "_" + lm.Params
last_result := result_map[key]
if last_result == nil {
pageData, err := fc.LuaSession.LoadData(lm.LuaPage)
if err != nil {
fmt.Printf("Error loading %s for %s.%s: %s\n", lm.Path, lm.ResultPath, lm.ResultKey, err.Error())
lua_collect_errors.Inc()
continue
}
last_result, err = lua.ParseJSON(pageData)
if err != nil {
fmt.Printf("Error parsing JSON from %s for %s.%s: %s\n", lm.Path, lm.ResultPath, lm.ResultKey, err.Error())
lua_collect_errors.Inc()
continue
}
result_map[key] = last_result
}
metricVals, err := lua.GetMetrics(fc.LabelRenames, last_result, lm.LuaMetricDef)
if err != nil {
fmt.Printf("Error getting metric values for %s.%s: %s\n", lm.ResultPath, lm.ResultKey, err.Error())
lua_collect_errors.Inc()
continue
}
for _, mv := range metricVals {
fc.reportLuaMetric(ch, lm, mv)
}
}
}
func (fc *FritzboxCollector) reportLuaMetric(ch chan<- prometheus.Metric, lm *LuaMetric, value lua.LuaMetricValue) {
labels := make([]string, len(lm.PromDesc.VarLabels))
for i, l := range lm.PromDesc.VarLabels {
if l == "gateway" {
labels[i] = fc.Gateway
} else {
lval, ok := value.Labels[l]
if !ok {
fmt.Printf("%s.%s from %s?%s has no resul for label %s", lm.ResultPath, lm.ResultKey, lm.Path, lm.Params, l)
lval = ""
}
// convert hostname and MAC tolower to avoid problems with labels
if l == "HostName" || l == "MACAddress" {
labels[i] = strings.ToLower(fmt.Sprintf("%v", lval))
} else {
labels[i] = fmt.Sprintf("%v", lval)
}
}
}
ch <- prometheus.MustNewConstMetric(
lm.Desc,
lm.MetricType,
value.Value,
labels...)
} }
func test() { func test() {
@ -470,75 +557,6 @@ func testLua() {
} }
} }
func extraceLuaData(jsonData []byte) {
data, err := lua.ParseJSON(jsonData)
if err != nil {
fmt.Println(err.Error())
return
}
labelRenames := make([]lua.LabelRename, 0)
labelRenames = addLabelRename(labelRenames, "(?i)prozessor", "CPU")
labelRenames = addLabelRename(labelRenames, "(?i)system", "System")
labelRenames = addLabelRename(labelRenames, "(?i)FON", "Phone")
labelRenames = addLabelRename(labelRenames, "(?i)WLAN", "WLAN")
labelRenames = addLabelRename(labelRenames, "(?i)USB", "USB")
labelRenames = addLabelRename(labelRenames, "(?i)Speicher.*FRITZ", "Internal eStorage")
pidMetric := lua.LuaMetricValueDefinition{Path: "", Key: "pid", Labels: nil}
dumpMetric(&labelRenames, data, pidMetric)
powerMetric := lua.LuaMetricValueDefinition{Path: "data.drain.*", Key: "actPerc", Labels: []string{"name"}}
dumpMetric(&labelRenames, data, powerMetric)
lanMetric := lua.LuaMetricValueDefinition{Path: "data.drain.*.lan.*", Key: "class", Labels: []string{"name"}}
dumpMetric(&labelRenames, data, lanMetric)
tempMetric := lua.LuaMetricValueDefinition{Path: "data.cputemp.series.0", Key: "-1", Labels: nil}
dumpMetric(&labelRenames, data, tempMetric)
loadMetric := lua.LuaMetricValueDefinition{Path: "data.cpuutil.series.0", Key: "-1", Labels: nil}
dumpMetric(&labelRenames, data, loadMetric)
ramMetric1 := lua.LuaMetricValueDefinition{Path: "data.ramusage.series.0", Key: "-1", Labels: nil, FixedLabels: map[string]string{"ram_type": "Fixed"}}
dumpMetric(&labelRenames, data, ramMetric1)
ramMetric2 := lua.LuaMetricValueDefinition{Path: "data.ramusage.series.1", Key: "-1", Labels: nil, FixedLabels: map[string]string{"ram_type": "Dynamic"}}
dumpMetric(&labelRenames, data, ramMetric2)
ramMetric3 := lua.LuaMetricValueDefinition{Path: "data.ramusage.series.2", Key: "-1", Labels: nil, FixedLabels: map[string]string{"ram_type": "Free"}}
dumpMetric(&labelRenames, data, ramMetric3)
usbMetric := lua.LuaMetricValueDefinition{Path: "data.usbOverview.devices.*", Key: "partitions.0.totalStorageInBytes", Labels: []string{"deviceType", "deviceName"}}
dumpMetric(&labelRenames, data, usbMetric)
usbMetric2 := lua.LuaMetricValueDefinition{Path: "data.usbOverview.devices.*", Key: "partitions.0.usedStorageInBytes", Labels: []string{"deviceType", "deviceName"}}
dumpMetric(&labelRenames, data, usbMetric2)
}
func dumpMetric(labelRenames *[]lua.LabelRename, data map[string]interface{}, metricDef lua.LuaMetricValueDefinition) {
metrics, err := lua.GetMetrics(labelRenames, data, metricDef)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(fmt.Sprintf("Metrics: %v", metrics))
}
func addLabelRename(labelRenames []lua.LabelRename, pattern string, name string) []lua.LabelRename {
regex, err := regexp.Compile(pattern)
if err == nil {
return append(labelRenames, lua.LabelRename{Pattern: *regex, Name: name})
}
return labelRenames
}
func getValueType(vt string) prometheus.ValueType { func getValueType(vt string) prometheus.ValueType {
switch vt { switch vt {
case "CounterValue": case "CounterValue":
@ -584,6 +602,8 @@ func main() {
return return
} }
var luaSession *lua.LuaSession
var luaLabelRenames *[]lua.LabelRename
if !*flag_disable_lua { if !*flag_disable_lua {
jsonData, err := ioutil.ReadFile(*flag_lua_metrics_file) jsonData, err := ioutil.ReadFile(*flag_lua_metrics_file)
if err != nil { if err != nil {
@ -591,11 +611,60 @@ func main() {
return return
} }
err = json.Unmarshal(jsonData, &luaMetricsFile) var lmf *LuaMetricsFile
err = json.Unmarshal(jsonData, &lmf)
if err != nil { if err != nil {
fmt.Println("error parsing lua JSON:", err) fmt.Println("error parsing lua JSON:", err)
return return
} }
// init label renames
lblRen := make([]lua.LabelRename, 0)
for _, ren := range lmf.LabelRenames {
regex, err := regexp.Compile(ren.MatchRegex)
if err != nil {
fmt.Println("error compiling lua rename regex:", err)
return
}
lblRen = append(lblRen, lua.LabelRename{Pattern: *regex, Name: ren.RenameLabel})
}
luaLabelRenames = &lblRen
// init metrics
luaMetrics = lmf.Metrics
for _, lm := range luaMetrics {
pd := lm.PromDesc
// make labels lower case
labels := make([]string, len(pd.VarLabels))
for i, l := range pd.VarLabels {
labels[i] = strings.ToLower(l)
}
lm.Desc = prometheus.NewDesc(pd.FqName, pd.Help, labels, nil)
lm.MetricType = getValueType(lm.PromType)
lm.LuaPage = lua.LuaPage{
Path: lm.Path,
Params: lm.Params,
}
lm.LuaMetricDef = lua.LuaMetricValueDefinition{
Path: lm.ResultPath,
Key: lm.ResultKey,
OkValue: lm.OkValue,
Labels: pd.VarLabels,
FixedLabels: lm.FixedLabels,
}
}
luaSession = &lua.LuaSession{
BaseURL: *flag_gateway_luaurl,
Username: *flag_gateway_username,
Password: *flag_gateway_password,
}
} }
// init metrics // init metrics
@ -617,6 +686,9 @@ func main() {
Gateway: u.Hostname(), Gateway: u.Hostname(),
Username: *flag_gateway_username, Username: *flag_gateway_username,
Password: *flag_gateway_password, Password: *flag_gateway_password,
LuaSession: luaSession,
LabelRenames: luaLabelRenames,
} }
if *flag_collect { if *flag_collect {
@ -624,6 +696,9 @@ func main() {
prometheus.MustRegister(collector) prometheus.MustRegister(collector)
prometheus.MustRegister(collect_errors) prometheus.MustRegister(collect_errors)
if luaSession != nil {
prometheus.MustRegister(lua_collect_errors)
}
fmt.Println("collecting metrics via http") fmt.Println("collecting metrics via http")
@ -641,6 +716,9 @@ func main() {
prometheus.MustRegister(collector) prometheus.MustRegister(collector)
prometheus.MustRegister(collect_errors) prometheus.MustRegister(collect_errors)
if luaSession != nil {
prometheus.MustRegister(lua_collect_errors)
}
http.Handle("/metrics", promhttp.Handler()) http.Handle("/metrics", promhttp.Handler())
fmt.Printf("metrics available at http://%s/metrics\n", *flag_addr) fmt.Printf("metrics available at http://%s/metrics\n", *flag_addr)

8
metrics-lua.json

@ -22,8 +22,12 @@
}, },
{ {
"matchRegex": "(?i)USB", "matchRegex": "(?i)USB",
"renameLabe": "USB" "renameLabel": "USB"
} },
{
"matchRegex": "(?i)Speicher.*FRITZ",
"renameLabel": "Internal eStorage"
}
], ],
"metrics": [ "metrics": [
{ {

Loading…
Cancel
Save