From: Daniel Tschlatscher <d.tschlatscher@proxmox.com>
To: pve-devel@lists.proxmox.com, pbs-devel@lists.proxmox.com,
pmg-devel@lists.proxmox.com
Subject: [pmg-devel] [PATCH proxmox-backup v2 1/7] make tasklog downloadable in the backup server backend
Date: Wed, 7 Sep 2022 10:56:27 +0200 [thread overview]
Message-ID: <20220907085633.89113-2-d.tschlatscher@proxmox.com> (raw)
In-Reply-To: <20220907085633.89113-1-d.tschlatscher@proxmox.com>
The API call for getting the tasklog now returns a file stream when
the URL parameter 'limit' is set to 0, in accordance with the changes
in the PMG and PVE backends.
To make this work I had to use one of the lower level apimethod types
from the proxmox-router. Therefore I also had to change declarations
for the API routing and the corresponding Object Schemas.
If the parameter 'limit' is not set to 0 the API should behave the
same as before.
Signed-off-by: Daniel Tschlatscher <d.tschlatscher@proxmox.com>
---
src/api2/node/tasks.rs | 159 ++++++++++++++++++++++++++---------------
1 file changed, 101 insertions(+), 58 deletions(-)
diff --git a/src/api2/node/tasks.rs b/src/api2/node/tasks.rs
index cbd0894e..99600bf4 100644
--- a/src/api2/node/tasks.rs
+++ b/src/api2/node/tasks.rs
@@ -1,11 +1,17 @@
use std::fs::File;
use std::io::{BufRead, BufReader};
+use std::path::PathBuf;
use anyhow::{bail, Error};
+use futures::FutureExt;
+use http::{Response, StatusCode, header};
+use http::request::Parts;
+use hyper::Body;
+use proxmox_async::stream::AsyncReaderStream;
use serde_json::{json, Value};
-use proxmox_router::{list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap};
-use proxmox_schema::api;
+use proxmox_router::{list_subdirs_api_method, Permission, Router, RpcEnvironment, SubdirMap, ApiMethod, ApiHandler, ApiResponseFuture};
+use proxmox_schema::{api, Schema, IntegerSchema, BooleanSchema, ObjectSchema};
use proxmox_sys::sortable;
use pbs_api_types::{
@@ -19,6 +25,20 @@ use crate::api2::pull::check_pull_privs;
use pbs_config::CachedUserInfo;
use proxmox_rest_server::{upid_log_path, upid_read_status, TaskListInfoIterator, TaskState};
+pub const START_PARAM_SCHEMA: Schema =
+ IntegerSchema::new("Start at this line when reading the tasklog")
+ .minimum(0)
+ .schema();
+
+pub const LIMIT_PARAM_SCHEMA: Schema =
+ IntegerSchema::new("The amount of lines to read from the tasklog")
+ .minimum(0)
+ .schema();
+
+pub const TEST_STATUS_PARAM_SCHEMA: Schema =
+ BooleanSchema::new("Test task status, and set result attribute \"active\" accordingly.")
+ .schema();
+
// matches respective job execution privileges
fn check_job_privs(auth_id: &Authid, user_info: &CachedUserInfo, upid: &UPID) -> Result<(), Error> {
match (upid.worker_type.as_str(), &upid.worker_id) {
@@ -268,58 +288,88 @@ fn extract_upid(param: &Value) -> Result<UPID, Error> {
upid_str.parse::<UPID>()
}
-#[api(
- input: {
- properties: {
- node: {
- schema: NODE_SCHEMA,
- },
- upid: {
- schema: UPID_SCHEMA,
- },
- "test-status": {
- type: bool,
- optional: true,
- description: "Test task status, and set result attribute \"active\" accordingly.",
- },
- start: {
- type: u64,
- optional: true,
- description: "Start at this line.",
- default: 0,
- },
- limit: {
- type: u64,
- optional: true,
- description: "Only list this amount of lines.",
- default: 50,
- },
- },
- },
- access: {
- description: "Users can access their own tasks, or need Sys.Audit on /system/tasks.",
- permission: &Permission::Anybody,
- },
-)]
-/// Read task log.
-async fn read_task_log(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
- let upid = extract_upid(¶m)?;
-
- let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
-
- check_task_access(&auth_id, &upid)?;
-
- let test_status = param["test-status"].as_bool().unwrap_or(false);
-
- let start = param["start"].as_u64().unwrap_or(0);
- let mut limit = param["limit"].as_u64().unwrap_or(50);
-
- let mut count: u64 = 0;
+#[sortable]
+pub const API_METHOD_READ_TASK_LOG: ApiMethod = ApiMethod::new(
+ &ApiHandler::AsyncHttp(&download_task_log),
+ &ObjectSchema::new(
+ "Read the task log",
+ &sorted!([
+ ("node", false, &NODE_SCHEMA),
+ ("upid", false, &UPID_SCHEMA),
+ ("start", true, &START_PARAM_SCHEMA),
+ ("limit", true, &LIMIT_PARAM_SCHEMA),
+ ("test-status", true, &TEST_STATUS_PARAM_SCHEMA)
+ ]),
+ ),
+)
+.access(
+ Some("Users can access their own tasks, or need Sys.Audit on /system/tasks."),
+ &Permission::Anybody,
+);
+fn download_task_log(
+ _parts: Parts,
+ _req_body: Body,
+ param: Value,
+ _info: &ApiMethod,
+ rpcenv: Box<dyn RpcEnvironment>,
+) -> ApiResponseFuture {
+ async move {
+ let upid: UPID = extract_upid(¶m)?;
+ let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
+ check_task_access(&auth_id, &upid)?;
+
+ let test_status = param["test-status"].as_bool().unwrap_or(false);
+ let start = param["start"].as_u64().unwrap_or(0);
+ let limit = param["limit"].as_u64().unwrap_or(50);
+
+ let path = upid_log_path(&upid)?;
+
+ if limit == 0 {
+ let file = tokio::fs::File::open(path).await?;
+
+ let header_disp = format!("attachment; filename={}", &upid.to_string());
+
+ let stream = AsyncReaderStream::new(file);
+
+ Ok(Response::builder()
+ .status(StatusCode::OK)
+ .header(header::CONTENT_TYPE, "text/plain")
+ .header(header::CONTENT_DISPOSITION, &header_disp)
+ .body(Body::wrap_stream(stream))
+ .unwrap())
+ } else {
+ let (count, lines) = read_tasklog_lines(path, start, limit).unwrap();
+
+ let mut json = json!({
+ "data": lines,
+ "total": count,
+ "success": 1,
+ });
+
+ if test_status {
+ let active = proxmox_rest_server::worker_is_active(&upid).await?;
+
+ json["test-status"] = Value::from(active);
+ }
- let path = upid_log_path(&upid)?;
+ Ok(Response::builder()
+ .status(StatusCode::OK)
+ .header(header::CONTENT_TYPE, "application/json")
+ .body(Body::from(json.to_string()))
+ .unwrap())
+ }
+ }
+ .boxed()
+}
+fn read_tasklog_lines(
+ path: PathBuf,
+ start: u64,
+ mut limit: u64,
+) -> Result<(u64, Vec<Value>), Error> {
let file = File::open(path)?;
+ let mut count: u64 = 0;
let mut lines: Vec<Value> = vec![];
for line in BufReader::new(file).lines() {
@@ -344,14 +394,7 @@ async fn read_task_log(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<
}
}
- rpcenv["total"] = Value::from(count);
-
- if test_status {
- let active = proxmox_rest_server::worker_is_active(&upid).await?;
- rpcenv["active"] = Value::from(active);
- }
-
- Ok(json!(lines))
+ Ok((count, lines))
}
#[api(
--
2.30.2
next prev parent reply other threads:[~2022-09-07 9:06 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-07 8:56 [pmg-devel] [PATCH http/common/manager/wt/backup/pmg v2] fix: #3971 Tasklog download button Daniel Tschlatscher
2022-09-07 8:56 ` Daniel Tschlatscher [this message]
2022-09-07 8:56 ` [pmg-devel] [PATCH http-server v2 2/7] acknowledge content-disposition header Daniel Tschlatscher
2022-09-29 12:36 ` [pmg-devel] applied: [pve-devel] " Thomas Lamprecht
2022-09-07 8:56 ` [pmg-devel] [PATCH common v2 3/7] stream file method for PMG and PVE Daniel Tschlatscher
2022-09-07 8:56 ` [pmg-devel] [PATCH manager v2 4/7] revised task log API call for PVE Daniel Tschlatscher
2022-10-05 12:30 ` [pmg-devel] [pve-devel] " Thomas Lamprecht
2022-10-10 11:40 ` Daniel Tschlatscher
2022-10-10 13:10 ` Thomas Lamprecht
2022-09-07 8:56 ` [pmg-devel] [PATCH pmg-api v2 5/7] revised task log download function in the PMG backend Daniel Tschlatscher
2022-09-08 9:36 ` Stefan Sterz
2022-09-08 11:19 ` Daniel Tschlatscher
2022-09-07 8:56 ` [pmg-devel] [PATCH widget-toolkit v2 6/7] Source file download call in central function Daniel Tschlatscher
2022-09-07 8:56 ` [pmg-devel] [PATCH widget-toolkit v2 7/7] add task log download button in TaskViewer Daniel Tschlatscher
2022-09-08 9:40 ` [pmg-devel] [PATCH http/common/manager/wt/backup/pmg v2] fix: #3971 Tasklog download button Stefan Sterz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220907085633.89113-2-d.tschlatscher@proxmox.com \
--to=d.tschlatscher@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
--cc=pmg-devel@lists.proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox