Feat/stats (#2761)

* Feat: Add the memory for the stats.

* Chore: Add %
This commit is contained in:
Jade
2024-10-22 13:49:01 -06:00
committed by GitHub
parent 28e39c57bd
commit 7694b68e06
4 changed files with 134 additions and 5 deletions

View File

@@ -70,7 +70,7 @@ pub struct RpcContextSeed {
pub hardware: Hardware, pub hardware: Hardware,
pub start_time: Instant, pub start_time: Instant,
pub crons: SyncMutex<BTreeMap<Guid, NonDetachingJoinHandle<()>>>, pub crons: SyncMutex<BTreeMap<Guid, NonDetachingJoinHandle<()>>>,
#[cfg(feature = "dev")] // #[cfg(feature = "dev")]
pub dev: Dev, pub dev: Dev,
} }
@@ -278,7 +278,7 @@ impl RpcContext {
hardware: Hardware { devices, ram }, hardware: Hardware { devices, ram },
start_time: Instant::now(), start_time: Instant::now(),
crons, crons,
#[cfg(feature = "dev")] // #[cfg(feature = "dev")]
dev: Dev { dev: Dev {
lxc: Mutex::new(BTreeMap::new()), lxc: Mutex::new(BTreeMap::new()),
}, },

View File

@@ -8,10 +8,13 @@ use rpc_toolkit::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ts_rs::TS; use ts_rs::TS;
use crate::context::{CliContext, RpcContext};
use crate::lxc::{ContainerId, LxcConfig}; use crate::lxc::{ContainerId, LxcConfig};
use crate::prelude::*; use crate::prelude::*;
use crate::rpc_continuations::Guid; use crate::rpc_continuations::Guid;
use crate::{
context::{CliContext, RpcContext},
service::ServiceStats,
};
pub fn lxc<C: Context>() -> ParentHandler<C> { pub fn lxc<C: Context>() -> ParentHandler<C> {
ParentHandler::new() ParentHandler::new()
@@ -36,6 +39,42 @@ pub fn lxc<C: Context>() -> ParentHandler<C> {
.with_about("List lxc containers") .with_about("List lxc containers")
.with_call_remote::<CliContext>(), .with_call_remote::<CliContext>(),
) )
.subcommand(
"stats",
from_fn_async(stats)
.with_custom_display_fn(|_, res| {
use prettytable::*;
let mut table = table!([
"Container ID",
"Name",
"Memory Usage",
"Memory Limit",
"Memory %"
]);
for ServiceStats {
container_id,
package_id,
memory_usage,
memory_limit,
} in res
{
table.add_row(row![
&*container_id,
&*package_id,
memory_usage,
memory_limit,
format!(
"{:.2}",
memory_usage.0 as f64 / memory_limit.0 as f64 * 100.0
)
]);
}
table.printstd();
Ok(())
})
.with_about("List information related to the lxc containers i.e. CPU, Memory, Disk")
.with_call_remote::<CliContext>(),
)
.subcommand( .subcommand(
"remove", "remove",
from_fn_async(remove) from_fn_async(remove)
@@ -63,6 +102,22 @@ pub async fn list(ctx: RpcContext) -> Result<Vec<ContainerId>, Error> {
Ok(ctx.dev.lxc.lock().await.keys().cloned().collect()) Ok(ctx.dev.lxc.lock().await.keys().cloned().collect())
} }
pub async fn stats(ctx: RpcContext) -> Result<Vec<ServiceStats>, Error> {
let ids = ctx.db.peek().await.as_public().as_package_data().keys()?;
let guids: Vec<_> = ctx.dev.lxc.lock().await.keys().cloned().collect();
let mut stats = Vec::with_capacity(guids.len());
for id in ids {
let service: tokio::sync::OwnedRwLockReadGuard<Option<crate::service::ServiceRef>> =
ctx.services.get(&id).await;
let service_ref = service.as_ref().or_not_found(&id)?;
stats.push(service_ref.stats().await?);
}
Ok(stats)
}
#[derive(Deserialize, Serialize, Parser, TS)] #[derive(Deserialize, Serialize, Parser, TS)]
pub struct RemoveParams { pub struct RemoveParams {
#[ts(type = "string")] #[ts(type = "string")]

View File

@@ -1,8 +1,8 @@
use std::collections::BTreeSet;
use std::net::Ipv4Addr; use std::net::Ipv4Addr;
use std::path::Path; use std::path::Path;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::time::Duration; use std::time::Duration;
use std::{collections::BTreeSet, ffi::OsString};
use clap::builder::ValueParserFactory; use clap::builder::ValueParserFactory;
use futures::{AsyncWriteExt, StreamExt}; use futures::{AsyncWriteExt, StreamExt};
@@ -32,7 +32,7 @@ use crate::util::io::open_file;
use crate::util::rpc_client::UnixRpcClient; use crate::util::rpc_client::UnixRpcClient;
use crate::util::{new_guid, Invoke}; use crate::util::{new_guid, Invoke};
#[cfg(feature = "dev")] // #[cfg(feature = "dev")]
pub mod dev; pub mod dev;
const LXC_CONTAINER_DIR: &str = "/var/lib/lxc"; const LXC_CONTAINER_DIR: &str = "/var/lib/lxc";
@@ -287,6 +287,30 @@ impl LxcContainer {
self.rpc_bind.path() self.rpc_bind.path()
} }
pub async fn command(&self, commands: &[&str]) -> Result<String, Error> {
let mut cmd = Command::new("lxc-attach");
cmd.kill_on_drop(true);
let output = cmd
.arg(&**self.guid)
.arg("--")
.args(commands)
.output()
.await?;
if !output.status.success() {
return Err(Error::new(
eyre!(
"Command failed with exit code: {:?} \n Message: {:?}",
output.status.code(),
String::from_utf8(output.stderr)
),
ErrorKind::Docker,
));
}
Ok(String::from_utf8(output.stdout)?)
}
#[instrument(skip_all)] #[instrument(skip_all)]
pub async fn exit(mut self) -> Result<(), Error> { pub async fn exit(mut self) -> Result<(), Error> {
Command::new("lxc-stop") Command::new("lxc-stop")

View File

@@ -83,6 +83,32 @@ pub enum LoadDisposition {
struct RootCommand(pub String); struct RootCommand(pub String);
#[derive(Clone, Debug, Serialize, Deserialize, Default, TS)]
pub struct MiB(pub u64);
impl MiB {
fn new(value: u64) -> Self {
Self(value / 1024 / 1024)
}
fn from_MiB(value: u64) -> Self {
Self(value)
}
}
impl std::fmt::Display for MiB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} MiB", self.0)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Default, TS)]
pub struct ServiceStats {
pub container_id: Arc<ContainerId>,
pub package_id: PackageId,
pub memory_usage: MiB,
pub memory_limit: MiB,
}
pub struct ServiceRef(Arc<Service>); pub struct ServiceRef(Arc<Service>);
impl ServiceRef { impl ServiceRef {
pub fn weak(&self) -> Weak<Service> { pub fn weak(&self) -> Weak<Service> {
@@ -553,6 +579,30 @@ impl Service {
.clone(); .clone();
Ok(container_id) Ok(container_id)
} }
#[instrument(skip_all)]
pub async fn stats(&self) -> Result<ServiceStats, Error> {
let container = &self.seed.persistent_container;
let lxc_container = container.lxc_container.get().or_not_found("container")?;
let (total, used) = lxc_container
.command(&["free", "-m"])
.await?
.split("\n")
.map(|x| x.split_whitespace().collect::<Vec<_>>())
.skip(1)
.filter_map(|x| {
Some((
x.get(1)?.parse::<u64>().ok()?,
x.get(2)?.parse::<u64>().ok()?,
))
})
.fold((0, 0), |acc, (total, used)| (acc.0 + total, acc.1 + used));
Ok(ServiceStats {
container_id: lxc_container.guid.clone(),
package_id: self.seed.id.clone(),
memory_limit: MiB::from_MiB(total),
memory_usage: MiB::from_MiB(used),
})
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]