ipmiprobe.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. "net"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. api "yunion.io/x/onecloud/pkg/apis/compute"
  22. baremetalapi "yunion.io/x/onecloud/pkg/apis/compute/baremetal"
  23. o "yunion.io/x/onecloud/pkg/baremetal/options"
  24. "yunion.io/x/onecloud/pkg/baremetal/profiles"
  25. "yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
  26. "yunion.io/x/onecloud/pkg/cloudcommon/types"
  27. "yunion.io/x/onecloud/pkg/httperrors"
  28. "yunion.io/x/onecloud/pkg/mcclient"
  29. modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
  30. "yunion.io/x/onecloud/pkg/util/redfish"
  31. )
  32. type SBaremetalIpmiProbeTask struct {
  33. SBaremetalTaskBase
  34. }
  35. func NewBaremetalIpmiProbeTask(
  36. userCred mcclient.TokenCredential,
  37. baremetal IBaremetal,
  38. taskId string,
  39. data jsonutils.JSONObject,
  40. ) ITask {
  41. task := &SBaremetalIpmiProbeTask{
  42. SBaremetalTaskBase: newBaremetalTaskBase(userCred, baremetal, taskId, data),
  43. }
  44. task.SetVirtualObject(task)
  45. task.SetStage(task.DoIpmiProbe)
  46. return task
  47. }
  48. func (self *SBaremetalIpmiProbeTask) GetName() string {
  49. return "BaremetalIpmiProbeTask"
  50. }
  51. func (self *SBaremetalIpmiProbeTask) DoIpmiProbe(ctx context.Context, args interface{}) error {
  52. ipmiInfo := self.Baremetal.GetRawIPMIConfig()
  53. if ipmiInfo == nil {
  54. ipmiInfo = &types.SIPMIInfo{}
  55. }
  56. if ipmiInfo.IpAddr == "" {
  57. return errors.Error("empty IPMI ip_addr")
  58. }
  59. if ipmiInfo.Username == "" {
  60. return errors.Error("empty IPMI username")
  61. }
  62. if ipmiInfo.Password == "" {
  63. return errors.Error("empty IPMI password")
  64. }
  65. // FIXME: 通过 redfish 探测出来的管理口网卡地址可能和 PXE 启动的网卡地址不一致
  66. // 所以暂时注释掉这个探测逻辑,使用 doRawIpmiProbe
  67. /* redfishCli := redfish.NewRedfishDriver(ctx, "https://"+ipmiInfo.IpAddr, ipmiInfo.Username, ipmiInfo.Password, false)
  68. if redfishCli != nil {
  69. redfishSuccess, err := self.doRedfishIpmiProbe(ctx, redfishCli)
  70. if err == nil {
  71. // success
  72. return nil
  73. }
  74. if redfishSuccess {
  75. return errors.Wrap(err, "doRedfishIpmiProbe")
  76. }
  77. // else, redfish call fails, try IPMI
  78. } */
  79. log.Warningf("BMC not redfish-compatible for IPMI: %s, use raw probe", ipmiInfo.IpAddr)
  80. ipmiTool := ipmitool.NewLanPlusIPMI(ipmiInfo.IpAddr, ipmiInfo.Username, ipmiInfo.Password)
  81. return self.doRawIpmiProbe(ctx, ipmiTool)
  82. }
  83. // return redfishSuccess, error
  84. // redfishSuccess: does Redfish API call success
  85. func (self *SBaremetalIpmiProbeTask) doRedfishIpmiProbe(ctx context.Context, drv redfish.IRedfishDriver) (bool, error) {
  86. confs, err := drv.GetLanConfigs(ctx)
  87. if err != nil {
  88. return false, errors.Wrap(err, "drv.GetLanConfigs")
  89. }
  90. if len(confs) == 0 {
  91. return false, errors.Wrap(httperrors.ErrNotFound, "no IPMI lan")
  92. }
  93. err = self.sendIpmiNicInfo(ctx, &confs[0])
  94. if err != nil {
  95. return false, errors.Wrap(err, "self.sendIpmiNicInfo")
  96. }
  97. _, sysInfo, err := drv.GetSystemInfo(ctx)
  98. if err != nil {
  99. return false, errors.Wrap(err, "drv.GetSystemInfo")
  100. }
  101. updateInfo := make(map[string]interface{})
  102. if len(sysInfo.EthernetNICs) > 0 {
  103. updateInfo["access_mac"] = sysInfo.EthernetNICs[0]
  104. }
  105. updateInfo["node_count"] = sysInfo.NodeCount
  106. updateInfo["cpu_count"] = sysInfo.NodeCount
  107. updateInfo["cpu_desc"] = sysInfo.CpuDesc
  108. updateInfo["mem_size"] = sysInfo.MemoryGB * 1024
  109. updateInfo["sn"] = sysInfo.SerialNumber
  110. updateInfo["uuid"] = sysInfo.UUID
  111. dmiSysInfo := &types.SSystemInfo{
  112. Manufacture: sysInfo.Manufacturer,
  113. Model: sysInfo.Model,
  114. SN: sysInfo.SerialNumber,
  115. OemName: types.ManufactureOemName(sysInfo.Manufacturer),
  116. }
  117. updateInfo["sys_info"] = dmiSysInfo
  118. updateInfo["is_baremetal"] = true
  119. ipmiInfo := self.Baremetal.GetRawIPMIConfig()
  120. if ipmiInfo == nil {
  121. ipmiInfo = &types.SIPMIInfo{}
  122. }
  123. ipmiInfo.Present = true
  124. ipmiInfo.Verified = true
  125. ipmiInfo.RedfishApi = true
  126. _, cdInfo, _ := drv.GetVirtualCdromInfo(ctx)
  127. ipmiInfo.CdromBoot = cdInfo.SupportAction
  128. ipmiInfo.PxeBoot = o.Options.EnablePxeBoot
  129. updateData := jsonutils.Marshal(updateInfo)
  130. updateData.(*jsonutils.JSONDict).Update(ipmiInfo.ToPrepareParams())
  131. _, err = modules.Hosts.Update(self.Baremetal.GetClientSession(), self.Baremetal.GetId(), updateData)
  132. if err != nil {
  133. log.Errorf("Update baremetal info error: %v", err)
  134. return true, errors.Wrap(err, "modules.Hosts.Update")
  135. }
  136. for i := range sysInfo.EthernetNICs {
  137. mac, err := net.ParseMAC(sysInfo.EthernetNICs[i])
  138. if err == nil {
  139. err = self.sendNicInfo(ctx, i, mac)
  140. if err != nil {
  141. return true, errors.Wrapf(err, "sendNicInfo %d %s", i, mac)
  142. }
  143. }
  144. }
  145. self.Baremetal.SyncStatus(ctx, "", "Probe Redfish finished")
  146. SetTaskComplete(self, nil)
  147. return true, nil
  148. }
  149. func (self *SBaremetalIpmiProbeTask) sendIpmiNicInfo(ctx context.Context, lanConf *types.SIPMILanConfig) error {
  150. speed := lanConf.SpeedMbps
  151. if speed <= 0 {
  152. speed = 100
  153. }
  154. up := true
  155. ipmiNic := &types.SNicDevInfo{
  156. Mac: lanConf.Mac,
  157. Up: &up,
  158. Speed: speed,
  159. Mtu: 1500,
  160. }
  161. err := self.Baremetal.SendNicInfo(ctx, ipmiNic, -1, api.NIC_TYPE_IPMI, true, lanConf.IPAddr, true)
  162. if err != nil {
  163. return errors.Wrap(err, "SendNicInfo")
  164. }
  165. return nil
  166. }
  167. func (self *SBaremetalIpmiProbeTask) sendNicInfo(ctx context.Context, index int, mac net.HardwareAddr) error {
  168. nicInfo := &types.SNicDevInfo{
  169. Mac: mac,
  170. }
  171. err := self.Baremetal.SendNicInfo(ctx, nicInfo, index, "", false, "", false)
  172. if err != nil {
  173. return errors.Wrap(err, "SendNicInfo")
  174. }
  175. return nil
  176. }
  177. func (self *SBaremetalIpmiProbeTask) doRawIpmiProbe(ctx context.Context, cli ipmitool.IPMIExecutor) error {
  178. sysInfo, err := ipmitool.GetSysInfo(cli)
  179. var profile *baremetalapi.BaremetalProfileSpec
  180. if err != nil {
  181. // ignore error for qemu
  182. log.Errorf("ipmitool.GetSysInfo error %s", err)
  183. } else {
  184. profile, err = profiles.GetProfile(ctx, sysInfo)
  185. if err != nil {
  186. return errors.Wrap(err, "GetProfile")
  187. }
  188. }
  189. guid := ipmitool.GetSysGuid(cli)
  190. var conf *types.SIPMILanConfig
  191. var channel uint8
  192. var errs []error
  193. for _, lanChannel := range profile.LanChannels {
  194. conf, err = ipmitool.GetLanConfig(cli, lanChannel)
  195. if err != nil {
  196. // ignore error
  197. err := errors.Wrapf(err, "ipmitool.GetLanConfig for channel %d failed", lanChannel)
  198. errs = append(errs, err)
  199. log.Warningf("%s", err.Error())
  200. } else if conf.IPAddr == "0.0.0.0" {
  201. err := errors.Errorf("get 0.0.0.0 ip address of channel %d", lanChannel)
  202. errs = append(errs, err)
  203. log.Warningf("%s", err.Error())
  204. continue
  205. } else {
  206. channel = lanChannel
  207. break
  208. }
  209. }
  210. if conf == nil {
  211. return errors.Wrapf(httperrors.ErrNotFound, "no IPMI lan: %s", errors.NewAggregate(errs).Error())
  212. }
  213. err = self.sendIpmiNicInfo(ctx, conf)
  214. if err != nil {
  215. return errors.Wrap(err, "self.sendIpmiNicInfo")
  216. }
  217. updateInfo := make(map[string]interface{})
  218. if len(sysInfo.SN) > 0 {
  219. updateInfo["sn"] = sysInfo.SN
  220. dmiSysInfo := &types.SSystemInfo{
  221. Manufacture: sysInfo.Manufacture,
  222. Model: sysInfo.Model,
  223. Version: sysInfo.Version,
  224. SN: sysInfo.SN,
  225. OemName: types.ManufactureOemName(sysInfo.Manufacture),
  226. }
  227. updateInfo["sys_info"] = dmiSysInfo
  228. }
  229. // XXX
  230. // Qemu's IPMI guid is not correct, just ignore it
  231. if len(guid) > 0 && sysInfo.SN != "" && sysInfo.SN != "Not Specified" {
  232. updateInfo["uuid"] = guid
  233. }
  234. updateInfo["is_baremetal"] = true
  235. ipmiInfo := self.Baremetal.GetRawIPMIConfig()
  236. if ipmiInfo == nil {
  237. ipmiInfo = &types.SIPMIInfo{}
  238. }
  239. ipmiInfo.Present = true
  240. ipmiInfo.Verified = true
  241. ipmiInfo.RedfishApi = false
  242. ipmiInfo.CdromBoot = false
  243. ipmiInfo.PxeBoot = o.Options.EnablePxeBoot
  244. ipmiInfo.LanChannel = channel
  245. updateData := jsonutils.Marshal(updateInfo)
  246. updateData.(*jsonutils.JSONDict).Update(ipmiInfo.ToPrepareParams())
  247. _, err = modules.Hosts.Update(self.Baremetal.GetClientSession(), self.Baremetal.GetId(), updateData)
  248. if err != nil {
  249. return errors.Wrap(err, "modules.Hosts.Update")
  250. }
  251. self.Baremetal.SyncStatus(ctx, "", "Probie IPMI finished")
  252. SetTaskComplete(self, nil)
  253. return nil
  254. }