// 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) doUpdateBmInfo( ctx context.Context, cli *ssh.Client, i *baremetalPrepareInfo, bmName, accessIp, accessMac string, ) error { updateInfo := make(map[string]interface{}) if bmName != "" { updateInfo["name"] = bmName } updateInfo["access_ip"] = accessIp if accessMac != "" { updateInfo["access_mac"] = accessMac } 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") } } 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") } } return nil } 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) } oname := fmt.Sprintf("BM%s", strings.Replace(adminNic.Mac, ":", "", -1)) if task.baremetal.GetName() != oname { oname = "" } err := task.doUpdateBmInfo(ctx, cli, i, oname, adminNic.IpAddr, adminNic.Mac) if err != nil { return err } 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], true); 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) } }