Merge pull request #104 from OpenCHAMI/power-control-output

Add Output Needed for Power Control
This commit is contained in:
David Allen 2025-06-25 11:51:26 -06:00 committed by GitHub
commit db45ca631a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -59,6 +59,18 @@ type Manager struct {
EthernetInterfaces []EthernetInterface `json:"ethernet_interfaces,omitempty"`
}
type Links struct {
Chassis []string `json:"chassis,omitempty"`
Managers []string `json:"managers,omitempty"`
}
type Power struct {
State string `json:"state,omitempty"`
Mode string `json:"mode,omitempty"`
RestorePolicy string `json:"restore_policy"`
PowerControlIDs []string `json:"power_control_ids`
}
type InventoryDetail struct {
URI string `json:"uri,omitempty"` // URI of the BMC
UUID string `json:"uuid,omitempty"` // UUID of Node
@ -70,7 +82,8 @@ type InventoryDetail struct {
BiosVersion string `json:"bios_version,omitempty"` // Version of the BIOS
EthernetInterfaces []EthernetInterface `json:"ethernet_interfaces,omitempty"` // Ethernet interfaces of the Node
NetworkInterfaces []NetworkInterface `json:"network_interfaces,omitempty"` // Network interfaces of the Node
PowerState string `json:"power_state,omitempty"` // Power state of the Node
Actions []string `json:"actions,omitempty"` // Available actions for Node
Power Power `json:"power,omitempty"` // Power related settings of Node
ProcessorCount int `json:"processor_count,omitempty"` // Processors of the Node
ProcessorType string `json:"processor_type,omitempty"` // Processor type of the Node
MemoryTotal float32 `json:"memory_total,omitempty"` // Total memory of the Node in Gigabytes
@ -81,6 +94,7 @@ type InventoryDetail struct {
Chassis_AssetTag string `json:"chassis_asset_tag,omitempty"` // Asset tag of the Chassis
Chassis_Manufacturer string `json:"chassis_manufacturer,omitempty"` // Manufacturer of the Chassis
Chassis_Model string `json:"chassis_model,omitempty"` // Model of the Chassis
Links Links `json:"links,omitempty"` // Links to specific resources
}
// CrawlBMCForSystems pulls all pertinent information from a BMC. It accepts a CrawlerConfig and returns a list of InventoryDetail structs.
@ -131,9 +145,23 @@ func CrawlBMCForSystems(config CrawlerConfig) ([]InventoryDetail, error) {
for _, chassis := range rf_chassis {
rf_chassis_systems, err := chassis.ComputerSystems()
if err == nil {
rf_systems = append(rf_systems, rf_chassis_systems...)
// rf_systems = append(rf_systems, rf_chassis_systems...)
log.Debug().Msgf("found %d systems in chassis %s", len(rf_chassis_systems), chassis.ID)
}
// Walk the systems found under Chassis with reference
newSystems, err := walkSystems(rf_chassis_systems, chassis, config.URI)
if err != nil {
log.Error().
Err(err).
Str("chassis_id", chassis.ID).
Str("uri", config.URI).
Msg("failed to get systems in chassis...continuing...")
continue
}
// add systems found from chassis to total collection
systems = append(systems, newSystems...)
}
}
rf_root_systems, err := rf_service.Systems()
@ -142,7 +170,12 @@ func CrawlBMCForSystems(config CrawlerConfig) ([]InventoryDetail, error) {
}
log.Debug().Msgf("found %d systems in ServiceRoot", len(rf_root_systems))
rf_systems = append(rf_systems, rf_root_systems...)
return walkSystems(rf_systems, nil, config.URI)
newSystems, err := walkSystems(rf_systems, nil, config.URI)
if err != nil {
return systems, fmt.Errorf("failed to get systems: %v", err)
}
systems = append(systems, newSystems...)
return systems, nil
}
// CrawlBMCForManagers connects to a BMC (Baseboard Management Controller) using the provided configuration,
@ -231,6 +264,49 @@ func CrawlBMCForManagers(config CrawlerConfig) ([]Manager, error) {
func walkSystems(rf_systems []*redfish.ComputerSystem, rf_chassis *redfish.Chassis, baseURI string) ([]InventoryDetail, error) {
systems := []InventoryDetail{}
for _, rf_computersystem := range rf_systems {
var (
managerLinks []string
chassisLinks []string
)
// get all of the links to managers
rf_managers, err := rf_computersystem.ManagedBy()
if err != nil {
log.Warn().Err(err).Msg("failed to get system managers")
log.Error().
Err(err).
Str("id", rf_computersystem.ID).
Str("system", rf_computersystem.Name).
Msg("failed to get manager for system")
} else {
for _, manager := range rf_managers {
managerLinks = append(managerLinks, manager.ODataID)
}
}
if rf_chassis != nil {
chassisLinks = append(chassisLinks, rf_chassis.ODataID)
}
// convert supported reset types to []string
actions := []string{}
for _, action := range rf_computersystem.SupportedResetTypes {
actions = append(actions, string(action))
}
// get power-related details from rf_chassis
power, err := rf_chassis.Power()
if err != nil {
log.Warn().Err(err).Str("id", rf_computersystem.ID).
Str("system", rf_computersystem.Name).Msg("failed to get power-related details from chassis")
}
// extract the power control odata.id resource
powercontrolIDs := []string{}
for _, rf_powercontrol := range power.PowerControl {
powercontrolIDs = append(powercontrolIDs, rf_powercontrol.ODataID)
}
// get all of the links to the chassis
system := InventoryDetail{
URI: baseURI + "/redfish/v1/Systems/" + rf_computersystem.ID,
UUID: rf_computersystem.UUID,
@ -240,7 +316,17 @@ func walkSystems(rf_systems []*redfish.ComputerSystem, rf_chassis *redfish.Chass
Model: rf_computersystem.Model,
Serial: rf_computersystem.SerialNumber,
BiosVersion: rf_computersystem.BIOSVersion,
PowerState: string(rf_computersystem.PowerState),
Links: Links{
Managers: managerLinks,
Chassis: chassisLinks,
},
Power: Power{
Mode: string(rf_computersystem.PowerMode),
State: string(rf_computersystem.PowerState),
RestorePolicy: string(rf_computersystem.PowerRestorePolicy),
PowerControlIDs: powercontrolIDs,
},
Actions: actions,
ProcessorCount: rf_computersystem.ProcessorSummary.Count,
ProcessorType: rf_computersystem.ProcessorSummary.Model,
MemoryTotal: rf_computersystem.MemorySummary.TotalSystemMemoryGiB,
@ -257,7 +343,6 @@ func walkSystems(rf_systems []*redfish.ComputerSystem, rf_chassis *redfish.Chass
if err != nil {
log.Error().Err(err).Msg("failed to get ethernet interfaces from computer system")
return systems, err
}
for _, rf_ethernetinterface := range rf_ethernetinterfaces {
ethernetinterface := EthernetInterface{
@ -369,6 +454,43 @@ func walkManagers(rf_managers []*redfish.Manager, baseURI string) ([]Manager, er
return managers, nil
}
// func getPowerInfo(serviceroot *gofish.Service) ([]Power, error) {
// // get the power control related information (Actions, URL, PowerControl, Links, etc.)
// // get the SupportedResetTypes from /redfish/v1/Systems
// // get the Power/PowerControl from /redfish/v1/Chassis
// rf_chassis, err := serviceroot.Chassis()
// if err != nil {
// }
// power := []Power{}
// for _, chassis := range rf_chassis {
// rf_power, err := chassis.Power()
// if err != nil {
// }
// rf_computersystems, err := chassis.ComputerSystems()
// if err != nil {
// }
// for _, computersystem := range rf_computersystems {
// computersystem.SupportedResetTypes
// }
// power = append(power, Power{
// URL: "",
// Control: PowerControl{
// MemberID: "",
// ResetTypes: rf_computersystem.SupportedResetTypes,
// RelatedItems: []string{},
// },
// })
// }
// }
func loadBMCCreds(config CrawlerConfig) (bmc.BMCCredentials, error) {
// NOTE: it is possible for the SecretStore to be nil, so we need a check
if config.CredentialStore == nil {