mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-09 07:02:06 +08:00
702 lines
20 KiB
Go
702 lines
20 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 (
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/pkg/errors"
|
|
"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"
|
|
"yunion.io/x/onecloud/pkg/httperrors"
|
|
)
|
|
|
|
var (
|
|
ErrorEmptyDesc = errors.Errorf("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 = ""
|
|
diskConfig.SizeMb = -1
|
|
|
|
oldPart, newPart := []string{}, []string{}
|
|
for _, d0 := range strings.Split(diskStr, ",") {
|
|
for _, d1 := range strings.Split(d0, ":") {
|
|
if len(d1) == 0 {
|
|
continue
|
|
}
|
|
if strings.Contains(d1, "=") {
|
|
newPart = append(newPart, d1)
|
|
continue
|
|
}
|
|
oldPart = append(oldPart, d1)
|
|
}
|
|
}
|
|
for _, p := range oldPart {
|
|
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 p == "autoreset" {
|
|
diskConfig.AutoReset = true
|
|
} else if utils.IsInStringArray(p, compute.STORAGE_TYPES) {
|
|
diskConfig.Backend = p
|
|
} else if len(p) > 0 {
|
|
diskConfig.ImageId = p
|
|
}
|
|
}
|
|
for _, p := range newPart {
|
|
info := strings.Split(p, "=")
|
|
if len(info) != 2 {
|
|
return nil, errors.Errorf("invalid disk description %s", p)
|
|
}
|
|
var err error
|
|
desc, str := info[0], info[1]
|
|
switch desc {
|
|
case "size":
|
|
diskConfig.SizeMb, err = fileutils.GetSizeMb(str, 'M', 1024)
|
|
if err != nil {
|
|
return nil, errors.Errorf("invalid disk size %s", str)
|
|
}
|
|
case "fs":
|
|
if !utils.IsInStringArray(str, osprofile.FS_TYPES) {
|
|
return nil, errors.Errorf("invalid disk fs %s, allow choices: %s", str, osprofile.FS_TYPES)
|
|
}
|
|
diskConfig.Fs = str
|
|
case "fs_features":
|
|
if diskConfig.Fs == "" {
|
|
return nil, errors.Errorf("disk fs is required")
|
|
}
|
|
diskConfig.FsFeatures = &compute.DiskFsFeatures{}
|
|
for _, feature := range strings.Split(str, ",") {
|
|
if diskConfig.Fs == "ext4" {
|
|
if diskConfig.FsFeatures.Ext4 == nil {
|
|
diskConfig.FsFeatures.Ext4 = &compute.DiskFsExt4Features{}
|
|
}
|
|
if feature == "casefold" {
|
|
diskConfig.FsFeatures.Ext4.CaseInsensitive = true
|
|
} else {
|
|
return nil, errors.Errorf("invalid feature %s of %s", feature, diskConfig.Fs)
|
|
}
|
|
}
|
|
if diskConfig.Fs == "f2fs" {
|
|
if diskConfig.FsFeatures.F2fs == nil {
|
|
diskConfig.FsFeatures.F2fs = &compute.DiskFsF2fsFeatures{}
|
|
}
|
|
if feature == "casefold" {
|
|
diskConfig.FsFeatures.F2fs.CaseInsensitive = true
|
|
} else {
|
|
return nil, errors.Errorf("invalid feature %s of %s", feature, diskConfig.Fs)
|
|
}
|
|
}
|
|
}
|
|
case "format":
|
|
if !utils.IsInStringArray(str, osprofile.IMAGE_FORMAT_TYPES) {
|
|
return nil, errors.Errorf("invalid disk format %s, allow choices: %s", str, osprofile.IMAGE_FORMAT_TYPES)
|
|
}
|
|
diskConfig.Format = str
|
|
case "driver":
|
|
if !utils.IsInStringArray(str, osprofile.DISK_DRIVERS) {
|
|
return nil, errors.Errorf("invalid disk driver %s, allow choices: %s", str, osprofile.DISK_DRIVERS)
|
|
}
|
|
diskConfig.Driver = str
|
|
case "cache", "cache_mode":
|
|
if !utils.IsInStringArray(str, osprofile.DISK_CACHE_MODES) {
|
|
return nil, errors.Errorf("invalid disk cache mode %s, allow choices: %s", str, osprofile.DISK_CACHE_MODES)
|
|
}
|
|
diskConfig.Cache = str
|
|
case "medium":
|
|
if !utils.IsInStringArray(str, compute.DISK_TYPES) {
|
|
return nil, errors.Errorf("invalid disk medium type %s, allow choices: %s", str, compute.DISK_TYPES)
|
|
}
|
|
diskConfig.Medium = str
|
|
case "type", "disk_type":
|
|
diskTypes := []string{compute.DISK_TYPE_SYS, compute.DISK_TYPE_DATA}
|
|
if !utils.IsInStringArray(str, diskTypes) {
|
|
return nil, errors.Errorf("invalid disk type %s, allow choices: %s", str, diskTypes)
|
|
}
|
|
diskConfig.DiskType = str
|
|
case "mountpoint":
|
|
diskConfig.Mountpoint = str
|
|
case "storage_type", "backend":
|
|
diskConfig.Backend = str
|
|
case "snapshot", "snapshot_id":
|
|
diskConfig.SnapshotId = str
|
|
case "disk", "disk_id":
|
|
diskConfig.DiskId = str
|
|
case "storage", "storage_id":
|
|
diskConfig.Storage = str
|
|
case "image", "image_id":
|
|
diskConfig.ImageId = str
|
|
case "existing_path":
|
|
diskConfig.ExistingPath = str
|
|
case "boot_index":
|
|
bootIndex, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "parse disk boot index %s", str)
|
|
}
|
|
bootIndex8 := int8(bootIndex)
|
|
diskConfig.BootIndex = &bootIndex8
|
|
case "nvme-device-id":
|
|
diskConfig.NVMEDevice = &compute.IsolatedDeviceConfig{
|
|
Id: str,
|
|
}
|
|
case "nvme-device-model":
|
|
diskConfig.NVMEDevice = &compute.IsolatedDeviceConfig{
|
|
Model: str,
|
|
}
|
|
case "iops":
|
|
diskConfig.Iops, _ = strconv.Atoi(str)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "parse disk iops %s", str)
|
|
}
|
|
case "throughput":
|
|
diskConfig.Throughput, _ = strconv.Atoi(str)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "parse disk iops %s", str)
|
|
}
|
|
case "preallocation":
|
|
if !utils.IsInStringArray(str, compute.DISK_PREALLOCATIONS) {
|
|
return nil, errors.Errorf("invalid preallocation %s, allow choices: %s", str, compute.DISK_PREALLOCATIONS)
|
|
}
|
|
diskConfig.Preallocation = str
|
|
default:
|
|
return nil, errors.Errorf("invalid disk description %s", 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 isQuoteChar(ch byte) (bool, string) {
|
|
switch ch {
|
|
case '[':
|
|
return true, "]"
|
|
default:
|
|
return false, ""
|
|
}
|
|
}
|
|
|
|
func splitConfig(confStr string) ([]string, error) {
|
|
return utils.FindWords2([]byte(confStr), 0, ":", isQuoteChar)
|
|
}
|
|
|
|
func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) {
|
|
if len(desc) == 0 {
|
|
return nil, ErrorEmptyDesc
|
|
}
|
|
parts, err := splitConfig(desc)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "splitConfig")
|
|
}
|
|
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) {
|
|
addr6, err := netutils.NewIPV6Addr(p)
|
|
if err != nil {
|
|
return nil, errors.Wrap(httperrors.ErrInvalidFormat, p)
|
|
}
|
|
netConfig.Address6 = addr6.String()
|
|
} else if regutils.MatchCompactMacAddr(p) {
|
|
netConfig.Mac = netutils.MacUnpackHex(p)
|
|
} else if strings.HasPrefix(p, "wire=") {
|
|
netConfig.Wire = p[len("wire="):]
|
|
} else if strings.HasPrefix(p, "macs=") {
|
|
macSegs := strings.Split(p[len("macs="):], ",")
|
|
macs := make([]string, len(macSegs))
|
|
for i := range macSegs {
|
|
macs[i] = netutils.MacUnpackHex(macSegs[i])
|
|
}
|
|
netConfig.Macs = macs
|
|
} else if strings.HasPrefix(p, "ips=") {
|
|
netConfig.Addresses = strings.Split(p[len("ips="):], ",")
|
|
for _, addr := range netConfig.Addresses {
|
|
_, err := netutils.NewIPV4Addr(addr)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, p)
|
|
}
|
|
}
|
|
} else if strings.HasPrefix(p, "ip6s=") {
|
|
netConfig.Addresses6 = strings.Split(p[len("ip6s="):], ",")
|
|
for i, addrStr := range netConfig.Addresses6 {
|
|
addr6, err := netutils.NewIPV6Addr(addrStr)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, p)
|
|
}
|
|
netConfig.Addresses6[i] = addr6.String()
|
|
}
|
|
} 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 p == "defaultgw" {
|
|
netConfig.IsDefault = true
|
|
} else if p == "ipv6" {
|
|
netConfig.RequireIPv6 = true
|
|
} else if p == "strict-ipv6" {
|
|
netConfig.RequireIPv6 = true
|
|
netConfig.StrictIPv6 = 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 strings.HasPrefix(p, "num-queues=") {
|
|
netConfig.NumQueues, _ = strconv.Atoi(p[len("num-queues="):])
|
|
} 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 strings.HasPrefix(p, "sriov-nic-id=") {
|
|
netConfig.SriovDevice = &compute.IsolatedDeviceConfig{
|
|
Id: p[len("sriov-nic-id="):],
|
|
}
|
|
} else if strings.HasPrefix(p, "sriov-nic-model=") {
|
|
netConfig.SriovDevice = &compute.IsolatedDeviceConfig{
|
|
Model: p[len("sriov-nic-model="):],
|
|
}
|
|
} else if strings.HasPrefix(p, "rx-traffic-limit=") {
|
|
var err error
|
|
netConfig.RxTrafficLimit, err = strconv.ParseInt(p[len("rx-traffic-limit="):], 10, 0)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "parse rx-traffic-limit")
|
|
}
|
|
} else if strings.HasPrefix(p, "tx-traffic-limit=") {
|
|
var err error
|
|
netConfig.TxTrafficLimit, err = strconv.ParseInt(p[len("tx-traffic-limit="):], 10, 0)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "parse tx-traffic-limit")
|
|
}
|
|
} else if compute.IsInNetworkTypes(compute.TNetworkType(p), compute.ALL_NETWORK_TYPES) {
|
|
netConfig.NetType = compute.TNetworkType(p)
|
|
} else {
|
|
netConfig.Network = p
|
|
}
|
|
}
|
|
return netConfig, nil
|
|
}
|
|
|
|
func ParseNetworkConfigPortMappings(descs []string) (map[int]compute.GuestPortMappings, error) {
|
|
if len(descs) == 0 {
|
|
return nil, ErrorEmptyDesc
|
|
}
|
|
pms := make(map[int]compute.GuestPortMappings, 0)
|
|
for _, desc := range descs {
|
|
idx, pm, err := parseNetworkConfigPortMapping(desc)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "parse port mapping: %s", desc)
|
|
}
|
|
mappings, ok := pms[idx]
|
|
if !ok {
|
|
mappings = make([]*compute.GuestPortMapping, 0)
|
|
}
|
|
mappings = append(mappings, pm)
|
|
pms[idx] = mappings
|
|
}
|
|
|
|
return pms, nil
|
|
}
|
|
|
|
func parseNetworkConfigPortMapping(desc string) (int, *compute.GuestPortMapping, error) {
|
|
pm := &compute.GuestPortMapping{
|
|
Protocol: compute.GuestPortMappingProtocolTCP,
|
|
}
|
|
idx := 0
|
|
for _, seg := range strings.Split(desc, ",") {
|
|
info := strings.Split(seg, "=")
|
|
if len(info) != 2 {
|
|
return -1, nil, errors.Errorf("invalid option %s", seg)
|
|
}
|
|
key := info[0]
|
|
val := info[1]
|
|
switch key {
|
|
case "index":
|
|
valIdx, err := strconv.Atoi(val)
|
|
if err != nil {
|
|
return -1, nil, errors.Wrapf(err, "invalid index %s", val)
|
|
}
|
|
idx = valIdx
|
|
case "host_port":
|
|
hp, err := strconv.Atoi(val)
|
|
if err != nil {
|
|
return -1, nil, errors.Wrapf(err, "invalid host_port %s", val)
|
|
}
|
|
pm.HostPort = &hp
|
|
case "container_port", "port":
|
|
cp, err := strconv.Atoi(val)
|
|
if err != nil {
|
|
return -1, nil, errors.Wrapf(err, "invalid container_port %s", val)
|
|
}
|
|
pm.Port = cp
|
|
case "proto", "protocol":
|
|
pm.Protocol = compute.GuestPortMappingProtocol(val)
|
|
case "host_port_range":
|
|
rangeParts := strings.Split(val, "-")
|
|
if len(rangeParts) != 2 {
|
|
return -1, nil, errors.Errorf("invalid range string %s", val)
|
|
}
|
|
start, err := strconv.Atoi(rangeParts[0])
|
|
if err != nil {
|
|
return -1, nil, errors.Wrapf(err, "invalid host_port_range %s", rangeParts[0])
|
|
}
|
|
end, err := strconv.Atoi(rangeParts[1])
|
|
if err != nil {
|
|
return -1, nil, errors.Wrapf(err, "invalid host_port_range %s", rangeParts[1])
|
|
}
|
|
pm.HostPortRange = &compute.GuestPortMappingPortRange{
|
|
Start: start,
|
|
End: end,
|
|
}
|
|
case "remote_ips", "remote_ip":
|
|
ips := strings.Split(val, "|")
|
|
pm.RemoteIps = ips
|
|
}
|
|
}
|
|
if pm.Port == 0 {
|
|
return -1, nil, errors.Error("container_port must specified")
|
|
}
|
|
if idx < 0 {
|
|
return -1, nil, errors.Errorf("invalid index %d", idx)
|
|
}
|
|
return idx, pm, 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, ":")
|
|
devTypes := sets.NewString(compute.VALID_PASSTHROUGH_TYPES...)
|
|
devTypes.Insert(compute.VALID_CONTAINER_DEVICE_TYPES...)
|
|
for _, p := range parts {
|
|
if regutils.MatchUUIDExact(p) {
|
|
dev.Id = p
|
|
} else if devTypes.Has(p) {
|
|
dev.DevType = p
|
|
} else if strings.HasPrefix(p, "vendor=") {
|
|
dev.Vendor = p[len("vendor="):]
|
|
} else if strings.HasPrefix(p, "device_path=") {
|
|
dev.DevicePath = p[len("device_path="):]
|
|
} else {
|
|
dev.Model = p
|
|
}
|
|
}
|
|
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
|
|
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
|
|
}
|