mirror of
https://github.com/davidallendj/magellan.git
synced 2025-12-20 11:37:01 -07:00
Print data in SMD format
This commit is contained in:
parent
a9d16f50cf
commit
266b3df0b7
2 changed files with 100 additions and 10 deletions
|
|
@ -10,6 +10,44 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func transformToSMDFormat(inventory *pdu.PDUInventory) []map[string]any {
|
||||||
|
smdRecords := make([]map[string]any, 0)
|
||||||
|
|
||||||
|
rtsHostname := fmt.Sprintf("%s-rts:8083", inventory.Hostname)
|
||||||
|
pduBank := "B"
|
||||||
|
|
||||||
|
for _, outlet := range inventory.Outlets {
|
||||||
|
smdID := fmt.Sprintf("%sp1v%s", inventory.Hostname, outlet.ID)
|
||||||
|
odataID := fmt.Sprintf("/redfish/v1/PowerEquipment/RackPDUs/%s/Outlets/%s", pduBank, outlet.ID)
|
||||||
|
redfishURL := fmt.Sprintf("%s%s", rtsHostname, odataID)
|
||||||
|
powerControlTarget := fmt.Sprintf("%s/Actions/Outlet.PowerControl", odataID)
|
||||||
|
|
||||||
|
record := map[string]any{
|
||||||
|
"ID": smdID,
|
||||||
|
"Type": "CabinetPDUPowerConnector",
|
||||||
|
"RedfishType": "Outlet",
|
||||||
|
"RedfishSubtype": "Cx",
|
||||||
|
"OdataID": odataID,
|
||||||
|
"RedfishEndpointID": inventory.Hostname,
|
||||||
|
"Enabled": true,
|
||||||
|
"RedfishEndpointFQDN": rtsHostname,
|
||||||
|
"RedfishURL": redfishURL,
|
||||||
|
"ComponentEndpointType": "ComponentEndpointOutlet",
|
||||||
|
"RedfishOutletInfo": map[string]any{
|
||||||
|
"Name": outlet.Name, // Directly from inventory
|
||||||
|
"Actions": map[string]any{
|
||||||
|
"#Outlet.PowerControl": map[string]any{
|
||||||
|
"PowerState@Redfish.AllowableValues": []string{"On", "Off"},
|
||||||
|
"target": powerControlTarget,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
smdRecords = append(smdRecords, record)
|
||||||
|
}
|
||||||
|
return smdRecords
|
||||||
|
}
|
||||||
|
|
||||||
var pduCollectCmd = &cobra.Command{
|
var pduCollectCmd = &cobra.Command{
|
||||||
Use: "collect [hosts...]",
|
Use: "collect [hosts...]",
|
||||||
Short: "Collect inventory from JAWS-based PDUs",
|
Short: "Collect inventory from JAWS-based PDUs",
|
||||||
|
|
@ -25,7 +63,8 @@ var pduCollectCmd = &cobra.Command{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
collection := make([]*pdu.PDUInventory, 0)
|
allSmdRecords := make([]map[string]any, 0)
|
||||||
|
|
||||||
for _, host := range args {
|
for _, host := range args {
|
||||||
log.Info().Msgf("Collecting from PDU: %s", host)
|
log.Info().Msgf("Collecting from PDU: %s", host)
|
||||||
config := jaws.CrawlerConfig{
|
config := jaws.CrawlerConfig{
|
||||||
|
|
@ -40,12 +79,15 @@ var pduCollectCmd = &cobra.Command{
|
||||||
log.Error().Err(err).Msgf("failed to crawl PDU %s", host)
|
log.Error().Err(err).Msgf("failed to crawl PDU %s", host)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
collection = append(collection, inventory)
|
|
||||||
|
smdRecords := transformToSMDFormat(inventory)
|
||||||
|
|
||||||
|
allSmdRecords = append(allSmdRecords, smdRecords...)
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err := json.MarshalIndent(collection, "", " ")
|
output, err := json.MarshalIndent(allSmdRecords, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("failed to marshal PDU collection to JSON")
|
log.Error().Err(err).Msgf("failed to marshal SMD records to JSON")
|
||||||
}
|
}
|
||||||
fmt.Println(string(output))
|
fmt.Println(string(output))
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,45 @@ type JawsOutlet struct {
|
||||||
ActivePower int `json:"active_power"`
|
ActivePower int `json:"active_power"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JawsSystemInfo is the struct to unmarshal /jaws/config/info/system
|
||||||
|
type JawsSystemInfo struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
SerialNumber string `json:"serial_number"`
|
||||||
|
FirmwareVersion string `json:"firmware_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSystemInfo queries the PDU for overall system details.
|
||||||
|
func getSystemInfo(client *http.Client, config CrawlerConfig) (*JawsSystemInfo, error) {
|
||||||
|
targetURL := fmt.Sprintf("https://%s/jaws/config/info/system", config.URI)
|
||||||
|
req, err := http.NewRequest("GET", targetURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create system info request: %w", err)
|
||||||
|
}
|
||||||
|
req.SetBasicAuth(config.Username, config.Password)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to execute system info request: %w", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("received non-200 status code from system info endpoint: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read system info response body: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemInfo JawsSystemInfo
|
||||||
|
if err := json.Unmarshal(body, &systemInfo); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal system info: %w", err)
|
||||||
|
}
|
||||||
|
return &systemInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CrawlPDU connects to a single JAWS PDU and collects its full inventory.
|
||||||
func CrawlPDU(config CrawlerConfig) (*pdu.PDUInventory, error) {
|
func CrawlPDU(config CrawlerConfig) (*pdu.PDUInventory, error) {
|
||||||
transport := &http.Transport{
|
transport := &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.Insecure},
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.Insecure},
|
||||||
|
|
@ -43,36 +82,45 @@ func CrawlPDU(config CrawlerConfig) (*pdu.PDUInventory, error) {
|
||||||
Hostname: config.URI,
|
Hostname: config.URI,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
systemInfo, err := getSystemInfo(client, config)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msgf("could not retrieve system info for %s, proceeding without it", config.URI)
|
||||||
|
} else if systemInfo != nil {
|
||||||
|
inventory.Model = systemInfo.Model
|
||||||
|
inventory.SerialNumber = systemInfo.SerialNumber
|
||||||
|
inventory.FirmwareVersion = systemInfo.FirmwareVersion
|
||||||
|
log.Info().Msgf("successfully collected system info from %s", config.URI)
|
||||||
|
}
|
||||||
|
|
||||||
targetURL := fmt.Sprintf("https://%s/jaws/monitor/outlets", config.URI)
|
targetURL := fmt.Sprintf("https://%s/jaws/monitor/outlets", config.URI)
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", targetURL, nil)
|
req, err := http.NewRequest("GET", targetURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to create new HTTP request")
|
log.Error().Err(err).Msg("failed to create new HTTP request for outlets")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.SetBasicAuth(config.Username, config.Password)
|
req.SetBasicAuth(config.Username, config.Password)
|
||||||
|
|
||||||
log.Debug().Msgf("querying JAWS endpoint: %s", targetURL)
|
log.Debug().Msgf("querying JAWS endpoint: %s", targetURL)
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msgf("failed to execute request to JAWS endpoint %s", targetURL)
|
log.Error().Err(err).Msgf("failed to execute request to JAWS outlets endpoint %s", targetURL)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
err := fmt.Errorf("received non-200 status code: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
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")
|
log.Error().Err(err).Str("url", targetURL).Msg("bad response from PDU")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to read response body")
|
log.Error().Err(err).Msg("failed to read outlets response body")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debug().RawJSON("response_body", body).Msg("received response from JAWS")
|
log.Debug().RawJSON("response_body", body).Msg("received response from JAWS outlets")
|
||||||
|
|
||||||
var rawOutlets []JawsOutlet
|
var rawOutlets []JawsOutlet
|
||||||
if err := json.Unmarshal(body, &rawOutlets); err != nil {
|
if err := json.Unmarshal(body, &rawOutlets); err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue