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:
Zexi Li
2026-08-07 17:22:56 +08:00
committed by GitHub
parent 2730639e1b
commit 3270a5dd79
2 changed files with 77 additions and 0 deletions
+35
View File
@@ -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