public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands
@ 2021-09-21 10:11 Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 1/7] server: refactor abort_local_worker Dominik Csapak
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

this series is a successor to my previous 'pbs-shell' series [0]
code wise its mostly the same but now a subcommand of 'proxmox-backup-debug'

changes from v3:
* rebase on master (use normalize_path_uri from proxmox_rest_server)
* fix worker task log output (upid was not wrapped in {data:upid})

changes from v2:
* now subcommand api of proxmox-backup-debug
* use http api on localhost by default
* add PROXMOX_DEBUG_API_CODE env variable to execute code directly
* use a macro for the completion helper calls
* combine get/post/put/delete requests into one and use 'fixed_param'
* drop priviliges to the backup user when executing 'non-protected' api
  calls directly

changes from v1:
* rebase on master
* rename 'path' parameter to 'api-path' since it clashed with some
  api calls that had a 'path variable'
* better handle workers:
  we must always wait for the local workers and do not need to
  print their logs, since most workers print to stdout if the
  rpcenv type is CLI. Also catch Ctrl+C and try to abort the worker

0: https://lists.proxmox.com/pipermail/pbs-devel/2021-September/003974.html

Dominik Csapak (7):
  server: refactor abort_local_worker
  move proxmox-backup-debug back to main crate
  proxmox-backup-debug: add 'api' subcommands
  api2: add missing token list match_all property
  api2: make some workers log on CLI
  docs: add proxmox-backup-debug to the list of command line tools
  docs: proxmox-backup-debug: add info about the 'api' subcommand

 Cargo.toml                                    |   1 -
 Makefile                                      |   2 -
 debian/proxmox-backup-debug.bash-completion   |   1 -
 debian/proxmox-backup-server.bash-completion  |   1 +
 docs/command-line-tools.rst                   |   4 +
 docs/proxmox-backup-debug/description.rst     |  11 +
 proxmox-backup-debug/Cargo.toml               |  18 -
 proxmox-backup-debug/src/main.rs              |  13 -
 src/api2/access/user.rs                       |  66 ++-
 src/api2/admin/datastore.rs                   |   4 +-
 src/api2/admin/sync.rs                        |   6 +-
 src/api2/admin/verify.rs                      |   5 +-
 src/api2/config/datastore.rs                  |   5 +-
 src/api2/pull.rs                              |   3 +-
 src/api2/tape/backup.rs                       |   7 +-
 src/bin/proxmox-backup-debug.rs               |  24 +
 src/bin/proxmox-backup-proxy.rs               |   6 +-
 src/bin/proxmox_backup_debug/api.rs           | 503 ++++++++++++++++++
 .../bin/proxmox_backup_debug}/inspect.rs      |   0
 src/bin/proxmox_backup_debug/mod.rs           |   3 +
 .../bin/proxmox_backup_debug}/recover.rs      |   0
 src/bin/proxmox_backup_manager/user.rs        |   6 +-
 src/server/verify_job.rs                      |   3 +-
 src/server/worker_task.rs                     |  12 +-
 24 files changed, 627 insertions(+), 77 deletions(-)
 delete mode 100644 debian/proxmox-backup-debug.bash-completion
 delete mode 100644 proxmox-backup-debug/Cargo.toml
 delete mode 100644 proxmox-backup-debug/src/main.rs
 create mode 100644 src/bin/proxmox-backup-debug.rs
 create mode 100644 src/bin/proxmox_backup_debug/api.rs
 rename {proxmox-backup-debug/src => src/bin/proxmox_backup_debug}/inspect.rs (100%)
 create mode 100644 src/bin/proxmox_backup_debug/mod.rs
 rename {proxmox-backup-debug/src => src/bin/proxmox_backup_debug}/recover.rs (100%)

-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 1/7] server: refactor abort_local_worker
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 2/7] move proxmox-backup-debug back to main crate Dominik Csapak
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

we'll need this outside the module

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/server/worker_task.rs | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/server/worker_task.rs b/src/server/worker_task.rs
index 565ce154..a74b18e1 100644
--- a/src/server/worker_task.rs
+++ b/src/server/worker_task.rs
@@ -100,9 +100,8 @@ pub fn register_task_control_commands(
     commando_sock.register_command("worker-task-abort".into(), move |args| {
         let upid = get_upid(args)?;
 
-        if let Some(ref worker) = WORKER_TASK_LIST.lock().unwrap().get(&upid.task_id) {
-            worker.request_abort();
-        }
+        abort_local_worker(upid);
+
         Ok(Value::Null)
     })?;
     commando_sock.register_command("worker-task-status".into(), move |args| {
@@ -828,3 +827,10 @@ pub async fn wait_for_local_worker(upid_str: &str) -> Result<(), Error> {
     }
     Ok(())
 }
+
+/// Request abort of a local worker (if existing and running)
+pub fn abort_local_worker(upid: UPID) {
+    if let Some(ref worker) = WORKER_TASK_LIST.lock().unwrap().get(&upid.task_id) {
+        worker.request_abort();
+    }
+}
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 2/7] move proxmox-backup-debug back to main crate
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 1/7] server: refactor abort_local_worker Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 3/7] proxmox-backup-debug: add 'api' subcommands Dominik Csapak
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

we want to add something to it that needs access to the
proxmox_backup::api2 stuff, so it cannot live in a sub crate

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 Cargo.toml                                     |  1 -
 Makefile                                       |  2 --
 debian/proxmox-backup-debug.bash-completion    |  1 -
 debian/proxmox-backup-server.bash-completion   |  1 +
 proxmox-backup-debug/Cargo.toml                | 18 ------------------
 .../main.rs => src/bin/proxmox-backup-debug.rs |  4 ++--
 .../bin/proxmox_backup_debug}/inspect.rs       |  0
 src/bin/proxmox_backup_debug/mod.rs            |  2 ++
 .../bin/proxmox_backup_debug}/recover.rs       |  0
 9 files changed, 5 insertions(+), 24 deletions(-)
 delete mode 100644 debian/proxmox-backup-debug.bash-completion
 delete mode 100644 proxmox-backup-debug/Cargo.toml
 rename proxmox-backup-debug/src/main.rs => src/bin/proxmox-backup-debug.rs (86%)
 rename {proxmox-backup-debug/src => src/bin/proxmox_backup_debug}/inspect.rs (100%)
 create mode 100644 src/bin/proxmox_backup_debug/mod.rs
 rename {proxmox-backup-debug/src => src/bin/proxmox_backup_debug}/recover.rs (100%)

diff --git a/Cargo.toml b/Cargo.toml
index cd7af590..b90b5021 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,7 +33,6 @@ members = [
 
     "proxmox-backup-banner",
     "proxmox-backup-client",
-    "proxmox-backup-debug",
     "proxmox-file-restore",
     "proxmox-restore-daemon",
     "pxar-bin",
diff --git a/Makefile b/Makefile
index 56fb5437..90723241 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,6 @@ SUBCRATES := \
 	pbs-tools \
 	proxmox-backup-banner \
 	proxmox-backup-client \
-	proxmox-backup-debug \
 	proxmox-file-restore \
 	proxmox-restore-daemon \
 	pxar-bin
@@ -180,7 +179,6 @@ $(COMPILED_BINS) $(COMPILEDIR)/dump-catalog-shell-cli $(COMPILEDIR)/docgen: .do-
 	    --bin proxmox-backup-banner \
 	    --package proxmox-backup-client \
 	    --bin proxmox-backup-client \
-	    --package proxmox-backup-debug \
 	    --bin proxmox-backup-debug \
 	    --package proxmox-file-restore \
 	    --bin proxmox-file-restore \
diff --git a/debian/proxmox-backup-debug.bash-completion b/debian/proxmox-backup-debug.bash-completion
deleted file mode 100644
index b977f953..00000000
--- a/debian/proxmox-backup-debug.bash-completion
+++ /dev/null
@@ -1 +0,0 @@
-debian/proxmox-backup-debug.bc proxmox-backup-debug
diff --git a/debian/proxmox-backup-server.bash-completion b/debian/proxmox-backup-server.bash-completion
index a2165699..d4257edd 100644
--- a/debian/proxmox-backup-server.bash-completion
+++ b/debian/proxmox-backup-server.bash-completion
@@ -1,4 +1,5 @@
 debian/proxmox-backup-manager.bc proxmox-backup-manager
+debian/proxmox-backup-debug.bc proxmox-backup-debug
 debian/proxmox-tape.bc proxmox-tape
 debian/pmtx.bc pmtx
 debian/pmt.bc pmt
diff --git a/proxmox-backup-debug/Cargo.toml b/proxmox-backup-debug/Cargo.toml
deleted file mode 100644
index 21b3cc2f..00000000
--- a/proxmox-backup-debug/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "proxmox-backup-debug"
-version = "0.1.0"
-authors = ["Proxmox Support Team <support@proxmox.com>"]
-edition = "2018"
-
-[dependencies]
-anyhow = "1.0"
-walkdir = "2"
-serde_json = "1.0"
-
-proxmox = { version = "0.13.3", features = [ "api-macro", "cli" ] }
-
-pbs-config = { path = "../pbs-config" }
-pbs-client = { path = "../pbs-client" }
-pbs-datastore = { path = "../pbs-datastore" }
-pbs-runtime = { path = "../pbs-runtime" }
-pbs-tools = { path = "../pbs-tools" }
diff --git a/proxmox-backup-debug/src/main.rs b/src/bin/proxmox-backup-debug.rs
similarity index 86%
rename from proxmox-backup-debug/src/main.rs
rename to src/bin/proxmox-backup-debug.rs
index b768f57a..4d6164ef 100644
--- a/proxmox-backup-debug/src/main.rs
+++ b/src/bin/proxmox-backup-debug.rs
@@ -1,7 +1,7 @@
 use proxmox::api::cli::{run_cli_command, CliCommandMap, CliEnvironment};
 
-mod inspect;
-mod recover;
+mod proxmox_backup_debug;
+use proxmox_backup_debug::*;
 
 fn main() {
     let cmd_def = CliCommandMap::new()
diff --git a/proxmox-backup-debug/src/inspect.rs b/src/bin/proxmox_backup_debug/inspect.rs
similarity index 100%
rename from proxmox-backup-debug/src/inspect.rs
rename to src/bin/proxmox_backup_debug/inspect.rs
diff --git a/src/bin/proxmox_backup_debug/mod.rs b/src/bin/proxmox_backup_debug/mod.rs
new file mode 100644
index 00000000..bbaca751
--- /dev/null
+++ b/src/bin/proxmox_backup_debug/mod.rs
@@ -0,0 +1,2 @@
+pub mod inspect;
+pub mod recover;
diff --git a/proxmox-backup-debug/src/recover.rs b/src/bin/proxmox_backup_debug/recover.rs
similarity index 100%
rename from proxmox-backup-debug/src/recover.rs
rename to src/bin/proxmox_backup_debug/recover.rs
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 3/7] proxmox-backup-debug: add 'api' subcommands
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 1/7] server: refactor abort_local_worker Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 2/7] move proxmox-backup-debug back to main crate Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 4/7] api2: add missing token list match_all property Dominik Csapak
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

this provides some generic api call mechanisms like pvesh/pmgsh.
by default it uses the https api on localhost (creating a token
if called as root, else requesting the root@pam password interactively)

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 an
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)

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/bin/proxmox-backup-debug.rs     |  17 +-
 src/bin/proxmox_backup_debug/api.rs | 503 ++++++++++++++++++++++++++++
 src/bin/proxmox_backup_debug/mod.rs |   1 +
 3 files changed, 518 insertions(+), 3 deletions(-)
 create mode 100644 src/bin/proxmox_backup_debug/api.rs

diff --git a/src/bin/proxmox-backup-debug.rs b/src/bin/proxmox-backup-debug.rs
index 4d6164ef..0ef37525 100644
--- a/src/bin/proxmox-backup-debug.rs
+++ b/src/bin/proxmox-backup-debug.rs
@@ -1,4 +1,7 @@
-use proxmox::api::cli::{run_cli_command, CliCommandMap, CliEnvironment};
+use proxmox::api::{
+    cli::{run_cli_command, CliCommandMap, CliEnvironment},
+    RpcEnvironment,
+};
 
 mod proxmox_backup_debug;
 use proxmox_backup_debug::*;
@@ -6,8 +9,16 @@ use proxmox_backup_debug::*;
 fn main() {
     let cmd_def = CliCommandMap::new()
         .insert("inspect", inspect::inspect_commands())
-        .insert("recover", recover::recover_commands());
+        .insert("recover", recover::recover_commands())
+        .insert("api", api::api_commands());
+
+    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)));
 
-    let rpcenv = CliEnvironment::new();
     run_cli_command(cmd_def, rpcenv, Some(|future| pbs_runtime::main(future)));
 }
diff --git a/src/bin/proxmox_backup_debug/api.rs b/src/bin/proxmox_backup_debug/api.rs
new file mode 100644
index 00000000..0292e628
--- /dev/null
+++ b/src/bin/proxmox_backup_debug/api.rs
@@ -0,0 +1,503 @@
+use anyhow::{bail, format_err, Error};
+use futures::FutureExt;
+use hyper::Method;
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+use tokio::signal::unix::{signal, SignalKind};
+
+use std::collections::HashMap;
+
+use proxmox::api::{
+    api,
+    cli::*,
+    format::DocumentationFormat,
+    schema::{parse_parameter_strings, ApiType, ParameterSchema, Schema},
+    ApiHandler, ApiMethod, RpcEnvironment, SubRoute,
+};
+
+use pbs_api_types::{PROXMOX_UPID_REGEX, UPID};
+use pbs_client::{connect_to_localhost, view_task_result};
+use proxmox_rest_server::normalize_uri_path;
+
+const PROG_NAME: &str = "proxmox-backup-debug api";
+const URL_ASCIISET: percent_encoding::AsciiSet = percent_encoding::NON_ALPHANUMERIC.remove(b'/');
+
+macro_rules! complete_api_path {
+    ($capability:expr) => {
+        |complete_me: &str, _map: &HashMap<String, String>| {
+            pbs_runtime::block_on(async { complete_api_path_do(complete_me, $capability).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 (path, components) = normalize_uri_path(&path)?;
+
+    let info = &proxmox_backup::api2::ROUTER
+        .find_route(&components, &mut HashMap::new())
+        .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 list = call_api("get", &path, rpcenv, None).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: &str,
+    path: &str,
+) -> Result<(&'static ApiMethod, HashMap<String, String>), Error> {
+    let method = match method {
+        "get" => Method::GET,
+        "set" => Method::PUT,
+        "create" => Method::POST,
+        "delete" => Method::DELETE,
+        _ => unreachable!(),
+    };
+    let mut uri_param = HashMap::new();
+    let (path, components) = 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: Option<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()));
+    }
+
+    let param = param.unwrap_or(json!({}));
+
+    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(&param_list, schema, true)?;
+
+    Ok(params)
+}
+
+fn use_http_client() -> bool {
+    match std::env::var("PROXMOX_DEBUG_API_CODE") {
+        Ok(var) => var != "1",
+        _ => true,
+    }
+}
+
+async fn call_api(
+    method: &str,
+    path: &str,
+    rpcenv: &mut dyn RpcEnvironment,
+    params: Option<Value>,
+) -> Result<Value, Error> {
+    if use_http_client() {
+        return call_api_http(method, path, params).await;
+    }
+
+    let (method, uri_param) = get_api_method(method, path)?;
+    let params = merge_parameters(uri_param, params, method.parameters)?;
+
+    call_api_code(method, rpcenv, params).await
+}
+
+async fn call_api_http(method: &str, path: &str, params: Option<Value>) -> Result<Value, Error> {
+    let mut client = connect_to_localhost()?;
+
+    let path = format!(
+        "api2/json/{}",
+        percent_encoding::utf8_percent_encode(path, &URL_ASCIISET)
+    );
+
+    match method {
+        "get" => client.get(&path, params).await,
+        "create" => client.post(&path, params).await,
+        "set" => client.put(&path, params).await,
+        "delete" => client.delete(&path, params).await,
+        _ => unreachable!(),
+    }
+    .map(|mut res| res["data"].take())
+}
+
+async fn call_api_code(
+    method: &'static ApiMethod,
+    rpcenv: &mut dyn RpcEnvironment,
+    params: Value,
+) -> Result<Value, Error> {
+    if !method.protected {
+        // drop privileges if we call non-protected code directly
+        let backup_user = pbs_config::backup_user()?;
+        nix::unistd::setgid(backup_user.gid)?;
+        nix::unistd::setuid(backup_user.uid)?;
+    }
+    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 handle_worker(upid_str: &str) -> Result<(), Error> {
+    let upid: UPID = upid_str.parse()?;
+    let mut signal_stream = signal(SignalKind::interrupt())?;
+    let abort_future = async move {
+        while signal_stream.recv().await.is_some() {
+            println!("got shutdown request (SIGINT)");
+            proxmox_backup::server::abort_local_worker(upid.clone());
+        }
+        Ok::<_, Error>(())
+    };
+
+    let result_future = proxmox_backup::server::wait_for_local_worker(upid_str);
+
+    futures::select! {
+        result = result_future.fuse() => result?,
+        abort = abort_future.fuse() => abort?,
+    };
+
+    Ok(())
+}
+
+async fn call_api_and_format_result(
+    method: String,
+    path: String,
+    mut param: Value,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    let mut output_format = extract_output_format(&mut param);
+    let mut result = call_api(&method, &path, rpcenv, Some(param)).await?;
+
+    if let Some(upid) = result.as_str() {
+        if PROXMOX_UPID_REGEX.is_match(upid) {
+            if use_http_client() {
+                let mut client = connect_to_localhost()?;
+                view_task_result(&mut client, json!({ "data": upid }), &output_format).await?;
+                return Ok(());
+            }
+
+            handle_worker(upid).await?;
+
+            if output_format == "text" {
+                return Ok(());
+            }
+        }
+    }
+
+    let (method, _) = get_api_method(&method, &path)?;
+    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: {
+            method: {
+                type: String,
+                description: "The Method",
+            },
+            "api-path": {
+                type: String,
+                description: "API path.",
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        },
+    },
+)]
+/// Call API on <api-path>
+async fn api_call(
+    method: String,
+    api_path: String,
+    param: Value,
+    rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+    call_api_and_format_result(method, api_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 (info, uri_params) = match get_api_method(command, &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, _) = 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 = &[("get", 'r'), ("set", 'w'), ("create", 'c'), ("delete", 'd')];
+
+        for (method, c) in cap_list {
+            if get_api_method(method, &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(())
+}
+
+pub fn api_commands() -> CommandLineInterface {
+    let cmd_def = CliCommandMap::new()
+        .insert(
+            "get",
+            CliCommand::new(&API_METHOD_API_CALL)
+                .fixed_param("method", "get".to_string())
+                .arg_param(&["api-path"])
+                .completion_cb("api-path", complete_api_path!(Some("r"))),
+        )
+        .insert(
+            "set",
+            CliCommand::new(&API_METHOD_API_CALL)
+                .fixed_param("method", "set".to_string())
+                .arg_param(&["api-path"])
+                .completion_cb("api-path", complete_api_path!(Some("w"))),
+        )
+        .insert(
+            "create",
+            CliCommand::new(&API_METHOD_API_CALL)
+                .fixed_param("method", "create".to_string())
+                .arg_param(&["api-path"])
+                .completion_cb("api-path", complete_api_path!(Some("c"))),
+        )
+        .insert(
+            "delete",
+            CliCommand::new(&API_METHOD_API_CALL)
+                .fixed_param("method", "delete".to_string())
+                .arg_param(&["api-path"])
+                .completion_cb("api-path", complete_api_path!(Some("d"))),
+        )
+        .insert(
+            "ls",
+            CliCommand::new(&API_METHOD_LS)
+                .arg_param(&["path"])
+                .completion_cb("path", complete_api_path!(Some("D"))),
+        )
+        .insert(
+            "usage",
+            CliCommand::new(&API_METHOD_USAGE)
+                .arg_param(&["path"])
+                .completion_cb("path", complete_api_path!(None)),
+        );
+
+    cmd_def.into()
+}
diff --git a/src/bin/proxmox_backup_debug/mod.rs b/src/bin/proxmox_backup_debug/mod.rs
index bbaca751..a3a526dd 100644
--- a/src/bin/proxmox_backup_debug/mod.rs
+++ b/src/bin/proxmox_backup_debug/mod.rs
@@ -1,2 +1,3 @@
 pub mod inspect;
 pub mod recover;
+pub mod api;
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 4/7] api2: add missing token list match_all property
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
                   ` (2 preceding siblings ...)
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 3/7] proxmox-backup-debug: add 'api' subcommands Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 5/7] api2: make some workers log on CLI Dominik Csapak
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 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                | 66 +++++++++++++++++---------
 src/bin/proxmox_backup_manager/user.rs |  6 +--
 2 files changed, 47 insertions(+), 25 deletions(-)

diff --git a/src/api2/access/user.rs b/src/api2/access/user.rs
index 75071cf1..1d866214 100644
--- a/src/api2/access/user.rs
+++ b/src/api2/access/user.rs
@@ -395,7 +395,7 @@ pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error>
             userid: {
                 type: Userid,
             },
-            tokenname: {
+            "token-name": {
                 type: Tokenname,
             },
         },
@@ -411,14 +411,14 @@ pub fn delete_user(userid: Userid, digest: Option<String>) -> Result<(), Error>
 /// Read user's API token metadata
 pub fn read_token(
     userid: Userid,
-    tokenname: Tokenname,
+    token_name: Tokenname,
     _info: &ApiMethod,
     mut rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<ApiToken, Error> {
 
     let (config, digest) = pbs_config::user::config()?;
 
-    let tokenid = Authid::from((userid, Some(tokenname)));
+    let tokenid = Authid::from((userid, Some(token_name)));
 
     rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
     config.lookup("token", &tokenid.to_string())
@@ -431,7 +431,7 @@ pub fn read_token(
             userid: {
                 type: Userid,
             },
-            tokenname: {
+            "token-name": {
                 type: Tokenname,
             },
             comment: {
@@ -475,7 +475,7 @@ pub fn read_token(
 /// Generate a new API token with given metadata
 pub fn generate_token(
     userid: Userid,
-    tokenname: Tokenname,
+    token_name: Tokenname,
     comment: Option<String>,
     enable: Option<bool>,
     expire: Option<i64>,
@@ -491,11 +491,11 @@ pub fn generate_token(
         crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
     }
 
-    let tokenid = Authid::from((userid.clone(), Some(tokenname.clone())));
+    let tokenid = Authid::from((userid.clone(), Some(token_name.clone())));
     let tokenid_string = tokenid.to_string();
 
     if config.sections.get(&tokenid_string).is_some() {
-        bail!("token '{}' for user '{}' already exists.", tokenname.as_str(), userid);
+        bail!("token '{}' for user '{}' already exists.", token_name.as_str(), userid);
     }
 
     let secret = format!("{:x}", proxmox::tools::uuid::Uuid::generate());
@@ -525,7 +525,7 @@ pub fn generate_token(
             userid: {
                 type: Userid,
             },
-            tokenname: {
+            "token-name": {
                 type: Tokenname,
             },
             comment: {
@@ -556,7 +556,7 @@ pub fn generate_token(
 /// Update user's API token metadata
 pub fn update_token(
     userid: Userid,
-    tokenname: Tokenname,
+    token_name: Tokenname,
     comment: Option<String>,
     enable: Option<bool>,
     expire: Option<i64>,
@@ -572,7 +572,7 @@ pub fn update_token(
         crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
     }
 
-    let tokenid = Authid::from((userid, Some(tokenname)));
+    let tokenid = Authid::from((userid, Some(token_name)));
     let tokenid_string = tokenid.to_string();
 
     let mut data: ApiToken = config.lookup("token", &tokenid_string)?;
@@ -608,7 +608,7 @@ pub fn update_token(
             userid: {
                 type: Userid,
             },
-            tokenname: {
+            "token-name": {
                 type: Tokenname,
             },
             digest: {
@@ -627,7 +627,7 @@ pub fn update_token(
 /// Delete a user's API token
 pub fn delete_token(
     userid: Userid,
-    tokenname: Tokenname,
+    token_name: Tokenname,
     digest: Option<String>,
 ) -> Result<(), Error> {
 
@@ -640,12 +640,12 @@ pub fn delete_token(
         crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
     }
 
-    let tokenid = Authid::from((userid.clone(), Some(tokenname.clone())));
+    let tokenid = Authid::from((userid.clone(), Some(token_name.clone())));
     let tokenid_string = tokenid.to_string();
 
     match config.sections.get(&tokenid_string) {
         Some(_) => { config.sections.remove(&tokenid_string); },
-        None => bail!("token '{}' of user '{}' does not exist.", tokenname.as_str(), userid),
+        None => bail!("token '{}' of user '{}' does not exist.", token_name.as_str(), userid),
     }
 
     token_shadow::delete_secret(&tokenid)?;
@@ -655,6 +655,22 @@ pub fn delete_token(
     Ok(())
 }
 
+#[api(
+    properties: {
+        "token-name": { type: Tokenname },
+        token: { type: ApiToken },
+    }
+)]
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all="kebab-case")]
+/// A Token Entry that contains the token-name
+pub struct TokenApiEntry {
+    /// The Token name
+    pub token_name: Tokenname,
+    #[serde(flatten)]
+    pub token: ApiToken,
+}
+
 #[api(
     input: {
         properties: {
@@ -666,7 +682,7 @@ pub fn delete_token(
     returns: {
         description: "List user's API tokens (with config digest).",
         type: Array,
-        items: { type: ApiToken },
+        items: { type: TokenApiEntry },
     },
     access: {
         permission: &Permission::Or(&[
@@ -680,7 +696,7 @@ pub fn list_tokens(
     userid: Userid,
     _info: &ApiMethod,
     mut rpcenv: &mut dyn RpcEnvironment,
-) -> Result<Vec<ApiToken>, Error> {
+) -> Result<Vec<TokenApiEntry>, Error> {
 
     let (config, digest) = pbs_config::user::config()?;
 
@@ -688,15 +704,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 token_name = token.tokenid.tokenname().unwrap().to_owned();
+            Some(TokenApiEntry {
+                token_name,
+                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()
@@ -707,7 +729,7 @@ const TOKEN_ITEM_ROUTER: Router = Router::new()
 
 const TOKEN_ROUTER: Router = Router::new()
     .get(&API_METHOD_LIST_TOKENS)
-    .match_all("tokenname", &TOKEN_ITEM_ROUTER);
+    .match_all("token-name", &TOKEN_ITEM_ROUTER);
 
 const USER_SUBDIRS: SubdirMap = &[
     ("token", &TOKEN_ROUTER),
diff --git a/src/bin/proxmox_backup_manager/user.rs b/src/bin/proxmox_backup_manager/user.rs
index 9681ba79..cff66ce7 100644
--- a/src/bin/proxmox_backup_manager/user.rs
+++ b/src/bin/proxmox_backup_manager/user.rs
@@ -198,15 +198,15 @@ pub fn user_commands() -> CommandLineInterface {
         .insert(
             "generate-token",
             CliCommand::new(&api2::access::user::API_METHOD_GENERATE_TOKEN)
-                .arg_param(&["userid", "tokenname"])
+                .arg_param(&["userid", "token-name"])
                 .completion_cb("userid", pbs_config::user::complete_userid)
         )
         .insert(
             "delete-token",
             CliCommand::new(&api2::access::user::API_METHOD_DELETE_TOKEN)
-                .arg_param(&["userid", "tokenname"])
+                .arg_param(&["userid", "token-name"])
                 .completion_cb("userid", pbs_config::user::complete_userid)
-                .completion_cb("tokenname", pbs_config::user::complete_token_name)
+                .completion_cb("token-name", pbs_config::user::complete_token_name)
         )
         .insert(
             "permissions",
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 5/7] api2: make some workers log on CLI
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
                   ` (3 preceding siblings ...)
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 4/7] api2: add missing token list match_all property Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 6/7] docs: add proxmox-backup-debug to the list of command line tools Dominik Csapak
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

some workers did not log when called via cli

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 src/api2/admin/datastore.rs     | 4 +++-
 src/api2/admin/sync.rs          | 6 ++++--
 src/api2/admin/verify.rs        | 5 +++--
 src/api2/config/datastore.rs    | 5 +++--
 src/api2/pull.rs                | 3 ++-
 src/api2/tape/backup.rs         | 7 +++++--
 src/bin/proxmox-backup-proxy.rs | 6 +++---
 src/server/verify_job.rs        | 3 ++-
 8 files changed, 25 insertions(+), 14 deletions(-)

diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs
index 20aca6fb..0b14dfbf 100644
--- a/src/api2/admin/datastore.rs
+++ b/src/api2/admin/datastore.rs
@@ -952,11 +952,13 @@ pub fn prune_datastore(
 
     let datastore = DataStore::lookup_datastore(&store)?;
 
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
     let upid_str = WorkerTask::new_thread(
         "prune",
         Some(store.clone()),
         auth_id.clone(),
-        false,
+        to_stdout,
         move |worker| crate::server::prune_datastore(
             worker.clone(),
             auth_id,
diff --git a/src/api2/admin/sync.rs b/src/api2/admin/sync.rs
index 07f268b5..94990adb 100644
--- a/src/api2/admin/sync.rs
+++ b/src/api2/admin/sync.rs
@@ -3,7 +3,7 @@
 use anyhow::{bail, format_err, Error};
 use serde_json::Value;
 
-use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment};
+use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment, RpcEnvironmentType};
 use proxmox::api::router::SubdirMap;
 use proxmox::{list_subdirs_api_method, sortable};
 
@@ -120,7 +120,9 @@ pub fn run_sync_job(
 
     let job = Job::new("syncjob", &id)?;
 
-    let upid_str = do_sync_job(job, sync_job, &auth_id, None)?;
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
+    let upid_str = do_sync_job(job, sync_job, &auth_id, None, to_stdout)?;
 
     Ok(upid_str)
 }
diff --git a/src/api2/admin/verify.rs b/src/api2/admin/verify.rs
index a5e5c83f..6bdfdede 100644
--- a/src/api2/admin/verify.rs
+++ b/src/api2/admin/verify.rs
@@ -5,7 +5,7 @@ use serde_json::Value;
 
 use proxmox::api::router::SubdirMap;
 use proxmox::{list_subdirs_api_method, sortable};
-use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment};
+use proxmox::api::{api, ApiMethod, Permission, Router, RpcEnvironment, RpcEnvironmentType};
 
 use pbs_api_types::{
     VerificationJobConfig, VerificationJobStatus, JOB_ID_SCHEMA, Authid,
@@ -117,8 +117,9 @@ pub fn run_verification_job(
     user_info.check_privs(&auth_id, &["datastore", &verification_job.store], PRIV_DATASTORE_VERIFY, true)?;
 
     let job = Job::new("verificationjob", &id)?;
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
 
-    let upid_str = do_verification_job(job, verification_job, &auth_id, None)?;
+    let upid_str = do_verification_job(job, verification_job, &auth_id, None, to_stdout)?;
 
     Ok(upid_str)
 }
diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs
index 6301ffaa..c6036fc3 100644
--- a/src/api2/config/datastore.rs
+++ b/src/api2/config/datastore.rs
@@ -4,7 +4,7 @@ use anyhow::{bail, Error};
 use serde_json::Value;
 use ::serde::{Deserialize, Serialize};
 
-use proxmox::api::{api, Router, RpcEnvironment, Permission};
+use proxmox::api::{api, Router, RpcEnvironment, RpcEnvironmentType, Permission};
 use proxmox::api::section_config::SectionConfigData;
 use proxmox::api::schema::{ApiType, parse_property_string};
 
@@ -114,12 +114,13 @@ pub fn create_datastore(
     }
 
     let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
 
     WorkerTask::new_thread(
         "create-datastore",
         Some(config.name.to_string()),
         auth_id,
-        false,
+        to_stdout,
         move |worker| do_create_datastore(lock, section_config, config, Some(&worker)),
     )
 }
diff --git a/src/api2/pull.rs b/src/api2/pull.rs
index d7b155a1..1eb86ea3 100644
--- a/src/api2/pull.rs
+++ b/src/api2/pull.rs
@@ -66,6 +66,7 @@ pub fn do_sync_job(
     sync_job: SyncJobConfig,
     auth_id: &Authid,
     schedule: Option<String>,
+    to_stdout: bool,
 ) -> Result<String, Error> {
 
     let job_id = format!("{}:{}:{}:{}",
@@ -81,7 +82,7 @@ pub fn do_sync_job(
         &worker_type,
         Some(job_id.clone()),
         auth_id.clone(),
-        false,
+        to_stdout,
         move |worker| async move {
 
             job.start(&worker.upid().to_string())?;
diff --git a/src/api2/tape/backup.rs b/src/api2/tape/backup.rs
index 9342eb59..6b533820 100644
--- a/src/api2/tape/backup.rs
+++ b/src/api2/tape/backup.rs
@@ -164,6 +164,7 @@ pub fn do_tape_backup_job(
     setup: TapeBackupJobSetup,
     auth_id: &Authid,
     schedule: Option<String>,
+    to_stdout: bool,
 ) -> Result<String, Error> {
 
     let job_id = format!("{}:{}:{}:{}",
@@ -195,7 +196,7 @@ pub fn do_tape_backup_job(
         &worker_type,
         Some(job_id.clone()),
         auth_id.clone(),
-        false,
+        to_stdout,
         move |worker| {
             job.start(&worker.upid().to_string())?;
             let mut drive_lock = drive_lock;
@@ -307,7 +308,9 @@ pub fn run_tape_backup_job(
 
     let job = Job::new("tape-backup-job", &id)?;
 
-    let upid_str = do_tape_backup_job(job, backup_job.setup, &auth_id, None)?;
+    let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
+
+    let upid_str = do_tape_backup_job(job, backup_job.setup, &auth_id, None, to_stdout)?;
 
     Ok(upid_str)
 }
diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
index dadce847..6734525b 100644
--- a/src/bin/proxmox-backup-proxy.rs
+++ b/src/bin/proxmox-backup-proxy.rs
@@ -638,7 +638,7 @@ async fn schedule_datastore_sync_jobs() {
             };
 
             let auth_id = Authid::root_auth_id().clone();
-            if let Err(err) = do_sync_job(job, job_config, &auth_id, Some(event_str)) {
+            if let Err(err) = do_sync_job(job, job_config, &auth_id, Some(event_str), false) {
                 eprintln!("unable to start datastore sync job {} - {}", &job_id, err);
             }
         };
@@ -674,7 +674,7 @@ async fn schedule_datastore_verify_jobs() {
                 Ok(job) => job,
                 Err(_) => continue, // could not get lock
             };
-            if let Err(err) = do_verification_job(job, job_config, &auth_id, Some(event_str)) {
+            if let Err(err) = do_verification_job(job, job_config, &auth_id, Some(event_str), false) {
                 eprintln!("unable to start datastore verification job {} - {}", &job_id, err);
             }
         };
@@ -710,7 +710,7 @@ async fn schedule_tape_backup_jobs() {
                 Ok(job) => job,
                 Err(_) => continue, // could not get lock
             };
-            if let Err(err) = do_tape_backup_job(job, job_config.setup, &auth_id, Some(event_str)) {
+            if let Err(err) = do_tape_backup_job(job, job_config.setup, &auth_id, Some(event_str), false) {
                 eprintln!("unable to start tape backup job {} - {}", &job_id, err);
             }
         };
diff --git a/src/server/verify_job.rs b/src/server/verify_job.rs
index 968e6bd4..7f6d73ff 100644
--- a/src/server/verify_job.rs
+++ b/src/server/verify_job.rs
@@ -19,6 +19,7 @@ pub fn do_verification_job(
     verification_job: VerificationJobConfig,
     auth_id: &Authid,
     schedule: Option<String>,
+    to_stdout: bool,
 ) -> Result<String, Error> {
 
     let datastore = DataStore::lookup_datastore(&verification_job.store)?;
@@ -36,7 +37,7 @@ pub fn do_verification_job(
         &worker_type,
         Some(job_id.clone()),
         auth_id.clone(),
-        false,
+        to_stdout,
         move |worker| {
             job.start(&worker.upid().to_string())?;
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 6/7] docs: add proxmox-backup-debug to the list of command line tools
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
                   ` (4 preceding siblings ...)
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 5/7] api2: make some workers log on CLI Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 7/7] docs: proxmox-backup-debug: add info about the 'api' subcommand Dominik Csapak
  2021-09-21 13:45 ` [pbs-devel] applied-series: [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Thomas Lamprecht
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 docs/command-line-tools.rst | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/docs/command-line-tools.rst b/docs/command-line-tools.rst
index bf3a92cc..37606f46 100644
--- a/docs/command-line-tools.rst
+++ b/docs/command-line-tools.rst
@@ -21,3 +21,7 @@ Command Line Tools
 
 .. include:: pxar/description.rst
 
+``proxmox-backup-debug``
+~~~~~~~~
+
+.. include:: proxmox-backup-debug/description.rst
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v4 7/7] docs: proxmox-backup-debug: add info about the 'api' subcommand
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
                   ` (5 preceding siblings ...)
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 6/7] docs: add proxmox-backup-debug to the list of command line tools Dominik Csapak
@ 2021-09-21 10:11 ` Dominik Csapak
  2021-09-21 13:45 ` [pbs-devel] applied-series: [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Thomas Lamprecht
  7 siblings, 0 replies; 9+ messages in thread
From: Dominik Csapak @ 2021-09-21 10:11 UTC (permalink / raw)
  To: pbs-devel

and mention PROXMOX_DEBUG_API_CODE and that its dangerous.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 docs/proxmox-backup-debug/description.rst | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/docs/proxmox-backup-debug/description.rst b/docs/proxmox-backup-debug/description.rst
index 0b263e8c..2e5f35fe 100644
--- a/docs/proxmox-backup-debug/description.rst
+++ b/docs/proxmox-backup-debug/description.rst
@@ -1,3 +1,14 @@
 Implements debugging functionality to inspect Proxmox Backup datastore
 files, verify the integrity of chunks.
 
+Also contains an 'api' subcommand where arbitrary api paths can be called
+(get/create/set/delete) as well as display their parameters (usage) and
+their child-links (ls).
+
+By default, it connects to the proxmox-backup-proxy on localhost via https,
+but by setting the environment variable `PROXMOX_DEBUG_API_CODE` to `1` the
+tool directly calls the corresponding code.
+
+.. WARNING:: Using `PROXMOX_DEBUG_API_CODE` can be dangerous and is only intended
+   for debugging purposes. It is not intended for use on a production system.
+
-- 
2.30.2





^ permalink raw reply	[flat|nested] 9+ messages in thread

* [pbs-devel] applied-series: [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands
  2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
                   ` (6 preceding siblings ...)
  2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 7/7] docs: proxmox-backup-debug: add info about the 'api' subcommand Dominik Csapak
@ 2021-09-21 13:45 ` Thomas Lamprecht
  7 siblings, 0 replies; 9+ messages in thread
From: Thomas Lamprecht @ 2021-09-21 13:45 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Dominik Csapak

On 21.09.21 12:11, Dominik Csapak wrote:
> this series is a successor to my previous 'pbs-shell' series [0]
> code wise its mostly the same but now a subcommand of 'proxmox-backup-debug'
> 
> changes from v3:
> * rebase on master (use normalize_path_uri from proxmox_rest_server)
> * fix worker task log output (upid was not wrapped in {data:upid})
> 
> changes from v2:
> * now subcommand api of proxmox-backup-debug
> * use http api on localhost by default
> * add PROXMOX_DEBUG_API_CODE env variable to execute code directly
> * use a macro for the completion helper calls
> * combine get/post/put/delete requests into one and use 'fixed_param'
> * drop priviliges to the backup user when executing 'non-protected' api
>   calls directly
> 
> changes from v1:
> * rebase on master
> * rename 'path' parameter to 'api-path' since it clashed with some
>   api calls that had a 'path variable'
> * better handle workers:
>   we must always wait for the local workers and do not need to
>   print their logs, since most workers print to stdout if the
>   rpcenv type is CLI. Also catch Ctrl+C and try to abort the worker
> 
> 0: https://lists.proxmox.com/pipermail/pbs-devel/2021-September/003974.html
> 
> Dominik Csapak (7):
>   server: refactor abort_local_worker
>   move proxmox-backup-debug back to main crate
>   proxmox-backup-debug: add 'api' subcommands
>   api2: add missing token list match_all property
>   api2: make some workers log on CLI
>   docs: add proxmox-backup-debug to the list of command line tools
>   docs: proxmox-backup-debug: add info about the 'api' subcommand

applied, thanks!

for `ls` I made the path optional with "/" as default in a followup, irked a bit
in pvesh since quite a bit (but not yet done there)




^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-09-21 13:46 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-21 10:11 [pbs-devel] [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 1/7] server: refactor abort_local_worker Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 2/7] move proxmox-backup-debug back to main crate Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 3/7] proxmox-backup-debug: add 'api' subcommands Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 4/7] api2: add missing token list match_all property Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 5/7] api2: make some workers log on CLI Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 6/7] docs: add proxmox-backup-debug to the list of command line tools Dominik Csapak
2021-09-21 10:11 ` [pbs-devel] [PATCH proxmox-backup v4 7/7] docs: proxmox-backup-debug: add info about the 'api' subcommand Dominik Csapak
2021-09-21 13:45 ` [pbs-devel] applied-series: [PATCH proxmox-backup v4 0/7] add 'proxmox-backup-debug api' commands Thomas Lamprecht

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal