mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-10 07:38:05 +08:00
在 #8830 中,对调度器增加了通过请求磁盘的介质类型过滤存储的功能。 如果使用 climc 创建虚拟机且不要求磁盘有固定的介质类型,将 只能分配到 hybrid 的存储;进一步,如果没有 hybrid 的存储,将会调度失败。
372 lines
9.5 KiB
Go
372 lines
9.5 KiB
Go
// 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 cmdline
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/util/fileutils"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/util/osprofile"
|
|
"yunion.io/x/pkg/util/regutils"
|
|
"yunion.io/x/pkg/util/sets"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis/compute"
|
|
)
|
|
|
|
var (
|
|
ErrorEmptyDesc = errors.New("Empty description")
|
|
)
|
|
|
|
// ParseSchedtagConfig desc format: <schedtagName>:<strategy>:<resource_type>
|
|
func ParseSchedtagConfig(desc string) (*compute.SchedtagConfig, error) {
|
|
if len(desc) == 0 {
|
|
return nil, ErrorEmptyDesc
|
|
}
|
|
parts := strings.Split(desc, ":")
|
|
if len(parts) < 2 {
|
|
return nil, fmt.Errorf("Invalid desc: %s", desc)
|
|
}
|
|
strategy := parts[1]
|
|
if !utils.IsInStringArray(strategy, compute.STRATEGY_LIST) {
|
|
return nil, fmt.Errorf("Invalid strategy: %s", strategy)
|
|
}
|
|
conf := &compute.SchedtagConfig{
|
|
Id: parts[0],
|
|
Strategy: parts[1],
|
|
}
|
|
if len(parts) == 3 {
|
|
conf.ResourceType = parts[2]
|
|
}
|
|
return conf, nil
|
|
}
|
|
|
|
// ParseResourceSchedtagConfig desc format: <idx>:<schedtagName>:<strategy>
|
|
func ParseResourceSchedtagConfig(desc string) (int, *compute.SchedtagConfig, error) {
|
|
if len(desc) == 0 {
|
|
return 0, nil, ErrorEmptyDesc
|
|
}
|
|
parts := strings.Split(desc, ":")
|
|
if len(parts) != 3 {
|
|
return 0, nil, fmt.Errorf("Invalid desc: %s", desc)
|
|
}
|
|
idx, err := strconv.Atoi(parts[0])
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
tag, err := ParseSchedtagConfig(fmt.Sprintf("%s:%s", parts[1], parts[2]))
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
return idx, tag, nil
|
|
}
|
|
|
|
func ParseDiskConfig(diskStr string, idx int) (*compute.DiskConfig, error) {
|
|
if len(diskStr) == 0 {
|
|
return nil, ErrorEmptyDesc
|
|
}
|
|
diskConfig := new(compute.DiskConfig)
|
|
diskConfig.Index = idx
|
|
|
|
// default backend and medium type
|
|
diskConfig.Backend = "" // STORAGE_LOCAL
|
|
diskConfig.Medium = ""
|
|
|
|
parts := strings.Split(diskStr, ":")
|
|
for _, p := range parts {
|
|
if len(p) == 0 {
|
|
continue
|
|
}
|
|
if regutils.MatchSize(p) {
|
|
diskConfig.SizeMb, _ = fileutils.GetSizeMb(p, 'M', 1024)
|
|
} else if utils.IsInStringArray(p, osprofile.FS_TYPES) {
|
|
diskConfig.Fs = p
|
|
} else if utils.IsInStringArray(p, osprofile.IMAGE_FORMAT_TYPES) {
|
|
diskConfig.Format = p
|
|
} else if utils.IsInStringArray(p, osprofile.DISK_DRIVERS) {
|
|
diskConfig.Driver = p
|
|
} else if utils.IsInStringArray(p, osprofile.DISK_CACHE_MODES) {
|
|
diskConfig.Cache = p
|
|
} else if utils.IsInStringArray(p, compute.DISK_TYPES) {
|
|
diskConfig.Medium = p
|
|
} else if utils.IsInStringArray(p, []string{compute.DISK_TYPE_VOLUME}) {
|
|
diskConfig.DiskType = p
|
|
} else if p[0] == '/' {
|
|
diskConfig.Mountpoint = p
|
|
} else if p == "autoextend" {
|
|
diskConfig.SizeMb = -1
|
|
} else if utils.IsInStringArray(p, compute.STORAGE_ALL_TYPES) {
|
|
diskConfig.Backend = p
|
|
} else if strings.HasPrefix(p, "snapshot-") {
|
|
// HACK: use snapshot creat disk format snapshot-id
|
|
// example: snapshot-3140cecb-ccc4-4865-abae-3a5ba8c69d9b
|
|
diskConfig.SnapshotId = p[len("snapshot-"):]
|
|
} else if len(p) > 0 {
|
|
diskConfig.ImageId = p
|
|
}
|
|
}
|
|
|
|
return diskConfig, nil
|
|
}
|
|
|
|
func ParseNetworkConfigByJSON(desc jsonutils.JSONObject, idx int) (*compute.NetworkConfig, error) {
|
|
if _, ok := desc.(*jsonutils.JSONString); ok {
|
|
descStr, _ := desc.GetString()
|
|
return ParseNetworkConfig(descStr, idx)
|
|
}
|
|
conf := new(compute.NetworkConfig)
|
|
conf.Index = idx
|
|
err := desc.Unmarshal(conf)
|
|
return conf, err
|
|
}
|
|
|
|
func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) {
|
|
if len(desc) == 0 {
|
|
return nil, ErrorEmptyDesc
|
|
}
|
|
parts := strings.Split(desc, ":")
|
|
netConfig := new(compute.NetworkConfig)
|
|
netConfig.Index = idx
|
|
for _, p := range parts {
|
|
if len(p) == 0 {
|
|
continue
|
|
}
|
|
if regutils.MatchIP4Addr(p) {
|
|
netConfig.Address = p
|
|
} else if regutils.MatchIP6Addr(p) {
|
|
netConfig.Address6 = p
|
|
} else if regutils.MatchCompactMacAddr(p) {
|
|
netConfig.Mac = netutils.MacUnpackHex(p)
|
|
} else if strings.HasPrefix(p, "wire=") {
|
|
netConfig.Wire = p[len("wire="):]
|
|
} else if p == "[require_designated_ip]" {
|
|
netConfig.RequireDesignatedIP = true
|
|
} else if p == "[random_exit]" {
|
|
netConfig.Exit = true
|
|
} else if p == "[random]" {
|
|
netConfig.Exit = false
|
|
} else if p == "[private]" {
|
|
netConfig.Private = true
|
|
} else if p == "[reserved]" {
|
|
netConfig.Reserved = true
|
|
} else if p == "[teaming]" {
|
|
netConfig.RequireTeaming = true
|
|
} else if p == "[try-teaming]" {
|
|
netConfig.TryTeaming = true
|
|
} else if strings.HasPrefix(p, "standby-port=") {
|
|
netConfig.StandbyPortCount, _ = strconv.Atoi(p[len("standby-port="):])
|
|
} else if strings.HasPrefix(p, "standby-addr=") {
|
|
netConfig.StandbyAddrCount, _ = strconv.Atoi(p[len("standby-addr="):])
|
|
} else if utils.IsInStringArray(p, []string{"virtio", "e1000", "vmxnet3"}) {
|
|
netConfig.Driver = p
|
|
} else if regutils.MatchSize(p) {
|
|
bw, err := fileutils.GetSizeMb(p, 'M', 1000)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
netConfig.BwLimit = bw
|
|
} else if p == "[vip]" {
|
|
netConfig.Vip = true
|
|
} else if utils.IsInStringArray(p, compute.ALL_NETWORK_TYPES) {
|
|
netConfig.NetType = p
|
|
} else {
|
|
netConfig.Network = p
|
|
}
|
|
}
|
|
return netConfig, nil
|
|
}
|
|
|
|
func ParseIsolatedDevice(desc string, idx int) (*compute.IsolatedDeviceConfig, error) {
|
|
if len(desc) == 0 {
|
|
return nil, ErrorEmptyDesc
|
|
}
|
|
if idx < 0 {
|
|
return nil, fmt.Errorf("Invalid index: %d", idx)
|
|
}
|
|
dev := new(compute.IsolatedDeviceConfig)
|
|
parts := strings.Split(desc, ":")
|
|
for _, p := range parts {
|
|
if regutils.MatchUUIDExact(p) {
|
|
dev.Id = p
|
|
} else if utils.IsInStringArray(p, compute.VALID_PASSTHROUGH_TYPES) {
|
|
dev.DevType = p
|
|
} else if strings.HasPrefix(p, "vendor=") {
|
|
dev.Vendor = p[len("vendor="):]
|
|
} else {
|
|
dev.Model = p
|
|
}
|
|
}
|
|
return dev, nil
|
|
}
|
|
|
|
func ParseBaremetalDiskConfig(desc string) (*compute.BaremetalDiskConfig, error) {
|
|
bdc := new(compute.BaremetalDiskConfig)
|
|
bdc.Type = compute.DISK_TYPE_HYBRID
|
|
bdc.Conf = compute.DISK_CONF_NONE
|
|
bdc.Count = 0
|
|
desc = strings.ToLower(desc)
|
|
if len(desc) == 0 {
|
|
return bdc, nil
|
|
}
|
|
|
|
parts := strings.Split(desc, ":")
|
|
drvMap := make(map[string]string)
|
|
for _, drv := range compute.DISK_DRIVERS.List() {
|
|
drvMap[strings.ToLower(drv)] = drv
|
|
}
|
|
|
|
for _, p := range parts {
|
|
if len(p) == 0 {
|
|
continue
|
|
} else if utils.IsInStringArray(p, compute.DISK_TYPES) {
|
|
bdc.Type = p
|
|
} else if compute.DISK_CONFS.Has(p) {
|
|
bdc.Conf = p
|
|
} else if drv, ok := drvMap[p]; ok {
|
|
bdc.Driver = drv
|
|
} else if utils.IsMatchInteger(p) {
|
|
bdc.Count, _ = strconv.ParseInt(p, 0, 0)
|
|
} else if len(p) > 2 && p[0] == '[' && p[len(p)-1] == ']' {
|
|
rg, err1 := ParseRange(p[1:(len(p) - 1)])
|
|
if err1 != nil {
|
|
return nil, err1
|
|
}
|
|
bdc.Range = rg
|
|
} else if len(p) > 2 && p[0] == '(' && p[len(p)-1] == ')' {
|
|
bdc.Splits = p[1 : len(p)-1]
|
|
} else if utils.HasPrefix(p, "strip") {
|
|
strip := parseStrip(p[len("strip"):], "k")
|
|
bdc.Strip = &strip
|
|
} else if utils.HasPrefix(p, "adapter") {
|
|
ada, _ := strconv.ParseInt(p[len("adapter"):], 0, 64)
|
|
pada := int(ada)
|
|
bdc.Adapter = &pada
|
|
} else if p == "ra" {
|
|
hasRA := true
|
|
bdc.RA = &hasRA
|
|
} else if p == "nora" {
|
|
noRA := false
|
|
bdc.RA = &noRA
|
|
} else if p == "wt" {
|
|
wt := true
|
|
bdc.WT = &wt
|
|
} else if p == "wb" {
|
|
wt := false
|
|
bdc.WT = &wt
|
|
} else if p == "direct" {
|
|
direct := true
|
|
bdc.Direct = &direct
|
|
} else if p == "cached" {
|
|
direct := false
|
|
bdc.Direct = &direct
|
|
} else if p == "cachedbadbbu" {
|
|
cached := true
|
|
bdc.Cachedbadbbu = &cached
|
|
} else if p == "nocachedbadbbu" {
|
|
cached := false
|
|
bdc.Cachedbadbbu = &cached
|
|
} else {
|
|
return nil, fmt.Errorf("ParseDiskConfig unkown option %q", p)
|
|
}
|
|
}
|
|
|
|
return bdc, nil
|
|
}
|
|
|
|
func ParseRange(rangeStr string) (ret []int64, err error) {
|
|
rss := regexp.MustCompile(`[\s,]+`).Split(rangeStr, -1)
|
|
intSet := sets.NewInt64()
|
|
|
|
for _, rs := range rss {
|
|
r, err1 := _parseRange(rs)
|
|
if err1 != nil {
|
|
err = err1
|
|
return
|
|
}
|
|
intSet.Insert(r...)
|
|
}
|
|
ret = intSet.List()
|
|
return
|
|
}
|
|
|
|
// range string should be: "1-3", "3"
|
|
func _parseRange(str string) (ret []int64, err error) {
|
|
if len(str) == 0 {
|
|
return
|
|
}
|
|
|
|
// exclude "," symbol
|
|
if len(str) == 1 && !utils.IsMatchInteger(str) {
|
|
return
|
|
}
|
|
|
|
// add int string
|
|
if utils.IsMatchInteger(str) {
|
|
i, _ := strconv.ParseInt(str, 10, 64)
|
|
ret = append(ret, i)
|
|
return
|
|
}
|
|
|
|
// add rang like string, "2-10" etc.
|
|
ret, err = parseRangeStr(str)
|
|
return
|
|
}
|
|
|
|
// return KB
|
|
func parseStrip(stripStr string, defaultSize string) int64 {
|
|
size, _ := utils.GetSize(stripStr, defaultSize, 1024)
|
|
return size / 1024
|
|
}
|
|
|
|
func parseRangeStr(str string) (ret []int64, err error) {
|
|
im := utils.IsMatchInteger
|
|
errGen := func(e string) error {
|
|
return fmt.Errorf("Incorrect range str: %q", e)
|
|
}
|
|
rs := strings.Split(str, "-")
|
|
if len(rs) != 2 {
|
|
err = errGen(str)
|
|
return
|
|
}
|
|
|
|
bs, es := rs[0], rs[1]
|
|
if !im(bs) {
|
|
err = errGen(str)
|
|
return
|
|
}
|
|
if !im(es) {
|
|
err = errGen(str)
|
|
return
|
|
}
|
|
|
|
begin, _ := strconv.ParseInt(bs, 10, 64)
|
|
end, _ := strconv.ParseInt(es, 10, 64)
|
|
|
|
if begin > end {
|
|
begin, end = end, begin
|
|
}
|
|
|
|
for i := begin; i <= end; i++ {
|
|
ret = append(ret, i)
|
|
}
|
|
return
|
|
}
|