feat: 完善堡垒机与 Agent 智能运维能力
- 支持 Windows Agent 构建、安装脚本和统一上报协议 - 增强 RDP 剪贴板、虚拟盘和文件传输能力 - 新增 AIOps 上下文工具、告警 RCA trace 落库和回放接口 - 重构 docs 模块文档并补充终端、堡垒机、监控、AI Agent 设计
This commit is contained in:
1 parent
a9513c2056
commit
e0d652d211
169 files changed
+12192
-9116
No files matched your search
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 的上下文切换采集器。
|
||||
// 读取 /proc/stat 中的 ctxt(上下文切换)和 intr(中断)累计值。
|
||||
// 注意:此处返回累计值而非速率,后端可用两次上报差值计算。
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build windows
|
||||
|
||||
package collector
|
||||
|
||||
import "ezops/agent/model"
|
||||
|
||||
type ContextSwitchCollector struct{}
|
||||
|
||||
func NewContextSwitchCollector() *ContextSwitchCollector {
|
||||
return &ContextSwitchCollector{}
|
||||
}
|
||||
|
||||
func (c *ContextSwitchCollector) Collect() (*model.ContextSwitchStat, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 提供服务器各项系统指标的采集能力。
|
||||
// 本文件实现 CPU 使用率与负载采集,主要通过解析 Linux /proc/stat 与 /proc/loadavg 实现。
|
||||
//
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 提供 Windows CPU 指标采集能力。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
type filetime struct {
|
||||
LowDateTime uint32
|
||||
HighDateTime uint32
|
||||
}
|
||||
|
||||
type cpuSample struct {
|
||||
total uint64
|
||||
idle uint64
|
||||
iowait uint64
|
||||
}
|
||||
|
||||
type CPUCollector struct {
|
||||
mu sync.Mutex
|
||||
prev *cpuSample
|
||||
}
|
||||
|
||||
func NewCPUCollector() *CPUCollector {
|
||||
return &CPUCollector{}
|
||||
}
|
||||
|
||||
func (c *CPUCollector) Collect() (model.CPUStat, error) {
|
||||
stat := model.CPUStat{CoreCount: runtime.NumCPU()}
|
||||
cur, err := readCPUSample()
|
||||
if err != nil {
|
||||
return stat, fmt.Errorf("读取 Windows CPU 采样失败: %w", err)
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.prev != nil {
|
||||
totalDelta := cur.total - c.prev.total
|
||||
idleDelta := cur.idle - c.prev.idle
|
||||
if totalDelta > 0 {
|
||||
stat.UsagePercent = float64(totalDelta-idleDelta) / float64(totalDelta) * 100.0
|
||||
if stat.UsagePercent < 0 {
|
||||
stat.UsagePercent = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
c.prev = &cur
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
func readCPUSample() (cpuSample, error) {
|
||||
var idle, kernel, user filetime
|
||||
proc := syscall.NewLazyDLL("kernel32.dll").NewProc("GetSystemTimes")
|
||||
ret, _, err := proc.Call(
|
||||
uintptr(unsafe.Pointer(&idle)),
|
||||
uintptr(unsafe.Pointer(&kernel)),
|
||||
uintptr(unsafe.Pointer(&user)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return cpuSample{}, err
|
||||
}
|
||||
idleTicks := filetimeToUint64(idle)
|
||||
kernelTicks := filetimeToUint64(kernel)
|
||||
userTicks := filetimeToUint64(user)
|
||||
return cpuSample{total: kernelTicks + userTicks, idle: idleTicks}, nil
|
||||
}
|
||||
|
||||
func filetimeToUint64(ft filetime) uint64 {
|
||||
return uint64(ft.HighDateTime)<<32 | uint64(ft.LowDateTime)
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 提供服务器各项系统指标的采集能力。
|
||||
// 本文件实现对所有已挂载的真实文件系统执行 statfs,采集磁盘使用情况。
|
||||
// 使用 golang.org/x/sys/unix 的 Statfs,兼容 Linux 与 macOS。
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 提供 Windows 逻辑磁盘使用率采集能力。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
type DiskCollector struct{}
|
||||
|
||||
func NewDiskCollector() *DiskCollector {
|
||||
return &DiskCollector{}
|
||||
}
|
||||
|
||||
func (d *DiskCollector) Collect() ([]model.DiskStat, error) {
|
||||
mask, _, err := syscall.NewLazyDLL("kernel32.dll").NewProc("GetLogicalDrives").Call()
|
||||
if mask == 0 {
|
||||
return nil, fmt.Errorf("GetLogicalDrives 失败: %w", err)
|
||||
}
|
||||
var disks []model.DiskStat
|
||||
for i := 0; i < 26; i++ {
|
||||
if mask&(1<<uint(i)) == 0 {
|
||||
continue
|
||||
}
|
||||
root := string(rune('A'+i)) + `:\`
|
||||
var freeAvail, totalBytes, totalFree uint64
|
||||
pathPtr, _ := syscall.UTF16PtrFromString(root)
|
||||
ret, _, _ := syscall.NewLazyDLL("kernel32.dll").NewProc("GetDiskFreeSpaceExW").Call(
|
||||
uintptr(unsafe.Pointer(pathPtr)),
|
||||
uintptr(unsafe.Pointer(&freeAvail)),
|
||||
uintptr(unsafe.Pointer(&totalBytes)),
|
||||
uintptr(unsafe.Pointer(&totalFree)),
|
||||
)
|
||||
if ret == 0 || totalBytes == 0 {
|
||||
continue
|
||||
}
|
||||
used := totalBytes - totalFree
|
||||
disks = append(disks, model.DiskStat{
|
||||
Mount: root,
|
||||
Device: root,
|
||||
TotalGB: bytesToGB(totalBytes),
|
||||
UsedGB: bytesToGB(used),
|
||||
UsagePercent: float64(used) / float64(totalBytes) * 100.0,
|
||||
})
|
||||
}
|
||||
return disks, nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector — 磁盘 IO 指标采集器
|
||||
// 通过读取 /proc/diskstats 获取磁盘读写 BPS、IOPS 和 util(磁盘繁忙程度)。
|
||||
//
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build windows
|
||||
|
||||
package collector
|
||||
|
||||
import "ezops/agent/model"
|
||||
|
||||
type DiskIOCollector struct{}
|
||||
|
||||
func NewDiskIOCollector() *DiskIOCollector {
|
||||
return &DiskIOCollector{}
|
||||
}
|
||||
|
||||
func (c *DiskIOCollector) Collect() (model.DiskIOStat, error) {
|
||||
return model.DiskIOStat{}, nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 的 FD 句柄采集器。
|
||||
// 读取 /proc/sys/fs/file-nr 和 /proc/sys/fs/file-max 获取系统级文件句柄使用情况。
|
||||
//
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//go:build windows
|
||||
|
||||
package collector
|
||||
|
||||
import "ezops/agent/model"
|
||||
|
||||
type FDCollector struct{}
|
||||
|
||||
func NewFDCollector() *FDCollector {
|
||||
return &FDCollector{}
|
||||
}
|
||||
|
||||
func (c *FDCollector) Collect() (*model.SocketFDStat, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -193,7 +194,7 @@ func (h *HealthProbeCollector) execCmd(parentCtx context.Context, cfg model.Prob
|
||||
ctx, cancel := context.WithTimeout(parentCtx, timeout)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", cfg.ExecCommand)
|
||||
cmd := execProbeCommand(ctx, cfg.ExecCommand)
|
||||
output, err := cmd.CombinedOutput()
|
||||
latencyMs := int(time.Since(start).Milliseconds())
|
||||
result.LatencyMs = latencyMs
|
||||
@@ -214,3 +215,10 @@ func (h *HealthProbeCollector) execCmd(parentCtx context.Context, cfg model.Prob
|
||||
result.Message = fmt.Sprintf("命令执行成功 (%dms)", latencyMs)
|
||||
return result
|
||||
}
|
||||
|
||||
func execProbeCommand(ctx context.Context, command string) *exec.Cmd {
|
||||
if runtime.GOOS == "windows" {
|
||||
return exec.CommandContext(ctx, "powershell.exe", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command)
|
||||
}
|
||||
return exec.CommandContext(ctx, "sh", "-c", command)
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 提供服务器各项系统指标的采集能力。
|
||||
// 本文件实现内存使用率采集,通过解析 Linux /proc/meminfo 获取。
|
||||
//
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 提供 Windows 内存指标采集能力。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
type memoryStatusEx struct {
|
||||
Length uint32
|
||||
MemoryLoad uint32
|
||||
TotalPhys uint64
|
||||
AvailPhys uint64
|
||||
TotalPageFile uint64
|
||||
AvailPageFile uint64
|
||||
TotalVirtual uint64
|
||||
AvailVirtual uint64
|
||||
AvailExtendedVirtual uint64
|
||||
}
|
||||
|
||||
type MemCollector struct{}
|
||||
|
||||
func NewMemCollector() *MemCollector {
|
||||
return &MemCollector{}
|
||||
}
|
||||
|
||||
func (m *MemCollector) Collect() (model.MemoryStat, error) {
|
||||
var ms memoryStatusEx
|
||||
ms.Length = uint32(unsafe.Sizeof(ms))
|
||||
ret, _, err := syscall.NewLazyDLL("kernel32.dll").NewProc("GlobalMemoryStatusEx").Call(uintptr(unsafe.Pointer(&ms)))
|
||||
if ret == 0 {
|
||||
return model.MemoryStat{}, fmt.Errorf("GlobalMemoryStatusEx 失败: %w", err)
|
||||
}
|
||||
total := ms.TotalPhys
|
||||
avail := ms.AvailPhys
|
||||
used := total - avail
|
||||
swapTotal := ms.TotalPageFile
|
||||
swapAvail := ms.AvailPageFile
|
||||
swapUsed := uint64(0)
|
||||
if swapTotal > swapAvail {
|
||||
swapUsed = swapTotal - swapAvail
|
||||
}
|
||||
stat := model.MemoryStat{
|
||||
TotalGB: bytesToGB(total),
|
||||
UsedGB: bytesToGB(used),
|
||||
AvailableGB: bytesToGB(avail),
|
||||
SwapUsedGB: bytesToGB(swapUsed),
|
||||
UsagePercent: float64(ms.MemoryLoad),
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 提供服务器各项系统指标的采集能力。
|
||||
// 本文件实现网络连接数、网卡流量等网络指标采集。
|
||||
//
|
||||
@@ -18,11 +20,11 @@ import (
|
||||
|
||||
// netSample 保存上一次的网络采样数据,用于计算速率
|
||||
type netSample struct {
|
||||
time time.Time
|
||||
rxBytes uint64
|
||||
txBytes uint64
|
||||
rxErrors uint64
|
||||
txErrors uint64
|
||||
time time.Time
|
||||
rxBytes uint64
|
||||
txBytes uint64
|
||||
rxErrors uint64
|
||||
txErrors uint64
|
||||
}
|
||||
|
||||
// NetworkCollector 网络指标采集器
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 提供 Windows 网络连接基础采集能力。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
type NetworkCollector struct{}
|
||||
|
||||
func NewNetworkCollector() *NetworkCollector {
|
||||
return &NetworkCollector{}
|
||||
}
|
||||
|
||||
func (c *NetworkCollector) Collect() (model.NetworkStat, error) {
|
||||
stat := model.NetworkStat{NicName: firstWindowsNIC()}
|
||||
out, err := exec.Command("netstat", "-ano", "-p", "tcp").Output()
|
||||
if err != nil {
|
||||
return stat, nil
|
||||
}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 4 || strings.ToUpper(fields[0]) != "TCP" {
|
||||
continue
|
||||
}
|
||||
state := strings.ToUpper(fields[3])
|
||||
switch state {
|
||||
case "ESTABLISHED":
|
||||
stat.TCPEstablished++
|
||||
case "TIME_WAIT":
|
||||
stat.TCPTimeWait++
|
||||
case "CLOSE_WAIT":
|
||||
stat.TCPCloseWait++
|
||||
}
|
||||
stat.TCPTotal++
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
func firstWindowsNIC() string {
|
||||
out, err := exec.Command("powershell.exe", "-NoProfile", "-Command", "Get-NetAdapter | Where-Object {$_.Status -eq 'Up'} | Select-Object -First 1 -ExpandProperty Name").Output()
|
||||
if err == nil {
|
||||
if name := strings.TrimSpace(string(bytes.TrimSpace(out))); name != "" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return "Ethernet"
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 提供服务器各项系统指标的采集能力。
|
||||
// 本文件实现监听 TCP 端口的自动发现:
|
||||
// - 扫描 /proc/net/tcp 与 /proc/net/tcp6 中状态为 LISTEN(0A) 的套接字;
|
||||
@@ -35,7 +37,7 @@ var systemProcesses = map[string]bool{
|
||||
"upowerd": true,
|
||||
"rtkit-daemon": true,
|
||||
"switcheroo-contr": true,
|
||||
"power-profiles-": true, // power-profiles-daemon
|
||||
"power-profiles-": true, // power-profiles-daemon
|
||||
"wpa_supplicant": true,
|
||||
"avahi-daemon": true,
|
||||
"cron": true,
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 提供 Windows 监听端口发现能力。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
type PortCollector struct {
|
||||
Policy *model.PortPolicy
|
||||
}
|
||||
|
||||
func NewPortCollector() *PortCollector {
|
||||
return &PortCollector{}
|
||||
}
|
||||
|
||||
func (p *PortCollector) SetPolicy(policy *model.PortPolicy) {
|
||||
p.Policy = policy
|
||||
}
|
||||
|
||||
func (p *PortCollector) Collect() ([]model.PortStat, error) {
|
||||
pidNames := windowsProcessNames()
|
||||
out, err := exec.Command("netstat", "-ano", "-p", "tcp").Output()
|
||||
if err != nil {
|
||||
return []model.PortStat{}, nil
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
var ports []model.PortStat
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 5 || strings.ToUpper(fields[0]) != "TCP" || strings.ToUpper(fields[3]) != "LISTENING" {
|
||||
continue
|
||||
}
|
||||
port := parseWindowsPort(fields[1])
|
||||
if port <= 0 {
|
||||
continue
|
||||
}
|
||||
if p.Policy != nil && p.Policy.IsBlacklistMode() && !p.Policy.InWhitelist(port) {
|
||||
continue
|
||||
}
|
||||
pid, _ := strconv.Atoi(fields[4])
|
||||
key := strconv.Itoa(port) + "|" + strconv.Itoa(pid)
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
seen[key] = true
|
||||
ports = append(ports, model.PortStat{
|
||||
Port: port,
|
||||
Protocol: "tcp",
|
||||
Process: pidNames[pid],
|
||||
PID: pid,
|
||||
})
|
||||
}
|
||||
return ports, nil
|
||||
}
|
||||
|
||||
func parseWindowsPort(addr string) int {
|
||||
addr = strings.Trim(addr, "[]")
|
||||
idx := strings.LastIndex(addr, ":")
|
||||
if idx < 0 || idx+1 >= len(addr) {
|
||||
return 0
|
||||
}
|
||||
port, _ := strconv.Atoi(addr[idx+1:])
|
||||
return port
|
||||
}
|
||||
|
||||
func windowsProcessNames() map[int]string {
|
||||
out, err := exec.Command("tasklist", "/FO", "CSV", "/NH").Output()
|
||||
if err != nil {
|
||||
return map[int]string{}
|
||||
}
|
||||
reader := csv.NewReader(strings.NewReader(string(out)))
|
||||
rows, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
return map[int]string{}
|
||||
}
|
||||
result := map[int]string{}
|
||||
for _, row := range rows {
|
||||
if len(row) < 2 {
|
||||
continue
|
||||
}
|
||||
pid, err := strconv.Atoi(strings.TrimSpace(row[1]))
|
||||
if err == nil {
|
||||
result[pid] = row[0]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector — 进程 TOP 采集器
|
||||
// 扫描 /proc 获取 TOP N 进程完整信息:PID/User/CMD/CPU%/MEM%/FDs
|
||||
// 阿里云 ECS 监控风格 — 直观 6 列布局
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 提供 Windows 进程 TOP 基础采集能力。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ezops/agent/model"
|
||||
)
|
||||
|
||||
type ProcessTopCollector struct{}
|
||||
|
||||
func NewProcessTopCollector() *ProcessTopCollector {
|
||||
return &ProcessTopCollector{}
|
||||
}
|
||||
|
||||
func (p *ProcessTopCollector) Collect(topN int) ([]model.ProcessStat, error) {
|
||||
out, err := exec.Command("tasklist", "/FO", "CSV", "/NH").Output()
|
||||
if err != nil {
|
||||
return []model.ProcessStat{}, nil
|
||||
}
|
||||
rows, err := csv.NewReader(strings.NewReader(string(out))).ReadAll()
|
||||
if err != nil {
|
||||
return []model.ProcessStat{}, nil
|
||||
}
|
||||
var procs []model.ProcessStat
|
||||
for _, row := range rows {
|
||||
if len(row) < 5 {
|
||||
continue
|
||||
}
|
||||
pid, _ := strconv.Atoi(strings.TrimSpace(row[1]))
|
||||
memKB := parseWindowsMemKB(row[4])
|
||||
procs = append(procs, model.ProcessStat{
|
||||
PID: pid,
|
||||
Name: row[0],
|
||||
Cmdline: row[0],
|
||||
User: "",
|
||||
MemoryMB: float64(memKB) / 1024.0,
|
||||
})
|
||||
}
|
||||
sort.Slice(procs, func(i, j int) bool {
|
||||
return procs[i].MemoryMB > procs[j].MemoryMB
|
||||
})
|
||||
if topN > len(procs) {
|
||||
topN = len(procs)
|
||||
}
|
||||
if topN < 0 {
|
||||
topN = 0
|
||||
}
|
||||
return procs[:topN], nil
|
||||
}
|
||||
|
||||
func parseWindowsMemKB(s string) int64 {
|
||||
clean := strings.NewReplacer(",", "", "K", "", "k", "", " ", "").Replace(s)
|
||||
v, _ := strconv.ParseInt(clean, 10, 64)
|
||||
return v
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector — Swap 采集器
|
||||
// 从 /proc/meminfo 读取 SwapTotal / SwapFree
|
||||
// 仅当系统有 swap 分区时返回有效数据
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build windows
|
||||
|
||||
package collector
|
||||
|
||||
import "ezops/agent/model"
|
||||
|
||||
type SwapCollector struct{}
|
||||
|
||||
func NewSwapCollector() *SwapCollector {
|
||||
return &SwapCollector{}
|
||||
}
|
||||
|
||||
func (s *SwapCollector) Collect() (*model.SwapStat, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func parseUint64(s string) (uint64, error) {
|
||||
var v uint64
|
||||
for _, c := range s {
|
||||
if c < '0' || c > '9' {
|
||||
break
|
||||
}
|
||||
v = v*10 + uint64(c-'0')
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !windows
|
||||
|
||||
// Package collector 实现 AIOps Agent 系统静态信息采集。
|
||||
// 仅在 Agent 启动时执行一次,采集 OS 版本、内核版本等信息。
|
||||
//
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//go:build windows
|
||||
|
||||
// Package collector 实现 Windows 系统静态信息采集。 // Logan.Li
|
||||
package collector
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type SystemInfo struct {
|
||||
OSVersion string
|
||||
Kernel string
|
||||
UptimeSeconds int64
|
||||
}
|
||||
|
||||
func CollectSystem() SystemInfo {
|
||||
return SystemInfo{
|
||||
OSVersion: windowsOSCaption(),
|
||||
Kernel: windowsVersion(),
|
||||
UptimeSeconds: windowsUptime(),
|
||||
}
|
||||
}
|
||||
|
||||
func windowsOSCaption() string {
|
||||
out, err := exec.Command("powershell.exe", "-NoProfile", "-Command", "(Get-CimInstance Win32_OperatingSystem).Caption").Output()
|
||||
if err != nil {
|
||||
return "Windows"
|
||||
}
|
||||
if s := strings.TrimSpace(string(out)); s != "" {
|
||||
return s
|
||||
}
|
||||
return "Windows"
|
||||
}
|
||||
|
||||
func windowsVersion() string {
|
||||
out, err := exec.Command("cmd.exe", "/C", "ver").Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
|
||||
func windowsUptime() int64 {
|
||||
out, err := exec.Command("powershell.exe", "-NoProfile", "-Command", "[int]((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime).TotalSeconds").Output()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
var n int64
|
||||
for _, ch := range strings.TrimSpace(string(out)) {
|
||||
if ch >= '0' && ch <= '9' {
|
||||
n = n*10 + int64(ch-'0')
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//go:build windows
|
||||
|
||||
package collector
|
||||
|
||||
func bytesToGB(b uint64) float64 {
|
||||
const gb = 1024.0 * 1024.0 * 1024.0
|
||||
return float64(b) / gb
|
||||
}
|
||||
Reference in new issue
Block a user