diff --git a/internal/app/wwctl/power/reset/power.go b/internal/app/wwctl/power/reset/power.go new file mode 100644 index 00000000..3b538202 --- /dev/null +++ b/internal/app/wwctl/power/reset/power.go @@ -0,0 +1,95 @@ +package powerreset + +import ( + "fmt" + "os" + + "github.com/hpcng/warewulf/internal/pkg/batch" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/power" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + var returnErr error = nil + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + os.Exit(1) + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Cloud not get nodeList: %s\n", err) + os.Exit(1) + } + + if len(args) > 0 { + nodes = node.FilterByName(nodes, args) + } else { + cmd.Usage() + os.Exit(1) + } + + if len(nodes) == 0 { + fmt.Printf("No nodes found\n") + os.Exit(1) + } + + batchpool := batch.New(50) + jobcount := len(nodes) + results := make(chan power.IPMI, jobcount) + + for _, node := range nodes { + + if node.IpmiIpaddr.Get() == "" { + wwlog.Printf(wwlog.ERROR, "%s: No IPMI IP address\n", node.Id.Get()) + continue + } + var ipmiInterface = "lan" + if node.IpmiInterface.Get() != "" { + ipmiInterface = node.IpmiInterface.Get() + } + var ipmiPort = "623" + if node.IpmiPort.Get() != "" { + ipmiPort = node.IpmiPort.Get() + } + ipmiCmd := power.IPMI{ + NodeName: node.Id.Get(), + HostName: node.IpmiIpaddr.Get(), + Port: ipmiPort, + User: node.IpmiUserName.Get(), + Password: node.IpmiPassword.Get(), + Interface: ipmiInterface, + AuthType: "MD5", + } + + batchpool.Submit(func() { + ipmiCmd.PowerReset() + results <- ipmiCmd + }) + + } + + batchpool.Run() + + close(results) + + for result := range results { + + out, err := result.Result() + + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s: %s\n", result.NodeName, out) + returnErr = err + continue + } + + fmt.Printf("%s: %s\n", result.NodeName, out) + + } + + return returnErr +} diff --git a/internal/app/wwctl/power/reset/root.go b/internal/app/wwctl/power/reset/root.go new file mode 100644 index 00000000..a55d780d --- /dev/null +++ b/internal/app/wwctl/power/reset/root.go @@ -0,0 +1,19 @@ +package powerreset + +import ( + "github.com/spf13/cobra" +) + +var ( + powerCmd = &cobra.Command{ + Use: "reset", + Short: "Issue a reset to the given node(s)", + Long: "This command will issue a reset to the given set of nodes.", + RunE: CobraRunE, + } +) + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return powerCmd +} diff --git a/internal/app/wwctl/power/root.go b/internal/app/wwctl/power/root.go index 7ef3dbde..c7a9e7ad 100644 --- a/internal/app/wwctl/power/root.go +++ b/internal/app/wwctl/power/root.go @@ -4,6 +4,8 @@ import ( powercycle "github.com/hpcng/warewulf/internal/app/wwctl/power/cycle" poweroff "github.com/hpcng/warewulf/internal/app/wwctl/power/off" poweron "github.com/hpcng/warewulf/internal/app/wwctl/power/on" + powerreset "github.com/hpcng/warewulf/internal/app/wwctl/power/reset" + powersoft "github.com/hpcng/warewulf/internal/app/wwctl/power/soft" powerstatus "github.com/hpcng/warewulf/internal/app/wwctl/power/status" "github.com/spf13/cobra" ) @@ -23,6 +25,8 @@ func init() { baseCmd.AddCommand(powercycle.GetCommand()) baseCmd.AddCommand(poweroff.GetCommand()) baseCmd.AddCommand(poweron.GetCommand()) + baseCmd.AddCommand(powerreset.GetCommand()) + baseCmd.AddCommand(powersoft.GetCommand()) baseCmd.AddCommand(powerstatus.GetCommand()) } diff --git a/internal/app/wwctl/power/soft/power.go b/internal/app/wwctl/power/soft/power.go new file mode 100644 index 00000000..2a2193fb --- /dev/null +++ b/internal/app/wwctl/power/soft/power.go @@ -0,0 +1,95 @@ +package powersoft + +import ( + "fmt" + "os" + + "github.com/hpcng/warewulf/internal/pkg/batch" + "github.com/hpcng/warewulf/internal/pkg/node" + "github.com/hpcng/warewulf/internal/pkg/power" + "github.com/hpcng/warewulf/internal/pkg/wwlog" + "github.com/spf13/cobra" +) + +func CobraRunE(cmd *cobra.Command, args []string) error { + var returnErr error = nil + + nodeDB, err := node.New() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Could not open node configuration: %s\n", err) + os.Exit(1) + } + + nodes, err := nodeDB.FindAllNodes() + if err != nil { + wwlog.Printf(wwlog.ERROR, "Cloud not get nodeList: %s\n", err) + os.Exit(1) + } + + if len(args) > 0 { + nodes = node.FilterByName(nodes, args) + } else { + cmd.Usage() + os.Exit(1) + } + + if len(nodes) == 0 { + fmt.Printf("No nodes found\n") + os.Exit(1) + } + + batchpool := batch.New(50) + jobcount := len(nodes) + results := make(chan power.IPMI, jobcount) + + for _, node := range nodes { + + if node.IpmiIpaddr.Get() == "" { + wwlog.Printf(wwlog.ERROR, "%s: No IPMI IP address\n", node.Id.Get()) + continue + } + var ipmiInterface = "lan" + if node.IpmiInterface.Get() != "" { + ipmiInterface = node.IpmiInterface.Get() + } + var ipmiPort = "623" + if node.IpmiPort.Get() != "" { + ipmiPort = node.IpmiPort.Get() + } + ipmiCmd := power.IPMI{ + NodeName: node.Id.Get(), + HostName: node.IpmiIpaddr.Get(), + Port: ipmiPort, + User: node.IpmiUserName.Get(), + Password: node.IpmiPassword.Get(), + Interface: ipmiInterface, + AuthType: "MD5", + } + + batchpool.Submit(func() { + ipmiCmd.PowerSoft() + results <- ipmiCmd + }) + + } + + batchpool.Run() + + close(results) + + for result := range results { + + out, err := result.Result() + + if err != nil { + wwlog.Printf(wwlog.ERROR, "%s: %s\n", result.NodeName, out) + returnErr = err + continue + } + + fmt.Printf("%s: %s\n", result.NodeName, out) + + } + + return returnErr +} diff --git a/internal/app/wwctl/power/soft/root.go b/internal/app/wwctl/power/soft/root.go new file mode 100644 index 00000000..41813cb6 --- /dev/null +++ b/internal/app/wwctl/power/soft/root.go @@ -0,0 +1,19 @@ +package powersoft + +import ( + "github.com/spf13/cobra" +) + +var ( + powerCmd = &cobra.Command{ + Use: "soft", + Short: "Gracefully shuts down the given node(s)", + Long: "This command will gracefully shutdown the given set of nodes.", + RunE: CobraRunE, + } +) + +// GetRootCommand returns the root cobra.Command for the application. +func GetCommand() *cobra.Command { + return powerCmd +} diff --git a/internal/pkg/power/ipmitool.go b/internal/pkg/power/ipmitool.go index b9e489b0..dcf33e66 100644 --- a/internal/pkg/power/ipmitool.go +++ b/internal/pkg/power/ipmitool.go @@ -95,6 +95,14 @@ func (ipmi *IPMI) PowerCycle() (string, error) { return ipmi.IPMICommand("chassis", "power", "cycle") } +func (ipmi *IPMI) PowerReset() (string, error) { + return ipmi.IPMICommand("chassis", "power", "reset") +} + +func (ipmi *IPMI) PowerSoft() (string, error) { + return ipmi.IPMICommand("chassis", "power", "soft") +} + func (ipmi *IPMI) PowerStatus() (string, error) { return ipmi.IPMICommand("chassis", "power", "status") } diff --git a/internal/pkg/power/power.go b/internal/pkg/power/power.go index 10bd60fc..2faa3e67 100644 --- a/internal/pkg/power/power.go +++ b/internal/pkg/power/power.go @@ -14,6 +14,18 @@ type PowerOffInterface interface { PowerOff() (result string, err error) } +type PowerResetInterface interface { + PowerReset() (result string, err error) +} + +type PowerSoftInterface interface { + PowerSoft() (result string, err error) +} + +type PowerCycleInterface interface { + PowerCycle() (result string, err error) +} + type PowerStatusInterface interface { PowerStatus() (result string, err error) }