From 311b771f7321ca445423ff16ba95a8e67116198c Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Fri, 23 Aug 2024 12:13:42 -0700 Subject: [PATCH] feature(ctl): add --format option to host status and improve cpu topology format --- crates/ctl/src/cli/device/list.rs | 2 +- crates/ctl/src/cli/host/cpu_topology.rs | 76 +++++++++++++++++++------ crates/ctl/src/cli/host/identify.rs | 25 -------- crates/ctl/src/cli/host/mod.rs | 4 +- crates/ctl/src/cli/host/status.rs | 60 +++++++++++++++++++ crates/ctl/src/cli/zone/list.rs | 2 +- 6 files changed, 124 insertions(+), 45 deletions(-) delete mode 100644 crates/ctl/src/cli/host/identify.rs create mode 100644 crates/ctl/src/cli/host/status.rs diff --git a/crates/ctl/src/cli/device/list.rs b/crates/ctl/src/cli/device/list.rs index 1280593..ea225e7 100644 --- a/crates/ctl/src/cli/device/list.rs +++ b/crates/ctl/src/cli/device/list.rs @@ -23,7 +23,7 @@ enum DeviceListFormat { } #[derive(Parser)] -#[command(about = "List the devices on the isolation engine")] +#[command(about = "List device information")] pub struct DeviceListCommand { #[arg(short, long, default_value = "table", help = "Output format")] format: DeviceListFormat, diff --git a/crates/ctl/src/cli/host/cpu_topology.rs b/crates/ctl/src/cli/host/cpu_topology.rs index cf7911b..07be264 100644 --- a/crates/ctl/src/cli/host/cpu_topology.rs +++ b/crates/ctl/src/cli/host/cpu_topology.rs @@ -5,7 +5,9 @@ use comfy_table::{Cell, Table}; use krata::v1::control::{ control_service_client::ControlServiceClient, GetHostCpuTopologyRequest, HostCpuTopologyClass, }; +use serde_json::Value; +use crate::format::{kv2line, proto2dynamic, proto2kv}; use tonic::{transport::Channel, Request}; fn class_to_str(input: HostCpuTopologyClass) -> String { @@ -19,6 +21,11 @@ fn class_to_str(input: HostCpuTopologyClass) -> String { #[derive(ValueEnum, Clone, Debug, PartialEq, Eq)] enum HostCpuTopologyFormat { Table, + Json, + JsonPretty, + Jsonl, + Yaml, + KeyValue, } #[derive(Parser)] @@ -35,24 +42,61 @@ impl HostCpuTopologyCommand { .await? .into_inner(); - let mut table = Table::new(); - table.load_preset(UTF8_FULL_CONDENSED); - table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic); - table.set_header(vec!["id", "node", "socket", "core", "thread", "class"]); + match self.format { + HostCpuTopologyFormat::Table => { + let mut table = Table::new(); + table.load_preset(UTF8_FULL_CONDENSED); + table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic); + table.set_header(vec!["id", "node", "socket", "core", "thread", "class"]); - for (i, cpu) in response.cpus.iter().enumerate() { - table.add_row(vec![ - Cell::new(i), - Cell::new(cpu.node), - Cell::new(cpu.socket), - Cell::new(cpu.core), - Cell::new(cpu.thread), - Cell::new(class_to_str(cpu.class())), - ]); - } + for (i, cpu) in response.cpus.iter().enumerate() { + table.add_row(vec![ + Cell::new(i), + Cell::new(cpu.node), + Cell::new(cpu.socket), + Cell::new(cpu.core), + Cell::new(cpu.thread), + Cell::new(class_to_str(cpu.class())), + ]); + } - if !table.is_empty() { - println!("{}", table); + if !table.is_empty() { + println!("{}", table); + } + } + + HostCpuTopologyFormat::Json + | HostCpuTopologyFormat::JsonPretty + | HostCpuTopologyFormat::Yaml => { + let mut values = Vec::new(); + for cpu in response.cpus { + let message = proto2dynamic(cpu)?; + values.push(serde_json::to_value(message)?); + } + let value = Value::Array(values); + let encoded = if self.format == HostCpuTopologyFormat::JsonPretty { + serde_json::to_string_pretty(&value)? + } else if self.format == HostCpuTopologyFormat::Yaml { + serde_yaml::to_string(&value)? + } else { + serde_json::to_string(&value)? + }; + println!("{}", encoded.trim()); + } + + HostCpuTopologyFormat::Jsonl => { + for cpu in response.cpus { + let message = proto2dynamic(cpu)?; + println!("{}", serde_json::to_string(&message)?); + } + } + + HostCpuTopologyFormat::KeyValue => { + for cpu in response.cpus { + let kvs = proto2kv(cpu)?; + println!("{}", kv2line(kvs),); + } + } } Ok(()) diff --git a/crates/ctl/src/cli/host/identify.rs b/crates/ctl/src/cli/host/identify.rs deleted file mode 100644 index aae48d8..0000000 --- a/crates/ctl/src/cli/host/identify.rs +++ /dev/null @@ -1,25 +0,0 @@ -use anyhow::Result; -use clap::Parser; -use krata::v1::control::{control_service_client::ControlServiceClient, HostStatusRequest}; - -use tonic::{transport::Channel, Request}; - -#[derive(Parser)] -#[command(about = "Get information about the host")] -pub struct HostStatusCommand {} - -impl HostStatusCommand { - pub async fn run(self, mut client: ControlServiceClient) -> Result<()> { - let response = client - .host_status(Request::new(HostStatusRequest {})) - .await? - .into_inner(); - println!("Host UUID: {}", response.host_uuid); - println!("Host Domain: {}", response.host_domid); - println!("Krata Version: {}", response.krata_version); - println!("Host IPv4: {}", response.host_ipv4); - println!("Host IPv6: {}", response.host_ipv6); - println!("Host Ethernet Address: {}", response.host_mac); - Ok(()) - } -} diff --git a/crates/ctl/src/cli/host/mod.rs b/crates/ctl/src/cli/host/mod.rs index 3c8143c..399b1dc 100644 --- a/crates/ctl/src/cli/host/mod.rs +++ b/crates/ctl/src/cli/host/mod.rs @@ -7,13 +7,13 @@ use krata::v1::control::control_service_client::ControlServiceClient; use crate::cli::host::cpu_topology::HostCpuTopologyCommand; use crate::cli::host::hv_console::HostHvConsoleCommand; -use crate::cli::host::identify::HostStatusCommand; use crate::cli::host::idm_snoop::HostIdmSnoopCommand; +use crate::cli::host::status::HostStatusCommand; pub mod cpu_topology; pub mod hv_console; -pub mod identify; pub mod idm_snoop; +pub mod status; #[derive(Parser)] #[command(about = "Manage the host of the isolation engine")] diff --git a/crates/ctl/src/cli/host/status.rs b/crates/ctl/src/cli/host/status.rs new file mode 100644 index 0000000..a14f5ab --- /dev/null +++ b/crates/ctl/src/cli/host/status.rs @@ -0,0 +1,60 @@ +use anyhow::Result; +use clap::{Parser, ValueEnum}; +use krata::v1::control::{control_service_client::ControlServiceClient, HostStatusRequest}; + +use crate::format::{kv2line, proto2dynamic, proto2kv}; +use tonic::{transport::Channel, Request}; + +#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)] +enum HostStatusFormat { + Simple, + Json, + JsonPretty, + Yaml, + KeyValue, +} + +#[derive(Parser)] +#[command(about = "Get information about the host")] +pub struct HostStatusCommand { + #[arg(short, long, default_value = "simple", help = "Output format")] + format: HostStatusFormat, +} + +impl HostStatusCommand { + pub async fn run(self, mut client: ControlServiceClient) -> Result<()> { + let response = client + .host_status(Request::new(HostStatusRequest {})) + .await? + .into_inner(); + match self.format { + HostStatusFormat::Simple => { + println!("Host UUID: {}", response.host_uuid); + println!("Host Domain: {}", response.host_domid); + println!("Krata Version: {}", response.krata_version); + println!("Host IPv4: {}", response.host_ipv4); + println!("Host IPv6: {}", response.host_ipv6); + println!("Host Ethernet Address: {}", response.host_mac); + } + + HostStatusFormat::Json | HostStatusFormat::JsonPretty | HostStatusFormat::Yaml => { + let message = proto2dynamic(response)?; + let value = serde_json::to_value(message)?; + let encoded = if self.format == HostStatusFormat::JsonPretty { + serde_json::to_string_pretty(&value)? + } else if self.format == HostStatusFormat::Yaml { + serde_yaml::to_string(&value)? + } else { + serde_json::to_string(&value)? + }; + println!("{}", encoded.trim()); + } + + HostStatusFormat::KeyValue => { + let kvs = proto2kv(response)?; + println!("{}", kv2line(kvs),); + } + } + Ok(()) + } +} diff --git a/crates/ctl/src/cli/zone/list.rs b/crates/ctl/src/cli/zone/list.rs index 4129603..40b91e3 100644 --- a/crates/ctl/src/cli/zone/list.rs +++ b/crates/ctl/src/cli/zone/list.rs @@ -29,7 +29,7 @@ enum ZoneListFormat { } #[derive(Parser)] -#[command(about = "List the zones on the isolation engine")] +#[command(about = "List zone information")] pub struct ZoneListCommand { #[arg(short, long, default_value = "table", help = "Output format")] format: ZoneListFormat,