diff --git a/bmc/bmc.go b/bmc/bmc.go index 99250007..fdbd7329 100644 --- a/bmc/bmc.go +++ b/bmc/bmc.go @@ -10,4 +10,6 @@ type Metadata struct { SuccessfulOpenConns []string // SuccessfulCloseConns is a slice of provider names that were closed successfully SuccessfulCloseConns []string + // FailedProviderDetail holds the failed providers error messages for called methods + FailedProviderDetail map[string]string } diff --git a/bmc/boot_device.go b/bmc/boot_device.go index 8ad60e15..ceabb991 100644 --- a/bmc/boot_device.go +++ b/bmc/boot_device.go @@ -25,7 +25,9 @@ type bootDeviceProviders struct { // setPersistent persists the next boot device. // efiBoot sets up the device to boot off UEFI instead of legacy. func setBootDevice(ctx context.Context, timeout time.Duration, bootDevice string, setPersistent, efiBoot bool, b []bootDeviceProviders) (ok bool, metadata Metadata, err error) { - var metadataLocal Metadata + metadataLocal := Metadata{ + FailedProviderDetail: make(map[string]string), + } for _, elem := range b { if elem.bootDeviceSetter == nil { @@ -43,6 +45,7 @@ func setBootDevice(ctx context.Context, timeout time.Duration, bootDevice string ok, setErr := elem.bootDeviceSetter.BootDeviceSet(ctx, bootDevice, setPersistent, efiBoot) if setErr != nil { err = multierror.Append(err, errors.WithMessagef(setErr, "provider: %v", elem.name)) + metadataLocal.FailedProviderDetail[elem.name] = setErr.Error() continue } if !ok { diff --git a/bmc/connection.go b/bmc/connection.go index e6ef8fe0..f861bd13 100644 --- a/bmc/connection.go +++ b/bmc/connection.go @@ -29,8 +29,10 @@ type connectionProviders struct { // OpenConnectionFromInterfaces will try all opener interfaces and remove failed ones. // The reason failed ones need to be removed is so that when other methods are called (like powerstate) // implementations that have connections wont nil pointer error when their connection fails. -func OpenConnectionFromInterfaces(ctx context.Context, timeout time.Duration, providers []interface{}) (opened []interface{}, _ Metadata, err error) { - var metadata Metadata +func OpenConnectionFromInterfaces(ctx context.Context, timeout time.Duration, providers []interface{}) (opened []interface{}, metadata Metadata, err error) { + metadata = Metadata{ + FailedProviderDetail: make(map[string]string), + } // Return immediately if the context is done. select { @@ -92,6 +94,7 @@ func OpenConnectionFromInterfaces(ctx context.Context, timeout time.Duration, pr for res := range results { if res.Err != nil { err = multierror.Append(err, res.Err) + metadata.FailedProviderDetail[res.ProviderName] = res.Err.Error() continue } @@ -108,7 +111,9 @@ func OpenConnectionFromInterfaces(ctx context.Context, timeout time.Duration, pr // closeConnection closes a connection to a BMC, trying all interface implementations passed in func closeConnection(ctx context.Context, c []connectionProviders) (_ Metadata, err error) { - var metadata Metadata + var metadata = Metadata{ + FailedProviderDetail: make(map[string]string), + } var connClosed bool for _, elem := range c { @@ -119,6 +124,7 @@ func closeConnection(ctx context.Context, c []connectionProviders) (_ Metadata, closeErr := elem.closer.Close(ctx) if closeErr != nil { err = multierror.Append(err, errors.WithMessagef(closeErr, "provider: %v", elem.name)) + metadata.FailedProviderDetail[elem.name] = closeErr.Error() continue } connClosed = true diff --git a/bmc/power.go b/bmc/power.go index c9a89ecb..1f5ca512 100644 --- a/bmc/power.go +++ b/bmc/power.go @@ -38,8 +38,10 @@ type powerProviders struct { } // setPowerState sets the power state for a BMC, trying all interface implementations passed in -func setPowerState(ctx context.Context, timeout time.Duration, state string, p []powerProviders) (ok bool, metadata Metadata, err error) { - var metadataLocal Metadata +func setPowerState(ctx context.Context, timeout time.Duration, state string, p []powerProviders) (ok bool, m Metadata, err error) { + metadataLocal := Metadata{ + FailedProviderDetail: make(map[string]string), + } for _, elem := range p { if elem.powerSetter == nil { @@ -49,7 +51,7 @@ func setPowerState(ctx context.Context, timeout time.Duration, state string, p [ case <-ctx.Done(): err = multierror.Append(err, ctx.Err()) - return false, metadata, err + return false, metadataLocal, err default: metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) ctx, cancel := context.WithTimeout(ctx, timeout) @@ -57,6 +59,7 @@ func setPowerState(ctx context.Context, timeout time.Duration, state string, p [ ok, setErr := elem.powerSetter.PowerSet(ctx, state) if setErr != nil { err = multierror.Append(err, errors.WithMessagef(setErr, "provider: %v", elem.name)) + metadataLocal.FailedProviderDetail[elem.name] = setErr.Error() continue } if !ok { @@ -91,7 +94,10 @@ func SetPowerStateFromInterfaces(ctx context.Context, timeout time.Duration, sta } // getPowerState gets the power state for a BMC, trying all interface implementations passed in -func getPowerState(ctx context.Context, timeout time.Duration, p []powerProviders) (state string, metadata Metadata, err error) { +func getPowerState(ctx context.Context, timeout time.Duration, p []powerProviders) (state string, m Metadata, err error) { + metadataLocal := Metadata{ + FailedProviderDetail: make(map[string]string), + } for _, elem := range p { if elem.powerStateGetter == nil { continue @@ -100,21 +106,22 @@ func getPowerState(ctx context.Context, timeout time.Duration, p []powerProvider case <-ctx.Done(): err = multierror.Append(err, ctx.Err()) - return state, metadata, err + return state, metadataLocal, err default: - metadata.ProvidersAttempted = append(metadata.ProvidersAttempted, elem.name) + metadataLocal.ProvidersAttempted = append(metadataLocal.ProvidersAttempted, elem.name) ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() state, stateErr := elem.powerStateGetter.PowerStateGet(ctx) if stateErr != nil { err = multierror.Append(err, errors.WithMessagef(stateErr, "provider: %v", elem.name)) + metadataLocal.FailedProviderDetail[elem.name] = stateErr.Error() continue } - metadata.SuccessfulProvider = elem.name - return state, metadata, nil + metadataLocal.SuccessfulProvider = elem.name + return state, metadataLocal, nil } } - return state, metadata, multierror.Append(err, errors.New("failed to get power state")) + return state, metadataLocal, multierror.Append(err, errors.New("failed to get power state")) } // GetPowerStateFromInterfaces identifies implementations of the PostStateGetter interface and passes the found implementations to the getPowerState() wrapper. diff --git a/client_test.go b/client_test.go index 949dd382..324ed826 100644 --- a/client_test.go +++ b/client_test.go @@ -21,15 +21,13 @@ func TestBMC(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() cl := NewClient(host, user, pass, WithLogger(log), WithPerProviderTimeout(5*time.Second)) - cl.FilterForCompatible(ctx) - var err error - err = cl.Open(ctx) + err := cl.Open(ctx) if err != nil { t.Logf("%+v", cl.GetMetadata()) t.Fatal(err) } defer cl.Close(ctx) - t.Logf("metadata: %+v", cl.GetMetadata()) + t.Logf("metadata for Open: %+v", cl.GetMetadata()) cl.Registry.Drivers = cl.Registry.PreferDriver("non-existent") state, err := cl.GetPowerState(ctx) @@ -37,7 +35,7 @@ func TestBMC(t *testing.T) { t.Fatal(err) } t.Log(state) - t.Logf("metadata %+v", cl.GetMetadata()) + t.Logf("metadata for GetPowerState: %+v", cl.GetMetadata()) cl.Registry.Drivers = cl.Registry.PreferDriver("ipmitool") state, err = cl.PreferProvider("gofish").GetPowerState(ctx) @@ -45,14 +43,14 @@ func TestBMC(t *testing.T) { t.Fatal(err) } t.Log(state) - t.Logf("metadata: %+v", cl.GetMetadata()) + t.Logf("metadata for GetPowerState: %+v", cl.GetMetadata()) users, err := cl.ReadUsers(ctx) if err != nil { t.Fatal(err) } t.Log(users) - t.Logf("metadata: %+v", cl.GetMetadata()) + t.Logf("metadata for ReadUsers: %+v", cl.GetMetadata()) t.Fatal() }