Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/stats #2761

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/startos/src/context/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub struct RpcContextSeed {
pub hardware: Hardware,
pub start_time: Instant,
pub crons: SyncMutex<BTreeMap<Guid, NonDetachingJoinHandle<()>>>,
#[cfg(feature = "dev")]
// #[cfg(feature = "dev")]
pub dev: Dev,
}

Expand Down Expand Up @@ -278,7 +278,7 @@ impl RpcContext {
hardware: Hardware { devices, ram },
start_time: Instant::now(),
crons,
#[cfg(feature = "dev")]
// #[cfg(feature = "dev")]
dev: Dev {
lxc: Mutex::new(BTreeMap::new()),
},
Expand Down
57 changes: 56 additions & 1 deletion core/startos/src/lxc/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ use rpc_toolkit::{
use serde::{Deserialize, Serialize};
use ts_rs::TS;

use crate::context::{CliContext, RpcContext};
use crate::lxc::{ContainerId, LxcConfig};
use crate::prelude::*;
use crate::rpc_continuations::Guid;
use crate::{
context::{CliContext, RpcContext},
service::ServiceStats,
};

pub fn lxc<C: Context>() -> ParentHandler<C> {
ParentHandler::new()
Expand All @@ -36,6 +39,42 @@ pub fn lxc<C: Context>() -> ParentHandler<C> {
.with_about("List lxc containers")
.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(
"remove",
from_fn_async(remove)
Expand Down Expand Up @@ -63,6 +102,22 @@ pub async fn list(ctx: RpcContext) -> Result<Vec<ContainerId>, Error> {
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)]
pub struct RemoveParams {
#[ts(type = "string")]
Expand Down
28 changes: 26 additions & 2 deletions core/startos/src/lxc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::BTreeSet;
use std::net::Ipv4Addr;
use std::path::Path;
use std::sync::{Arc, Weak};
use std::time::Duration;
use std::{collections::BTreeSet, ffi::OsString};

use clap::builder::ValueParserFactory;
use futures::{AsyncWriteExt, StreamExt};
Expand Down Expand Up @@ -32,7 +32,7 @@ use crate::util::io::open_file;
use crate::util::rpc_client::UnixRpcClient;
use crate::util::{new_guid, Invoke};

#[cfg(feature = "dev")]
// #[cfg(feature = "dev")]
pub mod dev;

const LXC_CONTAINER_DIR: &str = "/var/lib/lxc";
Expand Down Expand Up @@ -287,6 +287,30 @@ impl LxcContainer {
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)]
pub async fn exit(mut self) -> Result<(), Error> {
Command::new("lxc-stop")
Expand Down
50 changes: 50 additions & 0 deletions core/startos/src/service/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ pub enum LoadDisposition {

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>);
impl ServiceRef {
pub fn weak(&self) -> Weak<Service> {
Expand Down Expand Up @@ -553,6 +579,30 @@ impl Service {
.clone();
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)]
Expand Down