mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-05-07 06:02:09 +08:00
384 lines
8.6 KiB
Go
384 lines
8.6 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 sysutils
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"yunion.io/x/jsonutils"
|
|
"yunion.io/x/log"
|
|
|
|
"yunion.io/x/onecloud/pkg/apis/compute"
|
|
"yunion.io/x/onecloud/pkg/cloudcommon/types"
|
|
"yunion.io/x/onecloud/pkg/compute/baremetal"
|
|
)
|
|
|
|
func valueOfKeyword(line string, key string) *string {
|
|
lo := strings.ToLower(line)
|
|
ko := strings.ToLower(key)
|
|
pos := strings.Index(lo, ko)
|
|
if pos >= 0 {
|
|
val := strings.TrimSpace(line[pos+len(key):])
|
|
return &val
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func DumpMapToObject(data map[string]string, obj interface{}) error {
|
|
return jsonutils.Marshal(data).Unmarshal(obj)
|
|
}
|
|
|
|
func ParseDMISysinfo(lines []string) (*types.SSystemInfo, error) {
|
|
if len(lines) == 0 {
|
|
return nil, fmt.Errorf("Empty input")
|
|
}
|
|
keys := map[string]string{
|
|
"manufacture": "Manufacturer:",
|
|
"model": "Product Name:",
|
|
"version": "Version:",
|
|
"sn": "Serial Number:",
|
|
}
|
|
ret := make(map[string]string)
|
|
for _, line := range lines {
|
|
for key, keyword := range keys {
|
|
val := valueOfKeyword(line, keyword)
|
|
if val != nil {
|
|
ret[key] = *val
|
|
}
|
|
}
|
|
}
|
|
info := types.SSystemInfo{}
|
|
err := DumpMapToObject(ret, &info)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if strings.ToLower(info.Version) == "none" {
|
|
info.Version = ""
|
|
}
|
|
info.OemName = types.ManufactureOemName(info.Manufacture)
|
|
return &info, nil
|
|
}
|
|
|
|
func ParseCPUInfo(lines []string) (*types.SCPUInfo, error) {
|
|
cnt := 0
|
|
var (
|
|
model string
|
|
freq string
|
|
cache string
|
|
microcode string
|
|
part string
|
|
revision string
|
|
)
|
|
lv := func(line string) string {
|
|
return strings.TrimSpace(line[strings.Index(line, ":")+1:])
|
|
}
|
|
for _, line := range lines {
|
|
if len(model) == 0 && strings.HasPrefix(line, "model name") {
|
|
model = lv(line)
|
|
}
|
|
if len(freq) == 0 && strings.HasPrefix(line, "cpu MHz") {
|
|
freq = lv(line)
|
|
}
|
|
if len(cache) == 0 && strings.HasPrefix(line, "cache size") {
|
|
cache = strings.TrimSpace(line[strings.Index(line, ":")+1 : strings.Index(line, " KB")])
|
|
}
|
|
if len(microcode) == 0 && strings.HasPrefix(line, "microcode") {
|
|
microcode = lv(line)
|
|
}
|
|
if len(part) == 0 && strings.HasPrefix(line, "CPU part") {
|
|
part = lv(line)
|
|
}
|
|
if len(revision) == 0 && strings.HasPrefix(line, "CPU revision") {
|
|
revision = lv(line)
|
|
}
|
|
if strings.HasPrefix(line, "processor") {
|
|
cnt += 1
|
|
}
|
|
}
|
|
if len(model) == 0 {
|
|
model = part
|
|
}
|
|
if len(model) == 0 {
|
|
log.Errorf("Failed to get cpu model")
|
|
}
|
|
if len(freq) == 0 {
|
|
log.Errorf("Failed to get cpu MHz")
|
|
}
|
|
if len(cache) == 0 {
|
|
log.Errorf("Failed to get cpu cache size")
|
|
}
|
|
if len(microcode) == 0 {
|
|
microcode = revision
|
|
}
|
|
model = strings.TrimSpace(model)
|
|
info := &types.SCPUInfo{
|
|
Count: cnt,
|
|
Model: model,
|
|
Microcode: microcode,
|
|
}
|
|
if len(cache) > 0 {
|
|
info.Cache, _ = strconv.Atoi(cache)
|
|
}
|
|
if len(freq) > 0 {
|
|
freqF, _ := strconv.ParseFloat(freq, 32)
|
|
info.Freq = int(freqF)
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func ParseDMICPUInfo(lines []string) *types.SDMICPUInfo {
|
|
cnt := 0
|
|
for _, line := range lines {
|
|
if strings.HasPrefix(line, "Processor Information") {
|
|
cnt += 1
|
|
}
|
|
}
|
|
return &types.SDMICPUInfo{
|
|
Nodes: cnt,
|
|
}
|
|
}
|
|
|
|
func ParseDMIMemInfo(lines []string) *types.SDMIMemInfo {
|
|
size := 0
|
|
for _, line := range lines {
|
|
val := valueOfKeyword(line, "Size:")
|
|
if val == nil {
|
|
continue
|
|
}
|
|
// skip 'Volatile Size:' line
|
|
if strings.Contains(line, "Volatile") {
|
|
continue
|
|
}
|
|
value := strings.ToLower(*val)
|
|
if strings.HasSuffix(value, " mb") {
|
|
sizeMb, err := strconv.Atoi(strings.TrimSuffix(value, " mb"))
|
|
if err != nil {
|
|
log.Errorf("parse MB error: %v", err)
|
|
continue
|
|
}
|
|
size += sizeMb
|
|
} else if strings.HasSuffix(value, " gb") {
|
|
sizeGb, err := strconv.Atoi(strings.TrimSuffix(value, " gb"))
|
|
if err != nil {
|
|
log.Errorf("parse GB error: %v", err)
|
|
continue
|
|
}
|
|
size += sizeGb * 1024
|
|
}
|
|
}
|
|
return &types.SDMIMemInfo{Total: size}
|
|
}
|
|
|
|
func ParseDMIIPMIInfo(lines []string) bool {
|
|
for _, line := range lines {
|
|
val := valueOfKeyword(line, "Interface Type:")
|
|
if val != nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func ParseNicInfo(lines []string) []*types.SNicDevInfo {
|
|
ret := make([]*types.SNicDevInfo, 0)
|
|
for _, line := range lines {
|
|
dat := strings.Split(line, " ")
|
|
if len(dat) > 4 {
|
|
dev := dat[0]
|
|
mac, _ := net.ParseMAC(dat[1])
|
|
speed, _ := strconv.Atoi(dat[2])
|
|
up := false
|
|
if dat[3] == "1" {
|
|
up = true
|
|
}
|
|
mtu, _ := strconv.Atoi(dat[4])
|
|
ret = append(ret, &types.SNicDevInfo{
|
|
Dev: dev,
|
|
Mac: mac,
|
|
Speed: speed,
|
|
Up: &up,
|
|
Mtu: mtu,
|
|
})
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func ParseDiskInfo(lines []string, driver string) []*types.SDiskInfo {
|
|
ret := make([]*types.SDiskInfo, 0)
|
|
for _, line := range lines {
|
|
data := strings.Split(line, " ")
|
|
if len(data) <= 6 {
|
|
continue
|
|
}
|
|
dev := data[0]
|
|
sector, _ := strconv.Atoi(data[1])
|
|
size := sector * 512 / 1024 / 1024
|
|
block, _ := strconv.Atoi(data[2])
|
|
rotate := false
|
|
if data[3] == "1" {
|
|
rotate = true
|
|
}
|
|
kernel := data[4]
|
|
pciCls := data[5]
|
|
modinfo := strings.Join(data[6:], " ")
|
|
ret = append(ret, &types.SDiskInfo{
|
|
Dev: dev,
|
|
Sector: int64(sector),
|
|
Block: int64(block),
|
|
Size: int64(size),
|
|
Rotate: rotate,
|
|
ModuleInfo: modinfo,
|
|
Kernel: kernel,
|
|
PCIClass: pciCls,
|
|
Driver: driver,
|
|
})
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func ParsePCIEDiskInfo(lines []string) []*types.SDiskInfo {
|
|
return ParseDiskInfo(lines, baremetal.DISK_DRIVER_PCIE)
|
|
}
|
|
|
|
func ParseSCSIDiskInfo(lines []string) []*types.SDiskInfo {
|
|
return ParseDiskInfo(lines, baremetal.DISK_DRIVER_LINUX)
|
|
}
|
|
|
|
func GetSecureTTYs(lines []string) []string {
|
|
ttys := []string{}
|
|
for _, l := range lines {
|
|
if len(l) == 0 {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(l, "#") {
|
|
continue
|
|
}
|
|
ttys = append(ttys, l)
|
|
}
|
|
return ttys
|
|
}
|
|
|
|
func GetSerialPorts(lines []string) []string {
|
|
// http://wiki.networksecuritytoolkit.org/index.php/Console_Output_and_Serial_Terminals
|
|
ret := []string{}
|
|
for _, l := range lines {
|
|
if strings.Contains(l, "CTS") || strings.Contains(l, "RTS") {
|
|
pos := strings.Index(l, ":")
|
|
if pos < 0 {
|
|
continue
|
|
}
|
|
idx := l[0:pos]
|
|
ret = append(ret, fmt.Sprintf("ttyS%s", idx))
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// ParseSGMap parse command 'sg_map -x' outputs:
|
|
//
|
|
// /dev/sg1 0 2 0 0 0 /dev/sda
|
|
// /dev/sg2 0 2 1 0 0 /dev/sdb
|
|
// /dev/sg3 0 2 2 0 0 /dev/sdc
|
|
func ParseSGMap(lines []string) []compute.SGMapItem {
|
|
ret := make([]compute.SGMapItem, 0)
|
|
for _, l := range lines {
|
|
items := strings.Fields(l)
|
|
if len(items) != 7 {
|
|
continue
|
|
}
|
|
hostNum, _ := strconv.Atoi(items[1])
|
|
bus, _ := strconv.Atoi(items[2])
|
|
scsiId, _ := strconv.Atoi(items[3])
|
|
lun, _ := strconv.Atoi(items[4])
|
|
typ, _ := strconv.Atoi(items[5])
|
|
ret = append(ret, compute.SGMapItem{
|
|
SGDeviceName: items[0],
|
|
HostNumber: hostNum,
|
|
Bus: bus,
|
|
SCSIId: scsiId,
|
|
Lun: lun,
|
|
Type: typ,
|
|
LinuxDeviceName: items[6],
|
|
})
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func Start(closeFd bool, args ...string) (p *os.Process, err error) {
|
|
if args[0], err = exec.LookPath(args[0]); err == nil {
|
|
var procAttr os.ProcAttr
|
|
if closeFd {
|
|
procAttr.Files = []*os.File{nil, nil, nil}
|
|
} else {
|
|
procAttr.Files = []*os.File{os.Stdin,
|
|
os.Stdout, os.Stderr}
|
|
}
|
|
p, err := os.StartProcess(args[0], args, &procAttr)
|
|
if err == nil {
|
|
return p, nil
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
func ParseIPMIUser(lines []string) []compute.IPMIUser {
|
|
ret := make([]compute.IPMIUser, 0)
|
|
for _, l := range lines {
|
|
if strings.HasPrefix(l, "ID") {
|
|
continue
|
|
}
|
|
fields := strings.Fields(l)
|
|
if strings.Contains(l, "Empty User") {
|
|
id, err := strconv.Atoi(fields[0])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
ret = append(ret, compute.IPMIUser{Id: id})
|
|
continue
|
|
}
|
|
if len(fields) != 6 {
|
|
continue
|
|
}
|
|
id, err := strconv.Atoi(fields[0])
|
|
if err != nil {
|
|
continue
|
|
}
|
|
ret = append(ret, compute.IPMIUser{
|
|
Id: id,
|
|
Name: fields[1],
|
|
Priv: fields[5],
|
|
})
|
|
}
|
|
return ret
|
|
}
|
|
|
|
const (
|
|
UUID_EMPTY = "00000000-0000-0000-0000-000000000000"
|
|
)
|
|
|
|
func NormalizeUuid(uuid string) string {
|
|
uuid = strings.ToLower(uuid)
|
|
if uuid == UUID_EMPTY {
|
|
uuid = ""
|
|
}
|
|
return uuid
|
|
}
|