diff --git a/collector.go b/collector.go index b75984e..cc068f6 100644 --- a/collector.go +++ b/collector.go @@ -28,6 +28,8 @@ import ( "time" "unicode" + "github.com/magna5/sansay_exporter/models" + "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" "github.com/hooklift/gowsdl/soap" @@ -149,7 +151,7 @@ func (c collector) Describe(ch chan<- *prometheus.Desc) { // Collect implements Prometheus.Collector. func (c collector) Collect(ch chan<- prometheus.Metric) { - paths := []string{"realtime", "resource", "media_server"} + paths := []string{"stats/realtime", "stats/resource", "stats/media_server", "download/resource"} var wg sync.WaitGroup var err error start := time.Now() @@ -168,6 +170,9 @@ func (c collector) Collect(ch chan<- prometheus.Metric) { case XBMediaServerRealTimeStatList: err = nil c.processMediaCollection(ch, obj) + case models.XBResourceList: + err = nil + c.processXBResourceList(ch, obj) case error: err = obj default: @@ -208,6 +213,17 @@ func (c collector) processMediaCollection(ch chan<- prometheus.Metric, media XBM } } +// processXBResourceList creates the metrics for the resource configurations. +func (c collector) processXBResourceList(ch chan<- prometheus.Metric, resources models.XBResourceList) { + labels := []string{"trunkgroup", "alias"} + var labelValues []string + for _, resource := range resources.XBResource { + labelValues = []string{resource.TrunkId, resource.Name} + addLabeledMetric(ch, "config_trunk_sessions_max", resource.Capacity, labels, labelValues) + addLabeledMetric(ch, "config_trunk_cps_max", resource.CpsLimit, labels, labelValues) + } +} + func (c collector) processCollection(ch chan<- prometheus.Metric, sansay Sansay) { for _, table := range sansay.Database.Table { var direction string @@ -278,6 +294,7 @@ func ScrapeTarget(c collector, path string, result chan<- interface{}, wg *sync. var obj interface{} var sansay Sansay var media XBMediaServerRealTimeStatList + var resourceList models.XBResourceList var body []byte var err error @@ -299,12 +316,15 @@ func ScrapeTarget(c collector, path string, result chan<- interface{}, wg *sync. if strings.HasSuffix(path, "media_server") { err = xml.Unmarshal(body, &media) obj = media + } else if strings.HasSuffix(path, "download/resource") { + err = xml.Unmarshal(body, &resourceList) + obj = resourceList } else { err = xml.Unmarshal(body, &sansay) obj = sansay } if err != nil { - level.Error(logger).Log("msg", "Error parsing XML", "err", err) + level.Error(logger).Log("msg", "Error parsing XML", "path", path, "err", err) result <- err wg.Done() return @@ -366,23 +386,50 @@ func callRestAPI(c collector, path string) ([]byte, error) { // callSoapAPI makes a SOAP call to the Sansay SBC -- used for older OS versions func callSoapAPI(c collector, path string) ([]byte, error) { + var err error + var response []byte + var statName string + + // Determine the stat name by splitting the path + paths := strings.Split(path, "/") + if len(paths) == 1 { + statName = paths[0] + } else { + statName = paths[len(paths)-1] + } + target := fmt.Sprintf("%s%s", c.target, "/SSConfig/SansayWS") if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") { target = "http://" + target } client := soap.NewClient(target, soap.WithTLS(&tls.Config{InsecureSkipVerify: true})) service := NewSansayWS(client) - params := &RealTimeStatsParams{ - Username: c.username, - Password: c.password, - StatName: path, + if strings.HasSuffix(path, "download/resource") { + params := &DownloadParams{ + Username: c.username, + Password: c.password, + Page: 0, + Table: "resource", + } + + if reply, err := service.DoDownloadXmlFile(params); err == nil { + response = []byte(reply.Xmlfile) + } + } else { + params := &RealTimeStatsParams{ + Username: c.username, + Password: c.password, + StatName: statName, + } + if reply, err := service.DoRealTimeStats(params); err == nil { + response = []byte(reply.Xmlfile) + } } - reply, err := service.DoRealTimeStats(params) if err != nil { level.Error(c.logger).Log("msg", "Error calling SOAP API", "err", err) return nil, err } - return []byte(reply.Xmlfile), nil + return response, nil } func addMetric(ch chan<- prometheus.Metric, name string, value string) error { diff --git a/go.sum b/go.sum index 8bb261e..cbe35b8 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= diff --git a/main.go b/main.go index bb4687c..205ccf5 100644 --- a/main.go +++ b/main.go @@ -32,7 +32,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" ) -const targetPath = "/SSConfig/webresources/stats/" +const targetPath = "/SSConfig/webresources/" var Version = "dev" diff --git a/models/xbresource.go b/models/xbresource.go new file mode 100644 index 0000000..d2e0334 --- /dev/null +++ b/models/xbresource.go @@ -0,0 +1,130 @@ +package models + +import "encoding/xml" + +// XBResourceList represents the DownloadXML data for a resource +type XBResourceList struct { + XMLName xml.Name `xml:"XBResourceList"` + Text string `xml:",chardata"` + XBResource []struct { + Text string `xml:",chardata"` + Protocol string `xml:"protocol"` + TypeSIPgw struct { + Text string `xml:",chardata"` + PortAddress string `xml:"portAddress"` + ServiceState string `xml:"serviceState"` + Direction string `xml:"direction"` + NAT string `xml:"NAT"` + AllowDirectMedia string `xml:"allowDirectMedia"` + SipProfileIndex string `xml:"sipProfileIndex"` + OptionPoll string `xml:"optionPoll"` + AuthorizedRPS string `xml:"authorizedRPS"` + UnauthorizedRPS string `xml:"unauthorizedRPS"` + } `xml:"typeSIPgw"` + Name string `xml:"name"` + CompanyName string `xml:"companyName"` + TrunkId string `xml:"trunkId"` + SgId string `xml:"sgId"` + Capacity string `xml:"capacity"` + CpsLimit string `xml:"cpsLimit"` + Node struct { + Text string `xml:",chardata"` + Fqdn string `xml:"fqdn"` + Netmask string `xml:"netmask"` + Capacity string `xml:"capacity"` + CpsLimit string `xml:"cpsLimit"` + CacProfileId string `xml:"cacProfileId"` + } `xml:"node"` + Rtid string `xml:"rtid"` + Ingress1 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"ingress1"` + Ingress2 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"ingress2"` + Egress1 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"egress1"` + Egress2 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"egress2"` + OutboundANI string `xml:"outboundANI"` + TechPrefix string `xml:"techPrefix"` + RnIngress1 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"rnIngress1"` + RnIngress2 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"rnIngress2"` + RnEgress1 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"rnEgress1"` + RnEgress2 struct { + Text string `xml:",chardata"` + Match string `xml:"match"` + Action1 string `xml:"action1"` + Digits1 string `xml:"digits1"` + Action2 string `xml:"action2"` + Digits2 string `xml:"digits2"` + } `xml:"rnEgress2"` + CodecPolicy string `xml:"codecPolicy"` + GroupPolicy string `xml:"groupPolicy"` + Dtid string `xml:"dtid"` + T38 string `xml:"t38"` + Rfc2833 string `xml:"rfc2833"` + PayloadType string `xml:"payloadType"` + Tos string `xml:"tos"` + SvcPortIndex string `xml:"svcPortIndex"` + RadiusAuthGrpIndex string `xml:"radiusAuthGrpIndex"` + RadiusAcctGrpIndex string `xml:"radiusAcctGrpIndex"` + LnpGrpIndex string `xml:"lnpGrpIndex"` + TeleblockGrpIndex string `xml:"teleblockGrpIndex"` + CnamGrpIndex string `xml:"cnamGrpIndex"` + ErsGrpIndex string `xml:"ersGrpIndex"` + MaxCallDuration string `xml:"maxCallDuration"` + MinCallDuration string `xml:"minCallDuration"` + NoAnswerTimeout string `xml:"noAnswerTimeout"` + NoRingTimeout string `xml:"noRingTimeout"` + CauseCodeProfile string `xml:"causeCodeProfile"` + StopRouteProfile string `xml:"stopRouteProfile"` + PaiAction string `xml:"paiAction"` + PaiString string `xml:"paiString"` + InheritedGenericHeader string `xml:"inheritedGenericHeader"` + OutSMCProfileId string `xml:"outSMCProfileId"` + } `xml:"XBResource"` +}