Add remote host power status command
This change introduces a command that allows the user to retrieve the power status of a remote host. Relates-To: #5 Change-Id: I4d3ded6667a5427ad6814c3d000da3becaec50a1 Signed-off-by: Drew Walters <andrew.walters@att.com>
This commit is contained in:
parent
d25c22a538
commit
0c3eefe036
@ -16,7 +16,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/environment"
|
||||
"opendev.org/airship/airshipctl/pkg/errors"
|
||||
)
|
||||
|
||||
// NewRemoteCommand creates a new command that provides functionality to control remote entities.
|
||||
@ -24,10 +23,10 @@ func NewRemoteCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Comma
|
||||
remoteRootCmd := &cobra.Command{
|
||||
Use: "remote",
|
||||
Short: "Control remote entities, i.e. hosts.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return errors.ErrNotImplemented{}
|
||||
},
|
||||
}
|
||||
|
||||
powerStatusCmd := NewPowerStatusCommand(rootSettings)
|
||||
remoteRootCmd.AddCommand(powerStatusCmd)
|
||||
|
||||
return remoteRootCmd
|
||||
}
|
||||
|
48
cmd/remote/remote_power_status.go
Normal file
48
cmd/remote/remote_power_status.go
Normal file
@ -0,0 +1,48 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/environment"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewPowerStatusCommand provides a command to retrieve the power status of a remote host.
|
||||
func NewPowerStatusCommand(rootSettings *environment.AirshipCTLSettings) *cobra.Command {
|
||||
powerStatusCmd := &cobra.Command{
|
||||
Use: "powerstatus SYSTEM_ID",
|
||||
Short: "Retrieve the power status of a host",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
a, err := remote.NewAdapter(rootSettings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
powerStatus, err := a.OOBClient.SystemPowerStatus(a.Context, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Remote host %s has power status: %s\n", args[0], powerStatus)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return powerStatusCmd
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/errors"
|
||||
"opendev.org/airship/airshipctl/testutil"
|
||||
)
|
||||
|
||||
func TestRemoteCommand(t *testing.T) {
|
||||
tests := []*testutil.CmdTest{
|
||||
{
|
||||
Name: "remote-error-not-implemented",
|
||||
CmdLine: "",
|
||||
Cmd: NewRemoteCommand(nil),
|
||||
Error: errors.ErrNotImplemented{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
testutil.RunTest(t, tt)
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
Error: Not implemented
|
||||
Usage:
|
||||
remote [flags]
|
||||
|
||||
Flags:
|
||||
-h, --help help for remote
|
||||
|
@ -146,6 +146,16 @@ func (c *Client) SetVirtualMedia(ctx context.Context, isoPath string) error {
|
||||
return ScreenRedfishError(httpResp, err)
|
||||
}
|
||||
|
||||
// SystemPowerStatus retrieves the power status of a host as a human-readable string.
|
||||
func (c *Client) SystemPowerStatus(ctx context.Context, systemID string) (string, error) {
|
||||
computerSystem, httpResp, err := c.redfishAPI.GetSystem(ctx, systemID)
|
||||
if err = ScreenRedfishError(httpResp, err); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(computerSystem.PowerState), nil
|
||||
}
|
||||
|
||||
// NewClient returns a client with the capability to make Redfish requests.
|
||||
func NewClient(ephemeralNodeID string,
|
||||
isoPath string,
|
||||
|
@ -32,7 +32,7 @@ const (
|
||||
// Adapter bridges the gap between out-of-band clients. It can hold any type of OOB client, e.g. Redfish.
|
||||
type Adapter struct {
|
||||
OOBClient Client
|
||||
context context.Context
|
||||
Context context.Context
|
||||
remoteConfig *config.RemoteDirect
|
||||
remoteURL string
|
||||
username string
|
||||
@ -70,7 +70,7 @@ func (a *Adapter) configureClient(remoteURL string) error {
|
||||
}
|
||||
}
|
||||
|
||||
a.context, a.OOBClient, err = redfish.NewClient(
|
||||
a.Context, a.OOBClient, err = redfish.NewClient(
|
||||
nodeID,
|
||||
a.remoteConfig.IsoURL,
|
||||
baseURL,
|
||||
@ -136,7 +136,7 @@ func (a *Adapter) DoRemoteDirect() error {
|
||||
alog.Debugf("Using Remote Endpoint: %q", a.remoteURL)
|
||||
|
||||
/* Load ISO in manager's virtual media */
|
||||
err := a.OOBClient.SetVirtualMedia(a.context, a.remoteConfig.IsoURL)
|
||||
err := a.OOBClient.SetVirtualMedia(a.Context, a.remoteConfig.IsoURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -144,13 +144,13 @@ func (a *Adapter) DoRemoteDirect() error {
|
||||
alog.Debugf("Successfully loaded virtual media: %q", a.remoteConfig.IsoURL)
|
||||
|
||||
/* Set system's bootsource to selected media */
|
||||
err = a.OOBClient.SetEphemeralBootSourceByType(a.context)
|
||||
err = a.OOBClient.SetEphemeralBootSourceByType(a.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/* Reboot system */
|
||||
err = a.OOBClient.RebootSystem(a.context, a.OOBClient.EphemeralNodeID())
|
||||
err = a.OOBClient.RebootSystem(a.Context, a.OOBClient.EphemeralNodeID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -164,7 +164,7 @@ func (a *Adapter) DoRemoteDirect() error {
|
||||
// out-of-band client.
|
||||
func NewAdapter(settings *environment.AirshipCTLSettings) (*Adapter, error) {
|
||||
a := &Adapter{}
|
||||
a.context = context.Background()
|
||||
a.Context = context.Background()
|
||||
err := a.initializeAdapter(settings)
|
||||
if err != nil {
|
||||
return a, err
|
||||
|
@ -103,13 +103,13 @@ func TestDoRemoteDirectRedfish(t *testing.T) {
|
||||
ctx, rMock, err := redfishutils.NewClient(systemID, isoURL, redfishURL, false, false, "admin", "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
rMock.On("SetVirtualMedia", a.context, isoURL).Times(1).Return(nil)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.context).Times(1).Return(nil)
|
||||
rMock.On("SetVirtualMedia", a.Context, isoURL).Times(1).Return(nil)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.Context).Times(1).Return(nil)
|
||||
rMock.On("EphemeralNodeID").Times(1).Return(systemID)
|
||||
rMock.On("RebootSystem", a.context, systemID).Times(1).Return(nil)
|
||||
rMock.On("RebootSystem", a.Context, systemID).Times(1).Return(nil)
|
||||
|
||||
// Swap the redfish client initialized by the remote direct adapter with the above mocked client
|
||||
a.context = ctx
|
||||
a.Context = ctx
|
||||
a.OOBClient = rMock
|
||||
|
||||
err = a.DoRemoteDirect()
|
||||
@ -131,13 +131,13 @@ func TestDoRemoteDirectRedfishVirtualMediaError(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
|
||||
expectedErr := redfish.ErrRedfishClient{Message: "Unable to set virtual media."}
|
||||
rMock.On("SetVirtualMedia", a.context, isoURL).Times(1).Return(expectedErr)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.context).Times(1).Return(nil)
|
||||
rMock.On("SetVirtualMedia", a.Context, isoURL).Times(1).Return(expectedErr)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.Context).Times(1).Return(nil)
|
||||
rMock.On("EphemeralNodeID").Times(1).Return(systemID)
|
||||
rMock.On("RebootSystem", a.context, systemID).Times(1).Return(nil)
|
||||
rMock.On("RebootSystem", a.Context, systemID).Times(1).Return(nil)
|
||||
|
||||
// Swap the redfish client initialized by the remote direct adapter with the above mocked client
|
||||
a.context = ctx
|
||||
a.Context = ctx
|
||||
a.OOBClient = rMock
|
||||
|
||||
err = a.DoRemoteDirect()
|
||||
@ -159,15 +159,15 @@ func TestDoRemoteDirectRedfishBootSourceError(t *testing.T) {
|
||||
ctx, rMock, err := redfishutils.NewClient(systemID, isoURL, redfishURL, false, false, "admin", "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
rMock.On("SetVirtualMedia", a.context, isoURL).Times(1).Return(nil)
|
||||
rMock.On("SetVirtualMedia", a.Context, isoURL).Times(1).Return(nil)
|
||||
|
||||
expectedErr := redfish.ErrRedfishClient{Message: "Unable to set boot source."}
|
||||
rMock.On("SetEphemeralBootSourceByType", a.context).Times(1).Return(expectedErr)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.Context).Times(1).Return(expectedErr)
|
||||
rMock.On("EphemeralNodeID").Times(1).Return(systemID)
|
||||
rMock.On("RebootSystem", a.context, systemID).Times(1).Return(nil)
|
||||
rMock.On("RebootSystem", a.Context, systemID).Times(1).Return(nil)
|
||||
|
||||
// Swap the redfish client initialized by the remote direct adapter with the above mocked client
|
||||
a.context = ctx
|
||||
a.Context = ctx
|
||||
a.OOBClient = rMock
|
||||
|
||||
err = a.DoRemoteDirect()
|
||||
@ -189,15 +189,15 @@ func TestDoRemoteDirectRedfishRebootError(t *testing.T) {
|
||||
ctx, rMock, err := redfishutils.NewClient(systemID, isoURL, redfishURL, false, false, "admin", "password")
|
||||
assert.NoError(t, err)
|
||||
|
||||
rMock.On("SetVirtualMedia", a.context, isoURL).Times(1).Return(nil)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.context).Times(1).Return(nil)
|
||||
rMock.On("SetVirtualMedia", a.Context, isoURL).Times(1).Return(nil)
|
||||
rMock.On("SetEphemeralBootSourceByType", a.Context).Times(1).Return(nil)
|
||||
rMock.On("EphemeralNodeID").Times(1).Return(systemID)
|
||||
|
||||
expectedErr := redfish.ErrRedfishClient{Message: "Unable to set boot source."}
|
||||
rMock.On("RebootSystem", a.context, systemID).Times(1).Return(expectedErr)
|
||||
rMock.On("RebootSystem", a.Context, systemID).Times(1).Return(expectedErr)
|
||||
|
||||
// Swap the redfish client initialized by the remote direct adapter with the above mocked client
|
||||
a.context = ctx
|
||||
a.Context = ctx
|
||||
a.OOBClient = rMock
|
||||
|
||||
err = a.DoRemoteDirect()
|
||||
|
@ -20,6 +20,10 @@ import (
|
||||
// functions within client are used by power management commands and remote direct functionality.
|
||||
type Client interface {
|
||||
RebootSystem(context.Context, string) error
|
||||
|
||||
// TODO(drewwalters96): Should this be a string forever? We may want to define our own custom type, as the
|
||||
// string format will be client dependent when we add new clients.
|
||||
SystemPowerStatus(context.Context, string) (string, error)
|
||||
EphemeralNodeID() string
|
||||
|
||||
// TODO(drewwalters96): This function may be too tightly coupled to remoteDirect operations. This could probably
|
||||
|
@ -82,6 +82,19 @@ func (m *MockClient) SetVirtualMedia(ctx context.Context, isoPath string) error
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
// SystemPowerStatus provides a stubbed method that can be mocked to test functions that use the
|
||||
// Redfish client without making any Redfish API calls or requiring the appropriate Redfish client settings.
|
||||
//
|
||||
// Example usage:
|
||||
// client := redfishutils.NewClient()
|
||||
// client.On("SystemPowerStatus").Return(<return values>)
|
||||
//
|
||||
// err := client.SystemPowerStatus(<args>)
|
||||
func (m *MockClient) SystemPowerStatus(ctx context.Context, systemID string) (string, error) {
|
||||
args := m.Called(ctx, systemID)
|
||||
return args.String(0), args.Error(1)
|
||||
}
|
||||
|
||||
// NewClient returns a mocked Redfish client in order to test functions that use the Redfish client without making any
|
||||
// Redfish API calls.
|
||||
func NewClient(ephemeralNodeID string, isoPath string, redfishURL string, insecure bool,
|
||||
|
Loading…
x
Reference in New Issue
Block a user