Skip to content

Commit

Permalink
feat: add online\offline status to manager and datastore (#29)
Browse files Browse the repository at this point in the history
Added a new backend api to perform a simple query of the redfish root service for a blade\host device.  This allows for a quick check to see if the device, after previously being successfully added to the service, is still present.
This quick check translates directly to an online\offline status for the device.  This is mainly used to handle the situation when a blade\host get powered down.
This status is then used to modify the service's behavior for that device.  The status state is also returned via the client so that the webui\cli can display useful information to the user about the device's current state.
  • Loading branch information
scott-howe-1 authored Sep 24, 2024
1 parent e4221de commit 94fbf3e
Show file tree
Hide file tree
Showing 13 changed files with 549 additions and 332 deletions.
237 changes: 109 additions & 128 deletions pkg/api/api_default_service.go

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions pkg/api/api_redfish_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ func (cfm *CfmApiService) RedfishV1ChassisChassisIdPCIeDevicesGet(ctx context.Co
}

// order returned uris by memory device id
memdevIds := host.GetAllMemoryDeviceIds()
memdevIds := host.GetAllMemoryDeviceIds(ctx)
sort.Strings(memdevIds)

members := []redfishapi.OdataV4IdRef{}
Expand Down Expand Up @@ -588,7 +588,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdConnectionsGet(ctx context.Con
if err != nil {
return formatRedfishErrorResp(ctx, err.(*common.RequestError))
}
memoryIds := blade.GetAllMemoryIds()
memoryIds := blade.GetAllMemoryIds(ctx)
sort.Strings(memoryIds)

for _, memId := range memoryIds {
Expand Down Expand Up @@ -652,7 +652,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdConnectionsPost(ctx context.Co
endpointOdataId := connectionV131Connection.Links.TargetEndpoints[0].OdataId

for bladeId, blade := range appliance.GetBlades(ctx) {
for _, memoryId := range blade.GetAllMemoryIds() {
for _, memoryId := range blade.GetAllMemoryIds(ctx) {
if fmt.Sprintf("/redfish/v1/Systems/%s/MemoryDomains/%s/MemoryChunks/%s", fabricId, bladeId, memoryId) == memoryChunkOdataId {
targetBladeId = bladeId
r.MemoryId = memoryId
Expand All @@ -662,7 +662,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdConnectionsPost(ctx context.Co
if r.MemoryId == "" {
continue
}
for _, portid := range blade.GetAllPortIds() {
for _, portid := range blade.GetAllPortIds(ctx) {
if fmt.Sprintf("/redfish/v1/Fabrics/%s/Endpoints/%s", fabricId, bladeId+strings.Replace(portid, "port", "up", 1)) == endpointOdataId {
r.PortId = portid
break
Expand Down Expand Up @@ -784,7 +784,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdEndpointsGet(ctx context.Conte
members := []redfishapi.OdataV4IdRef{}
for _, bladeId := range bladeIds {
blade, _ := appliance.GetBladeById(ctx, bladeId)
for _, portid := range blade.GetAllPortIds() {
for _, portid := range blade.GetAllPortIds(ctx) {
members = append(members, redfishapi.OdataV4IdRef{
OdataId: fmt.Sprintf("/redfish/v1/Fabrics/%s/Endpoints/%s", fabricId, bladeId+strings.Replace(portid, "port", "up", 1))})
}
Expand Down Expand Up @@ -943,7 +943,7 @@ func (cfm *CfmApiService) RedfishV1FabricsFabricIdSwitchesSwitchIdPortsGet(ctx c
}

// order returned uris by port id
portIds := blade.GetAllPortIds()
portIds := blade.GetAllPortIds(ctx)
sort.Strings(portIds)
members := []redfishapi.OdataV4IdRef{}
for _, member := range portIds {
Expand Down Expand Up @@ -1377,7 +1377,7 @@ func (cfm *CfmApiService) RedfishV1SystemsComputerSystemIdMemoryDomainsMemoryDom
}

// order returned uris by memory id
memoryIds := blade.GetAllMemoryIds()
memoryIds := blade.GetAllMemoryIds(ctx)
sort.Strings(memoryIds)
members := []redfishapi.OdataV4IdRef{}
for _, member := range memoryIds {
Expand Down
1 change: 1 addition & 0 deletions pkg/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
type BackendOperations interface {
CreateSession(context.Context, *ConfigurationSettings, *CreateSessionRequest) (*CreateSessionResponse, error)
DeleteSession(context.Context, *ConfigurationSettings, *DeleteSessionRequest) (*DeleteSessionResponse, error)
GetRootService(context.Context, *ConfigurationSettings, *GetRootServiceRequest) (*GetRootServiceResponse, error)
GetMemoryResourceBlocks(context.Context, *ConfigurationSettings, *MemoryResourceBlocksRequest) (*MemoryResourceBlocksResponse, error)
GetMemoryResourceBlockById(context.Context, *ConfigurationSettings, *MemoryResourceBlockByIdRequest) (*MemoryResourceBlockByIdResponse, error)
GetPorts(context.Context, *ConfigurationSettings, *GetPortsRequest) (*GetPortsResponse, error)
Expand Down
15 changes: 15 additions & 0 deletions pkg/backend/httpfish.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,21 @@ func (session *Session) auth() error {
return response.err
}

// GetRootService: Retrieve root service from endpoint
func (service *httpfishService) GetRootService(ctx context.Context, settings *ConfigurationSettings, req *GetRootServiceRequest) (*GetRootServiceResponse, error) {
session := service.service.session.(*Session)

response := session.query(HTTPOperation.GET, redfish_serviceroot)
if response.err != nil {
return nil, fmt.Errorf("failed to get root service: %w", response.err)
}

name, _ := response.stringFromJSON("Name")
uuid, _ := response.stringFromJSON("UUID")

return &GetRootServiceResponse{Name: name, Uuid: uuid}, nil
}

// CreateSession: Create a new session with an endpoint service
func (service *httpfishService) CreateSession(ctx context.Context, settings *ConfigurationSettings, req *CreateSessionRequest) (*CreateSessionResponse, error) {
logger := klog.FromContext(ctx)
Expand Down
7 changes: 7 additions & 0 deletions pkg/backend/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,10 @@ type GetBackendInfoResponse struct {
Version string
SessionId string
}
type GetRootServiceRequest struct {
}

type GetRootServiceResponse struct {
Name string
Uuid string
}
9 changes: 9 additions & 0 deletions pkg/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package common

type ConnectionStatus string

const (
ONLINE ConnectionStatus = "online"
OFFLINE ConnectionStatus = "offline"
NOT_APPLICABLE ConnectionStatus = "n\\a"
)
163 changes: 91 additions & 72 deletions pkg/common/datastore/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"fmt"

"cfm/pkg/common"
"cfm/pkg/openapi"

"k8s.io/klog/v2"
Expand All @@ -16,59 +17,55 @@ const (
)

type DataStore struct {
SavedAppliances map[string]*ApplianceDataStore `json:"saved-appliances"`
SavedHosts map[string]*HostDataStore `json:"saved-hosts"`
ApplianceData map[string]*ApplianceDatum `json:"appliance-data"`
HostData map[string]*HostDatum `json:"host-data"`
}

func NewDataStore() *DataStore {
return &DataStore{
SavedAppliances: make(map[string]*ApplianceDataStore),
SavedHosts: make(map[string]*HostDataStore),
ApplianceData: make(map[string]*ApplianceDatum),
HostData: make(map[string]*HostDatum),
}
}

// AddAppliance: Add a new appliance to the data store
func (c *DataStore) AddAppliance(creds *openapi.Credentials) {
c.SavedAppliances[creds.CustomId] = NewApplianceDataStore(creds)
// AddApplianceDatum: Add a new appliance datum to the data store
func (c *DataStore) AddApplianceDatum(creds *openapi.Credentials) {
c.ApplianceData[creds.CustomId] = NewApplianceDatum(creds)
}

// AddBlade: Add a new blade to the data store
func (c *DataStore) AddBlade(creds *openapi.Credentials, applianceId string) error {
appliance, exists := c.SavedAppliances[applianceId]
if !exists {
return fmt.Errorf("appliance [%s] not found in data store add", applianceId)
}

appliance.AddBlade(creds)

return nil
// AddHostDatum: Add a new host datum to the data store
func (c *DataStore) AddHostDatum(creds *openapi.Credentials) {
c.HostData[creds.CustomId] = NewHostDatum(creds)
}

// AddHost: Add a new host to the data store
func (c *DataStore) AddHost(creds *openapi.Credentials) {
c.SavedHosts[creds.CustomId] = NewHostDataStore(creds)
// DeleteApplianceDatumById: Delete an appliance from the data store
func (c *DataStore) DeleteApplianceDatumById(applianceId string) {
delete(c.ApplianceData, applianceId)
}

// DeleteAppliance: Delete an appliance from the data store
func (c *DataStore) DeleteAppliance(applianceId string) {
delete(c.SavedAppliances, applianceId)
// DeleteHostDatumById: Delete a host datum from the data store
func (c *DataStore) DeleteHostDatumById(hostId string) {
delete(c.HostData, hostId)
}

// DeleteBlade: Delete a blade from an appliance's data store
func (c *DataStore) DeleteBlade(bladeId, applianceId string) error {
appliance, exists := c.SavedAppliances[applianceId]
// GetApplianceDatumById: Retrieve an appliance datum from the data store
func (c *DataStore) GetApplianceDatumById(applianceId string) (*ApplianceDatum, error) {
datum, exists := c.ApplianceData[applianceId]
if !exists {
return fmt.Errorf("appliance [%s] not found in data store delete", applianceId)
return nil, fmt.Errorf("appliance datum [%s] not found in data store", applianceId)
}

appliance.DeleteBlade(bladeId)

return nil
return datum, nil
}

// DeleteHost: Delete an host from the data store
func (c *DataStore) DeleteHost(hostId string) {
delete(c.SavedHosts, hostId)
// GetHostDatumById: Retrieve a host datum from the data store
func (c *DataStore) GetHostDatumById(hostId string) (*HostDatum, error) {
datum, exists := c.HostData[hostId]
if !exists {
return nil, fmt.Errorf("host datum [%s] not found in data store", hostId)
}

return datum, nil
}

// Init: initialize the data store using command line args, ENV, or a file
Expand All @@ -79,46 +76,77 @@ func (c *DataStore) InitDataStore(ctx context.Context, args []string) error {
return nil
}

type ApplianceDataStore struct {
Credentials *openapi.Credentials `json:"credentials"`
SavedBlades map[string]*BladeDataStore `json:"saved-blades"`
type ApplianceDatum struct {
Credentials *openapi.Credentials `json:"credentials"`
BladeData map[string]*BladeDatum `json:"blade-data"`
ConnectionStatus common.ConnectionStatus `json:"connection-status"`
}

func NewApplianceDataStore(creds *openapi.Credentials) *ApplianceDataStore {
return &ApplianceDataStore{
Credentials: creds,
SavedBlades: make(map[string]*BladeDataStore),
func NewApplianceDatum(creds *openapi.Credentials) *ApplianceDatum {
return &ApplianceDatum{
Credentials: creds,
BladeData: make(map[string]*BladeDatum),
ConnectionStatus: common.NOT_APPLICABLE, // Will use for single-BMC appliance
}
}

func (c *ApplianceDataStore) AddBlade(creds *openapi.Credentials) {
c.SavedBlades[creds.CustomId] = NewBladeDataStore(creds)
func (a *ApplianceDatum) AddBladeDatum(creds *openapi.Credentials) {
a.BladeData[creds.CustomId] = NewBladeDatum(creds)
}

func (c *ApplianceDataStore) DeleteBlade(bladeId string) {
delete(c.SavedBlades, bladeId)
func (a *ApplianceDatum) DeleteBladeDatumById(bladeId string) {
delete(a.BladeData, bladeId)
}

type BladeDataStore struct {
Credentials *openapi.Credentials `json:"credentials"`
func (a *ApplianceDatum) GetBladeDatumById(ctx context.Context, bladeId string) (*BladeDatum, error) {
logger := klog.FromContext(ctx)

blade, exists := a.BladeData[bladeId]
if !exists {
err := fmt.Errorf("blade datum [%s] not found in appliance data [%s] in data store", bladeId, a.Credentials.CustomId)
logger.Error(err, "failure: update blade")
return nil, err
}

return blade, nil
}

func (a *ApplianceDatum) SetConnectionStatus(status common.ConnectionStatus) {
a.ConnectionStatus = status
}

type BladeDatum struct {
Credentials *openapi.Credentials `json:"credentials"`
ConnectionStatus common.ConnectionStatus `json:"connection-status"`
}

func NewBladeDataStore(creds *openapi.Credentials) *BladeDataStore {
return &BladeDataStore{
Credentials: creds,
func NewBladeDatum(creds *openapi.Credentials) *BladeDatum {
return &BladeDatum{
Credentials: creds,
ConnectionStatus: common.ONLINE,
}
}

type HostDataStore struct {
Credentials *openapi.Credentials `json:"credentials"`
func (b *BladeDatum) SetConnectionStatus(status *common.ConnectionStatus) {
b.ConnectionStatus = *status
}

func NewHostDataStore(creds *openapi.Credentials) *HostDataStore {
return &HostDataStore{
Credentials: creds,
type HostDatum struct {
Credentials *openapi.Credentials `json:"credentials"`
ConnectionStatus common.ConnectionStatus `json:"connection-status"`
}

func NewHostDatum(creds *openapi.Credentials) *HostDatum {
return &HostDatum{
Credentials: creds,
ConnectionStatus: common.ONLINE,
}
}

func (h *HostDatum) SetConnectionStatus(status *common.ConnectionStatus) {
h.ConnectionStatus = *status
}

////////////////////////////////////////
///////////// Helpers //////////////////
////////////////////////////////////////
Expand All @@ -131,47 +159,38 @@ func ReloadDataStore(ctx context.Context, s openapi.DefaultAPIServicer, c *DataS

logger.V(2).Info("cfm-service: restoring saved appliances")
var appliancesToDelete []string
for applianceId, appliance := range c.SavedAppliances {
appliance.Credentials.CustomId = applianceId
_, err = s.AppliancesPost(ctx, *appliance.Credentials)
for applianceId, applianceDatum := range c.ApplianceData {
_, err = s.AppliancesPost(ctx, *applianceDatum.Credentials)
if err != nil {
logger.V(2).Info("cfm-service: appliance restore failure", "applianceId", applianceId)
appliancesToDelete = append(appliancesToDelete, applianceId)
continue
}

bladesToDelete := make(map[string]string)
for bladeId, blade := range appliance.SavedBlades {
blade.Credentials.CustomId = bladeId
_, err = s.BladesPost(ctx, applianceId, *blade.Credentials)
for bladeId, bladeDatum := range applianceDatum.BladeData {
_, err = s.BladesPost(ctx, applianceId, *bladeDatum.Credentials)
if err != nil {
logger.V(2).Info("cfm-service: blade restore failure", "bladeId", bladeId, "applianceId", applianceId)
bladesToDelete[applianceId] = bladeId
}
}

for applianceId, bladeId := range bladesToDelete {
delete(c.SavedAppliances[applianceId].SavedBlades, bladeId)
delete(c.ApplianceData[applianceId].BladeData, bladeId)
}
}

for _, applianceId := range appliancesToDelete {
delete(c.SavedAppliances, applianceId)
delete(c.ApplianceData, applianceId)
}

logger.V(2).Info("cfm-service: restoring saved hosts")
var hostsToDelete []string
for hostId, host := range c.SavedHosts {
host.Credentials.CustomId = hostId
_, err = s.HostsPost(ctx, *host.Credentials)
for hostId, hostDatum := range c.HostData {
_, err = s.HostsPost(ctx, *hostDatum.Credentials)
if err != nil {
logger.V(2).Info("cfm-service: host restore failure", "hostId", hostId)
hostsToDelete = append(hostsToDelete, hostId)
logger.V(2).Info("cfm-service: host datum restore failure", "hostId", hostId)
continue
}
}

for _, hostId := range hostsToDelete {
delete(c.SavedHosts, hostId)
}
}
8 changes: 8 additions & 0 deletions pkg/common/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ const (
StatusGetMemoryDevicesDetailsFailure //409

StatusApplianceResyncFailure //409
StatusBladeResyncFailure //409
StatusHostResyncFailure //409

StatusApplianceIdDuplicate //409
StatusBladeIdDuplicate //409
Expand Down Expand Up @@ -150,6 +152,10 @@ func (e StatusCodeType) String() string {
return "Get Memory Devices Details Failure"
case StatusApplianceResyncFailure:
return "Appliance Resync Failure"
case StatusBladeResyncFailure:
return "Blade Resync Failure"
case StatusHostResyncFailure:
return "Host Resync Failure"
case StatusBladeIdDoesNotExist:
return "Blade Id Does Not Exist"
case StatusAppliancesExceedMaximum:
Expand Down Expand Up @@ -209,6 +215,8 @@ func (e StatusCodeType) HttpStatusCode() int {
StatusGetMemoryDevicesFailure,
StatusGetMemoryDevicesDetailsFailure,
StatusApplianceResyncFailure,
StatusBladeResyncFailure,
StatusHostResyncFailure,
StatusApplianceIdDuplicate,
StatusBladeIdDuplicate,
StatusPortIdDuplicate,
Expand Down
Loading

0 comments on commit 94fbf3e

Please sign in to comment.