Nils Decker
9 years ago
commit
cb4aa46aa7
2 changed files with 251 additions and 0 deletions
@ -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) |
||||
|
} |
@ -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…
Reference in new issue