system.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package system
  2. import (
  3. "bufio"
  4. "fmt"
  5. "os"
  6. "strconv"
  7. "strings"
  8. "github.com/vishvananda/netlink"
  9. )
  10. type SystemStatus struct {
  11. Hostname string `json:"hostname"`
  12. OS string `json:"os"`
  13. Kernel string `json:"kernel"`
  14. UptimeSeconds float64 `json:"uptime_seconds"`
  15. UptimeHuman string `json:"uptime_human"`
  16. CPULoad []string `json:"cpu_load"` // 1m, 5m, 15m
  17. MemTotalMB uint64 `json:"mem_total_mb"`
  18. MemUsedMB uint64 `json:"mem_used_mb"`
  19. MemUsagePct float64 `json:"mem_usage_pct"`
  20. TotalIfaces int `json:"total_ifaces"`
  21. UpIfaces int `json:"up_ifaces"`
  22. DownIfaces int `json:"down_ifaces"`
  23. VlanIfaces int `json:"vlan_ifaces"`
  24. DefaultGateway string `json:"default_gateway"`
  25. }
  26. type RouteInfo struct {
  27. Destination string `json:"destination"`
  28. Gateway string `json:"gateway"`
  29. Interface string `json:"interface"`
  30. Protocol int `json:"protocol"`
  31. Scope int `json:"scope"`
  32. }
  33. // GetStatus returns system-level metrics and router telemetry.
  34. func GetStatus() (*SystemStatus, error) {
  35. hostname, _ := os.Hostname()
  36. osName := "Debian Linux"
  37. if f, err := os.Open("/etc/os-release"); err == nil {
  38. scanner := bufio.NewScanner(f)
  39. for scanner.Scan() {
  40. line := scanner.Text()
  41. if strings.HasPrefix(line, "PRETTY_NAME=") {
  42. osName = strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"")
  43. break
  44. }
  45. }
  46. _ = f.Close()
  47. }
  48. kernel := "Linux"
  49. if data, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
  50. kernel = strings.TrimSpace(string(data))
  51. }
  52. uptimeSec, uptimeHuman := getUptime()
  53. cpuLoads := getCPULoad()
  54. memTotal, memUsed, memPct := getMemory()
  55. defGateway := getDefaultGateway()
  56. return &SystemStatus{
  57. Hostname: hostname,
  58. OS: osName,
  59. Kernel: kernel,
  60. UptimeSeconds: uptimeSec,
  61. UptimeHuman: uptimeHuman,
  62. CPULoad: cpuLoads,
  63. MemTotalMB: memTotal,
  64. MemUsedMB: memUsed,
  65. MemUsagePct: memPct,
  66. DefaultGateway: defGateway,
  67. }, nil
  68. }
  69. func getUptime() (float64, string) {
  70. data, err := os.ReadFile("/proc/uptime")
  71. if err != nil {
  72. return 0, "Unknown"
  73. }
  74. parts := strings.Fields(string(data))
  75. if len(parts) == 0 {
  76. return 0, "Unknown"
  77. }
  78. sec, _ := strconv.ParseFloat(parts[0], 64)
  79. days := int(sec) / 86400
  80. hours := (int(sec) % 86400) / 3600
  81. minutes := (int(sec) % 3600) / 60
  82. if days > 0 {
  83. return sec, fmt.Sprintf("%dd %dh %dm", days, hours, minutes)
  84. }
  85. return sec, fmt.Sprintf("%dh %dm", hours, minutes)
  86. }
  87. func getCPULoad() []string {
  88. data, err := os.ReadFile("/proc/loadavg")
  89. if err != nil {
  90. return []string{"0.00", "0.00", "0.00"}
  91. }
  92. parts := strings.Fields(string(data))
  93. if len(parts) >= 3 {
  94. return parts[:3]
  95. }
  96. return []string{"0.00", "0.00", "0.00"}
  97. }
  98. func getMemory() (totalMB uint64, usedMB uint64, pct float64) {
  99. f, err := os.Open("/proc/meminfo")
  100. if err != nil {
  101. return 0, 0, 0
  102. }
  103. defer f.Close()
  104. var total, available uint64
  105. scanner := bufio.NewScanner(f)
  106. for scanner.Scan() {
  107. line := scanner.Text()
  108. if strings.HasPrefix(line, "MemTotal:") {
  109. parts := strings.Fields(line)
  110. if len(parts) >= 2 {
  111. total, _ = strconv.ParseUint(parts[1], 10, 64)
  112. }
  113. } else if strings.HasPrefix(line, "MemAvailable:") {
  114. parts := strings.Fields(line)
  115. if len(parts) >= 2 {
  116. available, _ = strconv.ParseUint(parts[1], 10, 64)
  117. }
  118. }
  119. }
  120. totalMB = total / 1024
  121. usedMB = (total - available) / 1024
  122. if total > 0 {
  123. pct = (float64(total-available) / float64(total)) * 100.0
  124. }
  125. return
  126. }
  127. func getDefaultGateway() string {
  128. routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
  129. if err != nil {
  130. return ""
  131. }
  132. for _, r := range routes {
  133. if r.Dst == nil && r.Gw != nil {
  134. return r.Gw.String()
  135. }
  136. }
  137. return ""
  138. }
  139. // GetRoutingTable returns the list of active routes.
  140. func GetRoutingTable() ([]RouteInfo, error) {
  141. routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
  142. if err != nil {
  143. return nil, err
  144. }
  145. links, _ := netlink.LinkList()
  146. linkMap := make(map[int]string)
  147. for _, l := range links {
  148. linkMap[l.Attrs().Index] = l.Attrs().Name
  149. }
  150. result := make([]RouteInfo, 0)
  151. for _, r := range routes {
  152. dst := "default"
  153. if r.Dst != nil {
  154. dst = r.Dst.String()
  155. }
  156. gw := ""
  157. if r.Gw != nil {
  158. gw = r.Gw.String()
  159. }
  160. result = append(result, RouteInfo{
  161. Destination: dst,
  162. Gateway: gw,
  163. Interface: linkMap[r.LinkIndex],
  164. Protocol: int(r.Protocol),
  165. Scope: int(r.Scope),
  166. })
  167. }
  168. return result, nil
  169. }