From 05c803b4655ec2307cc4e9bc1179382c9346e696 Mon Sep 17 00:00:00 2001 From: Nils Decker Date: Mon, 29 Feb 2016 21:12:24 +0100 Subject: [PATCH] split upnp into seperate package --- .gitignore | 1 + upnp.go => fritzbox_upnp/upnp.go | 62 ++++++++--- fritzbox_upnp/values.go | 67 +++++++++++ main.go | 184 ++++++++++++------------------- 4 files changed, 185 insertions(+), 129 deletions(-) create mode 100644 .gitignore rename upnp.go => fritzbox_upnp/upnp.go (58%) create mode 100644 fritzbox_upnp/values.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f71592 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fritzbox_exporter diff --git a/upnp.go b/fritzbox_upnp/upnp.go similarity index 58% rename from upnp.go rename to fritzbox_upnp/upnp.go index 1ceb2a8..d015d00 100644 --- a/upnp.go +++ b/fritzbox_upnp/upnp.go @@ -1,20 +1,21 @@ -package main +package fritzbox_upnp import ( - "bytes" + "bytes" "encoding/xml" "errors" "fmt" "io" "net/http" + "strconv" "strings" ) -const TEXT_XML = `text/xml; charset="utf-8"` +const text_xml = `text/xml; charset="utf-8"` var ( - ErrResultNotFound = errors.New("result not found") - ErrResultWithoutChardata = errors.New("result without chardata") + ErrResultNotFound = errors.New("result not found") + ErrResultWithoutChardata = errors.New("result without chardata") ) // curl "http://fritz.box:49000/igdupnp/control/WANIPConn1" @@ -27,14 +28,17 @@ var ( // " type UpnpValue struct { - path string - service string - method string - ret_tag string + Path string + Service string + Method string + RetTag string + + ShortName string + Help string } -func (v *UpnpValue) Query(device string) (string, error) { - url := fmt.Sprintf("http://%s:49000%s", device, v.path) +func (v *UpnpValue) query(device string, port uint16) (string, error) { + url := fmt.Sprintf("http://%s:%d%s", device, port, v.Path) bodystr := fmt.Sprintf(` @@ -43,7 +47,7 @@ func (v *UpnpValue) Query(device string) (string, error) { - `, v.method, v.service) + `, v.Method, v.Service) body := strings.NewReader(bodystr) @@ -52,9 +56,9 @@ func (v *UpnpValue) Query(device string) (string, error) { return "", err } - action := fmt.Sprintf("urn:schemas-upnp-org:service:%s#%s", v.service, v.method) + action := fmt.Sprintf("urn:schemas-upnp-org:service:%s#%s", v.Service, v.Method) - req.Header["Content-Type"] = []string{TEXT_XML} + req.Header["Content-Type"] = []string{text_xml} req.Header["SoapAction"] = []string{action} resp, err := http.DefaultClient.Do(req) @@ -62,10 +66,10 @@ func (v *UpnpValue) Query(device string) (string, error) { return "", err } - data := new(bytes.Buffer) - data.ReadFrom(resp.Body) + data := new(bytes.Buffer) + data.ReadFrom(resp.Body) - // fmt.Printf(data.String()) + // fmt.Printf(data.String()) dec := xml.NewDecoder(data) @@ -80,7 +84,7 @@ func (v *UpnpValue) Query(device string) (string, error) { } if se, ok := t.(xml.StartElement); ok { - if se.Name.Local == v.ret_tag { + if se.Name.Local == v.RetTag { t2, err := dec.Token() if err != nil { return "", err @@ -96,3 +100,25 @@ func (v *UpnpValue) Query(device string) (string, error) { } } + +type UpnpValueString struct{ UpnpValue } + +func (v *UpnpValueString) Query(device string, port uint16) (string, error) { + return v.query(device, port) +} + +type UpnpValueUint struct{ UpnpValue } + +func (v *UpnpValueUint) Query(device string, port uint16) (uint64, error) { + strval, err := v.query(device, port) + if err != nil { + return 0, err + } + + val, err := strconv.ParseUint(strval, 10, 64) + if err != nil { + return 0, err + } + + return val, nil +} diff --git a/fritzbox_upnp/values.go b/fritzbox_upnp/values.go new file mode 100644 index 0000000..656cb89 --- /dev/null +++ b/fritzbox_upnp/values.go @@ -0,0 +1,67 @@ +package fritzbox_upnp + +// curl http://fritz.box:49000/igddesc.xml +// curl http://fritz.box:49000/any.xml +// curl http://fritz.box:49000/igdconnSCPD.xml +// curl http://fritz.box:49000/igdicfgSCPD.xml +// curl http://fritz.box:49000/igddslSCPD.xml +// curl http://fritz.box:49000/igd2ipv6fwcSCPD.xml + +var ( + WAN_IP = UpnpValueString{UpnpValue{ + Path: "/igdupnp/control/WANIPConn1", + Service: "WANIPConnection:1", + Method: "GetExternalIPAddress", + RetTag: "NewExternalIPAddress", + + ShortName: "wan_ip", + Help: "WAN IP Adress", + }} + + WAN_Packets_Received = UpnpValueUint{UpnpValue{ + Path: "/igdupnp/control/WANCommonIFC1", + Service: "WANCommonInterfaceConfig:1", + Method: "GetTotalPacketsReceived", + RetTag: "NewTotalPacketsReceived", + + ShortName: "packets_received", + Help: "packets received on gateway WAN interface", + }} + + WAN_Packets_Sent = UpnpValueUint{UpnpValue{ + Path: "/igdupnp/control/WANCommonIFC1", + Service: "WANCommonInterfaceConfig:1", + Method: "GetTotalPacketsSent", + RetTag: "NewTotalPacketsSent", + + ShortName: "packets_sent", + Help: "packets sent on gateway WAN interface", + }} + + WAN_Bytes_Received = UpnpValueUint{UpnpValue{ + Path: "/igdupnp/control/WANCommonIFC1", + Service: "WANCommonInterfaceConfig:1", + Method: "GetAddonInfos", + RetTag: "NewTotalBytesReceived", + + ShortName: "bytes_received", + Help: "bytes received on gateway WAN interface", + }} + + WAN_Bytes_Sent = UpnpValueUint{UpnpValue{ + Path: "/igdupnp/control/WANCommonIFC1", + Service: "WANCommonInterfaceConfig:1", + Method: "GetAddonInfos", + RetTag: "NewTotalBytesSent", + + ShortName: "bytes_sent", + Help: "bytes sent on gateway WAN interface", + }} +) + +var Values = []UpnpValueUint{ + WAN_Packets_Received, + WAN_Packets_Sent, + WAN_Bytes_Received, + WAN_Bytes_Sent, +} diff --git a/main.go b/main.go index e266238..8339172 100644 --- a/main.go +++ b/main.go @@ -2,148 +2,110 @@ package main import ( "flag" + "fmt" "net/http" - "strconv" "github.com/prometheus/client_golang/prometheus" -) -var ( - flag_addr = flag.String("listen-address", ":9111", "The address to listen on for HTTP requests.") - flag_dev_address = flag.String("device-address", "fritz.box", "The URL of the upnp service") + upnp "github.com/ndecker/fritzbox_exporter/fritzbox_upnp" ) var ( - WAN_IP = UpnpValue{ - path: "/igdupnp/control/WANIPConn1", - service: "WANIPConnection:1", - method: "GetExternalIPAddress", - ret_tag: "NewExternalIPAddress", - } - - WAN_Packets_Received = UpnpValue{ - path: "/igdupnp/control/WANCommonIFC1", - service: "WANCommonInterfaceConfig:1", - method: "GetTotalPacketsReceived", - ret_tag: "NewTotalPacketsReceived", - } - - WAN_Packets_Sent = UpnpValue{ - path: "/igdupnp/control/WANCommonIFC1", - service: "WANCommonInterfaceConfig:1", - method: "GetTotalPacketsSent", - ret_tag: "NewTotalPacketsSent", - } + 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.") - WAN_Bytes_Received = UpnpValue{ - path: "/igdupnp/control/WANCommonIFC1", - service: "WANCommonInterfaceConfig:1", - method: "GetAddonInfos", - ret_tag: "NewTotalBytesReceived", - } + 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") +) - WAN_Bytes_Sent = UpnpValue{ - path: "/igdupnp/control/WANCommonIFC1", - service: "WANCommonInterfaceConfig:1", - method: "GetAddonInfos", - ret_tag: "NewTotalBytesSent", - } +var ( + collect_errors = prometheus.NewCounter(prometheus.CounterOpts{ + Name: "fritzbox_exporter_collect_errors", + Help: "Number of collection errors.", + }) ) -type Metric struct { - UpnpValue - *prometheus.Desc +type UpnpMetric struct { + upnp.UpnpValueUint + *prometheus.Desc } -func (m Metric) Value() (uint64, error) { - strval, err := m.Query(*flag_dev_address) - if err != nil { - return 0, err - } - - return strconv.ParseUint(strval, 10, 64) +func (m UpnpMetric) Describe(ch chan<- *prometheus.Desc) { + ch <- m.Desc } -func (m Metric) Describe(ch chan<- *prometheus.Desc) { - ch <- m.Desc -} +func (m UpnpMetric) Collect(gateway string, port uint16, ch chan<- prometheus.Metric) error { + val, err := m.Query(gateway, port) + if err != nil { + return err + } -func (m Metric) Collect(ch chan<- prometheus.Metric) error { - val, err := m.Value() - if err != nil { - return err - } - - ch <- prometheus.MustNewConstMetric( - m.Desc, - prometheus.CounterValue, - float64(val), - ) - return nil + ch <- prometheus.MustNewConstMetric( + m.Desc, + prometheus.CounterValue, + float64(val), + gateway, + ) + return nil } -var ( - packets_sent = Metric{ - WAN_Packets_Sent, - prometheus.NewDesc( - prometheus.BuildFQName("gateway", "wan", "packets_sent"), - "packets sent on gateway wan interface", - nil, - prometheus.Labels{"gateway": *flag_dev_address}, - ), - } - packets_received = Metric{ - WAN_Packets_Received, - prometheus.NewDesc( - prometheus.BuildFQName("gateway", "wan", "packets_received"), - "packets received on gateway wan interface", - nil, - prometheus.Labels{"gateway": *flag_dev_address}, - ), - } - bytes_sent = Metric{ - WAN_Bytes_Sent, - prometheus.NewDesc( - prometheus.BuildFQName("gateway", "wan", "bytes_sent"), - "bytes sent on gateway wan interface", - nil, - prometheus.Labels{"gateway": *flag_dev_address}, - ), - } - bytes_received = Metric{ - WAN_Bytes_Received, - prometheus.NewDesc( - prometheus.BuildFQName("gateway", "wan", "bytes_received"), - "bytes received on gateway wan interface", - nil, - prometheus.Labels{"gateway": *flag_dev_address}, - ), - } -) - +func NewUpnpMetric(v upnp.UpnpValueUint) UpnpMetric { + return UpnpMetric{ + v, + prometheus.NewDesc( + prometheus.BuildFQName("gateway", "wan", v.ShortName), + v.Help, + []string{"gateway"}, + nil, + ), + } +} type FritzboxCollector struct { + gateway string + port uint16 + metrics []UpnpMetric } func (fc *FritzboxCollector) Describe(ch chan<- *prometheus.Desc) { - packets_sent.Describe(ch) - packets_received.Describe(ch) - bytes_sent.Describe(ch) - bytes_received.Describe(ch) + for _, m := range fc.metrics { + m.Describe(ch) + } } func (fc *FritzboxCollector) Collect(ch chan<- prometheus.Metric) { - packets_sent.Collect(ch) - packets_received.Collect(ch) - bytes_sent.Collect(ch) - bytes_received.Collect(ch) + for _, m := range fc.metrics { + err := m.Collect(fc.gateway, fc.port, ch) + if err != nil { + collect_errors.Inc() + } + } } - func main() { flag.Parse() - prometheus.MustRegister(&FritzboxCollector{}) + if *flag_test { + for _, v := range upnp.Values { + res, err := v.Query(*flag_gateway_address, uint16(*flag_gateway_port)) + if err != nil { + panic(err) + } + fmt.Printf("%s: %d\n", v.ShortName, res) + } + return + } + + metrics := make([]UpnpMetric, len(upnp.Values)) + for _, v := range upnp.Values { + metrics = append(metrics, NewUpnpMetric(v)) + } + + prometheus.MustRegister(&FritzboxCollector{ + *flag_gateway_address, + uint16(*flag_gateway_port), + metrics, + }) // Since we are dealing with custom Collector implementations, it might // be a good idea to enable the collect checks in the registry. prometheus.EnableCollectChecks(true)