From 211a099d878427fd0fbcc1089130285cbc1a34a5 Mon Sep 17 00:00:00 2001 From: Mengling Ding <71745861+Meng-20@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:01:49 -0700 Subject: [PATCH] feat(service): add the discovered devices and change the connection status to unavailable for failed devices (#54) * feat: add the discovered devices and change the connection status to unavailable for failed ones * revert: revert the error handling * feat: add the failed devices after creating session and NewDevices * refactor: change all the FOUND status to UNAVAILABLE * refactor: change the locations for GetBladeDatumByIp and GetHostDatumByIp --------- Signed-off-by: Mengling Ding <71745861+Meng-20@users.noreply.github.com> --- cmd/cfm-service/main.go | 12 +++-- pkg/common/common.go | 38 ++++++++++++++-- pkg/common/datastore/datastore.go | 44 +++++++++++------- pkg/manager/appliance.go | 34 +++++++++++++- pkg/manager/blade.go | 2 +- pkg/manager/host.go | 6 +-- pkg/manager/manager.go | 30 ++++++++++++- services/discovery.go | 74 +++++++++++++++++++++++++++++++ 8 files changed, 212 insertions(+), 28 deletions(-) create mode 100644 services/discovery.go diff --git a/cmd/cfm-service/main.go b/cmd/cfm-service/main.go index f106b4a..ef7f5b1 100644 --- a/cmd/cfm-service/main.go +++ b/cmd/cfm-service/main.go @@ -11,15 +11,15 @@ import ( "strings" "sync" + "github.com/rs/cors" + "k8s.io/klog/v2" + "cfm/pkg/api" "cfm/pkg/common" "cfm/pkg/common/datastore" "cfm/pkg/openapi" "cfm/pkg/redfishapi" "cfm/services" - - "github.com/rs/cors" - "k8s.io/klog/v2" ) var Version = "1.x.x" @@ -68,6 +68,12 @@ func main() { defaultRedfishController := redfishapi.NewDefaultAPIController(defaultRedfishService) api.AddRedfishRouter(ctx, router, defaultRedfishController) + // Discover devices before loading datastore + bladeDevices, _ := services.DiscoverDevices(ctx, defaultApiService, "blade") + hostDevices, _ := services.DiscoverDevices(ctx, defaultApiService, "cxl-host") + // Add the discovered devices into datastore + services.AddDiscoveredDevices(ctx, defaultApiService, bladeDevices, hostDevices) + // Load datastore datastore.DStore().Restore() data := datastore.DStore().GetDataStore() diff --git a/pkg/common/common.go b/pkg/common/common.go index 82392f8..98218ed 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -1,10 +1,42 @@ package common +import "cfm/pkg/openapi" + type ConnectionStatus string const ( - ONLINE ConnectionStatus = "online" - FOUND ConnectionStatus = "found" - OFFLINE ConnectionStatus = "offline" + ONLINE ConnectionStatus = "online" // avahi-found, found root service, created backend session, added to service map + UNAVAILABLE ConnectionStatus = "unavailable" // avahi-found AND detect root service AND !created backend session AND !added to service map + OFFLINE ConnectionStatus = "offline" // !avahi-found (after previously adding it) NOT_APPLICABLE ConnectionStatus = "n\\a" ) + +var DefaultApplianceCredentials = &openapi.Credentials{ + Username: "root", + Password: "0penBmc", + IpAddress: "127.0.0.1", + Port: 8443, + Insecure: true, + Protocol: "https", + CustomId: "CMA_Discovered_Blades", +} + +var DefaultBladeCredentials = &openapi.Credentials{ + Username: "root", + Password: "0penBmc", + IpAddress: "127.0.0.1", + Port: 443, + Insecure: true, + Protocol: "https", + CustomId: "Discoverd_Blade_", +} + +var DefaultHostCredentials = &openapi.Credentials{ + Username: "admin", + Password: "admin12345", + IpAddress: "127.0.0.1", + Port: 8082, + Insecure: true, + Protocol: "http", + CustomId: "Discoverd_Host_", +} diff --git a/pkg/common/datastore/datastore.go b/pkg/common/datastore/datastore.go index 18affdc..880b485 100644 --- a/pkg/common/datastore/datastore.go +++ b/pkg/common/datastore/datastore.go @@ -6,10 +6,10 @@ import ( "context" "fmt" + "k8s.io/klog/v2" + "cfm/pkg/common" "cfm/pkg/openapi" - - "k8s.io/klog/v2" ) const ( @@ -58,6 +58,28 @@ func (c *DataStore) GetApplianceDatumById(applianceId string) (*ApplianceDatum, return datum, nil } +// Verify if the blade exists using the ipAddress +func (c *DataStore) GetBladeDatumByIp(IpAddress string) (*string, bool) { + for _, appliance := range c.ApplianceData { + for bladeId, blade := range appliance.BladeData { + if blade.Credentials.IpAddress == IpAddress { + return &bladeId, true + } + } + } + return nil, false +} + +// Verify if the host exists using the ipAddress +func (c *DataStore) GetHostDatumByIp(IpAddress string) (*string, bool) { + for hostId, host := range c.HostData { + if host.Credentials.IpAddress == IpAddress { + return &hostId, true + } + } + return nil, false +} + // GetHostDatumById: Retrieve a host datum from the data store func (c *DataStore) GetHostDatumById(hostId string) (*HostDatum, error) { datum, exists := c.HostData[hostId] @@ -158,31 +180,21 @@ func ReloadDataStore(ctx context.Context, s openapi.DefaultAPIServicer, c *DataS logger := klog.FromContext(ctx) logger.V(2).Info("cfm-service: restoring saved appliances") - var appliancesToDelete []string + 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, bladeDatum := range applianceDatum.BladeData { - _, err = s.BladesPost(ctx, applianceId, *bladeDatum.Credentials) - if err != nil { + _, postErr := s.BladesPost(ctx, applianceId, *bladeDatum.Credentials) + if postErr != nil { logger.V(2).Info("cfm-service: blade restore failure", "bladeId", bladeId, "applianceId", applianceId) - bladesToDelete[applianceId] = bladeId + continue } } - - for applianceId, bladeId := range bladesToDelete { - delete(c.ApplianceData[applianceId].BladeData, bladeId) - } - } - - for _, applianceId := range appliancesToDelete { - delete(c.ApplianceData, applianceId) } logger.V(2).Info("cfm-service: restoring saved hosts") diff --git a/pkg/manager/appliance.go b/pkg/manager/appliance.go index 28c884d..e52c672 100644 --- a/pkg/manager/appliance.go +++ b/pkg/manager/appliance.go @@ -96,6 +96,22 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad if err != nil || response == nil { newErr := fmt.Errorf("create session failure at [%s:%d] using interface [%s]: %w", c.IpAddress, c.Port, backendName, err) logger.Error(newErr, "failure: add blade") + + // Continue adding the failed blade to the datastore, but update the connection status to unavailable + newBlade := &Blade{ + Id: c.CustomId, + Uri: GetCfmUriBladeId(a.Id, c.CustomId), + Status: common.UNAVAILABLE, + ApplianceId: a.Id, + } + a.Blades[newBlade.Id] = newBlade + + applianceDatum, _ := datastore.DStore().GetDataStore().GetApplianceDatumById(newBlade.ApplianceId) + applianceDatum.AddBladeDatum(c) + unavailabelBlade, _ := applianceDatum.GetBladeDatumById(ctx, c.CustomId) + unavailabelBlade.ConnectionStatus = common.UNAVAILABLE + datastore.DStore().Store() + return nil, &common.RequestError{StatusCode: common.StatusBladeCreateSessionFailure, Err: newErr} } @@ -148,6 +164,22 @@ func (a *Appliance) AddBlade(ctx context.Context, c *openapi.Credentials) (*Blad newErr := fmt.Errorf("appliance [%s] new blade object creation failure: %w", a.Id, err) logger.Error(newErr, "failure: add blade") + + // Continue adding the failed blade to the datastore, but update the connection status to unavailable + newBlade := &Blade{ + Id: c.CustomId, + Uri: GetCfmUriBladeId(a.Id, c.CustomId), + Status: common.UNAVAILABLE, + ApplianceId: a.Id, + } + a.Blades[newBlade.Id] = newBlade + + applianceDatum, _ := datastore.DStore().GetDataStore().GetApplianceDatumById(newBlade.ApplianceId) + applianceDatum.AddBladeDatum(c) + unavailabelBlade, _ := applianceDatum.GetBladeDatumById(ctx, c.CustomId) + unavailabelBlade.ConnectionStatus = common.UNAVAILABLE + datastore.DStore().Store() + return nil, &common.RequestError{StatusCode: common.StatusManagerInitializationFailure, Err: newErr} } @@ -346,7 +378,7 @@ func (a *Appliance) GetBladeById(ctx context.Context, bladeId string) (*Blade, e if blade.CheckSync(ctx) { logger.V(4).Info("initiating auto-resync check", "bladeId", bladeId, "applianceId", a.Id) blade.UpdateConnectionStatusBackend(ctx) - if blade.Status == common.FOUND { // good power, bad session + if blade.Status == common.UNAVAILABLE { // good power, bad session blade, err = a.ResyncBladeById(ctx, bladeId) if err != nil { newErr := fmt.Errorf("failed to resync blade by id [%s]: %w", bladeId, err) diff --git a/pkg/manager/blade.go b/pkg/manager/blade.go index 0a12fb4..fced70c 100644 --- a/pkg/manager/blade.go +++ b/pkg/manager/blade.go @@ -731,7 +731,7 @@ func (b *Blade) UpdateConnectionStatusBackend(ctx context.Context) { if status.FoundSession { b.Status = common.ONLINE } else { - b.Status = common.FOUND + b.Status = common.UNAVAILABLE } } else { b.Status = common.OFFLINE diff --git a/pkg/manager/host.go b/pkg/manager/host.go index f32d249..7cb169d 100644 --- a/pkg/manager/host.go +++ b/pkg/manager/host.go @@ -8,12 +8,12 @@ import ( "strings" "time" + "k8s.io/klog/v2" + "cfm/pkg/backend" "cfm/pkg/common" "cfm/pkg/common/datastore" "cfm/pkg/openapi" - - "k8s.io/klog/v2" ) const ID_PREFIX_HOST_DFLT string = "host" @@ -507,7 +507,7 @@ func (h *Host) UpdateConnectionStatusBackend(ctx context.Context) { if status.FoundSession { h.Status = common.ONLINE } else { - h.Status = common.FOUND + h.Status = common.UNAVAILABLE } } else { h.Status = common.OFFLINE diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 0fd44be..ac26076 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -280,6 +280,20 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { if err != nil || response == nil { newErr := fmt.Errorf("create session failure at [%s:%d] using interface [%s]: %w", c.IpAddress, c.Port, backendName, err) logger.Error(newErr, "failure: add host") + + // Continue adding the failed host to the datastore, but update the connection status to unavailable + host := &Host{ + Id: c.CustomId, + Uri: GetCfmUriHostId(c.CustomId), + Status: common.UNAVAILABLE, + } + deviceCache.AddHost(host, false) + + datastore.DStore().GetDataStore().AddHostDatum(c) + unavailableHost, _ := datastore.DStore().GetDataStore().GetHostDatumById(c.CustomId) + unavailableHost.ConnectionStatus = common.UNAVAILABLE + datastore.DStore().Store() + return nil, &common.RequestError{StatusCode: common.StatusHostCreateSessionFailure, Err: newErr} } @@ -331,6 +345,20 @@ func AddHost(ctx context.Context, c *openapi.Credentials) (*Host, error) { newErr := fmt.Errorf("new host object creation failure: %w", err) logger.Error(newErr, "failure: add host") + + // Continue adding the failed host to the datastore, but update the connection status to unavailable + host := &Host{ + Id: c.CustomId, + Uri: GetCfmUriHostId(c.CustomId), + Status: common.UNAVAILABLE, + } + deviceCache.AddHost(host, false) + + datastore.DStore().GetDataStore().AddHostDatum(c) + unavailableHost, _ := datastore.DStore().GetDataStore().GetHostDatumById(c.CustomId) + unavailableHost.ConnectionStatus = common.UNAVAILABLE + datastore.DStore().Store() + return nil, &common.RequestError{StatusCode: common.StatusManagerInitializationFailure, Err: newErr} } @@ -533,7 +561,7 @@ func GetHostById(ctx context.Context, hostId string) (*Host, error) { if host.CheckSync(ctx) { logger.V(4).Info("initiating auto-resync check", "hostId", hostId) host.UpdateConnectionStatusBackend(ctx) - if host.Status == common.FOUND { // good power, bad session + if host.Status == common.UNAVAILABLE { // good power, bad session host, err = ResyncHostById(ctx, hostId) if err != nil { newErr := fmt.Errorf("failed to resync host by id [%s]: %w", hostId, err) diff --git a/services/discovery.go b/services/discovery.go new file mode 100644 index 0000000..df0955d --- /dev/null +++ b/services/discovery.go @@ -0,0 +1,74 @@ +package services + +import ( + "log" + + "golang.org/x/net/context" + + "cfm/pkg/common" + "cfm/pkg/common/datastore" + "cfm/pkg/openapi" +) + +// discoverDevices function to call the DiscoverDevices API +func DiscoverDevices(ctx context.Context, apiService openapi.DefaultAPIServicer, deviceType string) (openapi.ImplResponse, error) { + resp, err := apiService.DiscoverDevices(ctx, deviceType) + if err != nil { + log.Printf("Error discovering devices of type %s: %v", deviceType, err) + return resp, err + } else { + log.Printf("Discovered devices of type %s: %v", deviceType, resp) + return resp, nil + } +} + +func AddDiscoveredDevices(ctx context.Context, apiService openapi.DefaultAPIServicer, blades openapi.ImplResponse, hosts openapi.ImplResponse) { + // Verify the existence of the default appliance; if it doesn't exist, add it + datastore.DStore().Restore() + data := datastore.DStore().GetDataStore() + _, err := datastore.DStore().GetDataStore().GetApplianceDatumById(common.DefaultApplianceCredentials.CustomId) + if err != nil { + datastore.DStore().GetDataStore().AddApplianceDatum(common.DefaultApplianceCredentials) + datastore.DStore().Store() + } + + // Add blades + // Convert data type + bladeBodyBytes, ok := blades.Body.([]*openapi.DiscoveredDevice) + if !ok { + log.Fatalf("Response body is not []byte") + } + applianceDatum, _ := datastore.DStore().GetDataStore().GetApplianceDatumById(common.DefaultApplianceCredentials.CustomId) + for _, bladeDevice := range bladeBodyBytes { + _, exist := data.GetBladeDatumByIp(bladeDevice.Address) + if !exist { + newCredentials := *common.DefaultBladeCredentials + newCredentials.IpAddress = bladeDevice.Address + //Assign the actual ipAddress to customId to ensure its uniqueness + newCredentials.CustomId = newCredentials.CustomId + bladeDevice.Address + + applianceDatum.AddBladeDatum(&newCredentials) + datastore.DStore().Store() + } + } + + // Add cxl-hosts + // Convert data type + hostBodyBytes, ok := hosts.Body.([]*openapi.DiscoveredDevice) + if !ok { + log.Fatalf("Response body is not []byte") + } + for _, hostDevice := range hostBodyBytes { + _, exist := data.GetHostDatumByIp(hostDevice.Address) + if !exist { + newCredentials := *common.DefaultHostCredentials + newCredentials.IpAddress = hostDevice.Address + //Assign the actual ipAddress to customId to ensure its uniqueness + newCredentials.CustomId = newCredentials.CustomId + hostDevice.Address + + datastore.DStore().GetDataStore().AddHostDatum(&newCredentials) + datastore.DStore().Store() + } + + } +}