From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id A8F241FF0EA for ; Fri, 14 Aug 2026 15:45:58 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 80566223A2; Fri, 14 Aug 2026 15:45:53 +0200 (CEST) From: Dominik Csapak To: pdm-devel@lists.proxmox.com Subject: [PATCH datacenter-manager 7/8] ui: pve: show ha maintenance mode for nodes Date: Fri, 14 Aug 2026 15:44:03 +0200 Message-ID: <20260814134548.3446943-8-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260814134548.3446943-1-d.csapak@proxmox.com> References: <20260814134548.3446943-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.184 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust RDNS_NONE 1.274 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: QHUMWEC2W3FQP72UNENI267NRQ54ERSM X-Message-ID-Hash: QHUMWEC2W3FQP72UNENI267NRQ54ERSM X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: By rendering a small `Badge` after the nodename in the pve tree and the resource tree, when the node is in ha maintenance mode. I opted for showing the ha state here differently than in PVE, because seeing the online/offline state separately from the hastat can make sense (so there is no ambiguity if the node is online or offline). To do this we have to add the hastate to the `PveNodeResource` and wire that through from the /cluster/resources call. We also have to update the pwt-assets submodule to get the necessary CSS classes for the badge. This partially fixes #7371. Signed-off-by: Dominik Csapak --- lib/pdm-api-types/src/resource.rs | 2 ++ server/src/api/resources.rs | 1 + ui/pwt-assets | 2 +- ui/src/pve/tree.rs | 11 +++++++++-- ui/src/renderer.rs | 14 +++++++++++++- ui/src/widget/resource_tree.rs | 3 ++- 6 files changed, 28 insertions(+), 5 deletions(-) diff --git a/lib/pdm-api-types/src/resource.rs b/lib/pdm-api-types/src/resource.rs index 18177dab..0db56dc4 100644 --- a/lib/pdm-api-types/src/resource.rs +++ b/lib/pdm-api-types/src/resource.rs @@ -490,6 +490,8 @@ pub struct PveNodeResource { pub status: String, /// Subscription level pub level: String, + /// HA State + pub hastate: String, } #[api] diff --git a/server/src/api/resources.rs b/server/src/api/resources.rs index 09d2b88d..2e4b3aba 100644 --- a/server/src/api/resources.rs +++ b/server/src/api/resources.rs @@ -1189,6 +1189,7 @@ pub(super) fn map_pve_node(remote: &str, resource: ClusterResource) -> Option None, } diff --git a/ui/pwt-assets b/ui/pwt-assets index cd2819ed..5b3a866f 160000 --- a/ui/pwt-assets +++ b/ui/pwt-assets @@ -1 +1 @@ -Subproject commit cd2819edb53c4b01a9df7f0a2f1f43736ff677fa +Subproject commit 5b3a866ff9e2a0945bf5d84176d0d23ae83ef782 diff --git a/ui/src/pve/tree.rs b/ui/src/pve/tree.rs index f68c728e..75eaa362 100644 --- a/ui/src/pve/tree.rs +++ b/ui/src/pve/tree.rs @@ -29,7 +29,9 @@ use pdm_api_types::{ use crate::{ get_deep_url, - renderer::{render_resource_name, render_status_icon, render_tree_column}, + renderer::{ + render_resource_extra, render_resource_name, render_status_icon, render_tree_column, + }, widget::MigrateWindow, }; @@ -572,6 +574,7 @@ fn create_empty_node(node_id: String) -> PveTreeNode { uptime: Default::default(), status: Default::default(), level: Default::default(), + hastate: Default::default(), })) } @@ -590,6 +593,7 @@ fn columns( .flex(1) .tree_column(store) .render(move |entry: &PveTreeNode| { + let mut extra = None; let (icon, text) = match entry { PveTreeNode::Root if loading => ( Container::from_tag("i").class("pwt-loading-icon"), @@ -600,11 +604,14 @@ fn columns( tr!("Datacenter"), ), PveTreeNode::Resource(r) => { + extra = render_resource_extra(r); (render_status_icon(r), render_resource_name(r, true)) } }; - render_tree_column(icon.into(), text).into() + render_tree_column(icon.into(), text) + .with_optional_child(extra) + .into() }) .into(), DataTableColumn::new(tr!("Tags")) diff --git a/ui/src/renderer.rs b/ui/src/renderer.rs index 0eecc889..b116fab8 100644 --- a/ui/src/renderer.rs +++ b/ui/src/renderer.rs @@ -2,7 +2,7 @@ use proxmox_yew_comp::MeterLabel; use pwt::css::{AlignItems, FlexFit, FontStyle, JustifyContent}; use pwt::prelude::*; use pwt::props::ContainerBuilder; -use pwt::widget::{Column, Container, Fa, Row}; +use pwt::widget::{Badge, Column, Container, Fa, Row}; use proxmox_human_byte::HumanByte; @@ -22,6 +22,18 @@ pub fn render_resource_name(resource: impl AsResourceView, vmid_first: bool) -> } } +pub fn render_resource_extra(resource: impl AsResourceView) -> Option { + match resource.as_resource_view() { + ResourceView::PveNode(node) => (node.hastate == "maintenance").then_some( + Badge::new(tr!("HA Maintenance")) + .color_scheme(pwt::css::ColorScheme::PrimaryContainer) + .icon("wrench") + .into(), + ), + _ => None, + } +} + pub fn render_resource_icon(resource: impl AsResourceView) -> Fa { let class = match resource.as_resource_view() { ResourceView::PveStorage(_) => "database", diff --git a/ui/src/widget/resource_tree.rs b/ui/src/widget/resource_tree.rs index 337d8e23..bf2ffee3 100644 --- a/ui/src/widget/resource_tree.rs +++ b/ui/src/widget/resource_tree.rs @@ -35,7 +35,7 @@ use crate::{ dashboard::view::ViewContext, get_deep_url, pve::utils::render_guest_tags, - renderer::{render_resource_name, render_status_icon}, + renderer::{render_resource_extra, render_resource_name, render_status_icon}, }; const REFRESH_TIME_S: u32 = 60; @@ -381,6 +381,7 @@ fn columns( Row::new() .gap(1) .with_child(render_resource_name(resource, true)) + .with_optional_child(render_resource_extra(resource)) .with_child(render_guest_tags(match resource { Resource::PveQemu(pve_qemu_resource) => &pve_qemu_resource.tags[..], Resource::PveLxc(pve_lxc_resource) => &pve_lxc_resource.tags[..], -- 2.47.3