From 4b729998e9c94c55b3aaac473ea8ee07d368ac6d Mon Sep 17 00:00:00 2001 From: Drew Walters Date: Thu, 30 Apr 2020 21:13:25 +0000 Subject: [PATCH] Add system power status check to remotedirect Invoking remotedirect on a host that is powered off will result in a remotedirect failure; therefore, the power status of a host needs to be verified before remotedirect can begin. This change adds a status check of the ephemeral node before performing remotedirect. When the node is off, airshipctl powers the node on. NOTE: if the host does not power on quickly enough, remotedirect could still fail. The redfish client will be updated to handle the startup and shutdown waiting in another change. Change-Id: I725cf79e070864956e2991f60261328ebd6a5fc5 Signed-off-by: Drew Walters --- pkg/remote/remote_direct.go | 16 ++++++++++++++ pkg/remote/remote_direct_test.go | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/pkg/remote/remote_direct.go b/pkg/remote/remote_direct.go index fd05b3ea1..2b109fa50 100644 --- a/pkg/remote/remote_direct.go +++ b/pkg/remote/remote_direct.go @@ -18,6 +18,8 @@ import ( "opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/environment" "opendev.org/airship/airshipctl/pkg/log" + + "opendev.org/airship/airshipctl/pkg/remote/power" ) // DoRemoteDirect bootstraps the ephemeral node. @@ -36,6 +38,20 @@ func (b baremetalHost) DoRemoteDirect(settings *environment.AirshipCTLSettings) log.Debugf("Bootstrapping ephemeral host '%s' with ID '%s' and BMC Address '%s'.", b.HostName, b.NodeID(), b.BMCAddress) + powerStatus, err := b.SystemPowerStatus(b.Context) + if err != nil { + return err + } + + // Power on node if it is off + if powerStatus != power.StatusOn { + log.Debugf("Ephemeral node has power status '%s'. Attempting to power on.", powerStatus.String()) + if err = b.SystemPowerOn(b.Context); err != nil { + return err + } + } + + // Perform remote direct operations if remoteConfig.IsoURL == "" { return ErrMissingBootstrapInfoOption{What: "isoURL"} } diff --git a/pkg/remote/remote_direct_test.go b/pkg/remote/remote_direct_test.go index 914686ba5..af56fc611 100644 --- a/pkg/remote/remote_direct_test.go +++ b/pkg/remote/remote_direct_test.go @@ -22,6 +22,7 @@ import ( "opendev.org/airship/airshipctl/pkg/config" "opendev.org/airship/airshipctl/pkg/environment" + "opendev.org/airship/airshipctl/pkg/remote/power" "opendev.org/airship/airshipctl/pkg/remote/redfish" "opendev.org/airship/airshipctl/testutil/redfishutils" ) @@ -69,6 +70,7 @@ func TestDoRemoteDirectMissingISOURL(t *testing.T) { assert.NoError(t, err) rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("SystemPowerStatus", ctx).Times(1).Return(power.StatusOn, nil) ephemeralHost := baremetalHost{ rMock, @@ -91,6 +93,37 @@ func TestDoRemoteDirectRedfish(t *testing.T) { assert.NoError(t, err) rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("SystemPowerStatus", ctx).Times(1).Return(power.StatusOn, nil) + rMock.On("SetVirtualMedia", ctx, isoURL).Times(1).Return(nil) + rMock.On("SetBootSourceByType", ctx).Times(1).Return(nil) + rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("RebootSystem", ctx).Times(1).Return(nil) + + ephemeralHost := baremetalHost{ + rMock, + ctx, + redfishURL, + "doc-name", + username, + password, + } + + cfg := &config.RemoteDirect{ + IsoURL: isoURL, + } + + settings := initSettings(t, withRemoteDirectConfig(cfg), withTestDataPath("base")) + err = ephemeralHost.DoRemoteDirect(settings) + assert.NoError(t, err) +} + +func TestDoRemoteDirectRedfishNodePoweredOff(t *testing.T) { + ctx, rMock, err := redfishutils.NewClient(redfishURL, false, false, username, password) + assert.NoError(t, err) + + rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("SystemPowerStatus", ctx).Times(1).Return(power.StatusOff, nil) + rMock.On("SystemPowerOn", ctx).Times(1).Return(nil) rMock.On("SetVirtualMedia", ctx, isoURL).Times(1).Return(nil) rMock.On("SetBootSourceByType", ctx).Times(1).Return(nil) rMock.On("NodeID").Times(1).Return(systemID) @@ -121,6 +154,7 @@ func TestDoRemoteDirectRedfishVirtualMediaError(t *testing.T) { expectedErr := redfish.ErrRedfishClient{Message: "Unable to set virtual media."} rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("SystemPowerStatus", ctx).Times(1).Return(power.StatusOn, nil) rMock.On("SetVirtualMedia", ctx, isoURL).Times(1).Return(expectedErr) rMock.On("SetBootSourceByType", ctx).Times(1).Return(nil) rMock.On("NodeID").Times(1).Return(systemID) @@ -151,6 +185,7 @@ func TestDoRemoteDirectRedfishBootSourceError(t *testing.T) { assert.NoError(t, err) rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("SystemPowerStatus", ctx).Times(1).Return(power.StatusOn, nil) rMock.On("SetVirtualMedia", ctx, isoURL).Times(1).Return(nil) expectedErr := redfish.ErrRedfishClient{Message: "Unable to set boot source."} @@ -183,6 +218,7 @@ func TestDoRemoteDirectRedfishRebootError(t *testing.T) { assert.NoError(t, err) rMock.On("NodeID").Times(1).Return(systemID) + rMock.On("SystemPowerStatus", ctx).Times(1).Return(power.StatusOn, nil) rMock.On("SetVirtualMedia", ctx, isoURL).Times(1).Return(nil) rMock.On("SetVirtualMedia", ctx, isoURL).Times(1).Return(nil) rMock.On("SetBootSourceByType", ctx).Times(1).Return(nil)