From e12f3074516e11f00b4ff08713a0f096ec34ffdf Mon Sep 17 00:00:00 2001 From: Nils Decker Date: Wed, 2 Mar 2016 17:07:45 +0100 Subject: [PATCH] add documentation --- README.md | 157 +++++++++++++++++++++++++++++++++++++++ fritzbox_upnp/service.go | 37 ++++++--- main.go | 37 +++++---- 3 files changed, 202 insertions(+), 29 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..52fa8b9 --- /dev/null +++ b/README.md @@ -0,0 +1,157 @@ +# Fritz!Box Upnp statistics exporter for prometheus + +This exporter exports some variables from an +[AVM Fritzbox](http://avm.de/produkte/fritzbox/) +to prometheus. + +This exporter is tested with a Fritzbox 7490 and software version 06.51. + +## Building + + go get github.com/ndecker/fritzbox_exporter/ + cd $GOROOT/src/github.com/ndecker/fritzbox_exporter + go install + +## Running + +In the configuration of the Fritzbox the option "Statusinformationen über UPnP übertragen" in the dialog "Heimnetz > +Heimnetzübersicht > Netzwerkeinstellungen" has to be enabled. + +Usage: + + ./fritzbox_exporter -h + Usage of ./fritzbox_exporter: + -gateway-address string + The URL of the upnp service (default "fritz.box") + -gateway-port int + The URL of the upnp service (default 49000) + -listen-address string + The address to listen on for HTTP requests. (default ":9132") + -test + print all available metrics to stdout + +## Exported metrics + +These metrics are exported: + + # HELP fritzbox_exporter_collect_errors Number of collection errors. + # TYPE fritzbox_exporter_collect_errors counter + fritzbox_exporter_collect_errors 0 + # HELP gateway_wan_bytes_received bytes received on gateway WAN interface + # TYPE gateway_wan_bytes_received counter + gateway_wan_bytes_received{gateway="fritz.box"} 5.037749914e+09 + # HELP gateway_wan_bytes_sent bytes sent on gateway WAN interface + # TYPE gateway_wan_bytes_sent counter + gateway_wan_bytes_sent{gateway="fritz.box"} 2.55707479e+08 + # HELP gateway_wan_connection_status WAN connection status (Connected = 1) + # TYPE gateway_wan_connection_status gauge + gateway_wan_connection_status{gateway="fritz.box"} 1 + # HELP gateway_wan_connection_uptime_seconds WAN connection uptime + # TYPE gateway_wan_connection_uptime_seconds gauge + gateway_wan_connection_uptime_seconds{gateway="fritz.box"} 65259 + # HELP gateway_wan_layer1_downstream_max_bitrate Layer1 downstream max bitrate + # TYPE gateway_wan_layer1_downstream_max_bitrate gauge + gateway_wan_layer1_downstream_max_bitrate{gateway="fritz.box"} 1.286e+07 + # HELP gateway_wan_layer1_link_status Status of physical link (Up = 1) + # TYPE gateway_wan_layer1_link_status gauge + gateway_wan_layer1_link_status{gateway="fritz.box"} 1 + # HELP gateway_wan_layer1_upstream_max_bitrate Layer1 upstream max bitrate + # TYPE gateway_wan_layer1_upstream_max_bitrate gauge + gateway_wan_layer1_upstream_max_bitrate{gateway="fritz.box"} 1.148e+06 + # HELP gateway_wan_packets_received packets received on gateway WAN interface + # TYPE gateway_wan_packets_received counter + gateway_wan_packets_received{gateway="fritz.box"} 1.346625e+06 + # HELP gateway_wan_packets_sent packets sent on gateway WAN interface + # TYPE gateway_wan_packets_sent counter + gateway_wan_packets_sent{gateway="fritz.box"} 3.05051e+06 + + +## Output of -test + +The exporter prints all available Variables to stdout when called with the -test option. +These values are determined by parsing all services from http://fritz.box:49000/igddesc.xml + + Name: urn:schemas-any-com:service:Any:1 + WANDevice - FRITZ!Box 7490: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + GetCommonLinkProperties + WANAccessType: DSL + Layer1UpstreamMaxBitRate: 1148000 + Layer1DownstreamMaxBitRate: 12860000 + PhysicalLinkStatus: Up + GetTotalBytesSent + TotalBytesSent: 255710914 + GetTotalBytesReceived + TotalBytesReceived: 5037753042 + GetTotalPacketsSent + TotalPacketsSent: 3050536 + GetTotalPacketsReceived + TotalPacketsReceived: 1346651 + GetAddonInfos + ByteSendRate: 0 + ByteReceiveRate: 0 + PacketSendRate: 0 + PacketReceiveRate: 0 + TotalBytesSent: 255710914 + TotalBytesReceived: 5037753042 + AutoDisconnectTime: 0 + IdleDisconnectTime: 10 + DNSServer1: 1.1.1.1 + DNSServer2: 2.2.2.2 + VoipDNSServer1: 1.1.1.1 + VoipDNSServer2: 2.2.2.2 + UpnpControlEnabled: false + RoutedBridgedModeBoth: 1 + WANConnectionDevice - FRITZ!Box 7490: urn:schemas-upnp-org:service:WANDSLLinkConfig:1 + GetDSLLinkInfo + LinkType: PPPoE + LinkStatus: Up + GetModulationType + ModulationType: ADSL G.lite + GetDestinationAddress + DestinationAddress: NONE + GetATMEncapsulation + ATMEncapsulation: LLC + GetFCSPreserved + FCSPreserved: true + GetAutoConfig + AutoConfig: true + WANConnectionDevice - FRITZ!Box 7490: urn:schemas-upnp-org:service:WANIPConnection:1 + X_AVM_DE_GetDNSServer + IPv4DNSServer1: 1.1.1.1 + IPv4DNSServer2: 2.2.2.2 + GetAutoDisconnectTime + AutoDisconnectTime: 0 + GetIdleDisconnectTime + IdleDisconnectTime: 0 + X_AVM_DE_GetExternalIPv6Address + ExternalIPv6Address: + PrefixLength: 0 + ValidLifetime: 0 + PreferedLifetime: 0 + GetNATRSIPStatus + RSIPAvailable: false + NATEnabled: true + GetExternalIPAddress + ExternalIPAddress: 1.1.1.1 + X_AVM_DE_GetIPv6Prefix + IPv6Prefix: + PrefixLength: 0 + ValidLifetime: 0 + PreferedLifetime: 0 + X_AVM_DE_GetIPv6DNSServer + IPv6DNSServer1: + ValidLifetime1: 2002000000 + IPv6DNSServer2: + ValidLifetime2: 199800000 + GetConnectionTypeInfo + ConnectionType: IP_Routed + PossibleConnectionTypes: IP_Routed + GetStatusInfo + ConnectionStatus: Connected + LastConnectionError: ERROR_NONE + Uptime: 65386 + WANConnectionDevice - FRITZ!Box 7490: urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + GetFirewallStatus + FirewallEnabled: true + InboundPinholeAllowed: false + diff --git a/fritzbox_upnp/service.go b/fritzbox_upnp/service.go index 35e119e..f050bb8 100644 --- a/fritzbox_upnp/service.go +++ b/fritzbox_upnp/service.go @@ -1,13 +1,14 @@ +// Query UPNP variables from Fritz!Box devices. package fritzbox_upnp // Copyright 2016 Nils Decker -// +// // 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. @@ -36,12 +37,14 @@ const text_xml = `text/xml; charset="utf-8"` var ErrInvalidSOAPResponse = errors.New("invalid SOAP response") +// Root of the UPNP tree type Root struct { BaseUrl string Device Device `xml:"device"` - Services map[string]*Service + Services map[string]*Service // Map of all services indexed by .ServiceType } +// An UPNP Device type Device struct { root *Root @@ -55,12 +58,13 @@ type Device struct { ModelUrl string `xml:"modelURL"` UDN string `xml:"UDN"` - Services []*Service `xml:"serviceList>service"` - Devices []*Device `xml:"deviceList>device"` + Services []*Service `xml:"serviceList>service"` // Service of the device + Devices []*Device `xml:"deviceList>device"` // Sub-Devices of the device PresentationUrl string `xml:"presentationURL"` } +// An UPNP Service type Service struct { Device *Device @@ -70,8 +74,8 @@ type Service struct { EventSubUrl string `xml:"eventSubURL"` SCPDUrl string `xml:"SCPDURL"` - Actions map[string]*Action - StateVariables []*StateVariable + Actions map[string]*Action // All actions available on the service + StateVariables []*StateVariable // All state variables available on the service } type scpdRoot struct { @@ -79,14 +83,17 @@ type scpdRoot struct { StateVariables []*StateVariable `xml:"serviceStateTable>stateVariable"` } +// An UPNP Acton on a service type Action struct { service *Service Name string `xml:"name"` Arguments []*Argument `xml:"argumentList>argument"` - ArgumentMap map[string]*Argument + ArgumentMap map[string]*Argument // Map of arguments indexed by .Name } +// 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 { if a.Direction == "in" { @@ -96,6 +103,7 @@ func (a *Action) IsGetOnly() bool { return len(a.Arguments) > 0 } +// An Argument to an action type Argument struct { Name string `xml:"name"` Direction string `xml:"direction"` @@ -103,14 +111,19 @@ type Argument struct { StateVariable *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. +// 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{} +// load the whole tree func (r *Root) load() error { igddesc, err := http.Get( fmt.Sprintf("%s/igddesc.xml", r.BaseUrl), @@ -131,6 +144,7 @@ func (r *Root) load() error { return r.Device.fillServices(r) } +// load all service descriptions func (d *Device) fillServices(r *Root) error { d.root = r @@ -182,6 +196,8 @@ func (d *Device) fillServices(r *Root) error { return nil } +// Call an action. +// Currently only actions without input arguments are supported. func (a *Action) Call() (Result, error) { bodystr := fmt.Sprintf(` @@ -270,7 +286,7 @@ func convertResult(val string, arg *Argument) (interface{}, error) { return bool(val == "1"), nil case "ui1", "ui2", "ui4": - // type ui4 can contain values greater than 2^32! + // type ui4 can contain values greater than 2^32! res, err := strconv.ParseUint(val, 10, 64) if err != nil { return nil, err @@ -282,6 +298,7 @@ func convertResult(val string, arg *Argument) (interface{}, error) { } } +// Load the services tree from an device. func LoadServices(device string, port uint16) (*Root, error) { var root = &Root{ BaseUrl: fmt.Sprintf("http://%s:%d", device, port), diff --git a/main.go b/main.go index ff17a71..768bc84 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,13 @@ package main // Copyright 2016 Nils Decker -// +// // 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. @@ -26,7 +26,7 @@ import ( var ( flag_test = flag.Bool("test", false, "print all available metrics to stdout") - flag_addr = flag.String("listen-address", ":9111", "The address to listen on for HTTP requests.") + flag_addr = flag.String("listen-address", ":9132", "The address to listen on for HTTP requests.") flag_gateway_address = flag.String("gateway-address", "fritz.box", "The URL of the upnp service") flag_gateway_port = flag.Int("gateway-port", 49000, "The URL of the upnp service") @@ -45,8 +45,8 @@ type Metric struct { Result string OkValue string - Desc *prometheus.Desc - MetricType prometheus.ValueType + Desc *prometheus.Desc + MetricType prometheus.ValueType } var metrics = []*Metric{ @@ -60,8 +60,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.CounterValue, - + MetricType: prometheus.CounterValue, }, { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -73,7 +72,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.CounterValue, + MetricType: prometheus.CounterValue, }, { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -85,7 +84,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.CounterValue, + MetricType: prometheus.CounterValue, }, { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -97,7 +96,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.CounterValue, + MetricType: prometheus.CounterValue, }, { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -109,7 +108,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.GaugeValue, + MetricType: prometheus.GaugeValue, }, { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -121,7 +120,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.GaugeValue, + MetricType: prometheus.GaugeValue, }, { Service: "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1", @@ -134,7 +133,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.GaugeValue, + MetricType: prometheus.GaugeValue, }, { Service: "urn:schemas-upnp-org:service:WANIPConnection:1", @@ -147,7 +146,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.GaugeValue, + MetricType: prometheus.GaugeValue, }, { Service: "urn:schemas-upnp-org:service:WANIPConnection:1", @@ -159,7 +158,7 @@ var metrics = []*Metric{ []string{"gateway"}, nil, ), - MetricType: prometheus.GaugeValue, + MetricType: prometheus.GaugeValue, }, } @@ -236,7 +235,7 @@ func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) { ch <- prometheus.MustNewConstMetric( m.Desc, - m.MetricType, + m.MetricType, floatval, fc.Gateway, ) @@ -266,7 +265,7 @@ func main() { fmt.Printf(" %s\n", a.Name) for _, arg := range a.Arguments { - fmt.Printf(" %s - %s: %v\n", arg.RelatedStateVariable, arg.StateVariable.DataType, res[arg.StateVariable.Name]) + fmt.Printf(" %s: %v\n", arg.RelatedStateVariable, res[arg.StateVariable.Name]) } } } @@ -275,7 +274,7 @@ func main() { } prometheus.MustRegister(&FritzboxCollector{root, *flag_gateway_address}) - prometheus.MustRegister(collect_errors) + prometheus.MustRegister(collect_errors) // Since we are dealing with custom Collector implementations, it might // be a good idea to enable the collect checks in the registry.