Browse Source

cleaned up golint issues

pull/4/head
sberk42 4 years ago
parent
commit
a8da0da336
  1. 1
      fritzbox_lua/README.md
  2. 119
      fritzbox_upnp/service.go
  3. 171
      main.go

1
fritzbox_lua/README.md

@ -7,6 +7,7 @@ There does not seem to be a complete documentation of the API, the authenticatio
## Details
Most of the calls seem to be using the data.lua url with a http FORM POST request. As parameters the page and session id are required (e.g.: sid=<SID>&page=engery). The result is JSON with the data needed to create the respective UI.
Some calls (like inetstat_monitor.lua) seem to use GET rather than POST, the client also supports them, but prefix GET: is needed, otherwise a post is done.
Since no public documentation for the JSON format of the various pages seem to exist, you need to observe the calls made by the UI and analyse the JSON result. However the client should be generic enough to get metric and label values from all kind of nested hash and array structures contained in the JSONs.

119
fritzbox_upnp/service.go

@ -1,4 +1,4 @@
// Query UPNP variables from Fritz!Box devices.
// Package fritzbox_upnp Query UPNP variables from Fritz!Box devices.
package fritzbox_upnp
// Copyright 2016 Nils Decker
@ -16,17 +16,17 @@ package fritzbox_upnp
// limitations under the License.
import (
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/tls"
"encoding/xml"
"errors"
"bytes"
"fmt"
"io"
"net/http"
"crypto/tls"
"strconv"
"strings"
"crypto/md5"
"crypto/rand"
)
// curl http://fritz.box:49000/igddesc.xml
@ -36,47 +36,47 @@ import (
// curl http://fritz.box:49000/igddslSCPD.xml
// curl http://fritz.box:49000/igd2ipv6fwcSCPD.xml
const text_xml = `text/xml; charset="utf-8"`
const textXML = `text/xml; charset="utf-8"`
var ErrInvalidSOAPResponse = errors.New("invalid SOAP response")
var errInvalidSOAPResponse = errors.New("invalid SOAP response")
// Root of the UPNP tree
type Root struct {
BaseUrl string
BaseURL string
Username string
Password string
Device Device `xml:"device"`
Services map[string]*Service // Map of all services indexed by .ServiceType
}
// An UPNP Device
// Device an UPNP device
type Device struct {
root *Root
DeviceType string `xml:"deviceType"`
FriendlyName string `xml:"friendlyName"`
Manufacturer string `xml:"manufacturer"`
ManufacturerUrl string `xml:"manufacturerURL"`
ManufacturerURL string `xml:"manufacturerURL"`
ModelDescription string `xml:"modelDescription"`
ModelName string `xml:"modelName"`
ModelNumber string `xml:"modelNumber"`
ModelUrl string `xml:"modelURL"`
ModelURL string `xml:"modelURL"`
UDN string `xml:"UDN"`
Services []*Service `xml:"serviceList>service"` // Service of the device
Devices []*Device `xml:"deviceList>device"` // Sub-Devices of the device
PresentationUrl string `xml:"presentationURL"`
PresentationURL string `xml:"presentationURL"`
}
// An UPNP Service
// Service an UPNP Service
type Service struct {
Device *Device
ServiceType string `xml:"serviceType"`
ServiceId string `xml:"serviceId"`
ControlUrl string `xml:"controlURL"`
EventSubUrl string `xml:"eventSubURL"`
ServiceID string `xml:"serviceId"`
ControlURL string `xml:"controlURL"`
EventSubURL string `xml:"eventSubURL"`
SCPDUrl string `xml:"SCPDURL"`
Actions map[string]*Action // All actions available on the service
@ -88,7 +88,7 @@ type scpdRoot struct {
StateVariables []*StateVariable `xml:"serviceStateTable>stateVariable"`
}
// An UPNP Acton on a service
// Action an UPNP action on a service
type Action struct {
service *Service
@ -97,37 +97,44 @@ type Action struct {
ArgumentMap map[string]*Argument // Map of arguments indexed by .Name
}
// An Inüut Argument to pass to an action
// ActionArgument an Inüut Argument to pass to an action
type ActionArgument struct {
Name string
Value interface{}
}
// structs to unmarshal SOAP faults
// SoapEnvelope struct to unmarshal SOAP faults
type SoapEnvelope struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
Body SoapBody
}
// SoapBody struct
type SoapBody struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
Fault SoapFault
}
// SoapFault struct
type SoapFault struct {
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
FaultCode string `xml:"faultcode"`
FaultString string `xml:"faultstring"`
Detail FaultDetail `xml:"detail"`
}
// FaultDetail struct
type FaultDetail struct {
UpnpError UpnpError `xml:"UPnPError"`
}
// UpnpError struct
type UpnpError struct {
ErrorCode int `xml:"errorCode"`
ErrorDescription string `xml:"errorDescription"`
}
// Returns if the action seems to be a query for information.
// IsGetOnly Returns if the action seems to be a query for information.
// This is determined by checking if the action has no input arguments and at least one output argument.
func (a *Action) IsGetOnly() bool {
for _, a := range a.Arguments {
@ -136,9 +143,6 @@ func (a *Action) IsGetOnly() bool {
}
}
return len(a.Arguments) > 0
return false
}
// An Argument to an action
@ -149,14 +153,14 @@ type Argument struct {
StateVariable *StateVariable
}
// A state variable that can be manipulated through actions
// StateVariable a state variable that can be manipulated through actions
type StateVariable struct {
Name string `xml:"name"`
DataType string `xml:"dataType"`
DefaultValue string `xml:"defaultValue"`
}
// The result of a Call() contains all output arguments of the call.
// Result The result of a Call() contains all output arguments of the call.
// The map is indexed by the name of the state variable.
// The type of the value is string, uint64 or bool depending of the DataType of the variable.
type Result map[string]interface{}
@ -164,7 +168,7 @@ type Result map[string]interface{}
// load the whole tree
func (r *Root) load() error {
igddesc, err := http.Get(
fmt.Sprintf("%s/igddesc.xml", r.BaseUrl),
fmt.Sprintf("%s/igddesc.xml", r.BaseURL),
)
if err != nil {
@ -186,7 +190,7 @@ func (r *Root) load() error {
func (r *Root) loadTr64() error {
igddesc, err := http.Get(
fmt.Sprintf("%s/tr64desc.xml", r.BaseUrl),
fmt.Sprintf("%s/tr64desc.xml", r.BaseURL),
)
if err != nil {
@ -213,7 +217,7 @@ func (d *Device) fillServices(r *Root) error {
for _, s := range d.Services {
s.Device = d
response, err := http.Get(r.BaseUrl + s.SCPDUrl)
response, err := http.Get(r.BaseURL + s.SCPDUrl)
if err != nil {
return err
}
@ -260,24 +264,24 @@ func (d *Device) fillServices(r *Root) error {
return nil
}
const SoapActionXML = `<?xml version="1.0" encoding="utf-8"?>` +
const soapActionXML = `<?xml version="1.0" encoding="utf-8"?>` +
`<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">` +
`<s:Body><u:%s xmlns:u=%s>%s</u:%s xmlns:u=%s></s:Body>` +
`</s:Envelope>`
const SoapActionParamXML = `<%s>%s</%s>`
const soapActionParamXML = `<%s>%s</%s>`
func (a *Action) createCallHttpRequest(actionArg *ActionArgument) (*http.Request, error) {
func (a *Action) createCallHTTPRequest(actionArg *ActionArgument) (*http.Request, error) {
argsString := ""
if actionArg != nil {
var buf bytes.Buffer
sValue := fmt.Sprintf("%v", actionArg.Value)
xml.EscapeText(&buf, []byte(sValue))
argsString += fmt.Sprintf(SoapActionParamXML, actionArg.Name, buf.String(), actionArg.Name)
argsString += fmt.Sprintf(soapActionParamXML, actionArg.Name, buf.String(), actionArg.Name)
}
bodystr := fmt.Sprintf(SoapActionXML, a.Name, a.service.ServiceType, argsString, a.Name, a.service.ServiceType)
bodystr := fmt.Sprintf(soapActionXML, a.Name, a.service.ServiceType, argsString, a.Name, a.service.ServiceType)
url := a.service.Device.root.BaseUrl + a.service.ControlUrl
url := a.service.Device.root.BaseURL + a.service.ControlURL
body := strings.NewReader(bodystr)
req, err := http.NewRequest("POST", url, body)
@ -287,10 +291,10 @@ func (a *Action) createCallHttpRequest(actionArg *ActionArgument) (*http.Request
action := fmt.Sprintf("%s#%s", a.service.ServiceType, a.Name)
req.Header.Set("Content-Type", text_xml)
req.Header.Set("Content-Type", textXML)
req.Header.Set("SOAPAction", action)
return req, nil;
return req, nil
}
// store auth header for reuse
@ -298,7 +302,7 @@ var authHeader = ""
// Call an action with argument if given
func (a *Action) Call(actionArg *ActionArgument) (Result, error) {
req, err := a.createCallHttpRequest(actionArg)
req, err := a.createCallHTTPRequest(actionArg)
if err != nil {
return nil, err
@ -324,12 +328,12 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) {
// call failed, but we have a password so calculate header and try again
authHeader, err = a.getDigestAuthHeader(wwwAuth, a.service.Device.root.Username, a.service.Device.root.Password)
if err != nil {
return nil, errors.New(fmt.Sprintf("%s: %s", a.Name, err.Error))
return nil, fmt.Errorf("%s: %s", a.Name, err.Error())
}
req, err = a.createCallHttpRequest(actionArg)
req, err = a.createCallHTTPRequest(actionArg)
if err != nil {
return nil, errors.New(fmt.Sprintf("%s: %s", a.Name, err.Error))
return nil, fmt.Errorf("%s: %s", a.Name, err.Error())
}
req.Header.Set("Authorization", authHeader)
@ -337,11 +341,11 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) {
resp, err = http.DefaultClient.Do(req)
if err != nil {
return nil, errors.New(fmt.Sprintf("%s: %s", a.Name, err.Error))
return nil, fmt.Errorf("%s: %s", a.Name, err.Error())
}
} else {
return nil, errors.New(fmt.Sprintf("%s: Unauthorized, but no username and password given", a.Name))
return nil, fmt.Errorf("%s: Unauthorized, but no username and password given", a.Name)
}
}
@ -363,7 +367,7 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) {
soapFault := soapEnv.Body.Fault
if soapFault.FaultString == "UPnPError" {
upe := soapFault.Detail.UpnpError;
upe := soapFault.Detail.UpnpError
errMsg = fmt.Sprintf("SAOPFault: %s %d (%s)", soapFault.FaultString, upe.ErrorCode, upe.ErrorDescription)
} else {
@ -371,7 +375,7 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) {
}
}
}
return nil, errors.New(fmt.Sprintf("%s: %s", a.Name, errMsg))
return nil, fmt.Errorf("%s: %s", a.Name, errMsg)
}
return a.parseSoapResponse(resp.Body)
@ -379,8 +383,8 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) {
func (a *Action) getDigestAuthHeader(wwwAuth string, username string, password string) (string, error) {
// parse www-auth header
if ! strings.HasPrefix(wwwAuth, "Digest ") {
return "", errors.New(fmt.Sprintf("WWW-Authentication header is not Digest: '%s'", wwwAuth))
if !strings.HasPrefix(wwwAuth, "Digest ") {
return "", fmt.Errorf("WWW-Authentication header is not Digest: '%s'", wwwAuth)
}
s := wwwAuth[7:]
@ -396,35 +400,34 @@ func (a *Action) getDigestAuthHeader(wwwAuth string, username string, password s
if d["algorithm"] == "" {
d["algorithm"] = "MD5"
} else if d["algorithm"] != "MD5" {
return "", errors.New(fmt.Sprintf("digest algorithm not supported: %s != MD5", d["algorithm"]))
return "", fmt.Errorf("digest algorithm not supported: %s != MD5", d["algorithm"])
}
if d["qop"] != "auth" {
return "", errors.New(fmt.Sprintf("digest qop not supported: %s != auth", d["qop"]))
return "", fmt.Errorf("digest qop not supported: %s != auth", d["qop"])
}
// calc h1 and h2
ha1 := fmt.Sprintf("%x", md5.Sum([]byte(username + ":" + d["realm"] + ":" + password)))
ha1 := fmt.Sprintf("%x", md5.Sum([]byte(username+":"+d["realm"]+":"+password)))
ha2 := fmt.Sprintf("%x", md5.Sum([]byte("POST:" + a.service.ControlUrl)))
ha2 := fmt.Sprintf("%x", md5.Sum([]byte("POST:"+a.service.ControlURL)))
cn := make([]byte, 8)
rand.Read(cn)
cnonce := fmt.Sprintf("%x", cn)
nCounter := 1
nc:=fmt.Sprintf("%08x", nCounter)
nc := fmt.Sprintf("%08x", nCounter)
ds := strings.Join([]string{ha1, d["nonce"], nc, cnonce, d["qop"], ha2}, ":")
response := fmt.Sprintf("%x", md5.Sum([]byte(ds)))
authHeader := fmt.Sprintf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", cnonce=\"%s\", nc=%s, qop=%s, response=\"%s\", algorithm=%s",
username, d["realm"], d["nonce"], a.service.ControlUrl, cnonce, nc, d["qop"], response, d["algorithm"])
username, d["realm"], d["nonce"], a.service.ControlURL, cnonce, nc, d["qop"], response, d["algorithm"])
return authHeader, nil
}
func (a *Action) parseSoapResponse(r io.Reader) (Result, error) {
res := make(Result)
dec := xml.NewDecoder(r)
@ -455,7 +458,7 @@ func (a *Action) parseSoapResponse(r io.Reader) (Result, error) {
case xml.CharData:
val = string(element)
default:
return nil, ErrInvalidSOAPResponse
return nil, errInvalidSOAPResponse
}
converted, err := convertResult(val, arg)
@ -497,7 +500,7 @@ func convertResult(val string, arg *Argument) (interface{}, error) {
}
}
// Load the services tree from an device.
// LoadServices load the services tree from an device.
func LoadServices(baseurl string, username string, password string) (*Root, error) {
if strings.HasPrefix(baseurl, "https://") {
@ -506,7 +509,7 @@ func LoadServices(baseurl string, username string, password string) (*Root, erro
}
var root = &Root{
BaseUrl: baseurl,
BaseURL: baseurl,
Username: username,
Password: password,
}
@ -517,7 +520,7 @@ func LoadServices(baseurl string, username string, password string) (*Root, erro
}
var rootTr64 = &Root{
BaseUrl: baseurl,
BaseURL: baseurl,
Username: username,
Password: password,
}

171
main.go

@ -43,31 +43,31 @@ const serviceLoadRetryTime = 1 * time.Minute
const minCacheTTL = 30
var (
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 and dump results")
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_addr = flag.String("listen-address", "127.0.0.1:9042", "The address to listen on for HTTP requests.")
flag_metrics_file = flag.String("metrics-file", "metrics.json", "The JSON file with the metric definitions.")
flag_disable_lua = flag.Bool("nolua", false, "disable collecting lua metrics")
flag_lua_metrics_file = flag.String("lua-metrics-file", "metrics-lua.json", "The JSON file with the lua metric definitions.")
flag_gateway_url = flag.String("gateway-url", "http://fritz.box:49000", "The URL of the FRITZ!Box")
flag_gateway_luaurl = flag.String("gateway-luaurl", "http://fritz.box", "The URL of the FRITZ!Box UI")
flag_gateway_username = flag.String("username", "", "The user for the FRITZ!Box UPnP service")
flag_gateway_password = flag.String("password", "", "The password for the FRITZ!Box UPnP service")
flagTest = flag.Bool("test", false, "print all available metrics to stdout")
flagLuaTest = flag.Bool("testLua", false, "read luaTest.json file make all contained calls and dump results")
flagCollect = flag.Bool("collect", false, "print configured metrics to stdout and exit")
flagJSONOut = flag.String("json-out", "", "store metrics also to JSON file when running test")
flagAddr = flag.String("listen-address", "127.0.0.1:9042", "The address to listen on for HTTP requests.")
flagMetricsFile = flag.String("metrics-file", "metrics.json", "The JSON file with the metric definitions.")
flagDisableLua = flag.Bool("nolua", false, "disable collecting lua metrics")
flagLuaMetricsFile = flag.String("lua-metrics-file", "metrics-lua.json", "The JSON file with the lua metric definitions.")
flagGatewayURL = flag.String("gateway-url", "http://fritz.box:49000", "The URL of the FRITZ!Box")
flagGatewayLuaURL = flag.String("gateway-luaurl", "http://fritz.box", "The URL of the FRITZ!Box UI")
flagUsername = flag.String("username", "", "The user for the FRITZ!Box UPnP service")
flagPassword = flag.String("password", "", "The password for the FRITZ!Box UPnP service")
)
var (
collect_errors = prometheus.NewCounter(prometheus.CounterOpts{
Name: "fritzbox_exporter_collect_errors",
collectErrors = prometheus.NewCounter(prometheus.CounterOpts{
Name: "fritzbox_exporter_collectErrors",
Help: "Number of collection errors.",
})
)
var (
lua_collect_errors = prometheus.NewCounter(prometheus.CounterOpts{
Name: "fritzbox_exporter_lua_collect_errors",
luaCollectErrors = prometheus.NewCounter(prometheus.CounterOpts{
Name: "fritzbox_exporter_luaCollectErrors",
Help: "Number of lua collection errors.",
})
)
@ -92,13 +92,15 @@ var collectUpnpResultsLoaded = prometheus.NewCounter(prometheus.CounterOpts{
ConstLabels: prometheus.Labels{"Cache": "UPNP"},
})
type JSON_PromDesc struct {
// JSONPromDesc metric description loaded from JSON
type JSONPromDesc struct {
FqName string `json:"fqName"`
Help string `json:"help"`
VarLabels []string `json:"varLabels"`
FixedLabels map[string]string `json:"fixedLabels"`
}
// ActionArg argument for upnp action
type ActionArg struct {
Name string `json:"Name"`
IsIndex bool `json:"IsIndex"`
@ -106,6 +108,7 @@ type ActionArg struct {
Value string `json:"Value"`
}
// Metric upnp metric
type Metric struct {
// initialized loading JSON
Service string `json:"service"`
@ -113,7 +116,7 @@ type Metric struct {
ActionArgument *ActionArg `json:"actionArgument"`
Result string `json:"result"`
OkValue string `json:"okValue"`
PromDesc JSON_PromDesc `json:"promDesc"`
PromDesc JSONPromDesc `json:"promDesc"`
PromType string `json:"promType"`
CacheEntryTTL int64 `json:"cacheEntryTTL"`
@ -122,16 +125,19 @@ type Metric struct {
MetricType prometheus.ValueType
}
// LuaTest JSON struct for API tests
type LuaTest struct {
Path string `json:"path"`
Params string `json:"params"`
}
// LuaLabelRename struct
type LuaLabelRename struct {
MatchRegex string `json:"matchRegex"`
RenameLabel string `json:"renameLabel"`
}
// LuaMetric struct
type LuaMetric struct {
// initialized loading JSON
Path string `json:"path"`
@ -139,7 +145,7 @@ type LuaMetric struct {
ResultPath string `json:"resultPath"`
ResultKey string `json:"resultKey"`
OkValue string `json:"okValue"`
PromDesc JSON_PromDesc `json:"promDesc"`
PromDesc JSONPromDesc `json:"promDesc"`
PromType string `json:"promType"`
CacheEntryTTL int64 `json:"cacheEntryTTL"`
@ -150,28 +156,30 @@ type LuaMetric struct {
LuaMetricDef lua.LuaMetricValueDefinition
}
// LuaMetricsFile json struct
type LuaMetricsFile struct {
LabelRenames []LuaLabelRename `json:"labelRenames"`
Metrics []*LuaMetric `json:"metrics"`
}
type UpnpCacheEntry struct {
type upnpCacheEntry struct {
Timestamp int64
Result *upnp.Result
}
type LuaCacheEntry struct {
type luaCacheEntry struct {
Timestamp int64
Result *map[string]interface{}
}
var metrics []*Metric
var luaMetrics []*LuaMetric
var upnpCache map[string]*UpnpCacheEntry
var luaCache map[string]*LuaCacheEntry
var upnpCache map[string]*upnpCacheEntry
var luaCache map[string]*luaCacheEntry
// FritzboxCollector main struct
type FritzboxCollector struct {
Url string
URL string
Gateway string
Username string
Password string
@ -185,32 +193,32 @@ type FritzboxCollector struct {
}
// simple ResponseWriter to collect output
type TestResponseWriter struct {
type testResponseWriter struct {
header http.Header
statusCode int
body bytes.Buffer
}
func (w *TestResponseWriter) Header() http.Header {
func (w *testResponseWriter) Header() http.Header {
return w.header
}
func (w *TestResponseWriter) Write(b []byte) (int, error) {
func (w *testResponseWriter) Write(b []byte) (int, error) {
return w.body.Write(b)
}
func (w *TestResponseWriter) WriteHeader(statusCode int) {
func (w *testResponseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
}
func (w *TestResponseWriter) String() string {
func (w *testResponseWriter) String() string {
return w.body.String()
}
// LoadServices tries to load the service information. Retries until success.
func (fc *FritzboxCollector) LoadServices() {
for {
root, err := upnp.LoadServices(fc.Url, fc.Username, fc.Password)
root, err := upnp.LoadServices(fc.URL, fc.Username, fc.Password)
if err != nil {
fmt.Printf("cannot load services: %s\n", err)
@ -227,18 +235,19 @@ func (fc *FritzboxCollector) LoadServices() {
}
}
// Describe describe metric
func (fc *FritzboxCollector) Describe(ch chan<- *prometheus.Desc) {
for _, m := range metrics {
ch <- m.Desc
}
}
func (fc *FritzboxCollector) ReportMetric(ch chan<- prometheus.Metric, m *Metric, result upnp.Result) {
func (fc *FritzboxCollector) reportMetric(ch chan<- prometheus.Metric, m *Metric, result upnp.Result) {
val, ok := result[m.Result]
if !ok {
fmt.Printf("%s.%s has no result %s", m.Service, m.Action, m.Result)
collect_errors.Inc()
collectErrors.Inc()
return
}
@ -260,7 +269,7 @@ func (fc *FritzboxCollector) ReportMetric(ch chan<- prometheus.Metric, m *Metric
}
default:
fmt.Println("unknown type", val)
collect_errors.Inc()
collectErrors.Inc()
return
}
@ -291,7 +300,7 @@ func (fc *FritzboxCollector) ReportMetric(ch chan<- prometheus.Metric, m *Metric
labels...)
}
func (fc *FritzboxCollector) GetActionResult(metric *Metric, actionName string, actionArg *upnp.ActionArgument) (upnp.Result, error) {
func (fc *FritzboxCollector) getActionResult(metric *Metric, actionName string, actionArg *upnp.ActionArgument) (upnp.Result, error) {
key := metric.Service + "|" + actionName
@ -304,7 +313,7 @@ func (fc *FritzboxCollector) GetActionResult(metric *Metric, actionName string,
cacheEntry := upnpCache[key]
if cacheEntry == nil {
cacheEntry = &UpnpCacheEntry{}
cacheEntry = &upnpCacheEntry{}
upnpCache[key] = cacheEntry
} else if now-cacheEntry.Timestamp > metric.CacheEntryTTL {
cacheEntry.Result = nil
@ -337,6 +346,7 @@ func (fc *FritzboxCollector) GetActionResult(metric *Metric, actionName string,
return *cacheEntry.Result, nil
}
// Collect collect upnp metrics
func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
fc.Lock()
root := fc.Root
@ -355,11 +365,11 @@ func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
value = aa.Value
if aa.ProviderAction != "" {
provRes, err := fc.GetActionResult(m, aa.ProviderAction, nil)
provRes, err := fc.getActionResult(m, aa.ProviderAction, nil)
if err != nil {
fmt.Printf("Error getting provider action %s result for %s.%s: %s\n", aa.ProviderAction, m.Service, m.Action, err.Error())
collect_errors.Inc()
collectErrors.Inc()
continue
}
@ -367,7 +377,7 @@ func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
value, ok = provRes[aa.Value] // Value contains the result name for provider actions
if !ok {
fmt.Printf("provider action %s for %s.%s has no result", m.Service, m.Action, aa.Value)
collect_errors.Inc()
collectErrors.Inc()
continue
}
}
@ -377,21 +387,21 @@ func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
count, err := strconv.Atoi(sval)
if err != nil {
fmt.Println(err.Error())
collect_errors.Inc()
collectErrors.Inc()
continue
}
for i := 0; i < count; i++ {
actArg = &upnp.ActionArgument{Name: aa.Name, Value: i}
result, err := fc.GetActionResult(m, m.Action, actArg)
result, err := fc.getActionResult(m, m.Action, actArg)
if err != nil {
fmt.Println(err.Error())
collect_errors.Inc()
collectErrors.Inc()
continue
}
fc.ReportMetric(ch, m, result)
fc.reportMetric(ch, m, result)
}
continue
@ -400,15 +410,15 @@ func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) {
}
}
result, err := fc.GetActionResult(m, m.Action, actArg)
result, err := fc.getActionResult(m, m.Action, actArg)
if err != nil {
fmt.Println(err.Error())
collect_errors.Inc()
collectErrors.Inc()
continue
}
fc.ReportMetric(ch, m, result)
fc.reportMetric(ch, m, result)
}
// if lua is enabled now also collect metrics
@ -426,7 +436,7 @@ func (fc *FritzboxCollector) collectLua(ch chan<- prometheus.Metric) {
cacheEntry := luaCache[key]
if cacheEntry == nil {
cacheEntry = &LuaCacheEntry{}
cacheEntry = &luaCacheEntry{}
luaCache[key] = cacheEntry
} else if now-cacheEntry.Timestamp > lm.CacheEntryTTL {
cacheEntry.Result = nil
@ -437,7 +447,7 @@ func (fc *FritzboxCollector) collectLua(ch chan<- prometheus.Metric) {
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()
luaCollectErrors.Inc()
continue
}
@ -445,7 +455,7 @@ func (fc *FritzboxCollector) collectLua(ch chan<- prometheus.Metric) {
data, 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()
luaCollectErrors.Inc()
continue
}
@ -460,7 +470,7 @@ func (fc *FritzboxCollector) collectLua(ch chan<- prometheus.Metric) {
if err != nil {
fmt.Printf("Error getting metric values for %s.%s: %s\n", lm.ResultPath, lm.ResultKey, err.Error())
lua_collect_errors.Inc()
luaCollectErrors.Inc()
continue
}
@ -500,7 +510,7 @@ func (fc *FritzboxCollector) reportLuaMetric(ch chan<- prometheus.Metric, lm *Lu
}
func test() {
root, err := upnp.LoadServices(*flag_gateway_url, *flag_gateway_username, *flag_gateway_password)
root, err := upnp.LoadServices(*flagGatewayURL, *flagUsername, *flagPassword)
if err != nil {
panic(err)
}
@ -516,7 +526,7 @@ func test() {
sort.Strings(serviceKeys)
for _, k := range serviceKeys {
s := root.Services[k]
fmt.Printf("Service: %s (Url: %s)\n", k, s.ControlUrl)
fmt.Printf("Service: %s (Url: %s)\n", k, s.ControlURL)
actionKeys := []string{}
for l := range s.Actions {
@ -571,10 +581,10 @@ func test() {
json.WriteString("\n]")
if *flag_jsonout != "" {
err := ioutil.WriteFile(*flag_jsonout, json.Bytes(), 0644)
if *flagJSONOut != "" {
err := ioutil.WriteFile(*flagJSONOut, json.Bytes(), 0644)
if err != nil {
fmt.Printf("Failed writing JSON file '%s': %s\n", *flag_jsonout, err.Error())
fmt.Printf("Failed writing JSON file '%s': %s\n", *flagJSONOut, err.Error())
}
}
}
@ -595,7 +605,7 @@ func testLua() {
}
// create session struct and init params
luaSession := lua.LuaSession{BaseURL: *flag_gateway_luaurl, Username: *flag_gateway_username, Password: *flag_gateway_password}
luaSession := lua.LuaSession{BaseURL: *flagGatewayLuaURL, Username: *flagUsername, Password: *flagPassword}
for _, test := range luaTests {
fmt.Printf("TESTING: %s (%s)\n", test.Path, test.Params)
@ -609,7 +619,8 @@ func testLua() {
fmt.Println(string(pageData))
}
fmt.Println("\n")
fmt.Println()
fmt.Println()
}
}
@ -629,24 +640,24 @@ func getValueType(vt string) prometheus.ValueType {
func main() {
flag.Parse()
u, err := url.Parse(*flag_gateway_url)
u, err := url.Parse(*flagGatewayURL)
if err != nil {
fmt.Println("invalid URL:", err)
return
}
if *flag_test {
if *flagTest {
test()
return
}
if *flag_luatest {
if *flagLuaTest {
testLua()
return
}
// read metrics
jsonData, err := ioutil.ReadFile(*flag_metrics_file)
jsonData, err := ioutil.ReadFile(*flagMetricsFile)
if err != nil {
fmt.Println("error reading metric file:", err)
return
@ -659,12 +670,12 @@ func main() {
}
// create a map for caching results
upnpCache = make(map[string]*UpnpCacheEntry)
upnpCache = make(map[string]*upnpCacheEntry)
var luaSession *lua.LuaSession
var luaLabelRenames *[]lua.LabelRename
if !*flag_disable_lua {
jsonData, err := ioutil.ReadFile(*flag_lua_metrics_file)
if !*flagDisableLua {
jsonData, err := ioutil.ReadFile(*flagLuaMetricsFile)
if err != nil {
fmt.Println("error reading lua metric file:", err)
return
@ -678,7 +689,7 @@ func main() {
}
// create a map for caching results
luaCache = make(map[string]*LuaCacheEntry)
luaCache = make(map[string]*luaCacheEntry)
// init label renames
lblRen := make([]lua.LabelRename, 0)
@ -727,9 +738,9 @@ func main() {
}
luaSession = &lua.LuaSession{
BaseURL: *flag_gateway_luaurl,
Username: *flag_gateway_username,
Password: *flag_gateway_password,
BaseURL: *flagGatewayLuaURL,
Username: *flagUsername,
Password: *flagPassword,
}
}
@ -753,28 +764,28 @@ func main() {
}
collector := &FritzboxCollector{
Url: *flag_gateway_url,
URL: *flagGatewayURL,
Gateway: u.Hostname(),
Username: *flag_gateway_username,
Password: *flag_gateway_password,
Username: *flagUsername,
Password: *flagPassword,
LuaSession: luaSession,
LabelRenames: luaLabelRenames,
}
if *flag_collect {
if *flagCollect {
collector.LoadServices()
prometheus.MustRegister(collector)
prometheus.MustRegister(collect_errors)
prometheus.MustRegister(collectErrors)
if luaSession != nil {
prometheus.MustRegister(lua_collect_errors)
prometheus.MustRegister(luaCollectErrors)
}
fmt.Println("collecting metrics via http")
// simulate HTTP request without starting actual http server
writer := TestResponseWriter{header: http.Header{}}
writer := testResponseWriter{header: http.Header{}}
request := http.Request{}
promhttp.Handler().ServeHTTP(&writer, &request)
@ -786,18 +797,18 @@ func main() {
go collector.LoadServices()
prometheus.MustRegister(collector)
prometheus.MustRegister(collect_errors)
prometheus.MustRegister(collectErrors)
prometheus.MustRegister(collectUpnpResultsCached)
prometheus.MustRegister(collectUpnpResultsLoaded)
if luaSession != nil {
prometheus.MustRegister(lua_collect_errors)
prometheus.MustRegister(luaCollectErrors)
prometheus.MustRegister(collectLuaResultsCached)
prometheus.MustRegister(collectLuaResultsLoaded)
}
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", *flagAddr)
log.Fatal(http.ListenAndServe(*flag_addr, nil))
log.Fatal(http.ListenAndServe(*flagAddr, nil))
}

Loading…
Cancel
Save