This commit is contained in:
@@ -46,6 +46,7 @@ func main() {
|
|||||||
|
|
||||||
ctx := context.Background()
|
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)
|
log.Printf("Logging in to %s\n", config.Endpoint)
|
||||||
if _, err := ucsClient.AaaLogin(ctx); err != nil {
|
if _, err := ucsClient.AaaLogin(ctx); err != nil {
|
||||||
log.Fatalf("Unable to login: %s\n", err)
|
log.Fatalf("Unable to login: %s\n", err)
|
||||||
|
77
internal/exporters/UcsmBlade.go
Normal file
77
internal/exporters/UcsmBlade.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/dnaeon/go-ucs/api"
|
"github.com/dnaeon/go-ucs/api"
|
||||||
"github.com/dnaeon/go-ucs/mo"
|
"github.com/dnaeon/go-ucs/mo"
|
||||||
@@ -13,8 +14,14 @@ import (
|
|||||||
type UcsmTemperaturesCollector struct {
|
type UcsmTemperaturesCollector struct {
|
||||||
ucsClient *api.Client
|
ucsClient *api.Client
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
metrics []*prometheus.Desc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var metrics []prometheus.Desc
|
||||||
|
|
||||||
|
//var ucsClient *api.Client
|
||||||
|
//var ctx context.Context
|
||||||
|
|
||||||
/*
|
/*
|
||||||
type ComputeMbTempStats struct {
|
type ComputeMbTempStats struct {
|
||||||
XMLName xml.Name `xml:"computeMbTempStats"`
|
XMLName xml.Name `xml:"computeMbTempStats"`
|
||||||
@@ -24,91 +31,106 @@ type UcsmTemperaturesCollector struct {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
func NewUcsmTemperatureCollector(client *api.Client, ctx context.Context) *UcsmTemperaturesCollector {
|
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{
|
return &UcsmTemperaturesCollector{
|
||||||
ucsClient: client,
|
ucsClient: client,
|
||||||
ctx: ctx,
|
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
|
// Describe prometheus describe
|
||||||
func (u *UcsmTemperaturesCollector) Describe(ch chan<- *prometheus.Desc) {
|
func (u *UcsmTemperaturesCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
log.Printf("Running Describe for UcsmTemperaturesCollector\n")
|
log.Printf("Running Describe for UcsmTemperaturesCollector\n")
|
||||||
ch <- prometheus.NewDesc("ucsm_temperature_sensor",
|
|
||||||
"UCSM temperature sensor for motherboard/cpu/psu",
|
// Describe the metrics and send them on the channel
|
||||||
[]string{"server", "component", "description"}, nil,
|
for _, desc := range u.metrics {
|
||||||
)
|
ch <- desc
|
||||||
ch <- prometheus.NewDesc("ucsm_temperature_sensor",
|
}
|
||||||
"UCSM temperature sensor for motherboard/cpu/psu",
|
|
||||||
[]string{"server", "component", "description"}, nil,
|
/*
|
||||||
)
|
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
|
// Collect prometheus collect
|
||||||
func (u *UcsmTemperaturesCollector) Collect(ch chan<- prometheus.Metric) {
|
func (u *UcsmTemperaturesCollector) Collect(ch chan<- prometheus.Metric) {
|
||||||
// The type into which we unmarshal the result data
|
// The type into which we unmarshal the result data
|
||||||
type blades struct {
|
|
||||||
XMLName xml.Name
|
|
||||||
Blades []mo.ComputeBlade `xml:"computeBlade"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type temps struct {
|
type temps struct {
|
||||||
XMLName xml.Name
|
XMLName xml.Name
|
||||||
Temperatures []mo.ComputeMbTempStats `xml:"computeMbTempStats"`
|
Temperatures []mo.ComputeMbTempStats `xml:"computeMbTempStats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
bladeRequest := api.ConfigResolveClassRequest{
|
tempRequest := api.ConfigResolveClassRequest{
|
||||||
Cookie: u.ucsClient.Cookie,
|
Cookie: u.ucsClient.Cookie,
|
||||||
ClassId: "computeBlade",
|
ClassId: "computeMbTempStats",
|
||||||
InHierarchical: "false",
|
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 {
|
if err := u.ucsClient.ConfigResolveClass(u.ctx, tempRequest, &boardTempList); err != nil {
|
||||||
log.Fatalf("Unable to retrieve `computeBlade` managed object: %s", err)
|
log.Fatalf("Unable to retrieve `computeMbTempStats` managed object: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Retrieved %d compute blades\n", len(bladeList.Blades))
|
log.Printf("Retrieved temps for %d compute blades\n", len(boardTempList.Temperatures))
|
||||||
for _, blade := range bladeList.Blades {
|
for _, temps := range boardTempList.Temperatures {
|
||||||
log.Printf("%s:\n", blade.Dn)
|
// 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/"
|
log.Printf("Temps for blade %s:\n", bladeDn)
|
||||||
//boardDn := blade.Dn + "/board/"
|
log.Printf("Front Temperature: %v\n", temps.FmTempSenIo)
|
||||||
|
log.Printf("Rear Temperature: %v\n", temps.FmTempSenRear)
|
||||||
|
|
||||||
log.Printf("%s:\n", boardDn)
|
// TODO - work out a better way of running this twice, once for each temperature sensor
|
||||||
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)
|
|
||||||
|
|
||||||
filter := api.FilterWildcard{
|
// Collect the actual metric values and send them on the channel
|
||||||
FilterProperty: api.FilterProperty{
|
for _, desc := range u.metrics {
|
||||||
Class: "computeMbTempStats",
|
|
||||||
Property: "dn",
|
|
||||||
Value: boardDn,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
log.Printf("Filter: %v\n", filter)
|
|
||||||
|
|
||||||
tempReq := api.ConfigResolveClassRequest{
|
// populate the labels array
|
||||||
Cookie: u.ucsClient.Cookie,
|
//labelValues := []string{bladeDn, temps.Dn, "motherboard_rear_temperature"}
|
||||||
ClassId: "computeMbTempStats",
|
|
||||||
InHierarchical: "false",
|
// generate the metric
|
||||||
InFilter: filter,
|
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
|
//ch <- prometheus.MustNewConstMetric()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -186,8 +185,8 @@ func (c *Client) doRequest(ctx context.Context, in, out interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("doRequest sending following XML request:")
|
//fmt.Println("doRequest sending following XML request:")
|
||||||
fmt.Println(string(data[:]))
|
//fmt.Println(string(data[:]))
|
||||||
|
|
||||||
r, err := http.NewRequest("POST", c.apiUrl.String(), bytes.NewBuffer(data))
|
r, err := http.NewRequest("POST", c.apiUrl.String(), bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -209,8 +208,8 @@ func (c *Client) doRequest(ctx context.Context, in, out interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("doRequest received following response:")
|
//fmt.Println("doRequest received following response:")
|
||||||
fmt.Println(string(body[:]))
|
//fmt.Println(string(body[:]))
|
||||||
|
|
||||||
return xml.Unmarshal(body, &out)
|
return xml.Unmarshal(body, &out)
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,7 @@ import (
|
|||||||
// As of now XML marshaling in Go always uses start and end tags,
|
// As of now XML marshaling in Go always uses start and end tags,
|
||||||
// which results in XML elements like the one below.
|
// which results in XML elements like the one below.
|
||||||
//
|
//
|
||||||
// <Person name="me"></Person>
|
// <Person name="me"></Person>
|
||||||
//
|
//
|
||||||
// Above XML elements cannot be parsed by the remote Cisco UCS API endpoint,
|
// 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.
|
// 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.
|
// hopefully one day make into the language as a feature.
|
||||||
//
|
//
|
||||||
// https://groups.google.com/forum/#!topic/golang-nuts/guG6iOCRu08
|
// 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) {
|
func xmlMarshalWithSelfClosingTags(in interface{}) ([]byte, error) {
|
||||||
data, err := xml.Marshal(in)
|
data, err := xml.Marshal(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
re := regexp.MustCompile(`<([^/][\w\s\"\=\-\/]*)>\s*<(\/\w*)>`)
|
re := regexp.MustCompile(`<([^/][\w\s\"\=\-\/\^]*)>\s*<(\/\w*)>`)
|
||||||
newData := re.ReplaceAllString(string(data), "<$1/>")
|
newData := re.ReplaceAllString(string(data), "<$1/>")
|
||||||
|
|
||||||
return []byte(newData), nil
|
return []byte(newData), nil
|
||||||
|
@@ -263,8 +263,8 @@ type ComputeBoard struct {
|
|||||||
type ComputeMbTempStats struct {
|
type ComputeMbTempStats struct {
|
||||||
XMLName xml.Name `xml:"computeMbTempStats"`
|
XMLName xml.Name `xml:"computeMbTempStats"`
|
||||||
Dn string `xml:"dn,attr,omitempty"`
|
Dn string `xml:"dn,attr,omitempty"`
|
||||||
FmTempSenIo string `xml:"fmTempSenIo,attr,omitempty"`
|
FmTempSenIo float64 `xml:"fmTempSenIo,attr,omitempty"`
|
||||||
FmTempSenRear string `xml:"fmTempSenRear,attr,omitempty"`
|
FmTempSenRear float64 `xml:"fmTempSenRear,attr,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MemoryArray represents an array of memory units.
|
// MemoryArray represents an array of memory units.
|
||||||
|
Reference in New Issue
Block a user