mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/yunionio/cloudpods.git
synced 2026-09-20 08:03:53 +08:00
937 lines
27 KiB
Go
937 lines
27 KiB
Go
// 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"
|
|
"time"
|
|
|
|
"yunion.io/x/cloudmux/pkg/apis/compute"
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
"yunion.io/x/pkg/errors"
|
|
"yunion.io/x/pkg/util/netutils"
|
|
"yunion.io/x/pkg/util/seclib"
|
|
"yunion.io/x/pkg/utils"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis"
|
|
api "yunion.io/x/onecloud/pkg/apis/compute"
|
|
baremetalapi "yunion.io/x/onecloud/pkg/apis/compute/baremetal"
|
|
o "yunion.io/x/onecloud/pkg/baremetal/options"
|
|
"yunion.io/x/onecloud/pkg/baremetal/profiles"
|
|
"yunion.io/x/onecloud/pkg/baremetal/utils/detect_storages"
|
|
"yunion.io/x/onecloud/pkg/baremetal/utils/ipmitool"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
|
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
|
"yunion.io/x/onecloud/pkg/hostman/isolated_device"
|
|
"yunion.io/x/onecloud/pkg/mcclient"
|
|
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
|
"yunion.io/x/onecloud/pkg/util/logclient"
|
|
"yunion.io/x/onecloud/pkg/util/ssh"
|
|
"yunion.io/x/onecloud/pkg/util/sysutils"
|
|
)
|
|
|
|
type sBaremetalPrepareTask struct {
|
|
baremetal IBaremetal
|
|
startTime time.Time
|
|
userCred mcclient.TokenCredential
|
|
}
|
|
|
|
func newBaremetalPrepareTask(baremetal IBaremetal, userCred mcclient.TokenCredential) *sBaremetalPrepareTask {
|
|
return &sBaremetalPrepareTask{
|
|
baremetal: baremetal,
|
|
userCred: userCred,
|
|
startTime: time.Now().UTC(),
|
|
}
|
|
}
|
|
|
|
type baremetalPrepareInfo struct {
|
|
architecture string
|
|
sysInfo *types.SSystemInfo
|
|
cpuInfo *types.SCPUInfo
|
|
dmiCpuInfo *types.SDMICPUInfo
|
|
memInfo *types.SDMIMemInfo
|
|
nicsInfo []*types.SNicDevInfo
|
|
diskInfo []*baremetal.BaremetalStorage
|
|
storageDriver string
|
|
ipmiInfo *types.SIPMIInfo
|
|
isolatedDevicesInfo []*isolated_device.PCIDevice
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) GetStartTime() time.Time {
|
|
return task.startTime
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) prepareBaremetalInfo(cli *ssh.Client) (*baremetalPrepareInfo, error) {
|
|
arch, err := getArchitecture(cli)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "getArchitecture")
|
|
}
|
|
|
|
if arch == apis.OS_ARCH_X86_64 {
|
|
if _, err := cli.Run("/lib/mos/sysinit.sh"); err != nil {
|
|
return nil, errors.Wrap(err, "run /lib/mos/sysinit.sh")
|
|
}
|
|
} else {
|
|
log.Infof("Skip running /lib/mos/sysinit.sh when arch is %q", arch)
|
|
}
|
|
|
|
sysInfo, err := getDMISysinfo(cli)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cpuInfo, err := getCPUInfo(cli)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dmiCPUInfo, err := getDMICPUInfo(cli)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
memInfo, err := getDMIMemInfo(cli)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
nicsInfo, err := getNicsInfo(cli)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dhcpServerIp, err := task.baremetal.GetDHCPServerIP()
|
|
if err != nil {
|
|
log.Errorf("failed get dhcp server %s", err)
|
|
}
|
|
isolatedDevicesInfo, err := getIsolatedDevicesInfo(cli, dhcpServerIp)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
raidDiskInfo, nonRaidDiskInfo, pcieDiskInfo, err := detect_storages.DetectStorageInfo(cli, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
diskInfo := make([]*baremetal.BaremetalStorage, 0)
|
|
diskInfo = append(diskInfo, raidDiskInfo...)
|
|
diskInfo = append(diskInfo, nonRaidDiskInfo...)
|
|
diskInfo = append(diskInfo, pcieDiskInfo...)
|
|
var storageDriver string
|
|
if len(raidDiskInfo) > 0 {
|
|
raidDrivers := []string{}
|
|
for _, drv := range raidDiskInfo {
|
|
if !utils.IsInStringArray(drv.Driver, raidDrivers) {
|
|
raidDrivers = append(raidDrivers, drv.Driver)
|
|
}
|
|
}
|
|
storageDriver = strings.Join(raidDrivers, ",")
|
|
} else {
|
|
storageDriver = baremetal.DISK_DRIVER_LINUX
|
|
}
|
|
|
|
ipmiEnable, err := isIPMIEnable(cli)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ipmiInfo := task.baremetal.GetRawIPMIConfig()
|
|
if ipmiInfo == nil {
|
|
ipmiInfo = &types.SIPMIInfo{}
|
|
}
|
|
if !ipmiInfo.Verified && !ipmiInfo.Present && ipmiEnable {
|
|
ipmiInfo.Present = true
|
|
ipmiInfo.Verified = false
|
|
}
|
|
|
|
prepareInfo := &baremetalPrepareInfo{
|
|
architecture: arch,
|
|
sysInfo: sysInfo,
|
|
cpuInfo: cpuInfo,
|
|
dmiCpuInfo: dmiCPUInfo,
|
|
memInfo: memInfo,
|
|
nicsInfo: nicsInfo,
|
|
diskInfo: diskInfo,
|
|
storageDriver: storageDriver,
|
|
ipmiInfo: ipmiInfo,
|
|
isolatedDevicesInfo: isolatedDevicesInfo,
|
|
}
|
|
|
|
return prepareInfo, nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) configIPMISetting(ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo) error {
|
|
if !i.ipmiInfo.Present {
|
|
return nil
|
|
}
|
|
|
|
// if verified, skip ipmi config
|
|
if i.ipmiInfo.Verified {
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
sysInfo = i.sysInfo
|
|
ipmiInfo = i.ipmiInfo
|
|
)
|
|
sshIPMI := ipmitool.NewSSHIPMI(cli)
|
|
setIPMILanPortShared(sshIPMI, sysInfo)
|
|
profile, err := profiles.GetProfile(ctx, sysInfo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "GetProfile")
|
|
}
|
|
ipmiUser, ipmiPasswd, ipmiIpAddr := task.getIPMIUserPasswd(i.ipmiInfo, profile)
|
|
ipmiInfo.Username = ipmiUser
|
|
ipmiInfo.Password = ipmiPasswd
|
|
|
|
var ipmiLanChannel uint8 = 0
|
|
for _, lanChannel := range profile.LanChannels {
|
|
log.Infof("Try lan channel %d ...", lanChannel)
|
|
conf, err := ipmitool.GetLanConfig(sshIPMI, lanChannel)
|
|
if err != nil {
|
|
log.Errorf("Get lan channel %d config error: %v", lanChannel, err)
|
|
continue
|
|
}
|
|
if conf.Mac == nil {
|
|
log.Errorf("Lan channel %d MAC address is empty", lanChannel)
|
|
continue
|
|
}
|
|
|
|
ipmiNic := &types.SNicDevInfo{
|
|
Mac: conf.Mac,
|
|
Speed: 100,
|
|
Mtu: 1500,
|
|
}
|
|
if err := task.sendNicInfo(ctx, ipmiNic, -1, api.NIC_TYPE_IPMI, true, "", false); err != nil {
|
|
// ignore the error
|
|
log.Errorf("Send IPMI nic %#v info: %v", ipmiNic, err)
|
|
}
|
|
rootId := profile.RootId
|
|
err = ipmitool.CreateOrSetAdminUser(sshIPMI, lanChannel, rootId, ipmiUser, ipmiPasswd)
|
|
if err != nil {
|
|
// ignore the error
|
|
log.Errorf("Lan channel %d set user password error: %v", lanChannel, err)
|
|
}
|
|
err = ipmitool.EnableLanAccess(sshIPMI, lanChannel)
|
|
if err != nil {
|
|
// ignore the error
|
|
log.Errorf("Lan channel %d enable lan access error: %v", lanChannel, err)
|
|
}
|
|
|
|
tryAddrs := make([]string, 0)
|
|
if ipmiIpAddr != "" {
|
|
tryAddrs = append(tryAddrs, ipmiIpAddr)
|
|
}
|
|
if conf.IPAddr != "" && conf.IPAddr != ipmiIpAddr {
|
|
tryAddrs = append(tryAddrs, conf.IPAddr)
|
|
}
|
|
if len(tryAddrs) > 0 && !o.Options.ForceDhcpProbeIpmi {
|
|
for _, tryAddr := range tryAddrs {
|
|
tryResult := task.tryLocalIpmiAddr(ctx, sshIPMI, ipmiNic, lanChannel,
|
|
ipmiUser, ipmiPasswd, tryAddr)
|
|
if tryResult {
|
|
ipmiInfo.IpAddr = tryAddr
|
|
ipmiLanChannel = lanChannel
|
|
break
|
|
}
|
|
}
|
|
if ipmiLanChannel > 0 {
|
|
// found and set config on lanChannel
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(tryAddrs) > 0 {
|
|
task.baremetal.SetExistingIPMIIPAddr(tryAddrs[0])
|
|
}
|
|
|
|
err = ipmitool.SetLanDHCP(sshIPMI, lanChannel)
|
|
if err != nil {
|
|
// ignore error
|
|
log.Errorf("Set lan channel %d dhcp error: %v", lanChannel, err)
|
|
}
|
|
time.Sleep(2 * time.Second)
|
|
nic := task.baremetal.GetIPMINic(conf.Mac)
|
|
maxTries := 180 // wait 3 minutes
|
|
for tried := 0; nic != nil && nic.IpAddr == "" && tried < maxTries; tried++ {
|
|
nic = task.baremetal.GetIPMINic(conf.Mac)
|
|
}
|
|
if len(nic.IpAddr) == 0 {
|
|
err = ipmitool.DoBMCReset(sshIPMI) // do BMC reset to force DHCP request
|
|
if err != nil {
|
|
log.Errorf("Do BMC reset error: %v", err)
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
for tried := 0; nic != nil && nic.IpAddr == "" && tried < maxTries; tried++ {
|
|
nic = task.baremetal.GetIPMINic(conf.Mac)
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
if nic != nil && len(nic.IpAddr) == 0 {
|
|
log.Errorf("DHCP wait IPMI address fail, retry ...")
|
|
continue
|
|
}
|
|
log.Infof("DHCP get IPMI address succ, wait 2 seconds ...")
|
|
var tried int = 0
|
|
for tried < maxTries {
|
|
time.Sleep(2 * time.Second)
|
|
lanConf, err := ipmitool.GetLanConfig(sshIPMI, lanChannel)
|
|
if err != nil {
|
|
log.Errorf("Get lan config at channel %d error: %v", lanChannel, err)
|
|
tried += 2
|
|
continue
|
|
}
|
|
if lanConf.IPAddr == nic.IpAddr {
|
|
break
|
|
}
|
|
log.Infof("waiting IPMI DHCP address old:%s expect:%s", lanConf.IPAddr, nic.IpAddr)
|
|
tried += 2
|
|
}
|
|
if tried >= maxTries {
|
|
continue
|
|
}
|
|
err = ipmitool.SetLanStatic(
|
|
sshIPMI,
|
|
lanChannel,
|
|
nic.IpAddr,
|
|
nic.GetNetMask(),
|
|
nic.Gateway,
|
|
)
|
|
if err != nil {
|
|
log.Errorf("Set lanChannel %d static net %#v error: %v", lanChannel, nic, err)
|
|
continue
|
|
}
|
|
ipmiInfo.IpAddr = nic.IpAddr
|
|
ipmiLanChannel = lanChannel
|
|
}
|
|
if ipmiLanChannel == 0 {
|
|
return fmt.Errorf("Fail to get IPMI address from DHCP")
|
|
}
|
|
ipmiInfo.LanChannel = ipmiLanChannel
|
|
ipmiInfo.Verified = true
|
|
return nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) DoPrepare(ctx context.Context, cli *ssh.Client) error {
|
|
infos, err := task.prepareBaremetalInfo(cli)
|
|
if err != nil {
|
|
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
|
return err
|
|
}
|
|
|
|
// set ipmi nic address and user password
|
|
if err = task.configIPMISetting(ctx, cli, infos); err != nil {
|
|
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
|
return errors.Wrap(err, "Config IPMI setting")
|
|
}
|
|
|
|
if err = task.updateBmInfo(ctx, cli, infos); err != nil {
|
|
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
|
return err
|
|
}
|
|
|
|
// set NTP
|
|
if err = task.baremetal.DoNTPConfig(); err != nil {
|
|
// ignore error
|
|
log.Errorf("SetNTP fail: %s", err)
|
|
}
|
|
|
|
if err = AdjustUEFIBootOrder(ctx, cli, task.baremetal); err != nil {
|
|
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, err, task.userCred, false)
|
|
return errors.Wrap(err, "Adjust UEFI boot order")
|
|
}
|
|
|
|
logclient.AddActionLogWithStartable(task, task.baremetal, logclient.ACT_PREPARE, infos.sysInfo, task.userCred, true)
|
|
|
|
log.Infof("Prepare complete")
|
|
return nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) findAdminNic(cli *ssh.Client, nicsInfo []*types.SNicDevInfo) (int, *types.SNicDevInfo, error) {
|
|
accessIp := cli.GetConfig().Host
|
|
for idx := range nicsInfo {
|
|
nic := nicsInfo[idx]
|
|
output, err := cli.Run("/sbin/ifconfig " + nic.Dev)
|
|
if err != nil {
|
|
log.Errorf("ifconfig %s fail: %s", nic.Dev, err)
|
|
continue
|
|
}
|
|
isAdmin := false
|
|
for _, l := range output {
|
|
if strings.Contains(l, accessIp) {
|
|
isAdmin = true
|
|
break
|
|
}
|
|
}
|
|
if isAdmin {
|
|
return idx, nic, nil
|
|
}
|
|
}
|
|
return -1, nil, errors.Error("admin nic not found???")
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) updateBmInfo(ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo) error {
|
|
adminNic := task.baremetal.GetAdminNic()
|
|
if adminNic == nil || (adminNic != nil && !adminNic.LinkUp) {
|
|
adminIdx, adminNicDev, err := task.findAdminNic(cli, i.nicsInfo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "task.findAdminNic")
|
|
}
|
|
accessIp := cli.GetConfig().Host
|
|
err = task.sendNicInfo(ctx, adminNicDev, adminIdx, api.NIC_TYPE_ADMIN, false, accessIp, true)
|
|
if err != nil {
|
|
return errors.Wrap(err, "send Admin Nic Info")
|
|
}
|
|
adminNic = task.baremetal.GetNicByMac(adminNicDev.Mac)
|
|
}
|
|
// collect params
|
|
updateInfo := make(map[string]interface{})
|
|
oname := fmt.Sprintf("BM%s", strings.Replace(adminNic.Mac, ":", "", -1))
|
|
if task.baremetal.GetName() == oname {
|
|
updateInfo["name"] = fmt.Sprintf("BM-%s", strings.Replace(i.ipmiInfo.IpAddr, ".", "-", -1))
|
|
}
|
|
updateInfo["access_ip"] = adminNic.IpAddr
|
|
updateInfo["access_mac"] = adminNic.Mac
|
|
updateInfo["cpu_architecture"] = i.architecture
|
|
updateInfo["cpu_count"] = i.cpuInfo.Count
|
|
updateInfo["node_count"] = i.dmiCpuInfo.Nodes
|
|
updateInfo["cpu_desc"] = i.cpuInfo.Model
|
|
updateInfo["cpu_mhz"] = i.cpuInfo.Freq
|
|
updateInfo["cpu_cache"] = i.cpuInfo.Cache
|
|
updateInfo["mem_size"] = i.memInfo.Total
|
|
updateInfo["storage_driver"] = i.storageDriver
|
|
updateInfo["storage_info"] = i.diskInfo
|
|
updateInfo["sys_info"] = i.sysInfo
|
|
updateInfo["sn"] = i.sysInfo.SN
|
|
size, diskType := task.collectDiskInfo(i.diskInfo)
|
|
updateInfo["storage_size"] = size
|
|
updateInfo["storage_type"] = diskType
|
|
updateInfo["boot_mode"] = getBootMode(cli)
|
|
updateInfo["uuid"] = getSystemGuid(cli)
|
|
updateData := jsonutils.Marshal(updateInfo)
|
|
updateData.(*jsonutils.JSONDict).Update(i.ipmiInfo.ToPrepareParams())
|
|
_, err := modules.Hosts.Update(task.getClientSession(), task.baremetal.GetId(), updateData)
|
|
if err != nil {
|
|
log.Errorf("Update baremetal info error: %v", err)
|
|
return errors.Wrap(err, "Hosts.Update")
|
|
}
|
|
if err := task.sendStorageInfo(size); err != nil {
|
|
log.Errorf("sendStorageInfo error: %v", err)
|
|
return errors.Wrap(err, "task.sendStorageInfo")
|
|
}
|
|
if len(i.isolatedDevicesInfo) > 0 {
|
|
err = task.sendIsolatedDevicesInfo(task.getClientSession(), i.isolatedDevicesInfo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "send isolated devices info")
|
|
}
|
|
}
|
|
// XXX do not change nic order anymore
|
|
// for i := range nicsInfo {
|
|
// if nicsInfo[i].Mac.String() == adminNic.GetMac().String() {
|
|
// if i != 0 {
|
|
// nicsInfo = append(nicsInfo[i:], nicsInfo[0:i]...)
|
|
// }
|
|
// break
|
|
// }
|
|
// }
|
|
// err = task.removeAllNics()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
removedMacs := task.removeObsoleteNics(i)
|
|
for idx := range removedMacs {
|
|
err = task.removeNicInfo(ctx, removedMacs[idx])
|
|
if err != nil {
|
|
log.Errorf("Fail to remove Netif %s: %s", removedMacs[idx], err)
|
|
return errors.Wrap(err, "task.removeNicInfo")
|
|
}
|
|
}
|
|
for idx := range i.nicsInfo {
|
|
err = task.sendNicInfo(ctx, i.nicsInfo[idx], idx, "", false, "", false)
|
|
if err != nil {
|
|
log.Errorf("Send nicinfo idx: %d, %#v error: %v", idx, i.nicsInfo[idx], err)
|
|
return errors.Wrap(err, "task.sendNicInfo")
|
|
}
|
|
}
|
|
if o.Options.EnablePxeBoot && task.baremetal.EnablePxeBoot() {
|
|
for _, nicInfo := range i.nicsInfo {
|
|
if nicInfo.Mac.String() != adminNic.GetMac().String() && nicInfo.Up != nil && *nicInfo.Up {
|
|
err = task.doNicWireProbe(cli, nicInfo)
|
|
if err != nil {
|
|
// ignore the error
|
|
log.Errorf("doNicWireProbe nic %#v error: %v", nicInfo, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) removeObsoleteNics(i *baremetalPrepareInfo) []string {
|
|
removes := make([]string, 0)
|
|
existNics := task.baremetal.GetNics()
|
|
log.Debugf("Existing nics: %s", jsonutils.Marshal(existNics))
|
|
for idx := range existNics {
|
|
if existNics[idx].Type != api.NIC_TYPE_NORMAL {
|
|
continue
|
|
}
|
|
find := false
|
|
for j := range i.nicsInfo {
|
|
if existNics[idx].Mac == i.nicsInfo[j].Mac.String() {
|
|
find = true
|
|
break
|
|
}
|
|
}
|
|
if !find {
|
|
removes = append(removes, existNics[idx].Mac)
|
|
}
|
|
}
|
|
return removes
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) tryLocalIpmiAddr(ctx context.Context, sshIPMI *ipmitool.SSHIPMI, ipmiNic *types.SNicDevInfo, lanChannel uint8, ipmiUser, ipmiPasswd, tryAddr string) bool {
|
|
log.Infof("IP addr found in IPMI config, try use %s as IPMI address", tryAddr)
|
|
ipConf, err := task.getIPMIIPConfig(tryAddr)
|
|
if err != nil {
|
|
log.Errorf("Failed to get IPMI ipconfig for %s", tryAddr)
|
|
return false
|
|
}
|
|
err = ipmitool.SetLanStatic(sshIPMI, lanChannel, ipConf.IPAddr, ipConf.Netmask, ipConf.Gateway)
|
|
if err != nil {
|
|
log.Errorf("Failed to set IPMI static net config %#v for %s", *ipConf, tryAddr)
|
|
return false
|
|
}
|
|
|
|
var conf *types.SIPMILanConfig
|
|
|
|
tried := 0
|
|
maxTries := 5
|
|
time.Sleep(2 * time.Second)
|
|
for tried = 0; tried < maxTries; tried += 1 {
|
|
conf, err = ipmitool.GetLanConfig(sshIPMI, lanChannel)
|
|
if err != nil {
|
|
log.Errorf("Failed to get lan config after set static network: %v", err)
|
|
continue
|
|
}
|
|
log.Infof("Get lan config %#v", *conf)
|
|
if conf.IPAddr == "" || conf.IPAddr != tryAddr {
|
|
log.Errorf("Failed to set ipmi lan channel %d static ipaddr", lanChannel)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
if tried >= maxTries {
|
|
log.Errorf("Failed to get lan config after %d tries", tried)
|
|
return false
|
|
}
|
|
rmcpIPMI := ipmitool.NewLanPlusIPMI(tryAddr, ipmiUser, ipmiPasswd)
|
|
for tried = 0; tried < maxTries; tried += 1 {
|
|
conf2, err := ipmitool.GetLanConfig(rmcpIPMI, lanChannel)
|
|
if err != nil {
|
|
log.Errorf("Failed to get lan channel %d config use RMCP mode: %v", lanChannel, err)
|
|
continue
|
|
}
|
|
if len(conf2.Mac) != 0 &&
|
|
conf2.Mac.String() == conf.Mac.String() &&
|
|
conf2.IPAddr != "" && conf2.IPAddr == tryAddr {
|
|
break
|
|
} else {
|
|
log.Errorf("fail to rmcp get IPMI ip config %v", conf2)
|
|
time.Sleep(5 * time.Second)
|
|
}
|
|
}
|
|
if tried < maxTries {
|
|
// make sure the ipaddr is a IPMI address
|
|
// enable the netif
|
|
err := task.sendNicInfo(ctx, ipmiNic, -1, api.NIC_TYPE_IPMI, false, tryAddr, true)
|
|
if err != nil {
|
|
log.Errorf("Fail to set existing BMC IP address to %s", tryAddr)
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) getIPMIUserPasswd(oldIPMIConf *types.SIPMIInfo, profile *baremetalapi.BaremetalProfileSpec) (string, string, string) {
|
|
var (
|
|
ipmiUser string
|
|
ipmiPasswd string
|
|
ipmiIpAddr string
|
|
)
|
|
ipmiUser = profile.RootName
|
|
isStrongPass := profile.StrongPass
|
|
if !isStrongPass && o.Options.DefaultIpmiPassword != "" {
|
|
ipmiPasswd = o.Options.DefaultIpmiPassword
|
|
} else if isStrongPass && o.Options.DefaultStrongIpmiPassword != "" {
|
|
ipmiPasswd = o.Options.DefaultStrongIpmiPassword
|
|
} else if isStrongPass && o.Options.DefaultIpmiPassword != "" {
|
|
ipmiPasswd = o.Options.DefaultIpmiPassword
|
|
} else {
|
|
ipmiPasswd = seclib.RandomPassword(20)
|
|
}
|
|
if oldIPMIConf.Username != "" {
|
|
ipmiUser = oldIPMIConf.Username
|
|
}
|
|
if oldIPMIConf.Password != "" {
|
|
ipmiPasswd = oldIPMIConf.Password
|
|
}
|
|
if oldIPMIConf.IpAddr != "" {
|
|
ipmiIpAddr = oldIPMIConf.IpAddr
|
|
}
|
|
return ipmiUser, ipmiPasswd, ipmiIpAddr
|
|
}
|
|
|
|
type ipmiIPConfig struct {
|
|
IPAddr string
|
|
Netmask string
|
|
Gateway string
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) getIPMIIPConfig(ipAddr string) (*ipmiIPConfig, error) {
|
|
params := jsonutils.NewDict()
|
|
params.Add(jsonutils.NewString(ipAddr), "ip")
|
|
params.Add(jsonutils.NewString("system"), "scope")
|
|
params.Add(jsonutils.JSONTrue, "is_classic")
|
|
listRet, err := modules.Networks.List(task.getClientSession(), params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(listRet.Data) != 1 {
|
|
return nil, fmt.Errorf("Invalid network list count: %d", len(listRet.Data))
|
|
}
|
|
netObj := listRet.Data[0]
|
|
config := &ipmiIPConfig{}
|
|
config.IPAddr = ipAddr
|
|
maskLen, _ := netObj.Int("guest_ip_mask")
|
|
config.Netmask = netutils.Masklen2Mask(int8(maskLen)).String()
|
|
config.Gateway, _ = netObj.GetString("guest_gateway")
|
|
return config, nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) getClientSession() *mcclient.ClientSession {
|
|
return task.baremetal.GetClientSession()
|
|
}
|
|
|
|
func getArchitecture(cli *ssh.Client) (string, error) {
|
|
ret, err := cli.Run("/bin/uname -m")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(ret) == 0 {
|
|
return "", errors.Errorf("/bin/uname not output")
|
|
}
|
|
arch := strings.ToLower(ret[0])
|
|
switch arch {
|
|
case apis.OS_ARCH_AARCH64:
|
|
return apis.OS_ARCH_AARCH64, nil
|
|
case apis.OS_ARCH_X86_64:
|
|
return apis.OS_ARCH_X86_64, nil
|
|
default:
|
|
return arch, nil
|
|
}
|
|
}
|
|
|
|
func getDMISysinfo(cli *ssh.Client) (*types.SSystemInfo, error) {
|
|
ret, err := cli.Run("/usr/sbin/dmidecode -t 1")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sysutils.ParseDMISysinfo(ret)
|
|
}
|
|
|
|
func getCPUInfo(cli *ssh.Client) (*types.SCPUInfo, error) {
|
|
ret, err := cli.Run("cat /proc/cpuinfo")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sysutils.ParseCPUInfo(ret)
|
|
}
|
|
|
|
func getBootMode(cli *ssh.Client) string {
|
|
lines, err := cli.Run("cat /proc/cmdline")
|
|
if err != nil {
|
|
return api.BOOT_MODE_PXE
|
|
}
|
|
const (
|
|
key = "bootmode="
|
|
)
|
|
for _, line := range lines {
|
|
pos := strings.Index(line, key)
|
|
if pos >= 0 {
|
|
if strings.HasPrefix(line[pos+len(key):], api.BOOT_MODE_ISO) {
|
|
return api.BOOT_MODE_ISO
|
|
}
|
|
}
|
|
}
|
|
return api.BOOT_MODE_PXE
|
|
}
|
|
|
|
func getSystemGuid(cli *ssh.Client) string {
|
|
lines, err := cli.Run("/usr/sbin/dmidecode -s system-uuid")
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
for _, line := range lines {
|
|
line = strings.TrimSpace(line)
|
|
if len(line) > 0 {
|
|
return sysutils.NormalizeUuid(line)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func getDMICPUInfo(cli *ssh.Client) (*types.SDMICPUInfo, error) {
|
|
ret, err := cli.Run("/usr/sbin/dmidecode -t 4")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sysutils.ParseDMICPUInfo(ret), nil
|
|
}
|
|
|
|
func getDMIMemInfo(cli *ssh.Client) (*types.SDMIMemInfo, error) {
|
|
ret, err := cli.Run("/usr/sbin/dmidecode -t 17")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return sysutils.ParseDMIMemInfo(ret), nil
|
|
}
|
|
|
|
func GetNicsInfo(cli *ssh.Client) ([]*types.SNicDevInfo, error) {
|
|
return getNicsInfo(cli)
|
|
}
|
|
|
|
func getNicsInfo(cli *ssh.Client) ([]*types.SNicDevInfo, error) {
|
|
ret, err := cli.Run("/lib/mos/lsnic")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to retrieve NIC info: %v", err)
|
|
}
|
|
return sysutils.ParseNicInfo(ret), nil
|
|
}
|
|
|
|
func getIsolatedDevicesInfo(cli *ssh.Client, ip net.IP) ([]*isolated_device.PCIDevice, error) {
|
|
// fetch pci.ids from baremetal agent
|
|
var updatedPciids bool
|
|
if ip != nil {
|
|
out, err := cli.Run(fmt.Sprintf("tftp -g -r pci.ids %s -l /pci.ids", ip))
|
|
if err != nil {
|
|
log.Errorf("tftp failed download pciids %s %s", err, out)
|
|
} else {
|
|
updatedPciids = true
|
|
}
|
|
}
|
|
|
|
var bootVgaPath = []string{}
|
|
lines, err := cli.Run("ls /sys/bus/pci/devices/*/boot_vga")
|
|
if err != nil {
|
|
log.Errorf("failed find boot vga %s", err)
|
|
}
|
|
for i := 0; i < len(lines); i++ {
|
|
bootVgaPath = append(bootVgaPath, strings.TrimSpace(lines[i]))
|
|
}
|
|
|
|
cmd := "lspci -nnmm"
|
|
if updatedPciids {
|
|
cmd = "lspci -i /pci.ids -nnmm"
|
|
}
|
|
|
|
lines, err = cli.Run(cmd)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "run %s", cmd)
|
|
}
|
|
devs := []*isolated_device.PCIDevice{}
|
|
for _, line := range lines {
|
|
if len(line) > 0 {
|
|
dev := isolated_device.NewPCIDevice2(line, cli)
|
|
if len(dev.Addr) > 0 && utils.IsInArray(dev.ClassCode, isolated_device.GpuClassCodes) && !isBootVga(cli, dev, bootVgaPath) {
|
|
devs = append(devs, dev)
|
|
}
|
|
}
|
|
}
|
|
return devs, nil
|
|
}
|
|
|
|
func isBootVga(cli *ssh.Client, dev *isolated_device.PCIDevice, bootVgaPath []string) bool {
|
|
for i := 0; i < len(bootVgaPath); i++ {
|
|
if strings.Contains(bootVgaPath[i], dev.Addr) {
|
|
out, err := cli.RawRun(fmt.Sprintf("cat %s", bootVgaPath[i]))
|
|
if err != nil {
|
|
log.Errorf("cat boot_vga %s failed %s", bootVgaPath[i], err)
|
|
return false
|
|
} else if len(out) > 0 {
|
|
if strings.HasPrefix(out[0], "1") {
|
|
log.Infof("device %s is boot vga", bootVgaPath[i])
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
break
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isIPMIEnable(cli *ssh.Client) (bool, error) {
|
|
ret, err := cli.Run("/usr/sbin/dmidecode -t 38")
|
|
if err != nil {
|
|
return false, fmt.Errorf("Failed to retrieve IPMI info: %v", err)
|
|
}
|
|
return sysutils.ParseDMIIPMIInfo(ret), nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) removeNicInfo(ctx context.Context, mac string) error {
|
|
params := jsonutils.NewDict()
|
|
params.Add(jsonutils.NewString(mac), "mac")
|
|
resp, err := modules.Hosts.PerformAction(
|
|
task.getClientSession(),
|
|
task.baremetal.GetId(),
|
|
"remove-netif",
|
|
params,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return task.baremetal.SaveDesc(ctx, resp)
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) sendNicInfo(ctx context.Context,
|
|
nic *types.SNicDevInfo, idx int, nicType compute.TNicType, reset bool, ipAddr string, reserve bool,
|
|
) error {
|
|
return task.baremetal.SendNicInfo(ctx, nic, idx, nicType, reset, ipAddr, reserve)
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) sendStorageInfo(size int64) error {
|
|
params := jsonutils.NewDict()
|
|
params.Add(jsonutils.NewInt(size), "capacity")
|
|
params.Add(jsonutils.NewString(task.baremetal.GetZoneId()), "zone_id")
|
|
params.Add(jsonutils.NewString(task.baremetal.GetStorageCacheId()), "storagecache_id")
|
|
_, err := modules.Hosts.PerformAction(task.getClientSession(), task.baremetal.GetId(), "update-storage", params)
|
|
return err
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) getCloudIsolatedDevices(
|
|
session *mcclient.ClientSession,
|
|
) ([]jsonutils.JSONObject, error) {
|
|
params := jsonutils.NewDict()
|
|
params.Set("details", jsonutils.JSONTrue)
|
|
params.Set("limit", jsonutils.NewInt(0))
|
|
params.Set("host", jsonutils.NewString(task.baremetal.GetId()))
|
|
params.Set("scope", jsonutils.NewString("system"))
|
|
params.Set("show_baremetal_isolated_devices", jsonutils.JSONTrue)
|
|
res, err := modules.IsolatedDevices.List(session, params)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return res.Data, nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) sendIsolatedDevicesInfo(
|
|
session *mcclient.ClientSession, devs []*isolated_device.PCIDevice,
|
|
) error {
|
|
objs, err := task.getCloudIsolatedDevices(session)
|
|
if err != nil {
|
|
return errors.Wrap(err, "get cloud isolated devices")
|
|
}
|
|
|
|
gpuDevs := make([]isolated_device.IDevice, len(devs))
|
|
for i := 0; i < len(devs); i++ {
|
|
gpuDevs[i] = isolated_device.NewGPUHPCDevice(devs[i])
|
|
}
|
|
|
|
for _, obj := range objs {
|
|
var notFound = true
|
|
info := isolated_device.CloudDeviceInfo{}
|
|
if err := obj.Unmarshal(&info); err != nil {
|
|
return errors.Wrap(err, "unmarshal isolated device to cloud device info failed")
|
|
}
|
|
for i := 0; i < len(gpuDevs); i++ {
|
|
if gpuDevs[i].GetAddr() == info.Addr && gpuDevs[i].GetVendorDeviceId() == info.VendorDeviceId {
|
|
gpuDevs[i].SetDeviceInfo(info)
|
|
notFound = false
|
|
break
|
|
}
|
|
}
|
|
if notFound {
|
|
_, err := modules.IsolatedDevices.PerformAction(session, info.Id, "purge", nil)
|
|
if err != nil {
|
|
return errors.Wrap(err, "purge unknown isolated devices")
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(gpuDevs); i++ {
|
|
if _, err := isolated_device.SyncDeviceInfo(session, task.baremetal.GetId(), gpuDevs[i]); err != nil {
|
|
return errors.Wrap(err, "sync device info")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) doNicWireProbe(cli *ssh.Client, nic *types.SNicDevInfo) error {
|
|
maxTries := 6
|
|
for tried := 0; tried < maxTries; tried++ {
|
|
log.Infof("doNicWireProbe %v", nic)
|
|
_, err := cli.Run(fmt.Sprintf("/sbin/udhcpc -t 1 -T 3 -n -i %s", nic.Dev))
|
|
if err != nil {
|
|
log.Errorf("/sbin/udhcpc error: %v", err)
|
|
}
|
|
nicInfo := task.baremetal.GetNicByMac(nic.Mac)
|
|
if nicInfo != nil && nicInfo.WireId != "" {
|
|
log.Infof("doNicWireProbe success, get result %#v", nicInfo)
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (task *sBaremetalPrepareTask) collectDiskInfo(diskInfo []*baremetal.BaremetalStorage) (int64, string) {
|
|
cnt := 0
|
|
rotateCnt := 0
|
|
var size int64 = 0
|
|
var diskType string
|
|
for _, d := range diskInfo {
|
|
if d.Rotate {
|
|
rotateCnt += 1
|
|
}
|
|
size += d.Size
|
|
cnt += 1
|
|
}
|
|
if rotateCnt == cnt {
|
|
diskType = "rotate"
|
|
} else if rotateCnt == 0 {
|
|
diskType = "ssd"
|
|
} else {
|
|
diskType = "hybrid"
|
|
}
|
|
return size, diskType
|
|
}
|
|
|
|
func setIPMILanPortShared(cli ipmitool.IPMIExecutor, sysInfo *types.SSystemInfo) {
|
|
if !o.Options.IpmiLanPortShared {
|
|
return
|
|
}
|
|
oemName := strings.ToLower(sysInfo.Manufacture)
|
|
var err error
|
|
switch sysInfo.OemName {
|
|
case types.OEM_NAME_HUAWEI:
|
|
err = ipmitool.SetHuaweiIPMILanPortShared(cli)
|
|
case types.OEM_NAME_DELL:
|
|
err = ipmitool.SetDellIPMILanPortShared(cli)
|
|
}
|
|
if err != nil {
|
|
log.Errorf("Set %s ipmi lan port shared failed: %v", oemName, err)
|
|
}
|
|
}
|