* [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list
@ 2025-01-20 9:29 Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional Dominik Csapak
` (17 more replies)
0 siblings, 18 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
This series implements a remote task list and includes the remote
running tasks into the globabl running tasks list (if started through
the pdm or caught via caching).
for that i had to adapt the proxmox-yew-comp widget a bit so that
they're more customizable, as well as implement our usual task filters
in the task cache.
the first 2 yew-comp patches could be applied even if the rest of the
series is not, but the remaining patches depend on those for line
numbers etc.
a few things yet to implement
* proper task rendering for the task viewer title
* remote/node filter for remote task list.
I noticed that the task caching could be improved a bit, e.g. we discard
the cache when it's older than some 'max-age' but old (finished!) tasks
will never get updated, they could at most be purged from the cache.
IMHO it does not make sense to control the cache this way with a
'max-age' variable, maybe @Lukas can look over that again and see
if we can cache the tasks better here?
(note that we can't have much above ~1000 tasks in PVE since that
is the number where we purge older ones there. I think we could keep
older tasks for remotes and also just purge them if the number is above
some threshold, maybe a bit higher than what PVE has).
proxmox-yew-comp:
Dominik Csapak (7):
tasks: make date filter functional
tasks: load more tasks on end of list
utils: factor out task description into own function
running tasks: make TaskListItem renderer configurable
running tasks: make buttons configurable
tasks: make columns configurable
tasks: make the 'show task' action configurable
src/running_tasks.rs | 54 ++++++---
src/running_tasks_button.rs | 15 ++-
src/tasks.rs | 221 ++++++++++++++++++++++++++----------
src/utils.rs | 24 ++--
4 files changed, 232 insertions(+), 82 deletions(-)
datacenter-manager:
Dominik Csapak (9):
server: factor out task filters into `TaskFilters` type
server: task cache: skip remotes with errors on fetch
server: task cache: add filter options
server: task cache: reverse task order
pdm-client: export PveUpid
ui: refactor RemoteConfigPanel into own module
ui: remotes: add tasks to global remote panel
ui: register pve tasks
ui: also show running remote tasks in 'running tasks' list
lib/pdm-api-types/src/lib.rs | 81 ++++++-
lib/pdm-client/src/lib.rs | 2 +
server/src/api/nodes/tasks.rs | 80 ++-----
server/src/api/remote_tasks.rs | 10 +-
server/src/task_cache.rs | 77 +++++-
ui/src/lib.rs | 3 +
ui/src/main.rs | 25 +-
ui/src/main_menu.rs | 7 +-
ui/src/remotes/config.rs | 392 +++++++++++++++++++++++++++++++
ui/src/remotes/mod.rs | 413 +++------------------------------
ui/src/remotes/tasks.rs | 153 ++++++++++++
ui/src/tasks.rs | 101 ++++++++
ui/src/top_nav_bar.rs | 54 ++++-
13 files changed, 932 insertions(+), 466 deletions(-)
create mode 100644 ui/src/remotes/config.rs
create mode 100644 ui/src/remotes/tasks.rs
create mode 100644 ui/src/tasks.rs
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 11:30 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list Dominik Csapak
` (16 subsequent siblings)
17 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
by converting the strings like '2014-05-01' into epochs by using the
js-sys Date interface.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/tasks.rs | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/src/tasks.rs b/src/tasks.rs
index 4ea243f..fa17602 100644
--- a/src/tasks.rs
+++ b/src/tasks.rs
@@ -134,7 +134,27 @@ impl LoadableComponent for ProxmoxTasks {
let store = self.store.clone();
let form_context = self.filter_form_context.read();
- let filter = form_context.get_submit_data();
+ let mut filter = form_context.get_submit_data();
+
+ // Transform Date values
+ if let Some(since) = filter.get("since").and_then(|v| v.as_str()) {
+ let since = js_sys::Date::new(&wasm_bindgen::JsValue::from_str(since));
+ since.set_hours(0);
+ since.set_minutes(0);
+ since.set_seconds(0);
+ let since = (since.get_time() / 1000.0).round() as u64;
+ filter["since"] = since.into();
+ }
+
+ if let Some(until) = filter.get("until").and_then(|v| v.as_str()) {
+ let until = js_sys::Date::new(&wasm_bindgen::JsValue::from_str(until));
+ until.set_hours(23);
+ until.set_minutes(59);
+ until.set_seconds(59);
+ let until = (until.get_time() / 1000.0).round() as u64;
+ filter["until"] = until.into();
+ }
+
Box::pin(async move {
let data = crate::http_get(&path, Some(filter)).await?;
store.write().set_data(data);
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 17:29 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function Dominik Csapak
` (15 subsequent siblings)
17 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
and prevent the list to load multiple times the same data when filters
change rapidly (e.g. on first render or when the user enters text in
the filters)
reset the list when the filters changed or the reload button is pressed.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/tasks.rs | 72 ++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 59 insertions(+), 13 deletions(-)
diff --git a/src/tasks.rs b/src/tasks.rs
index fa17602..0ef80c6 100644
--- a/src/tasks.rs
+++ b/src/tasks.rs
@@ -6,6 +6,8 @@ use anyhow::Error;
use pwt::widget::form::{Field, Form, FormContext, InputType};
+use gloo_timers::callback::Timeout;
+use serde_json::Map;
use yew::html::IntoPropValue;
use yew::virtual_dom::{VComp, VNode};
@@ -26,6 +28,10 @@ use crate::{LoadableComponent, LoadableComponentContext, LoadableComponentMaster
use super::{TaskStatusSelector, TaskTypeSelector};
+const FILTER_UPDATE_BUFFER_MS: u32 = 150;
+const BATCH_LIMIT: u64 = 500;
+const LOAD_BUFFER_ROWS: usize = 20;
+
#[derive(PartialEq, Properties)]
#[builder]
pub struct Tasks {
@@ -75,6 +81,7 @@ pub enum ViewDialog {
pub enum Msg {
Redraw,
ToggleFilter,
+ LoadBatch(u64), // start
UpdateFilter,
}
pub struct ProxmoxTasks {
@@ -83,6 +90,9 @@ pub struct ProxmoxTasks {
show_filter: PersistentState<bool>,
filter_form_context: FormContext,
row_render_callback: DataTableRowRenderCallback<TaskListItem>,
+ start: u64,
+ last_filter: serde_json::Value,
+ load_timeout: Option<Timeout>,
}
impl LoadableComponent for ProxmoxTasks {
@@ -98,14 +108,21 @@ impl LoadableComponent for ProxmoxTasks {
let filter_form_context =
FormContext::new().on_change(ctx.link().callback(|_| Msg::UpdateFilter));
- let row_render_callback = DataTableRowRenderCallback::new(|args: &mut _| {
- let record: &TaskListItem = args.record();
- if let Some(status) = &record.status {
- if status != "OK" {
- if status.starts_with("WARNINGS:") {
- args.add_class("pwt-color-warning");
- } else {
- args.add_class("pwt-color-error");
+ let row_render_callback = DataTableRowRenderCallback::new({
+ let store = store.clone();
+ let link = link.clone();
+ move |args: &mut _| {
+ if args.row_index() > store.data_len().saturating_sub(LOAD_BUFFER_ROWS) {
+ link.send_message(Msg::LoadBatch(store.data_len() as u64));
+ }
+ let record: &TaskListItem = args.record();
+ if let Some(status) = &record.status {
+ if status != "OK" {
+ if status.starts_with("WARNINGS:") {
+ args.add_class("pwt-color-warning");
+ } else {
+ args.add_class("pwt-color-error");
+ }
}
}
}
@@ -117,6 +134,9 @@ impl LoadableComponent for ProxmoxTasks {
show_filter: PersistentState::new("ProxmoxTasksShowFilter"),
filter_form_context,
row_render_callback,
+ last_filter: serde_json::Value::Object(Map::new()),
+ start: 0,
+ load_timeout: None,
}
}
@@ -155,9 +175,17 @@ impl LoadableComponent for ProxmoxTasks {
filter["until"] = until.into();
}
+ let start = self.start;
+ filter["start"] = start.into();
+ filter["limit"] = BATCH_LIMIT.into();
+
Box::pin(async move {
- let data = crate::http_get(&path, Some(filter)).await?;
- store.write().set_data(data);
+ let mut data: Vec<_> = crate::http_get(&path, Some(filter)).await?;
+ if start == 0 {
+ store.write().set_data(data);
+ } else {
+ store.write().append(&mut data);
+ }
Ok(())
})
}
@@ -170,14 +198,32 @@ impl LoadableComponent for ProxmoxTasks {
true
}
Msg::UpdateFilter => {
- // fixme: delay load
let form_context = self.filter_form_context.read();
if !form_context.is_valid() {
return false;
}
- ctx.link().send_reload();
+ let filter_params = form_context.get_submit_data();
+ if ctx.loading() && self.last_filter == filter_params {
+ return false;
+ }
+
+ self.last_filter = filter_params;
+ self.start = 0;
+
+ let link = ctx.link().clone();
+ self.load_timeout = Some(Timeout::new(FILTER_UPDATE_BUFFER_MS, move || {
+ link.send_reload();
+ }));
true
}
+ Msg::LoadBatch(start) => {
+ self.start = start;
+ let link = ctx.link().clone();
+ self.load_timeout = Some(Timeout::new(FILTER_UPDATE_BUFFER_MS, move || {
+ link.send_reload();
+ }));
+ false
+ }
}
}
@@ -220,7 +266,7 @@ impl LoadableComponent for ProxmoxTasks {
.with_child({
let loading = ctx.loading();
let link = ctx.link();
- Button::refresh(loading).onclick(move |_| link.send_reload())
+ Button::refresh(loading).onclick(move |_| link.send_message(Msg::LoadBatch(0)))
});
let filter_classes = classes!(
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 17:29 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 4/7] running tasks: make TaskListItem renderer configurable Dominik Csapak
` (14 subsequent siblings)
17 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
we'll need this separated from the UPID parsing code, so we can also use
info from e.g. a `PveUpid` that we don't have here (yet).
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/utils.rs | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/src/utils.rs b/src/utils.rs
index 24de356..d500b6d 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -227,20 +227,22 @@ pub fn init_task_descr_table_base() {
register_task_description("srvreload", (tr!("Service"), tr!("Reload")));
}
+/// Uses information from the given [`UPID`] to render the task description with [`format_task_description`]
pub fn format_upid(upid: &str) -> String {
match upid.parse::<UPID>() {
Err(_) => upid.to_string(),
- Ok(upid) => {
- if let Some(text) =
- lookup_task_description(upid.worker_type.as_str(), upid.worker_id.as_deref())
- {
- text
- } else {
- match (upid.worker_type.as_str(), upid.worker_id) {
- (worker_type, Some(id)) => format!("{} {}", worker_type, id),
- (worker_type, None) => worker_type.to_string(),
- }
- }
+ Ok(upid) => format_task_description(&upid.worker_type, upid.worker_id.as_deref()),
+ }
+}
+
+/// Formats the given worker type and id to a Human readable task description
+pub fn format_task_description(worker_type: &str, worker_id: Option<&str>) -> String {
+ if let Some(text) = lookup_task_description(worker_type, worker_id) {
+ text
+ } else {
+ match worker_id {
+ Some(id) => format!("{} {}", worker_type, id),
+ None => worker_type.to_string(),
}
}
}
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 4/7] running tasks: make TaskListItem renderer configurable
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (2 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 5/7] running tasks: make buttons configurable Dominik Csapak
` (13 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
we currently don't have all types here (e.g. RemoteUpid), so simply
make it generally configurable, so the downstream user of this can
format the task the way it's necessary there.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/running_tasks.rs | 17 ++++++++++++++++-
src/running_tasks_button.rs | 7 +++++++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/src/running_tasks.rs b/src/running_tasks.rs
index 8ddc73c..b0b63af 100644
--- a/src/running_tasks.rs
+++ b/src/running_tasks.rs
@@ -4,6 +4,7 @@ use yew::html::IntoEventCallback;
use yew::virtual_dom::{VComp, VNode};
use pwt::prelude::*;
+use pwt::props::{IntoOptionalRenderFn, RenderFn};
use pwt::state::{Loader, LoaderState, SharedStateObserver, Store};
use pwt::widget::data_table::{DataTable, DataTableColumn, DataTableHeader};
use pwt::widget::{ActionIcon, Button, Container, Panel, Toolbar, Tooltip};
@@ -22,6 +23,11 @@ pub struct RunningTasks {
#[prop_or_default]
pub on_show_task: Option<Callback<(String, Option<i64>)>>,
+ #[builder_cb(IntoOptionalRenderFn, into_optional_render_fn, TaskListItem)]
+ #[prop_or_default]
+ /// Render function for the [`TaskListItem`]
+ pub render: Option<RenderFn<TaskListItem>>,
+
#[builder_cb(IntoEventCallback, into_event_callback, ())]
#[prop_or_default]
pub on_close: Option<Callback<()>>,
@@ -71,7 +77,16 @@ impl ProxmoxRunningTasks {
Rc::new(vec![
DataTableColumn::new(tr!("Task"))
.flex(1)
- .render(|item: &TaskListItem| html! {format_upid(&item.upid)})
+ .render({
+ let render = props.render.clone();
+ move |item: &TaskListItem| {
+ if let Some(render) = &render {
+ render.apply(item)
+ } else {
+ html! {format_upid(&item.upid)}
+ }
+ }
+ })
.into(),
DataTableColumn::new(tr!("Start Time"))
.width("130px")
diff --git a/src/running_tasks_button.rs b/src/running_tasks_button.rs
index 43ca6f3..e0e1062 100644
--- a/src/running_tasks_button.rs
+++ b/src/running_tasks_button.rs
@@ -8,6 +8,7 @@ use yew::virtual_dom::{VComp, VNode};
use pwt::dom::align::{align_to, AlignOptions, GrowDirection, Point};
use pwt::prelude::*;
+use pwt::props::{IntoOptionalRenderFn, RenderFn};
use pwt::state::{Loader, LoaderState, SharedStateObserver};
use pwt::widget::{Button, Container};
@@ -24,6 +25,11 @@ pub struct RunningTasksButton {
#[builder_cb(IntoEventCallback, into_event_callback, (String, Option<i64>))]
#[prop_or_default]
on_show_task: Option<Callback<(String, Option<i64>)>>,
+
+ #[builder_cb(IntoOptionalRenderFn, into_optional_render_fn, TaskListItem)]
+ #[prop_or_default]
+ /// Render function for the [`TaskListItem`]
+ pub render: Option<RenderFn<TaskListItem>>,
}
impl RunningTasksButton {
@@ -152,6 +158,7 @@ impl Component for ProxmoxRunningTasksButton {
RunningTasks::new(props.running_tasks.clone())
.as_dropdown(true)
.on_show_task(props.on_show_task.clone())
+ .render(props.render.clone())
.on_close(ctx.link().callback(|_| Msg::CloseMenu)),
)
});
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 5/7] running tasks: make buttons configurable
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (3 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 4/7] running tasks: make TaskListItem renderer configurable Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 6/7] tasks: make columns configurable Dominik Csapak
` (12 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
we sometimes want different buttons here (e.g. on PDM we want two)
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/running_tasks.rs | 37 +++++++++++++++++++++++++------------
src/running_tasks_button.rs | 8 +++++++-
2 files changed, 32 insertions(+), 13 deletions(-)
diff --git a/src/running_tasks.rs b/src/running_tasks.rs
index b0b63af..c5cea4c 100644
--- a/src/running_tasks.rs
+++ b/src/running_tasks.rs
@@ -1,6 +1,6 @@
use std::rc::Rc;
-use yew::html::IntoEventCallback;
+use yew::html::{IntoEventCallback, IntoPropValue};
use yew::virtual_dom::{VComp, VNode};
use pwt::prelude::*;
@@ -35,6 +35,11 @@ pub struct RunningTasks {
#[builder]
#[prop_or_default]
pub as_dropdown: bool,
+
+ #[builder(IntoPropValue, into_prop_value)]
+ #[prop_or_default]
+ /// Custom Buttons instead of the default 'Show all' one.
+ pub buttons: Option<Vec<Button>>,
}
impl RunningTasks {
@@ -167,17 +172,25 @@ impl Component for ProxmoxRunningTasks {
});
let toolbar = props.as_dropdown.then(|| {
- Toolbar::new().with_flex_spacer().with_child({
- let on_close = props.on_close.clone();
- Button::new(tr!("Show All Tasks"))
- .class("pwt-scheme-primary")
- .onclick(move |_| {
- crate::utils::set_location_href("#/administration/tasks");
- if let Some(on_close) = &on_close {
- on_close.emit(());
- }
- })
- })
+ if let Some(buttons) = props.buttons.clone() {
+ let mut tb = Toolbar::new().with_flex_spacer();
+ for button in buttons {
+ tb.add_child(button);
+ }
+ tb
+ } else {
+ Toolbar::new().with_flex_spacer().with_child({
+ let on_close = props.on_close.clone();
+ Button::new(tr!("Show All Tasks"))
+ .class("pwt-scheme-primary")
+ .onclick(move |_| {
+ crate::utils::set_location_href("#/administration/tasks");
+ if let Some(on_close) = &on_close {
+ on_close.emit(());
+ }
+ })
+ })
+ }
});
Panel::new()
diff --git a/src/running_tasks_button.rs b/src/running_tasks_button.rs
index e0e1062..331c006 100644
--- a/src/running_tasks_button.rs
+++ b/src/running_tasks_button.rs
@@ -3,7 +3,7 @@ use std::rc::Rc;
use gloo_timers::callback::Timeout;
use pwt::css::ColorScheme;
use wasm_bindgen::JsCast;
-use yew::html::IntoEventCallback;
+use yew::html::{IntoEventCallback, IntoPropValue};
use yew::virtual_dom::{VComp, VNode};
use pwt::dom::align::{align_to, AlignOptions, GrowDirection, Point};
@@ -30,6 +30,11 @@ pub struct RunningTasksButton {
#[prop_or_default]
/// Render function for the [`TaskListItem`]
pub render: Option<RenderFn<TaskListItem>>,
+
+ #[builder(IntoPropValue, into_prop_value)]
+ #[prop_or_default]
+ /// Custom Buttons instead of the default 'Show all' one.
+ pub buttons: Option<Vec<Button>>,
}
impl RunningTasksButton {
@@ -156,6 +161,7 @@ impl Component for ProxmoxRunningTasksButton {
.node_ref(self.submenu_ref.clone())
.with_child(
RunningTasks::new(props.running_tasks.clone())
+ .buttons(props.buttons.clone())
.as_dropdown(true)
.on_show_task(props.on_show_task.clone())
.render(props.render.clone())
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 6/7] tasks: make columns configurable
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (4 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 5/7] running tasks: make buttons configurable Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 17:37 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 7/7] tasks: make the 'show task' action configurable Dominik Csapak
` (11 subsequent siblings)
17 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
this enables us to have different columns for different task views
(e.g. remote tasks on PDM) and a custom renderer for the descriptions,
etc. (also necessary for PDM)
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/tasks.rs | 95 +++++++++++++++++++++++++++++++---------------------
1 file changed, 57 insertions(+), 38 deletions(-)
diff --git a/src/tasks.rs b/src/tasks.rs
index 0ef80c6..bb6eb1d 100644
--- a/src/tasks.rs
+++ b/src/tasks.rs
@@ -49,6 +49,11 @@ pub struct Tasks {
#[prop_or_default]
#[builder(IntoPropValue, into_prop_value)]
pub base_url: Option<AttrValue>,
+
+ #[builder(IntoPropValue, into_prop_value)]
+ #[prop_or_default]
+ /// An optional column configuration that overwrites the default one.
+ pub columns: Option<Rc<Vec<DataTableHeader<TaskListItem>>>>,
}
impl Default for Tasks {
@@ -93,6 +98,46 @@ pub struct ProxmoxTasks {
start: u64,
last_filter: serde_json::Value,
load_timeout: Option<Timeout>,
+ columns: Rc<Vec<DataTableHeader<TaskListItem>>>,
+}
+
+impl ProxmoxTasks {
+ fn columns(ctx: &LoadableComponentContext<Self>) -> Rc<Vec<DataTableHeader<TaskListItem>>> {
+ if let Some(columns) = ctx.props().columns.clone() {
+ columns
+ } else {
+ Rc::new(vec![
+ DataTableColumn::new(tr!("Start Time"))
+ .width("130px")
+ .render(|item: &TaskListItem| render_epoch_short(item.starttime).into())
+ .into(),
+ DataTableColumn::new(tr!("End Time"))
+ .width("130px")
+ .render(|item: &TaskListItem| match item.endtime {
+ Some(endtime) => render_epoch_short(endtime).into(),
+ None => html! {},
+ })
+ .into(),
+ DataTableColumn::new(tr!("User name"))
+ .width("150px")
+ .render(|item: &TaskListItem| {
+ html! {&item.user}
+ })
+ .into(),
+ DataTableColumn::new(tr!("Description"))
+ .flex(1)
+ .render(move |item: &TaskListItem| html! {format_upid(&item.upid)})
+ .into(),
+ DataTableColumn::new(tr!("Status"))
+ .width("200px")
+ .render(|item: &TaskListItem| {
+ let text = item.status.as_deref().unwrap_or("");
+ html! {text}
+ })
+ .into(),
+ ])
+ }
+ }
}
impl LoadableComponent for ProxmoxTasks {
@@ -137,6 +182,7 @@ impl LoadableComponent for ProxmoxTasks {
last_filter: serde_json::Value::Object(Map::new()),
start: 0,
load_timeout: None,
+ columns: Self::columns(ctx),
}
}
@@ -320,7 +366,7 @@ impl LoadableComponent for ProxmoxTasks {
}
fn main_view(&self, ctx: &LoadableComponentContext<Self>) -> Html {
- let columns = COLUMNS.with(Rc::clone);
+ let columns = self.columns.clone();
let link = ctx.link();
DataTable::new(columns, self.store.clone())
@@ -355,44 +401,17 @@ impl LoadableComponent for ProxmoxTasks {
}
}
}
-}
-thread_local! {
- static COLUMNS: Rc<Vec<DataTableHeader<TaskListItem>>> = Rc::new(vec![
- DataTableColumn::new(tr!("Start Time"))
- .width("130px")
- .render(|item: &TaskListItem| {
- render_epoch_short(item.starttime).into()
- })
- .into(),
- DataTableColumn::new(tr!("End Time"))
- .width("130px")
- .render(|item: &TaskListItem| {
- match item.endtime {
- Some(endtime) => render_epoch_short(endtime).into(),
- None => html!{},
- }})
- .into(),
- DataTableColumn::new(tr!("User name"))
- .width("150px")
- .render(|item: &TaskListItem| {
- html!{&item.user}
- })
- .into(),
- DataTableColumn::new(tr!("Description"))
- .flex(1)
- .render(|item: &TaskListItem| {
- html!{format_upid(&item.upid)}
- })
- .into(),
- DataTableColumn::new(tr!("Status"))
- .width("200px")
- .render(|item: &TaskListItem| {
- let text = item.status.as_deref().unwrap_or("");
- html!{text}
- })
- .into(),
- ]);
+ fn changed(
+ &mut self,
+ ctx: &LoadableComponentContext<Self>,
+ old_props: &Self::Properties,
+ ) -> bool {
+ if old_props.columns != ctx.props().columns {
+ self.columns = Self::columns(ctx);
+ }
+ true
+ }
}
impl From<Tasks> for VNode {
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH yew-comp 7/7] tasks: make the 'show task' action configurable
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (5 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 6/7] tasks: make columns configurable Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 1/9] server: factor out task filters into `TaskFilters` type Dominik Csapak
` (10 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
we sometimes want to use a different base url for a task (e.g. remote
tasks on PDM)
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
src/tasks.rs | 32 +++++++++++++++++++++++++++-----
1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/src/tasks.rs b/src/tasks.rs
index bb6eb1d..56da7dd 100644
--- a/src/tasks.rs
+++ b/src/tasks.rs
@@ -7,6 +7,7 @@ use anyhow::Error;
use pwt::widget::form::{Field, Form, FormContext, InputType};
use gloo_timers::callback::Timeout;
+use html::IntoEventCallback;
use serde_json::Map;
use yew::html::IntoPropValue;
use yew::virtual_dom::{VComp, VNode};
@@ -50,6 +51,11 @@ pub struct Tasks {
#[builder(IntoPropValue, into_prop_value)]
pub base_url: Option<AttrValue>,
+ #[builder_cb(IntoEventCallback, into_event_callback, (String, Option<i64>))]
+ #[prop_or_default]
+ /// Called when the task is opened
+ pub on_show_task: Option<Callback<(String, Option<i64>)>>,
+
#[builder(IntoPropValue, into_prop_value)]
#[prop_or_default]
/// An optional column configuration that overwrites the default one.
@@ -88,6 +94,7 @@ pub enum Msg {
ToggleFilter,
LoadBatch(u64), // start
UpdateFilter,
+ ShowTask,
}
pub struct ProxmoxTasks {
selection: Selection,
@@ -270,6 +277,22 @@ impl LoadableComponent for ProxmoxTasks {
}));
false
}
+ Msg::ShowTask => {
+ if let Some(on_show_task) = &ctx.props().on_show_task {
+ let selected_item = self
+ .selection
+ .selected_key()
+ .and_then(|key| self.store.read().lookup_record(&key).cloned());
+ let selected_item = match selected_item {
+ Some(item) => item,
+ None => return false,
+ };
+ on_show_task.emit((selected_item.upid, selected_item.endtime));
+ } else {
+ ctx.link().change_view(Some(ViewDialog::TaskViewer));
+ }
+ false
+ }
}
}
@@ -292,10 +315,9 @@ impl LoadableComponent for ProxmoxTasks {
.class("pwt-overflow-hidden")
.class("pwt-border-bottom")
.with_child(
- Button::new(tr!("View")).disabled(disabled).onclick(
- ctx.link()
- .change_view_callback(|_| Some(ViewDialog::TaskViewer)),
- ),
+ Button::new(tr!("View"))
+ .disabled(disabled)
+ .onclick(ctx.link().callback(|_| Msg::ShowTask)),
)
.with_flex_spacer()
.with_child({
@@ -373,7 +395,7 @@ impl LoadableComponent for ProxmoxTasks {
.class("pwt-flex-fit")
.selection(self.selection.clone())
.on_row_dblclick(move |_: &mut _| {
- link.change_view(Some(ViewDialog::TaskViewer));
+ link.send_message(Msg::ShowTask);
})
.row_render_callback(self.row_render_callback.clone())
.into()
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 1/9] server: factor out task filters into `TaskFilters` type
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (6 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 7/7] tasks: make the 'show task' action configurable Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 17:42 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch Dominik Csapak
` (9 subsequent siblings)
17 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
so we can reuse it
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
lib/pdm-api-types/src/lib.rs | 81 ++++++++++++++++++++++++++++++++++-
server/src/api/nodes/tasks.rs | 80 ++++++++--------------------------
2 files changed, 98 insertions(+), 63 deletions(-)
diff --git a/lib/pdm-api-types/src/lib.rs b/lib/pdm-api-types/src/lib.rs
index bf312cc..3844907 100644
--- a/lib/pdm-api-types/src/lib.rs
+++ b/lib/pdm-api-types/src/lib.rs
@@ -219,7 +219,7 @@ pub enum NodePowerCommand {
#[api]
/// The state of a task.
-#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TaskStateType {
/// Ok
@@ -418,3 +418,82 @@ impl fmt::Display for RemoteUpid {
serde_plain::derive_deserialize_from_fromstr!(RemoteUpid, "valid remote upid");
serde_plain::derive_serialize_from_display!(RemoteUpid);
+
+fn limit_default() -> u64 {
+ 50
+}
+
+#[api(
+ properties: {
+ start: {
+ type: u64,
+ description: "List tasks beginning from this offset.",
+ default: 0,
+ optional: true,
+ },
+ limit: {
+ type: u64,
+ description: "Only list this amount of tasks. (0 means no limit)",
+ default: 50,
+ optional: true,
+ },
+ running: {
+ type: bool,
+ description: "Only list running tasks.",
+ optional: true,
+ default: false,
+ },
+ errors: {
+ type: bool,
+ description: "Only list erroneous tasks.",
+ optional:true,
+ default: false,
+ },
+ userfilter: {
+ optional: true,
+ type: String,
+ description: "Only list tasks from this user.",
+ },
+ since: {
+ type: i64,
+ description: "Only list tasks since this UNIX epoch.",
+ optional: true,
+ },
+ until: {
+ type: i64,
+ description: "Only list tasks until this UNIX epoch.",
+ optional: true,
+ },
+ typefilter: {
+ optional: true,
+ type: String,
+ description: "Only list tasks whose type contains this.",
+ },
+ statusfilter: {
+ optional: true,
+ type: Array,
+ description: "Only list tasks which have any one of the listed status.",
+ items: {
+ type: TaskStateType,
+ },
+ },
+ }
+)]
+/// Task filter settings
+#[derive(Deserialize, Serialize, Clone, PartialEq, Eq)]
+#[serde(rename_all = "kebab-case")]
+pub struct TaskFilters {
+ #[serde(default)]
+ pub start: u64,
+ #[serde(default = "limit_default")]
+ pub limit: u64,
+ #[serde(default)]
+ pub errors: bool,
+ #[serde(default)]
+ pub running: bool,
+ pub userfilter: Option<String>,
+ pub since: Option<i64>,
+ pub until: Option<i64>,
+ pub typefilter: Option<String>,
+ pub statusfilter: Option<Vec<TaskStateType>>,
+}
diff --git a/server/src/api/nodes/tasks.rs b/server/src/api/nodes/tasks.rs
index 37fe0d8..13a713a 100644
--- a/server/src/api/nodes/tasks.rs
+++ b/server/src/api/nodes/tasks.rs
@@ -12,8 +12,8 @@ use proxmox_schema::{api, Schema};
use proxmox_sortable_macro::sortable;
use pdm_api_types::{
- Authid, TaskListItem, TaskStateType, Tokenname, Userid, NODE_SCHEMA, PRIV_SYS_AUDIT,
- PRIV_SYS_MODIFY, UPID, UPID_SCHEMA,
+ Authid, TaskFilters, TaskListItem, TaskStateType, Tokenname, Userid, NODE_SCHEMA,
+ PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, UPID, UPID_SCHEMA,
};
pub const ROUTER: Router = Router::new()
@@ -57,57 +57,9 @@ fn check_task_access(auth_id: &Authid, upid: &UPID) -> Result<(), Error> {
node: {
schema: NODE_SCHEMA
},
- start: {
- type: u64,
- description: "List tasks beginning from this offset.",
- default: 0,
- optional: true,
- },
- limit: {
- type: u64,
- description: "Only list this amount of tasks. (0 means no limit)",
- default: 50,
- optional: true,
- },
- running: {
- type: bool,
- description: "Only list running tasks.",
- optional: true,
- default: false,
- },
- errors: {
- type: bool,
- description: "Only list erroneous tasks.",
- optional:true,
- default: false,
- },
- userfilter: {
- optional: true,
- type: String,
- description: "Only list tasks from this user.",
- },
- since: {
- type: i64,
- description: "Only list tasks since this UNIX epoch.",
- optional: true,
- },
- until: {
- type: i64,
- description: "Only list tasks until this UNIX epoch.",
- optional: true,
- },
- typefilter: {
- optional: true,
- type: String,
- description: "Only list tasks whose type contains this.",
- },
- statusfilter: {
- optional: true,
- type: Array,
- description: "Only list tasks which have any one of the listed status.",
- items: {
- type: TaskStateType,
- },
+ filters: {
+ type: TaskFilters,
+ flatten: true,
},
},
},
@@ -120,17 +72,21 @@ fn check_task_access(auth_id: &Authid, upid: &UPID) -> Result<(), Error> {
/// List tasks.
#[allow(clippy::too_many_arguments)]
pub fn list_tasks(
- start: u64,
- limit: u64,
- errors: bool,
- running: bool,
- userfilter: Option<String>,
- since: Option<i64>,
- until: Option<i64>,
- typefilter: Option<String>,
- statusfilter: Option<Vec<TaskStateType>>,
+ filters: TaskFilters,
rpcenv: &mut dyn RpcEnvironment,
) -> Result<Vec<TaskListItem>, Error> {
+ let TaskFilters {
+ start,
+ limit,
+ errors,
+ running,
+ userfilter,
+ since,
+ until,
+ typefilter,
+ statusfilter,
+ } = filters;
+
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let user_info = CachedUserInfo::new()?;
let user_privs = user_info.lookup_privs(&auth_id, &["system", "tasks"]);
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (7 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 1/9] server: factor out task filters into `TaskFilters` type Dominik Csapak
@ 2025-01-20 9:29 ` Dominik Csapak
2025-01-20 17:45 ` Thomas Lamprecht
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 3/9] server: task cache: add filter options Dominik Csapak
` (8 subsequent siblings)
17 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:29 UTC (permalink / raw)
To: pdm-devel
instead of failing the whole cache retrieval, just skip the remote.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
server/src/task_cache.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/server/src/task_cache.rs b/server/src/task_cache.rs
index 3ef24b1..210aa5f 100644
--- a/server/src/task_cache.rs
+++ b/server/src/task_cache.rs
@@ -42,7 +42,13 @@ pub async fn get_tasks(max_age: i64) -> Result<Vec<TaskListItem>, Error> {
// Data in cache is recent enough and has not been invalidated.
all_tasks.extend(tasks);
} else {
- let tasks = fetch_tasks(remote).await?;
+ let tasks = match fetch_tasks(remote).await {
+ Ok(tasks) => tasks,
+ Err(err) => {
+ // ignore errors for not reachable remotes
+ continue;
+ }
+ };
cache.set_tasks(remote_name.as_str(), tasks.clone(), now);
all_tasks.extend(tasks);
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 3/9] server: task cache: add filter options
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (8 preceding siblings ...)
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 4/9] server: task cache: reverse task order Dominik Csapak
` (7 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
the same ones as the node task filters.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
server/src/api/remote_tasks.rs | 10 +++--
server/src/task_cache.rs | 67 +++++++++++++++++++++++++++++++++-
2 files changed, 72 insertions(+), 5 deletions(-)
diff --git a/server/src/api/remote_tasks.rs b/server/src/api/remote_tasks.rs
index d15a5d0..57b59fd 100644
--- a/server/src/api/remote_tasks.rs
+++ b/server/src/api/remote_tasks.rs
@@ -1,5 +1,5 @@
use anyhow::Error;
-use pdm_api_types::TaskListItem;
+use pdm_api_types::{TaskFilters, TaskListItem};
use proxmox_router::{list_subdirs_api_method, Permission, Router, SubdirMap};
use proxmox_schema::api;
use proxmox_sortable_macro::sortable;
@@ -27,13 +27,17 @@ const SUBDIRS: SubdirMap = &sorted!([("list", &Router::new().get(&API_METHOD_LIS
// TODO: sensible default max-age
default: 300,
description: "Maximum age of cached task data",
+ },
+ filters: {
+ type: TaskFilters,
+ flatten: true,
}
},
},
)]
/// Get the list of tasks for all remotes.
-async fn list_tasks(max_age: i64) -> Result<Vec<TaskListItem>, Error> {
- let tasks = task_cache::get_tasks(max_age).await?;
+async fn list_tasks(max_age: i64, filters: TaskFilters) -> Result<Vec<TaskListItem>, Error> {
+ let tasks = task_cache::get_tasks(max_age, filters).await?;
Ok(tasks)
}
diff --git a/server/src/task_cache.rs b/server/src/task_cache.rs
index 210aa5f..1faab12 100644
--- a/server/src/task_cache.rs
+++ b/server/src/task_cache.rs
@@ -9,7 +9,7 @@ use std::{
use anyhow::Error;
use pdm_api_types::{
remotes::{Remote, RemoteType},
- RemoteUpid, TaskListItem,
+ RemoteUpid, TaskFilters, TaskListItem, TaskStateType,
};
use proxmox_sys::fs::CreateOptions;
use pve_api_types::{ListTasks, ListTasksResponse, ListTasksSource, PveUpid};
@@ -19,7 +19,8 @@ use tokio::task::JoinHandle;
use crate::{api::pve, task_utils};
/// Get tasks for all remotes
-pub async fn get_tasks(max_age: i64) -> Result<Vec<TaskListItem>, Error> {
+// FIXME: filter for privileges
+pub async fn get_tasks(max_age: i64, filters: TaskFilters) -> Result<Vec<TaskListItem>, Error> {
let (remotes, _) = pdm_config::remotes::config()?;
let mut all_tasks = Vec::new();
@@ -57,6 +58,55 @@ pub async fn get_tasks(max_age: i64) -> Result<Vec<TaskListItem>, Error> {
let mut returned_tasks = add_running_tasks(all_tasks)?;
returned_tasks.sort_by(|a, b| a.starttime.cmp(&b.starttime));
+ let returned_tasks = returned_tasks
+ .into_iter()
+ .filter(|item| {
+ if filters.running && item.endtime.is_some() {
+ return false;
+ }
+
+ if let Some(until) = filters.until {
+ if item.starttime > until {
+ return false;
+ }
+ }
+
+ if let Some(since) = filters.since {
+ if item.starttime < since {
+ return false;
+ }
+ }
+
+ if let Some(needle) = &filters.userfilter {
+ if !item.user.contains(needle) {
+ return false;
+ }
+ }
+
+ if let Some(typefilter) = &filters.typefilter {
+ if !item.worker_type.contains(typefilter) {
+ return false;
+ }
+ }
+
+ let state = item.status.as_ref().map(|status| tasktype(status));
+
+ match (state, &filters.statusfilter) {
+ (Some(TaskStateType::OK), _) if filters.errors => return false,
+ (Some(state), Some(filters)) => {
+ if !filters.contains(&state) {
+ return false;
+ }
+ }
+ (None, Some(_)) => return false,
+ _ => {}
+ }
+
+ true
+ })
+ .skip(filters.start as usize)
+ .take(filters.limit as usize)
+ .collect();
// We don't need to wait for this task to finish
tokio::task::spawn_blocking(move || {
@@ -459,3 +509,16 @@ pub async fn get_finished_tasks() -> Vec<(RemoteUpid, String)> {
finished
}
+
+/// Parses a task status string into a TaskStateType
+pub fn tasktype(status: &str) -> TaskStateType {
+ if status == "unknown" || status.is_empty() {
+ TaskStateType::Unknown
+ } else if status == "OK" {
+ TaskStateType::OK
+ } else if status.starts_with("WARNINGS: ") {
+ TaskStateType::Warning
+ } else {
+ TaskStateType::Error
+ }
+}
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 4/9] server: task cache: reverse task order
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (9 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 3/9] server: task cache: add filter options Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 5/9] pdm-client: export PveUpid Dominik Csapak
` (6 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
we generally want to show newer tasks first, so reverse the sorting by
switching the parameter when comparing the starttime.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
server/src/task_cache.rs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/src/task_cache.rs b/server/src/task_cache.rs
index 1faab12..7ded540 100644
--- a/server/src/task_cache.rs
+++ b/server/src/task_cache.rs
@@ -57,7 +57,7 @@ pub async fn get_tasks(max_age: i64, filters: TaskFilters) -> Result<Vec<TaskLis
}
let mut returned_tasks = add_running_tasks(all_tasks)?;
- returned_tasks.sort_by(|a, b| a.starttime.cmp(&b.starttime));
+ returned_tasks.sort_by(|a, b| b.starttime.cmp(&a.starttime));
let returned_tasks = returned_tasks
.into_iter()
.filter(|item| {
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 5/9] pdm-client: export PveUpid
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (10 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 4/9] server: task cache: reverse task order Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 6/9] ui: refactor RemoteConfigPanel into own module Dominik Csapak
` (5 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
so we can use it in the UI
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
lib/pdm-client/src/lib.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/pdm-client/src/lib.rs b/lib/pdm-client/src/lib.rs
index 1253ded..a41b82c 100644
--- a/lib/pdm-client/src/lib.rs
+++ b/lib/pdm-client/src/lib.rs
@@ -54,6 +54,8 @@ pub mod types {
pub use pve_api_types::ListRealm;
pub use pve_api_types::ClusterNodeStatus;
+
+ pub use pve_api_types::PveUpid;
}
pub struct PdmClient<T: HttpApiClient>(pub T);
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 6/9] ui: refactor RemoteConfigPanel into own module
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (11 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 5/9] pdm-client: export PveUpid Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 7/9] ui: remotes: add tasks to global remote panel Dominik Csapak
` (4 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
we'll use the 'remote' module for a more general component where we use
the RemoteConfigPanel
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/remotes/config.rs | 392 +++++++++++++++++++++++++++++++++++++++
ui/src/remotes/mod.rs | 392 +--------------------------------------
2 files changed, 394 insertions(+), 390 deletions(-)
create mode 100644 ui/src/remotes/config.rs
diff --git a/ui/src/remotes/config.rs b/ui/src/remotes/config.rs
new file mode 100644
index 0000000..d9d16ae
--- /dev/null
+++ b/ui/src/remotes/config.rs
@@ -0,0 +1,392 @@
+use std::future::Future;
+use std::pin::Pin;
+use std::rc::Rc;
+
+use anyhow::Error;
+
+use proxmox_schema::property_string::PropertyString;
+
+use crate::remotes::edit_remote::EditRemote;
+//use pwt::widget::form::{Field, FormContext, InputType};
+
+use pdm_api_types::remotes::Remote;
+//use proxmox_schema::{property_string::PropertyString, ApiType};
+use proxmox_yew_comp::percent_encoding::percent_encode_component;
+
+//use pbs_api_types::CERT_FINGERPRINT_SHA256_SCHEMA;
+
+//use proxmox_schema::api_types::{CERT_FINGERPRINT_SHA256_SCHEMA, DNS_NAME_OR_IP_SCHEMA};
+
+use serde_json::Value;
+use yew::virtual_dom::{Key, VComp, VNode};
+
+use pwt::prelude::*;
+use pwt::state::{Selection, Store};
+use pwt::widget::data_table::{DataTable, DataTableColumn, DataTableHeader};
+//use pwt::widget::form::{delete_empty_values, Field, FormContext, InputType};
+use pwt::widget::{
+ //menu::{Menu, MenuButton, MenuItem},
+ Button,
+ Column,
+ Toolbar,
+ Tooltip,
+};
+//use pwt::widget::InputPanel;
+
+//use proxmox_yew_comp::EditWindow;
+use proxmox_yew_comp::{
+ ConfirmButton, LoadableComponent, LoadableComponentContext, LoadableComponentMaster,
+};
+
+use pdm_api_types::remotes::{NodeUrl, RemoteType};
+
+async fn load_remotes() -> Result<Vec<Remote>, Error> {
+ proxmox_yew_comp::http_get("/remotes", None).await
+}
+
+async fn delete_item(key: Key) -> Result<(), Error> {
+ let id = key.to_string();
+ let url = format!("/remotes/{}", percent_encode_component(&id));
+ proxmox_yew_comp::http_delete(&url, None).await?;
+ Ok(())
+}
+
+pub async fn create_remote(mut data: Value, remote_type: RemoteType) -> Result<(), Error> {
+ if data.get("nodes").is_none() {
+ let nodes = vec![PropertyString::new(NodeUrl {
+ hostname: data["hostname"].as_str().unwrap_or_default().to_string(),
+ fingerprint: data["fingerprint"].as_str().map(|fp| fp.to_string()),
+ })];
+ data["nodes"] = serde_json::to_value(nodes)?;
+ }
+ data["type"] = match remote_type {
+ RemoteType::Pve => "pve",
+ RemoteType::Pbs => "pbs",
+ }
+ .into();
+
+ let remote: Remote = serde_json::from_value(data.clone())?;
+
+ let mut params = serde_json::to_value(remote)?;
+ if let Some(token) = data["create-token"].as_str() {
+ params["create-token"] = token.into();
+ }
+
+ proxmox_yew_comp::http_post("/remotes", Some(params)).await
+}
+
+/*
+async fn update_item(form_ctx: FormContext) -> Result<(), Error> {
+ let data = form_ctx.get_submit_data();
+
+ let data = delete_empty_values(&data, &["fingerprint", "comment", "port"], true);
+
+ let name = form_ctx.read().get_field_text("name");
+
+ let url = format!("/config/remote/{}", percent_encode_component(&name));
+
+ proxmox_yew_comp::http_put(&url, Some(data)).await
+}
+*/
+
+#[derive(PartialEq, Properties)]
+pub struct RemoteConfigPanel;
+
+impl RemoteConfigPanel {
+ pub fn new() -> Self {
+ yew::props!(Self {})
+ }
+}
+
+#[derive(PartialEq)]
+pub enum ViewState {
+ Add(RemoteType),
+ Edit,
+}
+
+pub enum Msg {
+ SelectionChange,
+ RemoveItem,
+}
+
+pub struct PbsRemoteConfigPanel {
+ store: Store<Remote>,
+ selection: Selection,
+ remote_list_columns: Rc<Vec<DataTableHeader<Remote>>>,
+}
+
+impl LoadableComponent for PbsRemoteConfigPanel {
+ type Message = Msg;
+ type Properties = RemoteConfigPanel;
+ type ViewState = ViewState;
+
+ fn load(
+ &self,
+ _ctx: &LoadableComponentContext<Self>,
+ ) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
+ let store = self.store.clone();
+ Box::pin(async move {
+ let data = load_remotes().await?;
+ store.write().set_data(data);
+ Ok(())
+ })
+ }
+
+ fn create(ctx: &LoadableComponentContext<Self>) -> Self {
+ let store = Store::with_extract_key(|record: &Remote| Key::from(record.id.clone()));
+
+ let selection = Selection::new().on_select(ctx.link().callback(|_| Msg::SelectionChange));
+
+ let remote_list_columns = remote_list_columns();
+
+ Self {
+ store,
+ selection,
+ remote_list_columns,
+ }
+ }
+
+ fn update(&mut self, ctx: &LoadableComponentContext<Self>, msg: Self::Message) -> bool {
+ match msg {
+ Msg::SelectionChange => true,
+ Msg::RemoveItem => {
+ if let Some(key) = self.selection.selected_key() {
+ let link = ctx.link();
+ link.clone().spawn(async move {
+ if let Err(err) = delete_item(key).await {
+ link.show_error(tr!("Unable to delete item"), err, true);
+ }
+ link.send_reload();
+ })
+ }
+ false
+ }
+ }
+ }
+
+ fn toolbar(&self, ctx: &LoadableComponentContext<Self>) -> Option<Html> {
+ let link = ctx.link();
+
+ let disabled = self.selection.is_empty();
+
+ let toolbar = Toolbar::new()
+ .class("pwt-overflow-hidden")
+ .class("pwt-border-bottom")
+ .with_child({
+ Button::new(tr!("Add Proxmox VE"))
+ .icon_class("fa fa-building")
+ .onclick(link.change_view_callback(|_| Some(ViewState::Add(RemoteType::Pve))))
+ // FIXME: add PBS support
+ //MenuButton::new(tr!("Add")).show_arrow(true).menu(
+ // Menu::new()
+ // .with_item(
+ // MenuItem::new("Proxmox VE")
+ // .icon_class("fa fa-building")
+ // .on_select(link.change_view_callback(|_| {
+ // Some(ViewState::Add(RemoteType::Pve))
+ // })),
+ // )
+ // .with_item(
+ // MenuItem::new("Proxmox Backup Server")
+ // .icon_class("fa fa-floppy-o")
+ // .on_select(link.change_view_callback(|_| {
+ // Some(ViewState::Add(RemoteType::Pbs))
+ // })),
+ // ),
+ //)
+ })
+ .with_spacer()
+ .with_child(
+ Button::new(tr!("Edit"))
+ .disabled(disabled)
+ .onclick(link.change_view_callback(|_| Some(ViewState::Edit))),
+ )
+ .with_child(
+ ConfirmButton::new(tr!("Remove"))
+ .confirm_message(tr!("Are you sure you want to remove this remote?"))
+ .disabled(disabled)
+ .on_activate(link.callback(|_| Msg::RemoveItem)),
+ )
+ .with_flex_spacer()
+ .with_child({
+ let loading = ctx.loading();
+ let link = ctx.link();
+ Button::refresh(loading).onclick(move |_| link.send_reload())
+ });
+
+ Some(toolbar.into())
+ }
+
+ fn main_view(&self, ctx: &LoadableComponentContext<Self>) -> Html {
+ let columns = Rc::clone(&self.remote_list_columns);
+ let link = ctx.link();
+ DataTable::new(columns, self.store.clone())
+ .class(pwt::css::FlexFit)
+ .selection(self.selection.clone())
+ .on_row_dblclick(move |_: &mut _| {
+ link.change_view(Some(ViewState::Edit));
+ })
+ .into()
+ }
+
+ fn dialog_view(
+ &self,
+ ctx: &LoadableComponentContext<Self>,
+ view_state: &Self::ViewState,
+ ) -> Option<Html> {
+ match view_state {
+ ViewState::Add(ty) => Some(self.create_add_dialog(ctx, *ty)),
+ ViewState::Edit => self
+ .selection
+ .selected_key()
+ .map(|key| self.create_edit_dialog(ctx, key)),
+ }
+ }
+}
+
+/*
+fn add_remote_input_panel(_form_ctx: &FormContext) -> Html {
+ InputPanel::new()
+ .padding(4)
+ .with_field(tr!("Remote ID"), Field::new().name("id").required(true))
+ .with_right_field(
+ tr!("Fingerprint"),
+ Field::new()
+ .name("fingerprint")
+ .schema(&CERT_FINGERPRINT_SHA256_SCHEMA),
+ )
+ .with_field(
+ tr!("Server address"),
+ Field::new().name("server").required(true),
+ )
+ .with_field(
+ tr!("User/Token"),
+ Field::new()
+ .name("authid")
+ .schema(&pdm_api_types::Authid::API_SCHEMA)
+ .required(true),
+ )
+ .with_field(
+ tr!("Password/Secret"),
+ Field::new()
+ .name("token")
+ .input_type(InputType::Password)
+ .required(true),
+ )
+ .into()
+}
+*/
+
+impl PbsRemoteConfigPanel {
+ fn create_add_dialog(
+ &self,
+ ctx: &LoadableComponentContext<Self>,
+ remote_type: RemoteType,
+ ) -> Html {
+ super::AddWizard::new(remote_type)
+ .on_close(ctx.link().change_view_callback(|_| None))
+ .on_submit(move |ctx| create_remote(ctx, remote_type))
+ .into()
+
+ // EditWindow::new(tr!("Add") + ": " + &tr!("Remote"))
+ // .renderer(add_remote_input_panel)
+ // .on_submit(move |ctx: FormContext| create_item(ctx.get_submit_data(), remote_type))
+ // .on_done(ctx.link().change_view_callback(|_| None))
+ // .into()
+ }
+
+ fn create_edit_dialog(&self, ctx: &LoadableComponentContext<Self>, key: Key) -> Html {
+ EditRemote::new(&*key)
+ .on_done(ctx.link().change_view_callback(|_| None))
+ .into()
+ }
+}
+
+impl Into<VNode> for RemoteConfigPanel {
+ fn into(self) -> VNode {
+ let comp = VComp::new::<LoadableComponentMaster<PbsRemoteConfigPanel>>(Rc::new(self), None);
+ VNode::from(comp)
+ }
+}
+
+fn remote_list_columns() -> Rc<Vec<DataTableHeader<Remote>>> {
+ Rc::new(vec![
+ DataTableColumn::new(tr!("Remote ID"))
+ .width("200px")
+ .render(|item: &Remote| {
+ html! {
+ &item.id
+ }
+ })
+ .sorter(|a: &Remote, b: &Remote| a.id.cmp(&b.id))
+ .sort_order(true)
+ .into(),
+ DataTableColumn::new(tr!("Type"))
+ .width("60px")
+ .render(|item: &Remote| {
+ html! {
+ &item.ty
+ }
+ })
+ .sorter(|a: &Remote, b: &Remote| a.ty.cmp(&b.ty))
+ .into(),
+ DataTableColumn::new(tr!("AuthId"))
+ .width("200px")
+ .render(|item: &Remote| {
+ html! {
+ &item.authid
+ }
+ })
+ .sorter(|a: &Remote, b: &Remote| a.authid.cmp(&b.authid))
+ .into(),
+ DataTableColumn::new(tr!("Nodes"))
+ .flex(1)
+ .render(|item: &Remote| {
+ if item.nodes.is_empty() {
+ html! {tr!("None")}
+ } else {
+ let nodes = item
+ .nodes
+ .iter()
+ .map(|n| n.hostname.as_str())
+ .collect::<Vec<_>>()
+ .join(", ");
+ let mut tip = Column::new();
+ tip.add_children(item.nodes.iter().map(|n| {
+ let text = match n.fingerprint.clone() {
+ Some(fp) => format!("{} ({fp})", n.hostname),
+ None => n.hostname.to_string(),
+ };
+ html! {<div>{text}</div>}
+ }));
+ Tooltip::new(nodes).rich_tip(tip).into()
+ }
+ })
+ .into(),
+ /*
+ DataTableColumn::new(tr!("Auth ID"))
+ .width("200px")
+ .render(|item: &Remote| html!{
+ item.config.auth_id.clone()
+ })
+ .sorter(|a: &Remote, b: &Remote| {
+ a.config.auth_id.cmp(&b.config.auth_id)
+ })
+ .into(),
+
+ DataTableColumn::new(tr!("Fingerprint"))
+ .width("200px")
+ .render(|item: &Remote| html!{
+ item.config.fingerprint.clone().unwrap_or(String::new())
+ })
+ .into(),
+
+ DataTableColumn::new(tr!("Comment"))
+ .flex(1)
+ .render(|item: &Remote| html!{
+ item.config.comment.clone().unwrap_or(String::new())
+ })
+ .into()
+ */
+ ])
+}
diff --git a/ui/src/remotes/mod.rs b/ui/src/remotes/mod.rs
index d11c5e0..f221777 100644
--- a/ui/src/remotes/mod.rs
+++ b/ui/src/remotes/mod.rs
@@ -1,5 +1,4 @@
mod wizard_page_connect;
-use proxmox_schema::property_string::PropertyString;
use wizard_page_connect::WizardPageConnect;
mod wizard_page_nodes;
@@ -19,392 +18,5 @@ pub use node_url_list::NodeUrlList;
mod edit_remote;
-use std::future::Future;
-use std::pin::Pin;
-use std::rc::Rc;
-
-use anyhow::Error;
-use edit_remote::EditRemote;
-//use pwt::widget::form::{Field, FormContext, InputType};
-
-use pdm_api_types::remotes::Remote;
-//use proxmox_schema::{property_string::PropertyString, ApiType};
-use proxmox_yew_comp::percent_encoding::percent_encode_component;
-
-//use pbs_api_types::CERT_FINGERPRINT_SHA256_SCHEMA;
-
-//use proxmox_schema::api_types::{CERT_FINGERPRINT_SHA256_SCHEMA, DNS_NAME_OR_IP_SCHEMA};
-
-use serde_json::Value;
-use yew::virtual_dom::{Key, VComp, VNode};
-
-use pwt::prelude::*;
-use pwt::state::{Selection, Store};
-use pwt::widget::data_table::{DataTable, DataTableColumn, DataTableHeader};
-//use pwt::widget::form::{delete_empty_values, Field, FormContext, InputType};
-use pwt::widget::{
- //menu::{Menu, MenuButton, MenuItem},
- Button,
- Column,
- Toolbar,
- Tooltip,
-};
-//use pwt::widget::InputPanel;
-
-//use proxmox_yew_comp::EditWindow;
-use proxmox_yew_comp::{
- ConfirmButton, LoadableComponent, LoadableComponentContext, LoadableComponentMaster,
-};
-
-use pdm_api_types::remotes::{NodeUrl, RemoteType};
-
-async fn load_remotes() -> Result<Vec<Remote>, Error> {
- proxmox_yew_comp::http_get("/remotes", None).await
-}
-
-async fn delete_item(key: Key) -> Result<(), Error> {
- let id = key.to_string();
- let url = format!("/remotes/{}", percent_encode_component(&id));
- proxmox_yew_comp::http_delete(&url, None).await?;
- Ok(())
-}
-
-pub(crate) async fn create_remote(mut data: Value, remote_type: RemoteType) -> Result<(), Error> {
- if data.get("nodes").is_none() {
- let nodes = vec![PropertyString::new(NodeUrl {
- hostname: data["hostname"].as_str().unwrap_or_default().to_string(),
- fingerprint: data["fingerprint"].as_str().map(|fp| fp.to_string()),
- })];
- data["nodes"] = serde_json::to_value(nodes)?;
- }
- data["type"] = match remote_type {
- RemoteType::Pve => "pve",
- RemoteType::Pbs => "pbs",
- }
- .into();
-
- let remote: Remote = serde_json::from_value(data.clone())?;
-
- let mut params = serde_json::to_value(remote)?;
- if let Some(token) = data["create-token"].as_str() {
- params["create-token"] = token.into();
- }
-
- proxmox_yew_comp::http_post("/remotes", Some(params)).await
-}
-
-/*
-async fn update_item(form_ctx: FormContext) -> Result<(), Error> {
- let data = form_ctx.get_submit_data();
-
- let data = delete_empty_values(&data, &["fingerprint", "comment", "port"], true);
-
- let name = form_ctx.read().get_field_text("name");
-
- let url = format!("/config/remote/{}", percent_encode_component(&name));
-
- proxmox_yew_comp::http_put(&url, Some(data)).await
-}
-*/
-
-#[derive(PartialEq, Properties)]
-pub struct RemoteConfigPanel;
-
-impl RemoteConfigPanel {
- pub fn new() -> Self {
- yew::props!(Self {})
- }
-}
-
-#[derive(PartialEq)]
-pub enum ViewState {
- Add(RemoteType),
- Edit,
-}
-
-pub enum Msg {
- SelectionChange,
- RemoveItem,
-}
-
-pub struct PbsRemoteConfigPanel {
- store: Store<Remote>,
- selection: Selection,
- remote_list_columns: Rc<Vec<DataTableHeader<Remote>>>,
-}
-
-impl LoadableComponent for PbsRemoteConfigPanel {
- type Message = Msg;
- type Properties = RemoteConfigPanel;
- type ViewState = ViewState;
-
- fn load(
- &self,
- _ctx: &LoadableComponentContext<Self>,
- ) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
- let store = self.store.clone();
- Box::pin(async move {
- let data = load_remotes().await?;
- store.write().set_data(data);
- Ok(())
- })
- }
-
- fn create(ctx: &LoadableComponentContext<Self>) -> Self {
- let store = Store::with_extract_key(|record: &Remote| Key::from(record.id.clone()));
-
- let selection = Selection::new().on_select(ctx.link().callback(|_| Msg::SelectionChange));
-
- let remote_list_columns = remote_list_columns();
-
- Self {
- store,
- selection,
- remote_list_columns,
- }
- }
-
- fn update(&mut self, ctx: &LoadableComponentContext<Self>, msg: Self::Message) -> bool {
- match msg {
- Msg::SelectionChange => true,
- Msg::RemoveItem => {
- if let Some(key) = self.selection.selected_key() {
- let link = ctx.link();
- link.clone().spawn(async move {
- if let Err(err) = delete_item(key).await {
- link.show_error(tr!("Unable to delete item"), err, true);
- }
- link.send_reload();
- })
- }
- false
- }
- }
- }
-
- fn toolbar(&self, ctx: &LoadableComponentContext<Self>) -> Option<Html> {
- let link = ctx.link();
-
- let disabled = self.selection.is_empty();
-
- let toolbar = Toolbar::new()
- .class("pwt-overflow-hidden")
- .class("pwt-border-bottom")
- .with_child({
- Button::new(tr!("Add Proxmox VE"))
- .icon_class("fa fa-building")
- .onclick(link.change_view_callback(|_| Some(ViewState::Add(RemoteType::Pve))))
- // FIXME: add PBS support
- //MenuButton::new(tr!("Add")).show_arrow(true).menu(
- // Menu::new()
- // .with_item(
- // MenuItem::new("Proxmox VE")
- // .icon_class("fa fa-building")
- // .on_select(link.change_view_callback(|_| {
- // Some(ViewState::Add(RemoteType::Pve))
- // })),
- // )
- // .with_item(
- // MenuItem::new("Proxmox Backup Server")
- // .icon_class("fa fa-floppy-o")
- // .on_select(link.change_view_callback(|_| {
- // Some(ViewState::Add(RemoteType::Pbs))
- // })),
- // ),
- //)
- })
- .with_spacer()
- .with_child(
- Button::new(tr!("Edit"))
- .disabled(disabled)
- .onclick(link.change_view_callback(|_| Some(ViewState::Edit))),
- )
- .with_child(
- ConfirmButton::new(tr!("Remove"))
- .confirm_message(tr!("Are you sure you want to remove this remote?"))
- .disabled(disabled)
- .on_activate(link.callback(|_| Msg::RemoveItem)),
- )
- .with_flex_spacer()
- .with_child({
- let loading = ctx.loading();
- let link = ctx.link();
- Button::refresh(loading).onclick(move |_| link.send_reload())
- });
-
- Some(toolbar.into())
- }
-
- fn main_view(&self, ctx: &LoadableComponentContext<Self>) -> Html {
- let columns = Rc::clone(&self.remote_list_columns);
- let link = ctx.link();
- DataTable::new(columns, self.store.clone())
- .class(pwt::css::FlexFit)
- .selection(self.selection.clone())
- .on_row_dblclick(move |_: &mut _| {
- link.change_view(Some(ViewState::Edit));
- })
- .into()
- }
-
- fn dialog_view(
- &self,
- ctx: &LoadableComponentContext<Self>,
- view_state: &Self::ViewState,
- ) -> Option<Html> {
- match view_state {
- ViewState::Add(ty) => Some(self.create_add_dialog(ctx, *ty)),
- ViewState::Edit => self
- .selection
- .selected_key()
- .map(|key| self.create_edit_dialog(ctx, key)),
- }
- }
-}
-
-/*
-fn add_remote_input_panel(_form_ctx: &FormContext) -> Html {
- InputPanel::new()
- .padding(4)
- .with_field(tr!("Remote ID"), Field::new().name("id").required(true))
- .with_right_field(
- tr!("Fingerprint"),
- Field::new()
- .name("fingerprint")
- .schema(&CERT_FINGERPRINT_SHA256_SCHEMA),
- )
- .with_field(
- tr!("Server address"),
- Field::new().name("server").required(true),
- )
- .with_field(
- tr!("User/Token"),
- Field::new()
- .name("authid")
- .schema(&pdm_api_types::Authid::API_SCHEMA)
- .required(true),
- )
- .with_field(
- tr!("Password/Secret"),
- Field::new()
- .name("token")
- .input_type(InputType::Password)
- .required(true),
- )
- .into()
-}
-*/
-
-impl PbsRemoteConfigPanel {
- fn create_add_dialog(
- &self,
- ctx: &LoadableComponentContext<Self>,
- remote_type: RemoteType,
- ) -> Html {
- AddWizard::new(remote_type)
- .on_close(ctx.link().change_view_callback(|_| None))
- .on_submit(move |ctx| create_remote(ctx, remote_type))
- .into()
-
- // EditWindow::new(tr!("Add") + ": " + &tr!("Remote"))
- // .renderer(add_remote_input_panel)
- // .on_submit(move |ctx: FormContext| create_item(ctx.get_submit_data(), remote_type))
- // .on_done(ctx.link().change_view_callback(|_| None))
- // .into()
- }
-
- fn create_edit_dialog(&self, ctx: &LoadableComponentContext<Self>, key: Key) -> Html {
- EditRemote::new(&*key)
- .on_done(ctx.link().change_view_callback(|_| None))
- .into()
- }
-}
-
-impl Into<VNode> for RemoteConfigPanel {
- fn into(self) -> VNode {
- let comp = VComp::new::<LoadableComponentMaster<PbsRemoteConfigPanel>>(Rc::new(self), None);
- VNode::from(comp)
- }
-}
-
-fn remote_list_columns() -> Rc<Vec<DataTableHeader<Remote>>> {
- Rc::new(vec![
- DataTableColumn::new(tr!("Remote ID"))
- .width("200px")
- .render(|item: &Remote| {
- html! {
- &item.id
- }
- })
- .sorter(|a: &Remote, b: &Remote| a.id.cmp(&b.id))
- .sort_order(true)
- .into(),
- DataTableColumn::new(tr!("Type"))
- .width("60px")
- .render(|item: &Remote| {
- html! {
- &item.ty
- }
- })
- .sorter(|a: &Remote, b: &Remote| a.ty.cmp(&b.ty))
- .into(),
- DataTableColumn::new(tr!("AuthId"))
- .width("200px")
- .render(|item: &Remote| {
- html! {
- &item.authid
- }
- })
- .sorter(|a: &Remote, b: &Remote| a.authid.cmp(&b.authid))
- .into(),
- DataTableColumn::new(tr!("Nodes"))
- .flex(1)
- .render(|item: &Remote| {
- if item.nodes.is_empty() {
- html! {tr!("None")}
- } else {
- let nodes = item
- .nodes
- .iter()
- .map(|n| n.hostname.as_str())
- .collect::<Vec<_>>()
- .join(", ");
- let mut tip = Column::new();
- tip.add_children(item.nodes.iter().map(|n| {
- let text = match n.fingerprint.clone() {
- Some(fp) => format!("{} ({fp})", n.hostname),
- None => n.hostname.to_string(),
- };
- html! {<div>{text}</div>}
- }));
- Tooltip::new(nodes).rich_tip(tip).into()
- }
- })
- .into(),
- /*
- DataTableColumn::new(tr!("Auth ID"))
- .width("200px")
- .render(|item: &Remote| html!{
- item.config.auth_id.clone()
- })
- .sorter(|a: &Remote, b: &Remote| {
- a.config.auth_id.cmp(&b.config.auth_id)
- })
- .into(),
-
- DataTableColumn::new(tr!("Fingerprint"))
- .width("200px")
- .render(|item: &Remote| html!{
- item.config.fingerprint.clone().unwrap_or(String::new())
- })
- .into(),
-
- DataTableColumn::new(tr!("Comment"))
- .flex(1)
- .render(|item: &Remote| html!{
- item.config.comment.clone().unwrap_or(String::new())
- })
- .into()
- */
- ])
-}
+mod config;
+pub use config::{create_remote, RemoteConfigPanel};
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 7/9] ui: remotes: add tasks to global remote panel
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (12 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 6/9] ui: refactor RemoteConfigPanel into own module Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 8/9] ui: register pve tasks Dominik Csapak
` (3 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
this replaces the previous "global" remotes panel (that was just the
configuration) with a TabPanel that contains the configuration and the
tasks for all remotes.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/main_menu.rs | 7 +-
ui/src/remotes/mod.rs | 35 +++++++++
ui/src/remotes/tasks.rs | 153 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+), 3 deletions(-)
create mode 100644 ui/src/remotes/tasks.rs
diff --git a/ui/src/main_menu.rs b/ui/src/main_menu.rs
index 364ba94..4f40d2c 100644
--- a/ui/src/main_menu.rs
+++ b/ui/src/main_menu.rs
@@ -16,9 +16,10 @@ use proxmox_yew_comp::{NotesView, XTermJs};
use pdm_api_types::remotes::RemoteType;
+use crate::remotes::RemotesPanel;
use crate::{
- AccessControl, CertificatesPanel, Dashboard, RemoteConfigPanel, RemoteList,
- ServerAdministration, SystemConfiguration,
+ AccessControl, CertificatesPanel, Dashboard, RemoteList, ServerAdministration,
+ SystemConfiguration,
};
/*
@@ -279,7 +280,7 @@ impl Component for PdmMainMenu {
Container::new()
.class("pwt-content-spacer")
.class(pwt::css::FlexFit)
- .with_child(RemoteConfigPanel::new())
+ .with_child(html! {<RemotesPanel/>})
.into()
},
remote_submenu,
diff --git a/ui/src/remotes/mod.rs b/ui/src/remotes/mod.rs
index f221777..a2f471c 100644
--- a/ui/src/remotes/mod.rs
+++ b/ui/src/remotes/mod.rs
@@ -20,3 +20,38 @@ mod edit_remote;
mod config;
pub use config::{create_remote, RemoteConfigPanel};
+
+mod tasks;
+
+use yew::{function_component, Html};
+
+use pwt::{
+ props::{ContainerBuilder, StorageLocation},
+ state::NavigationContainer,
+ widget::{MiniScrollMode, TabBarItem, TabPanel},
+};
+
+#[function_component(RemotesPanel)]
+pub fn system_configuration() -> Html {
+ let panel = TabPanel::new()
+ .state_id(StorageLocation::session("RemotesPanelState"))
+ .class(pwt::css::FlexFit)
+ .router(true)
+ .scroll_mode(MiniScrollMode::Arrow)
+ .with_item_builder(
+ TabBarItem::new()
+ .key("configuration")
+ .label("Configuration")
+ .icon_class("fa fa-cogs"),
+ |_| RemoteConfigPanel::new().into(),
+ )
+ .with_item_builder(
+ TabBarItem::new()
+ .key("tasks")
+ .label("Tasks")
+ .icon_class("fa fa-book"),
+ |_| tasks::RemoteTaskList::new().into(),
+ );
+
+ NavigationContainer::new().with_child(panel).into()
+}
diff --git a/ui/src/remotes/tasks.rs b/ui/src/remotes/tasks.rs
new file mode 100644
index 0000000..6474035
--- /dev/null
+++ b/ui/src/remotes/tasks.rs
@@ -0,0 +1,153 @@
+use std::rc::Rc;
+
+use yew::{
+ html,
+ virtual_dom::{VComp, VNode},
+ Component, Properties,
+};
+
+use pdm_api_types::RemoteUpid;
+use pdm_client::types::PveUpid;
+
+use proxmox_yew_comp::{
+ common_api_types::TaskListItem,
+ utils::{format_task_description, format_upid, render_epoch_short},
+ TaskViewer, Tasks,
+};
+use pwt::{
+ css::FlexFit,
+ props::{ContainerBuilder, WidgetBuilder},
+ tr,
+ widget::{
+ data_table::{DataTableColumn, DataTableHeader},
+ Column,
+ },
+};
+
+#[derive(PartialEq, Properties)]
+pub struct RemoteTaskList;
+impl RemoteTaskList {
+ pub fn new() -> Self {
+ yew::props!(Self {})
+ }
+}
+
+pub struct PbsRemoteTaskList {
+ columns: Rc<Vec<DataTableHeader<TaskListItem>>>,
+ upid: Option<(String, Option<i64>)>,
+}
+
+fn columns() -> Rc<Vec<DataTableHeader<TaskListItem>>> {
+ Rc::new(vec![
+ DataTableColumn::new(tr!("Start Time"))
+ .width("130px")
+ .render(|item: &TaskListItem| render_epoch_short(item.starttime).into())
+ .into(),
+ DataTableColumn::new(tr!("End Time"))
+ .width("130px")
+ .render(|item: &TaskListItem| match item.endtime {
+ Some(endtime) => render_epoch_short(endtime).into(),
+ None => html! {},
+ })
+ .into(),
+ DataTableColumn::new(tr!("User name"))
+ .width("150px")
+ .render(|item: &TaskListItem| {
+ html! {&item.user}
+ })
+ .into(),
+ DataTableColumn::new(tr!("Remote"))
+ .width("150px")
+ .render(
+ |item: &TaskListItem| match item.upid.parse::<RemoteUpid>() {
+ Ok(remote) => html! {remote.remote()},
+ Err(_) => html! {{"-"}},
+ },
+ )
+ .into(),
+ DataTableColumn::new(tr!("Node"))
+ .width("150px")
+ .render(|item: &TaskListItem| {
+ html! {&item.node}
+ })
+ .into(),
+ DataTableColumn::new(tr!("Description"))
+ .flex(1)
+ .render(move |item: &TaskListItem| {
+ if let Ok(remote_upid) = item.upid.parse::<RemoteUpid>() {
+ match remote_upid.upid.parse::<PveUpid>() {
+ Ok(upid) => {
+ format_task_description(&upid.worker_type, upid.worker_id.as_deref())
+ }
+ Err(_) => format_upid(&remote_upid.upid),
+ }
+ } else {
+ format_upid(&item.upid)
+ }
+ .into()
+ })
+ .into(),
+ DataTableColumn::new(tr!("Status"))
+ .width("200px")
+ .render(|item: &TaskListItem| {
+ let text = item.status.as_deref().unwrap_or("");
+ html! {text}
+ })
+ .into(),
+ ])
+}
+
+impl Component for PbsRemoteTaskList {
+ type Message = Option<(String, Option<i64>)>;
+ type Properties = RemoteTaskList;
+
+ fn create(_ctx: &yew::Context<Self>) -> Self {
+ Self {
+ columns: columns(),
+ upid: None,
+ }
+ }
+
+ fn update(&mut self, _ctx: &yew::Context<Self>, msg: Self::Message) -> bool {
+ self.upid = msg;
+ true
+ }
+
+ fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
+ let task = self
+ .upid
+ .as_ref()
+ .and_then(|(upid, endtime)| upid.parse::<RemoteUpid>().ok().map(|upid| (upid, endtime)))
+ .map(|(remote_upid, endtime)| {
+ // TODO PBS
+ let base_url = format!("/pve/remotes/{}/tasks", remote_upid.remote());
+ TaskViewer::new(remote_upid.to_string())
+ .endtime(endtime)
+ .base_url(base_url)
+ .on_close({
+ let link = ctx.link().clone();
+ move |_| link.send_message(None)
+ })
+ });
+ Column::new()
+ .class(FlexFit)
+ .with_child(
+ Tasks::new()
+ .base_url("/remote-tasks/list")
+ .on_show_task({
+ let link = ctx.link().clone();
+ move |(upid_str, endtime)| link.send_message(Some((upid_str, endtime)))
+ })
+ .columns(self.columns.clone()),
+ )
+ .with_optional_child(task)
+ .into()
+ }
+}
+
+impl From<RemoteTaskList> for VNode {
+ fn from(val: RemoteTaskList) -> Self {
+ let comp = VComp::new::<PbsRemoteTaskList>(Rc::new(val), None);
+ VNode::from(comp)
+ }
+}
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 8/9] ui: register pve tasks
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (13 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 7/9] ui: remotes: add tasks to global remote panel Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 9/9] ui: also show running remote tasks in 'running tasks' list Dominik Csapak
` (2 subsequent siblings)
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
list copied from pve-manager and adapted for register_task_description
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/lib.rs | 3 ++
ui/src/main.rs | 6 ++-
ui/src/tasks.rs | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 108 insertions(+), 2 deletions(-)
create mode 100644 ui/src/tasks.rs
diff --git a/ui/src/lib.rs b/ui/src/lib.rs
index 5c6d076..cee7791 100644
--- a/ui/src/lib.rs
+++ b/ui/src/lib.rs
@@ -32,6 +32,9 @@ pub mod pve;
pub mod renderer;
+mod tasks;
+pub use tasks::register_pve_tasks;
+
pub fn pdm_client() -> pdm_client::PdmClient<std::rc::Rc<proxmox_yew_comp::HttpClientWasm>> {
pdm_client::PdmClient(proxmox_yew_comp::CLIENT.with(|c| std::rc::Rc::clone(&c.borrow())))
}
diff --git a/ui/src/main.rs b/ui/src/main.rs
index 2ba7d47..42257a9 100644
--- a/ui/src/main.rs
+++ b/ui/src/main.rs
@@ -13,6 +13,7 @@ use pwt::widget::{Column, DesktopApp, Dialog, Mask};
use proxmox_login::Authentication;
use proxmox_yew_comp::common_api_types::TaskListItem;
+use proxmox_yew_comp::utils::init_task_descr_table_base;
use proxmox_yew_comp::{
authentication_from_cookie, http_get, register_auth_observer, AuthObserver, LoginPanel,
SubscriptionAlert,
@@ -20,7 +21,7 @@ use proxmox_yew_comp::{
//use pbs::MainMenu;
use pdm_api_types::subscription::{RemoteSubscriptionState, RemoteSubscriptions};
-use pdm_ui::{MainMenu, RemoteList, TopNavBar};
+use pdm_ui::{register_pve_tasks, MainMenu, RemoteList, TopNavBar};
type MsgRemoteList = Result<RemoteList, Error>;
@@ -274,7 +275,8 @@ fn main() {
yew::set_custom_panic_hook(panic_hook());
- //init_task_descr_table_pbs();
+ init_task_descr_table_base();
+ register_pve_tasks();
proxmox_yew_comp::http_setup(&proxmox_yew_comp::ExistingProduct::PDM);
pwt::props::set_http_get_method(
diff --git a/ui/src/tasks.rs b/ui/src/tasks.rs
new file mode 100644
index 0000000..6aa202a
--- /dev/null
+++ b/ui/src/tasks.rs
@@ -0,0 +1,101 @@
+use proxmox_yew_comp::utils::register_task_description;
+use pwt::tr;
+
+pub fn register_pve_tasks() {
+ register_task_description("qmstart", ("VM", tr!("Start")));
+ register_task_description("acmedeactivate", ("ACME Account", tr!("Deactivate")));
+ register_task_description("acmenewcert", ("SRV", tr!("Order Certificate")));
+ register_task_description("acmerefresh", ("ACME Account", tr!("Refresh")));
+ register_task_description("acmeregister", ("ACME Account", tr!("Register")));
+ register_task_description("acmerenew", ("SRV", tr!("Renew Certificate")));
+ register_task_description("acmerevoke", ("SRV", tr!("Revoke Certificate")));
+ register_task_description("acmeupdate", ("ACME Account", tr!("Update")));
+ register_task_description("auth-realm-sync", (tr!("Realm"), tr!("Sync")));
+ register_task_description("auth-realm-sync-test", (tr!("Realm"), tr!("Sync Preview")));
+ register_task_description("cephcreatemds", ("Ceph Metadata Server", tr!("Create")));
+ register_task_description("cephcreatemgr", ("Ceph Manager", tr!("Create")));
+ register_task_description("cephcreatemon", ("Ceph Monitor", tr!("Create")));
+ register_task_description("cephcreateosd", ("Ceph OSD", tr!("Create")));
+ register_task_description("cephcreatepool", ("Ceph Pool", tr!("Create")));
+ register_task_description("cephdestroymds", ("Ceph Metadata Server", tr!("Destroy")));
+ register_task_description("cephdestroymgr", ("Ceph Manager", tr!("Destroy")));
+ register_task_description("cephdestroymon", ("Ceph Monitor", tr!("Destroy")));
+ register_task_description("cephdestroyosd", ("Ceph OSD", tr!("Destroy")));
+ register_task_description("cephdestroypool", ("Ceph Pool", tr!("Destroy")));
+ register_task_description("cephdestroyfs", ("CephFS", tr!("Destroy")));
+ register_task_description("cephfscreate", ("CephFS", tr!("Create")));
+ register_task_description("cephsetpool", ("Ceph Pool", tr!("Edit")));
+ register_task_description("cephsetflags", tr!("Change global Ceph flags"));
+ register_task_description("clustercreate", tr!("Create Cluster"));
+ register_task_description("clusterjoin", tr!("Join Cluster"));
+ register_task_description("dircreate", (tr!("Directory Storage"), tr!("Create")));
+ register_task_description("dirremove", (tr!("Directory"), tr!("Remove")));
+ register_task_description("download", (tr!("File"), tr!("Download")));
+ register_task_description("hamigrate", ("HA", tr!("Migrate")));
+ register_task_description("hashutdown", ("HA", tr!("Shutdown")));
+ register_task_description("hastart", ("HA", tr!("Start")));
+ register_task_description("hastop", ("HA", tr!("Stop")));
+ register_task_description("imgcopy", tr!("Copy data"));
+ register_task_description("imgdel", tr!("Erase data"));
+ register_task_description("lvmcreate", (tr!("LVM Storage"), tr!("Create")));
+ register_task_description("lvmremove", ("Volume Group", tr!("Remove")));
+ register_task_description("lvmthincreate", (tr!("LVM-Thin Storage"), tr!("Create")));
+ register_task_description("lvmthinremove", ("Thinpool", tr!("Remove")));
+ register_task_description("migrateall", tr!("Bulk migrate VMs and Containers"));
+ register_task_description("move_volume", ("CT", tr!("Move Volume")));
+ register_task_description("pbs-download", ("VM/CT", tr!("File Restore Download")));
+ register_task_description("pull_file", ("CT", tr!("Pull file")));
+ register_task_description("push_file", ("CT", tr!("Push file")));
+ register_task_description("qmclone", ("VM", tr!("Clone")));
+ register_task_description("qmconfig", ("VM", tr!("Configure")));
+ register_task_description("qmcreate", ("VM", tr!("Create")));
+ register_task_description("qmdelsnapshot", ("VM", tr!("Delete Snapshot")));
+ register_task_description("qmdestroy", ("VM", tr!("Destroy")));
+ register_task_description("qmigrate", ("VM", tr!("Migrate")));
+ register_task_description("qmmove", ("VM", tr!("Move disk")));
+ register_task_description("qmpause", ("VM", tr!("Pause")));
+ register_task_description("qmreboot", ("VM", tr!("Reboot")));
+ register_task_description("qmreset", ("VM", tr!("Reset")));
+ register_task_description("qmrestore", ("VM", tr!("Restore")));
+ register_task_description("qmresume", ("VM", tr!("Resume")));
+ register_task_description("qmrollback", ("VM", tr!("Rollback")));
+ register_task_description("qmshutdown", ("VM", tr!("Shutdown")));
+ register_task_description("qmsnapshot", ("VM", tr!("Snapshot")));
+ register_task_description("qmstart", ("VM", tr!("Start")));
+ register_task_description("qmstop", ("VM", tr!("Stop")));
+ register_task_description("qmsuspend", ("VM", tr!("Hibernate")));
+ register_task_description("qmtemplate", ("VM", tr!("Convert to template")));
+ register_task_description("resize", ("VM/CT", tr!("Resize")));
+ register_task_description("spiceproxy", ("VM/CT", tr!("Console") + " (Spice)"));
+ register_task_description("spiceshell", tr!("Shell") + " (Spice)");
+ register_task_description("startall", tr!("Bulk start VMs and Containers"));
+ register_task_description("stopall", tr!("Bulk shutdown VMs and Containers"));
+ register_task_description("suspendall", tr!("Suspend all VMs"));
+ register_task_description("unknownimgdel", tr!("Destroy image from unknown guest"));
+ register_task_description("wipedisk", ("Device", tr!("Wipe Disk")));
+ register_task_description("vncproxy", ("VM/CT", tr!("Console")));
+ register_task_description("vncshell", tr!("Shell"));
+ register_task_description("vzclone", ("CT", tr!("Clone")));
+ register_task_description("vzcreate", ("CT", tr!("Create")));
+ register_task_description("vzdelsnapshot", ("CT", tr!("Delete Snapshot")));
+ register_task_description("vzdestroy", ("CT", tr!("Destroy")));
+ register_task_description("vzdump", |_ty, id| match id {
+ Some(id) => format!("VM/CT {id} - {}", tr!("Backup")),
+ None => tr!("Backup Job"),
+ });
+ register_task_description("vzmigrate", ("CT", tr!("Migrate")));
+ register_task_description("vzmount", ("CT", tr!("Mount")));
+ register_task_description("vzreboot", ("CT", tr!("Reboot")));
+ register_task_description("vzrestore", ("CT", tr!("Restore")));
+ register_task_description("vzresume", ("CT", tr!("Resume")));
+ register_task_description("vzrollback", ("CT", tr!("Rollback")));
+ register_task_description("vzshutdown", ("CT", tr!("Shutdown")));
+ register_task_description("vzsnapshot", ("CT", tr!("Snapshot")));
+ register_task_description("vzstart", ("CT", tr!("Start")));
+ register_task_description("vzstop", ("CT", tr!("Stop")));
+ register_task_description("vzsuspend", ("CT", tr!("Suspend")));
+ register_task_description("vztemplate", ("CT", tr!("Convert to template")));
+ register_task_description("vzumount", ("CT", tr!("Unmount")));
+ register_task_description("zfscreate", (tr!("ZFS Storage"), tr!("Create")));
+ register_task_description("zfsremove", ("ZFS Pool", tr!("Remove")));
+}
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] [PATCH datacenter-manager 9/9] ui: also show running remote tasks in 'running tasks' list
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (14 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 8/9] ui: register pve tasks Dominik Csapak
@ 2025-01-20 9:30 ` Dominik Csapak
2025-01-20 11:27 ` [pdm-devel] applied: [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dietmar Maurer
2025-01-21 8:41 ` [pdm-devel] " Lukas Wagner
17 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 9:30 UTC (permalink / raw)
To: pdm-devel
by doing a second api call for the remote tasks, and mixing in the
results into the list.
To correctly show the remote tasks in the list, use the 'render'
property to render the remote tasks differently.
On the OpenTask viewstate, use a different baseurl for the task viewer
for remote tasks.
To determine if a UPID is a remote one, try to parse it as a
`RemoteUpid`.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
ui/src/main.rs | 19 ++++++++++++++-
ui/src/top_nav_bar.rs | 54 +++++++++++++++++++++++++++++++++++++------
2 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/ui/src/main.rs b/ui/src/main.rs
index 42257a9..6e2c9b2 100644
--- a/ui/src/main.rs
+++ b/ui/src/main.rs
@@ -1,5 +1,6 @@
use anyhow::Error;
use gloo_timers::callback::Timeout;
+use serde_json::json;
use wasm_bindgen::JsCast;
use web_sys::HtmlElement;
@@ -135,7 +136,23 @@ impl Component for DatacenterManagerApp {
let running_tasks = Loader::new()
.on_change(ctx.link().callback(|_| Msg::TaskChanged))
- .loader("/nodes/localhost/tasks?running=1&limit=100");
+ .loader((
+ |url: AttrValue| async move {
+ // TODO replace with pdm client call
+ let params = Some(json!({
+ "limit": 100,
+ "running": true,
+ }));
+ let mut res: Vec<TaskListItem> =
+ http_get(url.to_string(), params.clone()).await?;
+
+ let res2: Vec<_> = http_get("/remote-tasks/list", params).await?;
+ res.extend_from_slice(&res2);
+
+ Ok(res.into_iter().take(100).collect())
+ },
+ "/nodes/localhost/tasks",
+ ));
let login_info = authentication_from_cookie(&proxmox_yew_comp::ExistingProduct::PDM);
diff --git a/ui/src/top_nav_bar.rs b/ui/src/top_nav_bar.rs
index 70ba524..07b3b23 100644
--- a/ui/src/top_nav_bar.rs
+++ b/ui/src/top_nav_bar.rs
@@ -13,11 +13,15 @@ use pwt::state::{Loader, Theme, ThemeObserver};
use pwt::widget::{Button, Container, Row, ThemeModeSelector, Tooltip};
use proxmox_yew_comp::common_api_types::TaskListItem;
+use proxmox_yew_comp::utils::{format_task_description, format_upid, set_location_href};
use proxmox_yew_comp::RunningTasksButton;
use proxmox_yew_comp::{http_get, HelpButton, LanguageDialog, TaskViewer, ThemeDialog};
use pwt_macros::builder;
+use pdm_api_types::RemoteUpid;
+use pdm_client::types::PveUpid;
+
use crate::widget::SearchBox;
#[derive(Deserialize)]
@@ -185,10 +189,38 @@ impl Component for PdmTopNavBar {
if let Some(username) = &props.username {
button_group.add_child(
- RunningTasksButton::new(props.running_tasks.clone()).on_show_task(
- ctx.link()
- .callback(|info| Msg::ChangeView(Some(ViewState::OpenTask(info)))),
- ),
+ RunningTasksButton::new(props.running_tasks.clone())
+ .on_show_task(
+ ctx.link()
+ .callback(|info| Msg::ChangeView(Some(ViewState::OpenTask(info)))),
+ )
+ .buttons(vec![
+ Button::new(tr!("Show Local Tasks"))
+ .class(ColorScheme::Primary)
+ .onclick(move |_| {
+ set_location_href("#/administration/tasks");
+ }),
+ Button::new(tr!("Show Remote Tasks"))
+ .class(ColorScheme::Primary)
+ .onclick(move |_| {
+ set_location_href("#/remotes/tasks");
+ }),
+ ])
+ .render(|item: &TaskListItem| {
+ if let Ok(remote_upid) = (&item.upid).parse::<RemoteUpid>() {
+ let description = match remote_upid.upid.parse::<PveUpid>() {
+ Ok(upid) => format_task_description(
+ &upid.worker_type,
+ upid.worker_id.as_deref(),
+ ),
+ Err(_) => format_upid(&remote_upid.upid),
+ };
+ format!("{} - {}", remote_upid.remote(), description)
+ } else {
+ format_upid(&item.upid)
+ }
+ .into()
+ }),
);
button_group.add_child(
@@ -207,9 +239,17 @@ impl Component for PdmTopNavBar {
ViewState::ThemeDialog => ThemeDialog::new()
.on_close(ctx.link().callback(|_| Msg::ChangeView(None)))
.into(),
- ViewState::OpenTask((task_id, _endtime)) => TaskViewer::new(task_id)
- .on_close(ctx.link().callback(|_| Msg::ChangeView(None)))
- .into(),
+ ViewState::OpenTask((task_id, _endtime)) => {
+ // TODO PBS
+ let base_url = task_id
+ .parse::<RemoteUpid>()
+ .ok()
+ .map(|upid| format!("/pve/remotes/{}/tasks", upid.remote()));
+ TaskViewer::new(task_id)
+ .base_url(base_url.unwrap_or("/nodes/localhost/tasks".to_string()))
+ .on_close(ctx.link().callback(|_| Msg::ChangeView(None)))
+ .into()
+ }
});
let src = if self.dark_mode {
--
2.39.5
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pdm-devel] applied: [PATCH yew-comp/datacenter-manager] ui: implement remote task list
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (15 preceding siblings ...)
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 9/9] ui: also show running remote tasks in 'running tasks' list Dominik Csapak
@ 2025-01-20 11:27 ` Dietmar Maurer
2025-01-20 11:34 ` Thomas Lamprecht
2025-01-21 8:41 ` [pdm-devel] " Lukas Wagner
17 siblings, 1 reply; 32+ messages in thread
From: Dietmar Maurer @ 2025-01-20 11:27 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
applied
bumped proxmox-yew-comp to 0.4.0
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional Dominik Csapak
@ 2025-01-20 11:30 ` Thomas Lamprecht
2025-01-20 12:10 ` Dominik Csapak
0 siblings, 1 reply; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 11:30 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
Am 20.01.25 um 10:29 schrieb Dominik Csapak:
> by converting the strings like '2014-05-01' into epochs by using the
> js-sys Date interface.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> src/tasks.rs | 22 +++++++++++++++++++++-
> 1 file changed, 21 insertions(+), 1 deletion(-)
>
> diff --git a/src/tasks.rs b/src/tasks.rs
> index 4ea243f..fa17602 100644
> --- a/src/tasks.rs
> +++ b/src/tasks.rs
> @@ -134,7 +134,27 @@ impl LoadableComponent for ProxmoxTasks {
> let store = self.store.clone();
>
> let form_context = self.filter_form_context.read();
> - let filter = form_context.get_submit_data();
> + let mut filter = form_context.get_submit_data();
> +
> + // Transform Date values
> + if let Some(since) = filter.get("since").and_then(|v| v.as_str()) {
> + let since = js_sys::Date::new(&wasm_bindgen::JsValue::from_str(since));
> + since.set_hours(0);
> + since.set_minutes(0);
> + since.set_seconds(0);
> + let since = (since.get_time() / 1000.0).round() as u64;
Is round doing something here? Could only be the case if one could enter
milliseconds in the since/until strings, and if that would be the case
it might be better to just call set_milliseconds(0) too.
> + filter["since"] = since.into();
> + }
> +
> + if let Some(until) = filter.get("until").and_then(|v| v.as_str()) {
> + let until = js_sys::Date::new(&wasm_bindgen::JsValue::from_str(until));
> + until.set_hours(23);
> + until.set_minutes(59);
> + until.set_seconds(59);
> + let until = (until.get_time() / 1000.0).round() as u64;
same here.
> + filter["until"] = until.into();
> + }
> +
> Box::pin(async move {
> let data = crate::http_get(&path, Some(filter)).await?;
> store.write().set_data(data);
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] applied: [PATCH yew-comp/datacenter-manager] ui: implement remote task list
2025-01-20 11:27 ` [pdm-devel] applied: [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dietmar Maurer
@ 2025-01-20 11:34 ` Thomas Lamprecht
0 siblings, 0 replies; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 11:34 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion,
Dietmar Maurer, Dominik Csapak
Am 20.01.25 um 12:27 schrieb Dietmar Maurer:
> applied
>
> bumped proxmox-yew-comp to 0.4.0
Why do you continue to keep rushing it patches without real review?!
The cover letter explicitly asked a question, specifically for Lukas,
you neither bothered waiting for an answer nor giving a reason for why
that was not required.
I want to actually have a real review process, what you recently do
with that apply rushing causes friction, more work for me to recheck
and rework things after the fact and gains nobody anything...
_please_ stop that.
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional
2025-01-20 11:30 ` Thomas Lamprecht
@ 2025-01-20 12:10 ` Dominik Csapak
2025-01-21 8:33 ` Thomas Lamprecht
0 siblings, 1 reply; 32+ messages in thread
From: Dominik Csapak @ 2025-01-20 12:10 UTC (permalink / raw)
To: Thomas Lamprecht, Proxmox Datacenter Manager development discussion
On 1/20/25 12:30, Thomas Lamprecht wrote:
> Am 20.01.25 um 10:29 schrieb Dominik Csapak:
>> by converting the strings like '2014-05-01' into epochs by using the
>> js-sys Date interface.
>>
>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>> ---
>> src/tasks.rs | 22 +++++++++++++++++++++-
>> 1 file changed, 21 insertions(+), 1 deletion(-)
>>
>> diff --git a/src/tasks.rs b/src/tasks.rs
>> index 4ea243f..fa17602 100644
>> --- a/src/tasks.rs
>> +++ b/src/tasks.rs
>> @@ -134,7 +134,27 @@ impl LoadableComponent for ProxmoxTasks {
>> let store = self.store.clone();
>>
>> let form_context = self.filter_form_context.read();
>> - let filter = form_context.get_submit_data();
>> + let mut filter = form_context.get_submit_data();
>> +
>> + // Transform Date values
>> + if let Some(since) = filter.get("since").and_then(|v| v.as_str()) {
>> + let since = js_sys::Date::new(&wasm_bindgen::JsValue::from_str(since));
>> + since.set_hours(0);
>> + since.set_minutes(0);
>> + since.set_seconds(0);
>> + let since = (since.get_time() / 1000.0).round() as u64;
>
> Is round doing something here? Could only be the case if one could enter
> milliseconds in the since/until strings, and if that would be the case
> it might be better to just call set_milliseconds(0) too.
We currently rely on the browser to display a date time selector, and since
this is browser dependent, we technically cannot really know what we're getting here.
so the round was just to be overly careful to respect the input, but yeah
simply omitting the round to truncate the decimal places seems better.
and 'f64 as u64' already does rounds to zero, so i'll send a follow up to remove the round()
to reference the offical rust docs for 'as':
https://doc.rust-lang.org/1.84.0/reference/expressions/operator-expr.html#type-cast-expressions
>
>> + filter["since"] = since.into();
>> + }
>> +
>> + if let Some(until) = filter.get("until").and_then(|v| v.as_str()) {
>> + let until = js_sys::Date::new(&wasm_bindgen::JsValue::from_str(until));
>> + until.set_hours(23);
>> + until.set_minutes(59);
>> + until.set_seconds(59);
>> + let until = (until.get_time() / 1000.0).round() as u64;
>
> same here.
>
>> + filter["until"] = until.into();
>> + }
>> +
>> Box::pin(async move {
>> let data = crate::http_get(&path, Some(filter)).await?;
>> store.write().set_data(data);
>
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list Dominik Csapak
@ 2025-01-20 17:29 ` Thomas Lamprecht
2025-01-21 9:43 ` Dominik Csapak
0 siblings, 1 reply; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 17:29 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
Am 20.01.25 um 10:29 schrieb Dominik Csapak:
> and prevent the list to load multiple times the same data when filters
> change rapidly (e.g. on first render or when the user enters text in
> the filters)
The load debouncing would have been better off as separate commit IMO,
quite unrelated to the auto-load feature, but already pushed out so to
late to improve here..
>
> reset the list when the filters changed or the reload button is pressed.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> src/tasks.rs | 72 ++++++++++++++++++++++++++++++++++++++++++----------
> 1 file changed, 59 insertions(+), 13 deletions(-)
>
> diff --git a/src/tasks.rs b/src/tasks.rs
> index fa17602..0ef80c6 100644
> --- a/src/tasks.rs
> +++ b/src/tasks.rs
> @@ -6,6 +6,8 @@ use anyhow::Error;
>
> use pwt::widget::form::{Field, Form, FormContext, InputType};
>
> +use gloo_timers::callback::Timeout;
> +use serde_json::Map;
> use yew::html::IntoPropValue;
> use yew::virtual_dom::{VComp, VNode};
>
> @@ -26,6 +28,10 @@ use crate::{LoadableComponent, LoadableComponentContext, LoadableComponentMaster
>
> use super::{TaskStatusSelector, TaskTypeSelector};
>
> +const FILTER_UPDATE_BUFFER_MS: u32 = 150;
> +const BATCH_LIMIT: u64 = 500;
> +const LOAD_BUFFER_ROWS: usize = 20;
Any reasoning on those sizes? Don't get me wrong, I'm fine with values being
chosen empirically, hard to do that otherwise here, but some background would
be still nice to have should one have to adapt those sometimes.
I.e., could it make sense to make that dependent on the height of the
component? Or maybe allow overriding the batch-limit from the user, if one
wants to filter through much more tasks, but that one is not really important
for this patch now.
> #[derive(PartialEq, Properties)]
> #[builder]
> pub struct Tasks {
> @@ -75,6 +81,7 @@ pub enum ViewDialog {
> pub enum Msg {
> Redraw,
> ToggleFilter,
> + LoadBatch(u64), // start
"start offset" might be a bit more telling, "offset" probably would
have been the better name for this, but well, that ship has somewhat
sailed for now (in our released products backends).
> UpdateFilter,
> }
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function Dominik Csapak
@ 2025-01-20 17:29 ` Thomas Lamprecht
2025-01-21 9:44 ` Dominik Csapak
0 siblings, 1 reply; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 17:29 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
Am 20.01.25 um 10:29 schrieb Dominik Csapak:
> +/// Formats the given worker type and id to a Human readable task description
> +pub fn format_task_description(worker_type: &str, worker_id: Option<&str>) -> String {
> + if let Some(text) = lookup_task_description(worker_type, worker_id) {
> + text
> + } else {
> + match worker_id {
> + Some(id) => format!("{} {}", worker_type, id),
nit: please inline the variables into the format string where possible and
when touching the code anyway.
> + None => worker_type.to_string(),
> }
> }
> }
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 6/7] tasks: make columns configurable
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 6/7] tasks: make columns configurable Dominik Csapak
@ 2025-01-20 17:37 ` Thomas Lamprecht
0 siblings, 0 replies; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 17:37 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
Am 20.01.25 um 10:29 schrieb Dominik Csapak:
> impl Default for Tasks {
> @@ -93,6 +98,46 @@ pub struct ProxmoxTasks {
> start: u64,
> last_filter: serde_json::Value,
> load_timeout: Option<Timeout>,
> + columns: Rc<Vec<DataTableHeader<TaskListItem>>>,
> +}
> +
> +impl ProxmoxTasks {
> + fn columns(ctx: &LoadableComponentContext<Self>) -> Rc<Vec<DataTableHeader<TaskListItem>>> {
> + if let Some(columns) = ctx.props().columns.clone() {
> + columns
> + } else {
> + Rc::new(vec![
> + DataTableColumn::new(tr!("Start Time"))
> + .width("130px")
> + .render(|item: &TaskListItem| render_epoch_short(item.starttime).into())
> + .into(),
> + DataTableColumn::new(tr!("End Time"))
> + .width("130px")
> + .render(|item: &TaskListItem| match item.endtime {
> + Some(endtime) => render_epoch_short(endtime).into(),
> + None => html! {},
> + })
> + .into(),
> + DataTableColumn::new(tr!("User name"))
> + .width("150px")
> + .render(|item: &TaskListItem| {
> + html! {&item.user}
> + })
> + .into(),
pre-existing I know, but just noticing it here:
Username can be quite long, maybe we could make this a flex with a min-width.
The flex ratio should then still be quite a bit lower than the one from the
description.
> + DataTableColumn::new(tr!("Description"))
> + .flex(1)
> + .render(move |item: &TaskListItem| html! {format_upid(&item.upid)})
> + .into(),
> + DataTableColumn::new(tr!("Status"))
> + .width("200px")
> + .render(|item: &TaskListItem| {
> + let text = item.status.as_deref().unwrap_or("");
> + html! {text}
> + })
> + .into(),
Also pre-existing, but having that flex (with a bit lower ratio that description)
could also be nice to see more of the task error message when skimming through
the list.
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH datacenter-manager 1/9] server: factor out task filters into `TaskFilters` type
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 1/9] server: factor out task filters into `TaskFilters` type Dominik Csapak
@ 2025-01-20 17:42 ` Thomas Lamprecht
0 siblings, 0 replies; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 17:42 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
Am 20.01.25 um 10:29 schrieb Dominik Csapak:
> so we can reuse it
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> lib/pdm-api-types/src/lib.rs | 81 ++++++++++++++++++++++++++++++++++-
> server/src/api/nodes/tasks.rs | 80 ++++++++--------------------------
> 2 files changed, 98 insertions(+), 63 deletions(-)
>
> diff --git a/lib/pdm-api-types/src/lib.rs b/lib/pdm-api-types/src/lib.rs
> index bf312cc..3844907 100644
> --- a/lib/pdm-api-types/src/lib.rs
> +++ b/lib/pdm-api-types/src/lib.rs
> @@ -219,7 +219,7 @@ pub enum NodePowerCommand {
>
> #[api]
> /// The state of a task.
> -#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
> +#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
> #[serde(rename_all = "lowercase")]
> pub enum TaskStateType {
> /// Ok
> @@ -418,3 +418,82 @@ impl fmt::Display for RemoteUpid {
>
> serde_plain::derive_deserialize_from_fromstr!(RemoteUpid, "valid remote upid");
> serde_plain::derive_serialize_from_display!(RemoteUpid);
> +
> +fn limit_default() -> u64 {
> + 50
> +}
> +
> +#[api(
> + properties: {
> + start: {
> + type: u64,
> + description: "List tasks beginning from this offset.",
> + default: 0,
> + optional: true,
> + },
> + limit: {
> + type: u64,
> + description: "Only list this amount of tasks. (0 means no limit)",
> + default: 50,
> + optional: true,
> + },
> + running: {
> + type: bool,
> + description: "Only list running tasks.",
> + optional: true,
> + default: false,
> + },
> + errors: {
> + type: bool,
> + description: "Only list erroneous tasks.",
> + optional:true,
> + default: false,
> + },
> + userfilter: {
> + optional: true,
> + type: String,
> + description: "Only list tasks from this user.",
> + },
> + since: {
> + type: i64,
> + description: "Only list tasks since this UNIX epoch.",
> + optional: true,
> + },
> + until: {
> + type: i64,
> + description: "Only list tasks until this UNIX epoch.",
> + optional: true,
> + },
> + typefilter: {
> + optional: true,
> + type: String,
> + description: "Only list tasks whose type contains this.",
> + },
> + statusfilter: {
General thought:
Before relying on these spellings even more we might take the chance and clean that up,
i.e. here already now directly and in our other projects support both until next
major release. I.e., separate words and not glue them together or just drop "filter",
it's used inconsistently anyway.
Might be little, but doubling down on ugly things even if they are small is not really
great either. Maybe we can go through the whole API schema for the next major release
and do a bigger clean-up before the pain of doing that increases significantly.
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch Dominik Csapak
@ 2025-01-20 17:45 ` Thomas Lamprecht
2025-01-21 8:29 ` Dietmar Maurer
0 siblings, 1 reply; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-20 17:45 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion, Dominik Csapak
Am 20.01.25 um 10:29 schrieb Dominik Csapak:
> instead of failing the whole cache retrieval, just skip the remote.
>
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> server/src/task_cache.rs | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/server/src/task_cache.rs b/server/src/task_cache.rs
> index 3ef24b1..210aa5f 100644
> --- a/server/src/task_cache.rs
> +++ b/server/src/task_cache.rs
> @@ -42,7 +42,13 @@ pub async fn get_tasks(max_age: i64) -> Result<Vec<TaskListItem>, Error> {
> // Data in cache is recent enough and has not been invalidated.
> all_tasks.extend(tasks);
> } else {
> - let tasks = fetch_tasks(remote).await?;
> + let tasks = match fetch_tasks(remote).await {
> + Ok(tasks) => tasks,
> + Err(err) => {
> + // ignore errors for not reachable remotes
Even if it's now the case, it's not hard-coded for the future that an
error here will always stem from an unreachable remote; at least
logging that as warning might be good to allow debugging odd things
in a setup.
> + continue;
> + }
> + };
> cache.set_tasks(remote_name.as_str(), tasks.clone(), now);
>
> all_tasks.extend(tasks);
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch
2025-01-20 17:45 ` Thomas Lamprecht
@ 2025-01-21 8:29 ` Dietmar Maurer
0 siblings, 0 replies; 32+ messages in thread
From: Dietmar Maurer @ 2025-01-21 8:29 UTC (permalink / raw)
To: Proxmox Datacenter Manager development discussion,
Thomas Lamprecht, Dominik Csapak
> > - let tasks = fetch_tasks(remote).await?;
> > + let tasks = match fetch_tasks(remote).await {
> > + Ok(tasks) => tasks,
> > + Err(err) => {
> > + // ignore errors for not reachable remotes
>
> Even if it's now the case, it's not hard-coded for the future that an
> error here will always stem from an unreachable remote; at least
> logging that as warning might be good to allow debugging odd things
> in a setup.
Agreed. The problem is that it generate to much logs, so IMHO we need some special code to avoid frequent logging of the same message...
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional
2025-01-20 12:10 ` Dominik Csapak
@ 2025-01-21 8:33 ` Thomas Lamprecht
2025-01-21 9:46 ` Dominik Csapak
0 siblings, 1 reply; 32+ messages in thread
From: Thomas Lamprecht @ 2025-01-21 8:33 UTC (permalink / raw)
To: Dominik Csapak, Proxmox Datacenter Manager development discussion
Am 20.01.25 um 13:10 schrieb Dominik Csapak:
> On 1/20/25 12:30, Thomas Lamprecht wrote:
>> Am 20.01.25 um 10:29 schrieb Dominik Csapak:
>>> + let since = (since.get_time() / 1000.0).round() as u64;
>>
>> Is round doing something here? Could only be the case if one could enter
>> milliseconds in the since/until strings, and if that would be the case
>> it might be better to just call set_milliseconds(0) too.
>
> We currently rely on the browser to display a date time selector, and since
> this is browser dependent, we technically cannot really know what we're getting here.
>
> so the round was just to be overly careful to respect the input, but yeah
> simply omitting the round to truncate the decimal places seems better.
>
> and 'f64 as u64' already does rounds to zero, so i'll send a follow up to remove the round()
Alternatively one could use .trunc() then I guess, albeit rounding towards zero
should be actually already do exactly the same thing I think.
> to reference the offical rust docs for 'as':
> https://doc.rust-lang.org/1.84.0/reference/expressions/operator-expr.html#type-cast-expressions
Thanks for looking this actually up!
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
` (16 preceding siblings ...)
2025-01-20 11:27 ` [pdm-devel] applied: [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dietmar Maurer
@ 2025-01-21 8:41 ` Lukas Wagner
17 siblings, 0 replies; 32+ messages in thread
From: Lukas Wagner @ 2025-01-21 8:41 UTC (permalink / raw)
To: Dominik Csapak, pdm-devel
On 2025-01-20 10:29, Dominik Csapak wrote:
> I noticed that the task caching could be improved a bit, e.g. we discard
> the cache when it's older than some 'max-age' but old (finished!) tasks
> will never get updated, they could at most be purged from the cache.
>
> IMHO it does not make sense to control the cache this way with a
> 'max-age' variable, maybe @Lukas can look over that again and see
> if we can cache the tasks better here?
> (note that we can't have much above ~1000 tasks in PVE since that
> is the number where we purge older ones there. I think we could keep
> older tasks for remotes and also just purge them if the number is above
> some threshold, maybe a bit higher than what PVE has).
I'll have a look!
--
- Lukas
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list
2025-01-20 17:29 ` Thomas Lamprecht
@ 2025-01-21 9:43 ` Dominik Csapak
0 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-21 9:43 UTC (permalink / raw)
To: Thomas Lamprecht, Proxmox Datacenter Manager development discussion
On 1/20/25 18:29, Thomas Lamprecht wrote:
> Am 20.01.25 um 10:29 schrieb Dominik Csapak:
>> and prevent the list to load multiple times the same data when filters
>> change rapidly (e.g. on first render or when the user enters text in
>> the filters)
>
> The load debouncing would have been better off as separate commit IMO,
> quite unrelated to the auto-load feature, but already pushed out so to
> late to improve here..
>
>>
>> reset the list when the filters changed or the reload button is pressed.
>>
>> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
>> ---
>> src/tasks.rs | 72 ++++++++++++++++++++++++++++++++++++++++++----------
>> 1 file changed, 59 insertions(+), 13 deletions(-)
>>
>> diff --git a/src/tasks.rs b/src/tasks.rs
>> index fa17602..0ef80c6 100644
>> --- a/src/tasks.rs
>> +++ b/src/tasks.rs
>> @@ -6,6 +6,8 @@ use anyhow::Error;
>>
>> use pwt::widget::form::{Field, Form, FormContext, InputType};
>>
>> +use gloo_timers::callback::Timeout;
>> +use serde_json::Map;
>> use yew::html::IntoPropValue;
>> use yew::virtual_dom::{VComp, VNode};
>>
>> @@ -26,6 +28,10 @@ use crate::{LoadableComponent, LoadableComponentContext, LoadableComponentMaster
>>
>> use super::{TaskStatusSelector, TaskTypeSelector};
>>
>> +const FILTER_UPDATE_BUFFER_MS: u32 = 150;
>> +const BATCH_LIMIT: u64 = 500;
>> +const LOAD_BUFFER_ROWS: usize = 20;
>
> Any reasoning on those sizes? Don't get me wrong, I'm fine with values being
> chosen empirically, hard to do that otherwise here, but some background would
> be still nice to have should one have to adapt those sometimes.
the batch limit is the same as the PVE/PBS (extjs) one, so no real
thought went into here
the filter update buffer ms is a bit dependent on typing speed, so
this was just a value that seemed to work for me (when i typed
not overly fast or slow). I don't think there is really a good
value that fits all sadly
>
> I.e., could it make sense to make that dependent on the height of the
> component? Or maybe allow overriding the batch-limit from the user, if one
> wants to filter through much more tasks, but that one is not really important
> for this patch now.
the buffer rows has more to do with scroll speed than component height
and i tried to find a good compromise between loading too early
when it's not clear the user wants to look so far back vs
too late when the user is already at the end (while trying to scroll
in a "normal" speed)
i can add some comments here if you want
>
>> #[derive(PartialEq, Properties)]
>> #[builder]
>> pub struct Tasks {
>> @@ -75,6 +81,7 @@ pub enum ViewDialog {
>> pub enum Msg {
>> Redraw,
>> ToggleFilter,
>> + LoadBatch(u64), // start
>
> "start offset" might be a bit more telling, "offset" probably would
> have been the better name for this, but well, that ship has somewhat
> sailed for now (in our released products backends).
>
>> UpdateFilter,
>> }
>
>
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function
2025-01-20 17:29 ` Thomas Lamprecht
@ 2025-01-21 9:44 ` Dominik Csapak
0 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-21 9:44 UTC (permalink / raw)
To: Thomas Lamprecht, Proxmox Datacenter Manager development discussion
On 1/20/25 18:29, Thomas Lamprecht wrote:
> Am 20.01.25 um 10:29 schrieb Dominik Csapak:
>> +/// Formats the given worker type and id to a Human readable task description
>> +pub fn format_task_description(worker_type: &str, worker_id: Option<&str>) -> String {
>> + if let Some(text) = lookup_task_description(worker_type, worker_id) {
>> + text
>> + } else {
>> + match worker_id {
>> + Some(id) => format!("{} {}", worker_type, id),
>
> nit: please inline the variables into the format string where possible and
> when touching the code anyway.
yeah sorry I overlooked that. Should I send a follow up for this now or
should i leave it and adapt when we have to change it the next time around?
>
>> + None => worker_type.to_string(),
>> }
>> }
>> }
>
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional
2025-01-21 8:33 ` Thomas Lamprecht
@ 2025-01-21 9:46 ` Dominik Csapak
0 siblings, 0 replies; 32+ messages in thread
From: Dominik Csapak @ 2025-01-21 9:46 UTC (permalink / raw)
To: Thomas Lamprecht, Proxmox Datacenter Manager development discussion
On 1/21/25 09:33, Thomas Lamprecht wrote:
> Am 20.01.25 um 13:10 schrieb Dominik Csapak:
>> On 1/20/25 12:30, Thomas Lamprecht wrote:
>>> Am 20.01.25 um 10:29 schrieb Dominik Csapak:
>>>> + let since = (since.get_time() / 1000.0).round() as u64;
>>>
>>> Is round doing something here? Could only be the case if one could enter
>>> milliseconds in the since/until strings, and if that would be the case
>>> it might be better to just call set_milliseconds(0) too.
>>
>> We currently rely on the browser to display a date time selector, and since
>> this is browser dependent, we technically cannot really know what we're getting here.
>>
>> so the round was just to be overly careful to respect the input, but yeah
>> simply omitting the round to truncate the decimal places seems better.
>>
>> and 'f64 as u64' already does rounds to zero, so i'll send a follow up to remove the round()
>
> Alternatively one could use .trunc() then I guess, albeit rounding towards zero
> should be actually already do exactly the same thing I think.
>
>> to reference the offical rust docs for 'as':
>> https://doc.rust-lang.org/1.84.0/reference/expressions/operator-expr.html#type-cast-expressions
>
> Thanks for looking this actually up!
i already sent a patch for this on yew-devel (since it's not related to pdm):
https://lore.proxmox.com/yew-devel/20250120124146.2952068-1-d.csapak@proxmox.com/
without truncate (just the type casting)
please let me know if that's enough or if you'd prefer a manual truncate
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2025-01-21 9:46 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-01-20 9:29 [pdm-devel] [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 1/7] tasks: make date filter functional Dominik Csapak
2025-01-20 11:30 ` Thomas Lamprecht
2025-01-20 12:10 ` Dominik Csapak
2025-01-21 8:33 ` Thomas Lamprecht
2025-01-21 9:46 ` Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 2/7] tasks: load more tasks on end of list Dominik Csapak
2025-01-20 17:29 ` Thomas Lamprecht
2025-01-21 9:43 ` Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 3/7] utils: factor out task description into own function Dominik Csapak
2025-01-20 17:29 ` Thomas Lamprecht
2025-01-21 9:44 ` Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 4/7] running tasks: make TaskListItem renderer configurable Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 5/7] running tasks: make buttons configurable Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 6/7] tasks: make columns configurable Dominik Csapak
2025-01-20 17:37 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH yew-comp 7/7] tasks: make the 'show task' action configurable Dominik Csapak
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 1/9] server: factor out task filters into `TaskFilters` type Dominik Csapak
2025-01-20 17:42 ` Thomas Lamprecht
2025-01-20 9:29 ` [pdm-devel] [PATCH datacenter-manager 2/9] server: task cache: skip remotes with errors on fetch Dominik Csapak
2025-01-20 17:45 ` Thomas Lamprecht
2025-01-21 8:29 ` Dietmar Maurer
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 3/9] server: task cache: add filter options Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 4/9] server: task cache: reverse task order Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 5/9] pdm-client: export PveUpid Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 6/9] ui: refactor RemoteConfigPanel into own module Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 7/9] ui: remotes: add tasks to global remote panel Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 8/9] ui: register pve tasks Dominik Csapak
2025-01-20 9:30 ` [pdm-devel] [PATCH datacenter-manager 9/9] ui: also show running remote tasks in 'running tasks' list Dominik Csapak
2025-01-20 11:27 ` [pdm-devel] applied: [PATCH yew-comp/datacenter-manager] ui: implement remote task list Dietmar Maurer
2025-01-20 11:34 ` Thomas Lamprecht
2025-01-21 8:41 ` [pdm-devel] " Lukas Wagner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox