Files
cloudpods/pkg/hostman/hostinfo/hostinfohelper.go
Yousong Zhou bc89e8ab67 hostman: use "uname -m" for for fetch processor type
"uname -p" command from native busybox, coreutils may return "unknown".
CentOS coreutils has a patch fallback to using uts.machine if sysinfo
syscall is not available

Use "uname -m" for maximum compatibility
2020-02-27 20:00:00 +08:00

397 lines
9.5 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 hostinfo
import (
"bufio"
"context"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/mem"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/util/netutils"
"yunion.io/x/pkg/util/regutils"
"yunion.io/x/onecloud/pkg/cloudcommon/types"
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostbridge"
"yunion.io/x/onecloud/pkg/hostman/hostinfo/hostdhcp"
"yunion.io/x/onecloud/pkg/hostman/hostutils"
"yunion.io/x/onecloud/pkg/hostman/options"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/util/fileutils2"
"yunion.io/x/onecloud/pkg/util/netutils2"
"yunion.io/x/onecloud/pkg/util/procutils"
"yunion.io/x/onecloud/pkg/util/sysutils"
)
type SCPUInfo struct {
CpuCount int
cpuFreq int64 // MHZ
cpuFeatures []string
CpuArchitecture string
cpuInfoProc *types.SCPUInfo
cpuInfoDmi *types.SDMICPUInfo
}
func DetectCpuInfo() (*SCPUInfo, error) {
cpuinfo := new(SCPUInfo)
cpuCount, _ := cpu.Counts(true)
cpuinfo.CpuCount = cpuCount
spec, err := cpuinfo.fetchCpuSpecs()
if err != nil {
return nil, err
}
var freq float64
strCpuFreq, ok := spec["cpu_freq"]
if ok {
freq, err = strconv.ParseFloat(strCpuFreq, 64)
if err != nil {
log.Errorln(err)
return nil, err
}
}
cpuinfo.cpuFreq = int64(freq)
log.Infof("cpuinfo freq %d", cpuinfo.cpuFreq)
cpuinfo.cpuFeatures = strings.Split(spec["flags"], " ")
// cpu.Percent(interval, false)
ret, err := fileutils2.FileGetContents("/proc/cpuinfo")
if err != nil {
return nil, errors.Wrap(err, "get cpuinfo")
}
cpuinfo.cpuInfoProc, err = sysutils.ParseCPUInfo(strings.Split(ret, "\n"))
if err != nil {
return nil, errors.Wrap(err, "parse cpu info")
}
bret, err := procutils.NewCommand("dmidecode", "-t", "4").Output()
if err != nil {
return nil, errors.Wrap(err, "get dmidecode info -t 4")
}
cpuinfo.cpuInfoDmi = sysutils.ParseDMICPUInfo(strings.Split(string(bret), "\n"))
if err != nil {
return nil, errors.Wrap(err, "parse dmi cpuinfo")
}
cpuArch, err := procutils.NewCommand("uname", "-m").Output()
if err != nil {
return nil, errors.Wrap(err, "get cpu architecture")
}
cpuinfo.CpuArchitecture = strings.TrimSpace(string(cpuArch))
return cpuinfo, nil
}
func (c *SCPUInfo) fetchCpuSpecs() (map[string]string, error) {
f, err := os.Open("/proc/cpuinfo")
if err != nil {
return nil, err
}
defer f.Close()
var spec = make(map[string]string, 0)
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
colon := strings.Index(line, ":")
if colon > 0 {
key := strings.TrimSpace(line[:colon])
val := strings.TrimSpace(line[colon+1:])
if key == "cpu MHz" {
spec["cpu_freq"] = val
} else if key == "flags" {
spec["flags"] = val
}
}
}
if err := scanner.Err(); err != nil {
log.Errorln(err)
return nil, err
}
return spec, nil
}
// percentInterval(ms)
func (c *SCPUInfo) GetJsonDesc(percentInterval int) {
// perc, err := cpu.Percent(time.Millisecond*percentInterval, false)
// os. ?????可能不需要要写
}
type SMemory struct {
Total int
Free int
Used int
MemInfo *types.SDMIMemInfo
}
func DetectMemoryInfo() (*SMemory, error) {
var smem = new(SMemory)
info, err := mem.VirtualMemory()
if err != nil {
return nil, err
}
smem.Total = int(info.Total / 1024 / 1024)
smem.Free = int(info.Available / 1024 / 1024)
smem.Used = smem.Total - smem.Free
ret, err := procutils.NewCommand("dmidecode", "-t", "17").Output()
if err != nil {
return nil, err
}
smem.MemInfo = sysutils.ParseDMIMemInfo(strings.Split(string(ret), "\n"))
if smem.MemInfo.Total == 0 {
// in case dmidecode is not work, use gopsutil
smem.MemInfo.Total = smem.Total
}
return smem, nil
}
func (m *SMemory) GetHugepageTotal() (int, error) {
file, err := os.Open("/proc/meminfo")
if err != nil {
return 0, errors.Wrap(err, "open meminfo")
}
defer file.Close()
var (
nrHugePage int
sizeHugePage int
)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "Hugepagesize:") {
re := regexp.MustCompile(`\s+`)
segs := re.Split(line, -1)
v, err := strconv.Atoi(segs[1])
if err != nil {
return 0, errors.Wrap(err, "get hugepage size")
}
sizeHugePage = v / 1024 // MB
log.Debugf("Huge page size %v", sizeHugePage)
} else if strings.HasPrefix(line, "HugePages_Total:") {
re := regexp.MustCompile(`\s+`)
segs := re.Split(line, -1)
v, err := strconv.Atoi(segs[1])
if err != nil {
return 0, errors.Wrap(err, "get hugepages total")
}
nrHugePage = v
log.Debugf("Huge page number %v", nrHugePage)
}
}
return nrHugePage * sizeHugePage, nil
}
func (m *SMemory) GetHugepagesizeMb() int {
file, err := os.Open("/proc/meminfo")
if err != nil {
log.Errorln(err)
return 0
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "Hugepagesize:") {
re := regexp.MustCompile(`\s+`)
segs := re.Split(line, -1)
v, err := strconv.Atoi(segs[1])
if err != nil {
log.Errorln(err)
return 0
}
return int(v) / 1024
}
}
if err := scanner.Err(); err != nil {
log.Errorln(err)
}
return 0
}
type SNIC struct {
Inter string
Bridge string
Ip string
Network string
WireId string
Mask int
Bandwidth int
BridgeDev hostbridge.IBridgeDriver
dhcpServer *hostdhcp.SGuestDHCPServer
}
func (n *SNIC) EnableDHCPRelay() bool {
v4Ip, err := netutils.NewIPV4Addr(n.Ip)
if err != nil {
log.Errorln(err)
return false
}
if len(options.HostOptions.DhcpRelay) > 0 && !netutils.IsExitAddress(v4Ip) {
return true
} else {
return false
}
}
func (n *SNIC) SetupDhcpRelay() error {
if n.EnableDHCPRelay() {
if err := n.dhcpServer.RelaySetup(n.Ip); err != nil {
return err
}
}
return nil
}
func (n *SNIC) SetWireId(wire, wireId string, bandwidth int64) {
n.Network = wire
n.WireId = wireId
n.Bandwidth = int(bandwidth)
}
func (n *SNIC) ExitCleanup() {
n.BridgeDev.CleanupConfig()
log.Infof("Stop DHCP Server")
// TODO stop dhcp server
}
func NewNIC(desc string) (*SNIC, error) {
nic := new(SNIC)
data := strings.Split(desc, "/")
if len(data) < 3 {
return nil, fmt.Errorf("Parse nic conf %s failed, too short", desc)
}
nic.Inter = data[0]
nic.Bridge = data[1]
if regutils.MatchIP4Addr(data[2]) {
nic.Ip = data[2]
} else {
nic.Network = data[2]
}
nic.Bandwidth = 1000
log.Infof("IP %s/%s/%s", nic.Ip, nic.Bridge, nic.Inter)
if len(nic.Ip) > 0 {
// waiting for interface assign ip
// in case nic bonding is too slow
var max, wait = 30, 0
for wait < max {
inf := netutils2.NewNetInterfaceWithExpectIp(nic.Inter, nic.Ip)
if inf.Addr == nic.Ip {
mask, _ := inf.Mask.Size()
if mask > 0 {
nic.Mask = mask
}
break
}
br := netutils2.NewNetInterface(nic.Bridge)
if br.Addr == nic.Ip {
mask, _ := br.Mask.Size()
if nic.Mask == 0 && mask > 0 {
nic.Mask = mask
}
break
}
time.Sleep(time.Second * 2)
wait += 1
}
if wait >= max {
// if ip not found in inter or bridge
return nil, fmt.Errorf("Ip %s is not configure on %s/%s ?", nic.Ip, nic.Bridge, nic.Inter)
}
}
var err error
nic.BridgeDev, err = hostbridge.NewDriver(options.HostOptions.BridgeDriver,
nic.Bridge, nic.Inter, nic.Ip)
if err != nil {
log.Errorln(err)
return nil, err
}
confirm, err := nic.BridgeDev.ConfirmToConfig()
if err != nil {
log.Errorln(err)
return nil, err
}
if !confirm {
log.Infof("Not confirm to configuration")
if err = nic.BridgeDev.Setup(nic.BridgeDev); err != nil {
log.Errorln(err)
return nil, err
}
time.Sleep(time.Second * 1)
} else {
log.Infof("Confirm to configuration!!")
}
if err := nic.BridgeDev.PersistentMac(); err != nil {
return nil, err
}
var dhcpRelay []string
if nic.EnableDHCPRelay() {
dhcpRelay = options.HostOptions.DhcpRelay
}
nic.dhcpServer, err = hostdhcp.NewGuestDHCPServer(nic.Bridge, dhcpRelay)
if err != nil {
return nil, err
}
// dhcp server start after guest manager init
return nic, nil
}
type SSysInfo struct {
*types.SSystemInfo
Nest string `json:"nest,omitempty"`
OsDistribution string `json:"os_distribution"`
OsVersion string `json:"os_version"`
KernelVersion string `json:"kernel_version"`
QemuVersion string `json:"qemu_version"`
OvsVersion string `json:"ovs_version"`
KvmModule string `json:"kvm_module"`
CpuModelName string `json:"cpu_model_name"`
CpuMicrocode string `json:"cpu_microcode"`
StorageType string `json:"storage_type"`
}
func StartDetachStorages(hs []jsonutils.JSONObject) {
for len(hs) > 0 {
hostId, _ := hs[0].GetString("host_id")
storageId, _ := hs[0].GetString("storage_id")
_, err := modules.Hoststorages.Detach(
hostutils.GetComputeSession(context.Background()),
hostId, storageId, nil)
if err != nil {
log.Errorf("Host %s detach storage %s failed: %s",
hostId, storageId, err)
time.Sleep(30 * time.Second)
} else {
hs = hs[1:]
}
}
}