Merge pull request #442 from gmkurtzer/4.3.0-ssh

Add feature to use wwctl to spawn ssh commands
This commit is contained in:
Gregory M. Kurtzer
2022-06-08 21:16:13 -07:00
committed by GitHub
3 changed files with 136 additions and 10 deletions

View File

@@ -9,9 +9,10 @@ import (
"github.com/hpcng/warewulf/internal/app/wwctl/power"
"github.com/hpcng/warewulf/internal/app/wwctl/profile"
"github.com/hpcng/warewulf/internal/app/wwctl/server"
"github.com/hpcng/warewulf/internal/app/wwctl/ssh"
"github.com/hpcng/warewulf/internal/app/wwctl/version"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/hpcng/warewulf/internal/pkg/help"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
@@ -21,12 +22,12 @@ import (
var (
rootCmd = &cobra.Command{
DisableFlagsInUseLine: true,
Use: "wwctl COMMAND [OPTIONS]",
Short: "Warewulf Control",
Long: "Control interface to the Warewulf Cluster Provisioning System.",
PersistentPreRunE: rootPersistentPreRunE,
SilenceUsage: true,
SilenceErrors: true,
Use: "wwctl COMMAND [OPTIONS]",
Short: "Warewulf Control",
Long: "Control interface to the Warewulf Cluster Provisioning System.",
PersistentPreRunE: rootPersistentPreRunE,
SilenceUsage: true,
SilenceErrors: true,
}
verboseArg bool
DebugFlag bool
@@ -36,8 +37,8 @@ func init() {
rootCmd.PersistentFlags().BoolVarP(&verboseArg, "verbose", "v", false, "Run with increased verbosity.")
rootCmd.PersistentFlags().BoolVarP(&DebugFlag, "debug", "d", false, "Run with debugging messages enabled.")
rootCmd.SetUsageTemplate(help.UsageTemplate)
rootCmd.SetHelpTemplate(help.HelpTemplate)
rootCmd.SetUsageTemplate(help.UsageTemplate)
rootCmd.SetHelpTemplate(help.HelpTemplate)
rootCmd.AddCommand(overlay.GetCommand())
rootCmd.AddCommand(container.GetCommand())
@@ -48,7 +49,7 @@ func init() {
rootCmd.AddCommand(configure.GetCommand())
rootCmd.AddCommand(server.GetCommand())
rootCmd.AddCommand(version.GetCommand())
rootCmd.AddCommand(ssh.GetCommand())
}
// GetRootCommand returns the root cobra.Command for the application.

View File

@@ -0,0 +1,94 @@
package ssh
import (
"bufio"
"bytes"
"fmt"
"os"
"os/exec"
"strings"
"time"
"github.com/hpcng/warewulf/internal/pkg/batch"
"github.com/hpcng/warewulf/internal/pkg/node"
"github.com/hpcng/warewulf/internal/pkg/wwlog"
"github.com/hpcng/warewulf/pkg/hostlist"
"github.com/spf13/cobra"
)
func CobraRunE(cmd *cobra.Command, args []string) error {
batchpool := batch.New(FanOut)
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, "Could not get node list: %s\n", err)
os.Exit(1)
}
if len(args) > 0 {
nodes = node.FilterByName(nodes, hostlist.Expand(args))
} else {
//nolint:errcheck
cmd.Usage()
os.Exit(1)
}
for _, node := range nodes {
if _, ok := node.NetDevs["default"]; !ok {
fmt.Fprintf(os.Stderr, "%s: Default network device doesn't exist\n", node.Id.Get())
continue
}
if node.NetDevs["default"].Ipaddr.Get() == "" {
fmt.Fprintf(os.Stderr, "%s: Default network IP address not configured\n", node.Id.Get())
continue
}
nodename := node.Id.Print()
var command []string
command = append(command, node.NetDevs["default"].Ipaddr.Get())
command = append(command, args[1:]...)
batchpool.Submit(func() {
if DryRun {
fmt.Printf("%s: %s %s\n", nodename, SshPath, strings.Join(command, " "))
} else {
wwlog.Printf(wwlog.DEBUG, "Sending command to node '%s': %s\n", nodename, command)
var stdout, stderr bytes.Buffer
cmd := exec.Command(SshPath, command...)
cmd.Stdin = os.Stdin
cmd.Stdout = &stdout
cmd.Stderr = &stderr
_ = cmd.Run()
scan_stdout := bufio.NewScanner(&stdout)
for scan_stdout.Scan() {
fmt.Printf("%s: %s\n", nodename, scan_stdout.Text())
}
scan_stderr := bufio.NewScanner(&stderr)
for scan_stderr.Scan() {
fmt.Fprintf(os.Stderr, "%s: %s\n", nodename, scan_stderr.Text())
}
}
//util.ExecInteractive(SshPath, command...)
time.Sleep(time.Duration(Sleep) * time.Second)
})
}
batchpool.Run()
return nil
}

View File

@@ -0,0 +1,31 @@
package ssh
import (
"github.com/spf13/cobra"
)
var (
baseCmd = &cobra.Command{
DisableFlagsInUseLine: true,
Use: "ssh [OPTIONS] NODE_PATTERN...",
Short: "SSH into configured nodes in parallel",
Long: "Easily ssh into nodes in parallel to run non-interactive commands\n",
RunE: CobraRunE,
}
DryRun bool
FanOut int
Sleep int
SshPath string
)
func init() {
baseCmd.PersistentFlags().BoolVarP(&DryRun, "dryrun", "n", false, "Show commands to run")
baseCmd.PersistentFlags().IntVarP(&FanOut, "fanout", "f", 32, "How many connections to run in parallel")
baseCmd.PersistentFlags().IntVarP(&Sleep, "sleep", "s", 0, "Seconds to sleep inbetween processes")
baseCmd.PersistentFlags().StringVar(&SshPath, "rsh", "/usr/bin/ssh", "Path to use for RSH/SSH command")
}
// GetRootCommand returns the root cobra.Command for the application.
func GetCommand() *cobra.Command {
return baseCmd
}