diff --git a/cmd/server/main.go b/cmd/server/main.go
index cff2762..a6459d8 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -46,6 +46,7 @@ func main() {
ctx := context.Background()
+ log.Printf("Starting execution. Built on %s from sha1 %s\n", buildTime, sha1ver)
log.Printf("Logging in to %s\n", config.Endpoint)
if _, err := ucsClient.AaaLogin(ctx); err != nil {
log.Fatalf("Unable to login: %s\n", err)
diff --git a/internal/exporters/UcsmBlade.go b/internal/exporters/UcsmBlade.go
new file mode 100644
index 0000000..9a0c73d
--- /dev/null
+++ b/internal/exporters/UcsmBlade.go
@@ -0,0 +1,77 @@
+package exporters
+
+import (
+ "context"
+ "encoding/xml"
+ "log"
+
+ "github.com/dnaeon/go-ucs/api"
+ "github.com/dnaeon/go-ucs/mo"
+ "github.com/prometheus/client_golang/prometheus"
+)
+
+type UcsmBladeCollector struct {
+ ucsClient *api.Client
+ ctx context.Context
+}
+
+/*
+ type ComputeMbTempStats struct {
+ XMLName xml.Name `xml:"computeMbTempStats"`
+ Dn string `xml:"dn,attr,omitempty"`
+ FmTempSenIo string `xml:"fmTempSenIo,attr,omitempty"`
+ FmTempSenRear string `xml:"fmTempSenRear,attr,omitempty"`
+ }
+*/
+func NewUcsmBladeCollector(client *api.Client, ctx context.Context) *UcsmTemperaturesCollector {
+ return &UcsmTemperaturesCollector{
+ ucsClient: client,
+ ctx: ctx,
+ }
+}
+
+// Describe prometheus describe
+func (u *UcsmBladeCollector) Describe(ch chan<- *prometheus.Desc) {
+ log.Printf("Running Describe for UcsmTemperaturesCollector\n")
+ ch <- prometheus.NewDesc("ucsm_temperature_sensor",
+ "UCSM temperature sensor for motherboard/cpu/psu",
+ []string{"server", "component", "description"}, nil,
+ )
+ ch <- prometheus.NewDesc("ucsm_temperature_sensor",
+ "UCSM temperature sensor for motherboard/cpu/psu",
+ []string{"server", "component", "description"}, nil,
+ )
+}
+
+// Collect prometheus collect
+func (u *UcsmBladeCollector) Collect(ch chan<- prometheus.Metric) {
+ // The type into which we unmarshal the result data
+ type blades struct {
+ XMLName xml.Name
+ Blades []mo.ComputeBlade `xml:"computeBlade"`
+ }
+
+ bladeRequest := api.ConfigResolveClassRequest{
+ Cookie: u.ucsClient.Cookie,
+ ClassId: "computeBlade",
+ InHierarchical: "false",
+ }
+
+ var bladeList blades
+
+ log.Println("Retrieving managed objects with class `computeBlade`")
+
+ if err := u.ucsClient.ConfigResolveClass(u.ctx, bladeRequest, &bladeList); err != nil {
+ log.Fatalf("Unable to retrieve `computeBlade` managed object: %s", err)
+ }
+
+ log.Printf("Retrieved %d compute blades\n", len(bladeList.Blades))
+ for _, blade := range bladeList.Blades {
+ log.Printf("%s:\n", blade.Dn)
+ log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus)
+ log.Printf("\tTotal Memory: %d\n", blade.TotalMemory)
+ log.Printf("\tModel: %s\n", blade.Model)
+ log.Printf("\tVendor: %s\n", blade.Vendor)
+ }
+
+}
diff --git a/internal/exporters/UcsmTemperatures.go b/internal/exporters/UcsmTemperatures.go
index ac04c31..05dcfeb 100644
--- a/internal/exporters/UcsmTemperatures.go
+++ b/internal/exporters/UcsmTemperatures.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/xml"
"log"
+ "strings"
"github.com/dnaeon/go-ucs/api"
"github.com/dnaeon/go-ucs/mo"
@@ -13,8 +14,14 @@ import (
type UcsmTemperaturesCollector struct {
ucsClient *api.Client
ctx context.Context
+ metrics []*prometheus.Desc
}
+var metrics []prometheus.Desc
+
+//var ucsClient *api.Client
+//var ctx context.Context
+
/*
type ComputeMbTempStats struct {
XMLName xml.Name `xml:"computeMbTempStats"`
@@ -24,91 +31,106 @@ type UcsmTemperaturesCollector struct {
}
*/
func NewUcsmTemperatureCollector(client *api.Client, ctx context.Context) *UcsmTemperaturesCollector {
+
+ /*
+ ucsClient = client
+ ctx = ctx
+ */
+ /*
+ return &main.Collector{
+ UcsmTemperature: prometheus.NewDesc("ucsm_temperature_sensor",
+ "UCSM temperature sensor for motherboard/cpu/psu",
+ []string{"server", "component", "description"}, nil,
+ ),
+ }
+ */
+
return &UcsmTemperaturesCollector{
ucsClient: client,
ctx: ctx,
+ metrics: []*prometheus.Desc{
+ prometheus.NewDesc(
+ "ucsm_temperature_sensor",
+ "UCSM temperature sensor for motherboard/cpu/psu",
+ []string{"server", "component", "description"}, nil,
+ ),
+ },
}
+
}
// Describe prometheus describe
func (u *UcsmTemperaturesCollector) Describe(ch chan<- *prometheus.Desc) {
log.Printf("Running Describe for UcsmTemperaturesCollector\n")
- ch <- prometheus.NewDesc("ucsm_temperature_sensor",
- "UCSM temperature sensor for motherboard/cpu/psu",
- []string{"server", "component", "description"}, nil,
- )
- ch <- prometheus.NewDesc("ucsm_temperature_sensor",
- "UCSM temperature sensor for motherboard/cpu/psu",
- []string{"server", "component", "description"}, nil,
- )
+
+ // Describe the metrics and send them on the channel
+ for _, desc := range u.metrics {
+ ch <- desc
+ }
+
+ /*
+ metricDesc := prometheus.NewDesc("ucsm_temperature_sensor",
+ "UCSM temperature sensor for motherboard/cpu/psu",
+ []string{"server", "component", "description"}, nil)
+
+ fmt.Printf("metricDesc: %v\n", *metricDesc)
+
+ metrics = append(metrics, *metricDesc)
+ ch <- metricDesc
+ */
}
// Collect prometheus collect
func (u *UcsmTemperaturesCollector) Collect(ch chan<- prometheus.Metric) {
// The type into which we unmarshal the result data
- type blades struct {
- XMLName xml.Name
- Blades []mo.ComputeBlade `xml:"computeBlade"`
- }
-
type temps struct {
XMLName xml.Name
Temperatures []mo.ComputeMbTempStats `xml:"computeMbTempStats"`
}
- bladeRequest := api.ConfigResolveClassRequest{
+ tempRequest := api.ConfigResolveClassRequest{
Cookie: u.ucsClient.Cookie,
- ClassId: "computeBlade",
+ ClassId: "computeMbTempStats",
InHierarchical: "false",
}
- var bladeList blades
+ var boardTempList temps
- log.Println("Retrieving managed objects with class `computeBlade`")
+ log.Println("Retrieving managed objects with class `computeMbTempStats`")
- if err := u.ucsClient.ConfigResolveClass(u.ctx, bladeRequest, &bladeList); err != nil {
- log.Fatalf("Unable to retrieve `computeBlade` managed object: %s", err)
+ if err := u.ucsClient.ConfigResolveClass(u.ctx, tempRequest, &boardTempList); err != nil {
+ log.Fatalf("Unable to retrieve `computeMbTempStats` managed object: %s", err)
}
- log.Printf("Retrieved %d compute blades\n", len(bladeList.Blades))
- for _, blade := range bladeList.Blades {
- log.Printf("%s:\n", blade.Dn)
+ log.Printf("Retrieved temps for %d compute blades\n", len(boardTempList.Temperatures))
+ for _, temps := range boardTempList.Temperatures {
+ // Substring the Dn for the temperatures to get the Dn for the parent blade itself
+ bladeDn := strings.Replace(temps.Dn, "/board/temp-stats", "", -1)
- boardDn := "^" + blade.Dn + "/board/"
- //boardDn := blade.Dn + "/board/"
+ log.Printf("Temps for blade %s:\n", bladeDn)
+ log.Printf("Front Temperature: %v\n", temps.FmTempSenIo)
+ log.Printf("Rear Temperature: %v\n", temps.FmTempSenRear)
- log.Printf("%s:\n", boardDn)
- log.Printf("\tNumber of CPUs: %d\n", blade.NumOfCpus)
- log.Printf("\tTotal Memory: %d\n", blade.TotalMemory)
- log.Printf("\tModel: %s\n", blade.Model)
- log.Printf("\tVendor: %s\n", blade.Vendor)
- //log.Printf("\tThermal: %v\n", blade.ComputeBoard.Thermal)
+ // TODO - work out a better way of running this twice, once for each temperature sensor
- filter := api.FilterWildcard{
- FilterProperty: api.FilterProperty{
- Class: "computeMbTempStats",
- Property: "dn",
- Value: boardDn,
- },
- }
- log.Printf("Filter: %v\n", filter)
+ // Collect the actual metric values and send them on the channel
+ for _, desc := range u.metrics {
- tempReq := api.ConfigResolveClassRequest{
- Cookie: u.ucsClient.Cookie,
- ClassId: "computeMbTempStats",
- InHierarchical: "false",
- InFilter: filter,
+ // populate the labels array
+ //labelValues := []string{bladeDn, temps.Dn, "motherboard_rear_temperature"}
+
+ // generate the metric
+ metric1 := prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, temps.FmTempSenIo, bladeDn, temps.Dn, "motherboard_rear_temperature")
+ // send the data
+ ch <- metric1
+
+ // generate the metric
+ metric2 := prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, temps.FmTempSenRear, bladeDn, temps.Dn, "motherboard_front_temperature")
+ // send the data
+ ch <- metric2
}
- var tempList temps
- log.Printf("Retrieving temperatures for this blade\n")
- /*
- if err := u.ucsClient.ConfigResolveClass(u.ctx, tempReq, &tempList); err != nil {
- log.Fatalf("Unable to retrieve `computeMbTempStats` managed object: %s", err)
- }
- */
- u.ucsClient.ConfigResolveClass(u.ctx, tempReq, &tempList)
- log.Printf("Front Temperature: %v\n", tempList)
+ //ch <- prometheus.MustNewConstMetric()
}
}
diff --git a/local/go-ucs/api/client.go b/local/go-ucs/api/client.go
index 1a415ef..bb969f9 100755
--- a/local/go-ucs/api/client.go
+++ b/local/go-ucs/api/client.go
@@ -4,7 +4,6 @@ import (
"bytes"
"context"
"encoding/xml"
- "fmt"
"io"
"net/http"
"net/url"
@@ -186,8 +185,8 @@ func (c *Client) doRequest(ctx context.Context, in, out interface{}) error {
return err
}
- fmt.Println("doRequest sending following XML request:")
- fmt.Println(string(data[:]))
+ //fmt.Println("doRequest sending following XML request:")
+ //fmt.Println(string(data[:]))
r, err := http.NewRequest("POST", c.apiUrl.String(), bytes.NewBuffer(data))
if err != nil {
@@ -209,8 +208,8 @@ func (c *Client) doRequest(ctx context.Context, in, out interface{}) error {
return err
}
- fmt.Println("doRequest received following response:")
- fmt.Println(string(body[:]))
+ //fmt.Println("doRequest received following response:")
+ //fmt.Println(string(body[:]))
return xml.Unmarshal(body, &out)
}
diff --git a/local/go-ucs/api/xml.go b/local/go-ucs/api/xml.go
index 08ce52c..6909383 100755
--- a/local/go-ucs/api/xml.go
+++ b/local/go-ucs/api/xml.go
@@ -23,7 +23,7 @@ import (
// As of now XML marshaling in Go always uses start and end tags,
// which results in XML elements like the one below.
//
-//
+//
//
// Above XML elements cannot be parsed by the remote Cisco UCS API endpoint,
// and such API calls result in parse error returned to the client.
@@ -41,13 +41,15 @@ import (
// hopefully one day make into the language as a feature.
//
// https://groups.google.com/forum/#!topic/golang-nuts/guG6iOCRu08
+//
+// Update by Nathan: update regex to also match caret when searching for tags to update
func xmlMarshalWithSelfClosingTags(in interface{}) ([]byte, error) {
data, err := xml.Marshal(in)
if err != nil {
return nil, err
}
- re := regexp.MustCompile(`<([^/][\w\s\"\=\-\/]*)>\s*<(\/\w*)>`)
+ re := regexp.MustCompile(`<([^/][\w\s\"\=\-\/\^]*)>\s*<(\/\w*)>`)
newData := re.ReplaceAllString(string(data), "<$1/>")
return []byte(newData), nil
diff --git a/local/go-ucs/mo/mo.go b/local/go-ucs/mo/mo.go
index b5b945d..dcebecb 100755
--- a/local/go-ucs/mo/mo.go
+++ b/local/go-ucs/mo/mo.go
@@ -263,8 +263,8 @@ type ComputeBoard struct {
type ComputeMbTempStats struct {
XMLName xml.Name `xml:"computeMbTempStats"`
Dn string `xml:"dn,attr,omitempty"`
- FmTempSenIo string `xml:"fmTempSenIo,attr,omitempty"`
- FmTempSenRear string `xml:"fmTempSenRear,attr,omitempty"`
+ FmTempSenIo float64 `xml:"fmTempSenIo,attr,omitempty"`
+ FmTempSenRear float64 `xml:"fmTempSenRear,attr,omitempty"`
}
// MemoryArray represents an array of memory units.