Add CPU usage to host inventory

This commit is contained in:
2026-10-04 20:30:43 -04:00
parent 456af32b36
commit a1b957392f
7 changed files with 91 additions and 5 deletions
+33 -2
View File
@@ -30,7 +30,7 @@ import urllib.parse
import urllib.request
import uuid
VERSION = "1.4.1"
VERSION = "1.4.2"
LABEL = "io.electric-cherry"
MAX_FILE = 32 * 1024 * 1024
LIBRARY_DOWNLOAD_WORKERS = 4
@@ -710,6 +710,8 @@ class Agent:
self.docker = docker or Docker()
self.stopping = threading.Event()
self.current_operation = None
self.cpu_sample_lock = threading.Lock()
self.cpu_sample = None
self.instance_locks = {}
self.read_gate = ReadWriteGate()
self.read_claim_lock = threading.Lock()
@@ -899,7 +901,36 @@ class Agent:
except (OSError, ValueError):
raise AgentError("Controller request failed") from None
def cpu_usage_percent(self):
"""Host-wide busy time since the previous inventory sample, without waiting."""
with self.cpu_sample_lock:
previous = self.cpu_sample
self.cpu_sample = None
try:
with Path("/proc/stat").open() as source:
fields = source.readline().split()
if not fields or fields[0] != "cpu" or len(fields) < 9:
return None
# Guest time is already included in user/nice. Idle and I/O
# wait are not busy; steal counts as unavailable CPU capacity.
counters = tuple(int(value) for value in fields[1:9])
if any(value < 0 for value in counters):
return None
except (OSError, ValueError):
return None
self.cpu_sample = counters
if previous is None:
return None
deltas = [current - old for current, old in zip(counters, previous)]
total = sum(deltas)
# Counters can reset and Linux can revise I/O wait downwards.
# Rebase rather than reporting a misleading utilization value.
if total <= 0 or any(delta < 0 for delta in deltas):
return None
return round(100 * (total - deltas[3] - deltas[4]) / total, 1)
def inventory(self):
cpu_usage = self.cpu_usage_percent()
usage = shutil.disk_usage(self.root)
memory = {}
try:
@@ -1004,7 +1035,7 @@ class Agent:
os_name = line.split("=", 1)[1].strip('"')
update = self.update_inventory()
capabilities = ["instance.cancel-create", "operations.read-lane"] + (["host.decommission"] if self.can_uninstall() else []) + (["agent.update"] if update["enabled"] else [])
return {"hostname": socket.gethostname(), "agentVersion": VERSION, "protocolVersion": 1, "capabilities": capabilities, "agentUpdate": update, "os": os_name, "osVersion": platform.release(), "architecture": platform.machine(), "cpu": platform.processor() or platform.machine(), "cpuCount": os.cpu_count() or 0, "memoryTotal": memory.get("MemTotal", 0), "memoryAvailable": memory.get("MemAvailable", 0), "storageTotal": usage.total, "storageAvailable": usage.free, "dockerVersion": docker_version, "dockerRunning": docker_running, "directoriesWritable": os.access(self.root, os.W_OK), "runtimeImages": sorted(set(images)), "libraries": libraries, "libraryCaches": caches, "currentOperation": self.current_operation, "portRange": self.config.get("portRange", {"min": 28960, "max": 29060})}
return {"hostname": socket.gethostname(), "agentVersion": VERSION, "protocolVersion": 1, "capabilities": capabilities, "agentUpdate": update, "os": os_name, "osVersion": platform.release(), "architecture": platform.machine(), "cpu": platform.processor() or platform.machine(), "cpuCount": os.cpu_count() or 0, **({"cpuUsagePercent": cpu_usage} if cpu_usage is not None else {}), "memoryTotal": memory.get("MemTotal", 0), "memoryAvailable": memory.get("MemAvailable", 0), "storageTotal": usage.total, "storageAvailable": usage.free, "dockerVersion": docker_version, "dockerRunning": docker_running, "directoriesWritable": os.access(self.root, os.W_OK), "runtimeImages": sorted(set(images)), "libraries": libraries, "libraryCaches": caches, "currentOperation": self.current_operation, "portRange": self.config.get("portRange", {"min": 28960, "max": 29060})}
def safe_to_disrupt(self, instance_id, generation):
health = self.inspect(instance_id, generation)