feat(region,baremetal,climc): support baremetal root disk matcher (#20480)

This commit is contained in:
Zexi Li
2024-06-05 00:17:30 +08:00
committed by GitHub
parent f1d6aa8e5a
commit 83a96ef8f2
9 changed files with 216 additions and 6 deletions

View File

@@ -310,6 +310,21 @@ type BaremetalDiskConfig struct {
Direct *bool `json:"direct,omitempty"`
}
type RootDiskMatcherSizeMBRange struct {
Start int64 `json:"start"`
End int64 `json:"end"`
}
const (
BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER = "baremetal_root_disk_matcher"
)
type BaremetalRootDiskMatcher struct {
Device string `json:"device"`
SizeMB int64 `json:"size_mb"`
SizeMBRange *RootDiskMatcherSizeMBRange `json:"size_mb_range"`
}
type ServerConfigs struct {
// 调度使用指定的云账号
PreferManager string `json:"prefer_manager_id"`
@@ -405,6 +420,9 @@ type ServerConfigs struct {
// 裸金属磁盘配置列表
BaremetalDiskConfigs []*BaremetalDiskConfig `json:"baremetal_disk_configs"`
// 裸金属系统盘匹配器
BaremetalRootDiskMatcher *BaremetalRootDiskMatcher `json:"baremetal_root_disk_matcher"`
// 主机组列表, 参数可以是主机组名称或ID,建议使用ID
InstanceGroupIds []string `json:"groups"`

View File

@@ -2546,6 +2546,34 @@ func (s *SBaremetalServer) GetRootTemplateId() string {
return id
}
func (s *SBaremetalServer) GetMetadata() (*jsonutils.JSONDict, error) {
metadata, err := s.desc.Get("metadata")
if err != nil {
return nil, errors.Wrap(err, "get desc.metadata")
}
return metadata.(*jsonutils.JSONDict), nil
}
func (s *SBaremetalServer) GetRootDiskMatcher() (*api.BaremetalRootDiskMatcher, error) {
metadata, err := s.GetMetadata()
if err != nil {
return nil, errors.Wrap(err, "get metadata")
}
if !metadata.Contains(api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER) {
return nil, errors.Wrapf(errors.ErrNotFound, "not found %s in metadata", api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
}
jStr, _ := metadata.GetString(api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
jObj, err := jsonutils.ParseString(jStr)
if err != nil {
return nil, errors.Wrapf(err, "parse json string: %s", jStr)
}
matcher := new(api.BaremetalRootDiskMatcher)
if err := jObj.Unmarshal(matcher); err != nil {
return nil, errors.Wrapf(err, "unmarshal to matcher")
}
return matcher, nil
}
func (s *SBaremetalServer) GetDiskConfig() ([]*api.BaremetalDiskConfig, error) {
layouts := make([]baremetal.Layout, 0)
err := s.desc.Unmarshal(&layouts, "disk_config")
@@ -2598,7 +2626,11 @@ func (s *SBaremetalServer) NewConfigedSSHPartitionTool(term *ssh.Client) (*diskt
}
}
tool, err := disktool.NewSSHPartitionTool(term, layouts)
matcher, err := s.GetRootDiskMatcher()
if errors.Cause(err) != errors.ErrNotFound {
log.Errorf("GetRootDiskMatcher: %v", err)
}
tool, err := disktool.NewSSHPartitionTool(term, layouts, matcher)
if err != nil {
return nil, errors.Wrap(err, "NewSSHPartitionTool")
}
@@ -2651,7 +2683,11 @@ func (s *SBaremetalServer) DoDiskConfig(term *ssh.Client) (*disktool.SSHPartitio
}
}
tool, err := disktool.NewSSHPartitionTool(term, layouts)
matcher, err := s.GetRootDiskMatcher()
if errors.Cause(err) != errors.ErrNotFound {
log.Errorf("GetRootDiskMatcher: %v", err)
}
tool, err := disktool.NewSSHPartitionTool(term, layouts, matcher)
if err != nil {
return nil, errors.Wrap(err, "NewSSHPartitionTool")
}

View File

@@ -664,7 +664,7 @@ func (tool *PartitionTool) parseLsDisk(lines []string, driver string) {
}
}
func (tool *PartitionTool) FetchDiskConfs(diskConfs []baremetal.DiskConfiguration) *PartitionTool {
func (tool *PartitionTool) FetchDiskConfs(diskConfs []baremetal.DiskConfiguration, rootMatcher *api.BaremetalRootDiskMatcher) *PartitionTool {
for _, d := range diskConfs {
disk := newDiskPartitions(d.Driver, d.Adapter, d.RaidConfig, d.Size, d.Block, d.DiskType, tool)
tool.disks = append(tool.disks, disk)
@@ -681,9 +681,56 @@ func (tool *PartitionTool) FetchDiskConfs(diskConfs []baremetal.DiskConfiguratio
}
tool.diskTable[key] = append(tool.diskTable[key], disk)
}
// reorder tool.disks
if rootMatcher != nil {
tool.reorderRootDisk(rootMatcher)
}
return tool
}
func (tool *PartitionTool) reorderRootDisk(matcher *api.BaremetalRootDiskMatcher) {
var rootDiskIdx = 0
isDiskMatch := func(disk *DiskPartitions, matcher *api.BaremetalRootDiskMatcher) bool {
if matcher.Device != "" {
if disk.dev == matcher.Device {
return true
}
if disk.devName == matcher.Device {
return true
}
}
if matcher.SizeMB > 0 {
if disk.sizeMB == matcher.SizeMB {
return true
}
}
if matcher.SizeMBRange != nil {
if disk.sizeMB >= matcher.SizeMBRange.Start && disk.sizeMB <= matcher.SizeMBRange.End {
return true
}
}
return false
}
for idx, disk := range tool.disks {
if isDiskMatch(disk, matcher) {
rootDiskIdx = idx
break
}
}
log.Infof("Select %d as root disk", rootDiskIdx)
newDisks := make([]*DiskPartitions, 0)
newDisks = append(newDisks, tool.disks[rootDiskIdx])
for idx := range tool.disks {
if idx == rootDiskIdx {
continue
}
newDisks = append(newDisks, tool.disks[idx])
}
tool.disks = newDisks
}
func (tool *PartitionTool) IsAllDisksReady() bool {
for idx, d := range tool.disks {
if !d.IsReady() {
@@ -788,9 +835,9 @@ func newSSHPartitionTool(term *ssh.Client) *SSHPartitionTool {
return tool
}
func NewSSHPartitionTool(term *ssh.Client, layouts []baremetal.Layout) (*SSHPartitionTool, error) {
func NewSSHPartitionTool(term *ssh.Client, layouts []baremetal.Layout, rootMatcher *api.BaremetalRootDiskMatcher) (*SSHPartitionTool, error) {
tool := newSSHPartitionTool(term)
tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts))
tool.FetchDiskConfs(baremetal.GetDiskConfigurations(layouts), rootMatcher)
if err := tool.RetrieveDiskInfo(); err != nil {
return nil, errors.Wrapf(err, "RetrieveDiskInfo")
}

View File

@@ -371,6 +371,47 @@ func ParseIsolatedDevice(desc string, idx int) (*compute.IsolatedDeviceConfig, e
return dev, nil
}
func ParseBaremetalRootDiskMatcher(line string) (*compute.BaremetalRootDiskMatcher, error) {
ret := new(compute.BaremetalRootDiskMatcher)
for _, seg := range strings.Split(line, ",") {
info := strings.Split(seg, "=")
if len(info) != 2 {
return nil, errors.Errorf("invalid option %s", seg)
}
key := info[0]
val := info[1]
switch key {
case "size":
sizeMB, err := fileutils.GetSizeMb(val, 'M', 1024)
if err != nil {
return nil, errors.Wrapf(err, "parse size %s", val)
}
ret.SizeMB = int64(sizeMB)
case "device", "dev":
ret.Device = val
case "size_start":
sizeMB, err := fileutils.GetSizeMb(val, 'M', 1024)
if err != nil {
return nil, errors.Wrapf(err, "parse size_start %s", val)
}
if ret.SizeMBRange == nil {
ret.SizeMBRange = new(compute.RootDiskMatcherSizeMBRange)
}
ret.SizeMBRange.Start = int64(sizeMB)
case "size_end":
sizeMB, err := fileutils.GetSizeMb(val, 'M', 1024)
if err != nil {
return nil, errors.Wrapf(err, "parse size_end %s", val)
}
if ret.SizeMBRange == nil {
ret.SizeMBRange = new(compute.RootDiskMatcherSizeMBRange)
}
ret.SizeMBRange.End = int64(sizeMB)
}
}
return ret, nil
}
func ParseBaremetalDiskConfig(desc string) (*compute.BaremetalDiskConfig, error) {
bdc := new(compute.BaremetalDiskConfig)
bdc.Type = compute.DISK_TYPE_HYBRID

View File

@@ -241,3 +241,44 @@ func TestParseIsolatedDevice(t *testing.T) {
})
}
}
func TestParseBaremetalRootDiskMatcher(t *testing.T) {
tests := []struct {
args string
want *compute.BaremetalRootDiskMatcher
wantErr bool
}{
{
args: "size=100G",
want: &compute.BaremetalRootDiskMatcher{SizeMB: 102400},
},
{
args: "device=/dev/sda",
want: &compute.BaremetalRootDiskMatcher{Device: "/dev/sda"},
},
{
args: "size_end=100G",
want: &compute.BaremetalRootDiskMatcher{SizeMBRange: &compute.RootDiskMatcherSizeMBRange{End: 102400}},
},
{
args: "size_end=100G,size_start=50G",
want: &compute.BaremetalRootDiskMatcher{
SizeMBRange: &compute.RootDiskMatcherSizeMBRange{
Start: 51200,
End: 102400,
}},
},
}
for _, tt := range tests {
t.Run(tt.args, func(t *testing.T) {
got, err := ParseBaremetalRootDiskMatcher(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("ParseBaremetalRootDiskMatcher() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseBaremetalRootDiskMatcher() got = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -23,6 +23,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/sets"
"yunion.io/x/pkg/utils"
@@ -683,3 +684,12 @@ func ValidateDiskConfigs(confs []*api.BaremetalDiskConfig) error {
}
return nil
}
func ValidateRootDiskMatcher(input *api.BaremetalRootDiskMatcher) error {
if input.SizeMBRange != nil {
if input.SizeMBRange.Start > input.SizeMBRange.End {
return errors.Errorf("size_mb_range.start %d is large than size_mb_range.end %d", input.SizeMBRange.Start, input.SizeMBRange.End)
}
}
return nil
}

View File

@@ -417,6 +417,11 @@ func (self *SBaremetalGuestDriver) ValidateCreateData(ctx context.Context, userC
return nil, httperrors.NewInputParameterError("Invalid raid config: %v", err)
}
}
if input.BaremetalRootDiskMatcher != nil {
if err := baremetal.ValidateRootDiskMatcher(input.BaremetalRootDiskMatcher); err != nil {
return nil, httperrors.NewInputParameterError("Invalid root disk matcher: %v", err)
}
}
//if len(input.Disks) <= 0 {
// return nil, httperrors.NewInputParameterError("Root disk must be present")
//}

View File

@@ -2412,6 +2412,10 @@ func (guest *SGuest) PostCreate(ctx context.Context, userCred mcclient.TokenCred
if jsonutils.QueryBoolean(data, imageapi.IMAGE_DISABLE_USB_KBD, false) {
guest.SetMetadata(ctx, imageapi.IMAGE_DISABLE_USB_KBD, "true", userCred)
}
matcherJson, _ := data.Get(api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER)
if matcherJson != nil {
guest.SetMetadata(ctx, api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER, matcherJson, userCred)
}
userData, _ := data.GetString("user_data")
if len(userData) > 0 {

View File

@@ -353,7 +353,8 @@ type ServerConfigs struct {
AutoSwitchToBackupOnHostDown bool `help:"Auto switch to backup server on host down"`
Daemon *bool `help:"Set as a daemon server" json:"is_daemon"`
RaidConfig []string `help:"Baremetal raid config" json:"-"`
RaidConfig []string `help:"Baremetal raid config" json:"-"`
RootDiskMatcher string `help:"Baremetal root disk matcher, e.g. 'device=/dev/sdb' 'size=900G' 'size_start=800G,size_end=900G'" json:"-"`
}
func (o ServerConfigs) Data() (*computeapi.ServerConfigs, error) {
@@ -377,6 +378,13 @@ func (o ServerConfigs) Data() (*computeapi.ServerConfigs, error) {
data.BaremetalDiskConfigs = append(data.BaremetalDiskConfigs, raidConf)
}
}
if len(o.RootDiskMatcher) > 0 {
matcher, err := cmdline.ParseBaremetalRootDiskMatcher(o.RootDiskMatcher)
if err != nil {
return nil, err
}
data.BaremetalRootDiskMatcher = matcher
}
return data, nil
}