// 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 }