baremetal: fix build HPSARaid and enable serial console errors

This commit is contained in:
Zexi
2019-05-23 14:51:20 +08:00
parent 7fb6731647
commit 1fd62f7988
15 changed files with 204 additions and 54 deletions
+13 -10
View File
@@ -25,9 +25,11 @@ import (
"sync"
"time"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/util/errors"
yerrors "yunion.io/x/pkg/util/errors"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/pkg/util/seclib"
"yunion.io/x/pkg/util/sets"
@@ -120,7 +122,7 @@ func (m *SBaremetalManager) loadConfigs() error {
errs = append(errs, <-errsChannel)
}
}
return errors.NewAggregate(errs)
return yerrors.NewAggregate(errs)
}
func (m *SBaremetalManager) initBaremetal(session *mcclient.ClientSession, bmId string) error {
@@ -1016,6 +1018,7 @@ func (b *SBaremetalInstance) StartNewTask(factory tasks.TaskFactory, taskId stri
go func() {
task, err := factory(b, taskId, data)
if err != nil {
log.Errorf("New task %#v error: %v", factory, err)
tasks.SetTaskFail(task, err)
return
}
@@ -1328,40 +1331,40 @@ func (s *SBaremetalServer) DoPartitionDisk(term *ssh.Client) ([]*disktool.Partit
storages = append(storages, pcie...)
confs, err := s.GetDiskConfig()
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "do disk config")
}
layouts, err := baremetal.CalculateLayout(confs, storages)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "CalculateLayout")
}
tool := disktool.NewSSHPartitionTool(term)
tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts))
err = tool.RetrieveDiskInfo()
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "RetrieveDiskInfo")
}
disks, _ := s.desc.GetArray("disks")
if len(disks) == 0 {
return nil, fmt.Errorf("Empty disks in desc")
return nil, errors.New("Empty disks in desc")
}
rootDisk := disks[0]
rootSize, _ := rootDisk.Int("size")
err = s.doCreateRoot(term, tool.GetRootDisk().GetDevName())
if err != nil {
return nil, fmt.Errorf("Failed to create root: %v", err)
return nil, errors.Wrapf(err, "Failed to create root")
}
tool.RetrievePartitionInfo()
parts := tool.GetPartitions()
if len(parts) == 0 {
return nil, fmt.Errorf("Root disk create failed, no partitions")
return nil, errors.New("Root disk create failed, no partitions")
}
log.Infof("Resize root to %d MB", rootSize)
if err := tool.ResizePartition(0, rootSize); err != nil {
return nil, fmt.Errorf("Fail to resize root to %d, err: %v", rootSize, err)
return nil, errors.Wrapf(err, "Fail to resize root to %d", rootSize)
}
if len(disks) > 1 {
for _, disk := range disks[1:] {
@@ -1374,7 +1377,7 @@ func (s *SBaremetalServer) DoPartitionDisk(term *ssh.Client) ([]*disktool.Partit
driver, _ := disk.GetString("driver")
log.Infof("Create partition %d %s", sz, fs)
if err := tool.CreatePartition(-1, sz, fs, true, driver, uuid); err != nil {
return nil, fmt.Errorf("Fail to create disk %s: %v", disk.String(), err)
return nil, errors.Wrapf(err, "Fail to create disk %s", disk.String())
}
}
}
+6 -4
View File
@@ -21,6 +21,8 @@ import (
"sync"
"time"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
@@ -287,18 +289,18 @@ func (self *SBaremetalTaskBase) EnsurePowerUp() error {
log.Infof("EnsurePowerUp: bootdev=pxe")
status, err := self.Baremetal.GetPowerStatus()
if err != nil {
return err
return errors.Wrapf(err, "Get power status")
}
for status == "" || status == types.POWER_STATUS_OFF {
if status == types.POWER_STATUS_OFF {
err = self.Baremetal.DoPXEBoot()
if err != nil {
return err
return errors.Wrapf(err, "Do PXE boot")
}
}
status, err = self.Baremetal.GetPowerStatus()
if err != nil {
return err
return errors.Wrapf(err, "Get power status")
}
if status == "" || status == types.POWER_STATUS_OFF {
time.Sleep(40 * time.Second)
@@ -367,7 +369,7 @@ func (self *SBaremetalPXEBootTaskBase) InitPXEBootTask(pxeBootTask IPXEBootTask,
return self, fmt.Errorf("EnsurePowerShutdown: %v", err)
}
if err := self.EnsurePowerUp(); err != nil {
return self, fmt.Errorf("EnsurePowerUp to pxe: %v", err)
return self, errors.Wrapf(err, "EnsurePowerUp to pxe")
}
// this stage will be called by baremetalInstance when pxe start notify
self.SetSSHStage(pxeBootTask.OnPXEBoot)
+6 -4
View File
@@ -17,6 +17,8 @@ package tasks
import (
"context"
"github.com/pkg/errors"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/utils"
@@ -64,25 +66,25 @@ func (self *SBaremetalServerBaseDeployTask) OnPXEBoot(ctx context.Context, term
log.Infof("%s called on stage pxeboot, args: %v", self.GetName(), args)
result, err := self.serverDeployTask.DoDeploys(term)
if err != nil {
return err
return errors.Wrap(err, "Do deploy")
}
_, err = term.Run(
"/bin/sync",
"/sbin/sysctl -w vm.drop_caches=3",
)
if err != nil {
return err
return errors.Wrap(err, "Sync disk")
}
onFinishAction := self.GetFinishAction()
if utils.IsInStringArray(onFinishAction, []string{"restart", "shutdown"}) {
err = self.EnsurePowerShutdown(false)
if err != nil {
return err
return errors.Wrap(err, "Ensure power off")
}
if onFinishAction == "restart" {
err = self.EnsurePowerUp()
if err != nil {
return err
return errors.Wrap(err, "Ensure power up")
}
}
}
+1
View File
@@ -74,6 +74,7 @@ func (self *SBaremetalServerCreateTask) DoDeploys(term *ssh.Client) (jsonutils.J
}
func (self *SBaremetalServerCreateTask) onError(term *ssh.Client, err error) error {
log.Errorf("Create server error: %+v", err)
if err1 := self.Baremetal.GetServer().DoEraseDisk(term); err1 != nil {
log.Warningf("EraseDisk error: %v", err1)
}
@@ -70,8 +70,7 @@ func DetectStorageInfo(term *ssh.Client, wait bool) ([]*baremetal.BaremetalStora
raidDrivers = append(raidDrivers, drv.GetName())
}
log.Infof("Get Raid drivers: %v", raidDrivers)
log.Infof("Get Raid drivers: %v, collecting disks info ...", raidDrivers)
pcieRet, err := term.Run("/lib/mos/lsdisk --pcie")
if err != nil {
return nil, nil, nil, fmt.Errorf("Fail to retrieve PCIE DISK info")
+14 -17
View File
@@ -21,6 +21,8 @@ import (
"strings"
"time"
"github.com/pkg/errors"
"yunion.io/x/log"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/stringutils"
@@ -444,7 +446,7 @@ func DoReboot(exector IPMIExecutor) error {
var err error
status, err = GetChassisPowerStatus(exector)
if err != nil {
return err
log.Errorf("DoReboot get power status 1st: %v", err)
}
isValidStatus := func(s string) bool {
@@ -455,25 +457,24 @@ func DoReboot(exector IPMIExecutor) error {
time.Sleep(1 * time.Second)
status, err = GetChassisPowerStatus(exector)
if err != nil {
return err
log.Errorf("DoReboot %d tries to get power status: %v", tried, err)
}
}
if !isValidStatus(status) {
return fmt.Errorf("Unexpected status: %s", status)
return fmt.Errorf("Unexpected power status: %q", status)
}
// do shutdown
if status == types.POWER_STATUS_ON {
err = DoHardShutdown(exector)
if err != nil {
return err
if err := DoHardShutdown(exector); err != nil {
log.Errorf("DoHardShutdown: %v", err)
}
time.Sleep(1 * time.Second)
for tried := 0; tried < maxTries; tried++ {
status, err = GetChassisPowerStatus(exector)
if err != nil {
return err
log.Errorf("DoReboot %d tries to get power status: %v", tried, err)
}
if status == types.POWER_STATUS_OFF {
break
@@ -483,28 +484,24 @@ func DoReboot(exector IPMIExecutor) error {
}
// do power on
status, err = GetChassisPowerStatus(exector)
if err != nil {
return err
}
status, _ = GetChassisPowerStatus(exector)
for tried := 0; status != types.POWER_STATUS_ON && tried < maxTries; tried++ {
err = DoPowerOn(exector)
if err != nil {
return err
if err := DoPowerOn(exector); err != nil {
log.Errorf("DoReboot %d tries to power on: %v", tried, err)
}
time.Sleep(1 * time.Second)
status, err = GetChassisPowerStatus(exector)
if err != nil {
return err
log.Errorf("DoReboot %d tries to get power status: %v", tried, err)
}
}
status, err = GetChassisPowerStatus(exector)
if err != nil {
return err
return errors.Wrap(err, "Get power status after power on")
}
if status != types.POWER_STATUS_ON {
return fmt.Errorf("do reboot fail to poweron, current status: %s", status)
return errors.Errorf("do reboot fail to poweron, current status: %s", status)
}
return nil
}
+23 -11
View File
@@ -20,7 +20,10 @@ import (
"strconv"
"strings"
"github.com/pkg/errors"
"yunion.io/x/log"
"yunion.io/x/pkg/tristate"
"yunion.io/x/pkg/util/stringutils"
"yunion.io/x/pkg/utils"
@@ -39,6 +42,11 @@ type HPSARaidPhyDev struct {
func newHPSARaidPhyDev(addr string, adapter int, rotate bool) *HPSARaidPhyDev {
b := raid.NewRaidBasePhyDev(baremetal.DISK_DRIVER_HPSARAID)
b.Adapter = adapter
if rotate {
b.Rotate = tristate.True
} else {
b.Rotate = tristate.False
}
return &HPSARaidPhyDev{
RaidBasePhyDev: b,
addr: addr,
@@ -133,16 +141,20 @@ func (adapter *HPSARaidAdaptor) ParsePhyDevs() error {
adapter.parsePhyDevs(ret, isRotate)
return nil
}
cmd1 := GetCommand("controller", "slot=%d", fmt.Sprintf("%d", adapter.index), "ssdphysicaldrive", "all", "show", "detail")
cmd2 := GetCommand("controller", "slot=%d", fmt.Sprintf("%d", adapter.index), "physicaldrive", "all", "show", "detail")
var err error
if err = parseByCmd(cmd1, false); err != nil {
log.Errorf("parsePhyDevs by cmd %q: %v", cmd1, err)
cmd1 := GetCommand("controller", fmt.Sprintf("slot=%d", adapter.index), "ssdphysicaldrive", "all", "show", "detail")
cmd2 := GetCommand("controller", fmt.Sprintf("slot=%d", adapter.index), "physicaldrive", "all", "show", "detail")
var err1 error
var err2 error
if err1 = parseByCmd(cmd1, false); err1 != nil {
err1 = errors.Errorf("parsePhyDevs by cmd %q: %v", cmd1, err1)
}
if err = parseByCmd(cmd2, true); err != nil {
log.Errorf("parsePhyDevs by cmd %q: %v", cmd1, err)
if err2 = parseByCmd(cmd2, true); err2 != nil {
err2 = errors.Errorf("parsePhyDevs by cmd %q: %v", cmd1, err2)
}
return err
if err1 != nil && err2 != nil {
return errors.Errorf("ssd: %v, hdd: %v", err1, err2)
}
return nil
}
func (adapter *HPSARaidAdaptor) parsePhyDevs(lines []string, isRotate bool) {
@@ -153,7 +165,7 @@ func (adapter *HPSARaidAdaptor) parsePhyDevs(lines []string, isRotate bool) {
phydev = newHPSARaidPhyDev(m["addr"], adapter.index, isRotate)
} else if phydev != nil && phydev.parseLine(line) && phydev.isComplete() {
oldDev := adapter.getPhyDevByAddr(phydev.addr)
if oldDev != nil {
if oldDev == nil {
adapter.devs = append(adapter.devs, phydev)
}
phydev = nil
@@ -263,7 +275,7 @@ func (adapter *HPSARaidAdaptor) BuildRaid5(devs []*baremetal.BaremetalStorage, c
}
func (adapter *HPSARaidAdaptor) BuildRaid10(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
return adapter.buildRaid("10", devs, conf)
return adapter.buildRaid("1+0", devs, conf)
}
func (adapter *HPSARaidAdaptor) BuildNoneRaid(devs []*baremetal.BaremetalStorage) error {
@@ -315,7 +327,7 @@ func (adapter *HPSARaidAdaptor) RemoveLogicVolumes() error {
if err != nil {
return fmt.Errorf("Failed to get logic volumes: %v", err)
}
for i := len(lvs) - 1; i >= 0; i-- {
for _, i := range raid.ReverseIntArray(lvs) {
if err := adapter.removeLogicVolume(i); err != nil {
return fmt.Errorf("Remove %d logical volume: %v", i, err)
}
+1 -1
View File
@@ -602,7 +602,7 @@ func (adapter *MegaRaidAdaptor) RemoveLogicVolumes() error {
if err != nil {
return err
}
for i := len(lvIdx) - 1; i >= 0; i-- {
for _, i := range raiddrivers.ReverseIntArray(lvIdx) {
cmd := GetCommand("-CfgLdDel", fmt.Sprintf("-L%d", i), "-Force", fmt.Sprintf("-a%d", adapter.index))
cmds = append(cmds, cmd)
}
+1 -1
View File
@@ -186,7 +186,7 @@ func (adapter *MarvelRaidAdaptor) RemoveLogicVolumes() error {
if err != nil {
return fmt.Errorf("Failed to get logic volumes: %v", err)
}
for i := len(lvs) - 1; i >= 0; i-- {
for _, i := range raid.ReverseIntArray(lvs) {
if err := adapter.removeLogicVolume(i); err != nil {
return fmt.Errorf("Remove %d logical volume: %v", i, err)
}
+11
View File
@@ -103,3 +103,14 @@ func GetModules(term *ssh.Client) []string {
}
return ret
}
func ReverseIntArray(input []int) []int {
s := make([]int, len(input))
for i := range input {
s[i] = input[i]
}
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
+46
View File
@@ -0,0 +1,46 @@
// 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 raid
import (
"reflect"
"testing"
)
func TestReverseIntArray(t *testing.T) {
tests := []struct {
name string
input []int
want []int
}{
{
name: "empty input",
input: []int{},
want: []int{},
},
{
name: "reverse",
input: []int{1, 2, 3},
want: []int{3, 2, 1},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ReverseIntArray(tt.input); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ReverseIntArray() = %v, want %v", got, tt.want)
}
})
}
}
+22 -1
View File
@@ -371,7 +371,9 @@ func (l *sLinuxRootFs) getSerialPorts(rootFs IDiskPartition) []string {
log.Errorf("Get %s error: %v", confpath, err)
return nil
}
return sysutils.GetSerialPorts(strings.Split(string(content), "\n"))
ttys := sysutils.GetSerialPorts(strings.Split(string(content), "\n"))
log.Infof("Get serial ports content:\n%s, find serial ttys: %#v", string(content), ttys)
return ttys
}
func (l *sLinuxRootFs) enableSerialConsoleInitCentos(rootFs IDiskPartition) error {
@@ -390,10 +392,26 @@ exec /sbin/agetty /dev/%s 115200 vt100`, tty, tty, tty)
return err
}
func (l *sLinuxRootFs) enableSerialConsoleRootLogin(rootFs IDiskPartition, tty string) error {
secureTTYFile := "/etc/securetty"
content, err := rootFs.FileGetContents(secureTTYFile, false)
if err != nil {
return errors.Wrapf(err, "get contents of %s", secureTTYFile)
}
secureTTYs := sysutils.GetSecureTTYs(strings.Split(string(content), "\n"))
if utils.IsInStringArray(tty, secureTTYs) {
return nil
}
return rootFs.FilePutContents(secureTTYFile, fmt.Sprintf("\n%s", tty), true, false)
}
func (l *sLinuxRootFs) enableSerialConsoleInit(rootFs IDiskPartition) error {
// https://help.ubuntu.com/community/SerialConsoleHowto
var err error
for _, tty := range l.getSerialPorts(rootFs) {
if err := l.enableSerialConsoleRootLogin(rootFs, tty); err != nil {
log.Errorf("Enable %s root login: %v", tty, err)
}
content := fmt.Sprintf(
`start on stopped rc or RUNLEVEL=[12345]
stop on runlevel [!12345]
@@ -415,6 +433,9 @@ func (l *sLinuxRootFs) disableSerialConsoleInit(rootFs IDiskPartition) {
func (l *sLinuxRootFs) enableSerialConsoleSystemd(rootFs IDiskPartition) error {
for _, tty := range l.getSerialPorts(rootFs) {
if err := l.enableSerialConsoleRootLogin(rootFs, tty); err != nil {
log.Errorf("Enable %s root login: %v", tty, err)
}
sPath := fmt.Sprintf("/etc/systemd/system/getty.target.wants/getty@%s.service", tty)
if rootFs.Exists(sPath, false) {
rootFs.Remove(sPath, false)
+8 -2
View File
@@ -20,6 +20,7 @@ import (
"strings"
"time"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
"yunion.io/x/log"
@@ -131,8 +132,13 @@ func (s *Client) run(parseOutput bool, cmds ...string) ([]string, error) {
session.Stderr = &stdErr
err = session.Run(cmd)
if err != nil {
err = fmt.Errorf("%q error: %v, Stderr: %s", cmd, err, stdErr.String())
log.Errorf("%v", err)
var outputErr error
errMsg := stdErr.String()
if len(stdOut.String()) != 0 {
errMsg = fmt.Sprintf("%s %s", errMsg, stdOut.String())
}
outputErr = errors.New(errMsg)
err = errors.Errorf("%q error: %v, cmd error: %v", cmd, err, outputErr)
return nil, err
}
if parseOutput {
+16 -1
View File
@@ -233,10 +233,25 @@ func ParseSCSIDiskInfo(lines []string) []*types.SDiskInfo {
return ParseDiskInfo(lines, baremetal.DISK_DRIVER_LINUX)
}
func GetSecureTTYs(lines []string) []string {
ttys := []string{}
for _, l := range lines {
if len(l) == 0 {
continue
}
if strings.HasPrefix(l, "#") {
continue
}
ttys = append(ttys, l)
}
return ttys
}
func GetSerialPorts(lines []string) []string {
// http://wiki.networksecuritytoolkit.org/index.php/Console_Output_and_Serial_Terminals
ret := []string{}
for _, l := range lines {
if strings.Contains(l, "CTS") {
if strings.Contains(l, "CTS") || strings.Contains(l, "RTS") {
pos := strings.Index(l, ":")
if pos < 0 {
continue
+35
View File
@@ -273,3 +273,38 @@ func TestParseNicInfo(t *testing.T) {
})
}
}
func TestGetSecureTTYs(t *testing.T) {
tests := []struct {
name string
lines []string
want []string
}{
{
name: "empty tty",
lines: []string{"", "#comment"},
want: []string{},
},
{
name: "ttys",
lines: []string{
"tty1",
"ttyS0",
"console",
"#tty0",
},
want: []string{
"tty1",
"ttyS0",
"console",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetSecureTTYs(tt.lines); !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetSecureTTYs() = %v, want %v", got, tt.want)
}
})
}
}