manager.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package bwtest
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "sync"
  8. )
  9. const DefaultConfigPath = "/etc/proxmox-router/bwtest.json"
  10. // Manager coordinates the BwTest server and client subsystems
  11. type Manager struct {
  12. mu sync.RWMutex
  13. configPath string
  14. server *BwTestServer
  15. client *BwTestClient
  16. history []*HistoricalTestRecord
  17. }
  18. // NewManager initializes the Bandwidth Test manager and starts the background server
  19. func NewManager(cfgPath string) *Manager {
  20. if cfgPath == "" {
  21. cfgPath = DefaultConfigPath
  22. }
  23. cfg := ServerConfig{
  24. Enabled: true,
  25. Port: 2000,
  26. ListenAddr: "0.0.0.0",
  27. AllowAnonymous: true,
  28. }
  29. m := &Manager{
  30. configPath: cfgPath,
  31. history: make([]*HistoricalTestRecord, 0, 50),
  32. }
  33. // Try loading persisted configuration
  34. if loaded, err := m.loadConfig(); err == nil {
  35. cfg = *loaded
  36. }
  37. m.server = NewServer(cfg)
  38. m.client = NewClient()
  39. if cfg.Enabled {
  40. if err := m.server.Start(); err != nil {
  41. fmt.Printf("[BwTestManager] Warning: failed to start server on port %d: %v\n", cfg.Port, err)
  42. }
  43. }
  44. return m
  45. }
  46. func (m *Manager) loadConfig() (*ServerConfig, error) {
  47. data, err := os.ReadFile(m.configPath)
  48. if err != nil {
  49. return nil, err
  50. }
  51. var cfg ServerConfig
  52. if err := json.Unmarshal(data, &cfg); err != nil {
  53. return nil, err
  54. }
  55. return &cfg, nil
  56. }
  57. func (m *Manager) saveConfigLocked(cfg ServerConfig) error {
  58. _ = os.MkdirAll("/etc/proxmox-router", 0755)
  59. data, err := json.MarshalIndent(cfg, "", " ")
  60. if err != nil {
  61. return err
  62. }
  63. return os.WriteFile(m.configPath, data, 0644)
  64. }
  65. // GetServerStatus returns metrics and status for the local BwTest server
  66. func (m *Manager) GetServerStatus() ServerStatus {
  67. return m.server.Status()
  68. }
  69. // SetServerConfig updates server configuration (enabled state, port)
  70. func (m *Manager) SetServerConfig(cfg ServerConfig) error {
  71. m.mu.Lock()
  72. defer m.mu.Unlock()
  73. if cfg.Port <= 0 {
  74. cfg.Port = 2000
  75. }
  76. if cfg.ListenAddr == "" {
  77. cfg.ListenAddr = "0.0.0.0"
  78. }
  79. m.server.Stop()
  80. m.server.config = cfg
  81. if cfg.Enabled {
  82. if err := m.server.Start(); err != nil {
  83. return err
  84. }
  85. }
  86. return m.saveConfigLocked(cfg)
  87. }
  88. // StartTest initiates an asynchronous bandwidth test against the specified target
  89. func (m *Manager) StartTest(ctx context.Context, req SessionRequest) error {
  90. return m.client.Run(ctx, req)
  91. }
  92. // StopTest terminates any ongoing bandwidth test
  93. func (m *Manager) StopTest() {
  94. m.client.Stop()
  95. }
  96. // GetTestStatus retrieves current live test status and telemetry samples
  97. func (m *Manager) GetTestStatus() TestStatus {
  98. st := m.client.GetStatus()
  99. // If test completed with a summary, persist into history
  100. if !st.Running && st.Summary != nil && st.Error == "" {
  101. m.mu.Lock()
  102. alreadyExists := false
  103. for _, h := range m.history {
  104. if h.ID == st.SessionID {
  105. alreadyExists = true
  106. break
  107. }
  108. }
  109. if !alreadyExists && st.SessionID != "" {
  110. record := &HistoricalTestRecord{
  111. ID: st.SessionID,
  112. Target: fmt.Sprintf("%s:%d", st.Target, st.Port),
  113. Protocol: st.Protocol,
  114. Direction: st.Direction,
  115. Timestamp: st.StartTime,
  116. Summary: st.Summary,
  117. }
  118. m.history = append([]*HistoricalTestRecord{record}, m.history...)
  119. if len(m.history) > 50 {
  120. m.history = m.history[:50]
  121. }
  122. }
  123. m.mu.Unlock()
  124. }
  125. return st
  126. }
  127. // SubscribeClient returns a stream of real-time telemetry events
  128. func (m *Manager) SubscribeClient() (<-chan LiveSample, func()) {
  129. return m.client.Subscribe()
  130. }
  131. // GetHistory returns past completed bandwidth test results
  132. func (m *Manager) GetHistory() []*HistoricalTestRecord {
  133. m.mu.RLock()
  134. defer m.mu.RUnlock()
  135. res := make([]*HistoricalTestRecord, len(m.history))
  136. copy(res, m.history)
  137. return res
  138. }
  139. // Stop shuts down the manager and server listeners cleanly
  140. func (m *Manager) Stop() {
  141. m.client.Stop()
  142. m.server.Stop()
  143. }