diskconfig.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  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 baremetal
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "reflect"
  19. "strconv"
  20. "strings"
  21. "yunion.io/x/jsonutils"
  22. "yunion.io/x/log"
  23. "yunion.io/x/pkg/errors"
  24. "yunion.io/x/pkg/util/sets"
  25. "yunion.io/x/pkg/utils"
  26. api "yunion.io/x/onecloud/pkg/apis/compute"
  27. "yunion.io/x/onecloud/pkg/cloudcommon/cmdline"
  28. )
  29. func ParseDiskConfig(desc string) (api.BaremetalDiskConfig, error) {
  30. conf, err := cmdline.ParseBaremetalDiskConfig(desc)
  31. if err != nil {
  32. return api.BaremetalDiskConfig{}, err
  33. }
  34. return *conf, nil
  35. }
  36. func isDiskConfigStorageMatch(
  37. config *api.BaremetalDiskConfig,
  38. confDriver *string,
  39. confAdapter *int,
  40. storage *BaremetalStorage,
  41. selected []*BaremetalStorage,
  42. ) (bool, error) {
  43. isRotate := storage.Rotate
  44. adapter := storage.Adapter
  45. index := storage.Index
  46. driver := storage.Driver
  47. typeIsHybrid := config.Type == DISK_TYPE_HYBRID
  48. typeIsRotate := config.Type == DISK_TYPE_ROTATE && isRotate
  49. typeIsSSD := config.Type == DISK_TYPE_SSD && !isRotate
  50. rangeIsNoneAndCountZero := len(config.Range) == 0 && config.Count == 0
  51. rangeIsNotNoneAndIndexInRange := len(config.Range) != 0 && sets.NewInt64(config.Range...).Has(index)
  52. rangeIsNoneAndSmallThanCount := len(config.Range) == 0 && int64(len(selected)) < config.Count
  53. adapterIsEqual := (confAdapter == nil || *confAdapter == adapter) &&
  54. (confDriver == nil || *confDriver == driver)
  55. if (typeIsHybrid || typeIsRotate || typeIsSSD) &&
  56. (rangeIsNoneAndCountZero || rangeIsNotNoneAndIndexInRange || rangeIsNoneAndSmallThanCount) &&
  57. adapterIsEqual {
  58. return true, nil
  59. }
  60. errs := []error{}
  61. // aggregate errors
  62. if !(typeIsHybrid || typeIsRotate || typeIsSSD) {
  63. errs = append(errs, fmt.Errorf("check type: is_hybrid: %v, is_rotate: %v, is_ssd: %v, type: %s", typeIsHybrid, typeIsRotate, typeIsSSD, config.Type))
  64. }
  65. if !(rangeIsNoneAndCountZero || rangeIsNotNoneAndIndexInRange || rangeIsNoneAndSmallThanCount) {
  66. errs = append(errs, fmt.Errorf("check range: is_none: %v, index_in_range: %v, small_than_count: %v, index: %d, range: %v", rangeIsNoneAndCountZero, rangeIsNotNoneAndIndexInRange, rangeIsNoneAndSmallThanCount, index, config.Range))
  67. }
  68. if adapterIsEqual {
  69. errs = append(errs, fmt.Errorf("check adapter: is_equal: %v", adapterIsEqual))
  70. }
  71. return false, errors.NewAggregate(errs)
  72. }
  73. func RetrieveStorages(diskConfig *api.BaremetalDiskConfig, storages []*BaremetalStorage) (selected, rest []*BaremetalStorage, err error) {
  74. var confDriver *string = nil
  75. var confAdapter *int = nil
  76. if diskConfig.Adapter != nil {
  77. confAdapter = diskConfig.Adapter
  78. }
  79. if diskConfig.Driver != "" {
  80. confDriver = &diskConfig.Driver
  81. }
  82. selected = make([]*BaremetalStorage, 0)
  83. rest = make([]*BaremetalStorage, 0)
  84. errs := []error{}
  85. idx := 0
  86. curAdapter := 0
  87. adapterChange := false
  88. curDriver := ""
  89. driverChange := false
  90. for _, storage := range storages {
  91. if storage.Adapter != curAdapter {
  92. adapterChange = true
  93. curAdapter = storage.Adapter
  94. }
  95. if storage.Driver != curDriver {
  96. driverChange = true
  97. curDriver = storage.Driver
  98. }
  99. if adapterChange {
  100. idx = 0
  101. adapterChange = false
  102. }
  103. if driverChange {
  104. idx = 0
  105. driverChange = false
  106. }
  107. if storage.Index == 0 {
  108. storage.Index = int64(idx)
  109. }
  110. if isMatched, mErr := isDiskConfigStorageMatch(diskConfig, confDriver, confAdapter, storage, selected); isMatched {
  111. if confDriver == nil {
  112. confDriver = &storage.Driver
  113. }
  114. if confAdapter == nil {
  115. confAdapter = &storage.Adapter
  116. }
  117. selected = append(selected, storage)
  118. } else {
  119. rest = append(rest, storage)
  120. errs = append(errs, mErr)
  121. }
  122. if confDriver == nil {
  123. idx++
  124. } else if *confDriver == storage.Driver {
  125. idx++
  126. }
  127. }
  128. if len(errs) > 0 {
  129. err = errors.NewAggregate(errs)
  130. }
  131. return
  132. }
  133. func GetMinDiskRequirement(diskConfig string) int {
  134. minDisk := 1
  135. if diskConfig == DISK_CONF_RAID1 {
  136. minDisk = 2
  137. }
  138. if diskConfig == DISK_CONF_RAID5 {
  139. minDisk = 3
  140. } else if diskConfig == DISK_CONF_RAID10 {
  141. minDisk = 4
  142. }
  143. return minDisk
  144. }
  145. func RequireEvenDisks(diskConfig string) bool {
  146. if sets.NewString(
  147. DISK_CONF_RAID10,
  148. DISK_CONF_RAID1,
  149. ).Has(diskConfig) {
  150. return true
  151. }
  152. return false
  153. }
  154. type Layout struct {
  155. Disks []*BaremetalStorage `json:"disks"`
  156. Conf *api.BaremetalDiskConfig `json:"conf"`
  157. Size int64 `json:"size"`
  158. }
  159. func (l Layout) String() string {
  160. bytes, _ := json.MarshalIndent(l, "", " ")
  161. return string(bytes)
  162. }
  163. func RetrieveStorageDrivers(storages []*BaremetalStorage) sets.String {
  164. ret := sets.NewString()
  165. for _, s := range storages {
  166. if !ret.Has(s.Driver) {
  167. ret.Insert(s.Driver)
  168. }
  169. }
  170. return ret
  171. }
  172. func MeetConfig(
  173. conf *api.BaremetalDiskConfig,
  174. storages []*BaremetalStorage,
  175. ) error {
  176. storageDrvs := RetrieveStorageDrivers(storages)
  177. if len(storageDrvs.List()) > 1 {
  178. return fmt.Errorf("%v more than 1 storages drivers", storageDrvs)
  179. }
  180. driver := storageDrvs.List()[0]
  181. //if conf.Conf != DISK_CONF_NONE && !DISK_DRIVERS_RAID.Has(driver) {
  182. // return fmt.Errorf("BaremetalStorage driver %s not support RAID", driver)
  183. //}
  184. minDisk := GetMinDiskRequirement(conf.Conf)
  185. if len(storages) < minDisk {
  186. return fmt.Errorf("%q requires at least %d disks", conf.Conf, minDisk)
  187. }
  188. if RequireEvenDisks(conf.Conf) && (len(storages)%2) != 0 {
  189. return fmt.Errorf("%q requires event number of disks", conf.Conf)
  190. }
  191. if len(conf.Splits) > 0 &&
  192. sets.NewString(
  193. DISK_CONF_NONE,
  194. DISK_DRIVER_MPT2SAS).Has(conf.Conf) {
  195. return fmt.Errorf("Cannot divide a normal disk into splits")
  196. }
  197. if driver == DISK_DRIVER_MPT2SAS {
  198. if conf.Conf == DISK_CONF_RAID5 {
  199. return fmt.Errorf("%q not support RAID5", DISK_DRIVER_MPT2SAS)
  200. }
  201. if conf.Conf == DISK_CONF_RAID0 && len(storages) < 2 {
  202. return fmt.Errorf("%q %q requires at least 2 disks", DISK_DRIVER_MPT2SAS, DISK_CONF_RAID0)
  203. }
  204. if conf.Conf == DISK_CONF_RAID10 && len(storages) > 10 {
  205. return fmt.Errorf("%q %q only support no more than 10 disks", DISK_DRIVER_MPT2SAS, DISK_CONF_RAID10)
  206. }
  207. }
  208. if driver == DISK_DRIVER_MEGARAID && conf.Strip != nil {
  209. minStripSize := storages[0].MinStripSize
  210. maxStripSize := storages[0].MaxStripSize
  211. if maxStripSize != -1 && minStripSize != -1 {
  212. size := *conf.Strip
  213. if size > maxStripSize || size < minStripSize {
  214. return fmt.Errorf("%q input strip size out of range(%d, %d), input: %d", DISK_DRIVER_MEGARAID, minStripSize, maxStripSize, size)
  215. }
  216. }
  217. }
  218. return nil
  219. }
  220. func GetStoragesMinSize(ss []*BaremetalStorage) int64 {
  221. minSize := int64(-1)
  222. for _, s := range ss {
  223. if minSize < 0 || minSize > s.Size {
  224. minSize = s.Size
  225. }
  226. }
  227. return minSize
  228. }
  229. func CalculateSize(conf string, storages []*BaremetalStorage) int64 {
  230. if conf == "" {
  231. conf = DEFAULT_DISK_CONF
  232. }
  233. var size int64
  234. if conf == DISK_CONF_RAID5 {
  235. size = GetStoragesMinSize(storages) * int64(len(storages)-1)
  236. } else if sets.NewString(DISK_CONF_RAID10, DISK_CONF_RAID1).Has(conf) {
  237. size = GetStoragesMinSize(storages) * int64((len(storages) / 2))
  238. } else {
  239. for _, s := range storages {
  240. size += s.Size
  241. }
  242. }
  243. return size
  244. }
  245. func GetSplitSizes(size int64, splitConf string) []int64 {
  246. ssizes := strings.Split(splitConf, ",")
  247. isizes := make([]int64, len(ssizes))
  248. leftoverIdx := -1
  249. subtotal := int64(0)
  250. for index := range ssizes {
  251. if strings.HasSuffix(ssizes[index], "%") {
  252. ssizeFloat64, err := strconv.ParseFloat(ssizes[index][:len(ssizes[index])-1], 64)
  253. if err != nil {
  254. log.Errorf("GetSplitSizes ParseFloat err: %v", err)
  255. continue
  256. }
  257. isizes[index] = int64(ssizeFloat64 / float64(100) * float64(size))
  258. subtotal += isizes[index]
  259. } else if ssizes[index] != "" {
  260. isizes[index], _ = utils.GetSizeMB(ssizes[index], "M")
  261. subtotal += isizes[index]
  262. } else {
  263. if leftoverIdx >= 0 {
  264. log.Errorf("%v", ErrMoreThanOneSizeUnspecificSplit)
  265. return []int64{}
  266. }
  267. leftoverIdx = index
  268. }
  269. }
  270. if leftoverIdx >= 0 {
  271. isizes[leftoverIdx] = size - subtotal
  272. if isizes[leftoverIdx] <= 0 {
  273. log.Errorf("%v", ErrNoMoreSpaceForUnspecificSplit)
  274. return []int64{}
  275. }
  276. } else {
  277. if subtotal > size {
  278. log.Errorf("%v", ErrSubtotalOfSplitExceedsDiskSize)
  279. return []int64{}
  280. }
  281. }
  282. return isizes
  283. }
  284. func ExpandNoneConf(layouts []Layout) (ret []Layout) {
  285. for _, layout := range layouts {
  286. if layout.Conf.Conf == DISK_CONF_NONE && len(layout.Disks) >= 1 {
  287. conf := layout.Conf
  288. conf.Count = 1
  289. for _, disk := range layout.Disks {
  290. ret = append(ret, Layout{Disks: []*BaremetalStorage{disk}, Conf: conf, Size: disk.Size})
  291. }
  292. } else {
  293. ret = append(ret, layout)
  294. }
  295. }
  296. return ret
  297. }
  298. func GetLayoutRaidConfig(layouts []Layout) []*api.BaremetalDiskConfig {
  299. return getLayoutConfig(layouts, true)
  300. }
  301. func GetLayoutDiskConfig(layouts []Layout) []*api.BaremetalDiskConfig {
  302. return getLayoutConfig(layouts, false)
  303. }
  304. func getLayoutConfig(layouts []Layout, onlyRaidDisk bool) []*api.BaremetalDiskConfig {
  305. var disk []*BaremetalStorage
  306. ret := make([]*api.BaremetalDiskConfig, 0)
  307. for _, layout := range layouts {
  308. if onlyRaidDisk && layout.Conf.Conf == DISK_CONF_NONE &&
  309. sets.NewString(DISK_DRIVER_LINUX, DISK_DRIVER_PCIE).Has(layout.Disks[0].Driver) {
  310. continue
  311. }
  312. if !reflect.DeepEqual(disk, layout.Disks) {
  313. ret = append(ret, layout.Conf)
  314. disk = layout.Disks
  315. lastConf := ret[len(ret)-1]
  316. /*
  317. if 'size' in ret[-1]:
  318. ret[-1]['size'] = [ret[-1]['size']]
  319. */
  320. if lastConf.Size == nil {
  321. lastConf.Size = make([]int64, 0)
  322. }
  323. } else {
  324. lastConf := ret[len(ret)-1]
  325. lastConf.Size = append(lastConf.Size, layout.Conf.Size...)
  326. }
  327. }
  328. return ret
  329. }
  330. func CalculateLayout(confs []*api.BaremetalDiskConfig, storages []*BaremetalStorage) (layouts []Layout, err error) {
  331. var confIdx = 0
  332. var softRaidIdx = 0
  333. for len(storages) > 0 {
  334. var conf *api.BaremetalDiskConfig
  335. if confIdx < len(confs) {
  336. conf = confs[confIdx]
  337. confIdx += 1
  338. } else {
  339. noneConf, _ := ParseDiskConfig(DISK_CONF_NONE)
  340. conf = &noneConf
  341. }
  342. // is soft raid
  343. if DISK_DRIVERS_SOFT_RAID.Has(conf.Driver) && conf.Conf != DISK_CONF_NONE {
  344. idx := softRaidIdx
  345. conf.SoftRaidIdx = &idx
  346. softRaidIdx += 1
  347. }
  348. selected, restStorages, rErr := RetrieveStorages(conf, storages)
  349. storages = restStorages
  350. if len(selected) == 0 {
  351. err = errors.Wrapf(rErr, "not found matched storages by config: %#v", conf)
  352. return
  353. }
  354. resultErr := MeetConfig(conf, selected)
  355. if resultErr != nil {
  356. err = fmt.Errorf("selected storages %#v not meet baremetal dick config: %#v, err: %v", selected, conf, resultErr)
  357. return
  358. }
  359. sz := CalculateSize(conf.Conf, selected)
  360. if len(conf.Splits) == 0 {
  361. layouts = append(layouts, Layout{
  362. Disks: selected,
  363. Conf: conf,
  364. Size: sz,
  365. })
  366. } else {
  367. splitSizes := GetSplitSizes(sz, conf.Splits)
  368. conf.Size = splitSizes
  369. layouts = append(layouts, Layout{
  370. Disks: selected,
  371. Conf: conf,
  372. Size: sz,
  373. })
  374. }
  375. }
  376. if confIdx < len(confs) {
  377. err = fmt.Errorf("Not enough disks to meet configuration")
  378. }
  379. return
  380. }
  381. func expandLayoutSplits(layouts []Layout) []Layout {
  382. ret := make([]Layout, 0)
  383. for _, l := range layouts {
  384. splitSizes := GetSplitSizes(l.Size, l.Conf.Splits)
  385. if len(splitSizes) <= 0 {
  386. ret = append(ret, l)
  387. } else {
  388. for _, ssz := range splitSizes {
  389. subLayout := l
  390. subLayout.Size = ssz
  391. ret = append(ret, subLayout)
  392. }
  393. }
  394. }
  395. return ret
  396. }
  397. func IsDisksAllocable(layouts []Layout, disks []*api.DiskConfig) bool {
  398. allocable, _ := CheckDisksAllocable(layouts, disks)
  399. return allocable
  400. }
  401. func CheckDisksAllocable(layouts []Layout, disks []*api.DiskConfig) (bool, []*api.DiskConfig) {
  402. layouts = ExpandNoneConf(layouts)
  403. layouts = expandLayoutSplits(layouts)
  404. storeIndex := 0
  405. storeFreeSize := int64(-1)
  406. diskIndex := 0
  407. layoutLen := len(layouts)
  408. for _, disk := range disks {
  409. if storeIndex >= layoutLen {
  410. break
  411. }
  412. if storeFreeSize < 0 {
  413. storeFreeSize = layouts[storeIndex].Size - 2 // start, end space
  414. }
  415. if disk.SizeMb > 0 {
  416. if storeFreeSize >= int64(disk.SizeMb) {
  417. storeFreeSize -= int64(disk.SizeMb)
  418. diskIndex++
  419. if storeFreeSize == 0 {
  420. storeIndex++
  421. storeFreeSize = -1
  422. }
  423. } else {
  424. store := layouts[storeIndex]
  425. log.Warningf("Disk size %dMB large than storage size %dMB, skip storage %s", disk.SizeMb, storeFreeSize, jsonutils.Marshal(store).PrettyString())
  426. storeIndex++
  427. storeFreeSize = -1
  428. }
  429. } else {
  430. diskIndex++
  431. storeIndex++
  432. storeFreeSize = -1
  433. }
  434. }
  435. if diskIndex < len(disks) {
  436. return false, nil
  437. }
  438. extraDisks := make([]*api.DiskConfig, 0)
  439. for ; storeIndex < layoutLen; storeIndex += 1 {
  440. disk := api.DiskConfig{SizeMb: -1}
  441. extraDisks = append(extraDisks, &disk)
  442. }
  443. return true, extraDisks
  444. }
  445. func NewBaremetalDiskConfigs(dss ...string) ([]*api.BaremetalDiskConfig, error) {
  446. ret := make([]*api.BaremetalDiskConfig, 0)
  447. for _, ds := range dss {
  448. r, err := ParseDiskConfig(ds)
  449. if err != nil {
  450. return nil, err
  451. }
  452. ret = append(ret, &r)
  453. }
  454. return ret, nil
  455. }
  456. func getStorageDiskType(isRotate bool) string {
  457. if isRotate {
  458. return HDD_DISK_SPEC_TYPE
  459. }
  460. return SSD_DISK_SPEC_TYPE
  461. }
  462. func NewDiskSpec(s *BaremetalStorage, index int) *api.DiskSpec {
  463. return &api.DiskSpec{
  464. Type: getStorageDiskType(s.Rotate),
  465. Size: s.Size,
  466. StartIndex: index,
  467. EndIndex: index,
  468. Count: 1,
  469. }
  470. }
  471. func IsDiskSpecSameAs(ds *api.DiskSpec, s *BaremetalStorage, index int) bool {
  472. if ds.Size != s.Size {
  473. return false
  474. }
  475. dType := getStorageDiskType(s.Rotate)
  476. if ds.Type != dType {
  477. return false
  478. }
  479. // discontinuity check
  480. if ds.EndIndex != (index - 1) {
  481. return false
  482. }
  483. return true
  484. }
  485. func addDiskSpecStorage(ds *api.DiskSpec, s *BaremetalStorage, index int) {
  486. ds.Count++
  487. if s.Index != 0 {
  488. ds.EndIndex = index
  489. } else {
  490. ds.EndIndex++
  491. }
  492. }
  493. func GetDiskSpecs(storages []*BaremetalStorage) []*api.DiskSpec {
  494. diskSpecs := make([]*api.DiskSpec, 0)
  495. for idx, s := range storages {
  496. var lastSpec *api.DiskSpec
  497. if len(diskSpecs) != 0 {
  498. lastSpec = diskSpecs[len(diskSpecs)-1]
  499. }
  500. if lastSpec == nil || !IsDiskSpecSameAs(lastSpec, s, idx) {
  501. ds := NewDiskSpec(s, idx)
  502. diskSpecs = append(diskSpecs, ds)
  503. } else {
  504. addDiskSpecStorage(lastSpec, s, idx)
  505. }
  506. }
  507. return diskSpecs
  508. }
  509. func getStoragesByDriver(driver string, storages []*BaremetalStorage) []*BaremetalStorage {
  510. ret := make([]*BaremetalStorage, 0)
  511. for _, s := range storages {
  512. if s.Driver == driver {
  513. ret = append(ret, s)
  514. }
  515. }
  516. return ret
  517. }
  518. func groupByAdapter(storages []*BaremetalStorage) map[string][]*BaremetalStorage {
  519. ret := make(map[string][]*BaremetalStorage)
  520. for _, storage := range storages {
  521. adapter := storage.Adapter
  522. adapterKey := fmt.Sprintf("adapter%d", adapter)
  523. oldStorages := ret[adapterKey]
  524. if len(oldStorages) == 0 {
  525. ret[adapterKey] = []*BaremetalStorage{storage}
  526. } else {
  527. oldStorages = append(oldStorages, storage)
  528. ret[adapterKey] = oldStorages
  529. }
  530. }
  531. return ret
  532. }
  533. func getSpec(storages []*BaremetalStorage) api.DiskAdapterSpec {
  534. ret := make(map[string][]*api.DiskSpec)
  535. for adapterKey, newStorages := range groupByAdapter(storages) {
  536. if len(newStorages) == 0 {
  537. continue
  538. }
  539. ret[adapterKey] = GetDiskSpecs(newStorages)
  540. }
  541. return ret
  542. }
  543. func GetDiskSpecV2(storages []*BaremetalStorage) api.DiskDriverSpec {
  544. spec := make(map[string]api.DiskAdapterSpec)
  545. for _, driver := range DISK_DRIVERS.List() {
  546. driverStorages := getStoragesByDriver(driver, storages)
  547. if len(driverStorages) == 0 {
  548. continue
  549. }
  550. spec[driver] = getSpec(driverStorages)
  551. }
  552. return spec
  553. }
  554. type DiskConfiguration struct {
  555. Driver string
  556. Adapter int
  557. RaidConfig string
  558. Block int64
  559. Size int64
  560. DiskType string
  561. SoftRaidIdx *int
  562. }
  563. func GetDiskConfigurations(layouts []Layout) []DiskConfiguration {
  564. disks := make([]DiskConfiguration, 0)
  565. for _, rr := range layouts {
  566. driver := rr.Disks[0].Driver
  567. adapter := rr.Disks[0].Adapter
  568. block := rr.Disks[0].GetBlock()
  569. raidConf := rr.Conf.Conf
  570. if raidConf == DISK_CONF_NONE {
  571. for _, d := range rr.Disks {
  572. disks = append(disks, DiskConfiguration{
  573. Driver: driver,
  574. Adapter: adapter,
  575. RaidConfig: raidConf,
  576. Block: block,
  577. Size: d.Size,
  578. DiskType: rr.Conf.Type,
  579. SoftRaidIdx: rr.Conf.SoftRaidIdx,
  580. })
  581. }
  582. } else {
  583. if len(rr.Conf.Size) != 0 {
  584. for _, sz := range rr.Conf.Size {
  585. disks = append(disks, DiskConfiguration{
  586. Driver: driver,
  587. Adapter: adapter,
  588. RaidConfig: raidConf,
  589. Block: block,
  590. Size: sz,
  591. DiskType: rr.Conf.Type,
  592. SoftRaidIdx: rr.Conf.SoftRaidIdx,
  593. })
  594. }
  595. } else {
  596. disks = append(disks, DiskConfiguration{
  597. Driver: driver,
  598. Adapter: adapter,
  599. RaidConfig: raidConf,
  600. Block: block,
  601. Size: rr.Size,
  602. DiskType: rr.Conf.Type,
  603. SoftRaidIdx: rr.Conf.SoftRaidIdx,
  604. })
  605. }
  606. }
  607. }
  608. return disks
  609. }
  610. type DriverAdapterDiskConfig struct {
  611. Driver string
  612. Adapter int
  613. Configs []*api.BaremetalDiskConfig
  614. }
  615. func GroupLayoutResultsByDriverAdapter(layouts []Layout) []*DriverAdapterDiskConfig {
  616. ret := make([]*DriverAdapterDiskConfig, 0)
  617. tbl := make(map[string]*DriverAdapterDiskConfig)
  618. for _, layout := range layouts {
  619. driver := layout.Disks[0].Driver
  620. adapter := layout.Disks[0].Adapter
  621. key := fmt.Sprintf("%s.%d", driver, adapter)
  622. if item, ok := tbl[key]; ok {
  623. item.Configs = append(item.Configs, layout.Conf)
  624. } else {
  625. item := &DriverAdapterDiskConfig{
  626. Driver: driver,
  627. Adapter: adapter,
  628. Configs: []*api.BaremetalDiskConfig{layout.Conf},
  629. }
  630. ret = append(ret, item)
  631. tbl[key] = item
  632. }
  633. }
  634. return ret
  635. }
  636. func ValidateDiskConfigs(confs []*api.BaremetalDiskConfig) error {
  637. if len(confs) == 0 {
  638. return nil
  639. }
  640. for idx, conf := range confs {
  641. if conf.Conf != DISK_CONF_NONE {
  642. // raid validate
  643. if idx > 0 {
  644. preConf := confs[idx-1]
  645. if preConf.Conf == DISK_CONF_NONE {
  646. return fmt.Errorf("Raid config %d must before none raid config", idx)
  647. }
  648. }
  649. } else {
  650. // none raid validate
  651. if idx+1 == len(confs) {
  652. return nil
  653. }
  654. restConfs := confs[idx+1:]
  655. hasRaidConf := false
  656. for _, restConf := range restConfs {
  657. if restConf.Conf != DISK_CONF_NONE {
  658. hasRaidConf = true
  659. }
  660. }
  661. if hasRaidConf {
  662. return fmt.Errorf("Raid config after none raid config %d", idx)
  663. }
  664. }
  665. }
  666. return nil
  667. }
  668. func ValidateRootDiskMatcher(input *api.BaremetalRootDiskMatcher) error {
  669. if input.SizeMBRange != nil {
  670. if input.SizeMBRange.Start > input.SizeMBRange.End {
  671. return errors.Errorf("size_mb_range.start %d is large than size_mb_range.end %d", input.SizeMBRange.Start, input.SizeMBRange.End)
  672. }
  673. }
  674. return nil
  675. }