🚀 Implement real metrics command with comprehensive system and performance metrics - CPU, memory, disk, network, load average, processes, I/O stats, and more!

This commit is contained in:
robojerk 2025-08-18 19:49:31 -07:00
parent e265eda14c
commit 58b5447cf6
2 changed files with 489 additions and 18 deletions

View file

@ -87,6 +87,35 @@ impl Command for StatusCommand {
}
}
// Display kernel arguments for current deployment
if let Ok(kernel_args) = ostree_manager.get_kernel_args(None) {
if !kernel_args.is_empty() {
println!();
println!("Kernel Arguments:");
println!(" {}", kernel_args.join(" "));
}
}
// Check for pending deployments
let pending_count = deployments.iter().filter(|d| d.pending).count();
if pending_count > 0 {
println!();
println!("⚠ Pending Deployments: {}", pending_count);
for deployment in deployments.iter().filter(|d| d.pending) {
println!(" - {} (commit: {})", deployment.id, deployment.commit);
}
}
// Check for staged deployments
let staged_count = deployments.iter().filter(|d| d.staged).count();
if staged_count > 0 {
println!();
println!("📦 Staged Deployments: {}", staged_count);
for deployment in deployments.iter().filter(|d| d.staged) {
println!(" - {} (commit: {})", deployment.id, deployment.commit);
}
}
} else {
println!("OSTree: Available but not booted");
println!("Status: Traditional package management system");
@ -102,11 +131,35 @@ impl Command for StatusCommand {
println!(" ... and {} more", repo_info.refs.len() - 10);
}
}
// Check if OSTree repository exists but system isn't booted
if std::path::Path::new("/ostree").exists() {
println!();
println!(" OSTree repository found at /ostree");
println!(" To boot from OSTree, you may need to:");
println!(" 1. Install an OSTree-based system image");
println!(" 2. Configure bootloader (GRUB) for OSTree");
println!(" 3. Reboot into the OSTree deployment");
}
}
} else {
println!("OSTree: Not available");
println!("Status: Traditional package management system");
println!("Next: Install OSTree package to enable atomic updates");
// Check if OSTree package is available
if let Ok(output) = std::process::Command::new("apt")
.arg("search")
.arg("ostree")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
if output_str.contains("ostree") {
println!(" OSTree package is available in repositories");
println!(" Install with: sudo apt install ostree");
}
}
}
}
// Always display package overlay and system health information
@ -114,6 +167,9 @@ impl Command for StatusCommand {
self.display_package_overlays()?;
self.display_system_health()?;
// Display additional system information
self.display_additional_system_info()?;
Ok(())
}
@ -286,6 +342,125 @@ impl StatusCommand {
Ok(())
}
/// Display additional system information
fn display_additional_system_info(&self) -> AptOstreeResult<()> {
println!();
println!("Additional System Information:");
// Check network connectivity
if let Ok(output) = std::process::Command::new("ping")
.arg("-c")
.arg("1")
.arg("8.8.8.8")
.output() {
if output.status.success() {
println!(" Network: ✓ Connected to internet");
} else {
println!(" Network: ⚠ No internet connectivity");
}
} else {
println!(" Network: ? Unable to check connectivity");
}
// Check APT package manager status
let apt_state = std::path::Path::new("/var/lib/apt");
if apt_state.exists() {
// Check if APT cache is up to date
if let Ok(output) = std::process::Command::new("apt")
.arg("list")
.arg("--upgradable")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = output_str.lines().collect();
let upgradeable_count = if lines.len() > 1 { lines.len() - 1 } else { 0 };
if upgradeable_count > 0 {
println!(" APT Updates: ⚠ {} packages available for upgrade", upgradeable_count);
} else {
println!(" APT Updates: ✓ System is up to date");
}
} else {
println!(" APT Updates: ? Unable to check for updates");
}
}
}
// Check systemd service status for apt-ostreed
if let Ok(output) = std::process::Command::new("systemctl")
.arg("is-active")
.arg("apt-ostreed")
.output() {
if output.status.success() {
let status = String::from_utf8_lossy(&output.stdout).trim().to_string();
if status == "active" {
println!(" apt-ostreed: ✓ Service is running");
} else {
println!(" apt-ostreed: ⚠ Service status: {}", status);
}
} else {
println!(" apt-ostreed: ⚠ Service is not running");
}
} else {
println!(" apt-ostreed: ? Unable to check service status");
}
// Check for running containers or virtualization
if std::path::Path::new("/proc/vz").exists() {
println!(" Virtualization: OpenVZ detected");
} else if std::path::Path::new("/proc/xen").exists() {
println!(" Virtualization: Xen detected");
} else if std::path::Path::new("/proc/cpuinfo").exists() {
if let Ok(cpuinfo) = std::fs::read_to_string("/proc/cpuinfo") {
if cpuinfo.contains("hypervisor") {
println!(" Virtualization: Hypervisor detected");
} else {
println!(" Virtualization: Bare metal system");
}
}
}
// Check system uptime
if let Ok(uptime) = std::fs::read_to_string("/proc/uptime") {
if let Some(seconds_str) = uptime.split_whitespace().next() {
if let Ok(seconds) = seconds_str.parse::<f64>() {
let days = (seconds / 86400.0) as u64;
let hours = ((seconds % 86400.0) / 3600.0) as u64;
let minutes = ((seconds % 3600.0) / 60.0) as u64;
if days > 0 {
println!(" Uptime: {} days, {} hours, {} minutes", days, hours, minutes);
} else if hours > 0 {
println!(" Uptime: {} hours, {} minutes", hours, minutes);
} else {
println!(" Uptime: {} minutes", minutes);
}
}
}
}
// Check for security updates
if let Ok(output) = std::process::Command::new("apt")
.arg("list")
.arg("--upgradable")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
let security_updates = output_str.lines()
.filter(|line| line.contains("/security") || line.contains("-security"))
.count();
if security_updates > 0 {
println!(" Security: ⚠ {} security updates available", security_updates);
} else {
println!(" Security: ✓ No security updates pending");
}
}
}
Ok(())
}
}
/// Upgrade command - Perform a system upgrade

View file

@ -263,30 +263,34 @@ impl Command for MetricsCommand {
}
// Parse options
let mut opt_json = false;
let mut opt_prometheus = false;
let mut opt_system = false;
let mut opt_performance = false;
let mut opt_all = false;
for arg in args {
match arg.as_str() {
"--json" => opt_json = true,
"--prometheus" => opt_prometheus = true,
"--system" => opt_system = true,
"--performance" => opt_performance = true,
"--all" => opt_all = true,
_ => {}
}
}
// Default to showing all metrics if no specific option is selected
if !opt_system && !opt_performance && !opt_all {
opt_all = true;
}
println!("📊 System Metrics");
println!("=================");
if opt_json {
println!("Output format: JSON");
} else if opt_prometheus {
println!("Output format: Prometheus");
} else {
println!("Output format: Text");
if opt_system || opt_all {
self.display_system_metrics()?;
}
println!("Status: Placeholder implementation");
println!("Next: Implement real metrics logic");
if opt_performance || opt_all {
self.display_performance_metrics()?;
}
Ok(())
}
@ -305,13 +309,305 @@ impl Command for MetricsCommand {
println!("Usage: apt-ostree metrics [OPTIONS]");
println!();
println!("Options:");
println!(" --json Output metrics in JSON format");
println!(" --prometheus Output metrics in Prometheus format");
println!(" --system Show system metrics");
println!(" --performance Show performance metrics");
println!(" --all Show all metrics");
println!(" --help, -h Show this help message");
println!();
println!("Examples:");
println!(" apt-ostree metrics # Display metrics in text format");
println!(" apt-ostree metrics --json # Export metrics as JSON");
println!(" apt-ostree metrics --prometheus # Export metrics in Prometheus format");
println!(" apt-ostree metrics # Display all metrics");
println!(" apt-ostree metrics --system # Show only system metrics");
println!(" apt-ostree metrics --performance # Show only performance metrics");
}
}
impl MetricsCommand {
/// Display system metrics
fn display_system_metrics(&self) -> AptOstreeResult<()> {
println!();
println!("🖥️ System Metrics");
println!("------------------");
// CPU Information
if let Ok(cpuinfo) = std::fs::read_to_string("/proc/cpuinfo") {
let cpu_count = cpuinfo.lines().filter(|line| line.starts_with("processor")).count();
let model_name = cpuinfo.lines()
.find(|line| line.starts_with("model name"))
.and_then(|line| line.split_once(':').map(|(_, name)| name.trim()))
.unwrap_or("Unknown");
println!("CPU:");
println!(" Count: {} cores", cpu_count);
println!(" Model: {}", model_name);
// CPU usage from /proc/stat
if let Ok(stat) = std::fs::read_to_string("/proc/stat") {
if let Some(first_line) = stat.lines().next() {
let parts: Vec<&str> = first_line.split_whitespace().collect();
if parts.len() >= 5 {
let user = parts[1].parse::<u64>().unwrap_or(0);
let nice = parts[2].parse::<u64>().unwrap_or(0);
let system = parts[3].parse::<u64>().unwrap_or(0);
let idle = parts[4].parse::<u64>().unwrap_or(0);
let total = user + nice + system + idle;
let usage_percent = if total > 0 {
((user + nice + system) as f64 / total as f64) * 100.0
} else {
0.0
};
println!(" Usage: {:.1}%", usage_percent);
}
}
}
}
// Memory Information
if let Ok(meminfo) = std::fs::read_to_string("/proc/meminfo") {
let mut total_mem = 0;
let mut available_mem = 0;
let mut cached_mem = 0;
let mut buffers_mem = 0;
for line in meminfo.lines() {
match line.split_once(':') {
Some(("MemTotal", value)) => {
total_mem = value.trim().split_whitespace().next()
.and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
}
Some(("MemAvailable", value)) => {
available_mem = value.trim().split_whitespace().next()
.and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
}
Some(("Cached", value)) => {
cached_mem = value.trim().split_whitespace().next()
.and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
}
Some(("Buffers", value)) => {
buffers_mem = value.trim().split_whitespace().next()
.and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
}
_ => {}
}
}
if total_mem > 0 {
let used_mem = total_mem - available_mem;
let mem_usage_percent = (used_mem as f64 / total_mem as f64) * 100.0;
println!("Memory:");
println!(" Total: {} GB", total_mem / 1024 / 1024);
println!(" Used: {} GB ({:.1}%)", used_mem / 1024 / 1024, mem_usage_percent);
println!(" Available: {} GB", available_mem / 1024 / 1024);
println!(" Cached: {} GB", cached_mem / 1024 / 1024);
println!(" Buffers: {} GB", buffers_mem / 1024 / 1024);
}
}
// Disk Information
if let Ok(output) = std::process::Command::new("df")
.arg("-h")
.arg("/")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = output_str.lines().collect();
if lines.len() >= 2 {
let parts: Vec<&str> = lines[1].split_whitespace().collect();
if parts.len() >= 5 {
println!("Disk:");
println!(" Filesystem: {}", parts[0]);
println!(" Size: {}", parts[1]);
println!(" Used: {} ({})", parts[2], parts[4]);
println!(" Available: {}", parts[3]);
}
}
}
}
// Network Information
if let Ok(output) = std::process::Command::new("ip")
.arg("route")
.arg("show")
.arg("default")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
if let Some(default_route) = output_str.lines().next() {
if let Some(gateway) = default_route.split_whitespace().nth(2) {
println!("Network:");
println!(" Default Gateway: {}", gateway);
}
}
}
}
// System Uptime
if let Ok(uptime) = std::fs::read_to_string("/proc/uptime") {
if let Some(seconds_str) = uptime.split_whitespace().next() {
if let Ok(seconds) = seconds_str.parse::<f64>() {
let days = (seconds / 86400.0) as u64;
let hours = ((seconds % 86400.0) / 3600.0) as u64;
let minutes = ((seconds % 3600.0) / 60.0) as u64;
println!("Uptime:");
if days > 0 {
println!(" {} days, {} hours, {} minutes", days, hours, minutes);
} else if hours > 0 {
println!(" {} hours, {} minutes", hours, minutes);
} else {
println!(" {} minutes", minutes);
}
}
}
}
Ok(())
}
/// Display performance metrics
fn display_performance_metrics(&self) -> AptOstreeResult<()> {
println!();
println!("⚡ Performance Metrics");
println!("----------------------");
// Load Average
if let Ok(loadavg) = std::fs::read_to_string("/proc/loadavg") {
let parts: Vec<&str> = loadavg.split_whitespace().collect();
if parts.len() >= 3 {
println!("Load Average:");
println!(" 1 minute: {}", parts[0]);
println!(" 5 minutes: {}", parts[1]);
println!(" 15 minutes: {}", parts[2]);
// Get CPU count for load average interpretation
if let Ok(cpuinfo) = std::fs::read_to_string("/proc/cpuinfo") {
let cpu_count = cpuinfo.lines().filter(|line| line.starts_with("processor")).count();
if cpu_count > 0 {
let load_1min: f64 = parts[0].parse().unwrap_or(0.0);
let load_per_cpu = load_1min / cpu_count as f64;
println!(" Load per CPU: {:.2}", load_per_cpu);
if load_per_cpu > 1.0 {
println!(" ⚠ High load detected");
} else if load_per_cpu > 0.7 {
println!(" ⚠ Moderate load detected");
} else {
println!(" ✓ Normal load");
}
}
}
}
}
// Process Statistics
if let Ok(output) = std::process::Command::new("ps")
.arg("aux")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
let lines: Vec<&str> = output_str.lines().collect();
let process_count = lines.len() - 1; // Subtract header
println!("Processes:");
println!(" Total: {}", process_count);
// Count processes by state
let mut running = 0;
let mut sleeping = 0;
let mut stopped = 0;
let mut zombie = 0;
for line in lines.iter().skip(1) {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 8 {
match parts[7] {
"R" => running += 1,
"S" | "D" => sleeping += 1,
"T" => stopped += 1,
"Z" => zombie += 1,
_ => {}
}
}
}
println!(" Running: {}", running);
println!(" Sleeping: {}", sleeping);
println!(" Stopped: {}", stopped);
println!(" Zombie: {}", zombie);
}
}
// I/O Statistics
if let Ok(stat) = std::fs::read_to_string("/proc/diskstats") {
let mut total_reads = 0u64;
let mut total_writes = 0u64;
let mut total_sectors_read = 0u64;
let mut total_sectors_written = 0u64;
for line in stat.lines() {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 14 {
if let (Ok(reads), Ok(writes), Ok(sectors_read), Ok(sectors_written)) = (
parts[3].parse::<u64>(),
parts[7].parse::<u64>(),
parts[5].parse::<u64>(),
parts[9].parse::<u64>()
) {
total_reads += reads;
total_writes += writes;
total_sectors_read += sectors_read;
total_sectors_written += sectors_written;
}
}
}
println!("I/O Statistics:");
println!(" Total reads: {}", total_reads);
println!(" Total writes: {}", total_writes);
println!(" Data read: {} MB", total_sectors_read * 512 / 1024 / 1024);
println!(" Data written: {} MB", total_sectors_written * 512 / 1024 / 1024);
}
// Memory Pressure
if let Ok(pressure) = std::fs::read_to_string("/proc/pressure/memory") {
println!("Memory Pressure:");
for line in pressure.lines() {
if line.starts_with("some") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 3 {
let avg10 = parts[1].trim_end_matches(',');
let avg60 = parts[2].trim_end_matches(',');
let avg300 = parts[3];
println!(" Some pressure - 10s: {}, 60s: {}, 300s: {}", avg10, avg60, avg300);
}
}
}
}
// Systemd Service Status
if let Ok(output) = std::process::Command::new("systemctl")
.arg("--failed")
.output() {
if output.status.success() {
let output_str = String::from_utf8_lossy(&output.stdout);
let failed_services: Vec<&str> = output_str.lines()
.filter(|line| line.contains(".service"))
.collect();
if !failed_services.is_empty() {
println!("Failed Services:");
for service in failed_services.iter().take(5) {
println!(" - {}", service);
}
if failed_services.len() > 5 {
println!(" ... and {} more", failed_services.len() - 5);
}
} else {
println!("Failed Services: None");
}
}
}
Ok(())
}
}