mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
Feat/stats (#2761)
* Feat: Add the memory for the stats. * Chore: Add %
This commit is contained in:
@@ -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()),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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")]
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user