From a63bac656c979826627dc4b82d13530484fe7b1f Mon Sep 17 00:00:00 2001 From: Zexi Li Date: Wed, 19 Dec 2018 21:38:25 +0800 Subject: [PATCH] baremetal: megactl --- pkg/baremetal/tasks/baseprepare.go | 2 + .../utils/detect_storages/detect_storages.go | 11 + pkg/baremetal/utils/ipmitool/ipmitool.go | 35 +- pkg/baremetal/utils/megactl/megactl.go | 375 ++++++++++++++++++ 4 files changed, 401 insertions(+), 22 deletions(-) create mode 100644 pkg/baremetal/utils/megactl/megactl.go diff --git a/pkg/baremetal/tasks/baseprepare.go b/pkg/baremetal/tasks/baseprepare.go index 6405e65801..27a235a028 100644 --- a/pkg/baremetal/tasks/baseprepare.go +++ b/pkg/baremetal/tasks/baseprepare.go @@ -14,6 +14,7 @@ import ( "yunion.io/x/onecloud/pkg/baremetal/profiles" "yunion.io/x/onecloud/pkg/baremetal/sysutils" "yunion.io/x/onecloud/pkg/baremetal/types" + "yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages" "yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool" "yunion.io/x/onecloud/pkg/mcclient" "yunion.io/x/onecloud/pkg/mcclient/modules" @@ -57,6 +58,7 @@ func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error { return err } // TODO: diskinfo + raidDiskInfo, nonRaidDiskInfo, pcieDiskInfo := detect_storages.DetectStorageInfo(cli, true) ipmiEnable, err := isIPMIEnable(cli) if err != nil { diff --git a/pkg/baremetal/utils/detect_storages/detect_storages.go b/pkg/baremetal/utils/detect_storages/detect_storages.go index f695bbbdfb..ca4d4914bd 100644 --- a/pkg/baremetal/utils/detect_storages/detect_storages.go +++ b/pkg/baremetal/utils/detect_storages/detect_storages.go @@ -1,9 +1,20 @@ package detect_storages import ( + "fmt" + + "yunion.io/x/onecloud/pkg/baremetal/sysutils" "yunion.io/x/onecloud/pkg/util/ssh" ) func DetectStorageInfo(cli *ssh.Client, wait bool) (interface{}, error) { + raidDiskInfo := nil + + pcieRet, err := cli.Run("/lib/mos/lsdisk --pcie") + if err != nil { + return nil, fmt.Errorf("Fail to retrieve PCIE DISK info") + } + pcieDiskInfo := sysutils.ParsePCIEDiskInfo(pcieRet) + return nil, nil } diff --git a/pkg/baremetal/utils/ipmitool/ipmitool.go b/pkg/baremetal/utils/ipmitool/ipmitool.go index 9834274b84..f63e52ce1d 100644 --- a/pkg/baremetal/utils/ipmitool/ipmitool.go +++ b/pkg/baremetal/utils/ipmitool/ipmitool.go @@ -9,6 +9,7 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/tristate" + "yunion.io/x/pkg/util/stringutils" "yunion.io/x/pkg/utils" "yunion.io/x/onecloud/pkg/baremetal/profiles" @@ -119,16 +120,6 @@ func (ipmi *LanPlusIPMI) ExecuteCommand(args ...string) ([]string, error) { return ssh.ParseOutput(out), nil } -func splitKeyValue(line string) (*string, *string) { - pos := strings.Index(line, ":") - if pos > 0 { - key := strings.TrimSpace(line[:pos]) - val := strings.TrimSpace(line[pos+1:]) - return &key, &val - } - return nil, nil -} - func GetSysInfo(exector IPMIExecutor) (*types.IPMISystemInfo, error) { // TODO: do cache args := []string{"fru", "print", "0"} @@ -148,11 +139,11 @@ func GetSysInfo(exector IPMIExecutor) (*types.IPMISystemInfo, error) { } for _, line := range lines { - key, val := splitKeyValue(line) - if key != nil { + key, val := stringutils.SplitKeyValue(line) + if key != "" { for n, v := range keys { - if _, ok := ret[n]; v == *key && !ok { - ret[n] = *val + if _, ok := ret[n]; v == key && !ok { + ret[n] = val } } } @@ -188,23 +179,23 @@ func GetLanConfig(exector IPMIExecutor, channel int) (*types.IPMILanConfig, erro } ret := new(types.IPMILanConfig) for _, line := range lines { - key, val := splitKeyValue(line) - if key == nil { + key, val := stringutils.SplitKeyValue(line) + if key == "" { continue } - switch *key { + switch key { case "IP Address Source": - if *val == "Static Address" { + if val == "Static Address" { ret.IPSrc = "static" } case "IP Address": - ret.IPAddr = *val + ret.IPAddr = val case "Subnet Mask": - ret.Netmask = *val + ret.Netmask = val case "MAC Address": - ret.Mac, _ = net.ParseMAC(*val) + ret.Mac, _ = net.ParseMAC(val) case "Default Gateway IP": - ret.Gateway = *val + ret.Gateway = val } } return ret, nil diff --git a/pkg/baremetal/utils/megactl/megactl.go b/pkg/baremetal/utils/megactl/megactl.go new file mode 100644 index 0000000000..de9ccc3bc7 --- /dev/null +++ b/pkg/baremetal/utils/megactl/megactl.go @@ -0,0 +1,375 @@ +package megactl + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "yunion.io/x/log" + "yunion.io/x/pkg/tristate" + "yunion.io/x/pkg/util/stringutils" + "yunion.io/x/pkg/utils" + + "yunion.io/x/onecloud/pkg/compute/baremetal" + "yunion.io/x/onecloud/pkg/util/ssh" +) + +var ( + sizePattern = regexp.MustCompile(`(?P0x[0-9a-fA-F]+)`) + adapterPatter = regexp.MustCompile(`^Adapter #(?P[0-9]+)`) +) + +type MegaRaidPhyDev struct { + enclosure int + slot int + minStripSize int + maxStripSize int + model string + rotate tristate.TriState + adapter int + status string + driver string + sector int + block int +} + +func NewMegaRaidPhyDev() *MegaRaidPhyDev { + return &MegaRaidPhyDev{ + enclosure: -1, + slot: -1, + minStripSize: -1, + maxStripSize: -1, + rotate: tristate.None, + driver: baremetal.DISK_DRIVER_MEGARAID, + sector: -1, + block: 512, + } +} + +func (dev *MegaRaidPhyDev) GetSize() int { + return dev.sector * dev.block / 1024 / 1024 // MB +} + +func (dev *MegaRaidPhyDev) parseLine(line string) bool { + key, val := stringutils.SplitKeyValue(line) + if key == "" { + return false + } + switch key { + case "Media Type": + if val == "Hard Disk Device" { + dev.rotate = tristate.True + } else { + dev.rotate = tristate.False + } + case "Enclosure Device ID": + enclosure, err := strconv.Atoi(val) + if err == nil { + dev.enclosure = enclosure + } + case "Slot Number": + dev.slot, _ = strconv.Atoi(val) + case "Coerced Size": + matches := sizePattern.FindStringSubmatch(val) + if len(matches) != 0 { + paramsMap := make(map[string]string) + for i, name := range sizePattern.SubexpNames() { + if i > 0 && i <= len(matches) { + paramsMap[name] = matches[i] + } + } + sizeStr := paramsMap["sector"] + sector, err := strconv.ParseInt(sizeStr, 16, 32) + if err != nil { + dev.sector = 0 + } + dev.sector = int(sector) + } else { + dev.sector = 0 + } + case "Inquiry Data": + dev.model = strings.Join(regexp.MustCompile(`\s+`).Split(val, -1), " ") + case "Firmware state": + if val == "JBOD" { + dev.status = "jbod" + } else if strings.Contains(strings.ToLower(val), "online") { + dev.status = "online" + } else { + dev.status = "offline" + } + case "Logical Sector Size": + block, err := strconv.Atoi(val) + if err != nil { + log.Errorf("parse logical sector size error: %v", err) + dev.block = 512 + } else { + dev.block = block + } + } + return true +} + +func (dev *MegaRaidPhyDev) parseStripSize(lines []string) bool { + size2Int := func(sizeStr string) int { + // TODO + return -1 + } + for _, line := range lines { + if strings.Contains(line, "Min") { + dev.minStripSize = size2Int(strings.Split(line, ": ")[1]) + } + if strings.Contains(line, "Max") { + dev.maxStripSize = size2Int(strings.Split(line, ": ")[1]) + } + } + return true +} + +func (dev *MegaRaidPhyDev) isComplete() bool { + if dev.model == "" { + return false + } + if dev.sector < 0 { + return false + } + if dev.block < 0 { + return false + } + if dev.slot < 0 { + return false + } + if dev.rotate.IsNone() { + return false + } + if dev.status == "" { + return false + } + return true +} + +func (dev *MegaRaidPhyDev) isJBOD() bool { + return dev.status == "jbod" +} + +func (dev *MegaRaidPhyDev) String() string { + if dev.enclosure < 0 { + return fmt.Sprintf(":%d", dev.slot) + } + return fmt.Sprintf("%d:%d", dev.enclosure, dev.slot) +} + +type MegaRaidAdaptor struct { + index int + raid *MegaRaid + devs []*MegaRaidPhyDev + lvs []interface{} +} + +func NewMegaRaidAdaptor(index int, raid *MegaRaid) *MegaRaidAdaptor { + return &MegaRaidAdaptor{ + index: index, + raid: raid, + } +} + +func (adapter *MegaRaidAdaptor) AddPhyDev(dev *MegaRaidPhyDev) { + dev.adapter = adapter.index + adapter.devs = append(adapter.devs, dev) +} + +func (adapter *MegaRaidAdaptor) GetPhyDevs() []*MegaRaidPhyDev { + return adapter.devs +} + +func (adapter *MegaRaidAdaptor) getLogicVolumes() []int { + cmd := GetCommand("-LDInfo", "-Lall", fmt.Sprintf("-a%d", adapter.index)) + ret, err := adapter.remoteRun(cmd) + if err != nil { + return nil + } + return adapter.parseLogicVolumes(ret) +} + +func (adapter *MegaRaidAdaptor) parseLogicVolumes(lines []string) []int { + lvIdx := []int{} + for _, line := range lines { + key, val := stringutils.SplitKeyValue(line) + if key != "" && key == "Virtual Drive" { + idx, _ := strconv.Atoi(strings.Split(val, " ")[0]) + lvIdx = append(lvIdx, idx) + } + } + return lvIdx +} + +func (adapter *MegaRaidAdaptor) BuildRaid(confs interface{}) bool { + +} + +func (adapter *MegaRaidAdaptor) getTerm() *ssh.Client { + return adapter.raid.term +} + +func (adapter *MegaRaidAdaptor) remoteRun(cmds ...string) ([]string, error) { + return adapter.getTerm().Run(cmds...) +} + +func (adapter *MegaRaidAdaptor) megacliIsJBODEnabled() bool { + cmd := GetCommand("-AdpGetProp", "-EnableJBOD", fmt.Sprintf("-a%d", adapter.index)) + pref := fmt.Sprintf("Adapter %d: JBOD: ", adapter.index) + lines, err := adapter.remoteRun(cmd) + if err != nil { + log.Errorf("megacliIsJBODEnabled error: %v", err) + return false + } + for _, line := range lines { + if strings.HasPrefix(line, pref) { + val := strings.ToLower(strings.TrimSpace(line[len(pref):])) + if val == "disabled" { + return false + } + return true + } + } + return false +} + +func (adapter *MegaRaidAdaptor) megacliEnableJBOD(enable bool) bool { + var val string = "0" + if enable { + val = "1" + } + cmd := GetCommand("-AdpSetProp", "-EnableJBOD", fmt.Sprintf("-%s", val), fmt.Sprintf("-a%d", adapter.index)) + _, err := adapter.remoteRun(cmd) + if err != nil { + log.Errorf("enable jbod %v fail: %v", enable, err) + return false + } + return true +} + +func (adapter *MegaRaidAdaptor) megacliClearJBODDisks() error { + devIds := []string{} + for _, dev := range adapter.devs { + devIds = append(devIds, dev.String()) + } + cmd := GetCommand("-PDMakeGood", fmt.Sprintf("-PhysDrv[%s]", strings.Join(devIds, ",")), "-Force", fmt.Sprintf("-a%d", adapter.index)) + _, err := adapter.remoteRun(cmd) + return err +} + +func (adapter *MegaRaidAdaptor) clearJBODDisks() { + adapter.megacliEnableJBOD(true) + adapter.megacliEnableJBOD(false) + adapter.megacliEnableJBOD(true) + adapter.megacliEnableJBOD(false) +} + +type MegaRaid struct { + term *ssh.Client + adapters []*MegaRaidAdaptor + lv []interface{} + PhyDevsCnt int + Capacity int +} + +func GetCommand(args ...string) string { + bin := "/opt/MegaRAID/MegaCli/MegaCli64" + cmd := []string{bin} + cmd = append(cmd, args...) + return strings.Join(cmd, " ") +} + +func GetCommand2(args ...string) string { + bin := "/opt/MegaRAID/storcli/storcli64" + cmd := []string{bin} + cmd = append(cmd, args...) + return strings.Join(cmd, " ") +} + +func (raid *MegaRaid) GetModules() []string { + ret := []string{} + lines, err := raid.term.Run("/sbin/lsmod") + if err != nil { + log.Errorf("Remote lsmod error: %v", err) + return ret + } + for _, line := range lines { + mod := line[:strings.Index(line, " ")] + if mod != "Module" { + ret = append(ret, mod) + } + } + return ret +} + +func (raid *MegaRaid) ParsePhyDevs() bool { + if !utils.IsInStringArray("megaraid_sas", raid.GetModules()) { + log.Warningf("Not found megaraid_sas module") + return false + } + cmd := GetCommand("-PDList", "-aALL") + ret, err := raid.term.Run(cmd) + if err != nil { + log.Errorf("List raid disk error: %v", err) + return false + } + err := raid.parsePhyDevs(ret) + if err != nil { + log.Errorf("parse physical disk device error: %v", err) + return false + } + return true +} + +func (raid *MegaRaid) parsePhyDevs(lines []string) error { + phyDev := NewMegaRaidPhyDev() + var adapter *MegaRaidAdaptor + for _, line := range lines { + matches := adapterPatter.FindStringSubmatch(line) + if len(matches) != 0 { + paramsMap := make(map[string]string) + for i, name := range sizePattern.SubexpNames() { + if i > 0 && i <= len(matches) { + paramsMap[name] = matches[i] + } + } + adapterStr := paramsMap["idx"] + adapterInt, _ := strconv.Atoi(adapterStr) + adapter = NewMegaRaidAdaptor(adapterInt, raid) + raid.adapters = append(raid.adapters, adapter) + } else if phyDev.parseLine(line) && phyDev.isComplete() { + if adapter == nil { + return fmt.Errorf("Adapter is nil") + } + adapter.AddPhyDev(phyDev) + raid.PhyDevsCnt += 1 + raid.Capacity += phyDev.GetSize() + phyDev = NewMegaRaidPhyDev() + } + } + //map(self.add_phydev_strpsz, adapter.devs) + return nil +} + +func (raid *MegaRaid) addPhyDevStripSize(phyDev *MegaRaidPhyDev) bool { + grepCmd := []string{"grep", "-iE", "'^(Min|Max) Strip Size'"} + args := []string{"-adpallinfo", "-aall", "|"} + args = append(args, grepCmd...) + cmd := GetCommand(args...) + ret, err := raid.term.Run(cmd) + if err != nil { + log.Errorf("addPhyDevStripSize error: %v", err) + return false + } + return phyDev.parseStripSize(ret) +} + +func (raid *MegaRaid) GetPhyDevs() []*MegaRaidPhyDev { + devs := make([]*MegaRaidPhyDev, 0) + for _, ada := range raid.adapters { + devs = append(devs, ada.GetPhyDevs()...) + } + return devs +}