Commit f70b0280 authored by liming6's avatar liming6
Browse files

feature 添加opsflow的获取dcu、cpu和在线用户功能

parent 2051442e
.idea .idea
go.sum go.sum
.vscode
...@@ -241,7 +241,14 @@ func RSMI_dev_serial_number_get(deviceIndex uint32) (string, error) { ...@@ -241,7 +241,14 @@ func RSMI_dev_serial_number_get(deviceIndex uint32) (string, error) {
func RSMI_dev_subsystem_name_get(deviceIndex uint32) (string, error) { func RSMI_dev_subsystem_name_get(deviceIndex uint32) (string, error) {
buff := make([]uint8, 128) buff := make([]uint8, 128)
res := C.rsmi_dev_subsystem_name_get(C.uint(deviceIndex), (*C.char)(unsafe.Pointer(&buff[0])), 128) res := C.rsmi_dev_subsystem_name_get(C.uint(deviceIndex), (*C.char)(unsafe.Pointer(&buff[0])), 128)
return string(buff), ToRSMIResult(res) index := 0
for k, v := range buff {
if v == 0 {
index = k
break
}
}
return string(buff[:index]), ToRSMIResult(res)
} }
// RSMI_dev_perf_level_get 获取设备运行等级 // RSMI_dev_perf_level_get 获取设备运行等级
......
package backend
import (
"encoding/json"
"testing"
)
func TestGetOnlineUser(t *testing.T) {
lui, err := GetOnlineUser()
if err != nil {
t.Error(err)
}
for _, v := range lui {
t.Logf("%+v", v)
}
bj, err := json.Marshal(lui)
if err != nil {
t.Error(err)
}
t.Logf("%s", string(bj))
}
func TestUpdateInfo(t *testing.T) {
Init()
defer Shutdown()
err := DCUSInfoMap.UpdateInfo()
if err != nil {
t.Error(err)
}
info, lock := DCUSInfoMap.GetInfo()
defer lock.Unlock()
for k, v := range info {
t.Logf("%d: %+v", k, v)
}
}
func TestGetDCULoad(t *testing.T) {
Init()
defer Shutdown()
load, err := GetDCULoad()
if err != nil {
t.Error(err)
}
for _, v := range load {
t.Logf("%+v", v)
b := []byte(v.Name)
result := make([]byte, 0, len(b))
for _, v := range b {
if v != 0 {
result = append(result, v)
} else {
break
}
}
t.Logf("%s", string(result))
t.Logf("%d", len(result))
}
}
package backend
import (
"fmt"
"get-container/cmd/hytop/lib"
"math"
"strconv"
"strings"
"sync"
"time"
)
var (
DCUSInfoMap *DCUInfoMap = nil // 记录dcu信息
Rocmlib = lib.Rocmlib_instance
DriverVersion = ""
)
const (
ENVUser = "USER"
)
// Init 初始化本包的数据
func Init() error {
Rocmlib = lib.GetRocmlib()
_, err := Rocmlib.Init()
if err != nil {
return err
}
DriverVersion, err = Rocmlib.GetSystemDriverVersion()
if err != nil {
return err
}
_, err = Rocmlib.GetDevNumber()
DCUSInfoMap = &DCUInfoMap{
info: make(map[int]*DCUInfo),
lock: sync.RWMutex{},
}
return err
}
func Shutdown() {
Rocmlib.Shutdown()
}
type DCUInfoMap struct {
lock sync.RWMutex
info map[int]*DCUInfo
}
type DCUInfo struct {
lock sync.RWMutex
Id int
Name string
PerformanceLevel string
Fan string
Temp float32 // 单位是摄氏度
PwrAvg float32 // 单位是瓦
PwrCap float32 // 单位是瓦
BusId string
MemTotal uint64
MemUsed uint64
MemUsedPerent float32
DCUUTil float32
}
func (m *DCUInfoMap) UpdateInfo() error {
num, err := Rocmlib.GetDevNumber()
if err != nil {
return err
}
names, err := Rocmlib.GetDevName()
if err != nil {
return err
}
plevel, err := Rocmlib.GetPerfLevel()
if err != nil {
return err
}
fan, err := Rocmlib.GetFanSpeed()
if err != nil {
return err
}
temp, err := Rocmlib.GetTemp()
if err != nil {
return err
}
pwrAvg, err := Rocmlib.GetPowerAvg()
if err != nil {
return err
}
pwrCap, err := Rocmlib.GetPowerCap()
if err != nil {
return err
}
busid, err := Rocmlib.GetPCIBusId()
if err != nil {
return err
}
memTotal, err := Rocmlib.GetMemTotal()
if err != nil {
return err
}
memUsed, err := Rocmlib.GetMemUsed()
if err != nil {
return err
}
dcu, err := Rocmlib.GetBusyPercent()
if err != nil {
return err
}
set := make(map[int]bool)
m.lock.Lock()
defer m.lock.Unlock()
for i := range num {
info, have := m.info[i]
if !have {
info = &DCUInfo{}
m.info[i] = info
}
info.lock.Lock()
info.Id = i
info.Name = names[i]
info.PerformanceLevel = plevel[i]
if rpm, have := fan[i]; !have || rpm == 0 {
info.Fan = "N/A"
} else {
info.Fan = strconv.Itoa(int(rpm))
}
info.Temp = float32(temp[i]) / 1000
info.PwrAvg = float32(pwrAvg[i]) / 1000000
info.PwrCap = float32(pwrCap[i]) / 1000000
info.BusId = busid[i]
info.MemTotal = memTotal[i]
info.MemUsed = memUsed[i]
if info.MemTotal == 0 {
info.MemUsedPerent = 0
} else {
info.MemUsedPerent = float32(info.MemUsed) / float32(info.MemTotal) * 100
}
info.DCUUTil = float32(dcu[i])
info.lock.Unlock()
set[i] = true
}
for k := range m.info {
a, b := set[k]
if a && b {
continue
}
delete(m.info, k)
}
return nil
}
func (m *DCUInfoMap) GetInfo() (map[int]*DCUInfo, sync.Locker) {
rl := m.lock.RLocker()
rl.Lock()
return m.info, rl
}
// DurationStr 将时间段格式化为 小时:分钟:秒 的格式
func DurationStr(d time.Duration) string {
h := int(math.Floor(d.Hours()))
m := int(d.Minutes()) % 60
s := int(math.Floor(d.Seconds())) % 60
if h <= 96 {
return strings.Replace(fmt.Sprintf("%d:%2d:%2d", h, m, s), " ", "0", -1)
} else {
return fmt.Sprintf("%.1f days", d.Hours()/24)
}
}
package backend
import (
"fmt"
"get-container/utils"
"os/exec"
"sort"
"strconv"
"strings"
"time"
)
type LoginUserInfo struct {
Name string `json:"name"` // 用户名
TTY string `json:"tty"` // 占用的终端
LoginTime time.Time `json:"loginTime"` // 登录时间
Pid []int32 `json:"pid"` // 登录的接管进程
LoginFrom string `json:"loginFrom"` // 登录方式
}
func GetOnlineUser() ([]LoginUserInfo, error) {
output, err := exec.Command("who", "-u", "-H").Output()
if err != nil {
return nil, err
}
return parseWhoOutput(strings.Trim(string(output), "\n"))
}
func parseWhoOutput(s string) ([]LoginUserInfo, error) {
lines := strings.Split(s, "\n")
if len(lines) < 1 {
return make([]LoginUserInfo, 0), nil
}
result := make([]LoginUserInfo, 0, len(lines))
for _, v := range lines[1:] {
fs := strings.Fields(v)
lui := LoginUserInfo{}
lui.Name = fs[0]
lui.TTY = fs[1]
t, err := time.Parse("2006-01-02 15:04", fmt.Sprintf("%s %s", fs[2], fs[3]))
if err != nil {
t = time.Now()
}
lui.LoginTime = t
pidStr := strings.Split(fs[5], ",")
lui.Pid = make([]int32, 0, 2)
for _, pidstr := range pidStr {
i, err := strconv.Atoi(pidstr)
if err != nil {
continue
}
lui.Pid = append(lui.Pid, int32(i))
}
lui.LoginFrom = strings.Trim(strings.Trim(fs[6], "("), ")")
result = append(result, lui)
}
return result, nil
}
func GetSysLoad() (*utils.SysInfo, error) {
return utils.GetSysInfo()
}
type DCULoad struct {
Name string `json:"name"`
Index int `json:"index"`
Fan string `json:"fan"`
Temp float32 `json:"temp"` // 单位是摄氏度
PwrAvg float32 `json:"pwrAvg"` // 单位是瓦
PwrCap float32 `json:"pwrCap"` // 单位是瓦
MemTotal uint64 `json:"memTotal"`
MemUsed uint64 `json:"memUsed"`
MemUsedPerent float32 `json:"memUsedPercent"`
DCUUTil float32 `json:"dcuUtilPercent"`
}
func GetDCULoad() ([]DCULoad, error) {
if DCUSInfoMap == nil {
err := Init()
if err != nil {
return nil, err
}
}
if err := DCUSInfoMap.UpdateInfo(); err != nil {
return nil, err
}
info, lock := DCUSInfoMap.GetInfo()
result := make([]DCULoad, 0, len(info))
for _, v := range info {
l := DCULoad{
Name: v.Name,
Index: v.Id,
Fan: v.Fan,
Temp: v.Temp,
PwrAvg: v.PwrAvg,
PwrCap: v.PwrCap,
MemTotal: v.MemTotal,
MemUsed: v.MemUsed,
MemUsedPerent: v.MemUsedPerent,
DCUUTil: v.DCUUTil,
}
result = append(result, l)
}
lock.Unlock()
sort.Slice(result, func(i, j int) bool {
return result[i].Index < result[j].Index
})
return result, nil
}
type AllInfo struct {
DCUInfo []DCULoad `json:"dcuInfo"`
SysInfo utils.SysInfo `json:"sysInfo"`
OnlineUserInfo []LoginUserInfo `json:"loginUserInfo"`
}
package main
import (
"fmt"
"get-container/cmd/opsflow/backend"
"get-container/cmd/opsflow/web"
"get-container/utils"
"log"
"github.com/spf13/pflag"
)
var (
flagPort = pflag.Int16P("port", "p", 10880, "listen port for service")
flagServer = pflag.BoolP("server", "s", false, "run as server mode")
flagCmd = pflag.StringP("cmd", "c", "all", "command to execute, sys/dcu/login/all")
flagHelp = pflag.BoolP("help", "h", false, "show help message")
)
func main() {
pflag.Parse()
if *flagHelp {
fmt.Println(`this is opsflow command line tool.
Usage:
opsflow [options]
Options:`)
pflag.PrintDefaults()
return
}
backend.Init()
defer backend.Shutdown()
if *flagServer {
log.Println("start opsflow server mode")
err := web.WebServer(fmt.Sprintf(":%d", *flagPort))
if err != nil {
log.Fatalf("failed to start web server: %v", err)
}
}
switch *flagCmd {
case "sys":
PrintSysLoad()
case "dcu":
PrintDCUInfo()
case "login":
PrintLoginInfo()
case "all":
PrintSysLoad()
PrintDCUInfo()
PrintLoginInfo()
default:
log.Fatalf("unknown command: %s", *flagCmd)
}
}
func PrintSysLoad() {
load, err := backend.GetSysLoad()
if err != nil {
log.Fatalf("failed to get sys load: %v", err)
}
fmt.Println("============== sysload =================")
fmt.Printf("CPU Usage: %.2f%%\n", load.CPUPercent)
fmt.Printf("Load Average (1m, 5m, 15m): %.2f, %.2f, %.2f\n", load.LoadAverage1, load.LoadAverage5, load.LoadAverage15)
memSize := utils.MemorySize{Unit: utils.Byte, Num: load.MemTotal}
fmt.Printf("Total Memory: %s\n", memSize.HumanReadStr(1))
fmt.Printf("Memory Usage: %.2f%%\n", load.MemUsagePercent)
swapSize := utils.MemorySize{Unit: utils.Byte, Num: load.SwapTotal}
fmt.Printf("Total Swap: %s\n", swapSize.HumanReadStr(1))
fmt.Printf("Swap Usage: %.2f%%\n", load.SwapUsagePercent)
fmt.Println("")
}
func PrintLoginInfo() {
logins, err := backend.GetOnlineUser()
if err != nil {
log.Fatalf("failed to get login info: %v", err)
}
fmt.Println("============== login info =================")
for _, login := range logins {
fmt.Printf("User: %s, Terminal: %s, Login From: %s, Login Time: %s\n", login.Name, login.TTY, login.LoginFrom, login.LoginTime.Format("2006-01-02 15:04:05"))
}
fmt.Println("")
}
func PrintDCUInfo() {
dcus, err := backend.GetDCULoad()
if err != nil {
log.Fatalf("failed to get dcu info: %v", err)
}
fmt.Println("============== dcu info =================")
for _, dcu := range dcus {
memTotal := utils.MemorySize{Unit: utils.Byte, Num: dcu.MemTotal}
memUsed := utils.MemorySize{Unit: utils.Byte, Num: dcu.MemUsed}
fmt.Printf("DCU index: %d \n Fan speed: %s\n Temperature: %.2f%%\n Power Capture: %.2fw\n Power Capture: %.2fw\n VRAM total: %s\n VRAM used: %s\n DCUUtils: %.2f%%\n",dcu.Index, dcu.Fan, dcu.Temp,dcu.PwrCap,dcu.PwrAvg, memTotal.HumanReadStr(1), memUsed.HumanReadStr(1), dcu.DCUUTil)
}
fmt.Println("")
}
\ No newline at end of file
package web
import (
"get-container/cmd/opsflow/backend"
"github.com/gin-gonic/gin"
)
type RestfulResult struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data any `json:"data,omitempty"`
}
func ReturnGin(ctx *gin.Context, data any, err error) {
if err != nil {
ctx.JSON(500, RestfulResult{
Code: 500,
Msg: err.Error(),
})
return
}
ctx.JSON(200, RestfulResult{
Code: 200,
Msg: "ok",
Data: data,
})
}
func WebServer(addr string) error {
engine := gin.Default()
cmdGroup := engine.Group("/api/cmd")
cmdGroup.GET("/all", func(ctx *gin.Context) {
olu, err := backend.GetOnlineUser()
if err != nil {
ReturnGin(ctx, nil, err)
}
sys, err := backend.GetSysLoad()
if err != nil {
ReturnGin(ctx, nil, err)
}
dcu, err := backend.GetDCULoad()
if err != nil {
ReturnGin(ctx, nil, err)
}
ReturnGin(ctx, backend.AllInfo{
DCUInfo: dcu,
SysInfo: *sys,
OnlineUserInfo: olu,
}, err)
})
cmdGroup.GET("/loginUser", func(ctx *gin.Context) {
olu, err := backend.GetOnlineUser()
ReturnGin(ctx, olu, err)
})
cmdGroup.GET("/sysload", func(ctx *gin.Context) {
sys, err := backend.GetSysLoad()
ReturnGin(ctx, sys, err)
})
cmdGroup.GET("/dcuload", func(ctx *gin.Context) {
dcu, err := backend.GetDCULoad()
ReturnGin(ctx, dcu, err)
})
return engine.Run(addr)
}
...@@ -15,10 +15,38 @@ require ( ...@@ -15,10 +15,38 @@ require (
) )
require ( require (
github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 // indirect github.com/muesli/clusters v0.0.0-20200529215643-2700303c1762 // indirect
github.com/muesli/kmeans v0.3.1 // indirect github.com/muesli/kmeans v0.3.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/tools v0.34.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
) )
require ( require (
...@@ -43,6 +71,7 @@ require ( ...@@ -43,6 +71,7 @@ require (
github.com/ebitengine/purego v0.9.0 // indirect github.com/ebitengine/purego v0.9.0 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gin-gonic/gin v1.11.0
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
...@@ -61,6 +90,7 @@ require ( ...@@ -61,6 +90,7 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.10
github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect github.com/tklauser/numcpus v0.10.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
...@@ -71,5 +101,5 @@ require ( ...@@ -71,5 +101,5 @@ require (
go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/sys v0.36.0 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.20.0 // indirect golang.org/x/text v0.27.0 // indirect
) )
...@@ -11,16 +11,16 @@ import ( ...@@ -11,16 +11,16 @@ import (
*/ */
type SysInfo struct { type SysInfo struct {
CPUPercent float64 // CPU使用率 CPUPercent float64 `json:"cpuPercent"` // CPU使用率
LoadAverage1 float64 // 1分钟内平均负载 LoadAverage1 float64 `json:"loadAverage1"` // 1分钟内平均负载
LoadAverage5 float64 // 5分钟平均负载 LoadAverage5 float64 `json:"loadAverage5"` // 5分钟平均负载
LoadAverage15 float64 // 15分钟平均负载 LoadAverage15 float64 `json:"loadAverage15"` // 15分钟平均负载
MemTotal uint64 // 总内存 MemTotal uint64 `json:"memTotal"` // 总内存
SwapTotal uint64 // 总swap SwapTotal uint64 `json:"swapTotal"` // 总swap
MemUsage uint64 // 已使用内存 MemUsage uint64 `json:"memUsage"` // 已使用内存
SwapUsage uint64 // 已使用swap SwapUsage uint64 `json:"swapUsage"` // 已使用swap
MemUsagePercent float64 // 已使用内存百分比 MemUsagePercent float64 `json:"memUsagePercent"` // 已使用内存百分比
SwapUsagePercent float64 // 已使用swap百分比 SwapUsagePercent float64 `json:"swapUsagePercent"` // 已使用swap百分比
} }
func GetSysInfo() (*SysInfo, error) { func GetSysInfo() (*SysInfo, error) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment