Browse Source

initial working version

pull/1/head
Nils Decker 9 years ago
commit
cb4aa46aa7
  1. 153
      main.go
  2. 98
      upnp.go

153
main.go

@ -0,0 +1,153 @@
package main
import (
"flag"
"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")
)
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",
}
WAN_Bytes_Received = UpnpValue{
path: "/igdupnp/control/WANCommonIFC1",
service: "WANCommonInterfaceConfig:1",
method: "GetAddonInfos",
ret_tag: "NewTotalBytesReceived",
}
WAN_Bytes_Sent = UpnpValue{
path: "/igdupnp/control/WANCommonIFC1",
service: "WANCommonInterfaceConfig:1",
method: "GetAddonInfos",
ret_tag: "NewTotalBytesSent",
}
)
type Metric struct {
UpnpValue
*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 Metric) Describe(ch chan<- *prometheus.Desc) {
ch <- m.Desc
}
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
}
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},
),
}
)
type FritzboxCollector struct {
}
func (fc *FritzboxCollector) Describe(ch chan<- *prometheus.Desc) {
packets_sent.Describe(ch)
packets_received.Describe(ch)
bytes_sent.Describe(ch)
bytes_received.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)
}
func main() {
flag.Parse()
prometheus.MustRegister(&FritzboxCollector{})
// 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)
http.Handle("/metrics", prometheus.Handler())
http.ListenAndServe(*flag_addr, nil)
}

98
upnp.go

@ -0,0 +1,98 @@
package main
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"net/http"
"strings"
)
const TEXT_XML = `text/xml; charset="utf-8"`
var (
ErrResultNotFound = errors.New("result not found")
ErrResultWithoutChardata = errors.New("result without chardata")
)
// curl "http://fritz.box:49000/igdupnp/control/WANIPConn1"
// -H "Content-Type: text/xml; charset="utf-8""
// -H "SoapAction:urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"
// -d "<?xml version='1.0' encoding='utf-8'?>
// <s:Envelope s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>
// <s:Body>
// <u:GetExternalIPAddress xmlns:u='urn:schemas-upnp-org:service:WANIPConnection:1' />
// </s:Body> </s:Envelope>"
type UpnpValue struct {
path string
service string
method string
ret_tag string
}
func (v *UpnpValue) Query(device string) (string, error) {
url := fmt.Sprintf("http://%s:49000%s", device, v.path)
bodystr := fmt.Sprintf(`
<?xml version='1.0' encoding='utf-8'?>
<s:Envelope s:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/' xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>
<s:Body>
<u:%s xmlns:u='urn:schemas-upnp-org:service:%s' />
</s:Body>
</s:Envelope>
`, v.method, v.service)
body := strings.NewReader(bodystr)
req, err := http.NewRequest("POST", url, body)
if err != nil {
return "", err
}
action := fmt.Sprintf("urn:schemas-upnp-org:service:%s#%s", v.service, v.method)
req.Header["Content-Type"] = []string{TEXT_XML}
req.Header["SoapAction"] = []string{action}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
data := new(bytes.Buffer)
data.ReadFrom(resp.Body)
// fmt.Printf(data.String())
dec := xml.NewDecoder(data)
for {
t, err := dec.Token()
if err == io.EOF {
return "", ErrResultNotFound
}
if err != nil {
return "", err
}
if se, ok := t.(xml.StartElement); ok {
if se.Name.Local == v.ret_tag {
t2, err := dec.Token()
if err != nil {
return "", err
}
data, ok := t2.(xml.CharData)
if !ok {
return "", ErrResultWithoutChardata
}
return string(data), nil
}
}
}
}
Loading…
Cancel
Save