diff --git a/pkg/baremetal/utils/raid/megactl/megactl.go b/pkg/baremetal/utils/raid/megactl/megactl.go index e83b6f1c52..038173d55d 100644 --- a/pkg/baremetal/utils/raid/megactl/megactl.go +++ b/pkg/baremetal/utils/raid/megactl/megactl.go @@ -615,6 +615,7 @@ func (adapter *MegaRaidAdaptor) BuildNoneRaid(devs []*baremetal.BaremetalStorage type StorcliAdaptor struct { Controller int + isSNEmpty bool sn string name string } @@ -622,6 +623,7 @@ type StorcliAdaptor struct { func newStorcliAdaptor() *StorcliAdaptor { return &StorcliAdaptor{ Controller: -1, + isSNEmpty: false, sn: "", name: "", } @@ -632,26 +634,56 @@ func (a StorcliAdaptor) key() string { } func (a *StorcliAdaptor) isComplete() bool { + if a.isSNEmpty { + return a.Controller >= 0 && a.name != "" + } return a.Controller >= 0 && a.name != "" && a.sn != "" } -func (a *StorcliAdaptor) parseLine(l string) { - parts := strings.Split(l, " = ") +func parseLineForStorcli(a *StorcliAdaptor, l string) { + controllerKey := "Controller" + productNameKey := "Product Name" + snKey := "Serial Number" + + if !regexp.MustCompile(fmt.Sprintf("^(%s|%s|%s)\\s*=", controllerKey, productNameKey, snKey)).Match([]byte(l)) { + return + } + + parts := strings.Split(l, "=") if len(parts) != 2 { return } - key := parts[0] - val := parts[1] + + trimBeginEndSpace := func(s string) string { + return strings.TrimLeft(strings.TrimRight(s, " "), " ") + } + + key := trimBeginEndSpace(parts[0]) + val := trimBeginEndSpace(parts[1]) + + if key == snKey && val == "" { + a.isSNEmpty = true + return + } + switch key { - case "Controller": + case controllerKey: a.Controller, _ = strconv.Atoi(val) - case "Serial Number": + case snKey: a.sn = val - case "Product Name": + case productNameKey: a.name = val } } +func (a *StorcliAdaptor) parseLine(l string) { + parseLineForStorcli(a, l) +} + +func (a *StorcliAdaptor) String() string { + return fmt.Sprintf("{controller: %d, isSNEmpty: %v, sn: %q, name: %s}", a.Controller, a.isSNEmpty, a.sn, a.name) +} + func (raid *MegaRaid) GetStorcliAdaptor() (map[string]*StorcliAdaptor, error) { ret := make(map[string]*StorcliAdaptor) cmd := GetCommand2("/call", "show", "|", "grep", "-iE", `'^(Controller|Product Name|Serial Number)\s='`) diff --git a/pkg/baremetal/utils/raid/megactl/megactl_test.go b/pkg/baremetal/utils/raid/megactl/megactl_test.go new file mode 100644 index 0000000000..c929641869 --- /dev/null +++ b/pkg/baremetal/utils/raid/megactl/megactl_test.go @@ -0,0 +1,118 @@ +// Copyright 2019 Yunion +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package megactl + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseLineForStorcli(t *testing.T) { + type testCase struct { + name string + adapter *StorcliAdaptor + lines []string + assertFunc func(t *testing.T, a *StorcliAdaptor) + } + + cases := []testCase{ + { + name: "Should complete", + adapter: new(StorcliAdaptor), + lines: []string{ + "Controller = 0", + "Product Name = SAS3108", + "Serial Number = 1234", + }, + assertFunc: func(t *testing.T, a *StorcliAdaptor) { + assert.Equal(t, true, a.isComplete(), a.String()) + assert.Equal(t, "SAS3108"+"1234", a.key()) + }, + }, + { + name: "Should complete when no space beside '='", + adapter: new(StorcliAdaptor), + lines: []string{ + "Controller=0", + "Product Name = SAS3108", + "Serial Number=1234", + }, + assertFunc: func(t *testing.T, a *StorcliAdaptor) { + assert.Equal(t, true, a.isComplete(), a.String()) + assert.Equal(t, "SAS3108"+"1234", a.key()) + }, + }, + { + name: "Should parse empty SN", + adapter: new(StorcliAdaptor), + lines: []string{ + "Controller = 0", + "Product Name = SAS3108", + "Serial Number =", + }, + assertFunc: func(t *testing.T, a *StorcliAdaptor) { + assert.Equal(t, true, a.isComplete(), a.String()) + assert.Equal(t, true, a.isSNEmpty, a.String()) + assert.Equal(t, "SAS3108", a.key()) + }, + }, + { + name: "Should parse empty SN end with space", + adapter: new(StorcliAdaptor), + lines: []string{ + "Controller = 0", + "Product Name = SAS3108", + "Serial Number = ", + }, + assertFunc: func(t *testing.T, a *StorcliAdaptor) { + assert.Equal(t, true, a.isComplete(), a.String()) + assert.Equal(t, true, a.isSNEmpty, a.String()) + assert.Equal(t, "SAS3108", a.key()) + }, + }, + { + name: "Should not complete when no product name", + adapter: new(StorcliAdaptor), + lines: []string{ + "Controller = 0", + "Serial Number = 1234", + }, + assertFunc: func(t *testing.T, a *StorcliAdaptor) { + assert.Equal(t, false, a.isComplete(), a.String()) + }, + }, + { + name: "Should not complete when no SN", + adapter: new(StorcliAdaptor), + lines: []string{ + "Controller = 0", + "Product Name = SAS3108", + }, + assertFunc: func(t *testing.T, a *StorcliAdaptor) { + assert.Equal(t, false, a.isComplete(), a.String()) + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + for _, l := range c.lines { + parseLineForStorcli(c.adapter, l) + } + c.assertFunc(t, c.adapter) + }) + } +}