From c2e803756021b47a974e0357636db320ebc6eeff Mon Sep 17 00:00:00 2001 From: d3m0k1d Date: Sun, 5 Apr 2026 05:37:19 +0300 Subject: [PATCH] chore: add system metrics --- agent/internal/metrics/collector.go | 214 ++++++++++++++++++ agent/main.go | 62 +++++ backend/cmd/main.go | 1 + backend/docs/docs.go | 68 ++++++ backend/docs/swagger.json | 68 ++++++ backend/docs/swagger.yaml | 47 ++++ .../internal/grpcsrv/collector/collector.go | 5 + .../internal/grpcsrv/collector/services.go | 32 +++ backend/internal/grpcsrv/collector/tracker.go | 62 ++++- backend/internal/handlers/agents.go | 41 ++++ proto/hellreign.proto | 10 + proto/proto/hellreign.pb.go | 205 +++++++++++++---- proto/proto/hellreign_grpc.pb.go | 44 +++- 13 files changed, 812 insertions(+), 47 deletions(-) create mode 100644 agent/internal/metrics/collector.go diff --git a/agent/internal/metrics/collector.go b/agent/internal/metrics/collector.go new file mode 100644 index 0000000..2aa333d --- /dev/null +++ b/agent/internal/metrics/collector.go @@ -0,0 +1,214 @@ +package metrics + +import ( + "bufio" + "os" + "strconv" + "strings" + "syscall" + "time" +) + +// SystemMetrics holds current system resource usage. +type SystemMetrics struct { + CPUPercent float64 + MemoryPercent float64 + DiskPercent float64 + NetworkRxBytes float64 + NetworkTxBytes float64 +} + +// Collector collects system metrics from /proc and sysfs. +type Collector struct { + lastCPUTotal uint64 + lastCPUIdle uint64 + lastNetRx float64 + lastNetTx float64 + lastNetTime time.Time +} + +// NewCollector creates a new metrics collector. +func NewCollector() *Collector { + return &Collector{} +} + +// Collect gathers current system metrics. +func (c *Collector) Collect() (SystemMetrics, error) { + var m SystemMetrics + + cpu, err := c.readCPU() + if err == nil { + m.CPUPercent = cpu + } + + mem, err := c.readMemory() + if err == nil { + m.MemoryPercent = mem + } + + disk, err := c.readDisk("/") + if err == nil { + m.DiskPercent = disk + } + + netRx, netTx, err := c.readNetwork() + if err == nil { + m.NetworkRxBytes = netRx + m.NetworkTxBytes = netTx + } + + return m, nil +} + +// readCPU returns CPU usage percentage since last call. +func (c *Collector) readCPU() (float64, error) { + f, err := os.Open("/proc/stat") + if err != nil { + return 0, err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if !strings.HasPrefix(line, "cpu ") { + continue + } + + fields := strings.Fields(line) + if len(fields) < 8 { + return 0, nil + } + + var user, nice, system, idle, iowait, irq, softirq uint64 + user, _ = strconv.ParseUint(fields[1], 10, 64) + nice, _ = strconv.ParseUint(fields[2], 10, 64) + system, _ = strconv.ParseUint(fields[3], 10, 64) + idle, _ = strconv.ParseUint(fields[4], 10, 64) + iowait, _ = strconv.ParseUint(fields[5], 10, 64) + irq, _ = strconv.ParseUint(fields[6], 10, 64) + softirq, _ = strconv.ParseUint(fields[7], 10, 64) + + total := user + nice + system + idle + iowait + irq + softirq + idleTotal := idle + iowait + + if c.lastCPUTotal > 0 { + totalDiff := total - c.lastCPUTotal + idleDiff := idleTotal - c.lastCPUIdle + + if totalDiff > 0 { + cpuPercent := float64(totalDiff-idleDiff) / float64(totalDiff) * 100.0 + c.lastCPUTotal = total + c.lastCPUIdle = idleTotal + return cpuPercent, nil + } + } + + c.lastCPUTotal = total + c.lastCPUIdle = idleTotal + return 0, nil + } + + return 0, scanner.Err() +} + +// readMemory returns RAM usage percentage. +func (c *Collector) readMemory() (float64, error) { + f, err := os.Open("/proc/meminfo") + if err != nil { + return 0, err + } + defer f.Close() + + var total, available uint64 + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "MemTotal:") { + fields := strings.Fields(line) + total, _ = strconv.ParseUint(fields[1], 10, 64) + } else if strings.HasPrefix(line, "MemAvailable:") { + fields := strings.Fields(line) + available, _ = strconv.ParseUint(fields[1], 10, 64) + } + } + + if total == 0 { + return 0, nil + } + + used := total - available + return float64(used) / float64(total) * 100.0, nil +} + +// readDisk returns disk usage percentage for the given path. +func (c *Collector) readDisk(path string) (float64, error) { + var stat syscall.Statfs_t + if err := syscall.Statfs(path, &stat); err != nil { + return 0, err + } + + total := stat.Blocks * uint64(stat.Bsize) + free := stat.Bfree * uint64(stat.Bsize) + + if total == 0 { + return 0, nil + } + + used := total - free + return float64(used) / float64(total) * 100.0, nil +} + +// readNetwork returns network RX/TX bytes per second. +func (c *Collector) readNetwork() (float64, float64, error) { + f, err := os.Open("/proc/net/dev") + if err != nil { + return 0, 0, err + } + defer f.Close() + + var totalRx, totalTx uint64 + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + // Skip header lines + if strings.Contains(line, "|") || strings.HasPrefix(strings.TrimSpace(line), "Inter") { + continue + } + + parts := strings.SplitN(strings.TrimSpace(line), ":", 2) + if len(parts) < 2 { + continue + } + + fields := strings.Fields(parts[1]) + if len(fields) < 9 { + continue + } + + rx, _ := strconv.ParseUint(fields[0], 10, 64) + tx, _ := strconv.ParseUint(fields[8], 10, 64) + totalRx += rx + totalTx += tx + } + + now := time.Now() + var rxRate, txRate float64 + + if !c.lastNetTime.IsZero() { + elapsed := now.Sub(c.lastNetTime).Seconds() + if elapsed > 0 { + rxRate = float64(totalRx) - c.lastNetRx + txRate = float64(totalTx) - c.lastNetTx + // Convert to bytes per second + rxRate = rxRate / elapsed + txRate = txRate / elapsed + } + } + + c.lastNetRx = float64(totalRx) + c.lastNetTx = float64(totalTx) + c.lastNetTime = now + + return rxRate, txRate, nil +} diff --git a/agent/main.go b/agent/main.go index a03f494..b6b7d0f 100644 --- a/agent/main.go +++ b/agent/main.go @@ -12,6 +12,7 @@ import ( "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/client" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/commander" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/config" + agentmetrics "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/metrics" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/logger" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/logsource" "gitea.d3m0k1d.ru/d3m0k1d/HellreigN/agent/internal/logsource/docker" @@ -120,6 +121,11 @@ func main() { }) } + // Start system metrics reporting + wg.Go(func() error { + return reportSystemMetrics(ctx, grpcAddr, creds, cfg.Label, lgr) + }) + // Start log collectors if len(cfg.Services) > 0 { wg.Go(func() error { @@ -370,3 +376,59 @@ func reportServices( } } } + +// reportSystemMetrics periodically collects and sends system metrics to the backend via gRPC. +func reportSystemMetrics( + ctx context.Context, + grpcAddr string, + creds credentials.TransportCredentials, + label string, + lgr *logger.Logger, +) error { + conn, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(creds)) + if err != nil { + return fmt.Errorf("failed to connect for metrics report: %w", err) + } + defer conn.Close() + + ccli := proto.NewCollectorClient(conn) + collector := agentmetrics.NewCollector() + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + lgr.Info("System metrics collector started") + + for { + metrics, err := collector.Collect() + if err != nil { + lgr.Warn("Failed to collect system metrics", "err", err) + } else { + md := metadata.New(map[string]string{"whoami": label}) + _, err := ccli.ReportSystemMetrics( + metadata.NewOutgoingContext(ctx, md), + &proto.SystemMetrics{ + CpuPercent: metrics.CPUPercent, + MemoryPercent: metrics.MemoryPercent, + DiskPercent: metrics.DiskPercent, + NetworkRxBytes: metrics.NetworkRxBytes, + NetworkTxBytes: metrics.NetworkTxBytes, + }, + ) + if err != nil { + lgr.Warn("Failed to report system metrics", "err", err) + } else { + lgr.Debug("System metrics reported", + "cpu", metrics.CPUPercent, + "mem", metrics.MemoryPercent, + "disk", metrics.DiskPercent, + ) + } + } + + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + } + } +} diff --git a/backend/cmd/main.go b/backend/cmd/main.go index c368cdd..c217f05 100644 --- a/backend/cmd/main.go +++ b/backend/cmd/main.go @@ -200,6 +200,7 @@ func main() { agentsGroup.Use(auth.AuthMiddleware(), handlers.RequireManageAgent()) { agentsGroup.GET("", agents.List) + agentsGroup.GET("/system-metrics", agents.GetSystemMetrics) } // Jobs (requires admin permission) diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 4264e18..024b8b9 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -177,6 +177,37 @@ const docTemplate = `{ } } }, + "/agents/system-metrics": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Returns CPU, RAM, disk, and network usage metrics for all connected agents", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "agents" + ], + "summary": "Get agent system metrics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_handlers.AgentSystemMetricsOut" + } + } + } + } + } + }, "/auth/login": { "post": { "description": "Authenticate with login and password, returns a token and permissions", @@ -2706,6 +2737,43 @@ const docTemplate = `{ } } }, + "internal_handlers.AgentSystemMetricsOut": { + "type": "object", + "properties": { + "connected_at": { + "type": "string", + "example": "2026-04-04 10:30:00" + }, + "cpu_percent": { + "type": "number", + "example": 45.2 + }, + "disk_percent": { + "type": "number", + "example": 78.9 + }, + "id": { + "type": "string", + "example": "agent-001" + }, + "label": { + "type": "string", + "example": "web-server-1" + }, + "memory_percent": { + "type": "number", + "example": 62.5 + }, + "network_rx_bytes": { + "type": "number", + "example": 1048576 + }, + "network_tx_bytes": { + "type": "number", + "example": 524288 + } + } + }, "internal_handlers.CheckCmdIn": { "type": "object", "required": [ diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index d77e71f..15f9b89 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -166,6 +166,37 @@ } } }, + "/agents/system-metrics": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "description": "Returns CPU, RAM, disk, and network usage metrics for all connected agents", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "agents" + ], + "summary": "Get agent system metrics", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/internal_handlers.AgentSystemMetricsOut" + } + } + } + } + } + }, "/auth/login": { "post": { "description": "Authenticate with login and password, returns a token and permissions", @@ -2695,6 +2726,43 @@ } } }, + "internal_handlers.AgentSystemMetricsOut": { + "type": "object", + "properties": { + "connected_at": { + "type": "string", + "example": "2026-04-04 10:30:00" + }, + "cpu_percent": { + "type": "number", + "example": 45.2 + }, + "disk_percent": { + "type": "number", + "example": 78.9 + }, + "id": { + "type": "string", + "example": "agent-001" + }, + "label": { + "type": "string", + "example": "web-server-1" + }, + "memory_percent": { + "type": "number", + "example": 62.5 + }, + "network_rx_bytes": { + "type": "number", + "example": 1048576 + }, + "network_tx_bytes": { + "type": "number", + "example": 524288 + } + } + }, "internal_handlers.CheckCmdIn": { "type": "object", "required": [ diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 1d43988..59e1b37 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -374,6 +374,33 @@ definitions: example: agent-001 type: string type: object + internal_handlers.AgentSystemMetricsOut: + properties: + connected_at: + example: "2026-04-04 10:30:00" + type: string + cpu_percent: + example: 45.2 + type: number + disk_percent: + example: 78.9 + type: number + id: + example: agent-001 + type: string + label: + example: web-server-1 + type: string + memory_percent: + example: 62.5 + type: number + network_rx_bytes: + example: 1048576 + type: number + network_tx_bytes: + example: 524288 + type: number + type: object internal_handlers.CheckCmdIn: properties: command: @@ -619,6 +646,26 @@ paths: summary: Create registration token tags: - agents + /agents/system-metrics: + get: + consumes: + - application/json + description: Returns CPU, RAM, disk, and network usage metrics for all connected + agents + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/internal_handlers.AgentSystemMetricsOut' + type: array + security: + - Bearer: [] + summary: Get agent system metrics + tags: + - agents /auth/login: post: consumes: diff --git a/backend/internal/grpcsrv/collector/collector.go b/backend/internal/grpcsrv/collector/collector.go index ab738af..9f03f08 100644 --- a/backend/internal/grpcsrv/collector/collector.go +++ b/backend/internal/grpcsrv/collector/collector.go @@ -157,3 +157,8 @@ func (c *Collector) GetAgent(name string) (*Agent, bool) { func (c *Collector) Agents() []*Agent { return c.tracker.Agents() } + +// GetSystemMetrics delegates to the tracker. +func (c *Collector) GetSystemMetrics() map[string]AgentMetricsInfo { + return c.tracker.GetSystemMetrics() +} diff --git a/backend/internal/grpcsrv/collector/services.go b/backend/internal/grpcsrv/collector/services.go index ca3c0a9..1534182 100644 --- a/backend/internal/grpcsrv/collector/services.go +++ b/backend/internal/grpcsrv/collector/services.go @@ -36,3 +36,35 @@ func (c *Collector) ReportServices(ctx context.Context, req *proto.ServicesUpdat return &proto.ServicesUpdateResp{}, nil } + +// ReportSystemMetrics handles system metrics update from an agent. +// Agents send their current system metrics (CPU, RAM, disk, network). +func (c *Collector) ReportSystemMetrics(ctx context.Context, req *proto.SystemMetrics) (*proto.SystemMetricsResp, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, fmt.Errorf("no metadata in context") + } + + whoamiVals := md["whoami"] + if len(whoamiVals) == 0 { + return nil, fmt.Errorf("whoami metadata missing") + } + agentName := whoamiVals[0] + + metrics := SystemMetrics{ + CPUPercent: req.CpuPercent, + MemoryPercent: req.MemoryPercent, + DiskPercent: req.DiskPercent, + NetworkRxBytes: req.NetworkRxBytes, + NetworkTxBytes: req.NetworkTxBytes, + } + + if ok := c.tracker.UpdateSystemMetrics(agentName, metrics); ok { + log.Printf("Updated system metrics for agent %s: CPU=%.1f%%, RAM=%.1f%%, Disk=%.1f%%", + agentName, metrics.CPUPercent, metrics.MemoryPercent, metrics.DiskPercent) + } else { + log.Printf("Warning: received system metrics for unknown agent %s", agentName) + } + + return &proto.SystemMetricsResp{}, nil +} diff --git a/backend/internal/grpcsrv/collector/tracker.go b/backend/internal/grpcsrv/collector/tracker.go index 7998c6b..c02a980 100644 --- a/backend/internal/grpcsrv/collector/tracker.go +++ b/backend/internal/grpcsrv/collector/tracker.go @@ -97,15 +97,69 @@ func (t *ConnTracker) UpdateServices(id string, services []Service) bool { return true } +// UpdateSystemMetrics updates the system metrics for the given agent. +func (t *ConnTracker) UpdateSystemMetrics(id string, metrics SystemMetrics) bool { + t.mu.Lock() + defer t.mu.Unlock() + agent, ok := t.agents[id] + if !ok { + return false + } + agent.SystemMetrics = metrics + return true +} + +// GetSystemMetrics returns system metrics for all connected agents. +func (t *ConnTracker) GetSystemMetrics() map[string]AgentMetricsInfo { + t.mu.RLock() + defer t.mu.RUnlock() + result := make(map[string]AgentMetricsInfo) + for id, agent := range t.agents { + result[id] = AgentMetricsInfo{ + ID: id, + Label: agent.Label, + ConnectedAt: agent.ConnectedAt, + CPUPercent: agent.SystemMetrics.CPUPercent, + MemoryPercent: agent.SystemMetrics.MemoryPercent, + DiskPercent: agent.SystemMetrics.DiskPercent, + NetworkRxBytes: agent.SystemMetrics.NetworkRxBytes, + NetworkTxBytes: agent.SystemMetrics.NetworkTxBytes, + } + } + return result +} + // Service represents a named service with its current status. type Service struct { Name, Status string } +// SystemMetrics represents system resource metrics. +type SystemMetrics struct { + CPUPercent float64 + MemoryPercent float64 + DiskPercent float64 + NetworkRxBytes float64 + NetworkTxBytes float64 +} + +// AgentMetricsInfo contains agent info with its system metrics. +type AgentMetricsInfo struct { + ID string `json:"id"` + Label string `json:"label"` + ConnectedAt time.Time `json:"connected_at"` + CPUPercent float64 `json:"cpu_percent"` + MemoryPercent float64 `json:"memory_percent"` + DiskPercent float64 `json:"disk_percent"` + NetworkRxBytes float64 `json:"network_rx_bytes"` + NetworkTxBytes float64 `json:"network_tx_bytes"` +} + // Agent represents a connected agent streaming logs to the collector. type Agent struct { - ID string - Label string - Services []Service - ConnectedAt time.Time + ID string + Label string + Services []Service + SystemMetrics SystemMetrics + ConnectedAt time.Time } diff --git a/backend/internal/handlers/agents.go b/backend/internal/handlers/agents.go index 4850c90..76bd47a 100644 --- a/backend/internal/handlers/agents.go +++ b/backend/internal/handlers/agents.go @@ -51,3 +51,44 @@ func (ag *AgentsGroup) List(c *gin.Context) { c.JSON(http.StatusOK, agents) } + +// AgentSystemMetricsOut represents system metrics for a single agent. +type AgentSystemMetricsOut struct { + ID string `json:"id" example:"agent-001"` + Label string `json:"label" example:"web-server-1"` + ConnectedAt string `json:"connected_at" example:"2026-04-04 10:30:00"` + CPUPercent float64 `json:"cpu_percent" example:"45.2"` + MemoryPercent float64 `json:"memory_percent" example:"62.5"` + DiskPercent float64 `json:"disk_percent" example:"78.9"` + NetworkRxBytes float64 `json:"network_rx_bytes" example:"1048576.0"` + NetworkTxBytes float64 `json:"network_tx_bytes" example:"524288.0"` +} + +// GetSystemMetrics returns system load metrics for all connected agents. +// @Summary Get agent system metrics +// @Description Returns CPU, RAM, disk, and network usage metrics for all connected agents +// @Tags agents +// @Security Bearer +// @Accept json +// @Produce json +// @Success 200 {array} AgentSystemMetricsOut +// @Router /agents/system-metrics [get] +func (ag *AgentsGroup) GetSystemMetrics(c *gin.Context) { + metricsMap := ag.collector.GetSystemMetrics() + + metrics := make([]AgentSystemMetricsOut, 0, len(metricsMap)) + for _, m := range metricsMap { + metrics = append(metrics, AgentSystemMetricsOut{ + ID: m.ID, + Label: m.Label, + ConnectedAt: m.ConnectedAt.Format("2006-01-02 15:04:05"), + CPUPercent: m.CPUPercent, + MemoryPercent: m.MemoryPercent, + DiskPercent: m.DiskPercent, + NetworkRxBytes: m.NetworkRxBytes, + NetworkTxBytes: m.NetworkTxBytes, + }) + } + + c.JSON(http.StatusOK, metrics) +} diff --git a/proto/hellreign.proto b/proto/hellreign.proto index 0b2680e..c3f27e2 100644 --- a/proto/hellreign.proto +++ b/proto/hellreign.proto @@ -7,9 +7,19 @@ option go_package="gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/proto"; service Collector { rpc Stream(stream CollectorRequest) returns (CollectorResponse); rpc ReportServices(ServicesUpdate) returns (ServicesUpdateResp); + rpc ReportSystemMetrics(SystemMetrics) returns (SystemMetricsResp); } message ServicesUpdateResp { } +message SystemMetricsResp { +} +message SystemMetrics { + double cpu_percent = 1; // CPU usage percentage (0-100) + double memory_percent = 2; // RAM usage percentage (0-100) + double disk_percent = 3; // Disk usage percentage (0-100) + double network_rx_bytes = 4; // Network received bytes per second + double network_tx_bytes = 5; // Network transmitted bytes per second +} message ServicesUpdate { message ServiceUpdate { string name = 1; diff --git a/proto/proto/hellreign.pb.go b/proto/proto/hellreign.pb.go index 707ce37..bf63d5d 100644 --- a/proto/proto/hellreign.pb.go +++ b/proto/proto/hellreign.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11 -// protoc v3.21.9 +// protoc v7.34.1 // source: hellreign.proto package proto @@ -57,6 +57,118 @@ func (*ServicesUpdateResp) Descriptor() ([]byte, []int) { return file_hellreign_proto_rawDescGZIP(), []int{0} } +type SystemMetricsResp struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SystemMetricsResp) Reset() { + *x = SystemMetricsResp{} + mi := &file_hellreign_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SystemMetricsResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SystemMetricsResp) ProtoMessage() {} + +func (x *SystemMetricsResp) ProtoReflect() protoreflect.Message { + mi := &file_hellreign_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SystemMetricsResp.ProtoReflect.Descriptor instead. +func (*SystemMetricsResp) Descriptor() ([]byte, []int) { + return file_hellreign_proto_rawDescGZIP(), []int{1} +} + +type SystemMetrics struct { + state protoimpl.MessageState `protogen:"open.v1"` + CpuPercent float64 `protobuf:"fixed64,1,opt,name=cpu_percent,json=cpuPercent,proto3" json:"cpu_percent,omitempty"` // CPU usage percentage (0-100) + MemoryPercent float64 `protobuf:"fixed64,2,opt,name=memory_percent,json=memoryPercent,proto3" json:"memory_percent,omitempty"` // RAM usage percentage (0-100) + DiskPercent float64 `protobuf:"fixed64,3,opt,name=disk_percent,json=diskPercent,proto3" json:"disk_percent,omitempty"` // Disk usage percentage (0-100) + NetworkRxBytes float64 `protobuf:"fixed64,4,opt,name=network_rx_bytes,json=networkRxBytes,proto3" json:"network_rx_bytes,omitempty"` // Network received bytes per second + NetworkTxBytes float64 `protobuf:"fixed64,5,opt,name=network_tx_bytes,json=networkTxBytes,proto3" json:"network_tx_bytes,omitempty"` // Network transmitted bytes per second + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SystemMetrics) Reset() { + *x = SystemMetrics{} + mi := &file_hellreign_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SystemMetrics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SystemMetrics) ProtoMessage() {} + +func (x *SystemMetrics) ProtoReflect() protoreflect.Message { + mi := &file_hellreign_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SystemMetrics.ProtoReflect.Descriptor instead. +func (*SystemMetrics) Descriptor() ([]byte, []int) { + return file_hellreign_proto_rawDescGZIP(), []int{2} +} + +func (x *SystemMetrics) GetCpuPercent() float64 { + if x != nil { + return x.CpuPercent + } + return 0 +} + +func (x *SystemMetrics) GetMemoryPercent() float64 { + if x != nil { + return x.MemoryPercent + } + return 0 +} + +func (x *SystemMetrics) GetDiskPercent() float64 { + if x != nil { + return x.DiskPercent + } + return 0 +} + +func (x *SystemMetrics) GetNetworkRxBytes() float64 { + if x != nil { + return x.NetworkRxBytes + } + return 0 +} + +func (x *SystemMetrics) GetNetworkTxBytes() float64 { + if x != nil { + return x.NetworkTxBytes + } + return 0 +} + type ServicesUpdate struct { state protoimpl.MessageState `protogen:"open.v1"` Services []*ServicesUpdate_ServiceUpdate `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` @@ -66,7 +178,7 @@ type ServicesUpdate struct { func (x *ServicesUpdate) Reset() { *x = ServicesUpdate{} - mi := &file_hellreign_proto_msgTypes[1] + mi := &file_hellreign_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -78,7 +190,7 @@ func (x *ServicesUpdate) String() string { func (*ServicesUpdate) ProtoMessage() {} func (x *ServicesUpdate) ProtoReflect() protoreflect.Message { - mi := &file_hellreign_proto_msgTypes[1] + mi := &file_hellreign_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -91,7 +203,7 @@ func (x *ServicesUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ServicesUpdate.ProtoReflect.Descriptor instead. func (*ServicesUpdate) Descriptor() ([]byte, []int) { - return file_hellreign_proto_rawDescGZIP(), []int{1} + return file_hellreign_proto_rawDescGZIP(), []int{3} } func (x *ServicesUpdate) GetServices() []*ServicesUpdate_ServiceUpdate { @@ -110,7 +222,7 @@ type CollectorRequest struct { func (x *CollectorRequest) Reset() { *x = CollectorRequest{} - mi := &file_hellreign_proto_msgTypes[2] + mi := &file_hellreign_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -122,7 +234,7 @@ func (x *CollectorRequest) String() string { func (*CollectorRequest) ProtoMessage() {} func (x *CollectorRequest) ProtoReflect() protoreflect.Message { - mi := &file_hellreign_proto_msgTypes[2] + mi := &file_hellreign_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -135,7 +247,7 @@ func (x *CollectorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CollectorRequest.ProtoReflect.Descriptor instead. func (*CollectorRequest) Descriptor() ([]byte, []int) { - return file_hellreign_proto_rawDescGZIP(), []int{2} + return file_hellreign_proto_rawDescGZIP(), []int{4} } func (x *CollectorRequest) GetMessage() string { @@ -153,7 +265,7 @@ type CollectorResponse struct { func (x *CollectorResponse) Reset() { *x = CollectorResponse{} - mi := &file_hellreign_proto_msgTypes[3] + mi := &file_hellreign_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -165,7 +277,7 @@ func (x *CollectorResponse) String() string { func (*CollectorResponse) ProtoMessage() {} func (x *CollectorResponse) ProtoReflect() protoreflect.Message { - mi := &file_hellreign_proto_msgTypes[3] + mi := &file_hellreign_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -178,7 +290,7 @@ func (x *CollectorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CollectorResponse.ProtoReflect.Descriptor instead. func (*CollectorResponse) Descriptor() ([]byte, []int) { - return file_hellreign_proto_rawDescGZIP(), []int{3} + return file_hellreign_proto_rawDescGZIP(), []int{5} } type Command struct { @@ -192,7 +304,7 @@ type Command struct { func (x *Command) Reset() { *x = Command{} - mi := &file_hellreign_proto_msgTypes[4] + mi := &file_hellreign_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -204,7 +316,7 @@ func (x *Command) String() string { func (*Command) ProtoMessage() {} func (x *Command) ProtoReflect() protoreflect.Message { - mi := &file_hellreign_proto_msgTypes[4] + mi := &file_hellreign_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -217,7 +329,7 @@ func (x *Command) ProtoReflect() protoreflect.Message { // Deprecated: Use Command.ProtoReflect.Descriptor instead. func (*Command) Descriptor() ([]byte, []int) { - return file_hellreign_proto_rawDescGZIP(), []int{4} + return file_hellreign_proto_rawDescGZIP(), []int{6} } func (x *Command) GetId() int64 { @@ -253,7 +365,7 @@ type FinishedCommand struct { func (x *FinishedCommand) Reset() { *x = FinishedCommand{} - mi := &file_hellreign_proto_msgTypes[5] + mi := &file_hellreign_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -265,7 +377,7 @@ func (x *FinishedCommand) String() string { func (*FinishedCommand) ProtoMessage() {} func (x *FinishedCommand) ProtoReflect() protoreflect.Message { - mi := &file_hellreign_proto_msgTypes[5] + mi := &file_hellreign_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -278,7 +390,7 @@ func (x *FinishedCommand) ProtoReflect() protoreflect.Message { // Deprecated: Use FinishedCommand.ProtoReflect.Descriptor instead. func (*FinishedCommand) Descriptor() ([]byte, []int) { - return file_hellreign_proto_rawDescGZIP(), []int{5} + return file_hellreign_proto_rawDescGZIP(), []int{7} } func (x *FinishedCommand) GetId() int64 { @@ -319,7 +431,7 @@ type ServicesUpdate_ServiceUpdate struct { func (x *ServicesUpdate_ServiceUpdate) Reset() { *x = ServicesUpdate_ServiceUpdate{} - mi := &file_hellreign_proto_msgTypes[6] + mi := &file_hellreign_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -331,7 +443,7 @@ func (x *ServicesUpdate_ServiceUpdate) String() string { func (*ServicesUpdate_ServiceUpdate) ProtoMessage() {} func (x *ServicesUpdate_ServiceUpdate) ProtoReflect() protoreflect.Message { - mi := &file_hellreign_proto_msgTypes[6] + mi := &file_hellreign_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -344,7 +456,7 @@ func (x *ServicesUpdate_ServiceUpdate) ProtoReflect() protoreflect.Message { // Deprecated: Use ServicesUpdate_ServiceUpdate.ProtoReflect.Descriptor instead. func (*ServicesUpdate_ServiceUpdate) Descriptor() ([]byte, []int) { - return file_hellreign_proto_rawDescGZIP(), []int{1, 0} + return file_hellreign_proto_rawDescGZIP(), []int{3, 0} } func (x *ServicesUpdate_ServiceUpdate) GetName() string { @@ -366,7 +478,15 @@ var File_hellreign_proto protoreflect.FileDescriptor const file_hellreign_proto_rawDesc = "" + "\n" + "\x0fhellreign.proto\x12\x04chat\"\x14\n" + - "\x12ServicesUpdateResp\"\x8d\x01\n" + + "\x12ServicesUpdateResp\"\x13\n" + + "\x11SystemMetricsResp\"\xce\x01\n" + + "\rSystemMetrics\x12\x1f\n" + + "\vcpu_percent\x18\x01 \x01(\x01R\n" + + "cpuPercent\x12%\n" + + "\x0ememory_percent\x18\x02 \x01(\x01R\rmemoryPercent\x12!\n" + + "\fdisk_percent\x18\x03 \x01(\x01R\vdiskPercent\x12(\n" + + "\x10network_rx_bytes\x18\x04 \x01(\x01R\x0enetworkRxBytes\x12(\n" + + "\x10network_tx_bytes\x18\x05 \x01(\x01R\x0enetworkTxBytes\"\x8d\x01\n" + "\x0eServicesUpdate\x12>\n" + "\bservices\x18\x01 \x03(\v2\".chat.ServicesUpdate.ServiceUpdateR\bservices\x1a;\n" + "\rServiceUpdate\x12\x12\n" + @@ -384,10 +504,11 @@ const file_hellreign_proto_rawDesc = "" + "\x02id\x18\x01 \x01(\x03R\x02id\x12\x16\n" + "\x06status\x18\x02 \x01(\x05R\x06status\x12\x16\n" + "\x06stdout\x18\x03 \x01(\tR\x06stdout\x12\x16\n" + - "\x06stderr\x18\x04 \x01(\tR\x06stderr2\x8a\x01\n" + + "\x06stderr\x18\x04 \x01(\tR\x06stderr2\xcf\x01\n" + "\tCollector\x12;\n" + "\x06Stream\x12\x16.chat.CollectorRequest\x1a\x17.chat.CollectorResponse(\x01\x12@\n" + - "\x0eReportServices\x12\x14.chat.ServicesUpdate\x1a\x18.chat.ServicesUpdateResp2?\n" + + "\x0eReportServices\x12\x14.chat.ServicesUpdate\x1a\x18.chat.ServicesUpdateResp\x12C\n" + + "\x13ReportSystemMetrics\x12\x13.chat.SystemMetrics\x1a\x17.chat.SystemMetricsResp2?\n" + "\tCommander\x122\n" + "\x06Stream\x12\x15.chat.FinishedCommand\x1a\r.chat.Command(\x010\x01B0Z.gitea.d3m0k1d.ru/d3m0k1d/HellreigN/proto/protob\x06proto3" @@ -403,26 +524,30 @@ func file_hellreign_proto_rawDescGZIP() []byte { return file_hellreign_proto_rawDescData } -var file_hellreign_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_hellreign_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_hellreign_proto_goTypes = []any{ (*ServicesUpdateResp)(nil), // 0: chat.ServicesUpdateResp - (*ServicesUpdate)(nil), // 1: chat.ServicesUpdate - (*CollectorRequest)(nil), // 2: chat.CollectorRequest - (*CollectorResponse)(nil), // 3: chat.CollectorResponse - (*Command)(nil), // 4: chat.Command - (*FinishedCommand)(nil), // 5: chat.FinishedCommand - (*ServicesUpdate_ServiceUpdate)(nil), // 6: chat.ServicesUpdate.ServiceUpdate + (*SystemMetricsResp)(nil), // 1: chat.SystemMetricsResp + (*SystemMetrics)(nil), // 2: chat.SystemMetrics + (*ServicesUpdate)(nil), // 3: chat.ServicesUpdate + (*CollectorRequest)(nil), // 4: chat.CollectorRequest + (*CollectorResponse)(nil), // 5: chat.CollectorResponse + (*Command)(nil), // 6: chat.Command + (*FinishedCommand)(nil), // 7: chat.FinishedCommand + (*ServicesUpdate_ServiceUpdate)(nil), // 8: chat.ServicesUpdate.ServiceUpdate } var file_hellreign_proto_depIdxs = []int32{ - 6, // 0: chat.ServicesUpdate.services:type_name -> chat.ServicesUpdate.ServiceUpdate - 2, // 1: chat.Collector.Stream:input_type -> chat.CollectorRequest - 1, // 2: chat.Collector.ReportServices:input_type -> chat.ServicesUpdate - 5, // 3: chat.Commander.Stream:input_type -> chat.FinishedCommand - 3, // 4: chat.Collector.Stream:output_type -> chat.CollectorResponse - 0, // 5: chat.Collector.ReportServices:output_type -> chat.ServicesUpdateResp - 4, // 6: chat.Commander.Stream:output_type -> chat.Command - 4, // [4:7] is the sub-list for method output_type - 1, // [1:4] is the sub-list for method input_type + 8, // 0: chat.ServicesUpdate.services:type_name -> chat.ServicesUpdate.ServiceUpdate + 4, // 1: chat.Collector.Stream:input_type -> chat.CollectorRequest + 3, // 2: chat.Collector.ReportServices:input_type -> chat.ServicesUpdate + 2, // 3: chat.Collector.ReportSystemMetrics:input_type -> chat.SystemMetrics + 7, // 4: chat.Commander.Stream:input_type -> chat.FinishedCommand + 5, // 5: chat.Collector.Stream:output_type -> chat.CollectorResponse + 0, // 6: chat.Collector.ReportServices:output_type -> chat.ServicesUpdateResp + 1, // 7: chat.Collector.ReportSystemMetrics:output_type -> chat.SystemMetricsResp + 6, // 8: chat.Commander.Stream:output_type -> chat.Command + 5, // [5:9] is the sub-list for method output_type + 1, // [1:5] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name @@ -433,14 +558,14 @@ func file_hellreign_proto_init() { if File_hellreign_proto != nil { return } - file_hellreign_proto_msgTypes[4].OneofWrappers = []any{} + file_hellreign_proto_msgTypes[6].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_hellreign_proto_rawDesc), len(file_hellreign_proto_rawDesc)), NumEnums: 0, - NumMessages: 7, + NumMessages: 9, NumExtensions: 0, NumServices: 2, }, diff --git a/proto/proto/hellreign_grpc.pb.go b/proto/proto/hellreign_grpc.pb.go index 3c4b023..35aa8a2 100644 --- a/proto/proto/hellreign_grpc.pb.go +++ b/proto/proto/hellreign_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 -// - protoc v3.21.9 +// - protoc v7.34.1 // source: hellreign.proto package proto @@ -19,8 +19,9 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Collector_Stream_FullMethodName = "/chat.Collector/Stream" - Collector_ReportServices_FullMethodName = "/chat.Collector/ReportServices" + Collector_Stream_FullMethodName = "/chat.Collector/Stream" + Collector_ReportServices_FullMethodName = "/chat.Collector/ReportServices" + Collector_ReportSystemMetrics_FullMethodName = "/chat.Collector/ReportSystemMetrics" ) // CollectorClient is the client API for Collector service. @@ -29,6 +30,7 @@ const ( type CollectorClient interface { Stream(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[CollectorRequest, CollectorResponse], error) ReportServices(ctx context.Context, in *ServicesUpdate, opts ...grpc.CallOption) (*ServicesUpdateResp, error) + ReportSystemMetrics(ctx context.Context, in *SystemMetrics, opts ...grpc.CallOption) (*SystemMetricsResp, error) } type collectorClient struct { @@ -62,12 +64,23 @@ func (c *collectorClient) ReportServices(ctx context.Context, in *ServicesUpdate return out, nil } +func (c *collectorClient) ReportSystemMetrics(ctx context.Context, in *SystemMetrics, opts ...grpc.CallOption) (*SystemMetricsResp, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SystemMetricsResp) + err := c.cc.Invoke(ctx, Collector_ReportSystemMetrics_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // CollectorServer is the server API for Collector service. // All implementations must embed UnimplementedCollectorServer // for forward compatibility. type CollectorServer interface { Stream(grpc.ClientStreamingServer[CollectorRequest, CollectorResponse]) error ReportServices(context.Context, *ServicesUpdate) (*ServicesUpdateResp, error) + ReportSystemMetrics(context.Context, *SystemMetrics) (*SystemMetricsResp, error) mustEmbedUnimplementedCollectorServer() } @@ -84,6 +97,9 @@ func (UnimplementedCollectorServer) Stream(grpc.ClientStreamingServer[CollectorR func (UnimplementedCollectorServer) ReportServices(context.Context, *ServicesUpdate) (*ServicesUpdateResp, error) { return nil, status.Error(codes.Unimplemented, "method ReportServices not implemented") } +func (UnimplementedCollectorServer) ReportSystemMetrics(context.Context, *SystemMetrics) (*SystemMetricsResp, error) { + return nil, status.Error(codes.Unimplemented, "method ReportSystemMetrics not implemented") +} func (UnimplementedCollectorServer) mustEmbedUnimplementedCollectorServer() {} func (UnimplementedCollectorServer) testEmbeddedByValue() {} @@ -130,6 +146,24 @@ func _Collector_ReportServices_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _Collector_ReportSystemMetrics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SystemMetrics) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CollectorServer).ReportSystemMetrics(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Collector_ReportSystemMetrics_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CollectorServer).ReportSystemMetrics(ctx, req.(*SystemMetrics)) + } + return interceptor(ctx, in, info, handler) +} + // Collector_ServiceDesc is the grpc.ServiceDesc for Collector service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -141,6 +175,10 @@ var Collector_ServiceDesc = grpc.ServiceDesc{ MethodName: "ReportServices", Handler: _Collector_ReportServices_Handler, }, + { + MethodName: "ReportSystemMetrics", + Handler: _Collector_ReportSystemMetrics_Handler, + }, }, Streams: []grpc.StreamDesc{ {