mirror of
https://codeberg.org/icewind/palantir.git
synced 2026-06-03 18:24:08 +02:00
power
This commit is contained in:
parent
c1b77cd2ab
commit
825e7cace8
4 changed files with 105 additions and 1 deletions
34
README.md
Normal file
34
README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
## Power monitoring permissions
|
||||||
|
|
||||||
|
In recent kernel versions, precise power monitoring is only accessible to root users to prevent using it as a side-channel attack.
|
||||||
|
In order to get the power monitoring output you'll need to give the palantir user access to this data using the following steps.
|
||||||
|
|
||||||
|
- Create a group using
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo groupadd powermonitoring
|
||||||
|
```
|
||||||
|
|
||||||
|
- Create `/etc/udev/rules.d/99-powermonitoring.rules` with
|
||||||
|
```udev
|
||||||
|
SUBSYSTEM=="powercap", ACTION=="add", RUN+="/bin/chgrp -R powermonitoring /sys%p", RUN+="/bin/chmod -R g=u /sys%p"
|
||||||
|
SUBSYSTEM=="powercap", ACTION=="change", ENV{TRIGGER}!="none", RUN+="/bin/chgrp -R powermonitoring /sys%p", RUN+="/bin/chmod -R g=u /sys%p"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Apply the udev rules
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||||
|
```
|
||||||
|
|
||||||
|
- Add your user to the group
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo usermod -a G powermonitoring palantir
|
||||||
|
```
|
||||||
|
|
||||||
|
- Verify that you can read energy usage
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo su - palantir -c 'cat /sys/class/powercap/intel-rapl:0:0/energy_uj
|
||||||
|
```
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod docker;
|
pub mod docker;
|
||||||
|
pub mod power;
|
||||||
pub mod sensors;
|
pub mod sensors;
|
||||||
pub mod zfs;
|
pub mod zfs;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use futures_util::pin_mut;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use palantir::docker::{get_docker, stat, Container};
|
use palantir::docker::{get_docker, stat, Container};
|
||||||
use palantir::get_metrics;
|
use palantir::get_metrics;
|
||||||
|
use palantir::power::power_usage;
|
||||||
use warp::reject::Reject;
|
use warp::reject::Reject;
|
||||||
use warp::{Filter, Rejection};
|
use warp::{Filter, Rejection};
|
||||||
|
|
||||||
|
|
@ -21,8 +22,8 @@ impl Reject for ReportRejection {}
|
||||||
|
|
||||||
async fn serve_inner(docker: Option<Docker>) -> Result<String> {
|
async fn serve_inner(docker: Option<Docker>) -> Result<String> {
|
||||||
let mut metrics = get_metrics()?;
|
let mut metrics = get_metrics()?;
|
||||||
|
let hostname = palantir::sensors::hostname()?;
|
||||||
if let Some(docker) = docker {
|
if let Some(docker) = docker {
|
||||||
let hostname = palantir::sensors::hostname()?;
|
|
||||||
let containers = stat(docker).await?;
|
let containers = stat(docker).await?;
|
||||||
pin_mut!(containers);
|
pin_mut!(containers);
|
||||||
while let Some(container) = containers.next().await {
|
while let Some(container) = containers.next().await {
|
||||||
|
|
@ -30,6 +31,9 @@ async fn serve_inner(docker: Option<Docker>) -> Result<String> {
|
||||||
container.write(&mut metrics, &hostname);
|
container.write(&mut metrics, &hostname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(power) = power_usage()? {
|
||||||
|
power.write(&mut metrics, &hostname);
|
||||||
|
}
|
||||||
Ok(metrics)
|
Ok(metrics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
65
src/power.rs
Normal file
65
src/power.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use color_eyre::{Report, Result};
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::fs::{read_dir, read_to_string};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
static CAN_READ: AtomicBool = AtomicBool::new(true);
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct PowerUsage {
|
||||||
|
total_uj: u64,
|
||||||
|
packages_uj: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerUsage {
|
||||||
|
pub fn write<W: Write>(&self, mut w: W, hostname: &str) {
|
||||||
|
writeln!(
|
||||||
|
&mut w,
|
||||||
|
"total_power{{host=\"{}\"}} {:.3}",
|
||||||
|
hostname,
|
||||||
|
self.total_uj as f64 / 1_000_000.0
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
for (i, package) in self.packages_uj.iter().enumerate() {
|
||||||
|
writeln!(
|
||||||
|
&mut w,
|
||||||
|
"total_power{{host=\"{}\", package=\"{}\"}} {:.3}",
|
||||||
|
hostname,
|
||||||
|
i,
|
||||||
|
*package as f64 / 1_000_000.0
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn power_usage() -> Result<Option<PowerUsage>> {
|
||||||
|
if !CAN_READ.load(Ordering::Relaxed) {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dir = read_dir("/sys/devices/virtual/powercap/intel-rapl")?;
|
||||||
|
let mut usage = PowerUsage::default();
|
||||||
|
for package in dir {
|
||||||
|
let package = package?;
|
||||||
|
if package
|
||||||
|
.file_name()
|
||||||
|
.to_str()
|
||||||
|
.ok_or_else(|| Report::msg("Invalid name"))?
|
||||||
|
.starts_with("intel-rapl")
|
||||||
|
{
|
||||||
|
let mut package_path = package.path();
|
||||||
|
package_path.push("energy_uj");
|
||||||
|
let package_usage = match read_to_string(&package_path) {
|
||||||
|
Err(e) if e.raw_os_error() == Some(13) => {
|
||||||
|
CAN_READ.store(false, Ordering::Relaxed);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
result => result,
|
||||||
|
}?;
|
||||||
|
let package_usage = dbg!(package_usage.trim().parse::<u64>()?);
|
||||||
|
usage.total_uj += package_usage;
|
||||||
|
usage.packages_uj.push(package_usage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Some(usage))
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue