megactl.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149
  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 megactl
  15. import (
  16. "fmt"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/tristate"
  23. "yunion.io/x/pkg/util/stringutils"
  24. "yunion.io/x/pkg/utils"
  25. api "yunion.io/x/onecloud/pkg/apis/compute"
  26. "yunion.io/x/onecloud/pkg/baremetal/utils/raid"
  27. raiddrivers "yunion.io/x/onecloud/pkg/baremetal/utils/raid"
  28. "yunion.io/x/onecloud/pkg/compute/baremetal"
  29. "yunion.io/x/onecloud/pkg/util/regutils2"
  30. )
  31. var (
  32. sizePattern = regexp.MustCompile(`(?P<sector>0x[0-9a-fA-F]+)`)
  33. adapterPatter = regexp.MustCompile(`^Adapter #(?P<idx>[0-9]+)`)
  34. )
  35. type MegaRaidPhyDev struct {
  36. *raiddrivers.RaidBasePhyDev
  37. enclosure int
  38. slot int
  39. minStripSize int
  40. maxStripSize int
  41. sector int64
  42. block int64
  43. }
  44. func NewMegaRaidPhyDev() *MegaRaidPhyDev {
  45. return &MegaRaidPhyDev{
  46. RaidBasePhyDev: raiddrivers.NewRaidBasePhyDev(baremetal.DISK_DRIVER_MEGARAID),
  47. enclosure: -1,
  48. slot: -1,
  49. minStripSize: -1,
  50. maxStripSize: -1,
  51. sector: -1,
  52. block: 512,
  53. }
  54. }
  55. func (dev *MegaRaidPhyDev) ToBaremetalStorage(index int) *baremetal.BaremetalStorage {
  56. s := dev.RaidBasePhyDev.ToBaremetalStorage(index)
  57. s.Enclosure = dev.enclosure
  58. s.Slot = dev.slot
  59. s.Size = dev.GetSize()
  60. s.MinStripSize = int64(dev.minStripSize)
  61. s.MaxStripSize = int64(dev.maxStripSize)
  62. s.Block = dev.block
  63. s.Sector = dev.sector
  64. return s
  65. }
  66. func (dev *MegaRaidPhyDev) GetSize() int64 {
  67. return dev.sector * dev.block / 1024 / 1024 // MB
  68. }
  69. func (dev *MegaRaidPhyDev) parseLine(line string) bool {
  70. key, val := stringutils.SplitKeyValue(line)
  71. if key == "" {
  72. return false
  73. }
  74. switch key {
  75. case "Media Type":
  76. if val == "Hard Disk Device" {
  77. dev.Rotate = tristate.True
  78. } else {
  79. dev.Rotate = tristate.False
  80. }
  81. case "Enclosure Device ID":
  82. enclosure, err := strconv.Atoi(val)
  83. if err == nil {
  84. dev.enclosure = enclosure
  85. }
  86. case "Slot Number":
  87. dev.slot, _ = strconv.Atoi(val)
  88. case "Coerced Size":
  89. sizeStr := regutils2.GetParams(sizePattern, val)["sector"]
  90. if len(sizeStr) != 0 {
  91. sizeStr = strings.Replace(sizeStr, "0x", "", -1)
  92. sector, err := strconv.ParseInt(sizeStr, 16, 64)
  93. if err != nil {
  94. log.Errorf("Parse sector %q to Int error: %v", sizeStr, err)
  95. }
  96. dev.sector = sector
  97. } else {
  98. dev.sector = 0
  99. }
  100. case "Inquiry Data":
  101. dev.Model = dev.convertModel(val)
  102. case "Firmware state":
  103. dev.Status = dev.convertState(val)
  104. case "Logical Sector Size":
  105. block, err := strconv.Atoi(val)
  106. if err != nil {
  107. log.Errorf("parse logical sector size error: %v", err)
  108. dev.block = 512
  109. } else if block > 0 {
  110. dev.block = int64(block)
  111. }
  112. default:
  113. return false
  114. }
  115. return true
  116. }
  117. func (dev *MegaRaidPhyDev) fillByStorcliPD(pd *StorcliPhysicalDrive) error {
  118. if pd.MediaType == "HDD" {
  119. dev.Rotate = tristate.True
  120. } else {
  121. dev.Rotate = tristate.False
  122. }
  123. eId, slotId := stringutils.SplitKeyValue(pd.EnclosureIdSlotNo)
  124. eIdInt, err := strconv.Atoi(eId)
  125. if err != nil {
  126. return errors.Errorf("Can't convert enclosureId %q", eId)
  127. }
  128. slotIdInt, err := strconv.Atoi(slotId)
  129. if err != nil {
  130. return errors.Errorf("Can't convert slotId %q", slotId)
  131. }
  132. dev.enclosure = eIdInt
  133. dev.slot = slotIdInt
  134. dev.Model = dev.convertModel(pd.Model)
  135. dev.Status = dev.convertState(pd.State)
  136. sector := strings.TrimSuffix(pd.SectorSize, "B")
  137. sectorInt, err := strconv.Atoi(sector)
  138. if err != nil {
  139. return errors.Errorf("Can't convert sector %q", pd.SectorSize)
  140. }
  141. // Use block not sector ...
  142. dev.block = int64(sectorInt)
  143. // parse size then fill block count
  144. sizeStrUnit := strings.Split(pd.Size, " ")
  145. if len(sizeStrUnit) != 2 {
  146. return errors.Errorf("Invalid size string %q", pd.Size)
  147. }
  148. sizeStr := sizeStrUnit[0]
  149. unit := sizeStrUnit[1]
  150. size, err := strconv.ParseFloat(sizeStr, 64)
  151. if err != nil {
  152. return errors.Errorf("Invalid size %q", sizeStr)
  153. }
  154. // convert size to Bytes
  155. switch unit {
  156. case "TB":
  157. size = size * 1024 * 1024 * 1024 * 1024
  158. case "GB":
  159. size = size * 1024 * 1024 * 1024
  160. case "MB":
  161. size = size * 1024 * 1024
  162. }
  163. blockCnt := int64(size) / int64(sectorInt)
  164. dev.sector = blockCnt
  165. return nil
  166. }
  167. func (dev *MegaRaidPhyDev) convertModel(val string) string {
  168. return strings.Join(regexp.MustCompile(`\s+`).Split(val, -1), " ")
  169. }
  170. func (dev *MegaRaidPhyDev) convertState(val string) string {
  171. state := val
  172. if val == "JBOD" {
  173. state = "jbod"
  174. } else if strings.Contains(strings.ToLower(val), "online") || utils.IsInStringArray(val, []string{"Onln"}) {
  175. state = "online"
  176. } else if val == "Rebuild" {
  177. state = "rebuild"
  178. } else if strings.Contains(strings.ToLower(val), "hotspare") {
  179. state = "hotspare"
  180. } else if strings.Contains(strings.ToLower(val), "copyback") {
  181. state = "copyback"
  182. } else if strings.Contains(strings.ToLower(val), "unconfigured(good)") {
  183. state = "unconfigured_good"
  184. } else {
  185. state = "offline"
  186. }
  187. return state
  188. }
  189. func (dev *MegaRaidPhyDev) isComplete() bool {
  190. if !dev.RaidBasePhyDev.IsComplete() {
  191. return false
  192. }
  193. if dev.sector < 0 {
  194. return false
  195. }
  196. if dev.block < 0 {
  197. return false
  198. }
  199. if dev.slot < 0 {
  200. return false
  201. }
  202. return true
  203. }
  204. func (dev *MegaRaidPhyDev) isJBOD() bool {
  205. return dev.Status == "jbod"
  206. }
  207. func GetSpecString(dev *baremetal.BaremetalStorage) string {
  208. if dev.Enclosure < 0 {
  209. return fmt.Sprintf(":%d", dev.Slot)
  210. }
  211. return fmt.Sprintf("%d:%d", dev.Enclosure, dev.Slot)
  212. }
  213. type MegaRaidAdaptor struct {
  214. index int
  215. storcliIndex int
  216. raid *MegaRaid
  217. devs []*MegaRaidPhyDev
  218. sn string
  219. name string
  220. busNumber string
  221. deviceNumber string
  222. funcNumber string
  223. // used by sg_map
  224. hostNum int
  225. //channelNum int
  226. minStripSize int
  227. maxStripSize int
  228. }
  229. func NewMegaRaidAdaptor(index int, raid *MegaRaid) (*MegaRaidAdaptor, error) {
  230. adapter := &MegaRaidAdaptor{
  231. index: index,
  232. storcliIndex: -1,
  233. raid: raid,
  234. }
  235. if err := adapter.fillInfo(); err != nil {
  236. return adapter, errors.Wrapf(err, "%d fill info", adapter.index)
  237. }
  238. return adapter, nil
  239. }
  240. func NewMegaRaidAdaptorByStorcli(storAda *StorcliAdaptor, raid *MegaRaid) (*MegaRaidAdaptor, error) {
  241. adapter := &MegaRaidAdaptor{
  242. index: storAda.Controller,
  243. storcliIndex: storAda.Controller,
  244. raid: raid,
  245. sn: storAda.sn,
  246. name: storAda.name,
  247. busNumber: storAda.busNumber,
  248. deviceNumber: storAda.deviceNumber,
  249. funcNumber: storAda.funcNumber,
  250. }
  251. if err := adapter.checkPciDevice(); err != nil {
  252. return nil, errors.Wrap(err, "checkPciDevice")
  253. }
  254. return adapter, nil
  255. }
  256. func (adapter MegaRaidAdaptor) key() string {
  257. return adapter.name + adapter.sn
  258. }
  259. /*
  260. Adapter: 0
  261. Product Name: MegaRAID 9560-8i 4GB
  262. Memory: 4096MB
  263. BBU: Absent
  264. Serial No: SKC4011564
  265. */
  266. func (adapter *MegaRaidAdaptor) fillInfo() error {
  267. size2Int := func(sizeStr string) int {
  268. sz, _ := strconv.ParseFloat(strings.Fields(sizeStr)[0], 32)
  269. szInt := int(sz)
  270. if strings.Contains(sizeStr, "KB") {
  271. return szInt
  272. }
  273. if strings.Contains(sizeStr, "MB") {
  274. return szInt * 1024
  275. }
  276. return -1
  277. }
  278. cmd := GetCommand("-CfgDsply", fmt.Sprintf("-a%d", adapter.index))
  279. ret, err := adapter.remoteRun(cmd)
  280. if err != nil {
  281. return errors.Wrap(err, "remote get SN")
  282. }
  283. for _, l := range ret {
  284. key, val := stringutils.SplitKeyValue(l)
  285. if len(key) == 0 {
  286. continue
  287. }
  288. switch key {
  289. case "Serial No":
  290. adapter.sn = val
  291. case "Product Name":
  292. adapter.name = val
  293. case "Strip Size":
  294. sz := size2Int(val)
  295. adapter.minStripSize = sz
  296. adapter.maxStripSize = sz
  297. case "Min Strip Size":
  298. adapter.minStripSize = size2Int(val)
  299. case "Max Strip Size":
  300. adapter.maxStripSize = size2Int(val)
  301. }
  302. }
  303. if len(adapter.key()) == 0 {
  304. return errors.Error("Not found Serial No and Product Name")
  305. }
  306. return adapter.fillPCIInfo()
  307. }
  308. func (adapter *MegaRaidAdaptor) fillPCIInfo() error {
  309. cmd := GetCommand("-adpgetpciinfo", fmt.Sprintf("-a%d", adapter.index))
  310. ret, err := adapter.remoteRun(cmd)
  311. if err != nil {
  312. return errors.Wrapf(err, "%d remote run get pci info", adapter.index)
  313. }
  314. for _, l := range ret {
  315. key, val := stringutils.SplitKeyValue(l)
  316. if len(key) == 0 {
  317. continue
  318. }
  319. switch key {
  320. case "Bus Number":
  321. if len(val) == 1 {
  322. val = fmt.Sprintf("0%s", val)
  323. }
  324. if len(val) != 2 {
  325. return errors.Errorf("Invalid bus number: %s", val)
  326. }
  327. adapter.busNumber = val
  328. case "Device Number":
  329. if len(val) == 1 {
  330. val = fmt.Sprintf("0%s", val)
  331. }
  332. if len(val) != 2 {
  333. return errors.Errorf("Invalid device number: %s", val)
  334. }
  335. adapter.deviceNumber = val
  336. case "Function Number":
  337. if len(val) != 1 {
  338. return errors.Errorf("Invalid function number: %s", val)
  339. }
  340. adapter.funcNumber = val
  341. }
  342. }
  343. if err := adapter.checkPciDevice(); err != nil {
  344. return errors.Wrap(err, "checkPciDevice")
  345. }
  346. return nil
  347. }
  348. func (adapter *MegaRaidAdaptor) checkPciDevice() error {
  349. pciDir := fmt.Sprintf("/sys/bus/pci/devices/0000:%s:%s.%s/", adapter.busNumber, adapter.deviceNumber, adapter.funcNumber)
  350. cmd := raiddrivers.GetCommand("ls", pciDir, "|", "grep", "host")
  351. ret, err := adapter.remoteRun(cmd)
  352. if err != nil {
  353. return errors.Wrapf(err, "find pci host number")
  354. }
  355. if len(ret) == 0 {
  356. return errors.Errorf("Not find pci host dir")
  357. }
  358. hostNumStr := ret[0]
  359. hostNum, err := strconv.Atoi(strings.TrimLeft(hostNumStr, "host"))
  360. if err != nil {
  361. return errors.Errorf("Invalid hostNum %s", hostNumStr)
  362. }
  363. adapter.hostNum = hostNum
  364. pciHostDir := fmt.Sprintf("%s%s/", pciDir, hostNumStr)
  365. // $ ls /sys/bus/pci/devices/0000:03:00.0/host0/ | grep target | head -n 1
  366. // target0:2:0
  367. targetCmd := raiddrivers.GetCommand("ls", pciHostDir, "|", "grep", "target", "|", "head", "-n", "1")
  368. ret, err = adapter.remoteRun(targetCmd)
  369. if err != nil {
  370. return errors.Wrapf(err, "find target %q", targetCmd)
  371. }
  372. if len(ret) == 0 {
  373. return errors.Errorf("Not find target dir")
  374. }
  375. //targetStr := ret[0]
  376. //parts := strings.Split(targetStr, ":")
  377. //if len(parts) != 3 {
  378. //// not build raid logical volume yet
  379. //log.Warningf("Cmd %q invalid target string %q, skip fill logical volume info", targetCmd, targetStr)
  380. //return nil
  381. //}
  382. //channelNum, err := strconv.Atoi(parts[1])
  383. //if err != nil {
  384. //return errors.Errorf("Invalid channel number %s", parts[1])
  385. //}
  386. //adapter.channelNum = channelNum
  387. return nil
  388. }
  389. func (adapter *MegaRaidAdaptor) GetIndex() int {
  390. return adapter.index
  391. }
  392. func (adapter *MegaRaidAdaptor) getTerm() raid.IExecTerm {
  393. return adapter.raid.term
  394. }
  395. func (adapter *MegaRaidAdaptor) remoteRun(cmds ...string) ([]string, error) {
  396. return adapter.getTerm().Run(cmds...)
  397. }
  398. func (adapter *MegaRaidAdaptor) AddPhyDev(dev *MegaRaidPhyDev) {
  399. dev.Adapter = adapter.index
  400. adapter.devs = append(adapter.devs, dev)
  401. }
  402. func (adapter *MegaRaidAdaptor) GetDevices() []*baremetal.BaremetalStorage {
  403. ret := []*baremetal.BaremetalStorage{}
  404. for idx, dev := range adapter.devs {
  405. ret = append(ret, dev.ToBaremetalStorage(idx))
  406. }
  407. return ret
  408. }
  409. func (adapter *MegaRaidAdaptor) GetLogicVolumes() ([]*raiddrivers.RaidLogicalVolume, error) {
  410. errs := make([]error, 0)
  411. megaLvs, megacliErr := adapter.getMegacliLogicVolumes()
  412. if megacliErr != nil {
  413. errs = append(errs, megacliErr)
  414. }
  415. storeLvs, storcliErr := adapter.getStorcliLogicVolums()
  416. if storcliErr != nil {
  417. errs = append(errs, storcliErr)
  418. }
  419. if len(megaLvs) > 0 {
  420. return megaLvs, nil
  421. }
  422. if len(storeLvs) > 0 {
  423. return storeLvs, nil
  424. }
  425. if len(errs) == 0 || megacliErr == nil || storcliErr == nil {
  426. // no error, no volume
  427. return []*raiddrivers.RaidLogicalVolume{}, nil
  428. }
  429. return nil, errors.NewAggregate(errs)
  430. }
  431. func (adapter *MegaRaidAdaptor) getMegacliLogicVolumes() ([]*raiddrivers.RaidLogicalVolume, error) {
  432. cmd := GetCommand("-LDInfo", "-Lall", fmt.Sprintf("-a%d", adapter.index))
  433. ret, err := adapter.remoteRun(cmd)
  434. if err != nil {
  435. return nil, errors.Wrapf(err, "remoteRun %s", cmd)
  436. }
  437. lvs, err := adapter.parseLogicVolumes(ret)
  438. if err != nil {
  439. return nil, errors.Wrap(err, "")
  440. }
  441. for i := range lvs {
  442. lvs[i].Driver = raiddrivers.RaidDriverToolMegacli64
  443. }
  444. return lvs, nil
  445. }
  446. func (adapter *MegaRaidAdaptor) getStorcliLogicVolums() ([]*raiddrivers.RaidLogicalVolume, error) {
  447. lvs, err := adapter.getStorcliLogicVolumsV2()
  448. if err != nil {
  449. return nil, err
  450. }
  451. ret := make([]*raiddrivers.RaidLogicalVolume, len(lvs))
  452. for i := range lvs {
  453. lv := lvs[i]
  454. ret[i] = &raiddrivers.RaidLogicalVolume{
  455. Index: lv.Index,
  456. Adapter: adapter.index,
  457. BlockDev: lv.GetOSDevice(),
  458. IsSSD: tristate.NewFromBool(lv.IsSSD()),
  459. Driver: raiddrivers.RaidDriverToolStorecli,
  460. }
  461. }
  462. return ret, nil
  463. }
  464. func (adapter *MegaRaidAdaptor) getStorcliLogicVolumsV2() ([]*StorcliLogicalVolume, error) {
  465. cmd := GetCommand2(fmt.Sprintf("/c%d/vall", adapter.index), "show", "all", "J")
  466. ret, err := adapter.remoteRun(cmd)
  467. if err != nil {
  468. return nil, fmt.Errorf("getStorcliLogicVolumsV2 error: %v", err)
  469. }
  470. output := strings.Join(ret, "\n")
  471. lvs, err := parseStorcliLVs(output)
  472. if err != nil {
  473. return nil, errors.Wrap(err, "parseStorcliLVs")
  474. }
  475. return lvs.GetLogicalVolumes(adapter.index)
  476. }
  477. var storcliLVRegexp = regexp.MustCompile(`^(?P<dg>\d+)\/(?P<vd>\d+)\s+(?P<type>RAID\d+).*`)
  478. func parseStorcliLogicalVolumes(adapter int, lines []string) ([]*raiddrivers.RaidLogicalVolume, error) {
  479. lvs := make([]*raiddrivers.RaidLogicalVolume, 0)
  480. for _, line := range lines {
  481. result := regutils2.GetParams(storcliLVRegexp, line)
  482. if len(result) == 0 {
  483. continue
  484. }
  485. idxStr, ok := result["vd"]
  486. if !ok {
  487. return nil, errors.Errorf("Not found virtual drive by line %q", line)
  488. }
  489. idx, _ := strconv.Atoi(idxStr)
  490. lvs = append(lvs, &raiddrivers.RaidLogicalVolume{
  491. Index: idx,
  492. Adapter: adapter,
  493. })
  494. }
  495. return lvs, nil
  496. }
  497. var logicalVolumeIdRegexp = regexp.MustCompile(`.*(Target Id: (?P<idx>[0-9]+))`)
  498. func (adapter *MegaRaidAdaptor) parseLogicVolumes(lines []string) ([]*raiddrivers.RaidLogicalVolume, error) {
  499. lvs := make([]*raiddrivers.RaidLogicalVolume, 0)
  500. for _, line := range lines {
  501. key, val := stringutils.SplitKeyValue(line)
  502. if key != "" && key == "Virtual Drive" {
  503. idxStr := regutils2.GetParams(logicalVolumeIdRegexp, val)["idx"]
  504. idx, err := strconv.Atoi(idxStr)
  505. if err != nil {
  506. return nil, errors.Errorf("index %q to int: %v", idxStr, err)
  507. }
  508. blockDev, err := getLogicVolumeDeviceById(adapter.hostNum, idx, adapter.getTerm())
  509. if err != nil {
  510. return nil, err
  511. }
  512. lvs = append(lvs, &raiddrivers.RaidLogicalVolume{
  513. Index: idx,
  514. Adapter: adapter.index,
  515. BlockDev: blockDev,
  516. })
  517. }
  518. }
  519. return lvs, nil
  520. }
  521. func getLogicVolumeDeviceById(hostNum, scsiId int, term raid.IExecTerm) (string, error) {
  522. items, err := raiddrivers.SGMap(term)
  523. if err != nil {
  524. return "", err
  525. }
  526. isMatch := func(item api.SGMapItem) bool {
  527. return item.HostNumber == hostNum && item.SCSIId == scsiId
  528. }
  529. for _, item := range items {
  530. if isMatch(item) {
  531. return item.LinuxDeviceName, nil
  532. }
  533. }
  534. return "", errors.Errorf("Not found SG item by id: %d:%d", hostNum, scsiId)
  535. }
  536. func (adapter *MegaRaidAdaptor) PreBuildRaid(confs []*api.BaremetalDiskConfig) error {
  537. adapter.clearJBODDisks()
  538. return nil
  539. }
  540. func (adapter *MegaRaidAdaptor) PostBuildRaid() error {
  541. // sync rotational of logical block device
  542. if err := adapter.storcliSyncBlockDeviceAttrs(); err != nil {
  543. log.Warningf("adapter %d storcliSyncBlockDeviceAttrs: %v", adapter.index, err)
  544. }
  545. return nil
  546. }
  547. func (adapter *MegaRaidAdaptor) storcliSyncBlockDeviceAttrs() error {
  548. lvs, err := adapter.getStorcliLogicVolumsV2()
  549. if err != nil {
  550. return errors.Wrap(err, "getStorcliLogicVolumsV2")
  551. }
  552. for _, lv := range lvs {
  553. if !lv.IsSSD() {
  554. continue
  555. }
  556. // e.g: echo 0 | tee /sys/block/sda/queue/rotational if logical volume is SSD
  557. cmd := fmt.Sprintf("echo 0 | tee %s", lv.GetSysBlockRotationalPath())
  558. if out, err := adapter.remoteRun(cmd); err != nil {
  559. return errors.Wrapf(err, "%s out %v", cmd, out)
  560. }
  561. }
  562. return nil
  563. }
  564. func conf2Params(conf *api.BaremetalDiskConfig) []string {
  565. params := []string{}
  566. if conf.WT != nil {
  567. if *conf.WT {
  568. params = append(params, "WT")
  569. } else {
  570. params = append(params, "WB")
  571. }
  572. }
  573. if conf.RA != nil {
  574. if *conf.RA {
  575. params = append(params, "RA")
  576. } else {
  577. params = append(params, "NORA")
  578. }
  579. }
  580. if conf.Direct != nil {
  581. if *conf.Direct {
  582. params = append(params, "Direct")
  583. } else {
  584. params = append(params, "Cached")
  585. }
  586. }
  587. if conf.Cachedbadbbu != nil {
  588. if *conf.Cachedbadbbu {
  589. params = append(params, "CachedBadBBU")
  590. } else {
  591. params = append(params, "NoCachedBadBBU")
  592. }
  593. }
  594. if conf.Strip != nil {
  595. params = append(params, fmt.Sprintf("-strpsz%d", *conf.Strip))
  596. }
  597. if len(conf.Size) > 0 {
  598. for _, sz := range conf.Size {
  599. params = append(params, fmt.Sprintf("-sz%d", sz))
  600. }
  601. }
  602. return params
  603. }
  604. func (adapter *MegaRaidAdaptor) storcliBuildRaid0(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  605. return adapter.storcliBuildRaid(devs, conf, 0)
  606. }
  607. func (adapter *MegaRaidAdaptor) megacliBuildRaid0(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  608. return adapter.megacliBuildRaid(devs, conf, 0)
  609. }
  610. func (adapter *MegaRaidAdaptor) storcliBuildRaid1(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  611. return adapter.storcliBuildRaid(devs, conf, 1)
  612. }
  613. func (adapter *MegaRaidAdaptor) megacliBuildRaid1(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  614. return adapter.megacliBuildRaid(devs, conf, 1)
  615. }
  616. func (adapter *MegaRaidAdaptor) storcliBuildRaid5(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  617. return adapter.storcliBuildRaid(devs, conf, 5)
  618. }
  619. func (adapter *MegaRaidAdaptor) megacliBuildRaid5(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  620. return adapter.megacliBuildRaid(devs, conf, 5)
  621. }
  622. func (adapter *MegaRaidAdaptor) storcliBuildRaid10(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  623. return adapter.storcliBuildRaid(devs, conf, 10)
  624. }
  625. func (adapter *MegaRaidAdaptor) megacliBuildRaid10(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  626. if len(devs)%2 != 0 {
  627. return fmt.Errorf("Odd number of %d devs", len(devs))
  628. }
  629. devCnt := len(devs) / 2
  630. params := []string{}
  631. for i := 0; i < devCnt; i++ {
  632. d1 := devs[i]
  633. d2 := devs[i+devCnt]
  634. params = append(params, fmt.Sprintf("-Array%d[%s,%s]", i, GetSpecString(d1), GetSpecString(d2)))
  635. }
  636. args := []string{"-CfgSpanAdd", "-r10"}
  637. args = append(args, params...)
  638. args = append(args, conf2Params(conf)...)
  639. args = append(args, fmt.Sprintf("-a%d", adapter.index))
  640. cmd := GetCommand(args...)
  641. _, err := adapter.remoteRun(cmd)
  642. return err
  643. }
  644. func (adapter *MegaRaidAdaptor) storcliBuildRaid(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig, level uint) error {
  645. if err := storcliBuildRaid(
  646. adapter.GetStorcliCommand,
  647. adapter.getTerm(),
  648. devs, conf, level); err != nil {
  649. return err
  650. }
  651. return nil
  652. }
  653. func (adapter *MegaRaidAdaptor) megacliBuildRaid(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig, level uint) error {
  654. labels := []string{}
  655. for _, dev := range devs {
  656. labels = append(labels, GetSpecString(dev))
  657. }
  658. args := []string{"-CfgLdAdd", fmt.Sprintf("-r%d", level), fmt.Sprintf("[%s]", strings.Join(labels, ","))}
  659. args = append(args, conf2Params(conf)...)
  660. args = append(args, fmt.Sprintf("-a%d", adapter.index))
  661. cmd := GetCommand(args...)
  662. log.Infof("_megacliBuildRaid command: %s", cmd)
  663. _, err := adapter.remoteRun(cmd)
  664. return err
  665. }
  666. func cliBuildRaid(
  667. devs []*baremetal.BaremetalStorage,
  668. conf *api.BaremetalDiskConfig,
  669. funcs ...func([]*baremetal.BaremetalStorage, *api.BaremetalDiskConfig) error,
  670. ) error {
  671. var errs []error
  672. for _, f := range funcs {
  673. err := f(devs, conf)
  674. if err == nil {
  675. return nil
  676. }
  677. errs = append(errs, err)
  678. }
  679. return errors.NewAggregate(errs)
  680. }
  681. func (adapter *MegaRaidAdaptor) BuildRaid0(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  682. return cliBuildRaid(devs, conf, adapter.megacliBuildRaid0, adapter.storcliBuildRaid0)
  683. }
  684. func (adapter *MegaRaidAdaptor) BuildRaid1(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  685. return cliBuildRaid(devs, conf, adapter.megacliBuildRaid1, adapter.storcliBuildRaid1)
  686. }
  687. func (adapter *MegaRaidAdaptor) BuildRaid5(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  688. return cliBuildRaid(devs, conf, adapter.megacliBuildRaid5, adapter.storcliBuildRaid5)
  689. }
  690. func (adapter *MegaRaidAdaptor) BuildRaid10(devs []*baremetal.BaremetalStorage, conf *api.BaremetalDiskConfig) error {
  691. return cliBuildRaid(devs, conf, adapter.megacliBuildRaid10, adapter.storcliBuildRaid10)
  692. }
  693. func (adapter *MegaRaidAdaptor) BuildNoneRaid(devs []*baremetal.BaremetalStorage) error {
  694. return cliBuildRaid(devs, nil, adapter.megacliBuildNoRaid, adapter.storcliBuildNoRaid)
  695. }
  696. func (raid *MegaRaid) GetStorcliAdaptor() ([]*StorcliAdaptor, map[string]*StorcliAdaptor, error) {
  697. ret := make(map[string]*StorcliAdaptor)
  698. cmd := GetCommand2("/call", "show", "|", "grep", "-iE", `'^(Controller|Product Name|Serial Number|Bus Number|Device Number|Function Number)\s='`)
  699. lines, err := raid.term.Run(cmd)
  700. if err != nil {
  701. return nil, nil, errors.Wrap(err, "Get storcli adapter")
  702. }
  703. adapter := newStorcliAdaptor()
  704. list := make([]*StorcliAdaptor, 0)
  705. for _, l := range lines {
  706. adapter.parseLine(l)
  707. if adapter.isComplete() {
  708. ret[adapter.key()] = adapter
  709. list = append(list, adapter)
  710. adapter = newStorcliAdaptor()
  711. }
  712. }
  713. return list, ret, nil
  714. }
  715. func (adapter *MegaRaidAdaptor) storcliCtrlIndex() (int, error) {
  716. if adapter.storcliIndex >= 0 {
  717. return adapter.storcliIndex, nil
  718. }
  719. _, storcliAdaps, err := adapter.raid.GetStorcliAdaptor()
  720. if err != nil {
  721. return -1, errors.Wrap(err, "Get all Storcli adaptor")
  722. }
  723. storAdap, ok := storcliAdaps[adapter.key()]
  724. if !ok {
  725. return -1, errors.Errorf("Not found storcli adaptor by SN %q", adapter.key())
  726. }
  727. return storAdap.Controller, nil
  728. }
  729. func (adapter *MegaRaidAdaptor) GetStorcliCommand(args ...string) (string, error) {
  730. controllerIdx, err := adapter.storcliCtrlIndex()
  731. if err != nil {
  732. return "", errors.Errorf("Adapter %d get storcli controller index: %v", adapter.index, err)
  733. }
  734. nargs := []string{fmt.Sprintf("/c%d", controllerIdx)}
  735. nargs = append(nargs, args...)
  736. return GetCommand2(nargs...), nil
  737. }
  738. func (adapter *MegaRaidAdaptor) storcliIsJBODEnabled() bool {
  739. return storcliIsJBODEnabled(adapter.GetStorcliCommand, adapter.getTerm())
  740. }
  741. func (adapter *MegaRaidAdaptor) storcliEnableJBOD(enable bool) bool {
  742. val := "off"
  743. if enable {
  744. val = "on"
  745. }
  746. cmd, err := adapter.GetStorcliCommand("set", fmt.Sprintf("jbod=%s", val), "force")
  747. if err != nil {
  748. log.Errorf("get storcli controller cmd: %v", err)
  749. return false
  750. }
  751. _, err = adapter.remoteRun(cmd)
  752. if err != nil {
  753. log.Errorf("EnableJBOD %v fail: %v", enable, err)
  754. return false
  755. }
  756. return true
  757. }
  758. func (adapter *MegaRaidAdaptor) storcliBuildJBOD(devs []*baremetal.BaremetalStorage) error {
  759. return storcliBuildJBOD(adapter.GetStorcliCommand, adapter.getTerm(), devs)
  760. }
  761. func (adapter *MegaRaidAdaptor) storcliBuildNoRaid(devs []*baremetal.BaremetalStorage, _ *api.BaremetalDiskConfig) error {
  762. return storcliBuildNoRaid(adapter.GetStorcliCommand, adapter.getTerm(), devs)
  763. }
  764. func (adapter *MegaRaidAdaptor) megacliBuildNoRaid(devs []*baremetal.BaremetalStorage, _ *api.BaremetalDiskConfig) error {
  765. err := adapter.megacliBuildJBOD(devs)
  766. if err == nil {
  767. return nil
  768. }
  769. log.Errorf("Try megacli build jbod fail: %v", err)
  770. cmds := []string{}
  771. for _, dev := range devs {
  772. cmd := GetCommand("-CfgLdAdd", "-r0", fmt.Sprintf("[%s]", GetSpecString(dev)),
  773. "WT", "NORA", "Direct", "NoCachedBadBBU", fmt.Sprintf("-a%d", adapter.index))
  774. cmds = append(cmds, cmd)
  775. }
  776. _, err = adapter.remoteRun(cmds...)
  777. return err
  778. }
  779. func (adapter *MegaRaidAdaptor) megacliIsJBODEnabled() bool {
  780. cmd := GetCommand("-AdpGetProp", "-EnableJBOD", fmt.Sprintf("-a%d", adapter.index))
  781. pref := fmt.Sprintf("Adapter %d: JBOD: ", adapter.index)
  782. lines, err := adapter.remoteRun(cmd)
  783. if err != nil {
  784. log.Errorf("megacliIsJBODEnabled error: %v", err)
  785. return false
  786. }
  787. for _, line := range lines {
  788. if strings.HasPrefix(line, pref) {
  789. val := strings.ToLower(strings.TrimSpace(line[len(pref):]))
  790. if val == "disabled" {
  791. return false
  792. }
  793. return true
  794. }
  795. }
  796. return false
  797. }
  798. func (adapter *MegaRaidAdaptor) megacliEnableJBOD(enable bool) bool {
  799. var val string = "0"
  800. if enable {
  801. val = "1"
  802. }
  803. cmd := GetCommand("-AdpSetProp", "-EnableJBOD", fmt.Sprintf("-%s", val), fmt.Sprintf("-a%d", adapter.index))
  804. _, err := adapter.remoteRun(cmd)
  805. if err != nil {
  806. log.Errorf("enable jbod %v fail: %v", enable, err)
  807. return false
  808. }
  809. return true
  810. }
  811. func (adapter *MegaRaidAdaptor) megacliBuildJBOD(devs []*baremetal.BaremetalStorage) error {
  812. if !adapter.megacliIsJBODEnabled() {
  813. adapter.megacliEnableJBOD(true)
  814. adapter.megacliEnableJBOD(false)
  815. adapter.megacliEnableJBOD(true)
  816. }
  817. if !adapter.megacliIsJBODEnabled() {
  818. return fmt.Errorf("JBOD not supported")
  819. }
  820. // try clear jbod disk of devices
  821. if err := adapter.megacliClearJBODDisks(devs); err != nil {
  822. log.Warningf("try clear megaraid jbod disks before make jbod: %s", err)
  823. }
  824. devIds := []string{}
  825. for _, d := range devs {
  826. devIds = append(devIds, GetSpecString(d))
  827. }
  828. cmd := GetCommand("-PDMakeJBOD", fmt.Sprintf("-PhysDrv[%s]", strings.Join(devIds, ",")), fmt.Sprintf("-a%d", adapter.index))
  829. _, err := adapter.remoteRun(cmd)
  830. return err
  831. }
  832. func (adapter *MegaRaidAdaptor) RemoveLogicVolumes() error {
  833. lvIdx, err := adapter.GetLogicVolumes()
  834. if err != nil {
  835. return errors.Wrap(err, "GetLogicVolumes")
  836. }
  837. if len(lvIdx) == 0 {
  838. log.Infof("RemoveLogicVolumes: no logical volume to delete!")
  839. return nil
  840. }
  841. errs := make([]error, 0)
  842. lvIdx = raiddrivers.ReverseLogicalArray(lvIdx)
  843. for i := range lvIdx {
  844. lv := lvIdx[i]
  845. switch lv.Driver {
  846. case raiddrivers.RaidDriverToolMegacli64:
  847. cmd := GetCommand("-CfgLdDel", fmt.Sprintf("-L%d", lv.Index), "-Force", fmt.Sprintf("-a%d", adapter.index))
  848. _, err := adapter.remoteRun(cmd)
  849. if err != nil {
  850. errs = append(errs, err)
  851. }
  852. case raiddrivers.RaidDriverToolStorecli:
  853. cmd := GetCommand2(fmt.Sprintf("/c%d/v%d", adapter.index, lv.Index), "delete", "force")
  854. _, err := adapter.remoteRun(cmd)
  855. if err != nil {
  856. errs = append(errs, err)
  857. }
  858. }
  859. }
  860. if len(errs) > 0 {
  861. return errors.NewAggregate(errs)
  862. }
  863. return nil
  864. }
  865. func (adapter *MegaRaidAdaptor) storcliClearJBODDisks() error {
  866. return storcliClearJBODDisks(
  867. adapter.GetStorcliCommand, adapter.getTerm(),
  868. adapter.devs,
  869. )
  870. }
  871. func (adapter *MegaRaidAdaptor) megacliClearJBODDisks(devs []*baremetal.BaremetalStorage) error {
  872. devIds := []string{}
  873. for _, dev := range devs {
  874. devIds = append(devIds, GetSpecString(dev))
  875. }
  876. errs := make([]error, 0)
  877. for _, devId := range devIds {
  878. cmd := GetCommand("-PDMakeGood", "-PhysDrv", fmt.Sprintf("'[%s]'", devId), "-Force", fmt.Sprintf("-a%d", adapter.index))
  879. if _, err := adapter.remoteRun(cmd); err != nil {
  880. err = errors.Wrapf(err, "PDMakeGood megacli cmd %v", cmd)
  881. errs = append(errs, err)
  882. }
  883. }
  884. return errors.NewAggregate(errs)
  885. }
  886. func (adapter *MegaRaidAdaptor) megacliClearAllJBODDisks() error {
  887. allDevs := make([]*baremetal.BaremetalStorage, 0)
  888. for idx, dev := range adapter.devs {
  889. allDevs = append(allDevs, dev.ToBaremetalStorage(idx))
  890. }
  891. return adapter.megacliClearJBODDisks(allDevs)
  892. }
  893. func (adapter *MegaRaidAdaptor) clearJBODDisks() {
  894. if err := adapter.megacliClearAllJBODDisks(); err != nil {
  895. log.Errorf("megacliClearAllJBODDisks error: %v", err)
  896. log.Infof("try storcliClearJBODDisks")
  897. if err := adapter.storcliClearJBODDisks(); err != nil {
  898. log.Errorf("storcliClearJBODDisks error: %v", err)
  899. }
  900. }
  901. adapter.megacliEnableJBOD(true)
  902. adapter.megacliEnableJBOD(false)
  903. adapter.megacliEnableJBOD(true)
  904. adapter.megacliEnableJBOD(false)
  905. }
  906. type MegaRaid struct {
  907. term raid.IExecTerm
  908. adapters []*MegaRaidAdaptor
  909. PhyDevsCnt int
  910. Capacity int64
  911. }
  912. func NewMegaRaid(term raid.IExecTerm) raiddrivers.IRaidDriver {
  913. return &MegaRaid{
  914. term: term,
  915. adapters: make([]*MegaRaidAdaptor, 0),
  916. }
  917. }
  918. func GetCommand(args ...string) string {
  919. bin := "/opt/MegaRAID/MegaCli/MegaCli64"
  920. return raiddrivers.GetCommand(bin, args...)
  921. }
  922. func GetCommand2(args ...string) string {
  923. bin := "/opt/MegaRAID/storcli/storcli64"
  924. return raiddrivers.GetCommand(bin, args...)
  925. }
  926. func (raid *MegaRaid) GetName() string {
  927. return baremetal.DISK_DRIVER_MEGARAID
  928. }
  929. func (raid *MegaRaid) ParsePhyDevs() error {
  930. if !utils.IsInStringArray(raiddrivers.MODULE_MEGARAID, raiddrivers.GetModules(raid.term)) {
  931. return fmt.Errorf("Not found megaraid_sas module")
  932. }
  933. if err := raid.parsePhyDevsUseMegacli(); err == nil {
  934. return nil
  935. } else {
  936. // try use storecli parse physical devices
  937. if err := raid.parsePhyDevsUseStorcli(); err != nil {
  938. return errors.Wrap(err, "parsePhyDevsUseStorcli")
  939. }
  940. }
  941. return nil
  942. }
  943. func (raid *MegaRaid) parsePhyDevsUseMegacli() error {
  944. cmd := GetCommand("-PDList", "-aALL")
  945. ret, err := raid.term.Run(cmd)
  946. if err != nil {
  947. return fmt.Errorf("List raid disk error: %v", err)
  948. }
  949. if raiddrivers.Debug {
  950. log.Debugf("-PDList -aALL: %s", ret)
  951. }
  952. err = raid.parsePhyDevs(ret)
  953. if err != nil {
  954. return fmt.Errorf("parse physical disk device error: %v", err)
  955. }
  956. return nil
  957. }
  958. func (raid *MegaRaid) parsePhyDevsUseStorcli() error {
  959. adapters, _, err := raid.GetStorcliAdaptor()
  960. if err != nil {
  961. return errors.Wrap(err, "Get storcli adapter")
  962. }
  963. raid.adapters = make([]*MegaRaidAdaptor, 0)
  964. for _, ada := range adapters {
  965. megaAda, err := NewMegaRaidAdaptorByStorcli(ada, raid)
  966. if err != nil {
  967. return errors.Wrap(err, "NewMegaRaidAdaptorByStorcli")
  968. }
  969. devs, err := ada.getMegaPhyDevs(GetCommand2, raid.term)
  970. if err != nil {
  971. return errors.Wrapf(err, "get storcli %d mega PDs", ada.Controller)
  972. }
  973. for i := range devs {
  974. megaAda.AddPhyDev(devs[i])
  975. }
  976. raid.adapters = append(raid.adapters, megaAda)
  977. }
  978. return nil
  979. }
  980. func (raid *MegaRaid) parsePhyDevs(lines []string) error {
  981. phyDev := NewMegaRaidPhyDev()
  982. var adapter *MegaRaidAdaptor
  983. var err error
  984. for _, line := range lines {
  985. adapterStr := regutils2.GetParams(adapterPatter, line)["idx"]
  986. if adapterStr != "" {
  987. adapterInt, _ := strconv.Atoi(adapterStr)
  988. adapter, err = NewMegaRaidAdaptor(adapterInt, raid)
  989. if err != nil {
  990. return errors.Wrapf(err, "New raid adapter %d", adapterInt)
  991. }
  992. raid.adapters = append(raid.adapters, adapter)
  993. } else if phyDev.parseLine(line) && phyDev.isComplete() {
  994. if adapter == nil {
  995. return fmt.Errorf("Adapter is nil")
  996. }
  997. adapter.AddPhyDev(phyDev)
  998. raid.PhyDevsCnt += 1
  999. raid.Capacity += phyDev.GetSize()
  1000. phyDev = NewMegaRaidPhyDev()
  1001. }
  1002. }
  1003. for _, adapter := range raid.adapters {
  1004. if err := adapter.addPhyDevsStripSize(); err != nil {
  1005. log.Errorf("Adapter %d fill phsical devices strip size: %v", adapter.GetIndex(), err)
  1006. }
  1007. }
  1008. return nil
  1009. }
  1010. func (adapter *MegaRaidAdaptor) addPhyDevsStripSize() error {
  1011. for _, dev := range adapter.devs {
  1012. dev.minStripSize = adapter.minStripSize
  1013. dev.maxStripSize = adapter.maxStripSize
  1014. }
  1015. return nil
  1016. }
  1017. func (raid *MegaRaid) CleanRaid() error {
  1018. for _, adapter := range raid.adapters {
  1019. adapter.clearJBODDisks()
  1020. adapter.RemoveLogicVolumes()
  1021. }
  1022. return nil
  1023. }
  1024. func (raid *MegaRaid) PreBuildRaid(_ []*api.BaremetalDiskConfig, _ int) error {
  1025. return raid.clearForeignState()
  1026. }
  1027. func (raid *MegaRaid) GetAdapters() []raiddrivers.IRaidAdapter {
  1028. ret := make([]raiddrivers.IRaidAdapter, 0)
  1029. for _, a := range raid.adapters {
  1030. ret = append(ret, a)
  1031. }
  1032. return ret
  1033. }
  1034. func (raid *MegaRaid) clearForeignState() error {
  1035. errs := make([]error, 0)
  1036. cmd := GetCommand("-CfgForeign", "-Clear", "-aALL")
  1037. _, err := raid.term.Run(cmd)
  1038. if err != nil {
  1039. errs = append(errs, err)
  1040. cmd2 := GetCommand2("/call/fall", "delete")
  1041. if _, err := raid.term.Run(cmd2); err == nil {
  1042. return nil
  1043. } else {
  1044. errs = append(errs, err)
  1045. }
  1046. }
  1047. return errors.NewAggregate(errs)
  1048. }
  1049. func (raid *MegaRaid) RemoveLogicVolumes() {
  1050. for _, adapter := range raid.adapters {
  1051. adapter.RemoveLogicVolumes()
  1052. }
  1053. }