From da450a0077af531fe16688f5dc12da99d2034666 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Sun, 5 May 2024 16:18:25 +0200 Subject: [PATCH] Implement JSON --- Cargo.lock | 19 +++++++++++++++++++ Cargo.toml | 2 ++ README.md | 31 ++++++++++++++++++++++++++----- src/common.rs | 16 ++++++++++++++-- src/main.rs | 18 ++++++++++++++++-- 5 files changed, 77 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d4ebd0..7444c79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,6 +299,8 @@ dependencies = [ "ipmi-rs", "log", "pretty_env_logger", + "serde", + "serde_json", ] [[package]] @@ -501,6 +503,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "scopeguard" version = "1.2.0" @@ -527,6 +535,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "smallvec" version = "1.13.2" diff --git a/Cargo.toml b/Cargo.toml index 6a8c7f5..78a9eda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,3 +42,5 @@ clap = { version = "4.3", features = [ "derive" ]} log = "0.4" binary-layout = "4.0.2" chrono = "0.4.38" +serde = { version = "1.0.200", default-features = false, features = ["derive"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } diff --git a/README.md b/README.md index dcd6c2c..60d99db 100644 --- a/README.md +++ b/README.md @@ -21,19 +21,40 @@ The original C code can be found here: [ipmitool 1.8.19](https://github.com/ipmi Special thanks to the library [ipmi-rs](https://github.com/datdenkikniet/ipmi-rs) that made this possible. +## Use + +```text +A tool to fetch the power reading with ipmi dcmi + +Usage: ipmitool-dcmi-power-reading [OPTIONS] + +Options: + -c, --connection-uri + The connection URI to use [default: file:///dev/ipmi0] + --timeout-ms + How many milliseconds to wait before timing out while waiting for a response [default: 2000] + --format + The format to output [default: text] [possible values: text, json] + -h, --help + Print help + -V, --version + Print version +``` + ## Example (text) ```text -Instantaneous power reading : 214 Watts +Instantaneous power reading : 212 Watts Minimum during sampling period : 2 Watts Maximum during sampling period : 468 Watts Average power reading over sample period : 184 Watts -IPMI timestamp : 2024-05-05 13:06:44 UTC +IPMI timestamp : 2024-05-05 14:17:17 UTC Sampling period : 1000 Milliseconds Power reading state is : activated ``` -## TODO +## Example (json) -- [ ] Document options on the README -- [ ] Add JSON support +```json +{"grp_id":220,"curr_pwr":209,"min_sample":2,"max_sample":468,"avg_pwr":184,"time_stamp":1714918638,"sample":1000,"state":64} +``` diff --git a/src/common.rs b/src/common.rs index e0da981..6595e16 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,4 @@ -use clap::{Args, Parser}; +use clap::{Args, Parser, ValueEnum}; use ipmi_rs::{ connection::{ rmcp::{Active, Rmcp}, @@ -18,11 +18,17 @@ pub enum IpmiConnectionEnum { } #[derive(Parser)] -pub struct CliOpts { +struct CliOpts { #[clap(flatten)] pub common: CommonOpts, } +#[derive(ValueEnum, Clone, Copy)] +pub enum OutputFormats { + Text, + Json, +} + #[derive(Args)] pub struct CommonOpts { /// The connection URI to use @@ -31,6 +37,9 @@ pub struct CommonOpts { /// How many milliseconds to wait before timing out while waiting for a response #[clap(default_value = "2000", long)] timeout_ms: u64, + /// The format to output + #[clap(default_value = "text", value_enum, long)] + format: OutputFormats, } fn error(val: T) -> std::io::Error @@ -41,6 +50,9 @@ where } impl CommonOpts { + pub fn get_format(&self) -> OutputFormats { + self.format + } pub fn get_connection(&self) -> std::io::Result { let timeout = Duration::from_millis(self.timeout_ms); diff --git a/src/main.rs b/src/main.rs index 58da98e..01ed8f4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,17 @@ use std::io::Error; use binary_layout::binary_layout; use chrono::{TimeZone, Utc}; use clap::Parser; -use common::{CommonOpts, IpmiConnectionEnum}; +use common::{CommonOpts, IpmiConnectionEnum, OutputFormats}; use ipmi_rs::connection::{IpmiConnection, LogicalUnit, Message, NetFn, Request}; +use serde::{Deserialize, Serialize}; mod common; #[derive(Parser)] +#[clap(name = "ipmitool-dcmi-power-reading")] +#[clap(about = "A tool to fetch the power reading with ipmi dcmi")] +#[clap(author = "William Desportes ")] +#[clap(version = "1.0.0")] struct Command { #[clap(flatten)] common: CommonOpts, @@ -34,7 +39,9 @@ fn get_message() -> std::io::Result { )) } +#[derive(Serialize, Deserialize)] pub struct PowerConsumption { + #[allow(dead_code)] grp_id: u8, /* first byte: Group Extension ID */ curr_pwr: u16, min_sample: u16, @@ -134,6 +141,10 @@ pub fn ipmi_dcmi_pwr_format_text(pwr: PowerConsumption) { println!(""); } +pub fn ipmi_dcmi_pwr_format_json(pwr: PowerConsumption) { + print!("{}", serde_json::to_string(&pwr).unwrap()); +} + fn main() -> std::io::Result<()> { pretty_env_logger::formatted_builder() .parse_filters(&std::env::var("RUST_LOG").unwrap_or("info".to_string())) @@ -142,7 +153,10 @@ fn main() -> std::io::Result<()> { let command = Command::parse(); let ipmi = command.common.get_connection()?; match ipmi_dcmi_pwr_rd(ipmi) { - Ok(data) => Ok(ipmi_dcmi_pwr_format_text(data)), + Ok(data) => match command.common.get_format() { + OutputFormats::Json => Ok(ipmi_dcmi_pwr_format_json(data)), + OutputFormats::Text => Ok(ipmi_dcmi_pwr_format_text(data)), + }, Err(err) => Err(err), } }