| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package tasks
- import (
- "context"
- "fmt"
- "net"
- "strings"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- api "yunion.io/x/onecloud/pkg/apis/compute"
- o "yunion.io/x/onecloud/pkg/baremetal/options"
- "yunion.io/x/onecloud/pkg/baremetal/profiles"
- "yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
- "yunion.io/x/onecloud/pkg/cloudcommon/types"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/mcclient/auth"
- modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
- "yunion.io/x/onecloud/pkg/util/redfish"
- "yunion.io/x/onecloud/pkg/util/ssh"
- "yunion.io/x/onecloud/pkg/util/sysutils"
- )
- type sBaremetalRegisterTask struct {
- sBaremetalPrepareTask
- BmManager IBmManager
- SshCli *ssh.Client
- Hostname string
- RemoteIp string
- IpmiUsername string
- IpmiPassword string
- IpmiIpAddr string
- IpmiMac net.HardwareAddr
- IpmiLanChannel uint8
- AdminWire string
- IpmiWire string
- accessNic *types.SNicDevInfo
- }
- func NewBaremetalRegisterTask(
- userCred mcclient.TokenCredential, bmManager IBmManager, sshCli *ssh.Client,
- hostname, remoteIp, ipmiUsername, ipmiPassword, ipmiIpAddr string,
- ipmiMac net.HardwareAddr, ipmiLanChannel uint8, adminWire, ipmiWire string) *sBaremetalRegisterTask {
- return &sBaremetalRegisterTask{
- sBaremetalPrepareTask: sBaremetalPrepareTask{userCred: userCred},
- BmManager: bmManager,
- SshCli: sshCli,
- Hostname: hostname,
- RemoteIp: remoteIp,
- IpmiUsername: ipmiUsername,
- IpmiPassword: ipmiPassword,
- IpmiIpAddr: ipmiIpAddr,
- IpmiMac: ipmiMac,
- IpmiLanChannel: ipmiLanChannel,
- AdminWire: adminWire,
- IpmiWire: ipmiWire,
- }
- }
- func (s *sBaremetalRegisterTask) getSession() *mcclient.ClientSession {
- return auth.GetSession(context.Background(), s.userCred, o.Options.Region)
- }
- func (s *sBaremetalRegisterTask) getAccessDevMacAddr(ip string) (string, error) {
- var dev string
- var nicsRet []string
- var err error
- if strings.Contains(ip, ":") { // ipv6
- nicsRet, err = s.SshCli.Run("/sbin/ip -o -6 addr show")
- if err != nil {
- return "", fmt.Errorf("Failed get access nic %s", err)
- }
- } else { // ipv4
- nicsRet, err = s.SshCli.Run("/sbin/ip -o -4 addr show")
- if err != nil {
- return "", fmt.Errorf("Failed get access nic %s", err)
- }
- }
- for i := 0; i < len(nicsRet); i++ {
- if strings.Contains(nicsRet[i], ip+"/") {
- segs := strings.Split(nicsRet[i], " ")
- if len(segs) > 1 {
- dev = segs[1]
- break
- }
- }
- }
- if len(dev) == 0 {
- return "", fmt.Errorf("Can't get access dev")
- }
- log.Infof("Access dev is %s", dev)
- macRet, err := s.SshCli.Run("/sbin/ip a show " + dev)
- if err != nil || len(macRet) < 2 {
- return "", fmt.Errorf("Failed get access nic mac address %s", err)
- }
- segs := strings.Fields(macRet[1])
- if len(segs) < 2 {
- return "", fmt.Errorf("Failed to find mac address")
- }
- return segs[1], nil
- }
- func (s *sBaremetalRegisterTask) CreateBaremetal(ctx context.Context) (string, error) {
- zoneId := s.BmManager.GetZoneId()
- ret, err := s.SshCli.Run("/lib/mos/lsnic")
- if err != nil {
- return "", fmt.Errorf("Register baremeatl failed on lsnic: %s", err)
- }
- accessMac, err := s.getAccessDevMacAddr(s.RemoteIp)
- if err != nil {
- return "", err
- }
- accessMacAddr, err := net.ParseMAC(accessMac)
- if err != nil {
- return "", fmt.Errorf("Failed parse access mac %s", accessMac)
- }
- nicinfo := sysutils.ParseNicInfo(ret)
- for _, nic := range nicinfo {
- if nic.Mac.String() == accessMacAddr.String() {
- s.accessNic = nic
- }
- }
- if s.accessNic == nil {
- s.accessNic = nicinfo[0]
- }
- params := jsonutils.NewDict()
- params.Set("name", jsonutils.NewString(fmt.Sprintf("bm%s", strings.Replace(s.accessNic.Mac.String(), ":", "", -1))))
- params.Set("access_mac", jsonutils.NewString(s.accessNic.Mac.String()))
- params.Set("host_type", jsonutils.NewString("baremetal"))
- params.Set("is_baremetal", jsonutils.JSONTrue)
- params.Set("is_import", jsonutils.JSONTrue)
- res, err := modules.Hosts.CreateInContext(s.getSession(), params, &modules.Zones, zoneId)
- if err != nil {
- return "", fmt.Errorf("Create baremetal failed: %s", err)
- }
- pxeBm, err := s.BmManager.AddBaremetal(ctx, res)
- if err != nil {
- return "", fmt.Errorf("BmManager add baremetal failed: %s", err)
- }
- err = pxeBm.InitAdminNetif(ctx,
- s.accessNic.Mac, s.AdminWire, api.NIC_TYPE_ADMIN, api.NETWORK_TYPE_PXE, true, s.RemoteIp)
- if err != nil {
- return "", fmt.Errorf("BmManager add admin netif failed: %s", err)
- }
- err = pxeBm.InitAdminNetif(ctx,
- s.IpmiMac, s.IpmiWire, api.NIC_TYPE_IPMI, api.NETWORK_TYPE_IPMI, true, s.IpmiIpAddr)
- if err != nil {
- return "", fmt.Errorf("BmManager add ipmi netif failed: %s", err)
- }
- for _, nic := range nicinfo {
- if nic.Dev != s.accessNic.Dev {
- pxeBm.RegisterNetif(ctx, nic.Mac, s.AdminWire)
- }
- }
- s.baremetal = pxeBm.(IBaremetal)
- bmInstanceId, _ := res.GetString("id")
- return bmInstanceId, nil
- }
- func (s *sBaremetalRegisterTask) UpdateBaremetal(ctx context.Context) (string, error) {
- accessMac, err := s.getAccessDevMacAddr(s.RemoteIp)
- if err != nil {
- return "", errors.Wrap(err, "getAccessDevMacAddr")
- }
- accessMacAddr, err := net.ParseMAC(accessMac)
- if err != nil {
- return "", errors.Wrapf(err, "Failed parse access mac %s", accessMac)
- }
- params := jsonutils.NewDict()
- params.Set("any_mac", jsonutils.NewString(accessMacAddr.String()))
- params.Set("scope", jsonutils.NewString("system"))
- res, err := modules.Hosts.List(s.BmManager.GetClientSession(), params)
- if err != nil {
- return "", errors.Wrap(err, "Fetch baremetal failed")
- }
- if len(res.Data) == 0 {
- return "", errors.Wrapf(errors.ErrNotFound, "Cann't find baremetal by access mac %s", accessMacAddr)
- }
- pxeBm, err := s.BmManager.AddBaremetal(ctx, res.Data[0])
- if err != nil {
- return "", errors.Wrap(err, "BmManager add baremetal failed")
- }
- s.baremetal = pxeBm.(IBaremetal)
- return s.baremetal.GetId(), nil
- }
- func (s *sBaremetalRegisterTask) doRedfishProbe(ctx context.Context) (redfishSupport bool, cdromBoot bool) {
- var endpoint = "https://" + s.IpmiIpAddr
- if strings.Contains(s.IpmiIpAddr, ":") {
- endpoint = fmt.Sprintf("https://[%s]", s.IpmiIpAddr)
- }
- redfishCli := redfish.NewRedfishDriver(ctx, endpoint, s.IpmiUsername, s.IpmiPassword, false)
- if redfishCli != nil {
- _, cdInfo, _ := redfishCli.GetVirtualCdromInfo(ctx)
- redfishSupport = true
- cdromBoot = cdInfo.SupportAction
- }
- return
- }
- func (s *sBaremetalRegisterTask) DoPrepare(ctx context.Context, cli *ssh.Client, registered bool) error {
- infos, err := s.prepareBaremetalInfo(cli)
- if err != nil {
- return err
- }
- redfishSupport, cdromSupport := s.doRedfishProbe(ctx)
- infos.ipmiInfo.IpAddr = s.IpmiIpAddr
- infos.ipmiInfo.Username = s.IpmiUsername
- infos.ipmiInfo.Password = s.IpmiPassword
- infos.ipmiInfo.LanChannel = s.IpmiLanChannel
- infos.ipmiInfo.Verified = true
- infos.ipmiInfo.Present = true
- infos.ipmiInfo.RedfishApi = redfishSupport
- infos.ipmiInfo.CdromBoot = cdromSupport
- s.updateIpmiInfo(ctx, cli)
- return s.updateBmInfo(ctx, cli, infos, registered)
- }
- func (s *sBaremetalRegisterTask) updateIpmiInfo(ctx context.Context, cli *ssh.Client) {
- ipmiTool := ipmitool.NewSSHIPMI(cli)
- sysInfo, err := ipmitool.GetSysInfo(ipmiTool)
- if err == nil && o.Options.IpmiLanPortShared {
- oemName := strings.ToLower(sysInfo.Manufacture)
- if strings.Contains(oemName, "huawei") {
- ipmitool.SetHuaweiIPMILanPortShared(ipmiTool)
- } else if strings.Contains(oemName, "dell") {
- ipmitool.SetDellIPMILanPortShared(ipmiTool)
- }
- }
- up := true
- var nic = &types.SNicDevInfo{
- Up: &up,
- Speed: 100,
- Mtu: 1500,
- }
- var conf *types.SIPMILanConfig
- profile, err := profiles.GetProfile(ctx, sysInfo)
- if profile != nil {
- for _, lanChannel := range profile.LanChannels {
- conf, _ = ipmitool.GetLanConfig(ipmiTool, lanChannel)
- if conf == nil || len(conf.Mac) == 0 {
- continue
- }
- }
- }
- if conf == nil || len(conf.Mac) == 0 {
- log.Errorln("Fail to get IPMI lan config !!!")
- } else {
- nic.Mac = conf.Mac
- }
- s.sendNicInfo(ctx, nic, -1, api.NIC_TYPE_IPMI, false, "", false)
- }
- func (s *sBaremetalRegisterTask) updateBmInfo(ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo, registered bool) error {
- accessMac := ""
- if s.accessNic != nil {
- accessMac = s.accessNic.Mac.String()
- }
- err := s.doUpdateBmInfo(ctx, cli, i, "", s.RemoteIp, accessMac)
- if err != nil {
- log.Errorf("failed do update bminfo %s", err)
- }
- if registered {
- return nil
- }
- return s.initBaremetalServer(ctx)
- }
- func (s *sBaremetalRegisterTask) initBaremetalServer(ctx context.Context) error {
- if err := s.baremetal.InitializeServer(s.getSession(), s.Hostname); err != nil {
- return fmt.Errorf("Baremteal Create Server Failed %s", err)
- }
- // if err := s.baremetal.SaveSSHConfig("", ""); err != nil {
- // log.Errorf("Save ssh config failed %s", err)
- // }
- if err := s.baremetal.ServerLoadDesc(ctx); err != nil {
- log.Errorf("Server load desc failed %s", err)
- }
- s.baremetal.SyncStatus(ctx, "running", "Register success")
- log.Infof("%s Load baremetal info success ...", s.baremetal.GetId())
- return nil
- }
|