From a8da0da3362dd031b600c6e3b3d91dc6b90963a4 Mon Sep 17 00:00:00 2001 From: sberk42 Date: Sat, 12 Dec 2020 18:55:02 +0100 Subject: [PATCH] cleaned up golint issues --- fritzbox_lua/README.md | 1 + fritzbox_upnp/service.go | 195 +++++++++++++++++++------------------- main.go | 199 +++++++++++++++++++++------------------ 3 files changed, 205 insertions(+), 190 deletions(-) diff --git a/fritzbox_lua/README.md b/fritzbox_lua/README.md index b085fd5..0ad055e 100644 --- a/fritzbox_lua/README.md +++ b/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=&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. diff --git a/fritzbox_upnp/service.go b/fritzbox_upnp/service.go index 1cca2e9..0a3e8bf 100644 --- a/fritzbox_upnp/service.go +++ b/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{} + 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 + 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 + 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"` + 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 UpnpError `xml:"UPnPError"` } + +// UpnpError struct type UpnpError struct { - ErrorCode int `xml:"errorCode"` - ErrorDescription string `xml:"errorDescription"` + 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 { @@ -172,7 +176,7 @@ func (r *Root) load() error { } defer igddesc.Body.Close() - + dec := xml.NewDecoder(igddesc.Body) err = dec.Decode(r) @@ -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 = `` + +const soapActionXML = `` + `` + - `%s` + + `%s` + `` -const SoapActionParamXML = `<%s>%s` +const soapActionParamXML = `<%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,18 +291,18 @@ 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 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 @@ -308,7 +312,7 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) { if authHeader != "" { req.Header.Set("Authorization", authHeader) } - + // first try call without auth header resp, err := http.DefaultClient.Do(req) @@ -318,33 +322,33 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) { wwwAuth := resp.Header.Get("WWW-Authenticate") if resp.StatusCode == http.StatusUnauthorized { - resp.Body.Close() // close now, since we make a new request below or fail - + resp.Body.Close() // close now, since we make a new request below or fail + if wwwAuth != "" && a.service.Device.root.Username != "" && a.service.Device.root.Password != "" { // 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) - - resp, err = http.DefaultClient.Do(req) + + 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) } } - + defer resp.Body.Close() if resp.StatusCode != http.StatusOK { @@ -354,24 +358,24 @@ func (a *Action) Call(actionArg *ActionArgument) (Result, error) { io.Copy(buf, resp.Body) body := buf.String() //fmt.Println(body) - + var soapEnv SoapEnvelope err := xml.Unmarshal([]byte(body), &soapEnv) if err != nil { errMsg = fmt.Sprintf("error decoding SOAPFault: %s", err.Error()) } else { 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 { errMsg = fmt.Sprintf("SAOPFault: %s", soapFault.FaultString) } - } + } } - 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,10 +383,10 @@ 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:] d := map[string]string{} for _, kv := range strings.Split(s, ",") { @@ -392,39 +396,38 @@ func (a *Action) getDigestAuthHeader(wwwAuth string, username string, password s } d[strings.Trim(parts[0], "\" ")] = strings.Trim(parts[1], "\" ") } - + 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))) - - ha2 := fmt.Sprintf("%x", md5.Sum([]byte("POST:" + a.service.ControlUrl))) + ha1 := fmt.Sprintf("%x", md5.Sum([]byte(username+":"+d["realm"]+":"+password))) + + 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) + rand.Read(cn) + cnonce := fmt.Sprintf("%x", cn) + + nCounter := 1 + 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) @@ -491,13 +494,13 @@ func convertResult(val string, arg *Argument) (interface{}, error) { return int64(res), nil case "dateTime", "uuid": // data types we don't convert yet - return val, nil + return val, nil default: return nil, fmt.Errorf("unknown datatype: %s (%s)", arg.StateVariable.DataType, val) } } -// 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, } diff --git a/main.go b/main.go index 797e11c..0c16d1a 100644 --- a/main.go +++ b/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,42 +108,46 @@ type ActionArg struct { Value string `json:"Value"` } +// Metric upnp metric type Metric struct { // initialized loading JSON - Service string `json:"service"` - Action string `json:"action"` - ActionArgument *ActionArg `json:"actionArgument"` - Result string `json:"result"` - OkValue string `json:"okValue"` - PromDesc JSON_PromDesc `json:"promDesc"` - PromType string `json:"promType"` - CacheEntryTTL int64 `json:"cacheEntryTTL"` + Service string `json:"service"` + Action string `json:"action"` + ActionArgument *ActionArg `json:"actionArgument"` + Result string `json:"result"` + OkValue string `json:"okValue"` + PromDesc JSONPromDesc `json:"promDesc"` + PromType string `json:"promType"` + CacheEntryTTL int64 `json:"cacheEntryTTL"` // initialized at startup Desc *prometheus.Desc 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"` - Params string `json:"params"` - ResultPath string `json:"resultPath"` - ResultKey string `json:"resultKey"` - OkValue string `json:"okValue"` - PromDesc JSON_PromDesc `json:"promDesc"` - PromType string `json:"promType"` - CacheEntryTTL int64 `json:"cacheEntryTTL"` + Path string `json:"path"` + Params string `json:"params"` + ResultPath string `json:"resultPath"` + ResultKey string `json:"resultKey"` + OkValue string `json:"okValue"` + PromDesc JSONPromDesc `json:"promDesc"` + PromType string `json:"promType"` + CacheEntryTTL int64 `json:"cacheEntryTTL"` // initialized at startup Desc *prometheus.Desc @@ -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)) }