From fbb75c3d35e97d15de3508105f947778e8301455 Mon Sep 17 00:00:00 2001 From: dustoair <107600816+dustoair@users.noreply.github.com> Date: Mon, 25 Jul 2022 19:35:34 +0800 Subject: [PATCH] open src --- .gitignore | 8 +++ README.MD | 49 +++++++++++++ go.mod | 23 ++++++ main.go | 28 ++++++++ thirdpart/AliyunSMS.go.txt | 43 +++++++++++ thirdpart/WeChat.go | 114 +++++++++++++++++++++++++++++ util/SystemInfo.go | 133 ++++++++++++++++++++++++++++++++++ util/SystemInfo_test.go | 12 ++++ util/netTest/net_test.go | 144 +++++++++++++++++++++++++++++++++++++ 9 files changed, 554 insertions(+) create mode 100644 .gitignore create mode 100644 README.MD create mode 100644 go.mod create mode 100644 main.go create mode 100644 thirdpart/AliyunSMS.go.txt create mode 100644 thirdpart/WeChat.go create mode 100644 util/SystemInfo.go create mode 100644 util/SystemInfo_test.go create mode 100644 util/netTest/net_test.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b0833c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/.idea +/.idea/ +/.idea/* +.idea +.idea/ +.idea/* +go.sum +nogit.txt \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..92f0927 --- /dev/null +++ b/README.MD @@ -0,0 +1,49 @@ +探测url的ssl证书过期时间 并发微信告警 + +## build on arm64 +```bash +cd /root +rm -rf remoteMonitor +git clone https://git.sre.ink/go/remoteMonitor.git +cd remoteMonitor +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go get github.com/shirou/gopsutil/v3/cpu@v3.21.12 +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go get github.com/shirou/gopsutil/v3/disk@v3.21.12 +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go mod tidy +CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on go build -v -a -o rtm main.go +chmod +x rtm +rm -rf /usr/bin/rtm +mv rtm /usr/bin/ +rtm +``` + + +## build +```bash +cd /root +rm -rf remoteMonitor +git clone https://git.sre.ink/go/remoteMonitor.git +cd remoteMonitor +GOPROXY="https://goproxy.cn,direct" go get github.com/shirou/gopsutil/v3/cpu@v3.21.12 +GOPROXY="https://goproxy.cn,direct" go get github.com/shirou/gopsutil/v3/disk@v3.21.12 +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on GOPROXY="https://goproxy.cn,direct" go build -v -a -o rtm main.go +chmod +x rtm +rm -rf /usr/bin/rtm +mv rtm /usr/bin/ +``` + + +## crontab +```bash +*/1 * * * * /usr/bin/rtm +``` + +## usage +```bash +chmod +x /usr/bin/rtm +rtm +``` + +## build for arm64 +```bash +CGO_ENABLED=0 GOARM=7 GOOS=linux GOARCH=arm64 GO111MODULE=on GOPROXY="https://goproxy.cn,direct" go build -v -a -o certmonitor main.go +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5695131 --- /dev/null +++ b/go.mod @@ -0,0 +1,23 @@ +module remoteMonitor + +go 1.18 + +require ( + github.com/aliyun/alibaba-cloud-sdk-go v1.61.1449 + github.com/shirou/gopsutil/v3 v3.21.12 +) + +require ( + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect + github.com/json-iterator/go v1.1.5 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/tklauser/go-sysconf v0.3.9 // indirect + github.com/tklauser/numcpus v0.3.0 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect + golang.org/x/sys v0.0.0-20211013075003-97ac67df715c // indirect + gopkg.in/ini.v1 v1.66.2 // indirect +) diff --git a/main.go b/main.go new file mode 100644 index 0000000..4c40a3e --- /dev/null +++ b/main.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "remoteMonitor/thirdpart" + "remoteMonitor/util" +) + +func main() { + fmt.Println("获取主机信息") + util.GetHostInfo() + fmt.Println("获取CPU信息") + + util.GetCpuInfo() + fmt.Println("获取内存信息") + + util.GetMemInfo() + fmt.Println("获取系统负载") + + util.GetSysLoad() + fmt.Println("获取硬盘存储信息") + util.GetDiskInfo() + + if util.IsAlert { + thirdpart.SendWechat(util.MSG) + //thirdpart.SendToPhone("13152148384", "9999") + } +} diff --git a/thirdpart/AliyunSMS.go.txt b/thirdpart/AliyunSMS.go.txt new file mode 100644 index 0000000..316500a --- /dev/null +++ b/thirdpart/AliyunSMS.go.txt @@ -0,0 +1,43 @@ +/** + * https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?spm=a2c4g.11186623.0.0.257a7218t3HRei&version=2017-05-25&language=go-tea + */ + +package thirdpart + +import ( + "fmt" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" + "github.com/aliyun/alibaba-cloud-sdk-go/services/dysmsapi" +) + +func SendToPhone(phone string, code string) bool { + //调用阿里云短信接口发送短信 从配置中心拿配置项 + sign_name := "333333333333" + template_code := "3333333333333333" + app_key := "33333333333333333333333333" + app_secret := "333333333333333333333333333333" + region_id := "cn-hangzhou" + + client, err := dysmsapi.NewClientWithAccessKey(region_id, app_key, app_secret) + request := requests.NewCommonRequest() + request.Method = "POST" + request.Scheme = "https" // https | http + request.Domain = "dysmsapi.aliyuncs.com" + request.Version = "2017-05-25" + request.ApiName = "SendSms" + request.QueryParams["RegionId"] = region_id + request.QueryParams["PhoneNumbers"] = phone //手机号 + request.QueryParams["SignName"] = sign_name //阿里云验证过的项目名 自己设置 + request.QueryParams["TemplateCode"] = template_code //阿里云的短信模板号 自己设置 + request.QueryParams["TemplateParam"] = "{\"code\":" + code + "}" //短信模板中的验证码内容。 + response, err := client.ProcessCommonRequest(request) + fmt.Print(client.DoAction(request, response)) + // fmt.Print(response) + if err != nil { + fmt.Print(err.Error()) + return false + } + fmt.Printf("response is %#v\n", response) + //json数据解析 + return true +} diff --git a/thirdpart/WeChat.go b/thirdpart/WeChat.go new file mode 100644 index 0000000..9d273a2 --- /dev/null +++ b/thirdpart/WeChat.go @@ -0,0 +1,114 @@ +package thirdpart + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "reflect" +) + +var WECOM_CID string = GetEnvDefault("WECOM_CID", "333333333333333333333") +var WECOM_SECRET string = GetEnvDefault("WECOM_SECRET", "33333333333333333333333333") +var WECOM_AID string = GetEnvDefault("WECOM_AID", "33333333333333") +var WECOM_TOUID string = GetEnvDefault("WECOM_TOUID", "333333333333333333333") +var REDIS_STAT string = GetEnvDefault("REDIS_STAT", "OFF") +var REDIS_PASSWORD string = GetEnvDefault("REDIS_PASSWORD", "") +var ctx = context.Background() + +func GetEnvDefault(key, defVal string) string { + val, ex := os.LookupEnv(key) + if !ex { + return defVal + } + return val +} + +func praser_json(json_str string) map[string]interface{} { + var wecom_response map[string]interface{} + if string(json_str) != "" { + err := json.Unmarshal([]byte(string(json_str)), &wecom_response) + if err != nil { + log.Println("生成json字符串错误") + } + } + return wecom_response +} + +func get_token(corpid, app_secret string) string { + resp, err := http.Get("https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=" + corpid + "&corpsecret=" + app_secret) + if err != nil { + log.Println(err) + } + defer resp.Body.Close() + resp_data, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println(err) + } + token_response := praser_json(string(resp_data)) + return token_response["access_token"].(string) +} + +func post_msg(text_msg, msg_type, post_url string) string { + type msg struct { + Content string `json:"content"` + } + type JsonData struct { + Touser string `json:"touser"` + Agentid string `json:"agentid"` + Msgtype string `json:"msgtype"` + Text msg `json:"text"` + Duplicate_check_interval int `json:"duplicate_check_interval"` + } + post_data := JsonData{ + Touser: WECOM_TOUID, + Agentid: WECOM_AID, + Msgtype: msg_type, + Duplicate_check_interval: 600, + Text: msg{Content: text_msg}, + } + + post_json, _ := json.Marshal(post_data) + log.Println(string(post_json)) + msg_req, err := http.NewRequest("POST", post_url, bytes.NewBuffer(post_json)) + if err != nil { + log.Println(err) + } + msg_req.Header.Set("Content-Type", "application/json") + client := &http.Client{} + resp, err := client.Do(msg_req) + if err != nil { + panic(err) + } + defer msg_req.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + return string(body) +} + +func IsZero(v interface{}) (bool, error) { + t := reflect.TypeOf(v) + if !t.Comparable() { + return false, fmt.Errorf("type is not comparable: %v", t) + } + return v == reflect.Zero(t).Interface(), nil +} + +func SendWechat(msg string) { + access_token := get_token(WECOM_CID, WECOM_SECRET) + post_url := "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + access_token + msg_type := "text" + post_status := post_msg(msg, msg_type, post_url) + log.Println(post_status) + post_response := praser_json(string(post_status)) + log.Println(post_response) + errcode := post_response["errcode"] + _, err := IsZero(errcode) + if err != nil { + fmt.Printf("%v", err) + } + +} diff --git a/util/SystemInfo.go b/util/SystemInfo.go new file mode 100644 index 0000000..920f2d4 --- /dev/null +++ b/util/SystemInfo.go @@ -0,0 +1,133 @@ +package util + +import ( + "fmt" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/host" + "github.com/shirou/gopsutil/v3/load" + "github.com/shirou/gopsutil/v3/mem" + "net" + "strings" + "time" +) + +var ( + IsAlert = false + MSG = "" +) + +// 获取网络信息 +func GetNetInfo() string { + + addrs, err := net.InterfaceAddrs() + if err != nil { + fmt.Println(err) + return "" + } + + for _, address := range addrs { + // 检查ip地址判断是否回环地址 + if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + + if ipnet.IP.To4() != nil { + //fmt.Println(ipnet.Mask) + //fmt.Println(ipnet.IP) + return ipnet.IP.String() + } + } + } + return "" +} + +// 获取主机信息 +func GetHostInfo() { + hostInfo, err := host.Info() + if err != nil { + fmt.Println("get host info fail, error: ", err) + } + fmt.Printf("hostname is: %v, os platform: %v \n", hostInfo.Hostname, hostInfo.Platform) + MSG += fmt.Sprintf("hostname is: %v, os platform: %v \n", hostInfo.Hostname, hostInfo.Platform) +} + +// 获取CPU信息 +func GetCpuInfo() { + // cpuInfo,err := cpu.Info() + // if err != nil { + // fmt.Println("get cpu info fail, err: %v",err) + // } + // for _,ci := range cpuInfo { + // fmt.Printf("%v \n",ci) + // } + + cpuPercent, _ := cpu.Percent(time.Second, true) + + cpuNumber, _ := cpu.Counts(true) + + if cpuPercent[0] >= 95 { + //IsAlert = true + //暂时不监控cpu情况 + MSG += fmt.Sprintf("CPU核心数: %v \n", cpuNumber) + MSG += fmt.Sprintf("CPU使用率: %.3f%% \n", cpuPercent[0]) + } +} + +// 获取内存信息 +func GetMemInfo() { + memInfo, err := mem.VirtualMemory() + if err != nil { + fmt.Println("get memory info fail. err: ", err) + } + // 获取总内存大小,单位GB + memTotal := memInfo.Total / 1024 / 1024 / 1024 + // 获取已用内存大小,单位MB + memUsed := memInfo.Used / 1024 / 1024 + // 可用内存大小 + memAva := memInfo.Available / 1024 / 1024 + // 内存可用率 + memUsedPercent := memInfo.UsedPercent + + if memUsedPercent >= 95 { + IsAlert = true + MSG += fmt.Sprintf("总内存: %v GB, 已用内存: %v MB, 可用内存: %v MB, 内存使用率: %.3f %% \n", memTotal, memUsed, memAva, memUsedPercent) + } +} + +// 获取系统负载 +func GetSysLoad() { + loadInfo, err := load.Avg() + if err != nil { + fmt.Println("get average load fail. err: ", err) + } + + if loadInfo.Load15 >= 10 { + IsAlert = true + MSG += fmt.Sprintf("系统平均负载: %v \n", loadInfo) + } +} + +// 获取硬盘存储信息 +func GetDiskInfo() { + diskPart, err := disk.Partitions(false) + if err != nil { + fmt.Println(err) + } + for _, dp := range diskPart { + if find := strings.Contains(dp.String(), "/snap/core"); find { + //oracle arm cpu core 被误认为分区 + return + } + fmt.Println(dp) + diskUsed, _ := disk.Usage(dp.Mountpoint) + fmt.Printf("分区总大小: %d MB \n", diskUsed.Total/1024/1024) + fmt.Printf("分区使用率: %.3f %% \n", diskUsed.UsedPercent) + fmt.Printf("分区inode使用率: %.3f %% \n", diskUsed.InodesUsedPercent) + if diskUsed.UsedPercent >= 80 || diskUsed.InodesUsedPercent >= 80 { + IsAlert = true + MSG += fmt.Sprintf("异常分区: %s \n", dp.Mountpoint) + MSG += fmt.Sprintf("分区总大小: %d GB \n", diskUsed.Total/1024/1024/1024) + MSG += fmt.Sprintf("分区使用率: %.3f %% \n", diskUsed.UsedPercent) + MSG += fmt.Sprintf("分区inode使用率: %.3f %% \n", diskUsed.InodesUsedPercent) + } + } +} diff --git a/util/SystemInfo_test.go b/util/SystemInfo_test.go new file mode 100644 index 0000000..d8ada78 --- /dev/null +++ b/util/SystemInfo_test.go @@ -0,0 +1,12 @@ +package util + +import ( + "fmt" + "testing" +) + +func TestGetNetInfo(t *testing.T) { + fmt.Println("获取网络信息") + fmt.Println(GetNetInfo()) + +} diff --git a/util/netTest/net_test.go b/util/netTest/net_test.go new file mode 100644 index 0000000..a87b5b4 --- /dev/null +++ b/util/netTest/net_test.go @@ -0,0 +1,144 @@ +package netTest + +//原文链接:https://blog.csdn.net/qq_38858546/article/details/119873424 +//【golang】syscall 获取本机网卡IP、网关,可以通过IP地址来进行路由选择(多网卡) +import ( + "fmt" + "net" + "sort" + "syscall" + "testing" + "unsafe" +) + +type rtInfo struct { + Dst net.IPNet + Gateway, PrefSrc net.IP + OutputIface uint32 + Priority uint32 +} + +type routeSlice []*rtInfo + +type router struct { + ifaces []net.Interface + addrs []net.IP + v4 routeSlice +} + +func getRouteInfo() (*router, error) { + rtr := &router{} + + tab, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_INET) + if err != nil { + return nil, err + } + + msgs, err := syscall.ParseNetlinkMessage(tab) + if err != nil { + return nil, err + } + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + break + case syscall.RTM_NEWROUTE: + rtmsg := (*syscall.RtMsg)(unsafe.Pointer(&m.Data[0])) + attrs, err := syscall.ParseNetlinkRouteAttr(&m) + if err != nil { + return nil, err + } + routeInfo := rtInfo{} + rtr.v4 = append(rtr.v4, &routeInfo) + for _, attr := range attrs { + switch attr.Attr.Type { + case syscall.RTA_DST: + routeInfo.Dst.IP = net.IP(attr.Value) + routeInfo.Dst.Mask = net.CIDRMask(int(rtmsg.Dst_len), len(attr.Value)*8) + case syscall.RTA_GATEWAY: + routeInfo.Gateway = net.IPv4(attr.Value[0], attr.Value[1], attr.Value[2], attr.Value[3]) + case syscall.RTA_OIF: + routeInfo.OutputIface = *(*uint32)(unsafe.Pointer(&attr.Value[0])) + case syscall.RTA_PRIORITY: + routeInfo.Priority = *(*uint32)(unsafe.Pointer(&attr.Value[0])) + case syscall.RTA_PREFSRC: + routeInfo.PrefSrc = net.IPv4(attr.Value[0], attr.Value[1], attr.Value[2], attr.Value[3]) + } + } + } + } + + sort.Slice(rtr.v4, func(i, j int) bool { + return rtr.v4[i].Priority < rtr.v4[j].Priority + }) + + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + for i, iface := range ifaces { + + if i != iface.Index-1 { + break + } + + if iface.Flags&net.FlagUp == 0 { + continue + } + rtr.ifaces = append(rtr.ifaces, iface) + ifaceAddrs, err := iface.Addrs() + if err != nil { + return nil, err + } + var addrs net.IP + for _, addr := range ifaceAddrs { + if inet, ok := addr.(*net.IPNet); ok { + if v4 := inet.IP.To4(); v4 != nil { + if addrs == nil { + addrs = v4 + } + } + } + } + rtr.addrs = append(rtr.addrs, addrs) + } + return rtr, nil +} + +func TestRoute(tt *testing.T) { + newRoute, err := getRouteInfo() + if err != nil { + fmt.Println(err) + return + } + + fmt.Println("**************************************") + fmt.Printf("%-15v %-15v %-15v\n", "interfaceName", "gateway", "ip") + for _, rt := range newRoute.v4 { + if rt.Gateway != nil { + fmt.Printf("%-15v %-15v %-15v\n", newRoute.ifaces[rt.OutputIface-1].Name, rt.Gateway.String(), newRoute.addrs[rt.OutputIface-1]) + } + } + fmt.Println("**************************************") + + newRoute.getRoute(net.ParseIP("192.168.2.100")) + newRoute.getRoute(net.ParseIP("10.10.100.100")) + newRoute.getRoute(net.ParseIP("172.1.2.100")) +} + +func (r *router) getRoute(dst net.IP) { + for _, rt := range r.v4 { + if rt.Dst.IP != nil && !rt.Dst.Contains(dst) { + continue + } + fmt.Printf("%-15v : ", dst.String()) + if rt.PrefSrc == nil { + fmt.Println(r.ifaces[rt.OutputIface-1].Name, rt.Gateway.String(), r.addrs[rt.OutputIface-1].String()) + } else { + fmt.Println(r.ifaces[rt.OutputIface-1].Name, rt.Gateway.String(), rt.PrefSrc.String()) + } + return + } +}