Handle missing ipmi fields during bmc commands
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
committed by
Jonathon Anderson
parent
099e7cd4ea
commit
ea9a013812
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
- Fixed panic in warewulfd if node netdev is only defined in a profile. #1817
|
||||
- Replaced unreadable >= character external sphinx module for pdf doc build.
|
||||
- Handle missing ipmi fields during bmc commands. #1768
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{IpmiConf: *node.Ipmi}
|
||||
ipmiCmd := bmc.TemplateStruct{IpmiConf: *node.Ipmi}
|
||||
if err := ipmiCmd.Console(); err != nil {
|
||||
wwlog.Error("%s: Console problem", node.Id())
|
||||
returnErr = err
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -44,14 +44,14 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(50)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
if node.Ipmi == nil || node.Ipmi.Ipaddr == nil || node.Ipmi.Ipaddr.IsUnspecified() {
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"sensors": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ sdr list",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" sdr list`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(vars.Fanout)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
@@ -48,7 +48,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"power cycle": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ chassis power cycle",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" chassis power cycle`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(vars.Fanout)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
@@ -48,7 +48,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"power off": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ chassis power off",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" chassis power off`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(vars.Fanout)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
@@ -49,7 +49,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"power on": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ chassis power on",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" chassis power on`,
|
||||
},
|
||||
}
|
||||
for name, tt := range tests {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(vars.Fanout)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
@@ -49,7 +49,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"power reset": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ chassis power reset",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" chassis power reset`,
|
||||
},
|
||||
}
|
||||
for name, tt := range tests {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(vars.Fanout)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
@@ -49,7 +49,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"power soft": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ chassis power soft",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" chassis power soft`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/warewulf/warewulf/internal/pkg/batch"
|
||||
"github.com/warewulf/warewulf/internal/pkg/bmc"
|
||||
"github.com/warewulf/warewulf/internal/pkg/hostlist"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/power"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
|
||||
batchpool := batch.New(50)
|
||||
jobcount := len(nodes)
|
||||
results := make(chan power.IPMI, jobcount)
|
||||
results := make(chan bmc.TemplateStruct, jobcount)
|
||||
|
||||
for _, node := range nodes {
|
||||
|
||||
@@ -48,7 +48,7 @@ func CobraRunE(vars *variables) func(cmd *cobra.Command, args []string) (err err
|
||||
wwlog.Error("%s: No IPMI IP address", node.Id())
|
||||
continue
|
||||
}
|
||||
ipmiCmd := power.IPMI{
|
||||
ipmiCmd := bmc.TemplateStruct{
|
||||
IpmiConf: *node.Ipmi,
|
||||
ShowOnly: vars.Showcmd,
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ nodes:
|
||||
}{
|
||||
"sensors": {
|
||||
args: []string{"--show", "n01"},
|
||||
expected: "10.10.10.10: ipmitool -I lan -H 10.10.10.10 -p 623 -U admin -P admin -e ~ chassis power status",
|
||||
expected: `10.10.10.10: ipmitool -H 10.10.10.10 -U "admin" -P "admin" chassis power status`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
136
internal/pkg/bmc/bmc.go
Normal file
136
internal/pkg/bmc/bmc.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package bmc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
err error
|
||||
out string
|
||||
}
|
||||
|
||||
type TemplateStruct struct {
|
||||
node.IpmiConf
|
||||
ShowOnly bool
|
||||
Cmd string
|
||||
result Result
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) Result() (string, error) {
|
||||
return tstruct.result.out, tstruct.result.err
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) getCommand() (cmdStr string, err error) {
|
||||
if tstruct.Template == "" {
|
||||
return "", fmt.Errorf("no bmc template specified")
|
||||
}
|
||||
if !strings.HasPrefix(tstruct.Template, "/") {
|
||||
conf := warewulfconf.Get()
|
||||
tstruct.Template = path.Join(conf.Paths.Datadir, "warewulf/bmc", tstruct.Template)
|
||||
}
|
||||
fbuf, err := os.ReadFile(tstruct.Template)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't find the template which defines the bmc command: %s", err)
|
||||
}
|
||||
cmdTmpl, err := template.New("bmc command").Funcs(sprig.TxtFuncMap()).Parse(string(fbuf))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var tbuffer bytes.Buffer
|
||||
err = cmdTmpl.Execute(&tbuffer, *tstruct)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cmdStr = strings.TrimSpace(tbuffer.String())
|
||||
wwlog.Debug("bmc command: %s", cmdStr)
|
||||
return cmdStr, nil
|
||||
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) runCommand() ([]byte, error) {
|
||||
cmdStr, err := tstruct.getCommand()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
if tstruct.ShowOnly {
|
||||
return []byte(cmdStr), nil
|
||||
}
|
||||
return exec.Command("/bin/sh", "-c", cmdStr).CombinedOutput()
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) runInteractiveCommand() (err error) {
|
||||
cmdStr, err := tstruct.getCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.Command("/bin/sh", "-c", cmdStr)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) InteractiveCommand(cmd string) error {
|
||||
tstruct.Cmd = cmd
|
||||
return tstruct.runInteractiveCommand()
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) Command(cmd string) (string, error) {
|
||||
tstruct.Cmd = cmd
|
||||
out, err := tstruct.runCommand()
|
||||
tstruct.result.out = strings.TrimSpace(string(out))
|
||||
tstruct.result.err = err
|
||||
return tstruct.result.out, tstruct.result.err
|
||||
}
|
||||
|
||||
/*
|
||||
Just define meta commands here, implementation is in the template
|
||||
*/
|
||||
|
||||
func (tstruct *TemplateStruct) PowerOn() (string, error) {
|
||||
return tstruct.Command("PowerOn")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) PowerOff() (string, error) {
|
||||
return tstruct.Command("PowerOff")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) PowerCycle() (string, error) {
|
||||
return tstruct.Command("PowerCycle")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) PowerReset() (string, error) {
|
||||
return tstruct.Command("PowerReset")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) PowerSoft() (string, error) {
|
||||
return tstruct.Command("PowerSoft")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) PowerStatus() (string, error) {
|
||||
return tstruct.Command("PowerStatus")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) SDRList() (string, error) {
|
||||
return tstruct.Command("SDRList")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) SensorList() (string, error) {
|
||||
return tstruct.Command("SensorList")
|
||||
}
|
||||
|
||||
func (tstruct *TemplateStruct) Console() error {
|
||||
return tstruct.InteractiveCommand("Console")
|
||||
}
|
||||
81
internal/pkg/bmc/bmc_test.go
Normal file
81
internal/pkg/bmc/bmc_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package bmc
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/testenv"
|
||||
)
|
||||
|
||||
func Test_Ipmitool(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
bmc TemplateStruct
|
||||
err bool
|
||||
cmdStr string
|
||||
}{
|
||||
"no template": {
|
||||
bmc: TemplateStruct{},
|
||||
err: true,
|
||||
},
|
||||
"ipmitool PowerStatus empty": {
|
||||
bmc: TemplateStruct{
|
||||
Cmd: "PowerStatus",
|
||||
IpmiConf: node.IpmiConf{
|
||||
Template: "ipmitool.tmpl",
|
||||
},
|
||||
},
|
||||
cmdStr: `ipmitool chassis power status`,
|
||||
},
|
||||
"ipmitool PowerStatus full": {
|
||||
bmc: TemplateStruct{
|
||||
Cmd: "PowerStatus",
|
||||
IpmiConf: node.IpmiConf{
|
||||
Template: "ipmitool.tmpl",
|
||||
Interface: "lanplus",
|
||||
EscapeChar: "~",
|
||||
Port: "687",
|
||||
Ipaddr: net.IP{192, 168, 1, 100},
|
||||
UserName: "root",
|
||||
Password: "calvin",
|
||||
},
|
||||
},
|
||||
cmdStr: `ipmitool -I lanplus -e ~ -p 687 -H 192.168.1.100 -U "root" -P "calvin" chassis power status`,
|
||||
},
|
||||
"nobmc PowerStatus full": {
|
||||
bmc: TemplateStruct{
|
||||
Cmd: "PowerStatus",
|
||||
IpmiConf: node.IpmiConf{
|
||||
Template: "nobmc.tmpl",
|
||||
Interface: "lanplus",
|
||||
EscapeChar: "~",
|
||||
Port: "687",
|
||||
Ipaddr: net.IP{192, 168, 1, 100},
|
||||
UserName: "root",
|
||||
Password: "calvin",
|
||||
},
|
||||
},
|
||||
cmdStr: `ping -c 1 "192.168.1.100" &> /dev/null && echo ON || echo OFF`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, test := range tests {
|
||||
env := testenv.New(t)
|
||||
defer env.RemoveAll()
|
||||
env.ImportFile("usr/share/warewulf/bmc/ipmitool.tmpl", "../../../lib/warewulf/bmc/ipmitool.tmpl")
|
||||
env.ImportFile("usr/share/warewulf/bmc/nobmc.tmpl", "../../../lib/warewulf/bmc/nobmc.tmpl")
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
cmdStr, err := test.bmc.getCommand()
|
||||
if test.err {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
if test.cmdStr != "" {
|
||||
assert.Equal(t, test.cmdStr, cmdStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package power
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
warewulfconf "github.com/warewulf/warewulf/internal/pkg/config"
|
||||
"github.com/warewulf/warewulf/internal/pkg/node"
|
||||
"github.com/warewulf/warewulf/internal/pkg/wwlog"
|
||||
)
|
||||
|
||||
type IPMIResult struct {
|
||||
err error
|
||||
out string
|
||||
}
|
||||
|
||||
type IPMI struct {
|
||||
node.IpmiConf
|
||||
ShowOnly bool
|
||||
Cmd string
|
||||
result IPMIResult
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) Result() (string, error) {
|
||||
return ipmi.result.out, ipmi.result.err
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) getStr() (cmdStr string, err error) {
|
||||
if ipmi.Template == "" {
|
||||
return "", fmt.Errorf("no ipmi/bmc template specified")
|
||||
}
|
||||
if !strings.HasPrefix(ipmi.Template, "/") {
|
||||
conf := warewulfconf.Get()
|
||||
ipmi.Template = path.Join(conf.Paths.Datadir, "warewulf/bmc", ipmi.Template)
|
||||
}
|
||||
fbuf, err := os.ReadFile(ipmi.Template)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("couldn't find the template which defines the ipmi/bmc command: %s", err)
|
||||
}
|
||||
cmdTmpl, err := template.New("bmc command").Parse(string(fbuf))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var tbuffer bytes.Buffer
|
||||
err = cmdTmpl.Execute(&tbuffer, *ipmi)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rg := regexp.MustCompile(`(\r\n?|\n){2,}`)
|
||||
cmdStr = rg.ReplaceAllString(tbuffer.String(), " ")
|
||||
wwlog.Debug("bmc string is: %s", strings.TrimSpace(cmdStr))
|
||||
return strings.TrimSpace(cmdStr), nil
|
||||
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) Command() ([]byte, error) {
|
||||
cmdStr, err := ipmi.getStr()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
if ipmi.ShowOnly {
|
||||
return []byte(cmdStr), nil
|
||||
}
|
||||
ipmiCmd := exec.Command("/bin/sh", "-c", cmdStr)
|
||||
return ipmiCmd.CombinedOutput()
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) InteractiveCommand() (err error) {
|
||||
cmdStr, err := ipmi.getStr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipmiCmd := exec.Command("/bin/sh", "-c", cmdStr)
|
||||
ipmiCmd.Stdout = os.Stdout
|
||||
ipmiCmd.Stdin = os.Stdin
|
||||
ipmiCmd.Stderr = os.Stderr
|
||||
return ipmiCmd.Run()
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) IPMIInteractiveCommand(cmd string) error {
|
||||
ipmi.Cmd = cmd
|
||||
return ipmi.InteractiveCommand()
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) IPMICommand(cmd string) (string, error) {
|
||||
ipmi.Cmd = cmd
|
||||
ipmiOut, err := ipmi.Command()
|
||||
ipmi.result.out = strings.TrimSpace(string(ipmiOut))
|
||||
ipmi.result.err = err
|
||||
return ipmi.result.out, ipmi.result.err
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Just define meta commands here, implementation is in the template
|
||||
*/
|
||||
|
||||
func (ipmi *IPMI) PowerOn() (string, error) {
|
||||
return ipmi.IPMICommand("PowerOn")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) PowerOff() (string, error) {
|
||||
return ipmi.IPMICommand("PowerOff")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) PowerCycle() (string, error) {
|
||||
return ipmi.IPMICommand("PowerCycle")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) PowerReset() (string, error) {
|
||||
return ipmi.IPMICommand("PowerReset")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) PowerSoft() (string, error) {
|
||||
return ipmi.IPMICommand("PowerSoft")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) PowerStatus() (string, error) {
|
||||
return ipmi.IPMICommand("PowerStatus")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) SDRList() (string, error) {
|
||||
return ipmi.IPMICommand("SDRList")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) SensorList() (string, error) {
|
||||
return ipmi.IPMICommand("SensorList")
|
||||
}
|
||||
|
||||
func (ipmi *IPMI) Console() error {
|
||||
return ipmi.IPMIInteractiveCommand("Console")
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
{{/* used command to access the ipmi interface of the nodes */}}
|
||||
{{- $escapechar := "~" }}{{ if .EscapeChar }}{{ $escapechar = .EscapeChar }}{{ end }}
|
||||
{{- $port := "623" }}{{ if .Port }}{{ $port = .Port }}{{ end }}
|
||||
{{- $interface := "lan" }}{{- if .Interface }}{{ $interface = .Interface }}{{ end }}
|
||||
{{- $args := "" }}
|
||||
{{- if eq .Cmd "PowerOn" }}{{ $args = "chassis power on" }}
|
||||
{{- else if eq .Cmd "PowerOff" }}{{ $args = "chassis power off" }}
|
||||
{{- else if eq .Cmd "PowerCycle" }}{{ $args = "chassis power cycle" }}
|
||||
{{- else if eq .Cmd "PowerReset" }}{{ $args = "chassis power reset" }}
|
||||
{{- else if eq .Cmd "PowerSoft" }}{{ $args = "chassis power soft" }}
|
||||
{{- else if eq .Cmd "PowerStatus" }}{{ $args = "chassis power status" }}
|
||||
{{- else if eq .Cmd "SDRList" }}{{ $args = "sdr list" }}
|
||||
{{- else if eq .Cmd "SensorList" }}{{ $args = "sensor list" }}
|
||||
{{- else if eq .Cmd "Console" }}{{ $args = "sol activate" }}{{ end }}
|
||||
ipmitool -I {{ $interface }} -H {{ .Ipaddr }} -p {{ $port }} -U {{ .UserName }} -P {{ .Password }} -e {{ $escapechar }} {{ $args }}
|
||||
{{ $cmd := "ipmitool" }}
|
||||
{{ if .Interface }}{{ $cmd = cat $cmd "-I" .Interface }}{{ end }}
|
||||
{{ if .EscapeChar }}{{ $cmd = cat $cmd "-e" .EscapeChar }}{{ end }}
|
||||
{{ if .Port }}{{ $cmd = cat $cmd "-p" .Port }}{{ end }}
|
||||
{{ if .Ipaddr }}{{ $cmd = cat $cmd "-H" .Ipaddr }}{{ end }}
|
||||
{{ if .UserName }}{{ $cmd = cat $cmd "-U" (printf "\"%s\"" .UserName) }}{{ end }}
|
||||
{{ if .Password }}{{ $cmd = cat $cmd "-P" (printf "\"%s\"" .Password) }}{{ end }}
|
||||
{{ if eq .Cmd "PowerOn" }}{{ $cmd = cat $cmd "chassis power on" }}
|
||||
{{ else if eq .Cmd "PowerOff" }}{{ $cmd = cat $cmd "chassis power off" }}
|
||||
{{ else if eq .Cmd "PowerCycle" }}{{ $cmd = cat $cmd "chassis power cycle" }}
|
||||
{{ else if eq .Cmd "PowerReset" }}{{ $cmd = cat $cmd "chassis power reset" }}
|
||||
{{ else if eq .Cmd "PowerSoft" }}{{ $cmd = cat $cmd "chassis power soft" }}
|
||||
{{ else if eq .Cmd "PowerStatus" }}{{ $cmd = cat $cmd "chassis power status" }}
|
||||
{{ else if eq .Cmd "SDRList" }}{{ $cmd = cat $cmd "sdr list" }}
|
||||
{{ else if eq .Cmd "SensorList" }}{{ $cmd = cat $cmd "sensor list" }}
|
||||
{{ else if eq .Cmd "Console" }}{{ $cmd = cat $cmd "sol activate" }}
|
||||
{{ end }}
|
||||
{{- $cmd -}}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
{{/* used command to access nodes without bmc*/}}
|
||||
{{- if eq .Cmd "PowerOn" }}wol {{ .Interface }}
|
||||
{{- else if eq .Cmd "PowerOff" }}ssh {{ .Ipaddr }} echo o > /proc/sysrq-trigger
|
||||
{{- else if eq .Cmd "PowerCycle" }}ssh {{ .Ipaddr }} echo r > /proc/sysrq-trigger
|
||||
{{- else if eq .Cmd "PowerReset" }}ssh {{ .Ipaddr }} echo r > /proc/sysrq-trigger
|
||||
{{- else if eq .Cmd "PowerSoft" }}ssh {{ .Ipaddr }} reboot
|
||||
{{- else if eq .Cmd "PowerStatus" }}ping -c 1 {{ .Ipaddr }} &> /dev/null && echo ON || echo OFF
|
||||
{{- else if eq .Cmd "SDRList" }}ssh {{ .Ipaddr }} sensors
|
||||
{{- else if eq .Cmd "SensorList" }}ssh {{ .Ipaddr }} sensors
|
||||
{{- else if eq .Cmd "Console" }}echo node sol
|
||||
{{- if eq .Cmd "PowerOn" }}wol "{{ .Interface }}"
|
||||
{{- else if eq .Cmd "PowerOff" }}ssh "{{ .Ipaddr }}" echo o > /proc/sysrq-trigger
|
||||
{{- else if eq .Cmd "PowerCycle" }}ssh "{{ .Ipaddr }}" echo r > /proc/sysrq-trigger
|
||||
{{- else if eq .Cmd "PowerReset" }}ssh "{{ .Ipaddr }}" echo r > /proc/sysrq-trigger
|
||||
{{- else if eq .Cmd "PowerSoft" }}ssh "{{ .Ipaddr }}" reboot
|
||||
{{- else if eq .Cmd "PowerStatus" }}ping -c 1 "{{ .Ipaddr }}" &> /dev/null && echo ON || echo OFF
|
||||
{{- else if eq .Cmd "SDRList" }}ssh "{{ .Ipaddr }}" sensors
|
||||
{{- else if eq .Cmd "SensorList" }}ssh "{{ .Ipaddr }}" sensors
|
||||
{{- else if eq .Cmd "Console" }}echo "sol not available"
|
||||
{{- else }}echo "command not found"{{ end }}
|
||||
|
||||
@@ -75,25 +75,24 @@ configured with a template which defines Warewulf's IPMI behavior.
|
||||
|
||||
.. code-block::
|
||||
|
||||
{{/* used command to access the ipmi interface of the nodes */}}
|
||||
{{- $escapechar := "~" }}
|
||||
{{- $port := "623" }}
|
||||
{{- $interface := "lan" }}
|
||||
{{- $args := "" }}
|
||||
{{- if .EscapeChar }} $escapechar = .EscapeChar {{ end }}
|
||||
{{- if .Port }} {{ $port = .Port }} {{ end }}
|
||||
{{- if .Interface }} {{ $interface = .Interface }} {{ end }}
|
||||
{{- if eq .Cmd "PowerOn" }} {{ $args = "chassis power on" }} {{ end }}
|
||||
{{- if eq .Cmd "PowerOff" }} {{ $args = "chassis power off" }} {{ end }}
|
||||
{{- if eq .Cmd "PowerCycle" }} {{ $args = "chassis power cycle" }} {{ end }}
|
||||
{{- if eq .Cmd "PowerReset" }} {{ $args = "chassis power reset" }} {{ end }}
|
||||
{{- if eq .Cmd "PowerSoft" }} {{ $args = "chassis power soft" }} {{ end }}
|
||||
{{- if eq .Cmd "PowerStatus" }} {{ $args = "chassis power status" }} {{ end }}
|
||||
{{- if eq .Cmd "SDRList" }} {{ $args = "sdr list" }} {{ end }}
|
||||
{{- if eq .Cmd "SensorList" }} {{ $args = "sensor list" }} {{ end }}
|
||||
{{- if eq .Cmd "Console" }} {{ $args = "sol activate" }} {{ end }}
|
||||
{{- $cmd := printf "ipmitool -I %s -H %s -p %s -U %s -P %s -e %s %s" $interface .Ipaddr $port .UserName .Password $escapechar $args }}
|
||||
{{ $cmd }}
|
||||
{{ $cmd := "ipmitool" }}
|
||||
{{ if .Interface }}{{ $cmd = cat $cmd "-I" .Interface }}{{ end }}
|
||||
{{ if .EscapeChar }}{{ $cmd = cat $cmd "-e" .EscapeChar }}{{ end }}
|
||||
{{ if .Port }}{{ $cmd = cat $cmd "-p" .Port }}{{ end }}
|
||||
{{ if .Ipaddr }}{{ $cmd = cat $cmd "-H" .Ipaddr }}{{ end }}
|
||||
{{ if .UserName }}{{ $cmd = cat $cmd "-U" (printf "\"%s\"" .UserName) }}{{ end }}
|
||||
{{ if .Password }}{{ $cmd = cat $cmd "-P" (printf "\"%s\"" .Password) }}{{ end }}
|
||||
{{ if eq .Cmd "PowerOn" }}{{ $cmd = cat $cmd "chassis power on" }}
|
||||
{{ else if eq .Cmd "PowerOff" }}{{ $cmd = cat $cmd "chassis power off" }}
|
||||
{{ else if eq .Cmd "PowerCycle" }}{{ $cmd = cat $cmd "chassis power cycle" }}
|
||||
{{ else if eq .Cmd "PowerReset" }}{{ $cmd = cat $cmd "chassis power reset" }}
|
||||
{{ else if eq .Cmd "PowerSoft" }}{{ $cmd = cat $cmd "chassis power soft" }}
|
||||
{{ else if eq .Cmd "PowerStatus" }}{{ $cmd = cat $cmd "chassis power status" }}
|
||||
{{ else if eq .Cmd "SDRList" }}{{ $cmd = cat $cmd "sdr list" }}
|
||||
{{ else if eq .Cmd "SensorList" }}{{ $cmd = cat $cmd "sensor list" }}
|
||||
{{ else if eq .Cmd "Console" }}{{ $cmd = cat $cmd "sol activate" }}
|
||||
{{ end }}
|
||||
{{- $cmd -}}
|
||||
|
||||
A different template can be used to change the IPMI behavior using the
|
||||
``--ipmitemplate`` field. Referenced templates must be located in
|
||||
|
||||
Reference in New Issue
Block a user