mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-06-08 15:24:23 +08:00
feat(baremetal): add UEFI related util
This commit is contained in:
@@ -999,9 +999,12 @@ func (b *SBaremetalInstance) getDHCPConfig(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isPxe && !b.NeedPXEBoot() {
|
||||
if isPxe && IsUEFIPxeArch(arch) && !b.NeedPXEBoot() {
|
||||
// TODO: use chainloader boot UEFI firmware,
|
||||
// currently not response PXE request,
|
||||
// and let BIOS detect bootable device
|
||||
b.ClearSSHConfig()
|
||||
return nil, errors.Errorf("Baremetal %s not need PXE boot", b.GetName())
|
||||
return nil, errors.Errorf("Baremetal %s not need UEFI PXE boot", b.GetName())
|
||||
}
|
||||
return GetNicDHCPConfig(nic, serverIP.String(), hostName, isPxe, arch)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,30 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/util/dhcp"
|
||||
)
|
||||
|
||||
const (
|
||||
// ref: https://datatracker.ietf.org/doc/html/rfc4578#section-2.1
|
||||
PXE_CLIENT_ARCH_INTEL_X86PC = iota
|
||||
PXE_CLIENT_ARCH_NEC_PC98
|
||||
PXE_CLIENT_ARCH_EFI_ITANIUM
|
||||
PXE_CLIENT_ARCH_DEC_ALPHA
|
||||
PXE_CLIENT_ARCH_ARC_X86
|
||||
PXE_CLIENT_ARCH_INTEL_LEAN_CLIENT
|
||||
PXE_CLIENT_ARCH_EFI_IA32
|
||||
PXE_CLIENT_ARCH_EFI_BC
|
||||
PXE_CLIENT_ARCH_EFI_XSCALE
|
||||
PXE_CLIENT_ARCH_EFI_X86_64
|
||||
)
|
||||
|
||||
func IsUEFIPxeArch(arch uint16) bool {
|
||||
switch arch {
|
||||
case PXE_CLIENT_ARCH_EFI_IA32:
|
||||
return true
|
||||
case PXE_CLIENT_ARCH_EFI_BC, PXE_CLIENT_ARCH_EFI_XSCALE, PXE_CLIENT_ARCH_EFI_X86_64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetNicDHCPConfig(
|
||||
n *types.SNic,
|
||||
serverIP string,
|
||||
@@ -71,9 +95,9 @@ func GetNicDHCPConfig(
|
||||
if isPxe {
|
||||
conf.BootServer = serverIP
|
||||
switch arch {
|
||||
case 7, 9:
|
||||
case PXE_CLIENT_ARCH_EFI_BC, PXE_CLIENT_ARCH_EFI_X86_64:
|
||||
conf.BootFile = "bootx64.efi"
|
||||
case 6:
|
||||
case PXE_CLIENT_ARCH_EFI_IA32:
|
||||
conf.BootFile = "bootia32.efi"
|
||||
default:
|
||||
//if o.Options.EnableTftpHttpDownload {
|
||||
|
||||
@@ -26,6 +26,7 @@ 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"
|
||||
@@ -490,3 +491,31 @@ func (self *SBaremetalPXEBootTaskBase) OnStopComplete(ctx context.Context, args
|
||||
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")
|
||||
}
|
||||
|
||||
log.Errorf("=====before set ")
|
||||
time.Sleep(30 * time.Second)
|
||||
|
||||
if err := uefi.RemoteSetCurrentBootAtFirst(term, mgr); err != nil {
|
||||
return errors.Wrap(err, "Set current pxe boot at fist")
|
||||
}
|
||||
|
||||
log.Errorf("=====after set ")
|
||||
time.Sleep(30 * time.Second)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -89,6 +89,11 @@ func (self *SBaremetalServerBaseDeployTask) OnPXEBoot(ctx context.Context, term
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Do deploy")
|
||||
}
|
||||
|
||||
if err := AdjustUEFIBootOrder(term); err != nil {
|
||||
return errors.Wrap(err, "Adjust UEFI boot order")
|
||||
}
|
||||
|
||||
_, err = term.Run(
|
||||
"/bin/sync",
|
||||
"/sbin/sysctl -w vm.drop_caches=3",
|
||||
|
||||
@@ -327,6 +327,11 @@ func (task *sBaremetalPrepareTask) DoPrepare(cli *ssh.Client) error {
|
||||
log.Errorf("SetNTP fail: %s", err)
|
||||
}
|
||||
|
||||
if err = AdjustUEFIBootOrder(cli); err != nil {
|
||||
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
||||
return errors.Wrap(err, "Adjust UEFI boot order")
|
||||
}
|
||||
|
||||
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, infos.sysInfo, task.userCred, true)
|
||||
|
||||
log.Infof("Prepare complete")
|
||||
|
||||
324
pkg/baremetal/utils/uefi/uefi.go
Normal file
324
pkg/baremetal/utils/uefi/uefi.go
Normal file
@@ -0,0 +1,324 @@
|
||||
// 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 uefi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
type BootMgr struct {
|
||||
// bootCurrent - the boot entry used to start the currently running system.
|
||||
bootCurrent string
|
||||
|
||||
// bootOrder - the boot order as would appear in the boot manager.
|
||||
// The boot manager tries to boot the first active entry on this list.
|
||||
// If unsuccessful, it tries the next entry, and so on.
|
||||
bootOrder []string
|
||||
|
||||
// bootNext - the boot entry which is scheduled to be run on next boot.
|
||||
// This superceeds BootOrder for one boot only, and is deleted by the
|
||||
// boot manager after first use.
|
||||
// This allows you to change the next boot behavior without changing BootOrder.
|
||||
bootNext string
|
||||
|
||||
// timeout - the time in seconds between when the boot manager appears on the screen
|
||||
// until when it automatically chooses the startup value from BootNext or BootOrder.
|
||||
timeout int
|
||||
|
||||
// entries - the boot entry parsed in map
|
||||
entries map[string]*BootEntry
|
||||
}
|
||||
|
||||
type BootEntry struct {
|
||||
BootNum string
|
||||
Description string
|
||||
IsActive bool
|
||||
}
|
||||
|
||||
func ParseEFIBootMGR(input string) (*BootMgr, error) {
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
mgr := &BootMgr{
|
||||
bootOrder: []string{},
|
||||
timeout: -1,
|
||||
entries: make(map[string]*BootEntry),
|
||||
}
|
||||
|
||||
pf := func(ff func(string) bool) {
|
||||
for _, l := range lines {
|
||||
if ok := ff(l); ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse BootCurrent
|
||||
pf(func(l string) bool {
|
||||
if current := parseEFIBootMGRBootCurrent(l); current != "" {
|
||||
mgr.bootCurrent = current
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse Timeout second
|
||||
pf(func(l string) bool {
|
||||
if timeout := parseEFIBootMGRTimeout(l); timeout != -1 {
|
||||
mgr.timeout = timeout
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse BootOrder
|
||||
pf(func(l string) bool {
|
||||
if order := parseEFIBootMGRBootOrder(l); len(order) != 0 {
|
||||
mgr.bootOrder = order
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse BootNext
|
||||
pf(func(l string) bool {
|
||||
if next := parseEFIBootMGRBootNext(l); next != "" {
|
||||
mgr.bootNext = next
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// parse entries
|
||||
pf(func(l string) bool {
|
||||
if entry := parseEFIBootMGREntry(l); entry != nil {
|
||||
mgr.entries[entry.BootNum] = entry
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// finally check
|
||||
if err := mgr.DataCheck(); err != nil {
|
||||
return nil, errors.Wrap(err, "Invalid efibootmgr parse")
|
||||
}
|
||||
|
||||
return mgr, nil
|
||||
}
|
||||
|
||||
func (m *BootMgr) DataCheck() error {
|
||||
if m.bootCurrent == "" {
|
||||
return errors.Error("BootCurrent is empty")
|
||||
}
|
||||
|
||||
if len(m.bootOrder) == 0 {
|
||||
return errors.Error("BootOrder length is 0")
|
||||
}
|
||||
|
||||
// check if BootOrder in entries
|
||||
for _, orderNum := range m.bootOrder {
|
||||
if _, ok := m.entries[orderNum]; !ok {
|
||||
return errors.Errorf("Not found BootOrder %s entry", orderNum)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseEFIBootMGRBootCurrent(line string) string {
|
||||
prefix := "BootCurrent: "
|
||||
if strings.HasPrefix(line, prefix) {
|
||||
return strings.Split(line, prefix)[1]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func parseEFIBootMGRBootOrder(line string) []string {
|
||||
prefix := "BootOrder: "
|
||||
if !strings.HasPrefix(line, prefix) {
|
||||
return nil
|
||||
}
|
||||
orderStr := strings.Split(line, prefix)[1]
|
||||
return strings.Split(orderStr, ",")
|
||||
}
|
||||
|
||||
func parseEFIBootMGRBootNext(line string) string {
|
||||
prefix := "BootNext: "
|
||||
if !strings.HasPrefix(line, prefix) {
|
||||
return ""
|
||||
}
|
||||
return strings.Split(line, prefix)[1]
|
||||
}
|
||||
|
||||
func parseEFIBootMGRTimeout(line string) int {
|
||||
timeoutRegexp := `^Timeout: (?P<seconds>[0-9]{1,}) seconds`
|
||||
matches := regutils2.SubGroupMatch(timeoutRegexp, line)
|
||||
if len(matches) == 0 {
|
||||
return -1
|
||||
}
|
||||
secondStr := matches["seconds"]
|
||||
second, err := strconv.Atoi(secondStr)
|
||||
if err != nil {
|
||||
log.Errorf("parse %s seconds error: %v", secondStr, err)
|
||||
return -1
|
||||
}
|
||||
return second
|
||||
}
|
||||
|
||||
func parseEFIBootMGREntry(line string) *BootEntry {
|
||||
entryRegexp := `^Boot(?P<num>[0-9a-zA-Z]{4})[^:]+?\s+(?P<description>.*)`
|
||||
matches := regutils2.SubGroupMatch(entryRegexp, line)
|
||||
if len(matches) == 0 {
|
||||
return nil
|
||||
}
|
||||
num, ok := matches["num"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
desc, ok := matches["description"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
isActive := false
|
||||
if strings.Contains(line, "* ") {
|
||||
isActive = true
|
||||
}
|
||||
return &BootEntry{
|
||||
BootNum: num,
|
||||
Description: desc,
|
||||
IsActive: isActive,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEFIBootMgrFromRemote(cli *ssh.Client) (*BootMgr, error) {
|
||||
cmd := CMD_EFIBOOTMGR
|
||||
lines, err := cli.RawRun(cmd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Execute command: %s", cmd)
|
||||
}
|
||||
return ParseEFIBootMGR(strings.Join(lines, "\n"))
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetCommand() string {
|
||||
return CMD_EFIBOOTMGR
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootCurrent() string {
|
||||
return m.bootCurrent
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootOrder() []string {
|
||||
return m.bootOrder
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootNext() string {
|
||||
return m.bootNext
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetTimeout() int {
|
||||
return m.timeout
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetBootEntry(num string) *BootEntry {
|
||||
return m.entries[num]
|
||||
}
|
||||
|
||||
func (m *BootMgr) FindBootOrderPos(num string) int {
|
||||
return stringArraryFindItemPos(m.bootOrder, num)
|
||||
}
|
||||
|
||||
func stringArraryFindItemPos(items []string, item string) int {
|
||||
for idx, elem := range items {
|
||||
if elem == item {
|
||||
return idx
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func stringArraryMove(items []string, item string, pos int) []string {
|
||||
origPos := stringArraryFindItemPos(items, item)
|
||||
if origPos == -1 {
|
||||
items = append(items, item)
|
||||
origPos = stringArraryFindItemPos(items, item)
|
||||
}
|
||||
|
||||
for i := origPos; i != pos; {
|
||||
if i < pos {
|
||||
// from left to right
|
||||
tmp := items[i]
|
||||
items[i] = items[i+1]
|
||||
items[i+1] = tmp
|
||||
i++
|
||||
} else if i > pos {
|
||||
// from right to left
|
||||
tmp := items[i]
|
||||
items[i] = items[i-1]
|
||||
items[i-1] = tmp
|
||||
i--
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (m *BootMgr) MoveBootOrder(num string, pos int) *BootMgr {
|
||||
if entry := m.GetBootEntry(num); entry == nil {
|
||||
log.Warningf("Not found boot entry by %q", num)
|
||||
return m
|
||||
}
|
||||
m.bootOrder = stringArraryMove(m.bootOrder, num, pos)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *BootMgr) GetSetBootOrderArgs() string {
|
||||
return strings.Join(m.bootOrder, ",")
|
||||
}
|
||||
|
||||
func RemoteIsUEFIBoot(cli *ssh.Client) (bool, error) {
|
||||
checkCmd := "test -d /sys/firmware/efi && echo is || echo not"
|
||||
lines, err := cli.Run(checkCmd)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "is") {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func RemoteSetCurrentBootAtFirst(cli *ssh.Client, mgr *BootMgr) error {
|
||||
curPos := mgr.FindBootOrderPos(mgr.GetBootCurrent())
|
||||
if curPos == -1 {
|
||||
return errors.Errorf("Not found BootCurrent position %q", mgr.GetBootCurrent())
|
||||
}
|
||||
// move to first
|
||||
mgr.MoveBootOrder(mgr.GetBootCurrent(), 0)
|
||||
cmd := fmt.Sprintf("%s -o %s", mgr.GetCommand(), mgr.GetSetBootOrderArgs())
|
||||
_, err := cli.Run(cmd)
|
||||
return err
|
||||
}
|
||||
316
pkg/baremetal/utils/uefi/uefi_test.go
Normal file
316
pkg/baremetal/utils/uefi/uefi_test.go
Normal file
@@ -0,0 +1,316 @@
|
||||
// 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 uefi
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
TestPCOutput = `BootCurrent: 0007
|
||||
Timeout: 2 seconds
|
||||
BootOrder: 0003,0002,0001,0000,0004,0005,0007,0008,0009,000A
|
||||
Boot0000 Windows Boot Manager
|
||||
Boot0001 ubuntu
|
||||
Boot0002 ubuntu
|
||||
Boot0003 CentOS Linux
|
||||
Boot0004* Onboard NIC(IPV4)
|
||||
Boot0005 Onboard NIC(IPV6)
|
||||
Boot0007* PXE IPv4 Intel(R) Ethernet Server Adapter X520-2
|
||||
Boot0008 PXE IPv6 Intel(R) Ethernet Server Adapter X520-2
|
||||
Boot0009* PXE IPv4 Intel(R) Ethernet Server Adapter X520-2
|
||||
Boot000A PXE IPv6 Intel(R) Ethernet Server Adapter X520-2`
|
||||
|
||||
TestQemuOutput = `BootCurrent: 0005
|
||||
Timeout: 0 seconds
|
||||
BootOrder: 0005,0009,0008,0007,0002,0003,0004,0006,0001,0000
|
||||
Boot0000* UiApp
|
||||
Boot0001* UEFI QEMU DVD-ROM QM00003
|
||||
Boot0002* UEFI Floppy
|
||||
Boot0003* UEFI Floppy 2
|
||||
Boot0004* UEFI QEMU HARDDISK QM00001
|
||||
Boot0005* UEFI PXEv4 (MAC:525400123456)
|
||||
Boot0006* EFI Internal Shell
|
||||
Boot0007* CentOS
|
||||
Boot0008* CentOS Linux
|
||||
Boot0009* ubuntu`
|
||||
)
|
||||
|
||||
func Test_parseTimeout(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want int
|
||||
}{
|
||||
{
|
||||
input: "Timeout: 0 seconds",
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
input: "Timeout: 30 seconds",
|
||||
want: 30,
|
||||
},
|
||||
{
|
||||
input: "Timeout: seconds",
|
||||
want: -1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := parseEFIBootMGRTimeout(tc.input); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("parseEFIBootMGRTimeout() = %v, want %v", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseEFIBootMGRBootOrder(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
input: "BootOrder: 0005,0009,0008,0007,0002,0003,0004,0006,0001,0000",
|
||||
want: []string{"0005", "0009", "0008", "0007", "0002", "0003", "0004", "0006", "0001", "0000"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if got := parseEFIBootMGRBootOrder(tc.input); !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("parseEFIBootMGRBootOrder() = %v, want %v", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseEFIBootMGRBootCurrent(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
line string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "BootCurrent: 0005",
|
||||
line: "BootCurrent: 0005",
|
||||
want: "0005",
|
||||
},
|
||||
{
|
||||
name: "BootCurrent: ",
|
||||
line: "BootCurrent: ",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "BootCurrent:",
|
||||
line: "BootCurrent:",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := parseEFIBootMGRBootCurrent(tt.line); got != tt.want {
|
||||
t.Errorf("parseEFIBootMGRBootCurrent() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseEFIBootMGREntry(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
line string
|
||||
want *BootEntry
|
||||
}{
|
||||
{
|
||||
name: "Boot0009* ubuntu",
|
||||
line: "Boot0009* ubuntu",
|
||||
want: &BootEntry{
|
||||
BootNum: "0009",
|
||||
Description: "ubuntu",
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Boot0005* UEFI PXEv4 (MAC:525400123456)",
|
||||
line: "Boot0005* UEFI PXEv4 (MAC:525400123456)",
|
||||
want: &BootEntry{
|
||||
BootNum: "0005",
|
||||
Description: "UEFI PXEv4 (MAC:525400123456)",
|
||||
IsActive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Boot0003 CentOS Linux",
|
||||
line: "Boot0003 CentOS Linux",
|
||||
want: &BootEntry{
|
||||
BootNum: "0003",
|
||||
Description: "CentOS Linux",
|
||||
IsActive: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Timeout: 2 seconds",
|
||||
line: "Timeout: 2 seconds",
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := parseEFIBootMGREntry(tt.line); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseEFIBootMGREntry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEFIBootMGR(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want *BootMgr
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Parse PC output",
|
||||
input: TestPCOutput,
|
||||
want: &BootMgr{
|
||||
bootCurrent: "0007",
|
||||
timeout: 2,
|
||||
bootOrder: []string{"0003", "0002", "0001", "0000", "0004", "0005", "0007", "0008", "0009", "000A"},
|
||||
entries: map[string]*BootEntry{
|
||||
"0000": {
|
||||
BootNum: "0000",
|
||||
IsActive: false,
|
||||
Description: "Windows Boot Manager",
|
||||
},
|
||||
"0001": {
|
||||
BootNum: "0001",
|
||||
IsActive: false,
|
||||
Description: "ubuntu",
|
||||
},
|
||||
"0002": {
|
||||
BootNum: "0002",
|
||||
IsActive: false,
|
||||
Description: "ubuntu",
|
||||
},
|
||||
"0003": {
|
||||
BootNum: "0003",
|
||||
IsActive: false,
|
||||
Description: "CentOS Linux",
|
||||
},
|
||||
"0004": {
|
||||
BootNum: "0004",
|
||||
IsActive: true,
|
||||
Description: "Onboard NIC(IPV4)",
|
||||
},
|
||||
"0005": {
|
||||
BootNum: "0005",
|
||||
IsActive: false,
|
||||
Description: "Onboard NIC(IPV6)",
|
||||
},
|
||||
"0007": {
|
||||
BootNum: "0007",
|
||||
IsActive: true,
|
||||
Description: "PXE IPv4 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
"0008": {
|
||||
BootNum: "0008",
|
||||
IsActive: false,
|
||||
Description: "PXE IPv6 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
"0009": {
|
||||
BootNum: "0009",
|
||||
IsActive: true,
|
||||
Description: "PXE IPv4 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
"000A": {
|
||||
BootNum: "000A",
|
||||
IsActive: false,
|
||||
Description: "PXE IPv6 Intel(R) Ethernet Server Adapter X520-2",
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
assert := assert.New(t)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseEFIBootMGR(tt.input)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseEFIBootMGR() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if equal := assert.Equal(tt.want, got); !equal {
|
||||
t.Errorf("ParseEFIBootMGR() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_stringArraryMove(t *testing.T) {
|
||||
type args struct {
|
||||
items []string
|
||||
item string
|
||||
pos int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "move left to right",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "3",
|
||||
pos: 0,
|
||||
},
|
||||
want: []string{"3", "1", "2"},
|
||||
},
|
||||
{
|
||||
name: "move right to left",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "1",
|
||||
pos: 2,
|
||||
},
|
||||
want: []string{"2", "3", "1"},
|
||||
},
|
||||
{
|
||||
name: "no move",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "2",
|
||||
pos: 1,
|
||||
},
|
||||
want: []string{"1", "2", "3"},
|
||||
},
|
||||
{
|
||||
name: "add item move",
|
||||
args: args{
|
||||
items: []string{"1", "2", "3"},
|
||||
item: "4",
|
||||
pos: 0,
|
||||
},
|
||||
want: []string{"4", "1", "2", "3"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := stringArraryMove(tt.args.items, tt.args.item, tt.args.pos); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("stringArraryMove() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user