Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **`edgelet system status` resource breakdown:** `agentCpuPercent`, `agentMemoryMiB`, optional embedded `runtime*` fields, and `edgeletTotal*` stack totals on `GET /v1/system/status`.

### Changed

- **Agent resource metrics:** `cpuUsage` / `memoryUsage` now report edgelet stack totals using process RSS and smoothed CPU (control plane + embedded containerd child when present). Controller `PUT status` keys are unchanged; reported values are more accurate (especially memory). External `docker` / `podman` engines remain agent-only totals.

## [1.0.0-rc.6] — June 2026

Expand Down
14 changes: 11 additions & 3 deletions docs/cli/output-schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ Route: `GET /v1/system/status`
| Field | Type | Description |
|-------|------|-------------|
| `connectionToController` | string | Controller connectivity summary |
| `cpuUsage` | number | Host CPU usage snapshot |
| `agentCpuPercent` | number | Control-plane CPU percent |
| `agentMemoryMiB` | number | Control-plane RSS in MiB |
| `runtimeCpuPercent` | number | Embedded containerd child CPU (embedded engine only) |
| `runtimeMemoryMiB` | number | Embedded containerd child RSS in MiB |
| `runtimeAvailable` | boolean | Embedded runtime process present |
| `runtimeDegraded` | boolean | Embedded engine configured but runtime child missing |
| `edgeletTotalCpuPercent` | number | Edgelet stack CPU total |
| `edgeletTotalMemoryMiB` | number | Edgelet stack memory total in MiB |
| `cpuUsage` | number | Edgelet stack CPU total (controller alias) |
| `edgeletDaemon` | string | Daemon lifecycle state (e.g. `running`) |
| `memoryUsage` | string | Optional memory summary |
| `memoryUsage` | number | Edgelet stack memory total in MiB (controller alias) |
| `diskUsage` | string | Optional disk summary |
| `runningMicroservices` | number | Optional running MS count |
| `systemAvailableDisk` | string | Optional free disk |
| `systemAvailableMemory` | string | Optional free memory |
| `systemTime` | string | Optional agent time |
| `systemTotalCpu` | string | Optional total CPU |
| `systemTotalCpu` | string | Host CPU usage percent |
| `availableNetworkInterfaces` | string | Comma-separated interfaces |
| `availableRuntimes` | string | Comma-separated runtime handlers |

Expand Down
30 changes: 24 additions & 6 deletions docs/edgelet/modules/resourceconsumption.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
# Resource Consumption Manager

The resource consumption manager samples **host CPU, memory, and disk** usage against configured limits and publishes results to StatusReporter. It also participates in limit enforcement signaling for the agent.
The resource consumption manager samples **edgelet stack and host** usage against configured limits and publishes results to StatusReporter. It also participates in limit enforcement signaling for the agent.

**Code:** `internal/resourceconsumption/`

## Purpose

- Poll system metrics via gopsutil (CPU, mem, disk)
- Compare against configured limits (bytes / CPU percentage)
- Sample control-plane RSS/CPU via gopsutil (`process.MemoryInfo`, `process.Percent`)
- When `containerEngine=edgelet` on an embedded build, include the `--edgelet-containerd-child` data-plane process(es)
- Compare stack totals against configured limits (bytes / CPU percentage)
- Update `ResourceConsumptionManagerStatus` on StatusReporter
- Collect initial sample immediately on start (no wait for first tick)

## Metrics

| Field | Meaning |
|-------|---------|
| `agentCpuPercent` / `agentMemoryMiB` | Control-plane `edgelet daemon` process |
| `runtimeCpuPercent` / `runtimeMemoryMiB` | Embedded containerd child (embedded engine only) |
| `cpuUsage` / `memoryUsage` | **Edgelet stack total** (agent + runtime when available) — also sent to Pot in `PUT status` |
| `systemTotalCpu` / `systemAvailableMemory` | Whole host |

External `docker` / `podman` engines report agent-only stack totals (no runtime child tracking).

## Dependencies

| Depends on | Reason |
|------------|--------|
| `statusreporter` | Publish usage metrics |
| `config` | Limits and poll frequency |
| `gopsutil` | Host metrics |
| `gopsutil` | Process and host metrics |
| `pkg/containerd` | Discover embedded containerd child PIDs on Linux |

| Used by | Reason |
|---------|--------|
Expand Down Expand Up @@ -54,7 +67,7 @@ Exact YAML names match `config.yaml` profiles — see default config in `interna
| StatusReporter index | `0` (`utils.ResourceConsumptionManager`) |
| First slot in `modulesStatus[]` | Resource Consumption |

Status fields include `memoryUsage`, `cpuUsage`, `diskUsage` (human-readable in logs on start).
Status fields include stack breakdown plus legacy `memoryUsage`, `cpuUsage`, `diskUsage`.

## External APIs

Expand All @@ -64,18 +77,23 @@ Exposed indirectly via `GET /v1/system/status` resource section and Controller s

- Log module: `"Resource Consumption Manager"`
- Initial and periodic debug logs with MiB/GiB formatting
- Warn when multiple embedded containerd child PIDs are detected

## Failure modes

| Symptom | Typical cause |
|---------|----------------|
| Zero usage reported | gopsutil error on platform |
| Limit warnings | Usage exceeded configured thresholds |
| `runtimeDegraded=true` | Embedded engine configured but containerd child not running |
| Limit warnings | Stack usage exceeded configured thresholds |

## Code map

| File | Role |
|------|------|
| `manager.go` | Sampling loop, limit comparison, status updates |
| `stats.go` | Parallel process/host sampling and CPU smoothing |
| `runtime_linux.go` | Embedded runtime PID discovery |
| `host_cpu_linux.go` | Linux host CPU fallback |

Related: [statusreporter.md](statusreporter.md), [supervisor.md](supervisor.md).
8 changes: 8 additions & 0 deletions internal/cli/output/edgeletapi_human.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import (

var statusOutputOrder = []string{
"connectionToController",
"agentCpuPercent",
"agentMemoryMiB",
"runtimeCpuPercent",
"runtimeMemoryMiB",
"runtimeAvailable",
"runtimeDegraded",
"edgeletTotalCpuPercent",
"edgeletTotalMemoryMiB",
"cpuUsage",
"diskUsage",
"edgeletDaemon",
Expand Down
15 changes: 14 additions & 1 deletion internal/edgeletapi/handlers/report_key_normalization.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,18 @@ func normalizeReportKey(raw string) string {
runes[0] = unicode.ToUpper(runes[0])
camel += string(runes)
}
return camel
return fixStatusReportKeyAcronyms(camel)
}

func fixStatusReportKeyAcronyms(key string) string {
switch key {
case "agentMemoryMib":
return "agentMemoryMiB"
case "runtimeMemoryMib":
return "runtimeMemoryMiB"
case "edgeletTotalMemoryMib":
return "edgeletTotalMemoryMiB"
default:
return key
}
}
2 changes: 2 additions & 0 deletions internal/edgeletapi/handlers/report_key_normalization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ func TestNormalizeReportKey_CamelCase(t *testing.T) {
tests := map[string]string{
"connection-to-controller": "connectionToController",
"cpu usage": "cpuUsage",
"agent cpu percent": "agentCpuPercent",
"edgelet total memory mib": "edgeletTotalMemoryMiB",
"edgelet daemon": "edgeletDaemon",
"gps-coordinates(lat,lon)": "gpsCoordinates",
"developer's-mode": "developerMode",
Expand Down
24 changes: 19 additions & 5 deletions internal/models/resource_consumption_manager_status.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
package models

// ResourceConsumptionManagerStatus represents the Resource Consumption Manager status
// ResourceConsumptionManagerStatus represents the Resource Consumption Manager status.
// CPUUsage and MemoryUsage are edgelet stack totals (control plane + embedded runtime when available).
type ResourceConsumptionManagerStatus struct {
MemoryUsage float64 `json:"memoryUsage" yaml:"memoryUsage"` // Memory usage in MiB
AgentCPUPercent float64 `json:"agentCpuPercent" yaml:"agentCpuPercent"`
AgentMemoryMiB float64 `json:"agentMemoryMiB" yaml:"agentMemoryMiB"`
RuntimeCPUPercent float64 `json:"runtimeCpuPercent" yaml:"runtimeCpuPercent"`
RuntimeMemoryMiB float64 `json:"runtimeMemoryMiB" yaml:"runtimeMemoryMiB"`
RuntimeAvailable bool `json:"runtimeAvailable" yaml:"runtimeAvailable"`
RuntimeDegraded bool `json:"runtimeDegraded" yaml:"runtimeDegraded"`
RuntimeTracked bool `json:"runtimeTracked" yaml:"runtimeTracked"`
RuntimePIDCount int `json:"runtimePidCount" yaml:"runtimePidCount"`
EdgeletTotalCPUPercent float64 `json:"edgeletTotalCpuPercent" yaml:"edgeletTotalCpuPercent"`
EdgeletTotalMemoryMiB float64 `json:"edgeletTotalMemoryMiB" yaml:"edgeletTotalMemoryMiB"`

MemoryUsage float64 `json:"memoryUsage" yaml:"memoryUsage"` // Edgelet stack memory in MiB (alias of EdgeletTotalMemoryMiB)
DiskUsage float64 `json:"diskUsage" yaml:"diskUsage"` // Disk usage in GiB
CPUUsage float64 `json:"cpuUsage" yaml:"cpuUsage"` // CPU usage percentage (0-100)
CPUUsage float64 `json:"cpuUsage" yaml:"cpuUsage"` // Edgelet stack CPU percent (alias of EdgeletTotalCPUPercent)
MemoryViolation bool `json:"memoryViolation" yaml:"memoryViolation"` // Whether memory limit is violated
DiskViolation bool `json:"diskViolation" yaml:"diskViolation"` // Whether disk limit is violated
CPUViolation bool `json:"cpuViolation" yaml:"cpuViolation"` // Whether CPU limit is violated
AvailableMemory int64 `json:"availableMemory" yaml:"availableMemory"` // System available memory in bytes
TotalCPU float64 `json:"totalCpu" yaml:"totalCpu"` // Total system CPU usage percentage
TotalCPU float64 `json:"totalCpu" yaml:"totalCpu"` // Host CPU usage percentage
AvailableDisk int64 `json:"availableDisk" yaml:"availableDisk"` // System available disk space in bytes
TotalDiskSpace int64 `json:"totalDiskSpace" yaml:"totalDiskSpace"` // Total system disk space in bytes
}

// NewResourceConsumptionManagerStatus creates a new ResourceConsumptionManagerStatus
func NewResourceConsumptionManagerStatus() *ResourceConsumptionManagerStatus {
return &ResourceConsumptionManagerStatus{}
return &ResourceConsumptionManagerStatus{
RuntimeAvailable: true,
}
}

// SetMemoryUsage sets the memory usage and returns the status for chaining
Expand Down
53 changes: 53 additions & 0 deletions internal/resourceconsumption/host_cpu_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//go:build linux

package resourceconsumption

import (
"os"
"strconv"
"strings"

"github.com/eclipse-iofog/edgelet/internal/utils/logging"
)

func (rcm *Manager) getTotalCPULinux() float64 {
data, err := os.ReadFile("/proc/stat")
if err != nil {
logging.LogError(moduleName, "Error reading /proc/stat", err)
return 0.0
}

lines := strings.Split(string(data), "\n")
if len(lines) == 0 {
return 0.0
}

firstLine := strings.TrimSpace(lines[0])
if !strings.HasPrefix(firstLine, "cpu ") {
return 0.0
}

parts := strings.Fields(firstLine)
if len(parts) < 8 {
return 0.0
}

user, _ := strconv.ParseInt(parts[1], 10, 64)
nice, _ := strconv.ParseInt(parts[2], 10, 64)
system, _ := strconv.ParseInt(parts[3], 10, 64)
idle, _ := strconv.ParseInt(parts[4], 10, 64)
iowait, _ := strconv.ParseInt(parts[5], 10, 64)
irq, _ := strconv.ParseInt(parts[6], 10, 64)
softirq, _ := strconv.ParseInt(parts[7], 10, 64)
steal := int64(0)
if len(parts) >= 9 {
steal, _ = strconv.ParseInt(parts[8], 10, 64)
}

totalTime := user + nice + system + idle + iowait + irq + softirq + steal
idleTime := idle + iowait
if totalTime <= 0 {
return 0.0
}
return float64(totalTime-idleTime) / float64(totalTime) * 100.0
}
7 changes: 7 additions & 0 deletions internal/resourceconsumption/host_cpu_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build !linux

package resourceconsumption

func (rcm *Manager) getTotalCPULinux() float64 {
return 0
}
Loading