Merge "Add ability to perform baremetal operation against all nodes"
This commit is contained in:
commit
857b51612c
@ -15,12 +15,18 @@
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// Action type is used to perform specific baremetal action
|
||||
type Action int
|
||||
|
||||
const (
|
||||
flagLabel = "labels"
|
||||
flagLabelShort = "l"
|
||||
@ -32,25 +38,111 @@ const (
|
||||
|
||||
flagPhase = "phase"
|
||||
flagPhaseDescription = "airshipctl phase that contains the desired baremetal host document(s)"
|
||||
|
||||
ejectAction Action = iota
|
||||
powerOffAction
|
||||
powerOnAction
|
||||
powerStatusAction
|
||||
rebootAction
|
||||
remoteDirectAction
|
||||
)
|
||||
|
||||
// CommonOptions is used to store common variables from cmd flags for baremetal command group
|
||||
type CommonOptions struct {
|
||||
labels string
|
||||
name string
|
||||
phase string
|
||||
}
|
||||
|
||||
// NewBaremetalCommand creates a new command for interacting with baremetal using airshipctl.
|
||||
func NewBaremetalCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
options := &CommonOptions{}
|
||||
baremetalRootCmd := &cobra.Command{
|
||||
Use: "baremetal",
|
||||
Short: "Perform actions on baremetal hosts",
|
||||
}
|
||||
|
||||
baremetalRootCmd.AddCommand(NewEjectMediaCommand(cfgFactory))
|
||||
baremetalRootCmd.AddCommand(NewPowerOffCommand(cfgFactory))
|
||||
baremetalRootCmd.AddCommand(NewPowerOnCommand(cfgFactory))
|
||||
baremetalRootCmd.AddCommand(NewPowerStatusCommand(cfgFactory))
|
||||
baremetalRootCmd.AddCommand(NewRebootCommand(cfgFactory))
|
||||
baremetalRootCmd.AddCommand(NewRemoteDirectCommand(cfgFactory))
|
||||
baremetalRootCmd.AddCommand(NewEjectMediaCommand(cfgFactory, options))
|
||||
baremetalRootCmd.AddCommand(NewPowerOffCommand(cfgFactory, options))
|
||||
baremetalRootCmd.AddCommand(NewPowerOnCommand(cfgFactory, options))
|
||||
baremetalRootCmd.AddCommand(NewPowerStatusCommand(cfgFactory, options))
|
||||
baremetalRootCmd.AddCommand(NewRebootCommand(cfgFactory, options))
|
||||
baremetalRootCmd.AddCommand(NewRemoteDirectCommand(cfgFactory, options))
|
||||
|
||||
return baremetalRootCmd
|
||||
}
|
||||
|
||||
func initFlags(options *CommonOptions, cmd *cobra.Command) {
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&options.labels, flagLabel, flagLabelShort, "", flagLabelDescription)
|
||||
flags.StringVarP(&options.name, flagName, flagNameShort, "", flagNameDescription)
|
||||
flags.StringVar(&options.phase, flagPhase, config.BootstrapPhase, flagPhaseDescription)
|
||||
}
|
||||
|
||||
func performAction(cfgFactory config.Factory, options *CommonOptions, action Action, writer io.Writer) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectors := GetHostSelections(options.name, options.labels)
|
||||
m, err := remote.NewManager(cfg, options.phase, selectors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return selectAction(m, cfg, action, writer)
|
||||
}
|
||||
|
||||
func selectAction(m *remote.Manager, cfg *config.Config, action Action, writer io.Writer) error {
|
||||
if action == remoteDirectAction {
|
||||
if len(m.Hosts) != 1 {
|
||||
return remote.NewRemoteDirectErrorf("more than one node defined as the ephemeral node")
|
||||
}
|
||||
|
||||
ephemeralHost := m.Hosts[0]
|
||||
return ephemeralHost.DoRemoteDirect(cfg)
|
||||
}
|
||||
|
||||
for _, host := range m.Hosts {
|
||||
switch action {
|
||||
case ejectAction:
|
||||
if err := host.EjectVirtualMedia(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "All media ejected from host '%s'.\n", host.HostName)
|
||||
case powerOffAction:
|
||||
if err := host.SystemPowerOff(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "Powered off host '%s'.\n", host.HostName)
|
||||
case powerOnAction:
|
||||
if err := host.SystemPowerOn(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "Powered on host '%s'.\n", host.HostName)
|
||||
case powerStatusAction:
|
||||
powerStatus, err := host.SystemPowerStatus(host.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "Host '%s' has power status: '%s'\n",
|
||||
host.HostName, powerStatus)
|
||||
case rebootAction:
|
||||
if err := host.RebootSystem(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "Rebooted host '%s'.\n", host.HostName)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetHostSelections builds a list of selectors that can be passed to a manager
|
||||
// using the name and label flags passed to airshipctl.
|
||||
func GetHostSelections(name string, labels string) []remote.HostSelector {
|
||||
@ -63,5 +155,9 @@ func GetHostSelections(name string, labels string) []remote.HostSelector {
|
||||
selectors = append(selectors, remote.ByLabel(labels))
|
||||
}
|
||||
|
||||
if len(selectors) == 0 {
|
||||
selectors = append(selectors, remote.All())
|
||||
}
|
||||
|
||||
return selectors
|
||||
}
|
||||
|
@ -33,32 +33,32 @@ func TestBaremetal(t *testing.T) {
|
||||
{
|
||||
Name: "baremetal-ejectmedia-with-help",
|
||||
CmdLine: "-h",
|
||||
Cmd: baremetal.NewEjectMediaCommand(nil),
|
||||
Cmd: baremetal.NewEjectMediaCommand(nil, &baremetal.CommonOptions{}),
|
||||
},
|
||||
{
|
||||
Name: "baremetal-poweroff-with-help",
|
||||
CmdLine: "-h",
|
||||
Cmd: baremetal.NewPowerOffCommand(nil),
|
||||
Cmd: baremetal.NewPowerOffCommand(nil, &baremetal.CommonOptions{}),
|
||||
},
|
||||
{
|
||||
Name: "baremetal-poweron-with-help",
|
||||
CmdLine: "-h",
|
||||
Cmd: baremetal.NewPowerOnCommand(nil),
|
||||
Cmd: baremetal.NewPowerOnCommand(nil, &baremetal.CommonOptions{}),
|
||||
},
|
||||
{
|
||||
Name: "baremetal-powerstatus-with-help",
|
||||
CmdLine: "-h",
|
||||
Cmd: baremetal.NewPowerStatusCommand(nil),
|
||||
Cmd: baremetal.NewPowerStatusCommand(nil, &baremetal.CommonOptions{}),
|
||||
},
|
||||
{
|
||||
Name: "baremetal-reboot-with-help",
|
||||
CmdLine: "-h",
|
||||
Cmd: baremetal.NewRebootCommand(nil),
|
||||
Cmd: baremetal.NewRebootCommand(nil, &baremetal.CommonOptions{}),
|
||||
},
|
||||
{
|
||||
Name: "baremetal-remotedirect-with-help",
|
||||
CmdLine: "-h",
|
||||
Cmd: baremetal.NewRemoteDirectCommand(nil),
|
||||
Cmd: baremetal.NewRemoteDirectCommand(nil, &baremetal.CommonOptions{}),
|
||||
},
|
||||
}
|
||||
|
||||
@ -79,5 +79,5 @@ func TestGetHostSelectionsBothSelectors(t *testing.T) {
|
||||
|
||||
func TestGetHostSelectionsNone(t *testing.T) {
|
||||
selectors := baremetal.GetHostSelections("", "")
|
||||
assert.Len(t, selectors, 0)
|
||||
assert.Len(t, selectors, 1)
|
||||
}
|
||||
|
@ -15,52 +15,23 @@
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewEjectMediaCommand provides a command to eject media attached to a baremetal host.
|
||||
func NewEjectMediaCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
var labels string
|
||||
var name string
|
||||
var phase string
|
||||
|
||||
func NewEjectMediaCommand(cfgFactory config.Factory, options *CommonOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "ejectmedia",
|
||||
Short: "Eject media attached to a baremetal host",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectors := GetHostSelections(name, labels)
|
||||
m, err := remote.NewManager(cfg, phase, selectors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, host := range m.Hosts {
|
||||
if err := host.EjectVirtualMedia(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "All media ejected from host '%s'.\n", host.HostName)
|
||||
}
|
||||
|
||||
return nil
|
||||
return performAction(cfgFactory, options, ejectAction, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&labels, flagLabel, flagLabelShort, "", flagLabelDescription)
|
||||
flags.StringVarP(&name, flagName, flagNameShort, "", flagNameDescription)
|
||||
flags.StringVar(&phase, flagPhase, config.BootstrapPhase, flagPhaseDescription)
|
||||
initFlags(options, cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -15,52 +15,23 @@
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewPowerOffCommand provides a command to shutdown a remote host.
|
||||
func NewPowerOffCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
var labels string
|
||||
var name string
|
||||
var phase string
|
||||
|
||||
func NewPowerOffCommand(cfgFactory config.Factory, options *CommonOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "poweroff",
|
||||
Short: "Shutdown a baremetal host",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectors := GetHostSelections(name, labels)
|
||||
m, err := remote.NewManager(cfg, phase, selectors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, host := range m.Hosts {
|
||||
if err := host.SystemPowerOff(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Powered off host '%s'.\n", host.HostName)
|
||||
}
|
||||
|
||||
return nil
|
||||
return performAction(cfgFactory, options, powerOffAction, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&labels, flagLabel, flagLabelShort, "", flagLabelDescription)
|
||||
flags.StringVarP(&name, flagName, flagNameShort, "", flagNameDescription)
|
||||
flags.StringVar(&phase, flagPhase, config.BootstrapPhase, flagPhaseDescription)
|
||||
initFlags(options, cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -15,52 +15,23 @@ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewPowerOnCommand provides a command with the capability to power on baremetal hosts.
|
||||
func NewPowerOnCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
var labels string
|
||||
var name string
|
||||
var phase string
|
||||
|
||||
func NewPowerOnCommand(cfgFactory config.Factory, options *CommonOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "poweron",
|
||||
Short: "Power on a host",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectors := GetHostSelections(name, labels)
|
||||
m, err := remote.NewManager(cfg, phase, selectors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, host := range m.Hosts {
|
||||
if err := host.SystemPowerOn(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Powered on host '%s'.\n", host.HostName)
|
||||
}
|
||||
|
||||
return nil
|
||||
return performAction(cfgFactory, options, powerOnAction, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&labels, flagLabel, flagLabelShort, "", flagLabelDescription)
|
||||
flags.StringVarP(&name, flagName, flagNameShort, "", flagNameDescription)
|
||||
flags.StringVar(&phase, flagPhase, config.BootstrapPhase, flagPhaseDescription)
|
||||
initFlags(options, cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -15,54 +15,23 @@
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewPowerStatusCommand provides a command to retrieve the power status of a baremetal host.
|
||||
func NewPowerStatusCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
var labels string
|
||||
var name string
|
||||
var phase string
|
||||
|
||||
func NewPowerStatusCommand(cfgFactory config.Factory, options *CommonOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "powerstatus",
|
||||
Short: "Retrieve the power status of a baremetal host",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectors := GetHostSelections(name, labels)
|
||||
m, err := remote.NewManager(cfg, phase, selectors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, host := range m.Hosts {
|
||||
powerStatus, err := host.SystemPowerStatus(host.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Host '%s' has power status: '%s'\n",
|
||||
host.HostName, powerStatus)
|
||||
}
|
||||
|
||||
return nil
|
||||
return performAction(cfgFactory, options, powerStatusAction, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&labels, flagLabel, flagLabelShort, "", flagLabelDescription)
|
||||
flags.StringVarP(&name, flagName, flagNameShort, "", flagNameDescription)
|
||||
flags.StringVar(&phase, flagPhase, config.BootstrapPhase, flagPhaseDescription)
|
||||
initFlags(options, cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -15,52 +15,23 @@ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
package baremetal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewRebootCommand provides a command with the capability to reboot baremetal hosts.
|
||||
func NewRebootCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
var labels string
|
||||
var name string
|
||||
var phase string
|
||||
|
||||
func NewRebootCommand(cfgFactory config.Factory, options *CommonOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "reboot",
|
||||
Short: "Reboot a host",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
selectors := GetHostSelections(name, labels)
|
||||
m, err := remote.NewManager(cfg, phase, selectors...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, host := range m.Hosts {
|
||||
if err := host.RebootSystem(host.Context); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cmd.OutOrStdout(), "Rebooted host '%s'.\n", host.HostName)
|
||||
}
|
||||
|
||||
return nil
|
||||
return performAction(cfgFactory, options, rebootAction, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVarP(&labels, flagLabel, flagLabelShort, "", flagLabelDescription)
|
||||
flags.StringVarP(&name, flagName, flagNameShort, "", flagNameDescription)
|
||||
flags.StringVar(&phase, flagPhase, config.BootstrapPhase, flagPhaseDescription)
|
||||
initFlags(options, cmd)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
@ -19,33 +19,17 @@ import (
|
||||
|
||||
"opendev.org/airship/airshipctl/pkg/config"
|
||||
"opendev.org/airship/airshipctl/pkg/document"
|
||||
"opendev.org/airship/airshipctl/pkg/remote"
|
||||
)
|
||||
|
||||
// NewRemoteDirectCommand provides a command with the capability to perform remote direct operations.
|
||||
func NewRemoteDirectCommand(cfgFactory config.Factory) *cobra.Command {
|
||||
func NewRemoteDirectCommand(cfgFactory config.Factory, options *CommonOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "remotedirect",
|
||||
Short: "Bootstrap the ephemeral host",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cfgFactory()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manager, err := remote.NewManager(cfg,
|
||||
config.BootstrapPhase,
|
||||
remote.ByLabel(document.EphemeralHostSelector))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(manager.Hosts) != 1 {
|
||||
return remote.NewRemoteDirectErrorf("more than one node defined as the ephemeral node")
|
||||
}
|
||||
|
||||
ephemeralHost := manager.Hosts[0]
|
||||
return ephemeralHost.DoRemoteDirect(cfg)
|
||||
options.phase = config.BootstrapPhase
|
||||
options.labels = document.EphemeralHostSelector
|
||||
return performAction(cfgFactory, options, remoteDirectAction, cmd.OutOrStdout())
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -115,6 +115,35 @@ func ByName(name string) HostSelector {
|
||||
}
|
||||
}
|
||||
|
||||
// All adds the host to a manager whose documents match the BareMetalHostKind.
|
||||
func All() HostSelector {
|
||||
return func(a *Manager, mgmtCfg config.ManagementConfiguration, docBundle document.Bundle) error {
|
||||
selector := document.NewSelector().ByKind(document.BareMetalHostKind)
|
||||
docs, err := docBundle.Select(selector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(docs) == 0 {
|
||||
return document.ErrDocNotFound{Selector: selector}
|
||||
}
|
||||
|
||||
var matchingHosts []baremetalHost
|
||||
for _, doc := range docs {
|
||||
host, err := newBaremetalHost(mgmtCfg, doc, docBundle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
matchingHosts = append(matchingHosts, host)
|
||||
}
|
||||
|
||||
a.Hosts = reconcileHosts(a.Hosts, matchingHosts...)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewManager provides a manager that exposes the capability to perform remote direct functionality and other
|
||||
// out-of-band management on multiple hosts.
|
||||
func NewManager(cfg *config.Config, phaseName string, hosts ...HostSelector) (*Manager, error) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user