baseprepare.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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 tasks
  15. import (
  16. "context"
  17. "fmt"
  18. "net"
  19. "strings"
  20. "time"
  21. "yunion.io/x/cloudmux/pkg/apis/compute"
  22. "yunion.io/x/jsonutils"
  23. "yunion.io/x/log"
  24. "yunion.io/x/pkg/errors"
  25. "yunion.io/x/pkg/util/netutils"
  26. "yunion.io/x/pkg/util/seclib"
  27. "yunion.io/x/pkg/utils"
  28. "yunion.io/x/onecloud/pkg/apis"
  29. api "yunion.io/x/onecloud/pkg/apis/compute"
  30. baremetalapi "yunion.io/x/onecloud/pkg/apis/compute/baremetal"
  31. o "yunion.io/x/onecloud/pkg/baremetal/options"
  32. "yunion.io/x/onecloud/pkg/baremetal/profiles"
  33. "yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages"
  34. "yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
  35. "yunion.io/x/onecloud/pkg/cloudcommon/types"
  36. "yunion.io/x/onecloud/pkg/compute/baremetal"
  37. "yunion.io/x/onecloud/pkg/hostman/isolated_device"
  38. "yunion.io/x/onecloud/pkg/mcclient"
  39. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  40. "yunion.io/x/onecloud/pkg/util/logclient"
  41. "yunion.io/x/onecloud/pkg/util/ssh"
  42. "yunion.io/x/onecloud/pkg/util/sysutils"
  43. )
  44. type sBaremetalPrepareTask struct {
  45. baremetal IBaremetal
  46. startTime time.Time
  47. userCred mcclient.TokenCredential
  48. }
  49. func newBaremetalPrepareTask(baremetal IBaremetal, userCred mcclient.TokenCredential) *sBaremetalPrepareTask {
  50. return &sBaremetalPrepareTask{
  51. baremetal: baremetal,
  52. userCred: userCred,
  53. startTime: time.Now().UTC(),
  54. }
  55. }
  56. type baremetalPrepareInfo struct {
  57. architecture string
  58. sysInfo *types.SSystemInfo
  59. cpuInfo *types.SCPUInfo
  60. dmiCpuInfo *types.SDMICPUInfo
  61. memInfo *types.SDMIMemInfo
  62. nicsInfo []*types.SNicDevInfo
  63. diskInfo []*baremetal.BaremetalStorage
  64. storageDriver string
  65. ipmiInfo *types.SIPMIInfo
  66. isolatedDevicesInfo []*isolated_device.PCIDevice
  67. }
  68. func (task *sBaremetalPrepareTask) GetStartTime() time.Time {
  69. return task.startTime
  70. }
  71. func (task *sBaremetalPrepareTask) prepareBaremetalInfo(cli *ssh.Client) (*baremetalPrepareInfo, error) {
  72. arch, err := getArchitecture(cli)
  73. if err != nil {
  74. return nil, errors.Wrap(err, "getArchitecture")
  75. }
  76. if arch == apis.OS_ARCH_X86_64 {
  77. if _, err := cli.Run("/lib/mos/sysinit.sh"); err != nil {
  78. return nil, errors.Wrap(err, "run /lib/mos/sysinit.sh")
  79. }
  80. } else {
  81. log.Infof("Skip running /lib/mos/sysinit.sh when arch is %q", arch)
  82. }
  83. sysInfo, err := getDMISysinfo(cli)
  84. if err != nil {
  85. return nil, err
  86. }
  87. cpuInfo, err := getCPUInfo(cli)
  88. if err != nil {
  89. return nil, err
  90. }
  91. dmiCPUInfo, err := getDMICPUInfo(cli)
  92. if err != nil {
  93. return nil, err
  94. }
  95. memInfo, err := getDMIMemInfo(cli)
  96. if err != nil {
  97. return nil, err
  98. }
  99. nicsInfo, err := getNicsInfo(cli)
  100. if err != nil {
  101. return nil, err
  102. }
  103. dhcpServerIp, err := task.baremetal.GetDHCPServerIP()
  104. if err != nil {
  105. log.Errorf("failed get dhcp server %s", err)
  106. }
  107. isolatedDevicesInfo, err := getIsolatedDevicesInfo(cli, dhcpServerIp)
  108. if err != nil {
  109. return nil, err
  110. }
  111. raidDiskInfo, nonRaidDiskInfo, pcieDiskInfo, err := detect_storages.DetectStorageInfo(cli, true)
  112. if err != nil {
  113. return nil, err
  114. }
  115. diskInfo := make([]*baremetal.BaremetalStorage, 0)
  116. diskInfo = append(diskInfo, raidDiskInfo...)
  117. diskInfo = append(diskInfo, nonRaidDiskInfo...)
  118. diskInfo = append(diskInfo, pcieDiskInfo...)
  119. var storageDriver string
  120. if len(raidDiskInfo) > 0 {
  121. raidDrivers := []string{}
  122. for _, drv := range raidDiskInfo {
  123. if !utils.IsInStringArray(drv.Driver, raidDrivers) {
  124. raidDrivers = append(raidDrivers, drv.Driver)
  125. }
  126. }
  127. storageDriver = strings.Join(raidDrivers, ",")
  128. } else {
  129. storageDriver = baremetal.DISK_DRIVER_LINUX
  130. }
  131. ipmiEnable, err := isIPMIEnable(cli)
  132. if err != nil {
  133. return nil, err
  134. }
  135. ipmiInfo := task.baremetal.GetRawIPMIConfig()
  136. if ipmiInfo == nil {
  137. ipmiInfo = &types.SIPMIInfo{}
  138. }
  139. if !ipmiInfo.Verified && !ipmiInfo.Present && ipmiEnable {
  140. ipmiInfo.Present = true
  141. ipmiInfo.Verified = false
  142. }
  143. prepareInfo := &baremetalPrepareInfo{
  144. architecture: arch,
  145. sysInfo: sysInfo,
  146. cpuInfo: cpuInfo,
  147. dmiCpuInfo: dmiCPUInfo,
  148. memInfo: memInfo,
  149. nicsInfo: nicsInfo,
  150. diskInfo: diskInfo,
  151. storageDriver: storageDriver,
  152. ipmiInfo: ipmiInfo,
  153. isolatedDevicesInfo: isolatedDevicesInfo,
  154. }
  155. return prepareInfo, nil
  156. }
  157. func (task *sBaremetalPrepareTask) configIPMISetting(ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo) error {
  158. if !i.ipmiInfo.Present {
  159. return nil
  160. }
  161. // if verified, skip ipmi config
  162. if i.ipmiInfo.Verified {
  163. return nil
  164. }
  165. var (
  166. sysInfo = i.sysInfo
  167. ipmiInfo = i.ipmiInfo
  168. )
  169. sshIPMI := ipmitool.NewSSHIPMI(cli)
  170. setIPMILanPortShared(sshIPMI, sysInfo)
  171. profile, err := profiles.GetProfile(ctx, sysInfo)
  172. if err != nil {
  173. return errors.Wrap(err, "GetProfile")
  174. }
  175. ipmiUser, ipmiPasswd, ipmiIpAddr := task.getIPMIUserPasswd(i.ipmiInfo, profile)
  176. ipmiInfo.Username = ipmiUser
  177. ipmiInfo.Password = ipmiPasswd
  178. var ipmiLanChannel uint8 = 0
  179. for _, lanChannel := range profile.LanChannels {
  180. log.Infof("Try lan channel %d ...", lanChannel)
  181. conf, err := ipmitool.GetLanConfig(sshIPMI, lanChannel)
  182. if err != nil {
  183. log.Errorf("Get lan channel %d config error: %v", lanChannel, err)
  184. continue
  185. }
  186. if conf.Mac == nil {
  187. log.Errorf("Lan channel %d MAC address is empty", lanChannel)
  188. continue
  189. }
  190. ipmiNic := &types.SNicDevInfo{
  191. Mac: conf.Mac,
  192. Speed: 100,
  193. Mtu: 1500,
  194. }
  195. if err := task.sendNicInfo(ctx, ipmiNic, -1, api.NIC_TYPE_IPMI, true, "", false); err != nil {
  196. // ignore the error
  197. log.Errorf("Send IPMI nic %#v info: %v", ipmiNic, err)
  198. }
  199. rootId := profile.RootId
  200. err = ipmitool.CreateOrSetAdminUser(sshIPMI, lanChannel, rootId, ipmiUser, ipmiPasswd)
  201. if err != nil {
  202. // ignore the error
  203. log.Errorf("Lan channel %d set user password error: %v", lanChannel, err)
  204. }
  205. err = ipmitool.EnableLanAccess(sshIPMI, lanChannel)
  206. if err != nil {
  207. // ignore the error
  208. log.Errorf("Lan channel %d enable lan access error: %v", lanChannel, err)
  209. }
  210. tryAddrs := make([]string, 0)
  211. if ipmiIpAddr != "" {
  212. tryAddrs = append(tryAddrs, ipmiIpAddr)
  213. }
  214. if conf.IPAddr != "" && conf.IPAddr != ipmiIpAddr {
  215. tryAddrs = append(tryAddrs, conf.IPAddr)
  216. }
  217. if len(tryAddrs) > 0 && !o.Options.ForceDhcpProbeIpmi {
  218. for _, tryAddr := range tryAddrs {
  219. tryResult := task.tryLocalIpmiAddr(ctx, sshIPMI, ipmiNic, lanChannel,
  220. ipmiUser, ipmiPasswd, tryAddr)
  221. if tryResult {
  222. ipmiInfo.IpAddr = tryAddr
  223. ipmiLanChannel = lanChannel
  224. break
  225. }
  226. }
  227. if ipmiLanChannel > 0 {
  228. // found and set config on lanChannel
  229. break
  230. }
  231. }
  232. if len(tryAddrs) > 0 {
  233. task.baremetal.SetExistingIPMIIPAddr(tryAddrs[0])
  234. }
  235. err = ipmitool.SetLanDHCP(sshIPMI, lanChannel)
  236. if err != nil {
  237. // ignore error
  238. log.Errorf("Set lan channel %d dhcp error: %v", lanChannel, err)
  239. }
  240. time.Sleep(2 * time.Second)
  241. nic := task.baremetal.GetIPMINic(conf.Mac)
  242. maxTries := 180 // wait 3 minutes
  243. for tried := 0; nic != nil && nic.IpAddr == "" && tried < maxTries; tried++ {
  244. nic = task.baremetal.GetIPMINic(conf.Mac)
  245. }
  246. if len(nic.IpAddr) == 0 {
  247. err = ipmitool.DoBMCReset(sshIPMI) // do BMC reset to force DHCP request
  248. if err != nil {
  249. log.Errorf("Do BMC reset error: %v", err)
  250. }
  251. time.Sleep(1 * time.Second)
  252. }
  253. for tried := 0; nic != nil && nic.IpAddr == "" && tried < maxTries; tried++ {
  254. nic = task.baremetal.GetIPMINic(conf.Mac)
  255. time.Sleep(1 * time.Second)
  256. }
  257. if nic != nil && len(nic.IpAddr) == 0 {
  258. log.Errorf("DHCP wait IPMI address fail, retry ...")
  259. continue
  260. }
  261. log.Infof("DHCP get IPMI address succ, wait 2 seconds ...")
  262. var tried int = 0
  263. for tried < maxTries {
  264. time.Sleep(2 * time.Second)
  265. lanConf, err := ipmitool.GetLanConfig(sshIPMI, lanChannel)
  266. if err != nil {
  267. log.Errorf("Get lan config at channel %d error: %v", lanChannel, err)
  268. tried += 2
  269. continue
  270. }
  271. if lanConf.IPAddr == nic.IpAddr {
  272. break
  273. }
  274. log.Infof("waiting IPMI DHCP address old:%s expect:%s", lanConf.IPAddr, nic.IpAddr)
  275. tried += 2
  276. }
  277. if tried >= maxTries {
  278. continue
  279. }
  280. err = ipmitool.SetLanStatic(
  281. sshIPMI,
  282. lanChannel,
  283. nic.IpAddr,
  284. nic.GetNetMask(),
  285. nic.Gateway,
  286. )
  287. if err != nil {
  288. log.Errorf("Set lanChannel %d static net %#v error: %v", lanChannel, nic, err)
  289. continue
  290. }
  291. ipmiInfo.IpAddr = nic.IpAddr
  292. ipmiLanChannel = lanChannel
  293. }
  294. if ipmiLanChannel == 0 {
  295. return fmt.Errorf("Fail to get IPMI address from DHCP")
  296. }
  297. ipmiInfo.LanChannel = ipmiLanChannel
  298. ipmiInfo.Verified = true
  299. return nil
  300. }
  301. func (task *sBaremetalPrepareTask) DoPrepare(ctx context.Context, cli *ssh.Client) error {
  302. infos, err := task.prepareBaremetalInfo(cli)
  303. if err != nil {
  304. logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
  305. return err
  306. }
  307. // set ipmi nic address and user password
  308. if err = task.configIPMISetting(ctx, cli, infos); err != nil {
  309. logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
  310. return errors.Wrap(err, "Config IPMI setting")
  311. }
  312. if err = task.updateBmInfo(ctx, cli, infos); err != nil {
  313. logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
  314. return err
  315. }
  316. // set NTP
  317. if err = task.baremetal.DoNTPConfig(); err != nil {
  318. // ignore error
  319. log.Errorf("SetNTP fail: %s", err)
  320. }
  321. if err = AdjustUEFIBootOrder(ctx, cli, task.baremetal); err != nil {
  322. logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
  323. return errors.Wrap(err, "Adjust UEFI boot order")
  324. }
  325. logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, infos.sysInfo, task.userCred, true)
  326. log.Infof("Prepare complete")
  327. return nil
  328. }
  329. func (task *sBaremetalPrepareTask) findAdminNic(cli *ssh.Client, nicsInfo []*types.SNicDevInfo) (int, *types.SNicDevInfo, error) {
  330. accessIp := cli.GetConfig().Host
  331. for idx := range nicsInfo {
  332. nic := nicsInfo[idx]
  333. output, err := cli.Run("/sbin/ifconfig " + nic.Dev)
  334. if err != nil {
  335. log.Errorf("ifconfig %s fail: %s", nic.Dev, err)
  336. continue
  337. }
  338. isAdmin := false
  339. for _, l := range output {
  340. if strings.Contains(l, accessIp) {
  341. isAdmin = true
  342. break
  343. }
  344. }
  345. if isAdmin {
  346. return idx, nic, nil
  347. }
  348. }
  349. return -1, nil, errors.Error("admin nic not found???")
  350. }
  351. func (task *sBaremetalPrepareTask) doUpdateBmInfo(
  352. ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo,
  353. bmName, accessIp, accessMac string,
  354. ) error {
  355. updateInfo := make(map[string]interface{})
  356. if bmName != "" {
  357. updateInfo["name"] = bmName
  358. }
  359. updateInfo["access_ip"] = accessIp
  360. if accessMac != "" {
  361. updateInfo["access_mac"] = accessMac
  362. }
  363. updateInfo["cpu_architecture"] = i.architecture
  364. updateInfo["cpu_count"] = i.cpuInfo.Count
  365. updateInfo["node_count"] = i.dmiCpuInfo.Nodes
  366. updateInfo["cpu_desc"] = i.cpuInfo.Model
  367. updateInfo["cpu_mhz"] = i.cpuInfo.Freq
  368. updateInfo["cpu_cache"] = i.cpuInfo.Cache
  369. updateInfo["mem_size"] = i.memInfo.Total
  370. updateInfo["storage_driver"] = i.storageDriver
  371. updateInfo["storage_info"] = i.diskInfo
  372. updateInfo["sys_info"] = i.sysInfo
  373. updateInfo["sn"] = i.sysInfo.SN
  374. size, diskType := task.collectDiskInfo(i.diskInfo)
  375. updateInfo["storage_size"] = size
  376. updateInfo["storage_type"] = diskType
  377. updateInfo["boot_mode"] = getBootMode(cli)
  378. updateInfo["uuid"] = getSystemGuid(cli)
  379. updateData := jsonutils.Marshal(updateInfo)
  380. updateData.(*jsonutils.JSONDict).Update(i.ipmiInfo.ToPrepareParams())
  381. _, err := modules.Hosts.Update(task.getClientSession(), task.baremetal.GetId(), updateData)
  382. if err != nil {
  383. log.Errorf("Update baremetal info error: %v", err)
  384. return errors.Wrap(err, "Hosts.Update")
  385. }
  386. if err := task.sendStorageInfo(size); err != nil {
  387. log.Errorf("sendStorageInfo error: %v", err)
  388. return errors.Wrap(err, "task.sendStorageInfo")
  389. }
  390. if len(i.isolatedDevicesInfo) > 0 {
  391. err = task.sendIsolatedDevicesInfo(task.getClientSession(), i.isolatedDevicesInfo)
  392. if err != nil {
  393. return errors.Wrap(err, "send isolated devices info")
  394. }
  395. }
  396. removedMacs := task.removeObsoleteNics(i)
  397. for idx := range removedMacs {
  398. err = task.removeNicInfo(ctx, removedMacs[idx])
  399. if err != nil {
  400. log.Errorf("Fail to remove Netif %s: %s", removedMacs[idx], err)
  401. return errors.Wrap(err, "task.removeNicInfo")
  402. }
  403. }
  404. for idx := range i.nicsInfo {
  405. err = task.sendNicInfo(ctx, i.nicsInfo[idx], idx, "", false, "", false)
  406. if err != nil {
  407. log.Errorf("Send nicinfo idx: %d, %#v error: %v", idx, i.nicsInfo[idx], err)
  408. return errors.Wrap(err, "task.sendNicInfo")
  409. }
  410. }
  411. return nil
  412. }
  413. func (task *sBaremetalPrepareTask) updateBmInfo(ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo) error {
  414. adminNic := task.baremetal.GetAdminNic()
  415. if adminNic == nil || (adminNic != nil && !adminNic.LinkUp) {
  416. adminIdx, adminNicDev, err := task.findAdminNic(cli, i.nicsInfo)
  417. if err != nil {
  418. return errors.Wrap(err, "task.findAdminNic")
  419. }
  420. accessIp := cli.GetConfig().Host
  421. err = task.sendNicInfo(ctx, adminNicDev, adminIdx, api.NIC_TYPE_ADMIN, false, accessIp, true)
  422. if err != nil {
  423. return errors.Wrap(err, "send Admin Nic Info")
  424. }
  425. adminNic = task.baremetal.GetNicByMac(adminNicDev.Mac)
  426. }
  427. oname := fmt.Sprintf("BM%s", strings.Replace(adminNic.Mac, ":", "", -1))
  428. if task.baremetal.GetName() != oname {
  429. oname = ""
  430. }
  431. err := task.doUpdateBmInfo(ctx, cli, i, oname, adminNic.IpAddr, adminNic.Mac)
  432. if err != nil {
  433. return err
  434. }
  435. if o.Options.EnablePxeBoot && task.baremetal.EnablePxeBoot() {
  436. for _, nicInfo := range i.nicsInfo {
  437. if nicInfo.Mac.String() != adminNic.GetMac().String() && nicInfo.Up != nil && *nicInfo.Up {
  438. err = task.doNicWireProbe(cli, nicInfo)
  439. if err != nil {
  440. // ignore the error
  441. log.Errorf("doNicWireProbe nic %#v error: %v", nicInfo, err)
  442. }
  443. }
  444. }
  445. }
  446. return nil
  447. }
  448. func (task *sBaremetalPrepareTask) removeObsoleteNics(i *baremetalPrepareInfo) []string {
  449. removes := make([]string, 0)
  450. existNics := task.baremetal.GetNics()
  451. log.Debugf("Existing nics: %s", jsonutils.Marshal(existNics))
  452. for idx := range existNics {
  453. if existNics[idx].Type != api.NIC_TYPE_NORMAL {
  454. continue
  455. }
  456. find := false
  457. for j := range i.nicsInfo {
  458. if existNics[idx].Mac == i.nicsInfo[j].Mac.String() {
  459. find = true
  460. break
  461. }
  462. }
  463. if !find {
  464. removes = append(removes, existNics[idx].Mac)
  465. }
  466. }
  467. return removes
  468. }
  469. func (task *sBaremetalPrepareTask) tryLocalIpmiAddr(ctx context.Context, sshIPMI *ipmitool.SSHIPMI, ipmiNic *types.SNicDevInfo, lanChannel uint8, ipmiUser, ipmiPasswd, tryAddr string) bool {
  470. log.Infof("IP addr found in IPMI config, try use %s as IPMI address", tryAddr)
  471. ipConf, err := task.getIPMIIPConfig(tryAddr)
  472. if err != nil {
  473. log.Errorf("Failed to get IPMI ipconfig for %s", tryAddr)
  474. return false
  475. }
  476. err = ipmitool.SetLanStatic(sshIPMI, lanChannel, ipConf.IPAddr, ipConf.Netmask, ipConf.Gateway)
  477. if err != nil {
  478. log.Errorf("Failed to set IPMI static net config %#v for %s", *ipConf, tryAddr)
  479. return false
  480. }
  481. var conf *types.SIPMILanConfig
  482. tried := 0
  483. maxTries := 5
  484. time.Sleep(2 * time.Second)
  485. for tried = 0; tried < maxTries; tried += 1 {
  486. conf, err = ipmitool.GetLanConfig(sshIPMI, lanChannel)
  487. if err != nil {
  488. log.Errorf("Failed to get lan config after set static network: %v", err)
  489. continue
  490. }
  491. log.Infof("Get lan config %#v", *conf)
  492. if conf.IPAddr == "" || conf.IPAddr != tryAddr {
  493. log.Errorf("Failed to set ipmi lan channel %d static ipaddr", lanChannel)
  494. continue
  495. }
  496. break
  497. }
  498. if tried >= maxTries {
  499. log.Errorf("Failed to get lan config after %d tries", tried)
  500. return false
  501. }
  502. rmcpIPMI := ipmitool.NewLanPlusIPMI(tryAddr, ipmiUser, ipmiPasswd)
  503. for tried = 0; tried < maxTries; tried += 1 {
  504. conf2, err := ipmitool.GetLanConfig(rmcpIPMI, lanChannel)
  505. if err != nil {
  506. log.Errorf("Failed to get lan channel %d config use RMCP mode: %v", lanChannel, err)
  507. continue
  508. }
  509. if len(conf2.Mac) != 0 &&
  510. conf2.Mac.String() == conf.Mac.String() &&
  511. conf2.IPAddr != "" && conf2.IPAddr == tryAddr {
  512. break
  513. } else {
  514. log.Errorf("fail to rmcp get IPMI ip config %v", conf2)
  515. time.Sleep(5 * time.Second)
  516. }
  517. }
  518. if tried < maxTries {
  519. // make sure the ipaddr is a IPMI address
  520. // enable the netif
  521. err := task.sendNicInfo(ctx, ipmiNic, -1, api.NIC_TYPE_IPMI, false, tryAddr, true)
  522. if err != nil {
  523. log.Errorf("Fail to set existing BMC IP address to %s", tryAddr)
  524. } else {
  525. return true
  526. }
  527. }
  528. return false
  529. }
  530. func (task *sBaremetalPrepareTask) getIPMIUserPasswd(oldIPMIConf *types.SIPMIInfo, profile *baremetalapi.BaremetalProfileSpec) (string, string, string) {
  531. var (
  532. ipmiUser string
  533. ipmiPasswd string
  534. ipmiIpAddr string
  535. )
  536. ipmiUser = profile.RootName
  537. isStrongPass := profile.StrongPass
  538. if !isStrongPass && o.Options.DefaultIpmiPassword != "" {
  539. ipmiPasswd = o.Options.DefaultIpmiPassword
  540. } else if isStrongPass && o.Options.DefaultStrongIpmiPassword != "" {
  541. ipmiPasswd = o.Options.DefaultStrongIpmiPassword
  542. } else if isStrongPass && o.Options.DefaultIpmiPassword != "" {
  543. ipmiPasswd = o.Options.DefaultIpmiPassword
  544. } else {
  545. ipmiPasswd = seclib.RandomPassword(20)
  546. }
  547. if oldIPMIConf.Username != "" {
  548. ipmiUser = oldIPMIConf.Username
  549. }
  550. if oldIPMIConf.Password != "" {
  551. ipmiPasswd = oldIPMIConf.Password
  552. }
  553. if oldIPMIConf.IpAddr != "" {
  554. ipmiIpAddr = oldIPMIConf.IpAddr
  555. }
  556. return ipmiUser, ipmiPasswd, ipmiIpAddr
  557. }
  558. type ipmiIPConfig struct {
  559. IPAddr string
  560. Netmask string
  561. Gateway string
  562. }
  563. func (task *sBaremetalPrepareTask) getIPMIIPConfig(ipAddr string) (*ipmiIPConfig, error) {
  564. params := jsonutils.NewDict()
  565. params.Add(jsonutils.NewString(ipAddr), "ip")
  566. params.Add(jsonutils.NewString("system"), "scope")
  567. params.Add(jsonutils.JSONTrue, "is_classic")
  568. listRet, err := modules.Networks.List(task.getClientSession(), params)
  569. if err != nil {
  570. return nil, err
  571. }
  572. if len(listRet.Data) != 1 {
  573. return nil, fmt.Errorf("Invalid network list count: %d", len(listRet.Data))
  574. }
  575. netObj := listRet.Data[0]
  576. config := &ipmiIPConfig{}
  577. config.IPAddr = ipAddr
  578. maskLen, _ := netObj.Int("guest_ip_mask")
  579. config.Netmask = netutils.Masklen2Mask(int8(maskLen)).String()
  580. config.Gateway, _ = netObj.GetString("guest_gateway")
  581. return config, nil
  582. }
  583. func (task *sBaremetalPrepareTask) getClientSession() *mcclient.ClientSession {
  584. return task.baremetal.GetClientSession()
  585. }
  586. func getArchitecture(cli *ssh.Client) (string, error) {
  587. ret, err := cli.Run("/bin/uname -m")
  588. if err != nil {
  589. return "", err
  590. }
  591. if len(ret) == 0 {
  592. return "", errors.Errorf("/bin/uname not output")
  593. }
  594. arch := strings.ToLower(ret[0])
  595. switch arch {
  596. case apis.OS_ARCH_AARCH64:
  597. return apis.OS_ARCH_AARCH64, nil
  598. case apis.OS_ARCH_X86_64:
  599. return apis.OS_ARCH_X86_64, nil
  600. default:
  601. return arch, nil
  602. }
  603. }
  604. func getDMISysinfo(cli *ssh.Client) (*types.SSystemInfo, error) {
  605. ret, err := cli.Run("/usr/sbin/dmidecode -t 1")
  606. if err != nil {
  607. return nil, err
  608. }
  609. return sysutils.ParseDMISysinfo(ret)
  610. }
  611. func getCPUInfo(cli *ssh.Client) (*types.SCPUInfo, error) {
  612. ret, err := cli.Run("cat /proc/cpuinfo")
  613. if err != nil {
  614. return nil, err
  615. }
  616. return sysutils.ParseCPUInfo(ret)
  617. }
  618. func getBootMode(cli *ssh.Client) string {
  619. lines, err := cli.Run("cat /proc/cmdline")
  620. if err != nil {
  621. return api.BOOT_MODE_PXE
  622. }
  623. const (
  624. key = "bootmode="
  625. )
  626. for _, line := range lines {
  627. pos := strings.Index(line, key)
  628. if pos >= 0 {
  629. if strings.HasPrefix(line[pos+len(key):], api.BOOT_MODE_ISO) {
  630. return api.BOOT_MODE_ISO
  631. }
  632. }
  633. }
  634. return api.BOOT_MODE_PXE
  635. }
  636. func getSystemGuid(cli *ssh.Client) string {
  637. lines, err := cli.Run("/usr/sbin/dmidecode -s system-uuid")
  638. if err != nil {
  639. return ""
  640. }
  641. for _, line := range lines {
  642. line = strings.TrimSpace(line)
  643. if len(line) > 0 {
  644. return sysutils.NormalizeUuid(line)
  645. }
  646. }
  647. return ""
  648. }
  649. func getDMICPUInfo(cli *ssh.Client) (*types.SDMICPUInfo, error) {
  650. ret, err := cli.Run("/usr/sbin/dmidecode -t 4")
  651. if err != nil {
  652. return nil, err
  653. }
  654. return sysutils.ParseDMICPUInfo(ret), nil
  655. }
  656. func getDMIMemInfo(cli *ssh.Client) (*types.SDMIMemInfo, error) {
  657. ret, err := cli.Run("/usr/sbin/dmidecode -t 17")
  658. if err != nil {
  659. return nil, err
  660. }
  661. return sysutils.ParseDMIMemInfo(ret), nil
  662. }
  663. func GetNicsInfo(cli *ssh.Client) ([]*types.SNicDevInfo, error) {
  664. return getNicsInfo(cli)
  665. }
  666. func getNicsInfo(cli *ssh.Client) ([]*types.SNicDevInfo, error) {
  667. ret, err := cli.Run("/lib/mos/lsnic")
  668. if err != nil {
  669. return nil, fmt.Errorf("Failed to retrieve NIC info: %v", err)
  670. }
  671. return sysutils.ParseNicInfo(ret), nil
  672. }
  673. func getIsolatedDevicesInfo(cli *ssh.Client, ip net.IP) ([]*isolated_device.PCIDevice, error) {
  674. // fetch pci.ids from baremetal agent
  675. var updatedPciids bool
  676. if ip != nil {
  677. out, err := cli.Run(fmt.Sprintf("tftp -g -r pci.ids %s -l /pci.ids", ip))
  678. if err != nil {
  679. log.Errorf("tftp failed download pciids %s %s", err, out)
  680. } else {
  681. updatedPciids = true
  682. }
  683. }
  684. var bootVgaPath = []string{}
  685. lines, err := cli.Run("ls /sys/bus/pci/devices/*/boot_vga")
  686. if err != nil {
  687. log.Errorf("failed find boot vga %s", err)
  688. }
  689. for i := 0; i < len(lines); i++ {
  690. bootVgaPath = append(bootVgaPath, strings.TrimSpace(lines[i]))
  691. }
  692. cmd := "lspci -nnmm"
  693. if updatedPciids {
  694. cmd = "lspci -i /pci.ids -nnmm"
  695. }
  696. lines, err = cli.Run(cmd)
  697. if err != nil {
  698. return nil, errors.Wrapf(err, "run %s", cmd)
  699. }
  700. devs := []*isolated_device.PCIDevice{}
  701. for _, line := range lines {
  702. if len(line) > 0 {
  703. dev := isolated_device.NewPCIDevice2(line, cli)
  704. if len(dev.Addr) > 0 && utils.IsInArray(dev.ClassCode, isolated_device.GpuClassCodes) && !isBootVga(cli, dev, bootVgaPath) {
  705. devs = append(devs, dev)
  706. }
  707. }
  708. }
  709. return devs, nil
  710. }
  711. func isBootVga(cli *ssh.Client, dev *isolated_device.PCIDevice, bootVgaPath []string) bool {
  712. for i := 0; i < len(bootVgaPath); i++ {
  713. if strings.Contains(bootVgaPath[i], dev.Addr) {
  714. out, err := cli.RawRun(fmt.Sprintf("cat %s", bootVgaPath[i]))
  715. if err != nil {
  716. log.Errorf("cat boot_vga %s failed %s", bootVgaPath[i], err)
  717. return false
  718. } else if len(out) > 0 {
  719. if strings.HasPrefix(out[0], "1") {
  720. log.Infof("device %s is boot vga", bootVgaPath[i])
  721. return true
  722. } else {
  723. return false
  724. }
  725. }
  726. break
  727. }
  728. }
  729. return false
  730. }
  731. func isIPMIEnable(cli *ssh.Client) (bool, error) {
  732. ret, err := cli.Run("/usr/sbin/dmidecode -t 38")
  733. if err != nil {
  734. return false, fmt.Errorf("Failed to retrieve IPMI info: %v", err)
  735. }
  736. return sysutils.ParseDMIIPMIInfo(ret), nil
  737. }
  738. func (task *sBaremetalPrepareTask) removeNicInfo(ctx context.Context, mac string) error {
  739. params := jsonutils.NewDict()
  740. params.Add(jsonutils.NewString(mac), "mac")
  741. resp, err := modules.Hosts.PerformAction(
  742. task.getClientSession(),
  743. task.baremetal.GetId(),
  744. "remove-netif",
  745. params,
  746. )
  747. if err != nil {
  748. return err
  749. }
  750. return task.baremetal.SaveDesc(ctx, resp)
  751. }
  752. func (task *sBaremetalPrepareTask) sendNicInfo(ctx context.Context,
  753. nic *types.SNicDevInfo, idx int, nicType compute.TNicType, reset bool, ipAddr string, reserve bool,
  754. ) error {
  755. return task.baremetal.SendNicInfo(ctx, nic, idx, nicType, reset, ipAddr, reserve)
  756. }
  757. func (task *sBaremetalPrepareTask) sendStorageInfo(size int64) error {
  758. params := jsonutils.NewDict()
  759. params.Add(jsonutils.NewInt(size), "capacity")
  760. params.Add(jsonutils.NewString(task.baremetal.GetZoneId()), "zone_id")
  761. params.Add(jsonutils.NewString(task.baremetal.GetStorageCacheId()), "storagecache_id")
  762. _, err := modules.Hosts.PerformAction(task.getClientSession(), task.baremetal.GetId(), "update-storage", params)
  763. return err
  764. }
  765. func (task *sBaremetalPrepareTask) getCloudIsolatedDevices(
  766. session *mcclient.ClientSession,
  767. ) ([]jsonutils.JSONObject, error) {
  768. params := jsonutils.NewDict()
  769. params.Set("details", jsonutils.JSONTrue)
  770. params.Set("limit", jsonutils.NewInt(0))
  771. params.Set("host", jsonutils.NewString(task.baremetal.GetId()))
  772. params.Set("scope", jsonutils.NewString("system"))
  773. params.Set("show_baremetal_isolated_devices", jsonutils.JSONTrue)
  774. res, err := modules.IsolatedDevices.List(session, params)
  775. if err != nil {
  776. return nil, err
  777. }
  778. return res.Data, nil
  779. }
  780. func (task *sBaremetalPrepareTask) sendIsolatedDevicesInfo(
  781. session *mcclient.ClientSession, devs []*isolated_device.PCIDevice,
  782. ) error {
  783. objs, err := task.getCloudIsolatedDevices(session)
  784. if err != nil {
  785. return errors.Wrap(err, "get cloud isolated devices")
  786. }
  787. gpuDevs := make([]isolated_device.IDevice, len(devs))
  788. for i := 0; i < len(devs); i++ {
  789. gpuDevs[i] = isolated_device.NewGPUHPCDevice(devs[i])
  790. }
  791. for _, obj := range objs {
  792. var notFound = true
  793. info := isolated_device.CloudDeviceInfo{}
  794. if err := obj.Unmarshal(&info); err != nil {
  795. return errors.Wrap(err, "unmarshal isolated device to cloud device info failed")
  796. }
  797. for i := 0; i < len(gpuDevs); i++ {
  798. if gpuDevs[i].GetAddr() == info.Addr && gpuDevs[i].GetVendorDeviceId() == info.VendorDeviceId {
  799. gpuDevs[i].SetDeviceInfo(info)
  800. notFound = false
  801. break
  802. }
  803. }
  804. if notFound {
  805. _, err := modules.IsolatedDevices.PerformAction(session, info.Id, "purge", nil)
  806. if err != nil {
  807. return errors.Wrap(err, "purge unknown isolated devices")
  808. }
  809. }
  810. }
  811. for i := 0; i < len(gpuDevs); i++ {
  812. if _, err := isolated_device.SyncDeviceInfo(session, task.baremetal.GetId(), gpuDevs[i], true); err != nil {
  813. return errors.Wrap(err, "sync device info")
  814. }
  815. }
  816. return nil
  817. }
  818. func (task *sBaremetalPrepareTask) doNicWireProbe(cli *ssh.Client, nic *types.SNicDevInfo) error {
  819. maxTries := 6
  820. for tried := 0; tried < maxTries; tried++ {
  821. log.Infof("doNicWireProbe %v", nic)
  822. _, err := cli.Run(fmt.Sprintf("/sbin/udhcpc -t 1 -T 3 -n -i %s", nic.Dev))
  823. if err != nil {
  824. log.Errorf("/sbin/udhcpc error: %v", err)
  825. }
  826. nicInfo := task.baremetal.GetNicByMac(nic.Mac)
  827. if nicInfo != nil && nicInfo.WireId != "" {
  828. log.Infof("doNicWireProbe success, get result %#v", nicInfo)
  829. break
  830. }
  831. }
  832. return nil
  833. }
  834. func (task *sBaremetalPrepareTask) collectDiskInfo(diskInfo []*baremetal.BaremetalStorage) (int64, string) {
  835. cnt := 0
  836. rotateCnt := 0
  837. var size int64 = 0
  838. var diskType string
  839. for _, d := range diskInfo {
  840. if d.Rotate {
  841. rotateCnt += 1
  842. }
  843. size += d.Size
  844. cnt += 1
  845. }
  846. if rotateCnt == cnt {
  847. diskType = "rotate"
  848. } else if rotateCnt == 0 {
  849. diskType = "ssd"
  850. } else {
  851. diskType = "hybrid"
  852. }
  853. return size, diskType
  854. }
  855. func setIPMILanPortShared(cli ipmitool.IPMIExecutor, sysInfo *types.SSystemInfo) {
  856. if !o.Options.IpmiLanPortShared {
  857. return
  858. }
  859. oemName := strings.ToLower(sysInfo.Manufacture)
  860. var err error
  861. switch sysInfo.OemName {
  862. case types.OEM_NAME_HUAWEI:
  863. err = ipmitool.SetHuaweiIPMILanPortShared(cli)
  864. case types.OEM_NAME_DELL:
  865. err = ipmitool.SetDellIPMILanPortShared(cli)
  866. }
  867. if err != nil {
  868. log.Errorf("Set %s ipmi lan port shared failed: %v", oemName, err)
  869. }
  870. }