![Alan Meadows](/assets/img/avatar_default.png)
This commit also introduces a dochelper concept. This provides some convenience methods to the document pkg that help extract data from known document types as well as walk document relationships to discover related information such as BMC credentials for baremetal hosts. Once merged, a follow up patchset will leverage these within the cloud-init code to deduplicate some of these lookups. Change-Id: Ie6a770ce4b34adbea30281917f0cb2fdc460b4fb
359 lines
9.2 KiB
Go
359 lines
9.2 KiB
Go
package redfish_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
redfishAPI "opendev.org/airship/go-redfish/api"
|
|
redfishMocks "opendev.org/airship/go-redfish/api/mocks"
|
|
redfishClient "opendev.org/airship/go-redfish/client"
|
|
|
|
. "opendev.org/airship/airshipctl/pkg/remote/redfish"
|
|
)
|
|
|
|
const (
|
|
computerSystemID = "server-100"
|
|
defaultURL = "https://localhost:1234"
|
|
)
|
|
|
|
func TestRedfishRemoteDirectNormal(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
systemID := computerSystemID
|
|
httpResp := &http.Response{StatusCode: 200}
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
m.On("GetSystem", ctx, systemID).Times(1).
|
|
Return(getTestSystem(), httpResp, nil)
|
|
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
|
|
Return(redfishClient.RedfishError{}, httpResp, nil)
|
|
|
|
m.On("GetSystem", ctx, systemID).Times(1).
|
|
Return(getTestSystem(), httpResp, nil)
|
|
systemReq := redfishClient.ComputerSystem{
|
|
Boot: redfishClient.Boot{
|
|
BootSourceOverrideTarget: redfishClient.BOOTSOURCE_CD,
|
|
},
|
|
}
|
|
m.On("SetSystem", ctx, systemID, systemReq).
|
|
Times(1).
|
|
Return(redfishClient.ComputerSystem{}, httpResp, nil)
|
|
|
|
resetReq := redfishClient.ResetRequestBody{}
|
|
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
|
|
m.On("ResetSystem", ctx, systemID, resetReq).
|
|
Times(1).
|
|
Return(redfishClient.RedfishError{}, httpResp, nil)
|
|
|
|
m.On("GetSystem", ctx, systemID).Times(1).
|
|
Return(redfishClient.ComputerSystem{PowerState: redfishClient.POWERSTATE_OFF}, httpResp, nil)
|
|
|
|
resetReq.ResetType = redfishClient.RESETTYPE_ON
|
|
m.On("ResetSystem", ctx, systemID, resetReq).
|
|
Times(1).
|
|
Return(redfishClient.RedfishError{}, httpResp, nil)
|
|
|
|
m.On("GetSystem", ctx, systemID).Times(1).
|
|
Return(redfishClient.ComputerSystem{PowerState: redfishClient.POWERSTATE_ON}, httpResp, nil)
|
|
|
|
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
|
|
err := rDCfg.DoRemoteDirect()
|
|
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestRedfishRemoteDirectInvalidSystemId(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
systemID := "invalid-server"
|
|
localRDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
|
|
localRDCfg.EphemeralNodeID = systemID
|
|
|
|
realErr := fmt.Errorf("%s system do not exist", systemID)
|
|
m.On("GetSystem", ctx, systemID).
|
|
Times(1).
|
|
Return(redfishClient.ComputerSystem{}, nil, realErr)
|
|
|
|
err := localRDCfg.DoRemoteDirect()
|
|
|
|
_, ok := err.(ErrRedfishClient)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func TestRedfishRemoteDirectGetSystemNetworkError(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
systemID := computerSystemID
|
|
realErr := fmt.Errorf("server request timeout")
|
|
httpResp := &http.Response{StatusCode: 408}
|
|
m.On("GetSystem", ctx, systemID).
|
|
Times(1).
|
|
Return(redfishClient.ComputerSystem{}, httpResp, realErr)
|
|
|
|
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
|
|
err := rDCfg.DoRemoteDirect()
|
|
|
|
_, ok := err.(ErrRedfishClient)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func TestRedfishRemoteDirectInvalidIsoPath(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
systemID := computerSystemID
|
|
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
localRDCfg := rDCfg
|
|
localRDCfg.IsoPath = "bogus/path/to.iso"
|
|
|
|
realErr := redfishClient.GenericOpenAPIError{}
|
|
httpResp := &http.Response{StatusCode: 500}
|
|
m.On("GetSystem", ctx, systemID).
|
|
Times(1).
|
|
Return(getTestSystem(), nil, nil)
|
|
|
|
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
|
|
Return(redfishClient.RedfishError{}, httpResp, realErr)
|
|
|
|
err := localRDCfg.DoRemoteDirect()
|
|
|
|
_, ok := err.(ErrRedfishClient)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func TestRedfishRemoteDirectCdDvdNotAvailableInBootSources(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
systemID := computerSystemID
|
|
invalidSystem := getTestSystem()
|
|
invalidSystem.Boot.BootSourceOverrideTargetRedfishAllowableValues = []redfishClient.BootSource{
|
|
redfishClient.BOOTSOURCE_HDD,
|
|
redfishClient.BOOTSOURCE_PXE,
|
|
}
|
|
|
|
m.On("GetSystem", ctx, systemID).
|
|
Return(invalidSystem, nil, nil)
|
|
|
|
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
|
|
Return(redfishClient.RedfishError{}, nil, nil)
|
|
|
|
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
|
|
err := rDCfg.DoRemoteDirect()
|
|
|
|
_, ok := err.(ErrRedfishClient)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func TestRedfishRemoteDirectSetSystemBootSourceFailed(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
systemID := computerSystemID
|
|
httpSuccResp := &http.Response{StatusCode: 200}
|
|
m.On("GetSystem", ctx, systemID).
|
|
Return(getTestSystem(), httpSuccResp, nil)
|
|
|
|
m.On("InsertVirtualMedia", ctx, "manager-1", "Cd", mock.Anything).
|
|
Return(redfishClient.RedfishError{}, httpSuccResp, nil)
|
|
|
|
m.On("SetSystem", ctx, systemID, mock.Anything).
|
|
Times(1).
|
|
Return(redfishClient.ComputerSystem{}, &http.Response{StatusCode: 401},
|
|
redfishClient.GenericOpenAPIError{})
|
|
|
|
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
|
|
err := rDCfg.DoRemoteDirect()
|
|
|
|
_, ok := err.(ErrRedfishClient)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func TestRedfishRemoteDirectSystemRebootFailed(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
ctx := context.WithValue(
|
|
context.Background(),
|
|
redfishClient.ContextBasicAuth,
|
|
redfishClient.BasicAuth{UserName: "username", Password: "password"},
|
|
)
|
|
|
|
systemID := computerSystemID
|
|
httpSuccResp := &http.Response{StatusCode: 200}
|
|
m.On("GetSystem", ctx, systemID).
|
|
Return(getTestSystem(), httpSuccResp, nil)
|
|
|
|
m.On("InsertVirtualMedia", ctx, mock.Anything, mock.Anything, mock.Anything).
|
|
Return(redfishClient.RedfishError{}, httpSuccResp, nil)
|
|
|
|
m.On("SetSystem", ctx, systemID, mock.Anything).
|
|
Times(1).
|
|
Return(redfishClient.ComputerSystem{}, httpSuccResp, nil)
|
|
|
|
resetReq := redfishClient.ResetRequestBody{}
|
|
resetReq.ResetType = redfishClient.RESETTYPE_FORCE_OFF
|
|
m.On("ResetSystem", ctx, systemID, resetReq).
|
|
Times(1).
|
|
Return(redfishClient.RedfishError{}, &http.Response{StatusCode: 401},
|
|
redfishClient.GenericOpenAPIError{})
|
|
|
|
rDCfg := getDefaultRedfishRemoteDirectObj(t, m)
|
|
|
|
err := rDCfg.DoRemoteDirect()
|
|
|
|
_, ok := err.(ErrRedfishClient)
|
|
assert.True(t, ok)
|
|
}
|
|
|
|
func getTestSystem() redfishClient.ComputerSystem {
|
|
return redfishClient.ComputerSystem{
|
|
Id: computerSystemID,
|
|
Name: "server-100",
|
|
UUID: "58893887-8974-2487-2389-841168418919",
|
|
Status: redfishClient.Status{
|
|
State: "Enabled",
|
|
Health: "OK",
|
|
},
|
|
Links: redfishClient.SystemLinks{
|
|
ManagedBy: []redfishClient.IdRef{
|
|
{OdataId: "/redfish/v1/Managers/manager-1"},
|
|
},
|
|
},
|
|
Boot: redfishClient.Boot{
|
|
BootSourceOverrideTarget: redfishClient.BOOTSOURCE_CD,
|
|
BootSourceOverrideEnabled: redfishClient.BOOTSOURCEOVERRIDEENABLED_CONTINUOUS,
|
|
BootSourceOverrideTargetRedfishAllowableValues: []redfishClient.BootSource{
|
|
redfishClient.BOOTSOURCE_CD,
|
|
redfishClient.BOOTSOURCE_FLOPPY,
|
|
redfishClient.BOOTSOURCE_HDD,
|
|
redfishClient.BOOTSOURCE_PXE,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestNewRedfishRemoteDirectClient(t *testing.T) {
|
|
m := &redfishMocks.RedfishAPI{}
|
|
defer m.AssertExpectations(t)
|
|
|
|
_, err := NewRedfishRemoteDirectClient(
|
|
defaultURL,
|
|
computerSystemID,
|
|
"username",
|
|
"password",
|
|
"/tmp/test.iso",
|
|
true,
|
|
false,
|
|
)
|
|
assert.NoError(t, err)
|
|
|
|
// Test with empty remote URL
|
|
_, err = NewRedfishRemoteDirectClient(
|
|
"",
|
|
computerSystemID,
|
|
"username",
|
|
"password",
|
|
"/tmp/test.iso",
|
|
false,
|
|
false,
|
|
)
|
|
expectedError := "missing configuration: redfish remote url empty"
|
|
assert.EqualError(t, err, expectedError)
|
|
|
|
// Test with empty ephemeral NodeID
|
|
_, err = NewRedfishRemoteDirectClient(
|
|
defaultURL,
|
|
"",
|
|
"username",
|
|
"password",
|
|
"/tmp/test.iso",
|
|
false,
|
|
false,
|
|
)
|
|
expectedError = "missing configuration: redfish ephemeral node id empty"
|
|
assert.EqualError(t, err, expectedError)
|
|
|
|
// Test with empty Iso Path
|
|
_, err = NewRedfishRemoteDirectClient(
|
|
defaultURL,
|
|
computerSystemID,
|
|
"username",
|
|
"password",
|
|
"",
|
|
false,
|
|
false,
|
|
)
|
|
expectedError = "missing configuration: redfish ephemeral node iso Path empty"
|
|
assert.EqualError(t, err, expectedError)
|
|
}
|
|
|
|
func getDefaultRedfishRemoteDirectObj(t *testing.T, api redfishAPI.RedfishAPI) RemoteDirect {
|
|
t.Helper()
|
|
|
|
rDCfg, err := NewRedfishRemoteDirectClient(
|
|
defaultURL,
|
|
computerSystemID,
|
|
"username",
|
|
"password",
|
|
"/tmp/test.iso",
|
|
false,
|
|
false,
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
|
|
rDCfg.RedfishAPI = api
|
|
|
|
return rDCfg
|
|
}
|