新增多个脚本和配置文件,包括构建、上传、测试和主机信息收集功能,增强了项目的可用性和功能性。

This commit is contained in:
zeaslity
2025-03-27 23:26:31 +08:00
parent d8554ae8ae
commit 7dabce3fa5
11 changed files with 1334 additions and 0 deletions

177
agent-wdd/host_info/CPU.go Normal file
View File

@@ -0,0 +1,177 @@
package host_info
import (
"agent-wdd/log"
"bufio"
"bytes"
"io/ioutil"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
)
// Gather 获取CPU相关的信息
func (cpu *CPU) Gather() {
log.Info("Gathering INFO => CPU !")
cpu.getBasicInfo()
cpu.getVirtualizationInfo()
}
func (cpu *CPU) Save() {
log.Info("Saving INFO => CPU !")
ConfigCache.Agent.CPU = *cpu
SaveConfig()
}
func (cpu *CPU) getBasicInfo() {
switch runtime.GOOS {
case "linux":
cpu.getLinuxCPUInfo()
case "windows":
cpu.getWindowsCPUInfo()
case "darwin":
cpu.getDarwinCPUInfo()
default:
// 其他平台处理
}
}
func (cpu *CPU) getLinuxCPUInfo() {
data, err := os.ReadFile("/proc/cpuinfo")
if err != nil {
return
}
contents := string(data)
cpu.Cores = strings.Count(contents, "processor\t:")
// 创建带缓冲的扫描器
scanner := bufio.NewScanner(bytes.NewReader(data))
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "model name") {
cpu.Brand = strings.TrimSpace(strings.SplitN(line, ":", 2)[1])
}
if strings.Contains(line, "cpu MHz") {
cpu.Mhz = strings.TrimSpace(strings.Split(line, ":")[1])
}
}
cpu.Arch = runtime.GOARCH
}
func (cpu *CPU) getWindowsCPUInfo() {
cmd := exec.Command("wmic", "cpu", "get", "Name,NumberOfCores,CurrentClockSpeed", "/VALUE")
output, err := cmd.Output()
if err != nil {
return
}
info := string(output)
lines := strings.Split(info, "\n")
for _, line := range lines {
if strings.HasPrefix(line, "Name=") {
cpu.Brand = strings.TrimSpace(strings.SplitN(line, "=", 2)[1])
}
if strings.HasPrefix(line, "NumberOfCores=") {
cores, _ := strconv.Atoi(strings.SplitN(line, "=", 2)[1])
cpu.Cores = cores
}
if strings.HasPrefix(line, "CurrentClockSpeed=") {
cpu.Mhz = strings.SplitN(line, "=", 2)[1]
}
}
// 架构处理
switch runtime.GOARCH {
case "amd64":
cpu.Arch = "x86_64"
default:
cpu.Arch = runtime.GOARCH
}
}
func (cpu *CPU) getVirtualizationInfo() {
switch runtime.GOOS {
case "linux":
cpu.getLinuxVirtualization()
case "windows":
cpu.getWindowsVirtualization()
}
}
func (cpu *CPU) getLinuxVirtualization() {
// 检查CPU虚拟化支持
data, err := ioutil.ReadFile("/proc/cpuinfo")
if err == nil {
if strings.Contains(string(data), "vmx") || strings.Contains(string(data), "svm") {
cpu.Virt = "supported"
}
}
// 检测是否在虚拟机中
if _, err := os.Stat("/sys/hypervisor/uid"); !os.IsNotExist(err) {
cpu.Virt = "virtualized"
if data, err := ioutil.ReadFile("/sys/hypervisor/type"); err == nil {
cpu.Hypervisor = strings.TrimSpace(string(data))
}
return
}
// 使用systemd-detect-virt
cmd := exec.Command("systemd-detect-virt")
if output, err := cmd.Output(); err == nil {
result := strings.TrimSpace(string(output))
if result != "none" {
cpu.Virt = "virtualized"
cpu.Hypervisor = result
}
}
}
func (cpu *CPU) getWindowsVirtualization() {
cmd := exec.Command("wmic", "computersystem", "get", "model", "/VALUE")
output, err := cmd.Output()
if err != nil {
return
}
model := strings.ToLower(string(output))
switch {
case strings.Contains(model, "virtualbox"):
cpu.Hypervisor = "VirtualBox"
case strings.Contains(model, "vmware"):
cpu.Hypervisor = "VMware"
case strings.Contains(model, "kvm"):
cpu.Hypervisor = "KVM"
case strings.Contains(model, "hyper-v"):
cpu.Hypervisor = "Hyper-V"
case strings.Contains(model, "xen"):
cpu.Hypervisor = "Xen"
}
if cpu.Hypervisor != "" {
cpu.Virt = "virtualized"
}
}
func (cpu *CPU) getDarwinCPUInfo() {
// macOS实现
cmd := exec.Command("sysctl", "-n", "machdep.cpu.brand_string")
if output, err := cmd.Output(); err == nil {
cpu.Brand = strings.TrimSpace(string(output))
}
cmd = exec.Command("sysctl", "-n", "hw.ncpu")
if output, err := cmd.Output(); err == nil {
if cores, err := strconv.Atoi(strings.TrimSpace(string(output))); err == nil {
cpu.Cores = cores
}
}
cpu.Arch = runtime.GOARCH
}

View File

@@ -0,0 +1,206 @@
package host_info
import (
"agent-wdd/log"
"agent-wdd/utils"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
)
var WddConfigFilePath = "/usr/local/etc/wdd/agent-wdd-config.yaml"
// ConfigCache 配置缓存
var ConfigCache = &Config{
TimeStamp: "",
ModifiedTimes: 0,
Agent: Agent{
OS: OS{},
Network: Network{},
CPU: CPU{},
Mem: Memory{},
Swap: Swap{},
Disks: []Disk{},
},
}
func init() {
// 根据运行的操作系统不同, 修改 WddConfigFilePath 的位置
if runtime.GOOS == "windows" {
homedir, _ := os.UserHomeDir()
WddConfigFilePath = homedir + "\\agent-wdd\\agent-wdd-config.yaml"
}
}
type Config struct {
TimeStamp string `yaml:"timestamp"`
ModifiedTimes int `yaml:"modifiedTimes"`
Agent Agent `yaml:"agent"`
}
type Agent struct {
OS OS `yaml:"os"`
Network Network `yaml:"network"`
CPU CPU `yaml:"cpu"`
Mem Memory `yaml:"mem"`
Swap Swap `yaml:"swap"`
Disks []Disk `yaml:"disks"`
}
type OS struct {
Hostname string `mapstructure:"hostname" yaml:"hostname" comment:"主机名"`
OsName string `mapstructure:"os_name" yaml:"os_name" comment:"操作系统名称"`
OsFamily string `mapstructure:"os_family" yaml:"os_family" comment:"操作系统家族"`
OsVersion string `mapstructure:"os_version" yaml:"os_version" comment:"操作系统版本"`
OsType string `mapstructure:"os_type" yaml:"os_type" comment:"操作系统类型"`
Kernel string `mapstructure:"kernel" yaml:"kernel" comment:"内核版本"`
Arch string `mapstructure:"arch" yaml:"arch" comment:"架构"`
IsUbuntuType bool `mapstructure:"is_ubuntu_type" yaml:"is_ubuntu_type" comment:"是否是ubuntu类型的操作系统"`
PackInit bool `mapstructure:"pack_init" yaml:"pack_init" comment:"是否初始化ubuntu需要"`
OSReleaseCode string `mapstructure:"os_release_code" yaml:"os_release_code" comment:"主机操作系统的发行版代号, focal之类的"`
}
type Network struct {
Internet int `yaml:"internet" comment:"是否能够上网 外网为9 国内为7 不能上网为1"`
Public PublicInfo `yaml:"public"`
Interfaces []Interface `yaml:"interfaces"`
}
type PublicInfo struct {
IPv4 string `yaml:"ipv4"`
IPv6 string `yaml:"ipv6"`
Country string `yaml:"country"`
City string `yaml:"city"`
ASN string `yaml:"asn"`
Timezone string `yaml:"timezone"`
}
type Interface struct {
Name string `yaml:"name"`
IPv4 string `yaml:"ipv4"`
IPv6 string `yaml:"ipv6"`
MAC string `yaml:"mac"`
MTU int `yaml:"mtu"`
}
type CPU struct {
Cores int `yaml:"cores"`
Brand string `yaml:"brand"`
Mhz string `yaml:"mhz"`
Arch string `yaml:"arch"`
Virt string `yaml:"virt"`
Hypervisor string `yaml:"hypervisor"`
}
type Memory struct {
Size string `yaml:"size"`
Type string `yaml:"type"`
Speed int `yaml:"speed"`
}
type Swap struct {
Open bool `yaml:"open"`
Size string `yaml:"size"`
}
type Disk struct {
Path string `yaml:"path"`
Size string `yaml:"size"`
Usage string `yaml:"usage"`
Percent string `yaml:"percent"`
}
func InitConfig() {
// 检查配置文件是否存在
if !utils.FileOrFolderExists(WddConfigFilePath) {
utils.AppendContentToFile("", WddConfigFilePath)
}
v := viper.New()
v.SetConfigFile(WddConfigFilePath)
v.SetConfigType("yaml")
if err := v.ReadInConfig(); err != nil {
log.Error("读取配置文件失败: %w", err)
panic(err)
}
if err := v.Unmarshal(&ConfigCache); err != nil {
log.Error("解析配置失败: %w", err)
panic(err)
}
}
// 写入配置文件
func SaveConfig() {
// 每次写入新的时间
ConfigCache.TimeStamp = utils.CurrentTimeString()
// 每次增加修改文件的次数计数
ConfigCache.ModifiedTimes += 1
data, err := yaml.Marshal(&ConfigCache)
if err != nil {
log.Error("YAML序列化失败: %w", err)
}
if err := os.WriteFile(WddConfigFilePath, data, 0644); err != nil {
log.Error("写入文件失败: %w", err)
}
}
// 归一化配置-重命名主机名
func (c *Config) NormalizeConfig() {
// 重命名主机名
log.Info("归一化主机配置")
// 重新读取配置
InitConfig()
// 主机名称应该为 City(格式为首字母大写)-amd64-内网IP的最后一段(格式化为2位)-公网IPv4(如果公网IPv4为空则使用内网IPv4; ip的格式为127-0-0-1)
// 获取城市(格式为首字母大写)
city := strings.Title(ConfigCache.Agent.Network.Public.City)
// 获取公网IPv4 ip的格式为127-0-0-1
ipInfo := ConfigCache.Agent.Network.Public.IPv4
if ipInfo == "" {
ipInfo = ConfigCache.Agent.Network.Interfaces[0].IPv4
} else {
// 可以获取到公网IPv4, 修改IP的格式
innerIpv4 := ConfigCache.Agent.Network.Interfaces[0].IPv4
ipSegments := strings.Split(innerIpv4, ".")
prefix := fmt.Sprintf("%02s", ipSegments[len(ipSegments)-1])
ipInfo = prefix + "." + ipInfo
}
ipInfo = strings.ReplaceAll(ipInfo, ".", "-")
// 获取架构
arch := ConfigCache.Agent.CPU.Arch
uniformHostname := city + "-" + arch + "-" + ipInfo
// 重命名主机名
log.Info("重命名主机名: %s", uniformHostname)
cmd := exec.Command("hostnamectl", "set-hostname", uniformHostname)
// 执行命令
cmd.Run()
// 更新配置
ConfigCache.Agent.OS.Hostname = uniformHostname
// 更新配置
SaveConfig()
}

114
agent-wdd/host_info/Disk.go Normal file
View File

@@ -0,0 +1,114 @@
package host_info
import (
"agent-wdd/log"
"bufio"
"os"
"path/filepath"
"strings"
)
var CommonDiskPath = []string{
"/",
"/var",
"/var/lib/docker",
"/home",
"/user",
"var/log",
}
func CheckCommonMounts() (map[string]bool, error) {
// 读取/proc/mounts获取所有挂载点
file, err := os.Open("/proc/mounts")
if err != nil {
return nil, err
}
defer file.Close()
mountPoints := make(map[string]bool)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
fields := strings.Fields(line)
if len(fields) < 2 {
continue
}
mountPoint := filepath.Clean(fields[1])
mountPoints[mountPoint] = true
}
if err := scanner.Err(); err != nil {
return nil, err
}
return mountPoints, err
}
func DiskListGather() {
log.Info("Gathering INFO => DISK !")
var diskList []Disk
// 拿到所有挂载点
mountPoints, _ := CheckCommonMounts()
// 常用目录
for _, diskPath := range CommonDiskPath {
// 转换为绝对路径并标准化
absPath, err := filepath.Abs(diskPath)
if err != nil {
// 处理错误,例如路径无效,这里选择跳过
continue
}
cleanPath := filepath.Clean(absPath)
if mountPoints[cleanPath] {
// 挂载点存在,计算使用情况
disk := Disk{Path: cleanPath}
disk.calculateDiskUsage()
diskList = append(diskList, disk)
}
}
// 赋值回去
ConfigCache.Agent.Disks = diskList
//utils.BeautifulPrint(diskList)
}
// 计算磁盘使用情况
func (disk *Disk) calculateDiskUsage() {
// var stat syscall.Statfs_t
// err := syscall.Statfs(disk.Path, &stat)
// if err != nil {
// log.Error("disk syscall error: %v", err)
// disk.Size = "0B"
// disk.Usage = "0B"
// disk.Percent = "0.00%"
// return
// }
// // 计算存储空间大小
// totalBytes := stat.Blocks * uint64(stat.Bsize)
// availBytes := stat.Bavail * uint64(stat.Bsize)
// usedBytes := totalBytes - availBytes
// // 格式化输出
// disk.Size = utils.HumanDiskSize(totalBytes)
// disk.Usage = utils.HumanDiskSize(usedBytes)
// if totalBytes == 0 {
// disk.Percent = "0.00%"
// } else {
// percent := float64(usedBytes) / float64(totalBytes) * 100
// disk.Percent = fmt.Sprintf("%.2f%%", percent)
// }
}
func DiskListSaveConfig() {
log.Info("Saving INFO => DISK !")
SaveConfig()
}

View File

@@ -0,0 +1,140 @@
package host_info
import (
"agent-wdd/log"
"agent-wdd/utils"
"os"
"os/exec"
"strconv"
"strings"
)
func (mem *Memory) Gather() {
// 获取内存总大小
data, err := os.ReadFile("/proc/meminfo")
if err != nil {
mem.Size = "0B"
mem.Type = ""
mem.Speed = 0
return
}
lines := strings.Split(string(data), "\n")
//var totalKB uint64
for _, line := range lines {
if strings.HasPrefix(line, "MemTotal:") {
fields := strings.Fields(line)
if len(fields) >= 3 {
kb, err := strconv.ParseUint(fields[1], 10, 64)
if err != nil {
mem.Size = "0B"
} else {
mem.Size = utils.HumanSize(kb * 1024)
}
break
}
}
}
// 获取Type和Speed
mem.Type, mem.Speed = getMemoryTypeAndSpeed()
}
func getMemoryTypeAndSpeed() (string, int) {
cmd := exec.Command("dmidecode", "-t", "memory")
output, err := cmd.CombinedOutput()
if err != nil {
return "", 0
}
var memType string
var speed int
lines := strings.Split(string(output), "\n")
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, "Type:") {
parts := strings.SplitN(trimmed, ":", 2)
if len(parts) == 2 {
memType = strings.TrimSpace(parts[1])
// 可能dmidecode返回的类型中有更多细节例如"DDR4"或其他
// 例如,如果类型是"Unknown",可能需要处理
if memType == "Unknown" || memType == "Other" {
memType = ""
}
}
} else if strings.HasPrefix(trimmed, "Speed:") {
parts := strings.SplitN(trimmed, ":", 2)
if len(parts) == 2 {
speedStr := strings.TrimSpace(parts[1])
// 可能的格式如 "2667 MHz" 或 "Unknown"
if speedStr == "Unknown" {
continue
}
speedStr = strings.TrimSuffix(speedStr, " MHz")
s, err := strconv.Atoi(speedStr)
if err == nil {
speed = s
}
}
}
}
return memType, speed
}
func (mem *Memory) SaveConfig() {
log.Info("Saving INFO => MEM !")
ConfigCache.Agent.Mem = *mem
SaveConfig()
}
func (swap *Swap) Gather() {
log.Info("Gathering INFO => SWAP !")
const swapsFile = "/proc/swaps"
data, err := os.ReadFile(swapsFile)
if err != nil {
swap.Open = false
swap.Size = "0B"
return
}
lines := strings.Split(strings.TrimSpace(string(data)), "\n")
if len(lines) < 2 { // 空文件或只有标题行
swap.Open = false
swap.Size = "0B"
return
}
var totalKB uint64
for _, line := range lines[1:] {
line = strings.TrimSpace(line)
if line == "" {
continue
}
fields := strings.Fields(line)
if len(fields) < 3 {
continue
}
sizeKB, err := strconv.ParseUint(fields[2], 10, 64)
if err != nil {
continue
}
totalKB += sizeKB
}
if totalKB == 0 {
swap.Open = false
swap.Size = "0B"
} else {
swap.Open = true
swap.Size = utils.HumanSize(totalKB * 1024)
}
}
func (swap *Swap) SaveConfig() {
log.Info("Saving INFO => SWAP !")
ConfigCache.Agent.Swap = *swap
SaveConfig()
}

View File

@@ -0,0 +1,273 @@
package host_info
import (
"agent-wdd/log"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"regexp"
"time"
)
// 能够联网,就大于这个数字 外网9 国内7 不能联网1 未知0
const InternetBaseLine = 1
// 定义响应数据结构体
type IPInfo struct {
IP string `json:"ip"`
City string `json:"city"`
Region string `json:"region"`
Country string `json:"country"`
Loc string `json:"loc"`
Org string `json:"org"`
Postal string `json:"postal"`
Timezone string `json:"timezone"`
}
// Gather 获取网络相关的信息
func (network *Network) Gather() {
log.Info("Gathering INFO => NETWORK !")
// 能够联网
network.Internet = CanConnectInternet()
// 获取公网的相关信息
network.Public = network.Public.GetPublicInfo()
//获取本机网卡相关的内容
network.Interfaces = GetInterfaces()
}
// GetInterfaces 获取本机网卡相关的内容
func GetInterfaces() []Interface {
interfaces := []Interface{}
// 获取所有网卡信息
netInterfaces, err := net.Interfaces()
// log.Info("all network interfaces: %v", netInterfaces)
if err != nil {
log.Error("获取网卡信息失败: %v", err)
return interfaces
}
for _, netInterface := range netInterfaces {
// 过滤掉没有地址的网卡
addrs, err := netInterface.Addrs()
if err != nil || len(addrs) == 0 {
continue
}
// 检查网卡名称是否有效
if !isValidNICName(netInterface.Name) {
continue
}
// 创建 Interface 对象
iface := Interface{
Name: netInterface.Name,
MAC: netInterface.HardwareAddr.String(),
MTU: netInterface.MTU,
}
// 获取 IPv4 和 IPv6 地址
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}
if ipNet.IP.To4() != nil {
iface.IPv4 = ipNet.IP.String()
} else if ipNet.IP.To16() != nil {
iface.IPv6 = ipNet.IP.String()
}
}
interfaces = append(interfaces, iface)
}
return interfaces
}
func (network *Network) SaveConfig() {
ConfigCache.Agent.Network = *network
SaveConfig()
}
// CanConnectInternet 判定主机能够上网 外网为9 国内为7 不能上网为1
func CanConnectInternet() int {
// 读取 config 文件,判定能否上网
internetCode := ConfigCache.Agent.Network.Internet
if internetCode == 0 {
// 没有相关的信息,需要重新判定
internetCode = judgeCanConnectInternet()
// 持久化保存
ConfigCache.Agent.Network.Internet = internetCode
}
return internetCode
}
// judgeCanConnectInternet 请求网址判定主机能否上网 请求 www.google.com 如果请求正常 返回9 如果超时三秒 请求baidu.com如果没有错误返回7 如果错误返回1
func judgeCanConnectInternet() int {
client := http.Client{
Timeout: 3 * time.Second,
}
results := make(chan int, 2)
go func() {
_, err := client.Get("https://www.google.com")
if err == nil {
results <- 9
} else {
results <- 1
}
}()
go func() {
_, err := client.Get("https://www.baidu.com")
if err == nil {
results <- 7
} else {
results <- 1
}
}()
maxResult := 1
for i := 0; i < 2; i++ {
result := <-results
if result > maxResult {
maxResult = result
}
}
return maxResult
}
// GetPublicInfo 获取服务器的公网信息
func (p PublicInfo) GetPublicInfo() PublicInfo {
// 无法联网, 假信息
fakePublicInfo := PublicInfo{
IPv4: "1.1.1.1",
IPv6: "2400::1",
Country: "CN",
City: "Shanghai",
ASN: "Wdd Inc",
Timezone: "Asia/Shanghai",
}
if CanConnectInternet() == InternetBaseLine {
// 持久化保存
ConfigCache.Agent.Network.Public = fakePublicInfo
return fakePublicInfo
}
// 可以联网
// 创建带有超时的HTTP客户端
client := &http.Client{
Timeout: 3 * time.Second,
}
// 创建新的请求对象
req, err := http.NewRequest("GET", "https://ipinfo.io", nil)
if err != nil {
log.Error("创建请求失败: %v", err)
return fakePublicInfo
}
// 设置请求头
req.Header.Add("Authorization", "Bearer 6ecb0db9bd8f19")
req.Header.Add("Accept", "application/json")
// 发送请求
resp, err := client.Do(req)
if err != nil {
log.Error("请求PublicInfo失败: %s", err.Error())
return fakePublicInfo
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
log.Error("非200状态码: %d", resp.StatusCode)
}
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error("读取响应失败: %v", err)
}
// 解析JSON数据
var info IPInfo
if err := json.Unmarshal(body, &info); err != nil {
log.Error("JSON解析失败: %v", err)
}
// 打印解析结果
// log.Info("IP信息:\n%+v\n", info)
// 保存信息
p.IPv4 = info.IP
p.ASN = info.Org
p.Country = info.Country
p.City = info.City
p.Timezone = info.Timezone
ConfigCache.Agent.Network.Public = p
return p
}
func isValidNICName(name string) bool {
// 定义传统命名规则正则表达式
// eth0, eth1, wlan0, wlan1 等
traditionalPattern := `^(eth|wlan)[0-9]+$`
// 定义现代命名规则正则表达式
// 支持 eno1, ens3, enp0s3, enx<MAC>, wlp2s0 等格式
modernPattern := `^(en|wl)(o[0-9]+|s[0-9]+|p[0-9]+s[0-9]+|x[0-9a-fA-F]{12})$`
// 编译正则表达式
traditionalRegex := regexp.MustCompile(traditionalPattern)
modernRegex := regexp.MustCompile(modernPattern)
// 检查是否匹配传统命名或现代命名规则
return traditionalRegex.MatchString(name) || modernRegex.MatchString(name)
}
func main() {
// 测试用例
testCases := []string{
"eth0", // 传统以太网命名
"wlan1", // 传统无线网卡命名
"eno1", // 板载网卡
"ens3", // 插槽网卡
"enp0s3", // PCI网卡
"enx0a1b2c3d4e5f", // 基于MAC地址的命名
"wlp2s0", // 无线PCI网卡
"eth", // 无效:缺少数字
"enp0", // 无效:不符合现代规则
"abc123", // 无效:完全不匹配
}
for _, tc := range testCases {
if isValidNICName(tc) {
fmt.Printf("%s: 有效网卡名称\n", tc)
} else {
fmt.Printf("%s: 无效网卡名称\n", tc)
}
}
}

117
agent-wdd/host_info/OS.go Normal file
View File

@@ -0,0 +1,117 @@
package host_info
import (
"agent-wdd/log"
"bufio"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
)
func (os *OS) SaveConfig() {
log.Info("Saving INFO => OS !")
ConfigCache.Agent.OS = *os
SaveConfig()
}
func (o *OS) Gather() {
log.Info("Gathering INFO => OS !")
// 获取主机名
if hostname, err := os.Hostname(); err == nil {
o.Hostname = hostname
}
o.OsType = "linux" // 固定为linux
o.IsUbuntuType = true // 默认为ubuntu
// 解析系统信息
file, err := os.Open("/etc/os-release")
if err == nil {
defer file.Close()
scanner := bufio.NewScanner(file)
osInfo := make(map[string]string)
for scanner.Scan() {
line := scanner.Text()
if parts := strings.SplitN(line, "=", 2); len(parts) == 2 {
key := parts[0]
value := strings.Trim(parts[1], `"`)
osInfo[key] = value
}
}
//utils.BeautifulPrint(osInfo)
// 获取操作系统名称
if name, ok := osInfo["PRETTY_NAME"]; ok {
o.OsName = name
}
// 获取系统家族
if id, ok := osInfo["ID"]; ok {
o.OsFamily = id
}
// 判定系统是否是ubuntu类型
if o.OsFamily == "ubuntu" || o.OsFamily == "debian" || o.OsFamily == "linuxmint" || o.OsFamily == "elementary" || o.OsFamily == "pop" || o.OsFamily == "mint" || o.OsFamily == "kali" || o.OsFamily == "deepin" || o.OsFamily == "zorin" {
o.IsUbuntuType = true
} else {
// 设置系统为非ubuntus
o.IsUbuntuType = false
}
// 获取系统版本
if version, ok := osInfo["VERSION_ID"]; ok {
o.OsVersion = version
} else {
// 针对RedHat系特殊处理
if o.OsFamily == "centos" || o.OsFamily == "rhel" {
// 针对RedHat系特殊处理
if data, err := os.ReadFile("/etc/redhat-release"); err == nil {
re := regexp.MustCompile(`\d+(\.\d+)+`)
if match := re.FindString(string(data)); match != "" {
o.OsVersion = match
}
}
}
}
}
// 获取内核版本
if out, err := exec.Command("uname", "-r").Output(); err == nil {
o.Kernel = strings.TrimSpace(string(out))
}
// 获取系统架构
o.Arch = runtime.GOARCH
// 获取系统发行版代号
if o.IsUbuntuType {
o.OSReleaseCode = judgeUbuntuReleaseFromOsVersion(o.OsVersion)
} else {
o.OSReleaseCode = "non-ubuntu"
}
}
func judgeUbuntuReleaseFromOsVersion(osVersion string) string {
switch osVersion {
case "16.04":
return "xenial"
case "18.04":
return "bionic"
case "20.04":
return "focal"
case "22.04":
return "jammy"
default:
return "ubuntu-unknown"
}
}