mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-08-28 19:45:07 +08:00
fix(baremetal): tolerate ipmitool exit 1 when lan output is usable (#25291)
Some BMCs return exit status 1 from ipmitool lan print even with valid LAN config, which caused IPMI probe to fail with no IPMI lan NotFoundError.
This commit is contained in:
@@ -158,12 +158,47 @@ func (ipmi *LanPlusIPMI) GetCommand(args ...string) (*procutils.Command, context
|
||||
return procutils.NewCommandContext(ctx, "ipmitool", nArgs...), cancel
|
||||
}
|
||||
|
||||
// ipmitool may exit with status 1 while still printing usable output (e.g. lan print
|
||||
// when optional LAN parameters fail to read). Treat exit 1 as success only when output
|
||||
// is non-empty and does not look like a hard failure.
|
||||
var ipmitoolErrorSubstrings = []string{
|
||||
"Unable to establish IPMI",
|
||||
"Unable to open interface",
|
||||
"Authentication failed",
|
||||
"Password verification failed",
|
||||
"Invalid user name",
|
||||
"Insufficient privilege level",
|
||||
"Invalid command",
|
||||
"Command not supported in present state",
|
||||
"Get Channel Info command failed",
|
||||
"Invalid channel",
|
||||
"Error: Unable to open",
|
||||
}
|
||||
|
||||
func ipmitoolOutputAcceptable(out []byte) bool {
|
||||
if len(out) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(out)
|
||||
for _, p := range ipmitoolErrorSubstrings {
|
||||
if strings.Contains(s, p) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (ipmi *LanPlusIPMI) ExecuteCommand(args ...string) ([]string, error) {
|
||||
cmd, cancel := ipmi.GetCommand(args...)
|
||||
defer cancel()
|
||||
log.Debugf("[LanPlusIPMI] execute command: %s", cmd.String())
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
exitCode, ok := cmd.GetExitStatus(err)
|
||||
if ok && exitCode == 1 && ipmitoolOutputAcceptable(out) {
|
||||
log.Warningf("[LanPlusIPMI] command %s exited with status 1 but output looks usable", cmd.String())
|
||||
return ssh.ParseOutput(out), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return ssh.ParseOutput(out), nil
|
||||
|
||||
@@ -21,6 +21,48 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
)
|
||||
|
||||
func TestIpmitoolOutputAcceptable(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
out string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
out: "",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "valid lan print with exit 1 style output",
|
||||
out: `Set in Progress : Set Complete
|
||||
IP Address Source : Static Address
|
||||
IP Address : 10.127.223.102
|
||||
Subnet Mask : 255.255.255.0
|
||||
MAC Address : aa:bb:cc:dd:ee:ff
|
||||
Default Gateway IP : 10.127.223.1`,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "auth failure",
|
||||
out: "Error: Unable to establish IPMI v2 / RMCP+ session\n",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid channel",
|
||||
out: "Get Channel Info command failed\nInvalid channel: 8\n",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ipmitoolOutputAcceptable([]byte(tt.out))
|
||||
if got != tt.want {
|
||||
t.Errorf("ipmitoolOutputAcceptable() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSysInfo(t *testing.T) {
|
||||
type args struct {
|
||||
exector IPMIExecutor
|
||||
|
||||
Reference in New Issue
Block a user