[agent-wdd] 完成自定义log部分;完成info network部分; 项目结构基本完成

This commit is contained in:
zeaslity
2025-02-11 17:27:41 +08:00
parent 66dca6a080
commit e826b55240
38 changed files with 39009 additions and 33930 deletions

View File

@@ -0,0 +1,42 @@
timestamp: 2025-02-10 15:19:30
agent:
hostname: Shanghai-amd64-01
network:
# 2代表能上外网 1代表能上国内网 0代表无法访问网络
internet: 2
# 服务器公网信息
public:
ipv4: 42.192.52.227
ipv6: xxx
country: CN
city: Shanghai
asn: Oracle
interfaces:
- name: eth0
ipv4: 192.168.233.5
mac: asdasdasd
cpu:
cores: 4
brand: Intel Xeon Gold 5118
mhz: 2400
mem:
size: 4G
type: ddr4
speed: 2400
swap:
on: false
size: 2G
disk:
- path: /
size: 50G
usage: 12G
percent: 12/50
vg: ubuntuvg
lv: datalv

20
agent-wdd/cmd/Info.go Normal file
View File

@@ -0,0 +1,20 @@
package cmd
import (
"agent-wdd/config"
"github.com/spf13/cobra"
)
func addInfoSubcommands(cmd *cobra.Command) {
// network
network := &cobra.Command{
Use: "network",
Short: "主机Network相关的信息",
Run: func(cmd *cobra.Command, args []string) {
network := config.ConfigCache.Agent.Network
network.Gather()
},
}
cmd.AddCommand(network)
}

View File

@@ -1,14 +1,13 @@
package cmd
import (
"agent-wdd/config"
"agent-wdd/log"
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
)
const WddConfigFilePath = "/usr/local/etc/wdd/"
var rootCmd = &cobra.Command{
Use: "wdd",
Short: "wdd应用程序是wdd封装的NB工具",
@@ -23,7 +22,7 @@ var projectBase = ""
func init() {
cobra.OnInitialize(initConfig)
cobra.OnInitialize(config.InitConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
rootCmd.PersistentFlags().StringVarP(&projectBase, "projectbase", "b", "",
@@ -38,24 +37,6 @@ func init() {
viper.SetDefault("license", "apache")
}
func initConfig() {
// Don't forget to read config either from cfgFile or from home directory!
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
// Search config in home directory with name ".cobra" (without extension).
viper.AddConfigPath(WddConfigFilePath)
viper.SetConfigName(".cobra")
}
if err := viper.ReadInConfig(); err != nil {
fmt.Println("Can't read config:", err)
os.Exit(1)
}
}
func Execute() {
// 1. base命令
@@ -103,10 +84,8 @@ func Execute() {
infoCmd := &cobra.Command{
Use: "info",
Short: "打印主机详细信息",
Run: func(cmd *cobra.Command, args []string) {
// 实现info逻辑
},
}
addInfoSubcommands(infoCmd)
// 8. version命令
versionCmd := &cobra.Command{
@@ -114,6 +93,10 @@ func Execute() {
Short: "打印版本信息",
Run: func(cmd *cobra.Command, args []string) {
// 实现version逻辑
log.Debug("来自王达达的礼物 !")
log.Info("来自王达达的礼物 !")
log.Warning("来自王达达的礼物 !")
log.Error("来自王达达的礼物 !")
},
}
@@ -149,8 +132,4 @@ func Execute() {
fmt.Println(err)
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

120
agent-wdd/config/Config.go Normal file
View File

@@ -0,0 +1,120 @@
package config
import (
"agent-wdd/log"
"agent-wdd/utils"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
"os"
)
var WddConfigFilePath = "/usr/local/etc/wdd/agent-wdd-config.yaml"
var ConfigCache *Config
func init() {
// 根据运行的操作系统不同, 修改 WddConfigFilePath 的位置
}
// 配置结构体定义
type Config struct {
TimeStamp string `yaml:"timestamp"`
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"`
}
type Network struct {
Internet int `yaml:"internet"`
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"`
}
type Interface struct {
Name string `yaml:"name"`
IPv4 string `yaml:"ipv4"`
IPv6 string `yaml:"ipv6"`
MAC string `yaml:"mac"`
}
type CPU struct {
Cores int `yaml:"cores"`
Brand string `yaml:"brand"`
Mhz int `yaml:"mhz"`
}
type Memory struct {
Size string `yaml:"size"`
Type string `yaml:"type"`
Speed int `yaml:"speed"`
}
type Swap struct {
On bool `yaml:"on"`
Size string `yaml:"size"`
}
type Disk struct {
Path string `yaml:"path"`
Size string `yaml:"size"`
Usage string `yaml:"usage"`
Percent string `yaml:"percent"`
VG string `yaml:"vg"`
LV string `yaml:"lv"`
}
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()
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)
}
}

View File

@@ -1,67 +0,0 @@
package config
import (
"net"
"runtime"
)
type AgentServerInfo struct {
NetworkInfo
CPUInfo
}
type NetworkInfo struct {
Interfaces []InterfaceInfo
}
type InterfaceInfo struct {
Name string
HardwareAddr string
Addrs []string
}
func GetNetworkInfo() (*NetworkInfo, error) {
interfaces, err := net.Interfaces()
if err != nil {
return nil, err
}
var networkInfo NetworkInfo
for _, iface := range interfaces {
var addrs []string
addresses, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addresses {
addrs = append(addrs, addr.String())
}
interfaceInfo := InterfaceInfo{
Name: iface.Name,
HardwareAddr: iface.HardwareAddr.String(),
Addrs: addrs,
}
networkInfo.Interfaces = append(networkInfo.Interfaces, interfaceInfo)
}
return &networkInfo, nil
}
type CPUInfo struct {
ModelName string
Cores int
Mhz float64
}
func GetCpuInfo() CPUInfo {
info := CPUInfo{}
info.ModelName = runtime.GOARCH
info.Cores = runtime.NumCPU()
info.Mhz = 0.0
return info
}
func (info *AgentServerInfo) GetAgentInfo() {
}

View File

@@ -1,25 +1,149 @@
package config
import (
"agent-wdd/log"
"encoding/json"
"io"
"net/http"
"time"
)
// CanConnectInternet 判定主机能否上网 请求 www.google.com 如果请求正常 返回2 如果超时三秒 请求baidu.com如果没有错误返回1 如果错误返回0
// 能够联网,就大于这个数字 外网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() {
// 能够联网
network.Internet = CanConnectInternet()
// 获取公网的相关信息
pub := PublicInfo{}
pub.GetPublicInfo()
network.Public = pub
//获取本机网卡相关的内容
}
// CanConnectInternet 判定主机能够上网
func CanConnectInternet() int {
// 读取 config 文件,判定能否上网
internetCode := ConfigCache.Agent.Network.Internet
if internetCode == 0 {
// 没有相关的信息,需要重新判定
internetCode = judgeCanConnectInternet()
// 持久化保存
ConfigCache.Agent.Network.Internet = internetCode
SaveConfig()
}
return internetCode
}
// judgeCanConnectInternet 请求网址判定主机能否上网 请求 www.google.com 如果请求正常 返回9 如果超时三秒 请求baidu.com如果没有错误返回7 如果错误返回1
func judgeCanConnectInternet() int {
client := http.Client{
Timeout: 3 * time.Second,
}
_, err := client.Get("https://www.google.com")
if err == nil {
return 2
return 9
}
_, err = client.Get("https://www.baidu.com")
if err == nil {
return 1
return 7
}
return 0
return 1
}
// GetPublicInfo 获取服务器的公网信息
func (p PublicInfo) GetPublicInfo() {
if CanConnectInternet() == InternetBaseLine {
// 无法联网, 假信息
fakePublicInfo := PublicInfo{
IPv4: "1.1.1.1",
IPv6: "2400::1",
Country: "CN",
City: "Shanghai",
ASN: "Wdd Inc",
}
ConfigCache.Agent.Network.Public = fakePublicInfo
SaveConfig()
return
}
// 可以联网
// 创建带有超时的HTTP客户端
client := &http.Client{
Timeout: 3 * time.Second,
}
// 创建新的请求对象
req, err := http.NewRequest("GET", "https://ipinfo.io", nil)
if err != nil {
log.Error("创建请求失败: %v", err)
}
// 设置请求头
req.Header.Add("Authorization", "Bearer 6ecb0db9bd8f19")
req.Header.Add("Accept", "application/json")
// 发送请求
resp, err := client.Do(req)
if err != nil {
log.Error("请求PublicInfo失败: %v", err)
}
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
ConfigCache.Agent.Network.Public = p
SaveConfig()
}

View File

@@ -0,0 +1,71 @@
package log
import (
"fmt"
"runtime"
"strconv"
"strings"
"time"
)
const (
colorReset = "\033[0m"
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorYellow = "\033[33m"
colorBlue = "\033[34m"
)
func main() {
Debug("Debug message: %s", "connection established")
Info("System %s", "started successfully")
Warning("Disk space at %d%%", 85)
Error("Failed to %s", "load config")
}
func Debug(format string, args ...interface{}) {
log("DEBUG", colorBlue, format, args...)
}
func Info(format string, args ...interface{}) {
log("INFO", colorGreen, format, args...)
}
func Warning(format string, args ...interface{}) {
log("WARNING", colorYellow, format, args...)
}
func Error(format string, args ...interface{}) {
log("ERROR", colorRed, format, args...)
}
func log(level string, color string, format string, args ...interface{}) {
// 获取调用者信息跳过2层调用栈
_, file, line, _ := runtime.Caller(2)
s := strings.Split(file, "ProjectOctopus")[1]
callerInfo := strings.TrimLeft(s, "/") + " "
callerInfo += strconv.FormatInt(int64(line), 10)
timestamp := currentTimeString()
message := fmt.Sprintf(format, args...)
fmt.Printf("%s %s%-7s%s [%s] %s\n",
timestamp,
color,
level,
colorReset,
callerInfo,
message)
}
func currentTimeString() string {
// 加载东八区时区
loc, _ := time.LoadLocation("Asia/Shanghai")
// 获取当前时间并转换为东八区时间
now := time.Now().In(loc)
// 格式化为 "2006-01-02 15:04:05" 的布局
formattedTime := now.Format(time.DateTime)
return formattedTime
}

View File

@@ -2,6 +2,10 @@ package main
import "agent-wdd/cmd"
// C:\Users\wdd\go\bin\gox.exe -osarch="linux/amd64" -output "build/agent-wdd_{{.OS}}_{{.Arch}}"
// rm -rf agent-wdd_linux_amd64
// chmod +x agent-wdd_linux_amd64 && ./agent-wdd_linux_amd64 version
func main() {
// WDD 启动

View File

@@ -0,0 +1,323 @@
package utils
import (
"agent-wdd/log"
"bufio"
"fmt"
"io"
"os"
"os/user"
"path/filepath"
)
// AppendFileToFile 将源文件的内容添加到目标文件
func AppendFileToFile(sourceFile, targetFile string) bool {
// 打开源文件
source, err := os.Open(sourceFile)
if err != nil {
log.Error("[BasicAppendSourceToFile] - error open source file => %s, error is %s", sourceFile, err.Error())
return false
}
defer source.Close()
// 打开目标文件,如果不存在则创建,如果存在则在末尾追加
target, err := os.OpenFile(targetFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Error("[BasicAppendSourceToFile] - error open target file => %s, error is %s", sourceFile, err.Error())
return false
}
defer target.Close()
// 将源文件内容复制到目标文件
_, err = io.Copy(target, source)
if err != nil {
log.Error("[BasicAppendSourceToFile] - Error appending to target file: %s", err.Error())
return false
}
return true
}
// AppendOverwriteContentToFile 向目标文件中写入一些内容,覆盖源文件
func AppendOverwriteContentToFile(content string, targetFile string) bool {
err := os.Remove(targetFile)
if err != nil {
log.Warning("[BasicAppendOverwriteContentToFile] - Error removing file: %s , error is %s", targetFile, err.Error())
}
return AppendContentToFile(content, targetFile)
}
// AppendContentToFile 向目标文件(targetFile 文件的绝对路径)中追加写入一些内容, 如果文件不存在,那么就创建相应的目录及文件
func AppendContentToFile(content string, targetFile string) bool {
// 创建目标文件的目录(递归创建)
dir := filepath.Dir(targetFile)
if err := os.MkdirAll(dir, 0755); err != nil {
return false
}
// 打开文件用于追加。如果文件不存在,将会创建一个新文件。
file, err := os.OpenFile(targetFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Error("[BasicAppendContentToFile] - Error opening file: %s , error is %s", targetFile, err.Error())
return false
}
defer file.Close() // 确保文件最终被关闭
// 写入内容到文件
// 内容非空时执行写入操作
if content != "" {
if _, err := file.WriteString(content); err != nil {
log.Error("[BasicAppendContentToFile] - Error writing to file: %s , error is %s", targetFile, err.Error())
return false
}
}
return true
}
// AppendOverwriteListContentToFile 将一个字符串列表中的内容,一行一行的写入文件中
func AppendOverwriteListContentToFile(contentList []string, targetFile string) bool {
err := os.Remove(targetFile)
if err != nil {
log.Warning("[AppendOverwriteListContentToFile] - Error removing file: %s , error is %s", targetFile, err.Error())
}
// 打开文件用于追加。如果文件不存在,将会创建一个新文件。
file, err := os.OpenFile(targetFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Error("[AppendOverwriteListContentToFile] - Error opening file: %s , error is %s", targetFile, err.Error())
return false
}
defer file.Close() // 确保文件最终被关闭
// 写入内容到文件
for _, contentLine := range contentList {
//bytes, _ := json.Marshal(contentLine)
if _, err := file.WriteString(contentLine + "\n"); err != nil {
log.Error("[AppendOverwriteListContentToFile] - Error writing to file: %s , error is %s", targetFile, err.Error())
return false
}
}
return true
}
// AppendK8sYamlWithSplitLineToFile 专门为k8s的yaml文件设计的在每次写入内容之前先写入一行分隔符
func AppendK8sYamlWithSplitLineToFile(content string, targetFile string) bool {
// 打开文件用于追加。如果文件不存在,将会创建一个新文件。
file, err := os.OpenFile(targetFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Error("[BasicAppendContentToFile] - Error opening file: %s , error is %s", targetFile, err.Error())
return false
}
defer file.Close() // 确保文件最终被关闭
// 写入内容到文件
if _, err := file.WriteString("---"); err != nil {
log.Error("[BasicAppendContentToFile] - Error writing to file: %s , error is %s", targetFile, err.Error())
return false
}
if _, err := file.WriteString(content); err != nil {
log.Error("[BasicAppendContentToFile] - Error writing to file: %s , error is %s", targetFile, err.Error())
return false
}
return true
}
// AppendNullOverWriteToFile 清空一个文件
func AppendNullOverWriteToFile(targetFile string) bool {
// 使用os.O_TRUNC清空文件内容
file, err := os.OpenFile(targetFile, os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
log.Error("[AppendNullOverWriteToFile] - Error opening file: %s, error is %s", targetFile, err.Error())
return false
}
defer file.Close() // 确保在函数退出前关闭文件
return true
}
func WordSpaceCompletion(source string, totalLength int) string {
sourceLength := len(source)
if sourceLength >= totalLength {
return source
}
for i := 0; i < totalLength-sourceLength; i++ {
source += " "
}
return source
}
// IsDirOrFile 如果是目录则返回true是文件则返回false
func IsDirOrFile(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return info.IsDir()
}
// FileExists 文件存在返回true不存在返回false如果文件是一个目录也返回false
func FileExists(fileFullPath string) bool {
info, err := os.Stat(fileFullPath)
if err != nil {
return false
}
return !info.IsDir()
}
// FileOrFolderExists 文件或者目录是否返回true不存在返回false
func FileOrFolderExists(fileFullPath string) bool {
_, err := os.Stat(fileFullPath)
return !os.IsNotExist(err)
}
// FileExistAndNotNull 文件不为空返回true 文件为空返回false
func FileExistAndNotNull(filename string) bool {
// Check if the file exists
if _, err := os.Stat(filename); os.IsNotExist(err) {
log.Debug("文件 %s 不存在!", filename)
return false
}
// Open the file for reading
file, err := os.Open(filename)
defer file.Close()
if err != nil {
log.Debug("文件 %s 打开有误!", filename)
return false // Handle error according to your needs
}
// Get the file size
info, _ := file.Stat()
size := info.Size()
// Check if the file is not empty
return size > 0
}
// ListAllFileInFolder 列出一个目录中的所有文件返回文件名忽略folder不带全路径
func ListAllFileInFolder(folderName string) ([]string, error) {
return listAllFileInFolderWithFullPath(folderName, false)
}
func ListAllFileInFolderWithFullPath(folderName string) ([]string, error) {
return listAllFileInFolderWithFullPath(folderName, true)
}
func listAllFileInFolderWithFullPath(folderName string, fullPath bool) ([]string, error) {
files := make([]string, 0)
err := filepath.Walk(folderName, func(path string, info os.FileInfo, err error) error {
if !info.IsDir() {
if fullPath {
files = append(files, path)
} else {
files = append(files, info.Name())
}
}
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
func RemoveFolderComplete(folderName string) bool {
err := filepath.Walk(folderName,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
return os.Remove(path)
}
return nil
})
if err != nil {
return false
}
err = os.RemoveAll(folderName)
if err != nil {
return false
}
return true
}
func ReadAllContentFromFile(fileFullPath string) (result []string) {
f, err := os.Open(fileFullPath)
if err != nil {
fmt.Println(err)
return result
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if len(line) > 0 { // ignore empty lines
result = append(result, line)
}
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
return result
}
return result
}
// MoveFolerToAnother 将源文件夹中除了子文件夹外的所有文件移动到目标文件夹
func MoveFolerToAnother(srcDir, dstDir string) error {
// 读取源文件夹中的所有条目
entries, err := os.ReadDir(srcDir)
if err != nil {
return fmt.Errorf("读取源文件夹失败: %w", err)
}
// 遍历所有条目
for _, entry := range entries {
// 跳过子文件夹
if entry.IsDir() {
continue
}
// 构造源文件路径和目标文件路径
srcPath := filepath.Join(srcDir, entry.Name())
dstPath := filepath.Join(dstDir, entry.Name())
// 移动文件
if err := os.Rename(srcPath, dstPath); err != nil {
return fmt.Errorf("移动文件失败: %w", err)
}
}
return nil
}
// GetCurrentUserFolder 获取运行环境当前用户的根目录
func GetCurrentUserFolder() string {
usr, err := user.Current()
if err != nil {
fmt.Println(err)
return ""
}
return usr.HomeDir
}

View File

@@ -0,0 +1,24 @@
package utils
import (
"fmt"
"time"
)
const defaultTimeString = "2011-11-11 11:11:11"
func CurrentTimeString() string {
// 加载东八区时区
loc, err := time.LoadLocation("Asia/Shanghai")
if err != nil {
fmt.Println("无法加载时区:", err)
return defaultTimeString
}
// 获取当前时间并转换为东八区时间
now := time.Now().In(loc)
// 格式化为 "2006-01-02 15:04:05" 的布局
formattedTime := now.Format(time.DateTime)
return formattedTime
}