* [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool
@ 2021-09-09 13:48 Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes Dominik Csapak
` (6 more replies)
0 siblings, 7 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
this series adds the tool 'pbs-shell', similar to 'pvesh' and 'pmgsh'
this tool is intended mainly for debugging, but can be useful for
api calls not exposed via the client/manager.
proxmox (and the dependency in proxmox-backup) need to be bumped
proxmox-backup patches 2-5 are not strictly necessary, but improve
the api so that the 'ls' command works better
(there are still some api paths to be fixed)
proxmox:
Dominik Csapak (1):
proxmox: generate_usage_str: don't require static lifetimes
proxmox/src/api/cli/format.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
proxmox-backup:
Dominik Csapak (5):
add 'pbs-shell' utility
api2: add missing token list match_all property
api2: disks/directory: refactor BASE_MOUNT_DIR
api2: disks/directory: add 'name' property to directory mount listing
api2: nodes: add missing node list api call
Makefile | 2 +
debian/pbs-shell.bc | 3 +
debian/proxmox-backup-server.bash-completion | 1 +
debian/proxmox-backup-server.install | 3 +
docs/Makefile | 8 +
docs/pbs-shell/description.rst | 3 +
docs/pbs-shell/man1.rst | 40 ++
src/api2/access/user.rs | 35 +-
src/api2/mod.rs | 4 +-
src/api2/node/disks/directory.rs | 15 +-
src/api2/node/mod.rs | 12 +-
src/bin/pbs-shell.rs | 502 +++++++++++++++++++
zsh-completions/_pbs-shell | 13 +
13 files changed, 628 insertions(+), 13 deletions(-)
create mode 100644 debian/pbs-shell.bc
create mode 100644 docs/pbs-shell/description.rst
create mode 100644 docs/pbs-shell/man1.rst
create mode 100644 src/bin/pbs-shell.rs
create mode 100644 zsh-completions/_pbs-shell
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
@ 2021-09-09 13:48 ` Dominik Csapak
2021-09-15 7:40 ` [pbs-devel] applied: " Thomas Lamprecht
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 1/5] add 'pbs-shell' utility Dominik Csapak
` (5 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
this prevents us from using it under certain conditions and it's
actually not necessary, so drop them
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
proxmox/src/api/cli/format.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proxmox/src/api/cli/format.rs b/proxmox/src/api/cli/format.rs
index 17efcd6..a4fb78d 100644
--- a/proxmox/src/api/cli/format.rs
+++ b/proxmox/src/api/cli/format.rs
@@ -60,7 +60,7 @@ pub fn generate_usage_str(
cli_cmd: &CliCommand,
format: DocumentationFormat,
indent: &str,
- skip_options: &'static [&'static str],
+ skip_options: &[&str],
) -> String {
let arg_param = cli_cmd.arg_param;
let fixed_param = &cli_cmd.fixed_param;
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 1/5] add 'pbs-shell' utility
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes Dominik Csapak
@ 2021-09-09 13:48 ` Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 2/5] api2: add missing token list match_all property Dominik Csapak
` (4 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
similar to pve/pmg, a user can call the api with this utility without
going through the proxy/daemon, as well as list the api endpoints
(with child links) and get the api description of endpoints
this is mainly intended for debugging, but it is also useful for
situations where some api calls do not have an equivalent in a binary
and a user does not want to go through the api
not implemented are the http2 api calls (since it is a separate api and
it wouldn't be that easy to do)
there are a few quirks though, related to the 'ls' command:
i extract the 'child-link' from the property name of the
'match_all' statement of the router, but this does not
always match with the property from the relevant 'get' api call
so it fails there (e.g. /tape/drive )
this can be fixed in the respective api calls (e.g. by renaming
the parameter that comes from the path)
includes bash/zsh completion helpers and a basic manpage
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
Makefile | 2 +
debian/pbs-shell.bc | 3 +
debian/proxmox-backup-server.bash-completion | 1 +
debian/proxmox-backup-server.install | 3 +
docs/Makefile | 8 +
docs/pbs-shell/description.rst | 3 +
docs/pbs-shell/man1.rst | 40 ++
src/bin/pbs-shell.rs | 502 +++++++++++++++++++
zsh-completions/_pbs-shell | 13 +
9 files changed, 575 insertions(+)
create mode 100644 debian/pbs-shell.bc
create mode 100644 docs/pbs-shell/description.rst
create mode 100644 docs/pbs-shell/man1.rst
create mode 100644 src/bin/pbs-shell.rs
create mode 100644 zsh-completions/_pbs-shell
diff --git a/Makefile b/Makefile
index c1aecf61..abeaff37 100644
--- a/Makefile
+++ b/Makefile
@@ -11,6 +11,7 @@ USR_BIN := \
proxmox-backup-client \
proxmox-file-restore \
pxar \
+ pbs-shell \
proxmox-tape \
pmtx \
pmt
@@ -172,6 +173,7 @@ $(COMPILED_BINS) $(COMPILEDIR)/dump-catalog-shell-cli $(COMPILEDIR)/docgen: .do-
--bin proxmox-backup-api \
--bin proxmox-backup-proxy \
--bin proxmox-backup-manager \
+ --bin pbs-shell \
--bin docgen
$(CARGO) build $(CARGO_BUILD_ARGS) \
--package proxmox-backup-banner \
diff --git a/debian/pbs-shell.bc b/debian/pbs-shell.bc
new file mode 100644
index 00000000..3d17187c
--- /dev/null
+++ b/debian/pbs-shell.bc
@@ -0,0 +1,3 @@
+# pbs-shell bash completion
+
+complete -C 'pbs-shell bashcomplete' pbs-shell
diff --git a/debian/proxmox-backup-server.bash-completion b/debian/proxmox-backup-server.bash-completion
index a2165699..8d6a7047 100644
--- a/debian/proxmox-backup-server.bash-completion
+++ b/debian/proxmox-backup-server.bash-completion
@@ -2,3 +2,4 @@ debian/proxmox-backup-manager.bc proxmox-backup-manager
debian/proxmox-tape.bc proxmox-tape
debian/pmtx.bc pmtx
debian/pmt.bc pmt
+debian/pbs-shell.bc pbs-shell
diff --git a/debian/proxmox-backup-server.install b/debian/proxmox-backup-server.install
index 6e2219b4..5e1071fa 100644
--- a/debian/proxmox-backup-server.install
+++ b/debian/proxmox-backup-server.install
@@ -14,6 +14,7 @@ usr/sbin/proxmox-backup-manager
usr/bin/pmtx
usr/bin/pmt
usr/bin/proxmox-tape
+usr/bin/pbs-shell
usr/share/javascript/proxmox-backup/index.hbs
usr/share/javascript/proxmox-backup/css/ext6-pbs.css
usr/share/javascript/proxmox-backup/images
@@ -24,6 +25,7 @@ usr/share/man/man1/proxmox-backup-proxy.1
usr/share/man/man1/proxmox-tape.1
usr/share/man/man1/pmtx.1
usr/share/man/man1/pmt.1
+usr/share/man/man1/pbs-shell.1
usr/share/man/man5/acl.cfg.5
usr/share/man/man5/datastore.cfg.5
usr/share/man/man5/user.cfg.5
@@ -38,3 +40,4 @@ usr/share/zsh/vendor-completions/_proxmox-backup-manager
usr/share/zsh/vendor-completions/_proxmox-tape
usr/share/zsh/vendor-completions/_pmtx
usr/share/zsh/vendor-completions/_pmt
+usr/share/zsh/vendor-completions/_pbs-shell
diff --git a/docs/Makefile b/docs/Makefile
index 5e37f7d1..e67df2ea 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -10,6 +10,7 @@ GENERATED_SYNOPSIS := \
pxar/synopsis.rst \
pmtx/synopsis.rst \
pmt/synopsis.rst \
+ pbs-shell/synopsis.rst \
config/media-pool/config.rst \
config/tape/config.rst \
config/tape-job/config.rst \
@@ -24,6 +25,7 @@ MAN1_PAGES := \
pxar.1 \
pmtx.1 \
pmt.1 \
+ pbs-shell.1 \
proxmox-tape.1 \
proxmox-backup-proxy.1 \
proxmox-backup-client.1 \
@@ -117,6 +119,12 @@ pmt/synopsis.rst: ${COMPILEDIR}/pmt
pmt.1: pmt/man1.rst pmt/description.rst pmt/options.rst pmt/synopsis.rst
rst2man $< >$@
+pbs-shell/synopsis.rst: ${COMPILEDIR}/pbs-shell
+ ${COMPILEDIR}/pbs-shell printdoc > pbs-shell/synopsis.rst
+
+pbs-shell.1: pbs-shell/man1.rst pbs-shell/description.rst pbs-shell/synopsis.rst
+ rst2man $< >$@
+
config/datastore/config.rst: ${COMPILEDIR}/docgen
${COMPILEDIR}/docgen datastore.cfg >$@
diff --git a/docs/pbs-shell/description.rst b/docs/pbs-shell/description.rst
new file mode 100644
index 00000000..8dfcae15
--- /dev/null
+++ b/docs/pbs-shell/description.rst
@@ -0,0 +1,3 @@
+The ``pbs-shell`` command can show and execute api calls and their parameters.
+It is mainly intended for use during debugging.
+
diff --git a/docs/pbs-shell/man1.rst b/docs/pbs-shell/man1.rst
new file mode 100644
index 00000000..d0a1d07b
--- /dev/null
+++ b/docs/pbs-shell/man1.rst
@@ -0,0 +1,40 @@
+==========================
+pbs-shell
+==========================
+
+.. include:: ../epilog.rst
+
+-------------------------------------------------------------
+Show and execute PBS API calls
+-------------------------------------------------------------
+
+:Author: |AUTHOR|
+:Version: Version |VERSION|
+:Manual section: 1
+
+
+Synopsis
+==========
+
+.. include:: synopsis.rst
+
+
+Common Options
+==============
+
+Commands generating output supports the ``--output-format``
+parameter. It accepts the following values:
+
+:``text``: Text format (default). Human readable.
+
+:``json``: JSON (single line).
+
+:``json-pretty``: JSON (multiple lines, nicely formatted).
+
+
+Description
+============
+
+.. include:: description.rst
+
+.. include:: ../pbs-copyright.rst
diff --git a/src/bin/pbs-shell.rs b/src/bin/pbs-shell.rs
new file mode 100644
index 00000000..ce64617b
--- /dev/null
+++ b/src/bin/pbs-shell.rs
@@ -0,0 +1,502 @@
+use anyhow::{bail, format_err, Error};
+use hyper::Method;
+use serde::{Deserialize, Serialize};
+use serde_json::Value;
+
+use std::collections::HashMap;
+
+use proxmox::api::{
+ api,
+ cli::*,
+ format::DocumentationFormat,
+ schema::{parse_parameter_strings, ApiType, ParameterSchema, Schema},
+ ApiHandler, ApiMethod, RpcEnvironment, SubRoute,
+};
+
+use pbs_client::{connect_to_localhost, display_task_log};
+
+const PROG_NAME: &str = "pbs-shell";
+
+fn complete_api_path(complete_me: &str, _map: &HashMap<String, String>) -> Vec<String> {
+ pbs_runtime::main(async { complete_api_path_do(complete_me, None).await })
+}
+
+fn complete_api_path_get(complete_me: &str, _map: &HashMap<String, String>) -> Vec<String> {
+ pbs_runtime::main(async { complete_api_path_do(complete_me, Some("r")).await })
+}
+
+fn complete_api_path_set(complete_me: &str, _map: &HashMap<String, String>) -> Vec<String> {
+ pbs_runtime::main(async { complete_api_path_do(complete_me, Some("w")).await })
+}
+
+fn complete_api_path_create(complete_me: &str, _map: &HashMap<String, String>) -> Vec<String> {
+ pbs_runtime::main(async { complete_api_path_do(complete_me, Some("c")).await })
+}
+
+fn complete_api_path_delete(complete_me: &str, _map: &HashMap<String, String>) -> Vec<String> {
+ pbs_runtime::main(async { complete_api_path_do(complete_me, Some("d")).await })
+}
+
+fn complete_api_path_ls(complete_me: &str, _map: &HashMap<String, String>) -> Vec<String> {
+ pbs_runtime::main(async { complete_api_path_do(complete_me, Some("D")).await })
+}
+
+async fn complete_api_path_do(mut complete_me: &str, capability: Option<&str>) -> Vec<String> {
+ if complete_me.is_empty() {
+ complete_me = "/";
+ }
+
+ let mut list = Vec::new();
+
+ let mut lookup_path = complete_me.to_string();
+ let mut filter = "";
+ let last_path_index = complete_me.rfind('/');
+ if let Some(index) = last_path_index {
+ if index != complete_me.len() - 1 {
+ lookup_path = complete_me[..(index + 1)].to_string();
+ if index < complete_me.len() - 1 {
+ filter = &complete_me[(index + 1)..];
+ }
+ }
+ }
+
+ let uid = nix::unistd::Uid::current();
+
+ let username = match nix::unistd::User::from_uid(uid) {
+ Ok(Some(user)) => user.name,
+ _ => "root@pam".to_string(),
+ };
+ let mut rpcenv = CliEnvironment::new();
+ rpcenv.set_auth_id(Some(format!("{}@pam", username)));
+
+ while let Ok(children) = get_api_children(lookup_path.clone(), &mut rpcenv).await {
+ let old_len = list.len();
+ for entry in children {
+ let name = entry.name;
+ let caps = entry.capabilities;
+
+ if filter.is_empty() || name.starts_with(filter) {
+ let mut path = format!("{}{}", lookup_path, name);
+ if caps.contains('D') {
+ path.push('/');
+ list.push(path.clone());
+ } else if let Some(cap) = capability {
+ if caps.contains(cap) {
+ list.push(path);
+ }
+ } else {
+ list.push(path);
+ }
+ }
+ }
+
+ if list.len() == 1 && old_len != 1 && list[0].ends_with('/') {
+ // we added only one match and it was a directory, lookup again
+ lookup_path = list[0].clone();
+ filter = "";
+ continue;
+ }
+
+ break;
+ }
+
+ list
+}
+
+async fn get_child_links(
+ path: &str,
+ rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<String>, Error> {
+ let mut uri_param = HashMap::new();
+ let (path, components) = proxmox_backup::tools::normalize_uri_path(&path)?;
+
+ let info = &proxmox_backup::api2::ROUTER
+ .find_route(&components, &mut uri_param)
+ .ok_or_else(|| format_err!("no such resource"))?;
+
+ match info.subroute {
+ Some(SubRoute::Map(map)) => Ok(map.iter().map(|(name, _)| name.to_string()).collect()),
+ Some(SubRoute::MatchAll { param_name, .. }) => {
+ let get_call = info.get.ok_or_else(|| format_err!("no such resource"))?;
+ let list = call_api(get_call, rpcenv, serde_json::to_value(uri_param)?).await?;
+ Ok(list
+ .as_array()
+ .ok_or_else(|| format_err!("{} did not return an array", path))?
+ .iter()
+ .map(|item| {
+ item[param_name]
+ .as_str()
+ .map(|c| c.to_string())
+ .ok_or_else(|| format_err!("no such property {}", param_name))
+ })
+ .collect::<Result<Vec<_>, _>>()?)
+ }
+ None => bail!("link does not define child links"),
+ }
+}
+
+fn get_api_method(
+ method: Method,
+ path: &str,
+) -> Result<(&'static ApiMethod, HashMap<String, String>), Error> {
+ let mut uri_param = HashMap::new();
+ let (path, components) = proxmox_backup::tools::normalize_uri_path(&path)?;
+ if let Some(method) =
+ &proxmox_backup::api2::ROUTER.find_method(&components, method.clone(), &mut uri_param)
+ {
+ Ok((method, uri_param))
+ } else {
+ bail!("no {} handler defined for '{}'", method, path);
+ }
+}
+
+fn merge_parameters(
+ uri_param: HashMap<String, String>,
+ param: Value,
+ schema: ParameterSchema,
+) -> Result<Value, Error> {
+ let mut param_list: Vec<(String, String)> = vec![];
+
+ for (k, v) in uri_param {
+ param_list.push((k.clone(), v.clone()));
+ }
+
+ if let Some(map) = param.as_object() {
+ for (k, v) in map {
+ param_list.push((k.clone(), v.as_str().unwrap().to_string()));
+ }
+ }
+
+ let params = parse_parameter_strings(¶m_list, schema, true)?;
+
+ Ok(params)
+}
+
+async fn call_api(
+ method: &'static ApiMethod,
+ rpcenv: &mut dyn RpcEnvironment,
+ params: Value,
+) -> Result<Value, Error> {
+ match method.handler {
+ ApiHandler::AsyncHttp(_handler) => {
+ bail!("not implemented");
+ }
+ ApiHandler::Sync(handler) => (handler)(params, method, rpcenv),
+ ApiHandler::Async(handler) => (handler)(params, method, rpcenv).await,
+ }
+}
+
+async fn call_api_and_format_result(
+ method: Method,
+ path: String,
+ mut param: Value,
+ rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+ let mut output_format = extract_output_format(&mut param);
+ let (method, uri_param) = get_api_method(method, &path)?;
+ let params = merge_parameters(uri_param, param, method.parameters)?;
+
+ let mut result = call_api(method, rpcenv, params).await?;
+
+ if output_format == "text" {
+ if let Some(upid) = result.as_str() {
+ let mut client = connect_to_localhost()?;
+ display_task_log(&mut client, upid, true).await?;
+ return Ok(());
+ }
+ }
+
+ let options = default_table_format_options();
+ let return_type = &method.returns;
+ if matches!(return_type.schema, Schema::Null) {
+ output_format = "json-pretty".to_string();
+ }
+
+ format_and_print_result_full(&mut result, return_type, &output_format, &options);
+
+ Ok(())
+}
+
+#[api(
+ input: {
+ additional_properties: true,
+ properties: {
+ path: {
+ type: String,
+ description: "API path.",
+ },
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ },
+ },
+)]
+/// Call API PUT on <path>
+async fn set(path: String, param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ call_api_and_format_result(Method::PUT, path, param, rpcenv).await
+}
+
+#[api(
+ input: {
+ additional_properties: true,
+ properties: {
+ path: {
+ type: String,
+ description: "API path.",
+ },
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ },
+ },
+)]
+/// Call API POST on <path>
+async fn create(path: String, param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ call_api_and_format_result(Method::POST, path, param, rpcenv).await
+}
+
+#[api(
+ input: {
+ additional_properties: true,
+ properties: {
+ path: {
+ type: String,
+ description: "API path.",
+ },
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ },
+ },
+)]
+/// Call API GET on <path>
+async fn get(path: String, param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ call_api_and_format_result(Method::GET, path, param, rpcenv).await
+}
+
+#[api(
+ input: {
+ additional_properties: true,
+ properties: {
+ path: {
+ type: String,
+ description: "API path.",
+ },
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ },
+ },
+)]
+/// Call API DELETE on <path>
+async fn delete(path: String, param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ call_api_and_format_result(Method::DELETE, path, param, rpcenv).await
+}
+
+#[api(
+ input: {
+ properties: {
+ path: {
+ type: String,
+ description: "API path.",
+ },
+ verbose: {
+ type: Boolean,
+ description: "Verbose output format.",
+ optional: true,
+ default: false,
+ }
+ },
+ },
+)]
+/// Get API usage information for <path>
+async fn usage(
+ path: String,
+ verbose: bool,
+ _param: Value,
+ _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+ let docformat = if verbose {
+ DocumentationFormat::Full
+ } else {
+ DocumentationFormat::Short
+ };
+ let mut found = false;
+ for command in &["get", "set", "create", "delete"] {
+ let http_method = match *command {
+ "get" => Method::GET,
+ "set" => Method::PUT,
+ "create" => Method::POST,
+ "delete" => Method::DELETE,
+ _ => unreachable!(),
+ };
+ let (info, uri_params) = match get_api_method(http_method, &path) {
+ Ok(some) => some,
+ Err(_) => continue,
+ };
+ found = true;
+
+ let skip_params: Vec<&str> = uri_params.keys().map(|s| &**s).collect();
+
+ let cmd = CliCommand::new(info);
+ let prefix = format!("USAGE: {} {} {}", PROG_NAME, command, path);
+
+ print!(
+ "{}",
+ generate_usage_str(&prefix, &cmd, docformat, "", &skip_params)
+ );
+ }
+
+ if !found {
+ bail!("no such resource '{}'", path);
+ }
+ Ok(())
+}
+
+#[api()]
+#[derive(Debug, Serialize, Deserialize)]
+/// A child link with capabilities
+struct ApiDirEntry {
+ /// The name of the link
+ name: String,
+ /// The capabilities of the path (format Drwcd)
+ capabilities: String,
+}
+
+const LS_SCHEMA: &proxmox::api::schema::Schema =
+ &proxmox::api::schema::ArraySchema::new("List of child links", &ApiDirEntry::API_SCHEMA)
+ .schema();
+
+async fn get_api_children(
+ path: String,
+ rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<ApiDirEntry>, Error> {
+ let mut res = Vec::new();
+ for link in get_child_links(&path, rpcenv).await? {
+ let path = format!("{}/{}", path, link);
+ let (path, _) = proxmox_backup::tools::normalize_uri_path(&path)?;
+ let mut cap = String::new();
+
+ if get_child_links(&path, rpcenv).await.is_ok() {
+ cap.push('D');
+ } else {
+ cap.push('-');
+ }
+
+ let cap_list = &[
+ (Method::GET, 'r'),
+ (Method::PUT, 'w'),
+ (Method::POST, 'c'),
+ (Method::DELETE, 'd'),
+ ];
+
+ for (method, c) in cap_list {
+ if get_api_method(method.clone(), &path).is_ok() {
+ cap.push(*c);
+ } else {
+ cap.push('-');
+ }
+ }
+
+ res.push(ApiDirEntry {
+ name: link.to_string(),
+ capabilities: cap,
+ });
+ }
+
+ Ok(res)
+}
+
+#[api(
+ input: {
+ properties: {
+ path: {
+ type: String,
+ description: "API path.",
+ },
+ "output-format": {
+ schema: OUTPUT_FORMAT,
+ optional: true,
+ },
+ },
+ },
+)]
+/// Get API usage information for <path>
+async fn ls(path: String, mut param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
+ let output_format = extract_output_format(&mut param);
+
+ let options = TableFormatOptions::new()
+ .noborder(true)
+ .noheader(true)
+ .sortby("name", false);
+
+ let res = get_api_children(path, rpcenv).await?;
+
+ format_and_print_result_full(
+ &mut serde_json::to_value(res)?,
+ &proxmox::api::schema::ReturnType {
+ optional: false,
+ schema: &LS_SCHEMA,
+ },
+ &output_format,
+ &options,
+ );
+
+ Ok(())
+}
+
+fn main() -> Result<(), Error> {
+ let cmd_def = CliCommandMap::new()
+ .insert(
+ "get",
+ CliCommand::new(&API_METHOD_GET)
+ .arg_param(&["path"])
+ .completion_cb("path", complete_api_path_get),
+ )
+ .insert(
+ "set",
+ CliCommand::new(&API_METHOD_SET)
+ .arg_param(&["path"])
+ .completion_cb("path", complete_api_path_set),
+ )
+ .insert(
+ "create",
+ CliCommand::new(&API_METHOD_CREATE)
+ .arg_param(&["path"])
+ .completion_cb("path", complete_api_path_create),
+ )
+ .insert(
+ "delete",
+ CliCommand::new(&API_METHOD_DELETE)
+ .arg_param(&["path"])
+ .completion_cb("path", complete_api_path_delete),
+ )
+ .insert(
+ "ls",
+ CliCommand::new(&API_METHOD_LS)
+ .arg_param(&["path"])
+ .completion_cb("path", complete_api_path_ls),
+ )
+ .insert(
+ "usage",
+ CliCommand::new(&API_METHOD_USAGE)
+ .arg_param(&["path"])
+ .completion_cb("path", complete_api_path),
+ );
+
+ let uid = nix::unistd::Uid::current();
+
+ let username = match nix::unistd::User::from_uid(uid)? {
+ Some(user) => user.name,
+ None => bail!("unable to get user name"),
+ };
+ let mut rpcenv = CliEnvironment::new();
+ rpcenv.set_auth_id(Some(format!("{}@pam", username)));
+
+ pbs_runtime::main(run_async_cli_command(cmd_def, rpcenv));
+ Ok(())
+}
diff --git a/zsh-completions/_pbs-shell b/zsh-completions/_pbs-shell
new file mode 100644
index 00000000..507f15ae
--- /dev/null
+++ b/zsh-completions/_pbs-shell
@@ -0,0 +1,13 @@
+#compdef _pbs-shell() pbs-shell
+
+function _pbs-shell() {
+ local cwords line point cmd curr prev
+ cwords=${#words[@]}
+ line=$words
+ point=${#line}
+ cmd=${words[1]}
+ curr=${words[cwords]}
+ prev=${words[cwords-1]}
+ compadd -- $(COMP_CWORD="$cwords" COMP_LINE="$line" COMP_POINT="$point" \
+ pbs-shell bashcomplete "$cmd" "$curr" "$prev")
+}
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 2/5] api2: add missing token list match_all property
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 1/5] add 'pbs-shell' utility Dominik Csapak
@ 2021-09-09 13:48 ` Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 3/5] api2: disks/directory: refactor BASE_MOUNT_DIR Dominik Csapak
` (3 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
to have the proper link between the token list and the sub routes
in the api, include the 'tokenname' property in the token listing
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/api2/access/user.rs | 35 ++++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/src/api2/access/user.rs b/src/api2/access/user.rs
index 6a2fe83c..70065986 100644
--- a/src/api2/access/user.rs
+++ b/src/api2/access/user.rs
@@ -655,6 +655,21 @@ pub fn delete_token(
Ok(())
}
+#[api(
+ properties: {
+ tokenname: { type: Tokenname },
+ token: { type: ApiToken },
+ }
+)]
+#[derive(Serialize, Deserialize)]
+/// A Token Entry of a user
+pub struct TokenInfo {
+ /// The Token name
+ pub tokenname: Tokenname,
+ #[serde(flatten)]
+ pub token: ApiToken,
+}
+
#[api(
input: {
properties: {
@@ -666,7 +681,7 @@ pub fn delete_token(
returns: {
description: "List user's API tokens (with config digest).",
type: Array,
- items: { type: ApiToken },
+ items: { type: TokenInfo },
},
access: {
permission: &Permission::Or(&[
@@ -680,7 +695,7 @@ pub fn list_tokens(
userid: Userid,
_info: &ApiMethod,
mut rpcenv: &mut dyn RpcEnvironment,
-) -> Result<Vec<ApiToken>, Error> {
+) -> Result<Vec<TokenInfo>, Error> {
let (config, digest) = crate::config::user::config()?;
@@ -688,15 +703,21 @@ pub fn list_tokens(
rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
- let filter_by_owner = |token: &ApiToken| {
- if token.tokenid.is_token() {
- token.tokenid.user() == &userid
+ let filter_by_owner = |token: ApiToken| {
+ if token.tokenid.is_token() && token.tokenid.user() == &userid {
+ let tokenname = token.tokenid.tokenname().unwrap().to_owned();
+ Some(TokenInfo {
+ tokenname,
+ token,
+ })
} else {
- false
+ None
}
};
- Ok(list.into_iter().filter(filter_by_owner).collect())
+ let res = list.into_iter().filter_map(filter_by_owner).collect();
+
+ Ok(res)
}
const TOKEN_ITEM_ROUTER: Router = Router::new()
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 3/5] api2: disks/directory: refactor BASE_MOUNT_DIR
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
` (2 preceding siblings ...)
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 2/5] api2: add missing token list match_all property Dominik Csapak
@ 2021-09-09 13:48 ` Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 4/5] api2: disks/directory: add 'name' property to directory mount listing Dominik Csapak
` (2 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/api2/node/disks/directory.rs | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
index 0cb6f4e2..dd6912d3 100644
--- a/src/api2/node/disks/directory.rs
+++ b/src/api2/node/disks/directory.rs
@@ -22,6 +22,8 @@ use crate::server::WorkerTask;
use crate::config::datastore::{self, DataStoreConfig};
use pbs_config::open_backup_lockfile;
+const BASE_MOUNT_DIR: &str = "/mnt/datastore/";
+
#[api(
properties: {
"filesystem": {
@@ -146,7 +148,7 @@ pub fn create_datastore_disk(
bail!("disk '{}' is already in use.", disk);
}
- let mount_point = format!("/mnt/datastore/{}", &name);
+ let mount_point = format!("{}{}", BASE_MOUNT_DIR, &name);
// check if the default path does exist already and bail if it does
let default_path = std::path::PathBuf::from(&mount_point);
@@ -221,7 +223,7 @@ pub fn create_datastore_disk(
/// Remove a Filesystem mounted under '/mnt/datastore/<name>'.".
pub fn delete_datastore_disk(name: String) -> Result<(), Error> {
- let path = format!("/mnt/datastore/{}", name);
+ let path = format!("{}{}", BASE_MOUNT_DIR, name);
// path of datastore cannot be changed
let (config, _) = crate::config::datastore::config()?;
let datastores: Vec<DataStoreConfig> = config.convert_to_typed_array("datastore")?;
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 4/5] api2: disks/directory: add 'name' property to directory mount listing
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
` (3 preceding siblings ...)
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 3/5] api2: disks/directory: refactor BASE_MOUNT_DIR Dominik Csapak
@ 2021-09-09 13:48 ` Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 5/5] api2: nodes: add missing node list api call Dominik Csapak
2021-09-13 9:36 ` [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Hannes Laimer
6 siblings, 0 replies; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
so that we have the properties that match with 'match_all'
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/api2/node/disks/directory.rs | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/api2/node/disks/directory.rs b/src/api2/node/disks/directory.rs
index dd6912d3..f9282903 100644
--- a/src/api2/node/disks/directory.rs
+++ b/src/api2/node/disks/directory.rs
@@ -38,6 +38,8 @@ const BASE_MOUNT_DIR: &str = "/mnt/datastore/";
pub struct DatastoreMountInfo {
/// The path of the mount unit.
pub unitfile: String,
+ /// The name of the mount
+ pub name: String,
/// The mount path.
pub path: String,
/// The mounted device.
@@ -86,8 +88,15 @@ pub fn list_datastore_mounts() -> Result<Vec<DatastoreMountInfo>, Error> {
let config = systemd::config::parse_systemd_mount(&unitfile)?;
let data: SystemdMountSection = config.lookup("Mount", "Mount")?;
+ let name = data
+ .Where
+ .strip_prefix(BASE_MOUNT_DIR)
+ .unwrap_or_else(|| &data.Where)
+ .to_string();
+
list.push(DatastoreMountInfo {
unitfile,
+ name,
device: data.What,
path: data.Where,
filesystem: data.Type,
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] [PATCH proxmox-backup 5/5] api2: nodes: add missing node list api call
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
` (4 preceding siblings ...)
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 4/5] api2: disks/directory: add 'name' property to directory mount listing Dominik Csapak
@ 2021-09-09 13:48 ` Dominik Csapak
2021-09-15 9:44 ` [pbs-devel] applied: " Thomas Lamprecht
2021-09-13 9:36 ` [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Hannes Laimer
6 siblings, 1 reply; 12+ messages in thread
From: Dominik Csapak @ 2021-09-09 13:48 UTC (permalink / raw)
To: pbs-devel
to have an api call for api path traversal
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/api2/mod.rs | 4 +---
src/api2/node/mod.rs | 12 +++++++++++-
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/api2/mod.rs b/src/api2/mod.rs
index 132e2c2a..0ede4165 100644
--- a/src/api2/mod.rs
+++ b/src/api2/mod.rs
@@ -18,14 +18,12 @@ use proxmox::api::router::SubdirMap;
use proxmox::api::Router;
use proxmox::list_subdirs_api_method;
-const NODES_ROUTER: Router = Router::new().match_all("node", &node::ROUTER);
-
const SUBDIRS: SubdirMap = &[
("access", &access::ROUTER),
("admin", &admin::ROUTER),
("backup", &backup::ROUTER),
("config", &config::ROUTER),
- ("nodes", &NODES_ROUTER),
+ ("nodes", &node::ROUTER),
("ping", &ping::ROUTER),
("pull", &pull::ROUTER),
("reader", &reader::ROUTER),
diff --git a/src/api2/node/mod.rs b/src/api2/node/mod.rs
index f1a17934..194ec920 100644
--- a/src/api2/node/mod.rs
+++ b/src/api2/node/mod.rs
@@ -315,6 +315,12 @@ fn upgrade_to_websocket(
.boxed()
}
+#[api]
+/// List Nodes (only for compatiblity)
+fn list_nodes() -> Result<Value, Error> {
+ Ok(json!([ { "node": proxmox::tools::nodename().to_string() } ]))
+}
+
pub const SUBDIRS: SubdirMap = &[
("apt", &apt::ROUTER),
("certificates", &certificates::ROUTER),
@@ -338,6 +344,10 @@ pub const SUBDIRS: SubdirMap = &[
),
];
-pub const ROUTER: Router = Router::new()
+pub const ITEM_ROUTER: Router = Router::new()
.get(&list_subdirs_api_method!(SUBDIRS))
.subdirs(SUBDIRS);
+
+pub const ROUTER: Router = Router::new()
+ .get(&API_METHOD_LIST_NODES)
+ .match_all("node", &ITEM_ROUTER);
--
2.30.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
` (5 preceding siblings ...)
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 5/5] api2: nodes: add missing node list api call Dominik Csapak
@ 2021-09-13 9:36 ` Hannes Laimer
2021-09-13 10:38 ` Dominik Csapak
6 siblings, 1 reply; 12+ messages in thread
From: Hannes Laimer @ 2021-09-13 9:36 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
I just noticed two things while testing:
- if an endpoint expects a parameter called 'path'(e.g. POST /config
/datastore), the parameter conflicts with the api path itself
- even though commands like verify(POST /admin/datastore/<name>/verify)
or gc(POST /admin/datastore/<name>/gc) are started and run successfully,
`Error: control socket connect failed - Connection refused (os error
111)` is displayed almost always at the end.
Note: For testing I applied the patches on
b65dfff574f44e5a60db3903d2226f4dbe386b7a, I assume the moving of stuff
after that should not effect the functionality
Am 09.09.21 um 15:48 schrieb Dominik Csapak:
> this series adds the tool 'pbs-shell', similar to 'pvesh' and 'pmgsh'
> this tool is intended mainly for debugging, but can be useful for
> api calls not exposed via the client/manager.
>
> proxmox (and the dependency in proxmox-backup) need to be bumped
>
> proxmox-backup patches 2-5 are not strictly necessary, but improve
> the api so that the 'ls' command works better
> (there are still some api paths to be fixed)
>
> proxmox:
>
> Dominik Csapak (1):
> proxmox: generate_usage_str: don't require static lifetimes
>
> proxmox/src/api/cli/format.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> proxmox-backup:
>
> Dominik Csapak (5):
> add 'pbs-shell' utility
> api2: add missing token list match_all property
> api2: disks/directory: refactor BASE_MOUNT_DIR
> api2: disks/directory: add 'name' property to directory mount listing
> api2: nodes: add missing node list api call
>
> Makefile | 2 +
> debian/pbs-shell.bc | 3 +
> debian/proxmox-backup-server.bash-completion | 1 +
> debian/proxmox-backup-server.install | 3 +
> docs/Makefile | 8 +
> docs/pbs-shell/description.rst | 3 +
> docs/pbs-shell/man1.rst | 40 ++
> src/api2/access/user.rs | 35 +-
> src/api2/mod.rs | 4 +-
> src/api2/node/disks/directory.rs | 15 +-
> src/api2/node/mod.rs | 12 +-
> src/bin/pbs-shell.rs | 502 +++++++++++++++++++
> zsh-completions/_pbs-shell | 13 +
> 13 files changed, 628 insertions(+), 13 deletions(-)
> create mode 100644 debian/pbs-shell.bc
> create mode 100644 docs/pbs-shell/description.rst
> create mode 100644 docs/pbs-shell/man1.rst
> create mode 100644 src/bin/pbs-shell.rs
> create mode 100644 zsh-completions/_pbs-shell
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool
2021-09-13 9:36 ` [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Hannes Laimer
@ 2021-09-13 10:38 ` Dominik Csapak
2021-09-15 5:25 ` Thomas Lamprecht
0 siblings, 1 reply; 12+ messages in thread
From: Dominik Csapak @ 2021-09-13 10:38 UTC (permalink / raw)
To: Hannes Laimer, Proxmox Backup Server development discussion
thanks for testing!
On 9/13/21 11:36, Hannes Laimer wrote:
> I just noticed two things while testing:
> - if an endpoint expects a parameter called 'path'(e.g. POST /config
> /datastore), the parameter conflicts with the api path itself
i'll fix it in the v2 by renaming the variable to 'api-path'. or
should i prefix it with 'pbs-shell' or something?
(it must be something that will not occur in the normal api)
> - even though commands like verify(POST /admin/datastore/<name>/verify)
> or gc(POST /admin/datastore/<name>/gc) are started and run successfully,
> `Error: control socket connect failed - Connection refused (os error
> 111)` is displayed almost always at the end.
after looking at/testing the code a bit closer, i noticed it cannot
currently work as i do it:
* pbs-shell starts a worker
* pbs-shell connects to 8007 for the log
* proxy tries to query the worker status via command socket, which
does not exists for the pbs-shell
i'll have to display the task log manually from the pbs-shell
and wait for the worker to finish
while i think it *would* be better if we let the proxy/daemon
start the worker, there is no way to detect api calls
that start a worker before actually executing it...
since it's *only* a debug tool, its still fine i think..
alternatively, we can switch to a http client for executing
the api entirely...
>
> Note: For testing I applied the patches on
> b65dfff574f44e5a60db3903d2226f4dbe386b7a, I assume the moving of stuff
> after that should not effect the functionality
>
i'll rebase onto master for the v2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool
2021-09-13 10:38 ` Dominik Csapak
@ 2021-09-15 5:25 ` Thomas Lamprecht
0 siblings, 0 replies; 12+ messages in thread
From: Thomas Lamprecht @ 2021-09-15 5:25 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak,
Hannes Laimer
disclaimer: I had half an answer here but missed to send it out soon enough, it's somewhat
obsolete due to your v2 but also does not hurt to have on the list so still sending it..
On 13.09.21 12:38, Dominik Csapak wrote:
> thanks for testing!
>
> On 9/13/21 11:36, Hannes Laimer wrote:
>> I just noticed two things while testing:
>> - if an endpoint expects a parameter called 'path'(e.g. POST /config
>> /datastore), the parameter conflicts with the api path itself
>
> i'll fix it in the v2 by renaming the variable to 'api-path'. or
> should i prefix it with 'pbs-shell' or something?
> (it must be something that will not occur in the normal api)
meh... Ideally we'd use something that the API probably won't be allowed to use
any time in the future (e.g., 🛠️ ;P).
But, `api-path` sounds all right, it's unlikely to be used anytime soon, and
even if we can think through a better solution then, it's a fixed argument any
way I figure; so adapting that wouldn't be an actual breaking change.
So your v2 is fine.
> while i think it *would* be better if we let the proxy/daemon
> start the worker, there is no way to detect api calls
> that start a worker before actually executing it...
> since it's *only* a debug tool, its still fine i think..
I (low-priority) planned to add an actual UPID "type" in PVE since a bit for
API calls that return a worker, doing so in PBS could be used as indication
that a endpoint may return a worker.
>
> alternatively, we can switch to a http client for executing
> the api entirely...
In that case I'd really like to produce a full-feature API client crate that
can also cope with PVE/PMG (not much to change there). We need that anyway
for future projects and it shouldn't be to hard, can be somewhat modeled
after the pve-api-client perl thingy in API and semantics; it would be great
though if it could cope with either hyper or something simple & sync like
ureq (once the native-tls support pull-request is merged).
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] applied: [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes Dominik Csapak
@ 2021-09-15 7:40 ` Thomas Lamprecht
0 siblings, 0 replies; 12+ messages in thread
From: Thomas Lamprecht @ 2021-09-15 7:40 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
On 09.09.21 15:48, Dominik Csapak wrote:
> this prevents us from using it under certain conditions and it's
> actually not necessary, so drop them
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> proxmox/src/api/cli/format.rs | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>
applied this one already, thanks!
^ permalink raw reply [flat|nested] 12+ messages in thread
* [pbs-devel] applied: [PATCH proxmox-backup 5/5] api2: nodes: add missing node list api call
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 5/5] api2: nodes: add missing node list api call Dominik Csapak
@ 2021-09-15 9:44 ` Thomas Lamprecht
0 siblings, 0 replies; 12+ messages in thread
From: Thomas Lamprecht @ 2021-09-15 9:44 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
On 09.09.21 15:48, Dominik Csapak wrote:
> to have an api call for api path traversal
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> src/api2/mod.rs | 4 +---
> src/api2/node/mod.rs | 12 +++++++++++-
> 2 files changed, 12 insertions(+), 4 deletions(-)
>
>
applied this rather independent patch already, thanks!
Any thoughts on providing some resource usage and status info like pve has?
# pvesh get /nodes --output-format json-pretty
[
{
"cpu" : 0.00688381192943118,
"disk" : 81085153280,
"id" : "node/d7",
"level" : "c",
"maxcpu" : 16,
"maxdisk" : 133204897792,
"maxmem" : 6101630976,
"mem" : 1656844288,
"node" : "d7",
"ssl_fingerprint" : "8A:DF:09:A5:D8:CC:42:AD:B1:35:3B:E7:03:30:30:3C:C2:BD:BF:94:FA:F3:CD:46:62:92:6D:34:00:56:CC:8D",
"status" : "online",
"type" : "node",
"uptime" : 1990844
}
]
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2021-09-15 9:45 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-09 13:48 [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox 1/1] proxmox: generate_usage_str: don't require static lifetimes Dominik Csapak
2021-09-15 7:40 ` [pbs-devel] applied: " Thomas Lamprecht
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 1/5] add 'pbs-shell' utility Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 2/5] api2: add missing token list match_all property Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 3/5] api2: disks/directory: refactor BASE_MOUNT_DIR Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 4/5] api2: disks/directory: add 'name' property to directory mount listing Dominik Csapak
2021-09-09 13:48 ` [pbs-devel] [PATCH proxmox-backup 5/5] api2: nodes: add missing node list api call Dominik Csapak
2021-09-15 9:44 ` [pbs-devel] applied: " Thomas Lamprecht
2021-09-13 9:36 ` [pbs-devel] [PATCH proxmox/proxmox-backup] add 'pbs-shell' tool Hannes Laimer
2021-09-13 10:38 ` Dominik Csapak
2021-09-15 5:25 ` Thomas Lamprecht
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox