all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status
@ 2025-11-27 10:44 Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions Lukas Wagner
                   ` (18 more replies)
  0 siblings, 19 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel




proxmox:

Lukas Wagner (4):
  add bindings for /nodes/{node}/apt/versions
  add bindings for /nodes/{node}/apt/repositories
  make refresh
  proxmox-apt-api-types: make APTStandardRepository compatible with
    PVE's serialization of the type

 proxmox-apt-api-types/Cargo.toml     |   1 +
 proxmox-apt-api-types/src/lib.rs     |   7 +-
 pve-api-types/Cargo.toml             |   1 +
 pve-api-types/generate.pl            |   2 +
 pve-api-types/src/generated/code.rs  |  24 ++++-
 pve-api-types/src/generated/types.rs | 128 +++++++++++++++++++++++++++
 pve-api-types/src/types/mod.rs       |   2 +
 7 files changed, 162 insertions(+), 3 deletions(-)


proxmox-yew-comp:

Lukas Wagner (1):
  apt repositories: add 'status_only' property

 src/apt_repositories.rs | 41 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)


proxmox-datacenter-manager:

Lukas Wagner (13):
  pdm-api-types: reuse APTUpdateInfo from proxmox_apt_api_types
  pbs-client: add bindings for /nodes/localhost/apt/versions
  pbs-client: add bindings for /nodes/localhost/apt/repositories
  remote-updates:  include version information in node update summary
  remote updates: include repository status in node summary
  api: pve/pbs: add passthrough endpoint for APT repo configuration
  ui: remote updates: show table header
  ui: remote updates: show main product version in overview table
  ui: remote updates: show repository status column
  ui: remote updates: show repo status details when selecting a node
  ui: remote updates: don't attempt to load current status for
    unavailable nodes
  ui: remote updates: use 'building-o' icon for PBS nodes
  ui: remote updates: use explicit indices for parameters in tr! macro

 lib/pdm-api-types/Cargo.toml            |   1 +
 lib/pdm-api-types/src/lib.rs            |  33 +-
 lib/pdm-api-types/src/remote_updates.rs |  46 +++
 server/src/api/remote_updates.rs        |  53 +++-
 server/src/pbs_client.rs                |  18 ++
 server/src/remote_updates.rs            | 112 ++++++-
 ui/src/remotes/updates.rs               | 384 +++++++++++++++++-------
 7 files changed, 494 insertions(+), 153 deletions(-)


Summary over all repositories:
  15 files changed, 690 insertions(+), 163 deletions(-)

-- 
Generated by murpp 0.9.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] 24+ messages in thread

* [pdm-devel] [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 20:46   ` [pdm-devel] applied: " Thomas Lamprecht
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 2/4] add bindings for /nodes/{node}/apt/repositories Lukas Wagner
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 pve-api-types/generate.pl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/pve-api-types/generate.pl b/pve-api-types/generate.pl
index 88193a2a..9a56b966 100755
--- a/pve-api-types/generate.pl
+++ b/pve-api-types/generate.pl
@@ -365,6 +365,7 @@ Schema2Rust::derive('CreateToken' => 'Default');
 api(GET => '/nodes/{node}/apt/update', 'list_available_updates', 'return-name' => 'AptUpdateInfo');
 api(POST => '/nodes/{node}/apt/update', 'update_apt_database', 'output-type' => 'PveUpid', 'param-name' => 'AptUpdateParams');
 api(GET => '/nodes/{node}/apt/changelog', 'get_package_changelog', 'output-type' => 'String');
+api(GET => '/nodes/{node}/apt/versions', 'get_package_versions', 'return-name' => 'InstalledPackage');
 
 Schema2Rust::generate_enum('SdnObjectState', {
     type => 'string',
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH proxmox 2/4] add bindings for /nodes/{node}/apt/repositories
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 20:46   ` [pdm-devel] applied: " Thomas Lamprecht
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 3/4] make refresh Lukas Wagner
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 pve-api-types/Cargo.toml       | 1 +
 pve-api-types/generate.pl      | 1 +
 pve-api-types/src/types/mod.rs | 2 ++
 3 files changed, 4 insertions(+)

diff --git a/pve-api-types/Cargo.toml b/pve-api-types/Cargo.toml
index 44facfe8..04dd8de6 100644
--- a/pve-api-types/Cargo.toml
+++ b/pve-api-types/Cargo.toml
@@ -22,6 +22,7 @@ serde_json.workspace = true
 serde_plain.workspace = true
 #
 proxmox-api-macro.workspace = true
+proxmox-apt-api-types.workspace = true
 proxmox-serde = { workspace = true, features = [ "perl" ] }
 proxmox-schema = { workspace = true, features = [ "api-types", "api-macro" ] }
 
diff --git a/pve-api-types/generate.pl b/pve-api-types/generate.pl
index 9a56b966..a0d5c26a 100755
--- a/pve-api-types/generate.pl
+++ b/pve-api-types/generate.pl
@@ -366,6 +366,7 @@ api(GET => '/nodes/{node}/apt/update', 'list_available_updates', 'return-name' =
 api(POST => '/nodes/{node}/apt/update', 'update_apt_database', 'output-type' => 'PveUpid', 'param-name' => 'AptUpdateParams');
 api(GET => '/nodes/{node}/apt/changelog', 'get_package_changelog', 'output-type' => 'String');
 api(GET => '/nodes/{node}/apt/versions', 'get_package_versions', 'return-name' => 'InstalledPackage');
+api(GET => '/nodes/{node}/apt/repositories', 'get_apt_repositories', 'output-type' => 'APTRepositoriesResult');
 
 Schema2Rust::generate_enum('SdnObjectState', {
     type => 'string',
diff --git a/pve-api-types/src/types/mod.rs b/pve-api-types/src/types/mod.rs
index 1aea79de..d8e38425 100644
--- a/pve-api-types/src/types/mod.rs
+++ b/pve-api-types/src/types/mod.rs
@@ -20,6 +20,8 @@ pub mod verifiers;
 mod fixed_string;
 pub use fixed_string::{FixedString, TooLongError};
 
+pub use proxmox_apt_api_types::APTRepositoriesResult;
+
 include!("../generated/types.rs");
 
 /// A PVE Upid, contrary to a PBS Upid, contains no 'task-id' number.
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH proxmox 3/4] make refresh
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 2/4] add bindings for /nodes/{node}/apt/repositories Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type Lukas Wagner
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 pve-api-types/src/generated/code.rs  |  24 ++++-
 pve-api-types/src/generated/types.rs | 128 +++++++++++++++++++++++++++
 2 files changed, 150 insertions(+), 2 deletions(-)

diff --git a/pve-api-types/src/generated/code.rs b/pve-api-types/src/generated/code.rs
index d2b53f29..b0fb03a1 100644
--- a/pve-api-types/src/generated/code.rs
+++ b/pve-api-types/src/generated/code.rs
@@ -145,8 +145,6 @@
 /// - /nodes/{node}
 /// - /nodes/{node}/aplinfo
 /// - /nodes/{node}/apt
-/// - /nodes/{node}/apt/repositories
-/// - /nodes/{node}/apt/versions
 /// - /nodes/{node}/capabilities
 /// - /nodes/{node}/capabilities/qemu
 /// - /nodes/{node}/capabilities/qemu/cpu-flags
@@ -448,6 +446,11 @@ pub trait PveClient {
         Err(Error::Other("create_zone not implemented"))
     }
 
+    /// Get APT repository information.
+    async fn get_apt_repositories(&self, node: &str) -> Result<APTRepositoriesResult, Error> {
+        Err(Error::Other("get_apt_repositories not implemented"))
+    }
+
     /// Get package changelogs.
     async fn get_package_changelog(
         &self,
@@ -458,6 +461,11 @@ pub trait PveClient {
         Err(Error::Other("get_package_changelog not implemented"))
     }
 
+    /// Get package information for important Proxmox packages.
+    async fn get_package_versions(&self, node: &str) -> Result<Vec<InstalledPackage>, Error> {
+        Err(Error::Other("get_package_versions not implemented"))
+    }
+
     /// Read subscription info.
     async fn get_subscription(&self, node: &str) -> Result<NodeSubscriptionInfo, Error> {
         Err(Error::Other("get_subscription not implemented"))
@@ -949,6 +957,12 @@ where
         self.0.post(url, &params).await?.nodata()
     }
 
+    /// Get APT repository information.
+    async fn get_apt_repositories(&self, node: &str) -> Result<APTRepositoriesResult, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/apt/repositories");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Get package changelogs.
     async fn get_package_changelog(
         &self,
@@ -963,6 +977,12 @@ where
         Ok(self.0.get(url).await?.expect_json()?.data)
     }
 
+    /// Get package information for important Proxmox packages.
+    async fn get_package_versions(&self, node: &str) -> Result<Vec<InstalledPackage>, Error> {
+        let url = &format!("/api2/extjs/nodes/{node}/apt/versions");
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Read subscription info.
     async fn get_subscription(&self, node: &str) -> Result<NodeSubscriptionInfo, Error> {
         let url = &format!("/api2/extjs/nodes/{node}/subscription");
diff --git a/pve-api-types/src/generated/types.rs b/pve-api-types/src/generated/types.rs
index 740b3ca5..bde1e464 100644
--- a/pve-api-types/src/generated/types.rs
+++ b/pve-api-types/src/generated/types.rs
@@ -1719,6 +1719,134 @@ pub struct CreateZone {
     pub zone: String,
 }
 
+#[api(
+    properties: {
+        Arch: {
+            type: AptUpdateInfoArch,
+        },
+        CurrentState: {
+            type: InstalledPackageCurrentState,
+        },
+        Description: {
+            type: String,
+        },
+        ManagerVersion: {
+            optional: true,
+            type: String,
+        },
+        NotifyStatus: {
+            optional: true,
+            type: String,
+        },
+        OldVersion: {
+            optional: true,
+            type: String,
+        },
+        Origin: {
+            type: String,
+        },
+        Package: {
+            type: String,
+        },
+        Priority: {
+            type: String,
+        },
+        RunningKernel: {
+            optional: true,
+            type: String,
+        },
+        Section: {
+            type: String,
+        },
+        Title: {
+            type: String,
+        },
+        Version: {
+            type: String,
+        },
+    },
+)]
+/// Object.
+#[derive(Debug, serde::Deserialize, serde::Serialize)]
+pub struct InstalledPackage {
+    #[serde(rename = "Arch")]
+    pub arch: AptUpdateInfoArch,
+
+    #[serde(rename = "CurrentState")]
+    pub current_state: InstalledPackageCurrentState,
+
+    /// Package description.
+    #[serde(rename = "Description")]
+    pub description: String,
+
+    /// Version of the currently running pve-manager API server.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "ManagerVersion")]
+    pub manager_version: Option<String>,
+
+    /// Version for which PVE has already sent an update notification for.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "NotifyStatus")]
+    pub notify_status: Option<String>,
+
+    /// Old version currently installed.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "OldVersion")]
+    pub old_version: Option<String>,
+
+    /// Package origin, e.g., 'Proxmox' or 'Debian'.
+    #[serde(rename = "Origin")]
+    pub origin: String,
+
+    /// Package name.
+    #[serde(rename = "Package")]
+    pub package: String,
+
+    /// Package priority.
+    #[serde(rename = "Priority")]
+    pub priority: String,
+
+    /// Kernel release, only for package 'proxmox-ve'.
+    #[serde(default, skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "RunningKernel")]
+    pub running_kernel: Option<String>,
+
+    /// Package section.
+    #[serde(rename = "Section")]
+    pub section: String,
+
+    /// Package title.
+    #[serde(rename = "Title")]
+    pub title: String,
+
+    /// New version to be updated to.
+    #[serde(rename = "Version")]
+    pub version: String,
+}
+
+#[api]
+/// Current state of the package installed on the system.
+#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
+pub enum InstalledPackageCurrentState {
+    /// Installed.
+    Installed,
+    /// NotInstalled.
+    NotInstalled,
+    /// UnPacked.
+    UnPacked,
+    /// HalfConfigured.
+    HalfConfigured,
+    /// HalfInstalled.
+    HalfInstalled,
+    /// ConfigFiles.
+    ConfigFiles,
+    /// Unknown variants for forward compatibility.
+    #[serde(untagged)]
+    UnknownEnumValue(FixedString),
+}
+serde_plain::derive_display_from_serialize!(InstalledPackageCurrentState);
+serde_plain::derive_fromstr_from_deserialize!(InstalledPackageCurrentState);
+
 #[api]
 /// A guest's run state.
 #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH proxmox 4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (2 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 3/4] make refresh Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 20:46   ` [pdm-devel] applied: " Thomas Lamprecht
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox-yew-comp 1/1] apt repositories: add 'status_only' property Lukas Wagner
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

The bools were serialized as '0' and '1', so we need the
proxmox_serde::perl::deserialize_bool helpers  to deserialize them.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 proxmox-apt-api-types/Cargo.toml | 1 +
 proxmox-apt-api-types/src/lib.rs | 7 ++++++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/proxmox-apt-api-types/Cargo.toml b/proxmox-apt-api-types/Cargo.toml
index 72ceac91..37e422fb 100644
--- a/proxmox-apt-api-types/Cargo.toml
+++ b/proxmox-apt-api-types/Cargo.toml
@@ -14,4 +14,5 @@ exclude.workspace = true
 serde = { workspace = true, features = ["derive"] }
 serde_plain.workspace = true
 proxmox-schema = { workspace = true, features = ["api-macro"] }
+proxmox-serde.workspace = true
 proxmox-config-digest.workspace = true
diff --git a/proxmox-apt-api-types/src/lib.rs b/proxmox-apt-api-types/src/lib.rs
index 58bd1973..7f3a01c8 100644
--- a/proxmox-apt-api-types/src/lib.rs
+++ b/proxmox-apt-api-types/src/lib.rs
@@ -147,6 +147,7 @@ pub struct APTRepository {
     pub file_type: APTRepositoryFileType,
 
     /// Whether the repository is enabled or not.
+    #[serde(deserialize_with = "proxmox_serde::perl::deserialize_bool")]
     pub enabled: bool,
 }
 
@@ -256,7 +257,11 @@ pub struct APTStandardRepository {
 
     /// Configuration status of the associated repository, where `None` means
     /// not configured, and `Some(bool)` indicates enabled or disabled.
-    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        deserialize_with = "proxmox_serde::perl::deserialize_bool"
+    )]
     pub status: Option<bool>,
 
     /// Display name of the repository.
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH proxmox-yew-comp 1/1] apt repositories: add 'status_only' property
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (3 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 21:26   ` [pdm-devel] applied: " Thomas Lamprecht
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 01/13] pdm-api-types: reuse APTUpdateInfo from proxmox_apt_api_types Lukas Wagner
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

If enabled, this will only show the upper status message table (e.g. 'You
get updates for ...').

While it would be better to split out the status message table into its
own little component, this is a much bigger task than just adding the
new property and a couple of if-guards in the render function. Given the
time constaints this was the better choice. Extracting the component
can always be done at a later point in time.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 src/apt_repositories.rs | 41 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 34 insertions(+), 7 deletions(-)

diff --git a/src/apt_repositories.rs b/src/apt_repositories.rs
index fc5c1a9..5cd0ffa 100644
--- a/src/apt_repositories.rs
+++ b/src/apt_repositories.rs
@@ -47,6 +47,11 @@ pub struct AptRepositories {
     #[builder(IntoPropValue, into_prop_value)]
     #[prop_or_default]
     pub product: Option<ExistingProduct>,
+
+    /// Show status message table only.
+    #[builder(IntoPropValue, into_prop_value)]
+    #[prop_or_default]
+    pub status_only: bool,
 }
 
 impl Default for AptRepositories {
@@ -545,7 +550,25 @@ impl LoadableComponent for ProxmoxAptRepositories {
         }
     }
 
+    fn changed(
+        &mut self,
+        ctx: &LoadableComponentContext<Self>,
+        old_props: &Self::Properties,
+    ) -> bool {
+        let props = ctx.props();
+
+        if props.base_url != old_props.base_url || props.product != old_props.product {
+            ctx.link().send_reload();
+            true
+        } else {
+            false
+        }
+    }
+
     fn toolbar(&self, ctx: &LoadableComponentContext<Self>) -> Option<Html> {
+        if ctx.props().status_only {
+            return None;
+        }
         let selected_record = self.selected_record();
 
         let toolbar = Toolbar::new()
@@ -581,7 +604,17 @@ impl LoadableComponent for ProxmoxAptRepositories {
         Some(toolbar.into())
     }
 
-    fn main_view(&self, _ctx: &LoadableComponentContext<Self>) -> Html {
+    fn main_view(&self, ctx: &LoadableComponentContext<Self>) -> Html {
+        let status = DataTable::new(self.status_columns.clone(), self.status_store.clone())
+            .class("pwt-flex-fit")
+            .show_header(false)
+            .striped(false)
+            .borderless(true);
+
+        if ctx.props().status_only {
+            return status.into();
+        }
+
         let table = DataTable::new(self.columns.clone(), self.tree_store.clone())
             .selection(self.selection.clone())
             .class("pwt-flex-fit pwt-border-top")
@@ -589,12 +622,6 @@ impl LoadableComponent for ProxmoxAptRepositories {
 
         let mut panel = Column::new().class("pwt-flex-fit");
 
-        let status = DataTable::new(self.status_columns.clone(), self.status_store.clone())
-            .class("pwt-flex-fit")
-            .show_header(false)
-            .striped(false)
-            .borderless(true);
-
         panel.add_child(Row::new().padding(4).with_child(status));
 
         panel.with_child(table).into()
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 01/13] pdm-api-types: reuse APTUpdateInfo from proxmox_apt_api_types
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (4 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox-yew-comp 1/1] apt repositories: add 'status_only' property Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 02/13] pbs-client: add bindings for /nodes/localhost/apt/versions Lukas Wagner
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

... instead of defining of defining our own version locally.
Both structs are equivalent, with the exception of `change_log_url`,
which was present in the pdm-api-types version but not in the struct
definition in proxmox-apt-api-types. We never seem to use this member
(grepped PBS as well as PDM source), so it should be fine to use the
definition without it - it also still compiles fine.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 lib/pdm-api-types/Cargo.toml |  1 +
 lib/pdm-api-types/src/lib.rs | 32 ++------------------------------
 2 files changed, 3 insertions(+), 30 deletions(-)

diff --git a/lib/pdm-api-types/Cargo.toml b/lib/pdm-api-types/Cargo.toml
index ffc75cf3..67476271 100644
--- a/lib/pdm-api-types/Cargo.toml
+++ b/lib/pdm-api-types/Cargo.toml
@@ -16,6 +16,7 @@ serde_plain.workspace = true
 proxmox-acme-api.workspace = true
 proxmox-access-control.workspace = true
 proxmox-auth-api = { workspace = true, features = ["api-types"] }
+proxmox-apt-api-types.workspace = true
 proxmox-lang.workspace = true
 proxmox-config-digest.workspace = true
 proxmox-schema = { workspace = true, features = ["api-macro"] }
diff --git a/lib/pdm-api-types/src/lib.rs b/lib/pdm-api-types/src/lib.rs
index 666f89e1..5bce1b07 100644
--- a/lib/pdm-api-types/src/lib.rs
+++ b/lib/pdm-api-types/src/lib.rs
@@ -32,6 +32,8 @@ pub use proxy::HTTP_PROXY_SCHEMA;
 mod translation;
 pub use translation::Translation;
 
+pub use proxmox_apt_api_types::APTUpdateInfo;
+
 pub use proxmox_auth_api::types::{Authid, Userid};
 pub use proxmox_auth_api::types::{Realm, RealmRef};
 pub use proxmox_auth_api::types::{Tokenname, TokennameRef};
@@ -200,36 +202,6 @@ pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.")
     .max_length(64)
     .schema();
 
-#[api()]
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(rename_all = "PascalCase")]
-/// Describes a package for which an update is available.
-pub struct APTUpdateInfo {
-    /// Package name
-    pub package: String,
-    /// Package title
-    pub title: String,
-    /// Package architecture
-    pub arch: String,
-    /// Human readable package description
-    pub description: String,
-    /// New version to be updated to
-    pub version: String,
-    /// Old version currently installed
-    pub old_version: String,
-    /// Package origin
-    pub origin: String,
-    /// Package priority in human-readable form
-    pub priority: String,
-    /// Package section
-    pub section: String,
-    /// URL under which the package's changelog can be retrieved
-    pub change_log_url: String,
-    /// Custom extra field for additional package information
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub extra_info: Option<String>,
-}
-
 #[api()]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "lowercase")]
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 02/13] pbs-client: add bindings for /nodes/localhost/apt/versions
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (5 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 01/13] pdm-api-types: reuse APTUpdateInfo from proxmox_apt_api_types Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 03/13] pbs-client: add bindings for /nodes/localhost/apt/repositories Lukas Wagner
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

This allows us to get the most important package versions of a PBS
remote.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 server/src/pbs_client.rs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/server/src/pbs_client.rs b/server/src/pbs_client.rs
index 00f6030c..3a899084 100644
--- a/server/src/pbs_client.rs
+++ b/server/src/pbs_client.rs
@@ -353,6 +353,16 @@ impl PbsClient {
         Ok(self.0.get(&path).await?.expect_json()?.data)
     }
 
+    /// Return a list of the most important package versions.
+    pub async fn get_package_versions(&self) -> Result<Vec<pbs_api_types::APTUpdateInfo>, Error> {
+        Ok(self
+            .0
+            .get("/api2/extjs/nodes/localhost/apt/versions")
+            .await?
+            .expect_json()?
+            .data)
+    }
+
     /// Get list of tasks.
     ///
     /// `params`: Filters specifying which tasks to get.
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 03/13] pbs-client: add bindings for /nodes/localhost/apt/repositories
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (6 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 02/13] pbs-client: add bindings for /nodes/localhost/apt/versions Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 04/13] remote-updates: include version information in node update summary Lukas Wagner
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

This enables us to query a list of configured APT repositories.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 server/src/pbs_client.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/server/src/pbs_client.rs b/server/src/pbs_client.rs
index 3a899084..f4f1f820 100644
--- a/server/src/pbs_client.rs
+++ b/server/src/pbs_client.rs
@@ -363,6 +363,14 @@ impl PbsClient {
             .data)
     }
 
+    /// Get APT repository information.
+    pub async fn get_apt_repositories(
+        &self,
+    ) -> Result<pbs_api_types::APTRepositoriesResult, Error> {
+        let url = "/api2/extjs/nodes/localhost/apt/repositories";
+        Ok(self.0.get(url).await?.expect_json()?.data)
+    }
+
     /// Get list of tasks.
     ///
     /// `params`: Filters specifying which tasks to get.
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 04/13] remote-updates: include version information in node update summary
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (7 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 03/13] pbs-client: add bindings for /nodes/localhost/apt/repositories Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 05/13] remote updates: include repository status in node summary Lukas Wagner
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Includes the versions of the 'pve-manager' or 'proxmox-backup-server'
packages in the node update summary response. The changed are done in
such a way that we could include other important versions as well
without any breaking changes (e.g. kernel version).

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 lib/pdm-api-types/src/remote_updates.rs | 24 ++++++++++++++++
 server/src/remote_updates.rs            | 37 +++++++++++++++++++++++--
 2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/lib/pdm-api-types/src/remote_updates.rs b/lib/pdm-api-types/src/remote_updates.rs
index e42b6a96..05ff3379 100644
--- a/lib/pdm-api-types/src/remote_updates.rs
+++ b/lib/pdm-api-types/src/remote_updates.rs
@@ -106,6 +106,26 @@ pub enum NodeUpdateStatus {
 #[api]
 #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
 #[serde(rename_all = "kebab-case")]
+/// Package version information.
+pub struct PackageVersion {
+    /// Name of the package.
+    pub package: String,
+    /// Version of the package.
+    pub version: String,
+}
+
+#[api(
+    properties: {
+        versions: {
+            type: Array,
+            items: {
+                type: PackageVersion,
+            }
+        }
+    }
+)]
+#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
+#[serde(rename_all = "kebab-case")]
 /// Per-node update summary.
 pub struct NodeUpdateSummary {
     /// Number of available updates.
@@ -115,5 +135,9 @@ pub struct NodeUpdateSummary {
     /// Status
     pub status: NodeUpdateStatus,
     /// Status message (e.g. error message)
+    #[serde(skip_serializing_if = "Option::is_none")]
     pub status_message: Option<String>,
+    /// Versions of the most important packages.
+    #[serde(default, skip_serializing_if = "Vec::is_empty")]
+    pub versions: Vec<PackageVersion>,
 }
diff --git a/server/src/remote_updates.rs b/server/src/remote_updates.rs
index 895381a5..ad85ee85 100644
--- a/server/src/remote_updates.rs
+++ b/server/src/remote_updates.rs
@@ -7,8 +7,8 @@ use serde::{Deserialize, Serialize};
 use proxmox_apt_api_types::APTUpdateInfo;
 
 use pdm_api_types::remote_updates::{
-    NodeUpdateStatus, NodeUpdateSummary, NodeUpdateSummaryWrapper, RemoteUpdateStatus,
-    RemoteUpdateSummary, UpdateSummary,
+    NodeUpdateStatus, NodeUpdateSummary, NodeUpdateSummaryWrapper, PackageVersion,
+    RemoteUpdateStatus, RemoteUpdateSummary, UpdateSummary,
 };
 use pdm_api_types::remotes::{Remote, RemoteType};
 use pdm_api_types::RemoteUpid;
@@ -24,6 +24,7 @@ pub const UPDATE_CACHE: &str = concat!(PDM_CACHE_DIR_M!(), "/remote-updates.json
 struct NodeUpdateInfo {
     updates: Vec<APTUpdateInfo>,
     last_refresh: i64,
+    versions: Vec<PackageVersion>,
 }
 
 impl From<NodeUpdateInfo> for NodeUpdateSummary {
@@ -33,6 +34,7 @@ impl From<NodeUpdateInfo> for NodeUpdateSummary {
             last_refresh: value.last_refresh,
             status: NodeUpdateStatus::Success,
             status_message: None,
+            versions: value.versions,
         }
     }
 }
@@ -224,6 +226,7 @@ pub async fn refresh_update_summary_cache(remotes: Vec<Remote>) -> Result<(), Er
                                     last_refresh: 0,
                                     status: NodeUpdateStatus::Error,
                                     status_message: Some(format!("{err:#}")),
+                                    versions: Vec::new(),
                                 },
                             );
                             log::error!(
@@ -263,18 +266,34 @@ async fn fetch_available_updates(
                 .map(map_pve_update_info)
                 .collect();
 
+            let versions = client.get_package_versions(&node).await?;
+            let versions = versions
+                .into_iter()
+                .filter(|v| v.package == "pve-manager")
+                .map(map_pve_package_version)
+                .collect();
+
             Ok(NodeUpdateInfo {
                 last_refresh: proxmox_time::epoch_i64(),
                 updates,
+                versions,
             })
         }
         RemoteType::Pbs => {
             let client = connection::make_pbs_client(&remote)?;
             let updates = client.list_available_updates().await?;
 
+            let versions = client.get_package_versions().await?;
+            let versions = versions
+                .into_iter()
+                .filter(|v| v.package == "proxmox-backup-server")
+                .map(map_pbs_package_version)
+                .collect();
+
             Ok(NodeUpdateInfo {
                 last_refresh: proxmox_time::epoch_i64(),
                 updates,
+                versions,
             })
         }
     }
@@ -294,3 +313,17 @@ fn map_pve_update_info(info: pve_api_types::AptUpdateInfo) -> APTUpdateInfo {
         extra_info: None,
     }
 }
+
+fn map_pve_package_version(info: pve_api_types::InstalledPackage) -> PackageVersion {
+    PackageVersion {
+        package: info.package,
+        version: info.old_version.unwrap_or_default(),
+    }
+}
+
+fn map_pbs_package_version(info: pbs_api_types::APTUpdateInfo) -> PackageVersion {
+    PackageVersion {
+        package: info.package,
+        version: info.old_version.unwrap_or_default(),
+    }
+}
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 05/13] remote updates: include repository status in node summary
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (8 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 04/13] remote-updates: include version information in node update summary Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 06/13] api: pve/pbs: add passthrough endpoint for APT repo configuration Lukas Wagner
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Augment the per-node summary with a field that represents the APT
repository configuration. Its value is an enum that can represent the
most common issues, such as
  - missing product repo
  - unstable repos
  - missing subscription if the enterprise repo is enabled

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 lib/pdm-api-types/src/remote_updates.rs | 22 +++++++
 server/src/remote_updates.rs            | 77 ++++++++++++++++++++++++-
 2 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/lib/pdm-api-types/src/remote_updates.rs b/lib/pdm-api-types/src/remote_updates.rs
index 05ff3379..7b4bc112 100644
--- a/lib/pdm-api-types/src/remote_updates.rs
+++ b/lib/pdm-api-types/src/remote_updates.rs
@@ -114,6 +114,25 @@ pub struct PackageVersion {
     pub version: String,
 }
 
+#[api]
+#[derive(Default, Clone, Copy, Debug, Deserialize, Serialize, PartialEq, PartialOrd)]
+#[serde(rename_all = "kebab-case")]
+/// Product repository status.
+pub enum ProductRepositoryStatus {
+    // NOTE: These are sorted in ascending severity.
+    /// Enterprise repository with a valid subscription.
+    Ok,
+    /// Non-production-ready (no-subscription, test) repository is enabled.
+    NonProductionReady,
+    /// Enterprise-repository is enabled, but there is no valid subscription.
+    MissingSubscriptionForEnterprise,
+    /// No product-specific repository is enabled.
+    NoProductRepository,
+    /// Other kind of error.
+    #[default]
+    Error,
+}
+
 #[api(
     properties: {
         versions: {
@@ -140,4 +159,7 @@ pub struct NodeUpdateSummary {
     /// Versions of the most important packages.
     #[serde(default, skip_serializing_if = "Vec::is_empty")]
     pub versions: Vec<PackageVersion>,
+    /// Repository status.
+    #[serde(default)]
+    pub repository_status: ProductRepositoryStatus,
 }
diff --git a/server/src/remote_updates.rs b/server/src/remote_updates.rs
index ad85ee85..d6fe352c 100644
--- a/server/src/remote_updates.rs
+++ b/server/src/remote_updates.rs
@@ -4,11 +4,11 @@ use std::io::ErrorKind;
 use anyhow::Error;
 use serde::{Deserialize, Serialize};
 
-use proxmox_apt_api_types::APTUpdateInfo;
+use proxmox_apt_api_types::{APTRepositoriesResult, APTRepositoryHandle, APTUpdateInfo};
 
 use pdm_api_types::remote_updates::{
     NodeUpdateStatus, NodeUpdateSummary, NodeUpdateSummaryWrapper, PackageVersion,
-    RemoteUpdateStatus, RemoteUpdateSummary, UpdateSummary,
+    ProductRepositoryStatus, RemoteUpdateStatus, RemoteUpdateSummary, UpdateSummary,
 };
 use pdm_api_types::remotes::{Remote, RemoteType};
 use pdm_api_types::RemoteUpid;
@@ -25,6 +25,7 @@ struct NodeUpdateInfo {
     updates: Vec<APTUpdateInfo>,
     last_refresh: i64,
     versions: Vec<PackageVersion>,
+    repository_status: ProductRepositoryStatus,
 }
 
 impl From<NodeUpdateInfo> for NodeUpdateSummary {
@@ -35,6 +36,7 @@ impl From<NodeUpdateInfo> for NodeUpdateSummary {
             status: NodeUpdateStatus::Success,
             status_message: None,
             versions: value.versions,
+            repository_status: value.repository_status,
         }
     }
 }
@@ -227,6 +229,7 @@ pub async fn refresh_update_summary_cache(remotes: Vec<Remote>) -> Result<(), Er
                                     status: NodeUpdateStatus::Error,
                                     status_message: Some(format!("{err:#}")),
                                     versions: Vec::new(),
+                                    repository_status: ProductRepositoryStatus::Error,
                                 },
                             );
                             log::error!(
@@ -273,10 +276,19 @@ async fn fetch_available_updates(
                 .map(map_pve_package_version)
                 .collect();
 
+            let repos = client.get_apt_repositories(&node).await?;
+            let subscription_info = client.get_subscription(&node).await?;
+
+            let has_active_subscription =
+                subscription_info.status == pve_api_types::NodeSubscriptionInfoStatus::Active;
+
+            let repository_status = check_repository_status(&repos, has_active_subscription);
+
             Ok(NodeUpdateInfo {
                 last_refresh: proxmox_time::epoch_i64(),
                 updates,
                 versions,
+                repository_status,
             })
         }
         RemoteType::Pbs => {
@@ -290,10 +302,19 @@ async fn fetch_available_updates(
                 .map(map_pbs_package_version)
                 .collect();
 
+            let repos = client.get_apt_repositories().await?;
+            let subscription_info = client.get_subscription().await?;
+
+            let has_active_subscription =
+                subscription_info.status == proxmox_subscription::SubscriptionStatus::Active;
+
+            let repository_status = check_repository_status(&repos, has_active_subscription);
+
             Ok(NodeUpdateInfo {
                 last_refresh: proxmox_time::epoch_i64(),
                 updates,
                 versions,
+                repository_status,
             })
         }
     }
@@ -327,3 +348,55 @@ fn map_pbs_package_version(info: pbs_api_types::APTUpdateInfo) -> PackageVersion
         version: info.old_version.unwrap_or_default(),
     }
 }
+
+fn check_repository_status(
+    config: &APTRepositoriesResult,
+    active_subscription: bool,
+) -> ProductRepositoryStatus {
+    if !config.errors.is_empty() {
+        return ProductRepositoryStatus::Error;
+    }
+
+    let mut has_enterprise = false;
+    let mut has_no_subscription = false;
+    let mut has_test = false;
+    let mut has_ceph_enterprise = false;
+    let mut has_ceph_no_subscription = false;
+    let mut has_ceph_test = false;
+
+    for repo in &config.standard_repos {
+        if repo.status != Some(true) {
+            continue;
+        }
+        match repo.handle {
+            APTRepositoryHandle::CephSquidEnterprise => has_ceph_enterprise = true,
+            APTRepositoryHandle::CephSquidNoSubscription => has_ceph_no_subscription = true,
+            APTRepositoryHandle::CephSquidTest => has_ceph_test = true,
+            APTRepositoryHandle::Enterprise => has_enterprise = true,
+            APTRepositoryHandle::NoSubscription => has_no_subscription = true,
+            APTRepositoryHandle::Test => has_test = true,
+        }
+    }
+
+    if !(has_enterprise | has_no_subscription | has_test) {
+        return ProductRepositoryStatus::NoProductRepository;
+    }
+
+    if has_enterprise && !active_subscription {
+        return ProductRepositoryStatus::MissingSubscriptionForEnterprise;
+    }
+
+    if has_ceph_enterprise && !active_subscription {
+        return ProductRepositoryStatus::MissingSubscriptionForEnterprise;
+    }
+
+    if has_test || has_no_subscription {
+        return ProductRepositoryStatus::NonProductionReady;
+    }
+
+    if has_ceph_no_subscription || has_ceph_test {
+        return ProductRepositoryStatus::NonProductionReady;
+    }
+
+    ProductRepositoryStatus::Ok
+}
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 06/13] api: pve/pbs: add passthrough endpoint for APT repo configuration
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (9 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 05/13] remote updates: include repository status in node summary Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 07/13] ui: remote updates: show table header Lukas Wagner
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

This enables us to read the APT repository configuration of a remote
node.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 lib/pdm-api-types/src/lib.rs     |  1 +
 server/src/api/remote_updates.rs | 53 ++++++++++++++++++++++++++++----
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/lib/pdm-api-types/src/lib.rs b/lib/pdm-api-types/src/lib.rs
index 5bce1b07..1ca573ae 100644
--- a/lib/pdm-api-types/src/lib.rs
+++ b/lib/pdm-api-types/src/lib.rs
@@ -32,6 +32,7 @@ pub use proxy::HTTP_PROXY_SCHEMA;
 mod translation;
 pub use translation::Translation;
 
+pub use proxmox_apt_api_types::APTRepositoriesResult;
 pub use proxmox_apt_api_types::APTUpdateInfo;
 
 pub use proxmox_auth_api::types::{Authid, Userid};
diff --git a/server/src/api/remote_updates.rs b/server/src/api/remote_updates.rs
index bea2d864..591200b0 100644
--- a/server/src/api/remote_updates.rs
+++ b/server/src/api/remote_updates.rs
@@ -2,10 +2,11 @@
 
 use anyhow::Error;
 
-use pdm_api_types::remotes::Remote;
+use pdm_api_types::remotes::{Remote, RemoteType};
+use pdm_api_types::PRIV_RESOURCE_AUDIT;
 use pdm_api_types::{
-    remote_updates::UpdateSummary, remotes::REMOTE_ID_SCHEMA, RemoteUpid, NODE_SCHEMA,
-    PRIV_RESOURCE_MODIFY, UPID,
+    remote_updates::UpdateSummary, remotes::REMOTE_ID_SCHEMA, APTRepositoriesResult, RemoteUpid,
+    NODE_SCHEMA, PRIV_RESOURCE_MODIFY, UPID,
 };
 use proxmox_access_control::CachedUserInfo;
 use proxmox_apt_api_types::{APTGetChangelogOptions, APTUpdateInfo};
@@ -16,7 +17,7 @@ use proxmox_router::{
 use proxmox_schema::api;
 use proxmox_sortable_macro::sortable;
 
-use crate::remote_updates;
+use crate::{connection, remote_updates};
 
 use super::remotes::get_remote;
 
@@ -204,18 +205,58 @@ async fn apt_get_changelog(
     remote_updates::get_changelog(remote, &node, options.name).await
 }
 
-const APT_SUBDIRS: SubdirMap = &[
+#[api(
+    input: {
+        properties: {
+            remote: {
+                schema: REMOTE_ID_SCHEMA,
+            },
+            node: {
+                schema: NODE_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["resource", "{remote}", "node", "{node}", "system"], PRIV_RESOURCE_AUDIT, false),
+    },
+)]
+/// Get configured APT repositories.
+async fn get_apt_repositories(
+    remote: String,
+    node: String,
+) -> Result<APTRepositoriesResult, Error> {
+    let (config, _digest) = pdm_config::remotes::config()?;
+    let remote = get_remote(&config, &remote)?;
+
+    Ok(match remote.ty {
+        RemoteType::Pve => {
+            let client = connection::make_pve_client(remote)?;
+            client.get_apt_repositories(&node).await?
+        }
+        RemoteType::Pbs => {
+            let client = connection::make_pbs_client(remote)?;
+            client.get_apt_repositories().await?
+        }
+    })
+}
+
+#[sortable]
+const APT_SUBDIRS: SubdirMap = &sorted!([
     (
         "changelog",
         &Router::new().get(&API_METHOD_APT_GET_CHANGELOG),
     ),
+    (
+        "repositories",
+        &Router::new().get(&API_METHOD_GET_APT_REPOSITORIES),
+    ),
     (
         "update",
         &Router::new()
             .get(&API_METHOD_APT_UPDATE_AVAILABLE)
             .post(&API_METHOD_APT_UPDATE_DATABASE),
     ),
-];
+]);
 
 pub const APT_ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(APT_SUBDIRS))
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 07/13] ui: remote updates: show table header
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (10 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 06/13] api: pve/pbs: add passthrough endpoint for APT repo configuration Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 08/13] ui: remote updates: show main product version in overview table Lukas Wagner
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

There will be multiple columns, the column headers will be needed to
avoid confusion.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index 16703552..895eaa13 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -324,7 +324,7 @@ impl UpdateTreeComponent {
             .selection(self.selection.clone())
             .striped(false)
             .borderless(true)
-            .show_header(false)
+            .show_header(true)
             .class(css::FlexFit);
 
         let refresh_all_button = Button::new(tr!("Refresh all")).on_activate({
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 08/13] ui: remote updates: show main product version in overview table
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (11 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 07/13] ui: remote updates: show table header Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 09/13] ui: remote updates: show repository status column Lukas Wagner
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 79 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 74 insertions(+), 5 deletions(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index 895eaa13..e13e3831 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -1,4 +1,5 @@
 use std::cmp::Ordering;
+use std::collections::BTreeMap;
 use std::ops::Deref;
 use std::pin::Pin;
 use std::rc::Rc;
@@ -53,6 +54,8 @@ impl From<UpdateTree> for VNode {
 struct RemoteEntry {
     remote: String,
     ty: RemoteType,
+    product_version: Option<String>,
+    mixed_versions: bool,
     number_of_failed_nodes: u32,
     number_of_nodes: u32,
     number_of_updatable_nodes: u32,
@@ -125,7 +128,7 @@ impl UpdateTreeComponent {
         Rc::new(vec![
             DataTableColumn::new(tr!("Name"))
                 .tree_column(store)
-                .flex(1)
+                .flex(2)
                 .render(|entry: &UpdateTreeEntry| {
                     let icon = match entry {
                         UpdateTreeEntry::Remote(_) => Some("server"),
@@ -142,8 +145,16 @@ impl UpdateTreeComponent {
                 })
                 .sorter(default_sorter)
                 .into(),
+            DataTableColumn::new(tr!("Version"))
+                .flex(1)
+                .render_cell(DataTableCellRenderer::new(
+                    move |args: &mut DataTableCellRenderArgs<UpdateTreeEntry>| {
+                        render_version_column(args.record(), args.is_expanded())
+                    },
+                ))
+                .into(),
             DataTableColumn::new(tr!("Status"))
-                .flex(3)
+                .flex(6)
                 .render_cell(DataTableCellRenderer::new(
                     move |args: &mut DataTableCellRenderArgs<UpdateTreeEntry>| match args.record() {
                         UpdateTreeEntry::Root => {
@@ -184,6 +195,8 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
         let mut remote_entry = root.append(UpdateTreeEntry::Remote(RemoteEntry {
             remote: remote_name.clone(),
             ty: remote_summary.remote_type,
+            product_version: None,
+            mixed_versions: false,
             number_of_nodes: 0,
             number_of_updatable_nodes: 0,
             number_of_failed_nodes: 0,
@@ -191,11 +204,17 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
         }));
         remote_entry.set_expanded(false);
 
+        let mut product_version = None;
+        let mut mixed_versions = false;
         let number_of_nodes = remote_summary.nodes.len();
         let mut number_of_updatable_nodes = 0;
         let mut number_of_failed_nodes = 0;
 
-        for (node_name, node_summary) in remote_summary.nodes.deref() {
+        // use a BTreeMap to get a stable order. Can be removed once there is proper version
+        // comparison in place.
+        let nodes = BTreeMap::from_iter(remote_summary.nodes.deref());
+
+        for (node_name, node_summary) in nodes {
             match node_summary.status {
                 NodeUpdateStatus::Success => {
                     if node_summary.number_of_updates > 0 {
@@ -207,19 +226,35 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
                 }
             }
 
-            remote_entry.append(UpdateTreeEntry::Node(NodeEntry {
+            let entry = NodeEntry {
                 remote: remote_name.clone(),
                 node: node_name.clone(),
                 ty: remote_summary.remote_type,
                 summary: node_summary.clone(),
                 flat: false,
-            }));
+            };
+
+            if let Some(version) = get_product_version(&entry) {
+                if let Some(product_version) = &product_version {
+                    if version != *product_version {
+                        mixed_versions = true;
+                    }
+                    // TODO: Compare versions and report the highest (or lowest). For now we just
+                    // report the one of the first node.
+                } else {
+                    product_version = Some(version);
+                }
+            }
+
+            remote_entry.append(UpdateTreeEntry::Node(entry));
         }
 
         if let UpdateTreeEntry::Remote(info) = remote_entry.record_mut() {
             info.number_of_updatable_nodes = number_of_updatable_nodes;
             info.number_of_nodes = number_of_nodes as u32;
             info.number_of_failed_nodes = number_of_failed_nodes as u32;
+            info.product_version = product_version;
+            info.mixed_versions = mixed_versions;
         }
     }
 
@@ -511,6 +546,40 @@ fn render_node_info(entry: &NodeEntry) -> Row {
         .with_child(text)
 }
 
+fn render_version_column(tree_entry: &UpdateTreeEntry, expanded: bool) -> Html {
+    let text = match tree_entry {
+        UpdateTreeEntry::Node(node_entry) => {
+            get_product_version(node_entry).unwrap_or_default().into()
+        }
+        UpdateTreeEntry::Remote(remote_entry) => {
+            if !expanded {
+                let version_string = remote_entry.product_version.clone().unwrap_or_default();
+                // TRANSLATORS: The first parameter is a version string (e.g. '9.1.0')
+                tr!("{0} (mixed versions)", version_string)
+            } else {
+                "".to_string()
+            }
+        }
+        _ => "".to_string(),
+    };
+
+    text.into()
+}
+
+fn get_product_version(node_entry: &NodeEntry) -> Option<String> {
+    let package = match node_entry.ty {
+        RemoteType::Pve => "pve-manager",
+        RemoteType::Pbs => "proxmox-backup-server",
+    };
+
+    node_entry
+        .summary
+        .versions
+        .iter()
+        .find(|p| p.package == package)
+        .map(|p| p.version.to_string())
+}
+
 enum RemoteSummaryIcon {
     UpToDate,
     Updatable,
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 09/13] ui: remote updates: show repository status column
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (12 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 08/13] ui: remote updates: show main product version in overview table Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 10/13] ui: remote updates: show repo status details when selecting a node Lukas Wagner
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Add a status column for possible repository configuration issues. Due to
space constraints this commit removes the text representation from the
pre-existing update column ('Up-to-date', '42 updates pending') and
replaces it with an icon only (up-to-date, checkmark) or an icon with a
number (number of pending updates). The status text is moved to a
tooltip, but IMO the icon only view is clear enough to understand
without the tool tip. The error message that was previously shown there
will be moved somewhere else in a future commit.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 176 ++++++++++++++++++++++++--------------
 1 file changed, 113 insertions(+), 63 deletions(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index e13e3831..1346b7f7 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -9,7 +9,7 @@ use yew::virtual_dom::{Key, VComp, VNode};
 use yew::{html, Html, Properties};
 
 use pdm_api_types::remote_updates::{
-    NodeUpdateStatus, NodeUpdateSummary, RemoteUpdateStatus, UpdateSummary,
+    NodeUpdateStatus, NodeUpdateSummary, ProductRepositoryStatus, RemoteUpdateStatus, UpdateSummary,
 };
 use pdm_api_types::remotes::RemoteType;
 use pwt::css::{AlignItems, FlexFit, TextAlign};
@@ -19,7 +19,7 @@ use proxmox_yew_comp::{
     AptPackageManager, LoadableComponent, LoadableComponentContext, LoadableComponentMaster,
 };
 use pwt::props::{CssBorderBuilder, CssPaddingBuilder, WidgetStyleBuilder};
-use pwt::widget::{Button, Container, Panel};
+use pwt::widget::{Button, Container, Panel, Tooltip};
 use pwt::{
     css,
     css::FontColor,
@@ -59,6 +59,7 @@ struct RemoteEntry {
     number_of_failed_nodes: u32,
     number_of_nodes: u32,
     number_of_updatable_nodes: u32,
+    repo_status: ProductRepositoryStatus,
     poll_status: RemoteUpdateStatus,
 }
 
@@ -153,17 +154,19 @@ impl UpdateTreeComponent {
                     },
                 ))
                 .into(),
-            DataTableColumn::new(tr!("Status"))
-                .flex(6)
+            DataTableColumn::new(tr!("Update Status"))
+                .flex(1)
                 .render_cell(DataTableCellRenderer::new(
-                    move |args: &mut DataTableCellRenderArgs<UpdateTreeEntry>| match args.record() {
-                        UpdateTreeEntry::Root => {
-                            html!()
-                        }
-                        UpdateTreeEntry::Remote(remote_info) => {
-                            render_remote_summary(remote_info, args.is_expanded()).into()
-                        }
-                        UpdateTreeEntry::Node(info) => render_node_info(info).into(),
+                    move |args: &mut DataTableCellRenderArgs<UpdateTreeEntry>| {
+                        render_update_status_column(args.record(), args.is_expanded())
+                    },
+                ))
+                .into(),
+            DataTableColumn::new(tr!("Repository Status"))
+                .flex(1)
+                .render_cell(DataTableCellRenderer::new(
+                    move |args: &mut DataTableCellRenderArgs<UpdateTreeEntry>| {
+                        render_repo_status_column(args.record(), args.is_expanded())
                     },
                 ))
                 .into(),
@@ -200,6 +203,7 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
             number_of_nodes: 0,
             number_of_updatable_nodes: 0,
             number_of_failed_nodes: 0,
+            repo_status: ProductRepositoryStatus::Ok,
             poll_status: remote_summary.status.clone(),
         }));
         remote_entry.set_expanded(false);
@@ -209,6 +213,7 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
         let number_of_nodes = remote_summary.nodes.len();
         let mut number_of_updatable_nodes = 0;
         let mut number_of_failed_nodes = 0;
+        let mut repo_status = ProductRepositoryStatus::Ok;
 
         // use a BTreeMap to get a stable order. Can be removed once there is proper version
         // comparison in place.
@@ -226,6 +231,9 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
                 }
             }
 
+            if node_summary.repository_status > repo_status {
+                repo_status = node_summary.repository_status;
+            }
             let entry = NodeEntry {
                 remote: remote_name.clone(),
                 node: node_name.clone(),
@@ -255,6 +263,7 @@ fn build_store_from_response(update_summary: UpdateSummary) -> SlabTree<UpdateTr
             info.number_of_failed_nodes = number_of_failed_nodes as u32;
             info.product_version = product_version;
             info.mixed_versions = mixed_versions;
+            info.repo_status = repo_status;
         }
     }
 
@@ -470,7 +479,19 @@ impl UpdateTreeComponent {
     }
 }
 
-fn render_remote_summary(entry: &RemoteEntry, expanded: bool) -> Row {
+fn render_update_status_column(tree_entry: &UpdateTreeEntry, expanded: bool) -> Html {
+    match tree_entry {
+        UpdateTreeEntry::Root => {
+            html!()
+        }
+        UpdateTreeEntry::Remote(remote_info) => {
+            render_remote_update_status(remote_info, expanded).into()
+        }
+        UpdateTreeEntry::Node(node_info) => render_node_update_status(node_info).into(),
+    }
+}
+
+fn render_remote_update_status(entry: &RemoteEntry, expanded: bool) -> Row {
     let mut row = Row::new().class(css::AlignItems::Baseline).gap(2);
     match entry.poll_status {
         RemoteUpdateStatus::Success => {
@@ -479,71 +500,98 @@ fn render_remote_summary(entry: &RemoteEntry, expanded: bool) -> Row {
                     - entry.number_of_updatable_nodes
                     - entry.number_of_failed_nodes;
 
-                let text = if entry.number_of_nodes == up_to_date_nodes {
-                    row = row.with_child(render_remote_summary_icon(RemoteSummaryIcon::UpToDate));
-                    tr!("All nodes up-to-date")
+                if entry.number_of_nodes == up_to_date_nodes {
+                    row = row.with_child(render_status_icon(StatusIcon::Ok));
                 } else if entry.number_of_updatable_nodes > 0 {
-                    row = row.with_child(render_remote_summary_icon(RemoteSummaryIcon::Updatable));
+                    row = row.with_child(render_status_icon(StatusIcon::Updatable));
 
                     if entry.number_of_failed_nodes > 0 {
-                        row = row.with_child(render_remote_summary_icon(RemoteSummaryIcon::Error));
-                        // NOTE: This 'summary' line is only shown for remotes with multiple nodes,
-                        // so we don't really have to consider the singular form of 'x out of y
-                        // nodes'
-                        tr!("Some nodes have pending updates, some nodes unavailable")
-                    } else {
-                        tr!("Some nodes have pending updates")
+                        row = row.with_child(render_status_icon(StatusIcon::Error));
                     }
                 } else if entry.number_of_failed_nodes > 0 {
-                    row = row.with_child(render_remote_summary_icon(RemoteSummaryIcon::Error));
-                    tr!("Some nodes unavailable")
-                } else {
-                    String::new()
-                };
-
-                row = row.with_child(text);
+                    row = row.with_child(render_status_icon(StatusIcon::Error));
+                }
             }
         }
         RemoteUpdateStatus::Error => {
-            row = row.with_child(render_remote_summary_icon(RemoteSummaryIcon::Error));
-            row = row.with_child(tr!("Could not connect to remote"));
+            row = row.with_child(render_status_icon(StatusIcon::Error));
         }
         RemoteUpdateStatus::Unknown => {
-            row = row.with_child(render_remote_summary_icon(RemoteSummaryIcon::Unknown));
-            row = row.with_child(tr!("Update status unknown"));
+            row = row.with_child(render_status_icon(StatusIcon::Unknown));
         }
     }
 
     row
 }
 
-fn render_node_info(entry: &NodeEntry) -> Row {
-    let (icon, text) = if entry.summary.status == NodeUpdateStatus::Error {
-        let icon = render_remote_summary_icon(RemoteSummaryIcon::Error);
-        let text = if let Some(status) = &entry.summary.status_message {
-            tr!("Failed to retrieve update status: {}", status)
+fn render_node_update_status(entry: &NodeEntry) -> Html {
+    let mut row = Row::new().class(css::AlignItems::Baseline).gap(2);
+
+    let tooltip = if entry.summary.status == NodeUpdateStatus::Error {
+        row = row.with_child(render_status_icon(StatusIcon::Error));
+        if let Some(status) = &entry.summary.status_message {
+            tr!("Failed to retrieve update status: {0}", status)
         } else {
             tr!("Unknown error")
-        };
-
-        (icon, text)
+        }
     } else if entry.summary.number_of_updates > 0 {
-        (
-            render_remote_summary_icon(RemoteSummaryIcon::Updatable),
-            tr!("One update pending" | "{n} updates pending" % entry.summary.number_of_updates),
-        )
+        row = row.with_child(render_status_icon(StatusIcon::Updatable));
+        row = row.with_child(format!("{}", entry.summary.number_of_updates));
+        tr!("One update pending" | "{n} updates pending" % entry.summary.number_of_updates)
     } else {
-        (
-            render_remote_summary_icon(RemoteSummaryIcon::UpToDate),
-            tr!("Up-to-date"),
-        )
+        row = row.with_child(render_status_icon(StatusIcon::Ok));
+        tr!("Up-to-date")
     };
 
-    Row::new()
-        .class(css::AlignItems::Baseline)
-        .gap(2)
-        .with_child(icon)
-        .with_child(text)
+    Tooltip::new(row).tip(tooltip).into()
+}
+
+fn render_repo_status_column(tree_entry: &UpdateTreeEntry, expanded: bool) -> Html {
+    match tree_entry {
+        UpdateTreeEntry::Root => {
+            html!()
+        }
+        UpdateTreeEntry::Remote(remote_info) => {
+            if !expanded {
+                render_repo_status(remote_info.repo_status)
+            } else {
+                html!()
+            }
+        }
+        UpdateTreeEntry::Node(node_info) => {
+            render_repo_status(node_info.summary.repository_status).into()
+        }
+    }
+}
+
+fn render_repo_status(status: ProductRepositoryStatus) -> Html {
+    let mut row = Row::new().class(css::AlignItems::Baseline).gap(2);
+
+    let tooltip = match status {
+        ProductRepositoryStatus::Ok => {
+            row = row.with_child(render_status_icon(StatusIcon::Ok));
+            tr!("Production-ready enterprise repository enabled")
+        }
+        ProductRepositoryStatus::NoProductRepository => {
+            row = row.with_child(render_status_icon(StatusIcon::Error));
+            tr!("No product repository configured")
+        }
+        ProductRepositoryStatus::MissingSubscriptionForEnterprise => {
+            row = row.with_child(render_status_icon(StatusIcon::Warning));
+            tr!("Enterprise repository configured, but missing subscription")
+        }
+        ProductRepositoryStatus::NonProductionReady => {
+            row = row.with_child(render_status_icon(StatusIcon::Ok));
+            row = row.with_child(render_status_icon(StatusIcon::Warning));
+            tr!("Non-production-ready repositories enabled")
+        }
+        ProductRepositoryStatus::Error => {
+            row = row.with_child(render_status_icon(StatusIcon::Error));
+            tr!("Error")
+        }
+    };
+
+    Tooltip::new(row).tip(tooltip).into()
 }
 
 fn render_version_column(tree_entry: &UpdateTreeEntry, expanded: bool) -> Html {
@@ -580,19 +628,21 @@ fn get_product_version(node_entry: &NodeEntry) -> Option<String> {
         .map(|p| p.version.to_string())
 }
 
-enum RemoteSummaryIcon {
-    UpToDate,
+enum StatusIcon {
+    Ok,
     Updatable,
     Error,
+    Warning,
     Unknown,
 }
 
-fn render_remote_summary_icon(icon: RemoteSummaryIcon) -> Fa {
+fn render_status_icon(icon: StatusIcon) -> Fa {
     let (icon_class, icon_scheme) = match icon {
-        RemoteSummaryIcon::UpToDate => ("check", FontColor::Success),
-        RemoteSummaryIcon::Error => ("times-circle", FontColor::Error),
-        RemoteSummaryIcon::Updatable => ("refresh", FontColor::Primary),
-        RemoteSummaryIcon::Unknown => ("question-circle-o", FontColor::Primary),
+        StatusIcon::Ok => ("check", FontColor::Success),
+        StatusIcon::Error => ("times-circle", FontColor::Error),
+        StatusIcon::Warning => ("exclamation-triangle", FontColor::Warning),
+        StatusIcon::Updatable => ("refresh", FontColor::Primary),
+        StatusIcon::Unknown => ("question-circle-o", FontColor::Primary),
     };
 
     Fa::new(icon_class).class(icon_scheme)
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 10/13] ui: remote updates: show repo status details when selecting a node
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (13 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 09/13] ui: remote updates: show repository status column Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 11/13] ui: remote updates: don't attempt to load current status for unavailable nodes Lukas Wagner
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

This will show the status message table for the repository status above
the update list.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index 1346b7f7..cf025157 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -16,7 +16,8 @@ use pwt::css::{AlignItems, FlexFit, TextAlign};
 use pwt::widget::data_table::{DataTableCellRenderArgs, DataTableCellRenderer};
 
 use proxmox_yew_comp::{
-    AptPackageManager, LoadableComponent, LoadableComponentContext, LoadableComponentMaster,
+    AptPackageManager, AptRepositories, ExistingProduct, LoadableComponent,
+    LoadableComponentContext, LoadableComponentMaster,
 };
 use pwt::props::{CssBorderBuilder, CssPaddingBuilder, WidgetStyleBuilder};
 use pwt::widget::{Button, Container, Panel, Tooltip};
@@ -411,7 +412,7 @@ impl UpdateTreeComponent {
                 let task_base_url = format!("/{ty}/remotes/{remote}/tasks");
 
                 let apt = AptPackageManager::new()
-                    .base_url(base_url)
+                    .base_url(base_url.clone())
                     .task_base_url(task_base_url)
                     .enable_upgrade(true)
                     .on_upgrade({
@@ -440,11 +441,27 @@ impl UpdateTreeComponent {
                         }
                     });
 
+                let product = match ty {
+                    RemoteType::Pve => ExistingProduct::PVE,
+                    RemoteType::Pbs => ExistingProduct::PBS,
+                };
+
+                let repo_status = Container::new().min_height(150).with_child(
+                    AptRepositories::new()
+                        .product(product)
+                        .status_only(true)
+                        .base_url(base_url),
+                );
+
                 Panel::new()
                     .class(FlexFit)
                     .title(title)
                     .border(true)
                     .min_width(500)
+                    .with_child(repo_status)
+                    .with_child(
+                        html! {<div role="separator" class="pwt-w-100 pwt-horizontal-rule"/>},
+                    )
                     .with_child(apt)
                     .style("flex", "1 1 0")
             }
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 11/13] ui: remote updates: don't attempt to load current status for unavailable nodes
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (14 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 10/13] ui: remote updates: show repo status details when selecting a node Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 12/13] ui: remote updates: use 'building-o' icon for PBS nodes Lukas Wagner
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

If the cached update status indicates that a node is unavailable, don't
attempt to load the repo status and update table. Instead, show the
error message from the last poll attempt in-place where the tables would
else be. This avoids annoying error pop-ups when navigating through the
remote/node tree in the global update view.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 126 +++++++++++++++++++++-----------------
 1 file changed, 69 insertions(+), 57 deletions(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index cf025157..7284fd47 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -397,9 +397,19 @@ impl UpdateTreeComponent {
     }
 
     fn render_update_list_panel(&self, ctx: &LoadableComponentContext<Self>) -> Panel {
+        let mut panel = Panel::new()
+            .class(FlexFit)
+            .border(true)
+            .min_width(500)
+            .style("flex", "1 1 0");
+
         match &self.selected_entry {
             Some(UpdateTreeEntry::Node(NodeEntry {
-                remote, node, ty, ..
+                remote,
+                node,
+                ty,
+                summary,
+                ..
             })) => {
                 let title: Html = Row::new()
                     .gap(2)
@@ -408,62 +418,68 @@ impl UpdateTreeComponent {
                     .with_child(tr!("Update List - {} ({})", remote, node))
                     .into();
 
-                let base_url = format!("/{ty}/remotes/{remote}/nodes/{node}/apt",);
-                let task_base_url = format!("/{ty}/remotes/{remote}/tasks");
+                if summary.status == NodeUpdateStatus::Success {
+                    let base_url = format!("/{ty}/remotes/{remote}/nodes/{node}/apt",);
+                    let task_base_url = format!("/{ty}/remotes/{remote}/tasks");
 
-                let apt = AptPackageManager::new()
-                    .base_url(base_url.clone())
-                    .task_base_url(task_base_url)
-                    .enable_upgrade(true)
-                    .on_upgrade({
-                        let remote = remote.clone();
-                        let link = ctx.link().clone();
-                        let remote = remote.clone();
-                        let node = node.clone();
-                        let ty = *ty;
+                    let apt = AptPackageManager::new()
+                        .base_url(base_url.clone())
+                        .task_base_url(task_base_url)
+                        .enable_upgrade(true)
+                        .on_upgrade({
+                            let remote = remote.clone();
+                            let link = ctx.link().clone();
+                            let remote = remote.clone();
+                            let node = node.clone();
+                            let ty = *ty;
 
-                        move |_| match ty {
-                            RemoteType::Pve => {
-                                let id = format!("node/{node}::apt");
-                                if let Some(url) = get_deep_url(link.yew_link(), &remote, None, &id)
-                                {
-                                    let _ = gloo_utils::window().open_with_url(&url.href());
+                            move |_| match ty {
+                                RemoteType::Pve => {
+                                    let id = format!("node/{node}::apt");
+                                    if let Some(url) =
+                                        get_deep_url(link.yew_link(), &remote, None, &id)
+                                    {
+                                        let _ = gloo_utils::window().open_with_url(&url.href());
+                                    }
+                                }
+                                RemoteType::Pbs => {
+                                    let hash = "#pbsServerAdministration:updates";
+                                    if let Some(url) =
+                                        get_deep_url_low_level(link.yew_link(), &remote, None, hash)
+                                    {
+                                        let _ = gloo_utils::window().open_with_url(&url.href());
+                                    }
                                 }
                             }
-                            RemoteType::Pbs => {
-                                let hash = "#pbsServerAdministration:updates";
-                                if let Some(url) =
-                                    get_deep_url_low_level(link.yew_link(), &remote, None, hash)
-                                {
-                                    let _ = gloo_utils::window().open_with_url(&url.href());
-                                }
-                            }
-                        }
-                    });
+                        });
 
-                let product = match ty {
-                    RemoteType::Pve => ExistingProduct::PVE,
-                    RemoteType::Pbs => ExistingProduct::PBS,
-                };
+                    let product = match ty {
+                        RemoteType::Pve => ExistingProduct::PVE,
+                        RemoteType::Pbs => ExistingProduct::PBS,
+                    };
 
-                let repo_status = Container::new().min_height(150).with_child(
-                    AptRepositories::new()
-                        .product(product)
-                        .status_only(true)
-                        .base_url(base_url),
-                );
+                    let repo_status = Container::new().min_height(150).with_child(
+                        AptRepositories::new()
+                            .product(product)
+                            .status_only(true)
+                            .base_url(base_url),
+                    );
 
-                Panel::new()
-                    .class(FlexFit)
-                    .title(title)
-                    .border(true)
-                    .min_width(500)
-                    .with_child(repo_status)
-                    .with_child(
-                        html! {<div role="separator" class="pwt-w-100 pwt-horizontal-rule"/>},
-                    )
-                    .with_child(apt)
-                    .style("flex", "1 1 0")
+                    panel = panel
+                        .title(title)
+                        .with_child(repo_status)
+                        .with_child(
+                            html! {<div role="separator" class="pwt-w-100 pwt-horizontal-rule"/>},
+                        )
+                        .with_child(apt);
+                } else {
+                    let error_widget = pwt::widget::error_message(&tr!(
+                        "Could not fetch update status: {0}",
+                        summary.status_message.as_deref().unwrap_or_default()
+                    ));
+
+                    panel = panel.title(title).with_child(error_widget);
+                }
             }
             _ => {
                 let title: Html = Row::new()
@@ -484,15 +500,11 @@ impl UpdateTreeComponent {
                     .with_child(html! {<h1 class="pwt-font-headline-medium">{header}</h1>})
                     .with_child(Container::new().with_child(msg));
 
-                Panel::new()
-                    .class(FlexFit)
-                    .title(title)
-                    .border(true)
-                    .min_width(500)
-                    .with_child(select_node_msg)
-                    .style("flex", "1 1 0")
+                panel = panel.title(title).with_child(select_node_msg)
             }
         }
+
+        panel
     }
 }
 
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 12/13] ui: remote updates: use 'building-o' icon for PBS nodes
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (15 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 11/13] ui: remote updates: don't attempt to load current status for unavailable nodes Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 13/13] ui: remote updates: use explicit indices for parameters in tr! macro Lukas Wagner
  2025-11-27 10:47 ` [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

We do so in many other places in the UI. This gives a better visual
distinction between PVE and PBS nodes.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index 7284fd47..faa38fcd 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -134,7 +134,13 @@ impl UpdateTreeComponent {
                 .render(|entry: &UpdateTreeEntry| {
                     let icon = match entry {
                         UpdateTreeEntry::Remote(_) => Some("server"),
-                        UpdateTreeEntry::Node(_) => Some("building"),
+                        UpdateTreeEntry::Node(node_entry) => {
+                            if node_entry.ty == RemoteType::Pbs {
+                                Some("building-o")
+                            } else {
+                                Some("building")
+                            }
+                        }
                         _ => None,
                     };
 
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] [PATCH datacenter-manager 13/13] ui: remote updates: use explicit indices for parameters in tr! macro
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (16 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 12/13] ui: remote updates: use 'building-o' icon for PBS nodes Lukas Wagner
@ 2025-11-27 10:44 ` Lukas Wagner
  2025-11-27 10:47 ` [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:44 UTC (permalink / raw)
  To: pdm-devel

Otherwise the there will be errors in the browser console, warning about
implicit ordering of parameters.

Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
---
 ui/src/remotes/updates.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/ui/src/remotes/updates.rs b/ui/src/remotes/updates.rs
index faa38fcd..f9c34fc8 100644
--- a/ui/src/remotes/updates.rs
+++ b/ui/src/remotes/updates.rs
@@ -421,7 +421,9 @@ impl UpdateTreeComponent {
                     .gap(2)
                     .class(AlignItems::Baseline)
                     .with_child(Fa::new("list"))
-                    .with_child(tr!("Update List - {} ({})", remote, node))
+                    // TRANSLATORS: The first parameter is the name of the remote, the second one
+                    // the name of the node.
+                    .with_child(tr!("Update List - {0} ({1})", remote, node))
                     .into();
 
                 if summary.status == NodeUpdateStatus::Success {
-- 
2.47.3



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* Re: [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status
  2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
                   ` (17 preceding siblings ...)
  2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 13/13] ui: remote updates: use explicit indices for parameters in tr! macro Lukas Wagner
@ 2025-11-27 10:47 ` Lukas Wagner
  18 siblings, 0 replies; 24+ messages in thread
From: Lukas Wagner @ 2025-11-27 10:47 UTC (permalink / raw)
  To: Proxmox Datacenter Manager development discussion

On Thu Nov 27, 2025 at 11:44 AM CET, Lukas Wagner wrote:
>
>
>
> proxmox:
>
> Lukas Wagner (4):
>   add bindings for /nodes/{node}/apt/versions
>   add bindings for /nodes/{node}/apt/repositories
>   make refresh

Sorry, those first three patches are missing the 'pve-api-types' prefix,
can be fixed up when applying or when I send a v2.



_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] applied: [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions Lukas Wagner
@ 2025-11-27 20:46   ` Thomas Lamprecht
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Lamprecht @ 2025-11-27 20:46 UTC (permalink / raw)
  To: pdm-devel, Lukas Wagner

On Thu, 27 Nov 2025 11:44:30 +0100, Lukas Wagner wrote:
> 


Applied, thanks!

[1/4] add bindings for /nodes/{node}/apt/versions
      commit: 89479d22af41af469db5be479aacc281e8461deb


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] applied: [PATCH proxmox 2/4] add bindings for /nodes/{node}/apt/repositories
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 2/4] add bindings for /nodes/{node}/apt/repositories Lukas Wagner
@ 2025-11-27 20:46   ` Thomas Lamprecht
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Lamprecht @ 2025-11-27 20:46 UTC (permalink / raw)
  To: pdm-devel, Lukas Wagner

On Thu, 27 Nov 2025 11:44:31 +0100, Lukas Wagner wrote:
> 


Applied, thanks!

[2/4] add bindings for /nodes/{node}/apt/repositories
      commit: d4b29cff9d67c60bd0aa930bf155d901f1a7c525


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] applied: [PATCH proxmox 4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type Lukas Wagner
@ 2025-11-27 20:46   ` Thomas Lamprecht
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Lamprecht @ 2025-11-27 20:46 UTC (permalink / raw)
  To: pdm-devel, Lukas Wagner

On Thu, 27 Nov 2025 11:44:33 +0100, Lukas Wagner wrote:
> The bools were serialized as '0' and '1', so we need the
> proxmox_serde::perl::deserialize_bool helpers  to deserialize them.
> 
> 

Applied, thanks!

[4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type
      commit: 050b2afdcc8a51cdbf9c15fed1e719ada6ba6f45


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

* [pdm-devel] applied: [PATCH proxmox-yew-comp 1/1] apt repositories: add 'status_only' property
  2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox-yew-comp 1/1] apt repositories: add 'status_only' property Lukas Wagner
@ 2025-11-27 21:26   ` Thomas Lamprecht
  0 siblings, 0 replies; 24+ messages in thread
From: Thomas Lamprecht @ 2025-11-27 21:26 UTC (permalink / raw)
  To: pdm-devel, Lukas Wagner

On Thu, 27 Nov 2025 11:44:34 +0100, Lukas Wagner wrote:
> If enabled, this will only show the upper status message table (e.g. 'You
> get updates for ...').
> 
> While it would be better to split out the status message table into its
> own little component, this is a much bigger task than just adding the
> new property and a couple of if-guards in the render function. Given the
> time constaints this was the better choice. Extracting the component
> can always be done at a later point in time.
> 
> [...]

Applied, thanks!

[1/1] apt repositories: add 'status_only' property
      commit: a1c63c7af9dd44d701ff7ca2989ba8b14ceffbb1


_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel


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

end of thread, other threads:[~2025-11-27 21:27 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-27 10:44 [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 1/4] add bindings for /nodes/{node}/apt/versions Lukas Wagner
2025-11-27 20:46   ` [pdm-devel] applied: " Thomas Lamprecht
2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 2/4] add bindings for /nodes/{node}/apt/repositories Lukas Wagner
2025-11-27 20:46   ` [pdm-devel] applied: " Thomas Lamprecht
2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 3/4] make refresh Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox 4/4] proxmox-apt-api-types: make APTStandardRepository compatible with PVE's serialization of the type Lukas Wagner
2025-11-27 20:46   ` [pdm-devel] applied: " Thomas Lamprecht
2025-11-27 10:44 ` [pdm-devel] [PATCH proxmox-yew-comp 1/1] apt repositories: add 'status_only' property Lukas Wagner
2025-11-27 21:26   ` [pdm-devel] applied: " Thomas Lamprecht
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 01/13] pdm-api-types: reuse APTUpdateInfo from proxmox_apt_api_types Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 02/13] pbs-client: add bindings for /nodes/localhost/apt/versions Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 03/13] pbs-client: add bindings for /nodes/localhost/apt/repositories Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 04/13] remote-updates: include version information in node update summary Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 05/13] remote updates: include repository status in node summary Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 06/13] api: pve/pbs: add passthrough endpoint for APT repo configuration Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 07/13] ui: remote updates: show table header Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 08/13] ui: remote updates: show main product version in overview table Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 09/13] ui: remote updates: show repository status column Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 10/13] ui: remote updates: show repo status details when selecting a node Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 11/13] ui: remote updates: don't attempt to load current status for unavailable nodes Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 12/13] ui: remote updates: use 'building-o' icon for PBS nodes Lukas Wagner
2025-11-27 10:44 ` [pdm-devel] [PATCH datacenter-manager 13/13] ui: remote updates: use explicit indices for parameters in tr! macro Lukas Wagner
2025-11-27 10:47 ` [pdm-devel] [PATCH datacenter-manager/proxmox{, -yew-comp} 00/18] remote update view: include product version and repository status Lukas Wagner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal