magellan/pkg/jaws/pdu-crawler.go

96 lines
2.6 KiB
Go

package jaws
import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/davidallendj/magellan/pkg/pdu"
"github.com/rs/zerolog/log"
)
type CrawlerConfig struct {
URI string
Username string
Password string
Insecure bool
Timeout time.Duration
}
// JawsOutlet represents the structure of a single outlet object
type JawsOutlet struct {
ID string `json:"id"`
Name string `json:"name"`
State string `json:"state"`
SocketType string `json:"socket_type"`
Current float32 `json:"current"`
Voltage float32 `json:"voltage"`
ActivePower int `json:"active_power"`
}
// CrawlPDU connects to a single JAWS PDU and collects its full inventory.
func CrawlPDU(config CrawlerConfig) (*pdu.PDUInventory, error) {
transport := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.Insecure},
}
client := &http.Client{
Timeout: config.Timeout,
Transport: transport,
}
inventory := &pdu.PDUInventory{
Hostname: config.URI,
}
targetURL := fmt.Sprintf("https://%s/jaws/monitor/outlets", config.URI)
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
log.Error().Err(err).Msg("failed to create new HTTP request for outlets")
return nil, err
}
req.SetBasicAuth(config.Username, config.Password)
log.Debug().Msgf("querying JAWS endpoint: %s", targetURL)
resp, err := client.Do(req)
if err != nil {
log.Error().Err(err).Msgf("failed to execute request to JAWS outlets endpoint %s", targetURL)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
err := fmt.Errorf("received non-200 status code from outlets endpoint: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
log.Error().Err(err).Str("url", targetURL).Msg("bad response from PDU")
return nil, err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msg("failed to read outlets response body")
return nil, err
}
log.Debug().RawJSON("response_body", body).Msg("received response from JAWS outlets")
var rawOutlets []JawsOutlet
if err := json.Unmarshal(body, &rawOutlets); err != nil {
log.Error().Err(err).Msg("failed to unmarshal JAWS outlet data")
return nil, err
}
for _, rawOutlet := range rawOutlets {
outlet := pdu.PDUOutlet{
ID: rawOutlet.ID,
Name: rawOutlet.Name,
PowerState: rawOutlet.State,
SocketType: rawOutlet.SocketType,
}
inventory.Outlets = append(inventory.Outlets, outlet)
}
log.Info().Msgf("successfully collected inventory for %d outlets from %s", len(inventory.Outlets), config.URI)
return inventory, nil
}