report: add api endpoint and function to generate report
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
This commit is contained in:
parent
fb0d9833af
commit
b0ef9631e6
|
@ -38,6 +38,7 @@ mod services;
|
||||||
mod status;
|
mod status;
|
||||||
mod syslog;
|
mod syslog;
|
||||||
mod time;
|
mod time;
|
||||||
|
mod report;
|
||||||
|
|
||||||
pub const SHELL_CMD_SCHEMA: Schema = StringSchema::new("The command to run.")
|
pub const SHELL_CMD_SCHEMA: Schema = StringSchema::new("The command to run.")
|
||||||
.format(&ApiStringFormat::Enum(&[
|
.format(&ApiStringFormat::Enum(&[
|
||||||
|
@ -310,6 +311,7 @@ pub const SUBDIRS: SubdirMap = &[
|
||||||
("dns", &dns::ROUTER),
|
("dns", &dns::ROUTER),
|
||||||
("journal", &journal::ROUTER),
|
("journal", &journal::ROUTER),
|
||||||
("network", &network::ROUTER),
|
("network", &network::ROUTER),
|
||||||
|
("report", &report::ROUTER),
|
||||||
("rrd", &rrd::ROUTER),
|
("rrd", &rrd::ROUTER),
|
||||||
("services", &services::ROUTER),
|
("services", &services::ROUTER),
|
||||||
("status", &status::ROUTER),
|
("status", &status::ROUTER),
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
use anyhow::Error;
|
||||||
|
use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment};
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
use crate::api2::types::*;
|
||||||
|
use crate::config::acl::PRIV_SYS_AUDIT;
|
||||||
|
use crate::server::generate_report;
|
||||||
|
|
||||||
|
#[api(
|
||||||
|
input: {
|
||||||
|
properties: {
|
||||||
|
node: {
|
||||||
|
schema: NODE_SCHEMA,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
returns: {
|
||||||
|
type: String,
|
||||||
|
description: "Returns report of the node"
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
permission: &Permission::Privilege(&["system", "status"], PRIV_SYS_AUDIT, false),
|
||||||
|
},
|
||||||
|
)]
|
||||||
|
/// Generate a report
|
||||||
|
fn get_report(
|
||||||
|
_param: Value,
|
||||||
|
_info: &ApiMethod,
|
||||||
|
_rpcenv: &mut dyn RpcEnvironment,
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
Ok(json!(generate_report()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ROUTER: Router = Router::new()
|
||||||
|
.get(&API_METHOD_GET_REPORT);
|
|
@ -84,3 +84,6 @@ pub use gc_job::*;
|
||||||
|
|
||||||
mod email_notifications;
|
mod email_notifications;
|
||||||
pub use email_notifications::*;
|
pub use email_notifications::*;
|
||||||
|
|
||||||
|
mod report;
|
||||||
|
pub use report::*;
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
use crate::config::datastore;
|
||||||
|
use crate::tools::subscription::read_subscription;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref FILES: Vec<&'static str> = vec!["/etc/hosts", "/etc/network/interfaces"];
|
||||||
|
|
||||||
|
// (<command>, <arg [, arg]>)
|
||||||
|
static ref COMMANDS: Vec<(&'static str, Vec<&'static str>)> = vec![
|
||||||
|
("/usr/bin/df", vec!["-h"]),
|
||||||
|
("/usr/bin/lsblk", vec!["-ascii"])
|
||||||
|
];
|
||||||
|
|
||||||
|
// (<description>, <function to call>)
|
||||||
|
static ref FUNCTION_CALLS: Vec<(&'static str, fn() -> String)> = vec![
|
||||||
|
("Subscription status", || match read_subscription() {
|
||||||
|
Ok(Some(sub_info)) => sub_info.status.to_string(),
|
||||||
|
_ => String::from("No subscription found"),
|
||||||
|
}),
|
||||||
|
("Datastores", || {
|
||||||
|
let config = match datastore::config() {
|
||||||
|
Ok((config, _digest)) => config,
|
||||||
|
_ => return String::from("could not read datastore config"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut list = Vec::new();
|
||||||
|
for (store, _) in &config.sections {
|
||||||
|
list.push(store.as_str());
|
||||||
|
}
|
||||||
|
list.join(", ")
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_report() -> String {
|
||||||
|
use proxmox::tools::fs::file_read_optional_string;
|
||||||
|
|
||||||
|
let file_contents = FILES
|
||||||
|
.iter()
|
||||||
|
.map(|file_name| {
|
||||||
|
let content = match file_read_optional_string(Path::new(file_name)) {
|
||||||
|
Ok(Some(content)) => content,
|
||||||
|
Err(err) => err.to_string(),
|
||||||
|
_ => String::from("Could not be read!"),
|
||||||
|
};
|
||||||
|
format!("# {}\n{}", file_name, content)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
|
let command_outputs = COMMANDS
|
||||||
|
.iter()
|
||||||
|
.map(|(command, args)| {
|
||||||
|
let output = match Command::new(command).args(args).output() {
|
||||||
|
Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
|
||||||
|
Err(err) => err.to_string(),
|
||||||
|
};
|
||||||
|
format!("# {} {}\n{}", command, args.join(" "), output)
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
|
let function_outputs = FUNCTION_CALLS
|
||||||
|
.iter()
|
||||||
|
.map(|(desc, function)| format!("# {}\n{}", desc, function()))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n\n");
|
||||||
|
|
||||||
|
format!(
|
||||||
|
" FILES\n{}\n COMMANDS\n{}\n FUNCTIONS\n{}",
|
||||||
|
file_contents, command_outputs, function_outputs
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue