[agent-wdd] 基本完成Info部分的整理

This commit is contained in:
zeaslity
2025-02-13 15:29:26 +08:00
parent e826b55240
commit dabf63f10f
13 changed files with 764 additions and 41 deletions

View File

@@ -6,6 +6,19 @@ import (
)
func addInfoSubcommands(cmd *cobra.Command) {
// 操作系统
os := &cobra.Command{
Use: "os",
Short: "主机操作系统相关的信息",
Run: func(cmd *cobra.Command, args []string) {
os := config.ConfigCache.Agent.OS
os.Gather()
os.SaveConfig()
},
}
// network
network := &cobra.Command{
Use: "network",
@@ -16,5 +29,64 @@ func addInfoSubcommands(cmd *cobra.Command) {
},
}
cmd.AddCommand(network)
// cpu
cpu := &cobra.Command{
Use: "cpu",
Short: "主机cpu相关的信息",
Run: func(cmd *cobra.Command, args []string) {
cpu := config.ConfigCache.Agent.CPU
cpu.Gather()
cpu.Save()
},
}
// memory
memory := &cobra.Command{
Use: "mem",
Short: "主机memory相关的信息",
Run: func(cmd *cobra.Command, args []string) {
mem := config.ConfigCache.Agent.Mem
mem.Gather()
swap := config.ConfigCache.Agent.Swap
swap.Gather()
mem.SaveConfig()
swap.SaveConfig()
},
}
// disk
disk := &cobra.Command{
Use: "disk",
Short: "主机disk相关的信息",
Run: func(cmd *cobra.Command, args []string) {
//disks := config.ConfigCache.Agent.Disks
config.DiskListGather()
config.DiskListSaveConfig()
},
}
// all全部的info
all := &cobra.Command{
Use: "all",
Short: "主机全部相关的信息",
Run: func(cmd *cobra.Command, args []string) {
cpu.Run(cmd, args)
os.Run(cmd, args)
memory.Run(cmd, args)
network.Run(cmd, args)
disk.Run(cmd, args)
},
}
cmd.AddCommand(
all,
os,
cpu,
memory,
network,
disk,
)
}

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

@@ -0,0 +1,177 @@
package config
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

@@ -18,17 +18,28 @@ func init() {
// 配置结构体定义
type Config struct {
TimeStamp string `yaml:"timestamp"`
Agent Agent `yaml:"agent"`
TimeStamp string `yaml:"timestamp"`
ModifiedTimes int `yaml:"modifiedTimes"`
Agent Agent `yaml:"agent"`
}
type Agent struct {
Hostname string `yaml:"hostname"`
Network Network `yaml:"network"`
CPU CPU `yaml:"cpu"`
Mem Memory `yaml:"mem"`
Swap Swap `yaml:"swap"`
Disk []Disk `yaml:"disk"`
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 `yaml:"hostname"`
OsName string `yaml:"os_name"`
OsFamily string `yaml:"os_family"`
OsVersion string `yaml:"os_version"`
OsType string `yaml:"os_type"`
Kernel string `yaml:"kernel"`
Arch string `yaml:"arch"`
}
type Network struct {
@@ -53,9 +64,12 @@ type Interface struct {
}
type CPU struct {
Cores int `yaml:"cores"`
Brand string `yaml:"brand"`
Mhz int `yaml:"mhz"`
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 {
@@ -65,7 +79,7 @@ type Memory struct {
}
type Swap struct {
On bool `yaml:"on"`
Open bool `yaml:"open"`
Size string `yaml:"size"`
}
@@ -74,8 +88,6 @@ type Disk struct {
Size string `yaml:"size"`
Usage string `yaml:"usage"`
Percent string `yaml:"percent"`
VG string `yaml:"vg"`
LV string `yaml:"lv"`
}
func InitConfig() {
@@ -109,6 +121,9 @@ func SaveConfig() {
// 每次写入新的时间
ConfigCache.TimeStamp = utils.CurrentTimeString()
// 每次增加修改文件的次数计数
ConfigCache.ModifiedTimes += 1
data, err := yaml.Marshal(&ConfigCache)
if err != nil {
log.Error("YAML序列化失败: %w", err)

133
agent-wdd/config/Disk.go Normal file
View File

@@ -0,0 +1,133 @@
package config
import (
"agent-wdd/log"
"bufio"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"syscall"
)
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 of %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 = formatDiskSize(totalBytes)
disk.Usage = formatDiskSize(usedBytes)
if totalBytes == 0 {
disk.Percent = "0.00%"
} else {
percent := float64(usedBytes) / float64(totalBytes) * 100
disk.Percent = fmt.Sprintf("%.2f%%", percent)
}
}
func formatDiskSize(bytes uint64) string {
units := []string{"B", "KB", "MB", "GB", "TB", "PB"}
var unitIndex int
size := float64(bytes)
for size >= 1000 && unitIndex < len(units)-1 {
size /= 1000
unitIndex++
}
if unitIndex == 0 {
return strconv.FormatUint(bytes, 10) + units[unitIndex]
}
return strconv.FormatFloat(size, 'f', 1, 64) + units[unitIndex]
}
func DiskListSaveConfig() {
log.Info("Saving INFO => DISK !")
SaveConfig()
}

155
agent-wdd/config/Memory.go Normal file
View File

@@ -0,0 +1,155 @@
package config
import (
"agent-wdd/log"
"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 = formatSize(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 = formatSize(totalKB * 1024)
}
}
func formatSize(bytes uint64) string {
units := []string{"B", "KB", "MB", "GB", "TB", "PB"}
var unitIndex int
size := float64(bytes)
for size >= 1024 && unitIndex < len(units)-1 {
size /= 1024
unitIndex++
}
if unitIndex == 0 {
return strconv.FormatUint(bytes, 10) + units[unitIndex]
}
return strconv.FormatFloat(size, 'f', 1, 64) + units[unitIndex]
}
func (swap *Swap) SaveConfig() {
log.Info("Saving INFO => SWAP !")
ConfigCache.Agent.Swap = *swap
SaveConfig()
}

View File

@@ -25,6 +25,8 @@ type IPInfo struct {
// Gather 获取网络相关的信息
func (network *Network) Gather() {
log.Info("Gathering INFO => NETWORK !")
// 能够联网
network.Internet = CanConnectInternet()

82
agent-wdd/config/OS.go Normal file
View File

@@ -0,0 +1,82 @@
package config
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
// 解析系统信息
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
}
// 获取系统版本
if version, ok := osInfo["VERSION_ID"]; ok {
o.OsVersion = version
} else {
// 针对RedHat系特殊处理
if o.OsFamily == "centos" || o.OsFamily == "rhel" {
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
}

View File

@@ -0,0 +1,28 @@
try {
$ErrorActionPreference = "Stop"
Write-Host "1. Building binary exec file..." -ForegroundColor Cyan
& "C:\Users\wdd\go\bin\gox.exe" -osarch="linux/amd64" -output "build/agent-wdd_{{.OS}}_{{.Arch}}"
# 执行远程ssh命令 ssh root@192.168.35.71 "rm /root/agent-wdd_linux_amd64"
Write-Host "2. Cleaning old binary file..." -ForegroundColor Yellow
ssh root@192.168.35.71 "rm -f /root/agent-wdd_linux_amd64 && rm /usr/local/etc/wdd/agent-wdd-config.yaml"
Write-Host "3. Uploading binary exec..." -ForegroundColor Green
scp build/agent-wdd_linux_amd64 root@192.168.35.71:/root/
Write-Host "4. Exec the command ..." -ForegroundColor Blue
Write-Host ""
Write-Host ""
ssh root@192.168.35.71 "chmod +x agent-wdd_linux_amd64 && ./agent-wdd_linux_amd64 info os"
Write-Host ""
Write-Host ""
Write-Host "5. Cheak Info Result ..." -ForegroundColor Blue
ssh root@192.168.35.71 "cat /usr/local/etc/wdd/agent-wdd-config.yaml"
} catch {
Write-Host "操作失败: $_" -ForegroundColor Red
exit 1
}

View File

@@ -0,0 +1,55 @@
package utils
import (
"agent-wdd/log"
"encoding/json"
"fmt"
)
func BeautifulPrint(object interface{}) {
bytes, err := json.MarshalIndent(object, "", " ")
if err != nil {
log.Error("[BeautifulPrint] - json marshal error ! => %v", object)
}
fmt.Println()
fmt.Println(string(bytes))
fmt.Println()
}
func BeautifulPrintToString(object interface{}) string {
bytes, err := json.MarshalIndent(object, "", " ")
if err != nil {
log.Error("[BeautifulPrint] - json marshal error ! => %v", object)
}
return string(bytes)
}
func BeautifulPrintWithTitle(contend any, title string) {
fmt.Println()
fmt.Println(fmt.Sprintf("content tile is => %s", title))
bytes, _ := json.MarshalIndent(contend, "", " ")
fmt.Println(string(bytes))
fmt.Println("---------- end -----------")
}
func BeautifulPrintListWithTitle(contend []string, title string) {
fmt.Println()
fmt.Println(fmt.Sprintf("content tile is => %s", title))
for _, line := range contend {
fmt.Println(line)
}
fmt.Println("---------- end -----------")
}
func SplitLinePrint() {
fmt.Println()
fmt.Println()
fmt.Println()
}