mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-28 03:23:25 +08:00
fix(baremetal): record uefi info
This commit is contained in:
+111
-3
@@ -50,6 +50,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/disktool"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
|
||||
raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid/drivers"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestfs"
|
||||
@@ -1306,6 +1307,19 @@ func (b *SBaremetalInstance) GetRawIPMIConfig() *types.SIPMIInfo {
|
||||
return &ipmiInfo
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetUEFIInfo() (*types.EFIBootMgrInfo, error) {
|
||||
key := "uefi_info"
|
||||
if !b.desc.Contains(key) {
|
||||
return nil, nil
|
||||
}
|
||||
info := new(types.EFIBootMgrInfo)
|
||||
if err := b.desc.Unmarshal(info, key); err != nil {
|
||||
|
||||
return nil, errors.Wrap(err, "Unmarshal json")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) GetAccessIp() string {
|
||||
accessIp, _ := b.desc.GetString("access_ip")
|
||||
return accessIp
|
||||
@@ -1434,13 +1448,16 @@ func (b *SBaremetalInstance) SSHReachable() (bool, error) {
|
||||
return false, errors.NewAggregate(errs)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) sshRun(hostCmd string, serverCmd string) ([]string, error) {
|
||||
func (b *SBaremetalInstance) sshRunWrapper(
|
||||
hostRun func(hostCli *ssh.Client) ([]string, error),
|
||||
serverRun func(serverCli *ssh.Client) ([]string, error),
|
||||
) ([]string, error) {
|
||||
hostCli, err := b.GetHostSSHClient()
|
||||
if err != nil {
|
||||
log.Warningf("Get host ssh client error: %v", err)
|
||||
} else {
|
||||
if hostCli != nil {
|
||||
return hostCli.RawRun(hostCmd)
|
||||
return hostRun(hostCli)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1448,10 +1465,99 @@ func (b *SBaremetalInstance) sshRun(hostCmd string, serverCmd string) ([]string,
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Get baremetal %s server ssh client", b.GetName())
|
||||
}
|
||||
return serverCli.RunWithTTY(serverCmd)
|
||||
return serverRun(serverCli)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) sshRun(hostCmd string, serverCmd string) ([]string, error) {
|
||||
return b.sshRunWrapper(
|
||||
func(hostCli *ssh.Client) ([]string, error) {
|
||||
return hostCli.RawRun(hostCmd)
|
||||
},
|
||||
func(serverCli *ssh.Client) ([]string, error) {
|
||||
return serverCli.RunWithTTY(serverCmd)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) adjustUEFIWrapper(cli *ssh.Client, f func() error) error {
|
||||
isUEFI, err := uefi.RemoteIsUEFIBoot(cli)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Check is uefi boot")
|
||||
}
|
||||
if !isUEFI {
|
||||
return nil
|
||||
}
|
||||
return f()
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) AdjustUEFICurrentBootOrder(hostCli *ssh.Client) error {
|
||||
return b.adjustUEFIWrapper(hostCli, func() error {
|
||||
mgr, err := uefi.NewEFIBootMgrFromRemote(hostCli, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewEFIBootMgrFromRemote")
|
||||
}
|
||||
if err := uefi.RemoteSetCurrentBootAtFirst(hostCli, mgr); err != nil {
|
||||
return errors.Wrap(err, "Set current boot order at first")
|
||||
}
|
||||
return b.SendUEFIInfo(mgr)
|
||||
})
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) SendUEFIInfo(mgr *uefi.BootMgr) error {
|
||||
info, err := mgr.ToEFIBootMgrInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
desc := b.desc
|
||||
uefiData := jsonutils.Marshal(info)
|
||||
desc.Add(uefiData, "uefi_info")
|
||||
if err := b.SaveDesc(desc); err != nil {
|
||||
return errors.Wrap(err, "Save uefi_info")
|
||||
}
|
||||
updateData := jsonutils.NewDict()
|
||||
updateData.Add(uefiData, "uefi_info")
|
||||
if _, err := modules.Hosts.Update(b.GetClientSession(), b.GetId(), updateData); err != nil {
|
||||
return errors.Wrap(err, "Update cloud uefi info")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) adjustServerUEFIBootOrder(srvCli *ssh.Client) error {
|
||||
return b.adjustUEFIWrapper(srvCli, func() error {
|
||||
info, err := b.GetUEFIInfo()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetUEFIInfo from local desc")
|
||||
}
|
||||
if info == nil {
|
||||
return uefi.RemoteTryToSetPXEBoot(srvCli)
|
||||
}
|
||||
if err := uefi.RemoteTryToSetPXEBoot(srvCli); err != nil {
|
||||
log.Warningf("RemoteTryToSetPXEBoot error: %v", err)
|
||||
}
|
||||
_, err = uefi.RemoteSetBootOrderByInfo(srvCli, info.GetPXEEntry())
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) AdjustUEFIBootOrder() error {
|
||||
_, err := b.sshRunWrapper(
|
||||
func(hostCli *ssh.Client) ([]string, error) {
|
||||
return nil, b.AdjustUEFICurrentBootOrder(hostCli)
|
||||
},
|
||||
func(srvCli *ssh.Client) ([]string, error) {
|
||||
return nil, b.adjustServerUEFIBootOrder(srvCli)
|
||||
},
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *SBaremetalInstance) SSHReboot() error {
|
||||
if !b.HasBMC() {
|
||||
// try adjust uefi boot order before reboot
|
||||
if err := b.AdjustUEFIBootOrder(); err != nil {
|
||||
return errors.Wrap(err, "Adjust uefi boot order")
|
||||
}
|
||||
}
|
||||
if _, err := b.sshRun("/sbin/reboot", "sudo shutdown -r now && exit"); err != nil {
|
||||
if !ssh.IsExitMissingError(err) {
|
||||
return errors.Wrap(err, "Try reboot")
|
||||
@@ -2288,7 +2394,9 @@ func (s *SBaremetalServer) DoDiskConfig(term *ssh.Client) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("CalculateLayout: %v", err)
|
||||
}
|
||||
log.Errorf("===layouts: %s", jsonutils.Marshal(layouts).PrettyString())
|
||||
diskConfs := baremetal.GroupLayoutResultsByDriverAdapter(layouts)
|
||||
log.Errorf("===diskConfs: %s", jsonutils.Marshal(diskConfs).PrettyString())
|
||||
for _, dConf := range diskConfs {
|
||||
driver := dConf.Driver
|
||||
raidDrv := raiddrivers.GetDriver(driver, term)
|
||||
|
||||
+10
-10
@@ -328,23 +328,23 @@ func (req *dhcpRequest) findBaremetalsOfAnyMac(session *mcclient.ClientSession,
|
||||
|
||||
// createOrUpdateBaremetal create or update baremetal by client MAC
|
||||
func (req *dhcpRequest) createOrUpdateBaremetal(session *mcclient.ClientSession) (jsonutils.JSONObject, error) {
|
||||
// first try UUID
|
||||
ret, err := req.findBaremetalsByUuid(session)
|
||||
// first try mac and is_baremetal=true
|
||||
ret, err := req.findBaremetalsOfAnyMac(session, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
// try mac and is_baremetal=true
|
||||
ret, err = req.findBaremetalsOfAnyMac(session, true)
|
||||
// try mac and host_type=baremetal
|
||||
ret, err = req.findBaremetalsOfAnyMac(session, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
// try mac and host_type=baremetal
|
||||
ret, err = req.findBaremetalsOfAnyMac(session, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(ret.Data) == 0 {
|
||||
// try UUID
|
||||
ret, err = req.findBaremetalsByUuid(session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
switch len(ret.Data) {
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
o "yunion.io/x/onecloud/pkg/baremetal/options"
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/object"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -492,24 +491,6 @@ func (self *SBaremetalPXEBootTaskBase) GetName() string {
|
||||
return "BaremetalPXEBootTaskBase"
|
||||
}
|
||||
|
||||
func AdjustUEFIBootOrder(term *ssh.Client) error {
|
||||
isUEFI, err := uefi.RemoteIsUEFIBoot(term)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Check baremetal is UEFI boot")
|
||||
}
|
||||
|
||||
if !isUEFI {
|
||||
return nil
|
||||
}
|
||||
|
||||
mgr, err := uefi.NewEFIBootMgrFromRemote(term)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewEFIBootMgrFromRemote")
|
||||
}
|
||||
|
||||
if err := uefi.RemoteSetCurrentBootAtFirst(term, mgr); err != nil {
|
||||
return errors.Wrap(err, "Set current pxe boot at fist")
|
||||
}
|
||||
|
||||
return nil
|
||||
func AdjustUEFIBootOrder(term *ssh.Client, bm IBaremetal) error {
|
||||
return bm.AdjustUEFICurrentBootOrder(term)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/baremetal/utils/uefi"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
type IServerBaseDeployTask interface {
|
||||
IPXEBootTask
|
||||
|
||||
RemoveEFIOSEntry() bool
|
||||
DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error)
|
||||
PostDeploys(term *ssh.Client) error
|
||||
}
|
||||
@@ -75,6 +77,10 @@ func (self *SBaremetalServerBaseDeployTask) GetFinishAction() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) RemoveEFIOSEntry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) DoDeploys(_ *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -85,12 +91,19 @@ func (self *SBaremetalServerBaseDeployTask) PostDeploys(_ *ssh.Client) error {
|
||||
|
||||
func (self *SBaremetalServerBaseDeployTask) OnPXEBoot(ctx context.Context, term *ssh.Client, args interface{}) error {
|
||||
log.Infof("%s called on stage pxeboot, args: %v", self.GetName(), args)
|
||||
|
||||
if self.IServerBaseDeployTask().RemoveEFIOSEntry() {
|
||||
if err := uefi.RemoteTryRemoveOSBootEntry(term); err != nil {
|
||||
return errors.Wrap(err, "Remote uefi boot entry")
|
||||
}
|
||||
}
|
||||
|
||||
result, err := self.IServerBaseDeployTask().DoDeploys(term)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Do deploy")
|
||||
}
|
||||
|
||||
if err := AdjustUEFIBootOrder(term); err != nil {
|
||||
if err := AdjustUEFIBootOrder(term, self.Baremetal); err != nil {
|
||||
return errors.Wrap(err, "Adjust UEFI boot order")
|
||||
}
|
||||
|
||||
|
||||
@@ -137,22 +137,24 @@ func (task *sBaremetalPrepareTask) prepareBaremetalInfo(cli *ssh.Client) (*barem
|
||||
if ipmiInfo == nil {
|
||||
ipmiInfo = &types.SIPMIInfo{}
|
||||
}
|
||||
if !ipmiInfo.Present && ipmiEnable {
|
||||
if !ipmiInfo.Verified && !ipmiInfo.Present && ipmiEnable {
|
||||
ipmiInfo.Present = true
|
||||
ipmiInfo.Verified = false
|
||||
}
|
||||
|
||||
return &baremetalPrepareInfo{
|
||||
sysInfo,
|
||||
cpuInfo,
|
||||
dmiCPUInfo,
|
||||
memInfo,
|
||||
nicsInfo,
|
||||
diskInfo,
|
||||
storageDriver,
|
||||
ipmiInfo,
|
||||
isolatedDevicesInfo,
|
||||
}, nil
|
||||
prepareInfo := &baremetalPrepareInfo{
|
||||
sysInfo: sysInfo,
|
||||
cpuInfo: cpuInfo,
|
||||
dmiCpuInfo: dmiCPUInfo,
|
||||
memInfo: memInfo,
|
||||
nicsInfo: nicsInfo,
|
||||
diskInfo: diskInfo,
|
||||
storageDriver: storageDriver,
|
||||
ipmiInfo: ipmiInfo,
|
||||
isolatedDevicesInfo: isolatedDevicesInfo,
|
||||
}
|
||||
|
||||
return prepareInfo, nil
|
||||
}
|
||||
|
||||
func (task *sBaremetalPrepareTask) configIPMISetting(cli *ssh.Client, i *baremetalPrepareInfo) error {
|
||||
@@ -327,7 +329,7 @@ func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error {
|
||||
log.Errorf("SetNTP fail: %s", err)
|
||||
}
|
||||
|
||||
if err = AdjustUEFIBootOrder(cli); err != nil {
|
||||
if err = AdjustUEFIBootOrder(cli, task.baremetal); err != nil {
|
||||
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
||||
return errors.Wrap(err, "Adjust UEFI boot order")
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ func (self *SBaremetalServerCreateTask) GetName() string {
|
||||
return "BaremetalServerCreateTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerCreateTask) RemoveEFIOSEntry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerCreateTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
// Build raid
|
||||
err := self.Baremetal.GetServer().DoDiskConfig(term)
|
||||
|
||||
@@ -43,6 +43,10 @@ func (self *SBaremetalServerDeployTask) GetName() string {
|
||||
return "BaremetalServerDeployTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDeployTask) RemoveEFIOSEntry() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDeployTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
return self.Baremetal.GetServer().DoDeploy(term, self.data, false)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ func (self *SBaremetalServerDestroyTask) GetName() string {
|
||||
return "BaremetalServerDestroyTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDestroyTask) RemoveEFIOSEntry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerDestroyTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
if err := self.Baremetal.GetServer().DoEraseDisk(term); err != nil {
|
||||
log.Errorf("Delete server do erase disk: %v", err)
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
baremetaltypes "yunion.io/x/onecloud/pkg/baremetal/types"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
type IBaremetal interface {
|
||||
@@ -73,6 +74,7 @@ type IBaremetal interface {
|
||||
SSHReachable() (bool, error)
|
||||
SSHReboot() error
|
||||
SSHShutdown() error
|
||||
AdjustUEFICurrentBootOrder(cli *ssh.Client) error
|
||||
}
|
||||
|
||||
type IBmManager interface {
|
||||
|
||||
@@ -45,6 +45,10 @@ func (self *SBaremetalServerRebuildTask) GetName() string {
|
||||
return "BaremetalServerRebuildTask"
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerRebuildTask) RemoveEFIOSEntry() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SBaremetalServerRebuildTask) DoDeploys(term *ssh.Client) (jsonutils.JSONObject, error) {
|
||||
parts, err := self.Baremetal.GetServer().DoRebuildRootDisk(term)
|
||||
if err != nil {
|
||||
|
||||
@@ -616,7 +616,24 @@ func (tool *PartitionTool) parseLsDisk(lines []string, driver string) {
|
||||
}
|
||||
minCnt := int(math.Min(float64(len(disks)), float64(len(tool.diskTable[driver]))))
|
||||
for i := 0; i < minCnt; i++ {
|
||||
tool.diskTable[driver][i].SetInfo(disks[i])
|
||||
driverDisk := tool.diskTable[driver][i]
|
||||
if utils.IsInStringArray(driver, []string{NONRAID_DRIVER, PCIE_DRIVER}) {
|
||||
// find size matched disk from disks
|
||||
size := driverDisk.sizeMB
|
||||
var remoteDisk *types.SDiskInfo
|
||||
for ri := range disks {
|
||||
if disks[ri].Size == size {
|
||||
remoteDisk = disks[ri]
|
||||
// remove matched disks
|
||||
disks = append(disks[:ri], disks[ri+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
driverDisk.SetInfo(remoteDisk)
|
||||
} else {
|
||||
// raid disk set by order
|
||||
driverDisk.SetInfo(disks[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,17 @@ import (
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
||||
"yunion.io/x/onecloud/pkg/util/regutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/ssh"
|
||||
)
|
||||
|
||||
const (
|
||||
// efibootmgr useage: https://github.com/rhboot/efibootmgr
|
||||
CMD_EFIBOOTMGR = "/usr/sbin/efibootmgr"
|
||||
CMD_EFIBOOTMGR = "/usr/sbin/efibootmgr"
|
||||
SUDO_EFIBOOTMGR = "sudo efibootmgr"
|
||||
|
||||
MAC_KEYWORD = "MAC"
|
||||
)
|
||||
|
||||
type BootMgr struct {
|
||||
@@ -60,6 +64,13 @@ type BootEntry struct {
|
||||
IsActive bool
|
||||
}
|
||||
|
||||
func getEFIBootMgrCmd(sudo bool) string {
|
||||
if sudo {
|
||||
return SUDO_EFIBOOTMGR
|
||||
}
|
||||
return CMD_EFIBOOTMGR
|
||||
}
|
||||
|
||||
func ParseEFIBootMGR(input string) (*BootMgr, error) {
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
@@ -213,8 +224,15 @@ func parseEFIBootMGREntry(line string) *BootEntry {
|
||||
}
|
||||
}
|
||||
|
||||
func NewEFIBootMgrFromRemote(cli *ssh.Client) (*BootMgr, error) {
|
||||
cmd := CMD_EFIBOOTMGR
|
||||
func NewEFIBootMgrFromRemote(cli *ssh.Client, sudo bool) (*BootMgr, error) {
|
||||
return newEFIBootMgrFromRemote(cli, sudo, true)
|
||||
}
|
||||
|
||||
func newEFIBootMgrFromRemote(cli *ssh.Client, sudo bool, verbose bool) (*BootMgr, error) {
|
||||
cmd := getEFIBootMgrCmd(sudo)
|
||||
if verbose {
|
||||
cmd = fmt.Sprintf("%s -v", cmd)
|
||||
}
|
||||
lines, err := cli.RawRun(cmd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Execute command: %s", cmd)
|
||||
@@ -222,8 +240,8 @@ func NewEFIBootMgrFromRemote(cli *ssh.Client) (*BootMgr, error) {
|
||||
return ParseEFIBootMGR(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetCommand() string {
|
||||
return CMD_EFIBOOTMGR
|
||||
func (m *BootMgr) GetCommand(sudo bool) string {
|
||||
return getEFIBootMgrCmd(sudo)
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootCurrent() string {
|
||||
@@ -246,6 +264,15 @@ func (m *BootMgr) GetBootEntry(num string) *BootEntry {
|
||||
return m.entries[num]
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootEntryByDesc(desc string) *BootEntry {
|
||||
for _, entry := range m.entries {
|
||||
if strings.Contains(entry.Description, desc) {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BootMgr) FindBootOrderPos(num string) int {
|
||||
return stringArraryFindItemPos(m.bootOrder, num)
|
||||
}
|
||||
@@ -293,8 +320,12 @@ func (m *BootMgr) MoveBootOrder(num string, pos int) *BootMgr {
|
||||
return m
|
||||
}
|
||||
|
||||
func getSetBootOrderArgs(bootOrder []string) string {
|
||||
return strings.Join(bootOrder, ",")
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetSetBootOrderArgs() string {
|
||||
return strings.Join(m.bootOrder, ",")
|
||||
return getSetBootOrderArgs(m.bootOrder)
|
||||
}
|
||||
|
||||
func RemoteIsUEFIBoot(cli *ssh.Client) (bool, error) {
|
||||
@@ -311,6 +342,45 @@ func RemoteIsUEFIBoot(cli *ssh.Client) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func convertToEFIBootMgrInfo(info *BootMgr) (*types.EFIBootMgrInfo, error) {
|
||||
data := &types.EFIBootMgrInfo{
|
||||
PxeBootNum: info.GetBootCurrent(),
|
||||
BootOrder: make([]*types.EFIBootEntry, len(info.GetBootOrder())),
|
||||
}
|
||||
for idx, orderNum := range info.GetBootOrder() {
|
||||
entry := info.GetBootEntry(orderNum)
|
||||
if entry == nil {
|
||||
return nil, errors.Errorf("Not found boot entry by %q", orderNum)
|
||||
}
|
||||
data.BootOrder[idx] = &types.EFIBootEntry{
|
||||
BootNum: entry.BootNum,
|
||||
Description: entry.Description,
|
||||
IsActive: entry.IsActive,
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (mgr *BootMgr) ToEFIBootMgrInfo() (*types.EFIBootMgrInfo, error) {
|
||||
return convertToEFIBootMgrInfo(mgr)
|
||||
}
|
||||
|
||||
func (mgr *BootMgr) sortEntryByKeyword(keyword string) *BootMgr {
|
||||
newOrder := []string{}
|
||||
oldOrder := []string{}
|
||||
for _, num := range mgr.bootOrder {
|
||||
entry := mgr.GetBootEntry(num)
|
||||
if strings.Contains(entry.Description, keyword) {
|
||||
newOrder = append(newOrder, num)
|
||||
} else {
|
||||
oldOrder = append(oldOrder, num)
|
||||
}
|
||||
}
|
||||
newOrder = append(newOrder, oldOrder...)
|
||||
mgr.bootOrder = newOrder
|
||||
return mgr
|
||||
}
|
||||
|
||||
func RemoteSetCurrentBootAtFirst(cli *ssh.Client, mgr *BootMgr) error {
|
||||
curPos := mgr.FindBootOrderPos(mgr.GetBootCurrent())
|
||||
if curPos == -1 {
|
||||
@@ -318,7 +388,91 @@ func RemoteSetCurrentBootAtFirst(cli *ssh.Client, mgr *BootMgr) error {
|
||||
}
|
||||
// move to first
|
||||
mgr.MoveBootOrder(mgr.GetBootCurrent(), 0)
|
||||
cmd := fmt.Sprintf("%s -o %s", mgr.GetCommand(), mgr.GetSetBootOrderArgs())
|
||||
cmd := fmt.Sprintf("%s -o %s", mgr.GetCommand(false), mgr.GetSetBootOrderArgs())
|
||||
_, err := cli.Run(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoteSetBootOrder(cli *ssh.Client, order []string) error {
|
||||
cmd := fmt.Sprintf("%s -o %s", SUDO_EFIBOOTMGR, getSetBootOrderArgs(order))
|
||||
_, err := cli.RunWithTTY(cmd)
|
||||
return err
|
||||
}
|
||||
|
||||
func RemoteSetBootOrderByInfo(cli *ssh.Client, entry *types.EFIBootEntry) (*BootMgr, error) {
|
||||
mgr, err := newEFIBootMgrFromRemote(cli, true, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
curEntry := mgr.GetBootEntryByDesc(entry.Description)
|
||||
if curEntry == nil {
|
||||
return nil, errors.Wrapf(err, "Not found remote boot entry by %q", entry.Description)
|
||||
}
|
||||
|
||||
mgr = mgr.sortEntryByKeyword(curEntry.Description)
|
||||
return mgr, RemoteSetBootOrder(cli, mgr.GetBootOrder())
|
||||
}
|
||||
|
||||
func RemoteTryToSetPXEBoot(cli *ssh.Client) error {
|
||||
mgr, err := newEFIBootMgrFromRemote(cli, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mgr = mgr.sortEntryByKeyword(MAC_KEYWORD)
|
||||
return RemoteSetBootOrder(cli, mgr.GetBootOrder())
|
||||
}
|
||||
|
||||
func remoteISUEFIBootWrap(cli *ssh.Client, f func(*ssh.Client) error) error {
|
||||
isUEFI, err := RemoteIsUEFIBoot(cli)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Check is UEFI boot")
|
||||
}
|
||||
if !isUEFI {
|
||||
return nil
|
||||
}
|
||||
return f(cli)
|
||||
}
|
||||
|
||||
func RemoteTryRemoveOSBootEntry(hostCli *ssh.Client) error {
|
||||
return remoteISUEFIBootWrap(hostCli, remoteTryRemoveOSBootEntry)
|
||||
}
|
||||
|
||||
func remoteTryRemoveOSBootEntry(hostCli *ssh.Client) error {
|
||||
mgr, err := newEFIBootMgrFromRemote(hostCli, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: find other ways to decide whether entry is OS boot
|
||||
osKeywords := []string{
|
||||
"linux",
|
||||
"centos",
|
||||
"ubuntu",
|
||||
"windows",
|
||||
"grub",
|
||||
}
|
||||
|
||||
isOsEntry := func(desc string) bool {
|
||||
for _, key := range osKeywords {
|
||||
desc := strings.ToLower(desc)
|
||||
if strings.Contains(desc, key) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, entry := range mgr.entries {
|
||||
if !isOsEntry(entry.Description) {
|
||||
continue
|
||||
}
|
||||
// delete entry and remove it from BootOrder
|
||||
cmd := fmt.Sprintf("%s -b %s -B", mgr.GetCommand(false), entry.BootNum)
|
||||
if _, err := hostCli.Run(cmd); err != nil {
|
||||
return errors.Wrapf(err, "remove boot entry: %s", entry.Description)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 types
|
||||
|
||||
type EFIBootMgrInfo struct {
|
||||
PxeBootNum string `json:"pxe_boot_num"`
|
||||
BootOrder []*EFIBootEntry `json:"boot_order"`
|
||||
}
|
||||
|
||||
type EFIBootEntry struct {
|
||||
BootNum string `json:"boot_num"`
|
||||
Description string `json:"description"`
|
||||
IsActive bool `json:"is_active"`
|
||||
}
|
||||
|
||||
func (info *EFIBootMgrInfo) GetPXEEntry() *EFIBootEntry {
|
||||
for _, e := range info.BootOrder {
|
||||
if e.BootNum == info.PxeBootNum {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -21,6 +21,9 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
)
|
||||
|
||||
@@ -1277,6 +1280,33 @@ var (
|
||||
Index: 0,
|
||||
},
|
||||
}
|
||||
|
||||
pcie2Storages = []*BaremetalStorage{
|
||||
{
|
||||
Driver: DISK_DRIVER_LINUX,
|
||||
Dev: "sda",
|
||||
Rotate: true,
|
||||
Size: 3815447,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Dev: "nvme0n1",
|
||||
Rotate: false,
|
||||
Size: 1953514,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
{
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Dev: "nvme1n1",
|
||||
Rotate: false,
|
||||
Size: 122104,
|
||||
Adapter: 0,
|
||||
Index: 0,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestPCIEStoragesAllocable(t *testing.T) {
|
||||
@@ -1331,3 +1361,34 @@ func TestPCIEStoragesAllocable(t *testing.T) {
|
||||
t.Errorf("Disk not allocable")
|
||||
}
|
||||
}
|
||||
|
||||
func Test2PCIEStoragesAllocable(t *testing.T) {
|
||||
adapter0 := 0
|
||||
confs := []*api.BaremetalDiskConfig{
|
||||
{
|
||||
Adapter: &adapter0,
|
||||
Conf: DISK_CONF_NONE,
|
||||
Count: 0,
|
||||
Driver: DISK_DRIVER_PCIE,
|
||||
Range: []int64{1},
|
||||
Type: DISK_TYPE_SSD,
|
||||
},
|
||||
}
|
||||
|
||||
layouts, err := CalculateLayout(confs, pcie2Storages)
|
||||
if err != nil {
|
||||
t.Errorf("CalculateLayout error: %v", err)
|
||||
return
|
||||
}
|
||||
log.Errorf("layouts: %s", jsonutils.Marshal(layouts))
|
||||
disks := []*api.DiskConfig{
|
||||
{
|
||||
Backend: api.STORAGE_LOCAL,
|
||||
Driver: "scsi",
|
||||
SizeMb: -1,
|
||||
},
|
||||
}
|
||||
if ok := IsDisksAllocable(layouts, disks); !ok {
|
||||
t.Errorf("Disk not allocable")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,9 @@ type SHost struct {
|
||||
|
||||
// IPv4地址,作为么有云vpc访问外网时的网关
|
||||
OvnMappedIpAddr string `width:"16" charset:"ascii" nullable:"true" list:"user"`
|
||||
|
||||
// UEFI详情
|
||||
UefiInfo jsonutils.JSONObject `nullable:"true" get:"domain" update:"domain" create:"domain_optional"`
|
||||
}
|
||||
|
||||
func (manager *SHostManager) GetContextManagers() [][]db.IModelManager {
|
||||
@@ -3527,6 +3530,10 @@ func (self *SHost) PostUpdate(ctx context.Context, userCred mcclient.TokenCreden
|
||||
log.Errorf("baremetal host %s update related server %s spec error: %v", self.GetName(), guest.GetName(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.startSyncConfig(ctx, userCred, "", true); err != nil {
|
||||
log.Errorf("start sync host %q config after updated", self.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *SHost) UpdateDnsRecords(isAdd bool) {
|
||||
@@ -3940,6 +3947,17 @@ func (self *SHost) HasBMC() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *SHost) IsUEFIBoot() bool {
|
||||
info, _ := self.GetUEFIInfo()
|
||||
if info == nil {
|
||||
return false
|
||||
}
|
||||
if len(info.PxeBootNum) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *SHost) isRedfishCapable() bool {
|
||||
ipmiInfo, _ := self.GetIpmiInfo()
|
||||
if ipmiInfo.Verified && ipmiInfo.RedfishApi {
|
||||
@@ -5624,6 +5642,17 @@ func (host *SHost) GetIpmiInfo() (types.SIPMIInfo, error) {
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (host *SHost) GetUEFIInfo() (*types.EFIBootMgrInfo, error) {
|
||||
if host.UefiInfo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
info := new(types.EFIBootMgrInfo)
|
||||
if err := host.UefiInfo.Unmarshal(info); err != nil {
|
||||
return nil, errors.Wrap(err, "host.UefiInfo.Unmarshal")
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (self *SHost) AllowGetDetailsJnlp(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
|
||||
return db.IsAdminAllowGetSpec(userCred, self, "jnlp")
|
||||
}
|
||||
@@ -5725,7 +5754,13 @@ func (self *SHost) PerformSyncConfig(ctx context.Context, userCred mcclient.Toke
|
||||
}
|
||||
|
||||
func (self *SHost) StartSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncConfigTask", self, userCred, nil, parentTaskId, "", nil)
|
||||
return self.startSyncConfig(ctx, userCred, parentTaskId, false)
|
||||
}
|
||||
|
||||
func (self *SHost) startSyncConfig(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string, noStatus bool) error {
|
||||
data := jsonutils.NewDict()
|
||||
data.Add(jsonutils.NewBool(noStatus), "not_sync_status")
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "BaremetalSyncConfigTask", self, userCred, data, parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -54,9 +54,14 @@ func (self *BaremetalSyncConfigTask) DoSyncConfig(ctx context.Context, baremetal
|
||||
}
|
||||
|
||||
func (self *BaremetalSyncConfigTask) OnSyncConfigComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
logclient.AddActionLogWithStartable(self, baremetal, logclient.ACT_SYNC_CONF, "", self.UserCred, true)
|
||||
notSyncStatus, _ := self.Params.Bool("not_sync_status")
|
||||
if notSyncStatus {
|
||||
self.SetStageComplete(ctx, nil)
|
||||
return
|
||||
}
|
||||
self.SetStage("OnSyncstatusComplete", nil)
|
||||
baremetal.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
|
||||
logclient.AddActionLogWithStartable(self, baremetal, logclient.ACT_SYNC_CONF, "", self.UserCred, true)
|
||||
}
|
||||
|
||||
func (self *BaremetalSyncConfigTask) OnSyncstatusComplete(ctx context.Context, baremetal *models.SHost, body jsonutils.JSONObject) {
|
||||
|
||||
@@ -596,6 +596,10 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
if mainNic != nil {
|
||||
mainIp = mainNic.Ip
|
||||
}
|
||||
|
||||
var systemdResolveConfig strings.Builder
|
||||
dnss := []string{}
|
||||
domains := []string{}
|
||||
for i := range allNics {
|
||||
nicDesc := allNics[i]
|
||||
cmds.WriteString(fmt.Sprintf("auto %s\n", nicDesc.Name))
|
||||
@@ -628,8 +632,10 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
dnslist := netutils2.GetNicDns(nicDesc)
|
||||
if len(dnslist) > 0 {
|
||||
cmds.WriteString(fmt.Sprintf(" dns-nameservers %s\n", strings.Join(dnslist, " ")))
|
||||
dnss = append(dnss, dnslist...)
|
||||
if len(nicDesc.Domain) > 0 {
|
||||
cmds.WriteString(fmt.Sprintf(" dns-search %s\n", nicDesc.Domain))
|
||||
domains = append(domains, nicDesc.Domain)
|
||||
}
|
||||
}
|
||||
if len(nicDesc.TeamingSlaves) > 0 {
|
||||
@@ -644,6 +650,19 @@ func (d *sDebianLikeRootFs) DeployNetworkingScripts(rootFs IDiskPartition, nics
|
||||
cmds.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if len(dnss) != 0 {
|
||||
systemdResolveConfig.WriteString("[Resolve]\n")
|
||||
systemdResolveConfig.WriteString(fmt.Sprintf("DNS=%s\n", strings.Join(dnss, " ")))
|
||||
if len(domains) != 0 {
|
||||
systemdResolveConfig.WriteString(fmt.Sprintf("Domains=%s\n", strings.Join(domains, " ")))
|
||||
}
|
||||
systemdResolveFn := "/etc/systemd/resolved.conf"
|
||||
content := systemdResolveConfig.String()
|
||||
if err := rootFs.FilePutContents(systemdResolveFn, content, false, false); err != nil {
|
||||
log.Warningf("Put %s to %s error: %v", content, systemdResolveFn, err)
|
||||
}
|
||||
}
|
||||
log.Debugf("%s", cmds.String())
|
||||
return rootFs.FilePutContents(fn, cmds.String(), false, false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user