parser.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package cmdline
  15. import (
  16. "fmt"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. "yunion.io/x/jsonutils"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/fileutils"
  23. "yunion.io/x/pkg/util/netutils"
  24. "yunion.io/x/pkg/util/osprofile"
  25. "yunion.io/x/pkg/util/regutils"
  26. "yunion.io/x/pkg/util/sets"
  27. "yunion.io/x/pkg/utils"
  28. billing_api "yunion.io/x/onecloud/pkg/apis/billing"
  29. "yunion.io/x/onecloud/pkg/apis/compute"
  30. "yunion.io/x/onecloud/pkg/httperrors"
  31. )
  32. var (
  33. ErrorEmptyDesc = errors.Errorf("Empty description")
  34. )
  35. // ParseSchedtagConfig desc format: <schedtagName>:<strategy>:<resource_type>
  36. func ParseSchedtagConfig(desc string) (*compute.SchedtagConfig, error) {
  37. if len(desc) == 0 {
  38. return nil, ErrorEmptyDesc
  39. }
  40. parts := strings.Split(desc, ":")
  41. if len(parts) < 2 {
  42. return nil, fmt.Errorf("Invalid desc: %s", desc)
  43. }
  44. strategy := parts[1]
  45. if !utils.IsInStringArray(strategy, compute.STRATEGY_LIST) {
  46. return nil, fmt.Errorf("Invalid strategy: %s", strategy)
  47. }
  48. conf := &compute.SchedtagConfig{
  49. Id: parts[0],
  50. Strategy: parts[1],
  51. }
  52. if len(parts) == 3 {
  53. conf.ResourceType = parts[2]
  54. }
  55. return conf, nil
  56. }
  57. // ParseResourceSchedtagConfig desc format: <idx>:<schedtagName>:<strategy>
  58. func ParseResourceSchedtagConfig(desc string) (int, *compute.SchedtagConfig, error) {
  59. if len(desc) == 0 {
  60. return 0, nil, ErrorEmptyDesc
  61. }
  62. parts := strings.Split(desc, ":")
  63. if len(parts) != 3 {
  64. return 0, nil, fmt.Errorf("Invalid desc: %s", desc)
  65. }
  66. idx, err := strconv.Atoi(parts[0])
  67. if err != nil {
  68. return 0, nil, err
  69. }
  70. tag, err := ParseSchedtagConfig(fmt.Sprintf("%s:%s", parts[1], parts[2]))
  71. if err != nil {
  72. return 0, nil, err
  73. }
  74. return idx, tag, nil
  75. }
  76. func ParseDiskConfig(diskStr string, idx int) (*compute.DiskConfig, error) {
  77. if len(diskStr) == 0 {
  78. return nil, ErrorEmptyDesc
  79. }
  80. diskConfig := new(compute.DiskConfig)
  81. diskConfig.Index = idx
  82. // default backend and medium type
  83. diskConfig.Backend = "" // STORAGE_LOCAL
  84. diskConfig.Medium = ""
  85. diskConfig.SizeMb = -1
  86. oldPart, newPart := []string{}, []string{}
  87. for _, d0 := range strings.Split(diskStr, ",") {
  88. for _, d1 := range strings.Split(d0, ":") {
  89. if len(d1) == 0 {
  90. continue
  91. }
  92. if strings.Contains(d1, "=") {
  93. newPart = append(newPart, d1)
  94. continue
  95. }
  96. oldPart = append(oldPart, d1)
  97. }
  98. }
  99. for _, p := range oldPart {
  100. if regutils.MatchSize(p) {
  101. diskConfig.SizeMb, _ = fileutils.GetSizeMb(p, 'M', 1024)
  102. } else if utils.IsInStringArray(p, osprofile.FS_TYPES) {
  103. diskConfig.Fs = p
  104. } else if utils.IsInStringArray(p, osprofile.IMAGE_FORMAT_TYPES) {
  105. diskConfig.Format = p
  106. } else if utils.IsInStringArray(p, osprofile.DISK_DRIVERS) {
  107. diskConfig.Driver = p
  108. } else if utils.IsInStringArray(p, osprofile.DISK_CACHE_MODES) {
  109. diskConfig.Cache = p
  110. } else if utils.IsInStringArray(p, compute.DISK_TYPES) {
  111. diskConfig.Medium = p
  112. } else if utils.IsInStringArray(p, []string{compute.DISK_TYPE_VOLUME}) {
  113. diskConfig.DiskType = p
  114. } else if p[0] == '/' {
  115. diskConfig.Mountpoint = p
  116. } else if p == "autoextend" {
  117. diskConfig.SizeMb = -1
  118. } else if p == "autoreset" {
  119. diskConfig.AutoReset = true
  120. } else if utils.IsInStringArray(p, compute.STORAGE_TYPES) {
  121. diskConfig.Backend = p
  122. } else if len(p) > 0 {
  123. diskConfig.ImageId = p
  124. }
  125. }
  126. for _, p := range newPart {
  127. info := strings.Split(p, "=")
  128. if len(info) != 2 {
  129. return nil, errors.Errorf("invalid disk description %s", p)
  130. }
  131. var err error
  132. desc, str := info[0], info[1]
  133. switch desc {
  134. case "size":
  135. diskConfig.SizeMb, err = fileutils.GetSizeMb(str, 'M', 1024)
  136. if err != nil {
  137. return nil, errors.Errorf("invalid disk size %s", str)
  138. }
  139. case "fs":
  140. if !utils.IsInStringArray(str, osprofile.FS_TYPES) {
  141. return nil, errors.Errorf("invalid disk fs %s, allow choices: %s", str, osprofile.FS_TYPES)
  142. }
  143. diskConfig.Fs = str
  144. case "fs_features":
  145. if diskConfig.Fs == "" {
  146. return nil, errors.Errorf("disk fs is required")
  147. }
  148. diskConfig.FsFeatures = &compute.DiskFsFeatures{}
  149. for _, feature := range strings.Split(str, ",") {
  150. if diskConfig.Fs == "ext4" {
  151. if diskConfig.FsFeatures.Ext4 == nil {
  152. diskConfig.FsFeatures.Ext4 = &compute.DiskFsExt4Features{}
  153. }
  154. if feature == "casefold" {
  155. diskConfig.FsFeatures.Ext4.CaseInsensitive = true
  156. } else {
  157. return nil, errors.Errorf("invalid feature %s of %s", feature, diskConfig.Fs)
  158. }
  159. }
  160. if diskConfig.Fs == "f2fs" {
  161. if diskConfig.FsFeatures.F2fs == nil {
  162. diskConfig.FsFeatures.F2fs = &compute.DiskFsF2fsFeatures{}
  163. }
  164. if feature == "casefold" {
  165. diskConfig.FsFeatures.F2fs.CaseInsensitive = true
  166. } else {
  167. return nil, errors.Errorf("invalid feature %s of %s", feature, diskConfig.Fs)
  168. }
  169. }
  170. }
  171. case "format":
  172. if !utils.IsInStringArray(str, osprofile.IMAGE_FORMAT_TYPES) {
  173. return nil, errors.Errorf("invalid disk format %s, allow choices: %s", str, osprofile.IMAGE_FORMAT_TYPES)
  174. }
  175. diskConfig.Format = str
  176. case "driver":
  177. if !utils.IsInStringArray(str, osprofile.DISK_DRIVERS) {
  178. return nil, errors.Errorf("invalid disk driver %s, allow choices: %s", str, osprofile.DISK_DRIVERS)
  179. }
  180. diskConfig.Driver = str
  181. case "cache", "cache_mode":
  182. if !utils.IsInStringArray(str, osprofile.DISK_CACHE_MODES) {
  183. return nil, errors.Errorf("invalid disk cache mode %s, allow choices: %s", str, osprofile.DISK_CACHE_MODES)
  184. }
  185. diskConfig.Cache = str
  186. case "medium":
  187. if !utils.IsInStringArray(str, compute.DISK_TYPES) {
  188. return nil, errors.Errorf("invalid disk medium type %s, allow choices: %s", str, compute.DISK_TYPES)
  189. }
  190. diskConfig.Medium = str
  191. case "type", "disk_type":
  192. diskTypes := []string{compute.DISK_TYPE_SYS, compute.DISK_TYPE_DATA}
  193. if !utils.IsInStringArray(str, diskTypes) {
  194. return nil, errors.Errorf("invalid disk type %s, allow choices: %s", str, diskTypes)
  195. }
  196. diskConfig.DiskType = str
  197. case "mountpoint":
  198. diskConfig.Mountpoint = str
  199. case "storage_type", "backend":
  200. diskConfig.Backend = str
  201. case "snapshot", "snapshot_id":
  202. diskConfig.SnapshotId = str
  203. case "disk", "disk_id":
  204. diskConfig.DiskId = str
  205. case "storage", "storage_id":
  206. diskConfig.Storage = str
  207. case "image", "image_id":
  208. diskConfig.ImageId = str
  209. case "existing_path":
  210. diskConfig.ExistingPath = str
  211. case "boot_index":
  212. bootIndex, err := strconv.Atoi(str)
  213. if err != nil {
  214. return nil, errors.Wrapf(err, "parse disk boot index %s", str)
  215. }
  216. bootIndex8 := int8(bootIndex)
  217. diskConfig.BootIndex = &bootIndex8
  218. case "nvme-device-id":
  219. diskConfig.NVMEDevice = &compute.IsolatedDeviceConfig{
  220. Id: str,
  221. }
  222. case "nvme-device-model":
  223. diskConfig.NVMEDevice = &compute.IsolatedDeviceConfig{
  224. Model: str,
  225. }
  226. case "iops":
  227. diskConfig.Iops, _ = strconv.Atoi(str)
  228. if err != nil {
  229. return nil, errors.Wrapf(err, "parse disk iops %s", str)
  230. }
  231. case "throughput":
  232. diskConfig.Throughput, _ = strconv.Atoi(str)
  233. if err != nil {
  234. return nil, errors.Wrapf(err, "parse disk iops %s", str)
  235. }
  236. case "preallocation":
  237. if !utils.IsInStringArray(str, compute.DISK_PREALLOCATIONS) {
  238. return nil, errors.Errorf("invalid preallocation %s, allow choices: %s", str, compute.DISK_PREALLOCATIONS)
  239. }
  240. diskConfig.Preallocation = str
  241. case "auto_delete":
  242. v, err := strconv.ParseBool(str)
  243. if err != nil {
  244. return nil, errors.Wrapf(err, "parse disk auto_delete %s", str)
  245. }
  246. diskConfig.AutoDelete = &v
  247. default:
  248. return nil, errors.Errorf("invalid disk description %s", p)
  249. }
  250. }
  251. return diskConfig, nil
  252. }
  253. func ParseNetworkConfigByJSON(desc jsonutils.JSONObject, idx int) (*compute.NetworkConfig, error) {
  254. if _, ok := desc.(*jsonutils.JSONString); ok {
  255. descStr, _ := desc.GetString()
  256. return ParseNetworkConfig(descStr, idx)
  257. }
  258. conf := new(compute.NetworkConfig)
  259. conf.Index = idx
  260. err := desc.Unmarshal(conf)
  261. return conf, err
  262. }
  263. func isQuoteChar(ch byte) (bool, string) {
  264. switch ch {
  265. case '[':
  266. return true, "]"
  267. default:
  268. return false, ""
  269. }
  270. }
  271. func splitConfig(confStr string) ([]string, error) {
  272. return utils.FindWords2([]byte(confStr), 0, ":", isQuoteChar)
  273. }
  274. func ParseNetworkConfig(desc string, idx int) (*compute.NetworkConfig, error) {
  275. if len(desc) == 0 {
  276. return nil, ErrorEmptyDesc
  277. }
  278. parts, err := splitConfig(desc)
  279. if err != nil {
  280. return nil, errors.Wrap(err, "splitConfig")
  281. }
  282. netConfig := new(compute.NetworkConfig)
  283. netConfig.Index = idx
  284. for _, p := range parts {
  285. if len(p) == 0 {
  286. continue
  287. }
  288. if regutils.MatchIP4Addr(p) {
  289. netConfig.Address = p
  290. } else if regutils.MatchIP6Addr(p) {
  291. addr6, err := netutils.NewIPV6Addr(p)
  292. if err != nil {
  293. return nil, errors.Wrap(httperrors.ErrInvalidFormat, p)
  294. }
  295. netConfig.Address6 = addr6.String()
  296. } else if regutils.MatchCompactMacAddr(p) {
  297. netConfig.Mac = netutils.MacUnpackHex(p)
  298. } else if strings.HasPrefix(p, "wire=") {
  299. netConfig.Wire = p[len("wire="):]
  300. } else if strings.HasPrefix(p, "macs=") {
  301. macSegs := strings.Split(p[len("macs="):], ",")
  302. macs := make([]string, len(macSegs))
  303. for i := range macSegs {
  304. macs[i] = netutils.MacUnpackHex(macSegs[i])
  305. }
  306. netConfig.Macs = macs
  307. } else if strings.HasPrefix(p, "ips=") {
  308. netConfig.Addresses = strings.Split(p[len("ips="):], ",")
  309. for _, addr := range netConfig.Addresses {
  310. _, err := netutils.NewIPV4Addr(addr)
  311. if err != nil {
  312. return nil, errors.Wrap(err, p)
  313. }
  314. }
  315. } else if strings.HasPrefix(p, "ip6s=") {
  316. netConfig.Addresses6 = strings.Split(p[len("ip6s="):], ",")
  317. for i, addrStr := range netConfig.Addresses6 {
  318. addr6, err := netutils.NewIPV6Addr(addrStr)
  319. if err != nil {
  320. return nil, errors.Wrap(err, p)
  321. }
  322. netConfig.Addresses6[i] = addr6.String()
  323. }
  324. } else if strings.HasPrefix(p, "secgroups=") {
  325. netConfig.Secgroups = strings.Split(p[len("secgroups="):], ",")
  326. } else if p == "require_designated_ip" {
  327. netConfig.RequireDesignatedIP = true
  328. } else if p == "random_exit" {
  329. netConfig.Exit = true
  330. } else if p == "random" {
  331. netConfig.Exit = false
  332. } else if p == "private" {
  333. netConfig.Private = true
  334. } else if p == "reserved" {
  335. netConfig.Reserved = true
  336. } else if p == "teaming" {
  337. netConfig.RequireTeaming = true
  338. } else if p == "try-teaming" {
  339. netConfig.TryTeaming = true
  340. } else if p == "defaultgw" {
  341. netConfig.IsDefault = true
  342. } else if p == "ipv6" {
  343. netConfig.RequireIPv6 = true
  344. } else if p == "strict-ipv6" {
  345. netConfig.RequireIPv6 = true
  346. netConfig.StrictIPv6 = true
  347. } else if strings.HasPrefix(p, "standby-port=") {
  348. netConfig.StandbyPortCount, _ = strconv.Atoi(p[len("standby-port="):])
  349. } else if strings.HasPrefix(p, "standby-addr=") {
  350. netConfig.StandbyAddrCount, _ = strconv.Atoi(p[len("standby-addr="):])
  351. } else if utils.IsInStringArray(p, []string{"virtio", "e1000", "vmxnet3"}) {
  352. netConfig.Driver = p
  353. } else if strings.HasPrefix(p, "num-queues=") {
  354. netConfig.NumQueues, _ = strconv.Atoi(p[len("num-queues="):])
  355. } else if strings.HasPrefix(p, "billing-type=") {
  356. netConfig.BillingType = billing_api.ParseBillingType(p[len("billing-type="):])
  357. } else if strings.HasPrefix(p, "charge-type=") {
  358. netConfig.ChargeType = billing_api.ParseNetChargeType(p[len("charge-type="):])
  359. } else if regutils.MatchSize(p) {
  360. bw, err := fileutils.GetSizeMb(p, 'M', 1000)
  361. if err != nil {
  362. return nil, err
  363. }
  364. netConfig.BwLimit = bw
  365. } else if p == "vip" {
  366. netConfig.Vip = true
  367. } else if strings.HasPrefix(p, "sriov-nic-id=") {
  368. netConfig.SriovDevice = &compute.IsolatedDeviceConfig{
  369. Id: p[len("sriov-nic-id="):],
  370. }
  371. } else if strings.HasPrefix(p, "sriov-nic-model=") {
  372. netConfig.SriovDevice = &compute.IsolatedDeviceConfig{
  373. Model: p[len("sriov-nic-model="):],
  374. }
  375. } else if strings.HasPrefix(p, "rx-traffic-limit=") {
  376. var err error
  377. netConfig.RxTrafficLimit, err = strconv.ParseInt(p[len("rx-traffic-limit="):], 10, 0)
  378. if err != nil {
  379. return nil, errors.Wrap(err, "parse rx-traffic-limit")
  380. }
  381. } else if strings.HasPrefix(p, "tx-traffic-limit=") {
  382. var err error
  383. netConfig.TxTrafficLimit, err = strconv.ParseInt(p[len("tx-traffic-limit="):], 10, 0)
  384. if err != nil {
  385. return nil, errors.Wrap(err, "parse tx-traffic-limit")
  386. }
  387. } else if compute.IsInNetworkTypes(compute.TNetworkType(p), compute.ALL_NETWORK_TYPES) {
  388. netConfig.NetType = compute.TNetworkType(p)
  389. } else {
  390. netConfig.Network = p
  391. }
  392. }
  393. return netConfig, nil
  394. }
  395. func ParseNetworkConfigPortMappings(descs []string) (map[int]compute.GuestPortMappings, error) {
  396. if len(descs) == 0 {
  397. return nil, ErrorEmptyDesc
  398. }
  399. pms := make(map[int]compute.GuestPortMappings, 0)
  400. for _, desc := range descs {
  401. idx, pm, err := parseNetworkConfigPortMapping(desc)
  402. if err != nil {
  403. return nil, errors.Wrapf(err, "parse port mapping: %s", desc)
  404. }
  405. mappings, ok := pms[idx]
  406. if !ok {
  407. mappings = make([]*compute.GuestPortMapping, 0)
  408. }
  409. mappings = append(mappings, pm)
  410. pms[idx] = mappings
  411. }
  412. return pms, nil
  413. }
  414. func parseNetworkConfigPortMapping(desc string) (int, *compute.GuestPortMapping, error) {
  415. pm := &compute.GuestPortMapping{
  416. Protocol: compute.GuestPortMappingProtocolTCP,
  417. }
  418. idx := 0
  419. for _, seg := range strings.Split(desc, ",") {
  420. info := strings.Split(seg, "=")
  421. if len(info) != 2 {
  422. return -1, nil, errors.Errorf("invalid option %s", seg)
  423. }
  424. key := info[0]
  425. val := info[1]
  426. switch key {
  427. case "index":
  428. valIdx, err := strconv.Atoi(val)
  429. if err != nil {
  430. return -1, nil, errors.Wrapf(err, "invalid index %s", val)
  431. }
  432. idx = valIdx
  433. case "host_port":
  434. hp, err := strconv.Atoi(val)
  435. if err != nil {
  436. return -1, nil, errors.Wrapf(err, "invalid host_port %s", val)
  437. }
  438. pm.HostPort = &hp
  439. case "container_port", "port":
  440. cp, err := strconv.Atoi(val)
  441. if err != nil {
  442. return -1, nil, errors.Wrapf(err, "invalid container_port %s", val)
  443. }
  444. pm.Port = cp
  445. case "proto", "protocol":
  446. pm.Protocol = compute.GuestPortMappingProtocol(val)
  447. case "host_port_range":
  448. rangeParts := strings.Split(val, "-")
  449. if len(rangeParts) != 2 {
  450. return -1, nil, errors.Errorf("invalid range string %s", val)
  451. }
  452. start, err := strconv.Atoi(rangeParts[0])
  453. if err != nil {
  454. return -1, nil, errors.Wrapf(err, "invalid host_port_range %s", rangeParts[0])
  455. }
  456. end, err := strconv.Atoi(rangeParts[1])
  457. if err != nil {
  458. return -1, nil, errors.Wrapf(err, "invalid host_port_range %s", rangeParts[1])
  459. }
  460. pm.HostPortRange = &compute.GuestPortMappingPortRange{
  461. Start: start,
  462. End: end,
  463. }
  464. case "remote_ips", "remote_ip":
  465. ips := strings.Split(val, "|")
  466. pm.RemoteIps = ips
  467. }
  468. }
  469. if pm.Port == 0 {
  470. return -1, nil, errors.Error("container_port must specified")
  471. }
  472. if idx < 0 {
  473. return -1, nil, errors.Errorf("invalid index %d", idx)
  474. }
  475. return idx, pm, nil
  476. }
  477. func ParseIsolatedDevice(desc string, idx int) (*compute.IsolatedDeviceConfig, error) {
  478. if len(desc) == 0 {
  479. return nil, ErrorEmptyDesc
  480. }
  481. if idx < 0 {
  482. return nil, fmt.Errorf("Invalid index: %d", idx)
  483. }
  484. dev := new(compute.IsolatedDeviceConfig)
  485. parts := strings.Split(desc, ":")
  486. devTypes := sets.NewString(compute.VALID_PASSTHROUGH_TYPES...)
  487. devTypes.Insert(compute.VALID_CONTAINER_DEVICE_TYPES...)
  488. for _, p := range parts {
  489. if regutils.MatchUUIDExact(p) {
  490. dev.Id = p
  491. } else if devTypes.Has(p) {
  492. dev.DevType = p
  493. } else if strings.HasPrefix(p, "vendor=") {
  494. dev.Vendor = p[len("vendor="):]
  495. } else if strings.HasPrefix(p, "device_path=") {
  496. dev.DevicePath = p[len("device_path="):]
  497. } else {
  498. dev.Model = p
  499. }
  500. }
  501. return dev, nil
  502. }
  503. func ParseBaremetalRootDiskMatcher(line string) (*compute.BaremetalRootDiskMatcher, error) {
  504. ret := new(compute.BaremetalRootDiskMatcher)
  505. for _, seg := range strings.Split(line, ",") {
  506. info := strings.Split(seg, "=")
  507. if len(info) != 2 {
  508. return nil, errors.Errorf("invalid option %s", seg)
  509. }
  510. key := info[0]
  511. val := info[1]
  512. switch key {
  513. case "size":
  514. sizeMB, err := fileutils.GetSizeMb(val, 'M', 1024)
  515. if err != nil {
  516. return nil, errors.Wrapf(err, "parse size %s", val)
  517. }
  518. ret.SizeMB = int64(sizeMB)
  519. case "device", "dev":
  520. ret.Device = val
  521. case "size_start":
  522. sizeMB, err := fileutils.GetSizeMb(val, 'M', 1024)
  523. if err != nil {
  524. return nil, errors.Wrapf(err, "parse size_start %s", val)
  525. }
  526. if ret.SizeMBRange == nil {
  527. ret.SizeMBRange = new(compute.RootDiskMatcherSizeMBRange)
  528. }
  529. ret.SizeMBRange.Start = int64(sizeMB)
  530. case "size_end":
  531. sizeMB, err := fileutils.GetSizeMb(val, 'M', 1024)
  532. if err != nil {
  533. return nil, errors.Wrapf(err, "parse size_end %s", val)
  534. }
  535. if ret.SizeMBRange == nil {
  536. ret.SizeMBRange = new(compute.RootDiskMatcherSizeMBRange)
  537. }
  538. ret.SizeMBRange.End = int64(sizeMB)
  539. }
  540. }
  541. return ret, nil
  542. }
  543. func ParseBaremetalDiskConfig(desc string) (*compute.BaremetalDiskConfig, error) {
  544. bdc := new(compute.BaremetalDiskConfig)
  545. bdc.Type = compute.DISK_TYPE_HYBRID
  546. bdc.Conf = compute.DISK_CONF_NONE
  547. bdc.Count = 0
  548. desc = strings.ToLower(desc)
  549. if len(desc) == 0 {
  550. return bdc, nil
  551. }
  552. parts := strings.Split(desc, ":")
  553. drvMap := make(map[string]string)
  554. for _, drv := range compute.DISK_DRIVERS.List() {
  555. drvMap[strings.ToLower(drv)] = drv
  556. }
  557. for _, p := range parts {
  558. if len(p) == 0 {
  559. continue
  560. } else if utils.IsInStringArray(p, compute.DISK_TYPES) {
  561. bdc.Type = p
  562. } else if compute.DISK_CONFS.Has(p) {
  563. bdc.Conf = p
  564. } else if drv, ok := drvMap[p]; ok {
  565. bdc.Driver = drv
  566. } else if utils.IsMatchInteger(p) {
  567. bdc.Count, _ = strconv.ParseInt(p, 0, 0)
  568. } else if len(p) > 2 && p[0] == '[' && p[len(p)-1] == ']' {
  569. rg, err1 := ParseRange(p[1:(len(p) - 1)])
  570. if err1 != nil {
  571. return nil, err1
  572. }
  573. bdc.Range = rg
  574. } else if len(p) > 2 && p[0] == '(' && p[len(p)-1] == ')' {
  575. bdc.Splits = p[1 : len(p)-1]
  576. } else if utils.HasPrefix(p, "strip") {
  577. strip := parseStrip(p[len("strip"):], "k")
  578. bdc.Strip = &strip
  579. } else if utils.HasPrefix(p, "adapter") {
  580. ada, _ := strconv.ParseInt(p[len("adapter"):], 0, 64)
  581. pada := int(ada)
  582. bdc.Adapter = &pada
  583. } else if p == "ra" {
  584. hasRA := true
  585. bdc.RA = &hasRA
  586. } else if p == "nora" {
  587. noRA := false
  588. bdc.RA = &noRA
  589. } else if p == "wt" {
  590. wt := true
  591. bdc.WT = &wt
  592. } else if p == "wb" {
  593. wt := false
  594. bdc.WT = &wt
  595. } else if p == "direct" {
  596. direct := true
  597. bdc.Direct = &direct
  598. } else if p == "cached" {
  599. direct := false
  600. bdc.Direct = &direct
  601. } else if p == "cachedbadbbu" {
  602. cached := true
  603. bdc.Cachedbadbbu = &cached
  604. } else if p == "nocachedbadbbu" {
  605. cached := false
  606. bdc.Cachedbadbbu = &cached
  607. } else {
  608. return nil, fmt.Errorf("ParseDiskConfig unkown option %q", p)
  609. }
  610. }
  611. return bdc, nil
  612. }
  613. func ParseRange(rangeStr string) (ret []int64, err error) {
  614. rss := regexp.MustCompile(`[\s,]+`).Split(rangeStr, -1)
  615. intSet := sets.NewInt64()
  616. for _, rs := range rss {
  617. r, err1 := _parseRange(rs)
  618. if err1 != nil {
  619. err = err1
  620. return
  621. }
  622. intSet.Insert(r...)
  623. }
  624. ret = intSet.List()
  625. return
  626. }
  627. // range string should be: "1-3", "3"
  628. func _parseRange(str string) (ret []int64, err error) {
  629. if len(str) == 0 {
  630. return
  631. }
  632. // exclude "," symbol
  633. if len(str) == 1 && !utils.IsMatchInteger(str) {
  634. return
  635. }
  636. // add int string
  637. if utils.IsMatchInteger(str) {
  638. i, _ := strconv.ParseInt(str, 10, 64)
  639. ret = append(ret, i)
  640. return
  641. }
  642. // add rang like string, "2-10" etc.
  643. ret, err = parseRangeStr(str)
  644. return
  645. }
  646. // return KB
  647. func parseStrip(stripStr string, defaultSize string) int64 {
  648. size, _ := utils.GetSize(stripStr, defaultSize, 1024)
  649. return size / 1024
  650. }
  651. func parseRangeStr(str string) (ret []int64, err error) {
  652. im := utils.IsMatchInteger
  653. errGen := func(e string) error {
  654. return fmt.Errorf("Incorrect range str: %q", e)
  655. }
  656. rs := strings.Split(str, "-")
  657. if len(rs) != 2 {
  658. err = errGen(str)
  659. return
  660. }
  661. bs, es := rs[0], rs[1]
  662. if !im(bs) {
  663. err = errGen(str)
  664. return
  665. }
  666. if !im(es) {
  667. err = errGen(str)
  668. return
  669. }
  670. begin, _ := strconv.ParseInt(bs, 10, 64)
  671. end, _ := strconv.ParseInt(es, 10, 64)
  672. if begin > end {
  673. begin, end = end, begin
  674. }
  675. for i := begin; i <= end; i++ {
  676. ret = append(ret, i)
  677. }
  678. return
  679. }