From 21631fb01c38c516468c7c8ccbc8e9ad3b2492e0 Mon Sep 17 00:00:00 2001 From: leongross Date: Thu, 18 Jan 2024 14:21:18 +0100 Subject: [PATCH 1/4] init --- Cargo.toml | 1 + src/blocks.rs | 1 + src/blocks/virt.rs | 135 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/blocks/virt.rs diff --git a/Cargo.toml b/Cargo.toml index a235693d93..f6519b1003 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,6 +69,7 @@ unicode-segmentation = "1.10.1" wayrs-client = { version = "1.0", features = ["tokio"] } wayrs-protocols = { version = "0.13", features = ["wlr-foreign-toplevel-management-unstable-v1"] } zbus = { version = "3.14", default-features = false, features = ["tokio"] } +virt = "0.3.1" [dependencies.tokio] version = "1.12" diff --git a/src/blocks.rs b/src/blocks.rs index 518b00823d..673f8b8e8b 100644 --- a/src/blocks.rs +++ b/src/blocks.rs @@ -175,6 +175,7 @@ define_blocks!( tea_timer, toggle, uptime, + virt, vpn, watson, weather, diff --git a/src/blocks/virt.rs b/src/blocks/virt.rs new file mode 100644 index 0000000000..2a114155f5 --- /dev/null +++ b/src/blocks/virt.rs @@ -0,0 +1,135 @@ +//! Local virtual machine state. +//! +//! # Configuration +//! +//! Key | Values | Default +//! ----|--------|-------- +//! uri | URI of the hypervisor | qemu:///system +//! `interval` | Update interval, in seconds. | `5` +//! `format` | A string to customise the output of this block. See below for available placeholders. | `" $icon $running.eng(w:1) "` +//! `uru` | The path to the virtualization domain. +//! +//! Key | Value | Type | Unit +//! ----------|----------------------------------------|--------|----- +//! `icon` | A static icon | Icon | - +//! `total` | Total containers on the host | Number | - +//! `running` | Virtual machines running on the host | Number | - +//! `stopped` | Virtual machines stopped on the host | Number | - +//! `paused` | Virtual machines paused on the host | Number | - +//! `total` | Total Virtual machines on the host | Number | - +//! `memory | Total memory used by the virtual machines | Number | +//! `cpu` | Total percentage cpu used by the virtual machines | Number | +//! +//! # Example +//! +//! ```toml +//! [[block]] +//! block = "virt" +//! uri = "qemu:///system" +//! interval = 2 +//! format = " $icon $running/$total ($memory@$cpu) " +//! ``` +//! + +use super::prelude::*; +use virt::connect::Connect; +use virt::error::Error; +use virt::sys; + +#[derive(Deserialize, Debug, SmartDefault)] +#[serde(default)] +pub struct Config { + #[default("qemu:///system".into())] + pub uri: ShellString, + pub format: FormatConfig, + #[default(5.into())] + pub interval: Seconds, +} + +fn disconnect(mut conn: Connect) { + if let Err(e) = conn.close() { + panic!("Failed to disconnect from hypervisor: {}", e); + } + println!("Disconnected from hypervisor"); +} + + +pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { + let format = config.format.with_default("$icon $running/$total")?; + let mut widget = Widget::new().with_format(format.clone()); + let flags = sys::VIR_CONNECT_LIST_DOMAINS_ACTIVE | sys::VIR_CONNECT_LIST_DOMAINS_INACTIVE; + let uri: &str = "qemu:///system"; + + let mut timer = config.interval.timer(); + dbg!(&format); + + loop { + // 0. connect to hypervisor + println!("Connecting to hypervisor"); + let con = match Connect::open(uri) { + Ok(c) => c, + Err(e) => panic!("No connection to hypervisor: {}", e), + }; + + println!("Connected to hypervisor"); + match con.get_uri() { + Ok(u) => println!("Connected to hypervisor at '{}'", u), + Err(e) => { + disconnect(con); + panic!("Failed to get URI for hypervisor connection: {}", e); + } + }; + + // 1. get all the information + println!("Getting information about domains"); + if let Ok(virt_active_domains) = con.num_of_domains() { + if let Ok(virt_inactive_domains) = con.num_of_defined_domains() { + println!( + "There are {} active and {} inactive domains", + virt_active_domains, virt_inactive_domains + ); + + widget.set_values(map!( + "icon" => Value::icon("".to_string()), + // "total" => Value::number(virt_active_doms + virt_inactive_doms), + "running" => Value::number(virt_active_domains), + // "stopped" => Value::number(virt_inactive_doms), + // "paused" => Value::number(virt_inactive_domains), + "total" => Value::number(virt_active_domains + virt_inactive_domains), + )); + + dbg!(&widget); + api.set_widget(widget.clone())?; + } + } + + select! { + _ = sleep(config.interval.0) => (), + } + + // 2. disconnect + disconnect(con); + } +} + + +#[derive(Deserialize, Debug)] +struct LibvirtInfo { + #[serde(rename = "VMTotal")] + total: i64, + #[serde(rename = "VMActive")] + active: i64, + #[serde(rename = "VMInactive")] + inactive: i64, +} + +impl LibvirtInfo { + // Create a new `LibvirtInfo` struct. + fn new(con: &Connect) -> Self { + LibvirtInfo { + total: 0, + active: 0, + inactive: 0, + } + } +} From 0538609f84d21c56fe33c425095227f286fa9a63 Mon Sep 17 00:00:00 2001 From: leongross Date: Thu, 18 Jan 2024 23:39:44 +0100 Subject: [PATCH 2/4] restrucure to use struct --- src/blocks/virt.rs | 167 +++++++++++++++++++++++++++------------------ 1 file changed, 101 insertions(+), 66 deletions(-) diff --git a/src/blocks/virt.rs b/src/blocks/virt.rs index 2a114155f5..0efb56d3d5 100644 --- a/src/blocks/virt.rs +++ b/src/blocks/virt.rs @@ -11,14 +11,13 @@ //! //! Key | Value | Type | Unit //! ----------|----------------------------------------|--------|----- -//! `icon` | A static icon | Icon | - -//! `total` | Total containers on the host | Number | - -//! `running` | Virtual machines running on the host | Number | - -//! `stopped` | Virtual machines stopped on the host | Number | - -//! `paused` | Virtual machines paused on the host | Number | - -//! `total` | Total Virtual machines on the host | Number | - -//! `memory | Total memory used by the virtual machines | Number | -//! `cpu` | Total percentage cpu used by the virtual machines | Number | +//! `active` | Virtual machines running on the host | Number | - +//! `inactive` | Virtual machines stopped on the host | Number | - +//! `total` | Virtual machines in total the host | Number | - +//! `memory_active | Total memory used by running virtual machines | Number | - +//! `memory_max` | Total memory used by virtual machines of any state | Number | - +//! `cpu_active` | Total cpu cores used by the virtual machines | Number | - +//! `cpu_inactive` | Total cpu used by stopped virtual machines | Number | - //! //! # Example //! @@ -27,7 +26,7 @@ //! block = "virt" //! uri = "qemu:///system" //! interval = 2 -//! format = " $icon $running/$total ($memory@$cpu) " +//! format = " $icon $active/$total ($memory_activey@$cpu_active) " //! ``` //! @@ -46,90 +45,126 @@ pub struct Config { pub interval: Seconds, } -fn disconnect(mut conn: Connect) { - if let Err(e) = conn.close() { - panic!("Failed to disconnect from hypervisor: {}", e); - } - println!("Disconnected from hypervisor"); -} - - pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { - let format = config.format.with_default("$icon $running/$total")?; + let format = config.format.with_default("$icon $active/$total")?; let mut widget = Widget::new().with_format(format.clone()); - let flags = sys::VIR_CONNECT_LIST_DOMAINS_ACTIVE | sys::VIR_CONNECT_LIST_DOMAINS_INACTIVE; + + let flags: sys::virConnectListAllDomainsFlags = sys::VIR_CONNECT_LIST_DOMAINS_ACTIVE | sys::VIR_CONNECT_LIST_DOMAINS_INACTIVE; let uri: &str = "qemu:///system"; - let mut timer = config.interval.timer(); - dbg!(&format); + dbg!(&widget); loop { - // 0. connect to hypervisor - println!("Connecting to hypervisor"); - let con = match Connect::open(uri) { + println!("Connecting to hypervisor '{}'", &uri); + let mut con = match Connect::open(uri) { Ok(c) => c, Err(e) => panic!("No connection to hypervisor: {}", e), }; - println!("Connected to hypervisor"); - match con.get_uri() { - Ok(u) => println!("Connected to hypervisor at '{}'", u), - Err(e) => { - disconnect(con); - panic!("Failed to get URI for hypervisor connection: {}", e); - } - }; + let info: LibvirtInfo = LibvirtInfo::new(&mut con, flags).unwrap(); - // 1. get all the information - println!("Getting information about domains"); - if let Ok(virt_active_domains) = con.num_of_domains() { - if let Ok(virt_inactive_domains) = con.num_of_defined_domains() { - println!( - "There are {} active and {} inactive domains", - virt_active_domains, virt_inactive_domains - ); - - widget.set_values(map!( - "icon" => Value::icon("".to_string()), - // "total" => Value::number(virt_active_doms + virt_inactive_doms), - "running" => Value::number(virt_active_domains), - // "stopped" => Value::number(virt_inactive_doms), - // "paused" => Value::number(virt_inactive_domains), - "total" => Value::number(virt_active_domains + virt_inactive_domains), - )); - - dbg!(&widget); - api.set_widget(widget.clone())?; - } - } + widget.set_values(map!( + "icon" => Value::icon("".to_string()), + // "total" => Value::number(virt_active_doms + virt_inactive_doms), + "running" => Value::number(info.active), + "stopped" => Value::number(info.inactive), + // "paused" => Value::number(virt_inactive_domains), + "total" => Value::number(info.total), + )); + + api.set_widget(widget.clone())?; + + println!("Disconnecting from hypervisor"); + disconnect(&mut con); select! { _ = sleep(config.interval.0) => (), } - // 2. disconnect - disconnect(con); } } #[derive(Deserialize, Debug)] struct LibvirtInfo { - #[serde(rename = "VMTotal")] - total: i64, #[serde(rename = "VMActive")] - active: i64, + active: u32, #[serde(rename = "VMInactive")] - inactive: i64, + inactive: u32, + #[serde(rename = "VMTotal")] + total: u32, + #[serde(rename = "MemoryActive")] + memory_active: u32, + #[serde(rename = "MemoryInactive")] + memory_max: u32, + #[serde(rename = "CPUActive")] + cpu_active: u32, + #[serde(rename = "CPUInactive")] + cpu_inactive: u32, +} + +fn disconnect(con: &mut Connect) { + if let Err(e) = con.close() { + panic!("Failed to disconnect from hypervisor: {}", e); + } + println!("Disconnected from hypervisor"); } impl LibvirtInfo { - // Create a new `LibvirtInfo` struct. - fn new(con: &Connect) -> Self { - LibvirtInfo { - total: 0, - active: 0, - inactive: 0, + pub fn new(con: &mut Connect, flags: sys::virConnectListAllDomainsFlags) -> Result { + println!("Connected to hypervisor"); + match con.get_uri() { + Ok(u) => println!("Connected to hypervisor at '{}'", u), + Err(e) => { + disconnect(con); + panic!("Failed to get URI for hypervisor connection: {}", e); + } + }; + + println!("Getting information about domains"); + if let Ok(virt_active_domains) = con.num_of_domains() { + if let Ok(virt_inactive_domains) = con.num_of_defined_domains() { + if let Ok(domains) = con.list_all_domains(flags) { + let mut info = LibvirtInfo { + active: virt_active_domains, + inactive: virt_inactive_domains, + total: virt_active_domains + virt_inactive_domains, + memory_active: 0, + memory_max: 0, + cpu_active: 0, + cpu_inactive: 0, + }; + + for domain in domains { + if let Ok(domain_info) = domain.get_info() { + info.memory_max += domain_info.max_mem as u32; + + if domain.is_active().unwrap_or(false) { + info.memory_active += domain_info.memory as u32; + info.cpu_active += domain_info.nr_virt_cpu as u32; + } else { + info.cpu_inactive += domain_info.nr_virt_cpu as u32; + } + } + } + + return Ok(info); + } + else { + disconnect(con); + return Err(Error::last_error()); + } + + } + else { + disconnect(con); + return Err(Error::last_error()); + } + } + else { + disconnect(con); + return Err(Error::last_error()); } } } + From 010c044b1241bc684630f911d53ce0f272e6f7cc Mon Sep 17 00:00:00 2001 From: leongross Date: Sun, 28 Jan 2024 19:15:54 +0100 Subject: [PATCH 3/4] fix cspell typos --- cspell.yaml | 1 + src/blocks/virt.rs | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/cspell.yaml b/cspell.yaml index 09d3eb952f..adf393ee15 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -178,3 +178,4 @@ words: - zbus - zvariant - zwlr + - virt diff --git a/src/blocks/virt.rs b/src/blocks/virt.rs index 0efb56d3d5..a5108cf5af 100644 --- a/src/blocks/virt.rs +++ b/src/blocks/virt.rs @@ -7,7 +7,7 @@ //! uri | URI of the hypervisor | qemu:///system //! `interval` | Update interval, in seconds. | `5` //! `format` | A string to customise the output of this block. See below for available placeholders. | `" $icon $running.eng(w:1) "` -//! `uru` | The path to the virtualization domain. +//! `uri` | The path to the virtualization domain. //! //! Key | Value | Type | Unit //! ----------|----------------------------------------|--------|----- @@ -26,7 +26,7 @@ //! block = "virt" //! uri = "qemu:///system" //! interval = 2 -//! format = " $icon $active/$total ($memory_activey@$cpu_active) " +//! format = " $icon $active/$total ($memory $active@$cpu_active) " //! ``` //! @@ -36,7 +36,7 @@ use virt::error::Error; use virt::sys; #[derive(Deserialize, Debug, SmartDefault)] -#[serde(default)] +#[serde(deny_unknown_fields, default)] pub struct Config { #[default("qemu:///system".into())] pub uri: ShellString, @@ -47,13 +47,10 @@ pub struct Config { pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { let format = config.format.with_default("$icon $active/$total")?; - let mut widget = Widget::new().with_format(format.clone()); let flags: sys::virConnectListAllDomainsFlags = sys::VIR_CONNECT_LIST_DOMAINS_ACTIVE | sys::VIR_CONNECT_LIST_DOMAINS_INACTIVE; let uri: &str = "qemu:///system"; - dbg!(&widget); - loop { println!("Connecting to hypervisor '{}'", &uri); let mut con = match Connect::open(uri) { @@ -61,10 +58,13 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { Err(e) => panic!("No connection to hypervisor: {}", e), }; - let info: LibvirtInfo = LibvirtInfo::new(&mut con, flags).unwrap(); + let info: LibvirtInfo = LibvirtInfo::new(&mut con, flags) + .await + .unwrap(); + let mut widget = Widget::new().with_format(format.clone()); widget.set_values(map!( - "icon" => Value::icon("".to_string()), + // "icon" => Value::icon("".to_string()), // "total" => Value::number(virt_active_doms + virt_inactive_doms), "running" => Value::number(info.active), "stopped" => Value::number(info.inactive), @@ -72,20 +72,20 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> { "total" => Value::number(info.total), )); - api.set_widget(widget.clone())?; - - println!("Disconnecting from hypervisor"); + api.set_widget(widget)?; disconnect(&mut con); select! { _ = sleep(config.interval.0) => (), + _ = api.wait_for_update_request() => (), } } } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, SmartDefault)] +#[serde(deny_unknown_fields, default)] struct LibvirtInfo { #[serde(rename = "VMActive")] active: u32, @@ -111,7 +111,7 @@ fn disconnect(con: &mut Connect) { } impl LibvirtInfo { - pub fn new(con: &mut Connect, flags: sys::virConnectListAllDomainsFlags) -> Result { + async fn new(con: &mut Connect, flags: sys::virConnectListAllDomainsFlags) -> Result { println!("Connected to hypervisor"); match con.get_uri() { Ok(u) => println!("Connected to hypervisor at '{}'", u), From eb1c24a1b12b81a99031055c9da8958134de6fd1 Mon Sep 17 00:00:00 2001 From: leongross Date: Sun, 28 Jan 2024 19:19:29 +0100 Subject: [PATCH 4/4] fix clippy --- src/blocks/virt.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blocks/virt.rs b/src/blocks/virt.rs index a5108cf5af..38e49c2f20 100644 --- a/src/blocks/virt.rs +++ b/src/blocks/virt.rs @@ -141,29 +141,29 @@ impl LibvirtInfo { if domain.is_active().unwrap_or(false) { info.memory_active += domain_info.memory as u32; - info.cpu_active += domain_info.nr_virt_cpu as u32; + info.cpu_active += domain_info.nr_virt_cpu; } else { - info.cpu_inactive += domain_info.nr_virt_cpu as u32; + info.cpu_inactive += domain_info.nr_virt_cpu; } } } - return Ok(info); + Ok(info) } else { disconnect(con); - return Err(Error::last_error()); + Err(Error::last_error()) } } else { disconnect(con); - return Err(Error::last_error()); + Err(Error::last_error()) } } else { disconnect(con); - return Err(Error::last_error()); + Err(Error::last_error()) } } }