public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable
@ 2025-01-14 11:21 Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 1/4] pdm-client: add cluster status method Dominik Csapak
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Dominik Csapak @ 2025-01-14 11:21 UTC (permalink / raw)
  To: pdm-devel

since we cannot currently select a target node for remote migration
(to avoid another network transfer of disks) the target of the
remote migration is always the given endpoint.

Currently pdm auto selects always the first configured endpoint, this
series makes this now user configurable.

changes from v1:
* rebase on master (drops most patches)
* fix commit that introduces a new parameter for remote migration in the
  pdm-client (non-usable in the cli for now)
* fix typos in commit messages

Dominik Csapak (4):
  pdm-client: add cluster status method
  pdm-client: add target-endpoint parameter to remote migration methods
  ui: widget: add remote endpoint selector
  ui: migrate: make target endpoint selectable for remote migration

 cli/client/src/pve.rs                     |   4 +-
 lib/pdm-client/src/lib.rs                 |  20 +++++
 ui/src/widget/migrate_window.rs           |  68 +++++++++++++-
 ui/src/widget/mod.rs                      |   2 +
 ui/src/widget/remote_endpoint_selector.rs | 103 ++++++++++++++++++++++
 5 files changed, 193 insertions(+), 4 deletions(-)
 create mode 100644 ui/src/widget/remote_endpoint_selector.rs

-- 
2.39.5



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


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

* [pdm-devel] [PATCH datacenter-manager v2 1/4] pdm-client: add cluster status method
  2025-01-14 11:21 [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dominik Csapak
@ 2025-01-14 11:21 ` Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 2/4] pdm-client: add target-endpoint parameter to remote migration methods Dominik Csapak
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2025-01-14 11:21 UTC (permalink / raw)
  To: pdm-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 lib/pdm-client/src/lib.rs | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/lib/pdm-client/src/lib.rs b/lib/pdm-client/src/lib.rs
index 14e6fc8..4ef560e 100644
--- a/lib/pdm-client/src/lib.rs
+++ b/lib/pdm-client/src/lib.rs
@@ -52,6 +52,8 @@ pub mod types {
     };
 
     pub use pve_api_types::ListRealm;
+
+    pub use pve_api_types::ClusterNodeStatus;
 }
 
 pub struct PdmClient<T: HttpApiClient>(pub T);
@@ -347,6 +349,16 @@ impl<T: HttpApiClient> PdmClient<T> {
         Ok(self.0.get(&query).await?.expect_json()?.data)
     }
 
+    pub async fn pve_cluster_status(
+        &self,
+        remote: &str,
+        target_endpoint: Option<&str>,
+    ) -> Result<Vec<ClusterNodeStatus>, Error> {
+        let mut query = format!("/api2/extjs/pve/remotes/{remote}/cluster-status");
+        add_query_arg(&mut query, &mut '?', "target-endpoint", &target_endpoint);
+        Ok(self.0.get(&query).await?.expect_json()?.data)
+    }
+
     pub async fn pve_list_qemu(
         &self,
         remote: &str,
-- 
2.39.5



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


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

* [pdm-devel] [PATCH datacenter-manager v2 2/4] pdm-client: add target-endpoint parameter to remote migration methods
  2025-01-14 11:21 [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 1/4] pdm-client: add cluster status method Dominik Csapak
@ 2025-01-14 11:21 ` Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 3/4] ui: widget: add remote endpoint selector Dominik Csapak
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2025-01-14 11:21 UTC (permalink / raw)
  To: pdm-devel

and set it to None for all users for now

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
changes from v1:
* fix cli code to set that parameter to None
* also fix UI code in one go so the repo state is compileable

 cli/client/src/pve.rs           | 4 ++--
 lib/pdm-client/src/lib.rs       | 8 ++++++++
 ui/src/widget/migrate_window.rs | 2 ++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/cli/client/src/pve.rs b/cli/client/src/pve.rs
index c355293..1b12403 100644
--- a/cli/client/src/pve.rs
+++ b/cli/client/src/pve.rs
@@ -618,7 +618,7 @@ async fn remote_migrate_qemu(
 
     let client = client()?;
     let upid = client
-        .pve_qemu_remote_migrate(&remote, node.as_deref(), vmid, target, params)
+        .pve_qemu_remote_migrate(&remote, node.as_deref(), vmid, target, None, params)
         .await?;
     println!("upid: {upid}");
     let status = client.pve_wait_for_task(&upid).await?;
@@ -996,7 +996,7 @@ async fn remote_migrate_lxc(
 
     let client = client()?;
     let upid = client
-        .pve_lxc_remote_migrate(&remote, node.as_deref(), vmid, target, params)
+        .pve_lxc_remote_migrate(&remote, node.as_deref(), vmid, target, None, params)
         .await?;
     println!("upid: {upid}");
     let status = client.pve_wait_for_task(&upid).await?;
diff --git a/lib/pdm-client/src/lib.rs b/lib/pdm-client/src/lib.rs
index 4ef560e..1253ded 100644
--- a/lib/pdm-client/src/lib.rs
+++ b/lib/pdm-client/src/lib.rs
@@ -488,6 +488,7 @@ impl<T: HttpApiClient> PdmClient<T> {
         node: Option<&str>,
         vmid: u32,
         target: String,
+        target_endpoint: Option<&str>,
         params: RemoteMigrateQemu,
     ) -> Result<RemoteUpid, Error> {
         let path = format!("/api2/extjs/pve/remotes/{remote}/qemu/{vmid}/remote-migrate");
@@ -496,6 +497,9 @@ impl<T: HttpApiClient> PdmClient<T> {
         if let Some(node) = node {
             request["node"] = node.into();
         }
+        if let Some(target_endpoint) = target_endpoint {
+            request["target-endpoint"] = target_endpoint.into();
+        }
         Ok(self.0.post(&path, &request).await?.expect_json()?.data)
     }
 
@@ -581,6 +585,7 @@ impl<T: HttpApiClient> PdmClient<T> {
         node: Option<&str>,
         vmid: u32,
         target: String,
+        target_endpoint: Option<&str>,
         params: RemoteMigrateLxc,
     ) -> Result<RemoteUpid, Error> {
         let path = format!("/api2/extjs/pve/remotes/{remote}/lxc/{vmid}/remote-migrate");
@@ -589,6 +594,9 @@ impl<T: HttpApiClient> PdmClient<T> {
         if let Some(node) = node {
             request["node"] = node.into();
         }
+        if let Some(target_endpoint) = target_endpoint {
+            request["target-endpoint"] = target_endpoint.into();
+        }
         Ok(self.0.post(&path, &request).await?.expect_json()?.data)
     }
 
diff --git a/ui/src/widget/migrate_window.rs b/ui/src/widget/migrate_window.rs
index c87cf9a..42b4e95 100644
--- a/ui/src/widget/migrate_window.rs
+++ b/ui/src/widget/migrate_window.rs
@@ -174,6 +174,7 @@ impl PdmMigrateWindow {
                             None,
                             guest_info.vmid,
                             target_remote.to_string(),
+                            None,
                             migrate_opts,
                         )
                         .await?
@@ -218,6 +219,7 @@ impl PdmMigrateWindow {
                             None,
                             guest_info.vmid,
                             target_remote.to_string(),
+                            None,
                             migrate_opts,
                         )
                         .await?
-- 
2.39.5



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


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

* [pdm-devel] [PATCH datacenter-manager v2 3/4] ui: widget: add remote endpoint selector
  2025-01-14 11:21 [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 1/4] pdm-client: add cluster status method Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 2/4] pdm-client: add target-endpoint parameter to remote migration methods Dominik Csapak
@ 2025-01-14 11:21 ` Dominik Csapak
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 4/4] ui: migrate: make target endpoint selectable for remote migration Dominik Csapak
  2025-01-14 13:25 ` [pdm-devel] applied: [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dietmar Maurer
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2025-01-14 11:21 UTC (permalink / raw)
  To: pdm-devel

this is a widget to select a specific endpoint, showing the hostname as
the value. This can be useful in situations where we wan to explicitly
select an endpoint, e.g. remote migration.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
changes from v1:
* fix typo in commit message
 ui/src/widget/mod.rs                      |   2 +
 ui/src/widget/remote_endpoint_selector.rs | 103 ++++++++++++++++++++++
 2 files changed, 105 insertions(+)
 create mode 100644 ui/src/widget/remote_endpoint_selector.rs

diff --git a/ui/src/widget/mod.rs b/ui/src/widget/mod.rs
index b885d1b..ee9e799 100644
--- a/ui/src/widget/mod.rs
+++ b/ui/src/widget/mod.rs
@@ -21,3 +21,5 @@ pub use search_box::SearchBox;
 
 mod remote_selector;
 pub use remote_selector::RemoteSelector;
+
+mod remote_endpoint_selector;
diff --git a/ui/src/widget/remote_endpoint_selector.rs b/ui/src/widget/remote_endpoint_selector.rs
new file mode 100644
index 0000000..779d98c
--- /dev/null
+++ b/ui/src/widget/remote_endpoint_selector.rs
@@ -0,0 +1,103 @@
+use std::rc::Rc;
+
+use wasm_bindgen::UnwrapThrowExt;
+use yew::{
+    html::{IntoEventCallback, IntoPropValue},
+    AttrValue, Callback, Component, Properties,
+};
+
+use pwt::{
+    props::{FieldBuilder, WidgetBuilder},
+    widget::form::Combobox,
+};
+use pwt_macros::{builder, widget};
+
+use crate::RemoteList;
+
+#[widget(comp=PdmEndpointSelector, @input)]
+#[derive(Clone, Properties, PartialEq)]
+#[builder]
+pub struct EndpointSelector {
+    /// The default value
+    #[builder(IntoPropValue, into_prop_value)]
+    #[prop_or_default]
+    pub default: Option<AttrValue>,
+
+    /// Change callback
+    #[builder_cb(IntoEventCallback, into_event_callback, String)]
+    #[prop_or_default]
+    pub on_change: Option<Callback<String>>,
+
+    /// The remote to list Endpoints from
+    #[builder(IntoPropValue, into_prop_value)]
+    #[prop_or_default]
+    pub remote: AttrValue,
+}
+
+impl EndpointSelector {
+    pub fn new(remote: AttrValue) -> Self {
+        yew::props!(Self { remote })
+    }
+}
+
+pub struct PdmEndpointSelector {
+    endpoints: Rc<Vec<AttrValue>>,
+}
+
+impl PdmEndpointSelector {
+    fn update_endpoint_list(&mut self, ctx: &yew::Context<Self>) {
+        let (remotes, _): (RemoteList, _) = ctx
+            .link()
+            .context(ctx.link().callback(|_| ()))
+            .unwrap_throw();
+
+        let remote_id = ctx.props().remote.as_str();
+
+        for remote in remotes.iter() {
+            if remote.id != remote_id {
+                continue;
+            }
+
+            let endpoints = remote
+                .nodes
+                .iter()
+                .map(|endpoint| AttrValue::from(endpoint.hostname.clone()))
+                .collect();
+            self.endpoints = Rc::new(endpoints);
+            break;
+        }
+    }
+}
+
+impl Component for PdmEndpointSelector {
+    type Message = ();
+    type Properties = EndpointSelector;
+
+    fn create(ctx: &yew::Context<Self>) -> Self {
+        let mut this = Self {
+            endpoints: Rc::new(Vec::new()),
+        };
+
+        this.update_endpoint_list(ctx);
+        this
+    }
+
+    fn changed(&mut self, ctx: &yew::Context<Self>, old_props: &Self::Properties) -> bool {
+        if ctx.props().remote != old_props.remote {
+            log::info!("{} {}", ctx.props().remote, old_props.remote);
+            self.update_endpoint_list(ctx);
+        }
+        true
+    }
+
+    fn view(&self, ctx: &yew::Context<Self>) -> yew::Html {
+        let props = ctx.props();
+        Combobox::new()
+            .with_std_props(&props.std_props)
+            .with_input_props(&props.input_props)
+            .on_change(props.on_change.clone())
+            .default(props.default.clone())
+            .items(self.endpoints.clone())
+            .into()
+    }
+}
-- 
2.39.5



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


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

* [pdm-devel] [PATCH datacenter-manager v2 4/4] ui: migrate: make target endpoint selectable for remote migration
  2025-01-14 11:21 [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dominik Csapak
                   ` (2 preceding siblings ...)
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 3/4] ui: widget: add remote endpoint selector Dominik Csapak
@ 2025-01-14 11:21 ` Dominik Csapak
  2025-01-14 13:25 ` [pdm-devel] applied: [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dietmar Maurer
  4 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2025-01-14 11:21 UTC (permalink / raw)
  To: pdm-devel

by showing a target endpoint selector instead of a target node one when
it's a remote migration.

For the user to be able to select the target storage/network properly,
we have to query the nodename for the target and update the
storage/network selectors.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
changes from v1:
* adapt to changes from pdm-client patch (basic rebase)

 ui/src/widget/migrate_window.rs | 70 +++++++++++++++++++++++++++++++--
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/ui/src/widget/migrate_window.rs b/ui/src/widget/migrate_window.rs
index 42b4e95..7214ff4 100644
--- a/ui/src/widget/migrate_window.rs
+++ b/ui/src/widget/migrate_window.rs
@@ -23,6 +23,7 @@ use pdm_client::{MigrateLxc, MigrateQemu, RemoteMigrateLxc, RemoteMigrateQemu};
 use crate::pve::GuestInfo;
 use crate::pve::GuestType;
 
+use super::remote_endpoint_selector::EndpointSelector;
 use super::{
     PveMigrateMap, PveNetworkSelector, PveNodeSelector, PveStorageSelector, RemoteSelector,
 };
@@ -62,6 +63,8 @@ impl MigrateWindow {
 
 pub enum Msg {
     RemoteChange(String),
+    EndpointChange(String),
+    NodenameResult(Result<String, proxmox_client::Error>),
     Result(RemoteUpid),
     LoadPreconditions(Option<AttrValue>),
     PreconditionResult(Result<QemuMigratePreconditions, proxmox_client::Error>),
@@ -71,9 +74,29 @@ pub struct PdmMigrateWindow {
     target_remote: AttrValue,
     _async_pool: AsyncPool,
     preconditions: Option<QemuMigratePreconditions>,
+    target_node: Option<AttrValue>,
 }
 
 impl PdmMigrateWindow {
+    async fn get_nodename(
+        remote: AttrValue,
+        target_endpoint: AttrValue,
+    ) -> Result<String, proxmox_client::Error> {
+        let status_list = crate::pdm_client()
+            .pve_cluster_status(&remote, Some(&target_endpoint))
+            .await?;
+
+        for status in status_list {
+            if status.local.unwrap_or(false) {
+                return Ok(status.name);
+            }
+        }
+
+        Err(proxmox_client::Error::Other(
+            "could not find local nodename",
+        ))
+    }
+
     async fn load_preconditions(
         remote: String,
         guest_info: GuestInfo,
@@ -132,6 +155,7 @@ impl PdmMigrateWindow {
         let target_remote = value["remote"].as_str().unwrap_or_default();
 
         let upid = if target_remote != remote {
+            let target_endpoint = value.get("target-endpoint").and_then(|e| e.as_str());
             match guest_info.guest_type {
                 crate::pve::GuestType::Qemu => {
                     let mut migrate_opts = RemoteMigrateQemu::new()
@@ -174,7 +198,7 @@ impl PdmMigrateWindow {
                             None,
                             guest_info.vmid,
                             target_remote.to_string(),
-                            None,
+                            target_endpoint,
                             migrate_opts,
                         )
                         .await?
@@ -219,7 +243,7 @@ impl PdmMigrateWindow {
                             None,
                             guest_info.vmid,
                             target_remote.to_string(),
-                            None,
+                            target_endpoint,
                             migrate_opts,
                         )
                         .await?
@@ -268,10 +292,12 @@ impl PdmMigrateWindow {
         source_remote: AttrValue,
         guest_info: GuestInfo,
         preconditions: Option<QemuMigratePreconditions>,
+        target_node: Option<AttrValue>,
     ) -> Html {
         let same_remote = target_remote == source_remote;
         if !same_remote {
-            form_ctx.write().set_field_value("node", "".into());
+            let node = target_node.unwrap_or_default().to_string();
+            form_ctx.write().set_field_value("node", node.into());
         }
         let detail_mode = form_ctx.read().get_field_checked("detailed-mode");
         let mut uses_local_disks = false;
@@ -338,13 +364,27 @@ impl PdmMigrateWindow {
                 tr!("Mode"),
                 DisplayField::new("").name("migrate-mode").key("mode"),
             )
-            .with_right_field(
+            .with_field_and_options(
+                pwt::widget::FieldPosition::Right,
+                false,
+                !same_remote,
                 tr!("Target Node"),
                 PveNodeSelector::new(target_remote.clone())
                     .name("node")
                     .required(same_remote)
                     .on_change(link.callback(Msg::LoadPreconditions))
                     .disabled(!same_remote),
+            )
+            .with_field_and_options(
+                pwt::widget::FieldPosition::Right,
+                false,
+                same_remote,
+                tr!("Target Endpoint"),
+                EndpointSelector::new(target_remote.clone())
+                    .placeholder(tr!("Automatic"))
+                    .name("target-endpoint")
+                    .on_change(link.callback(Msg::EndpointChange))
+                    .disabled(same_remote),
             );
 
         if !same_remote || uses_local_disks || uses_local_resources {
@@ -462,6 +502,7 @@ impl Component for PdmMigrateWindow {
             target_remote: ctx.props().remote.clone(),
             _async_pool: AsyncPool::new(),
             preconditions: None,
+            target_node: None,
         }
     }
 
@@ -503,6 +544,25 @@ impl Component for PdmMigrateWindow {
                 }
                 true
             }
+            Msg::EndpointChange(endpoint) => {
+                let remote = self.target_remote.clone();
+                self._async_pool
+                    .send_future(ctx.link().clone(), async move {
+                        let res = Self::get_nodename(remote, endpoint.into()).await;
+                        Msg::NodenameResult(res)
+                    });
+                false
+            }
+            Msg::NodenameResult(result) => match result {
+                Ok(nodename) => {
+                    self.target_node = Some(nodename.into());
+                    true
+                }
+                Err(err) => {
+                    log::error!("could not extract nodename from endpoint: {err}");
+                    false
+                }
+            },
         }
     }
 
@@ -527,6 +587,7 @@ impl Component for PdmMigrateWindow {
                 let source_remote = ctx.props().remote.clone();
                 let link = ctx.link().clone();
                 let preconditions = self.preconditions.clone();
+                let target_node = self.target_node.clone();
                 move |form| {
                     Self::input_panel(
                         &link,
@@ -535,6 +596,7 @@ impl Component for PdmMigrateWindow {
                         source_remote.clone(),
                         guest_info,
                         preconditions.clone(),
+                        target_node.clone(),
                     )
                 }
             })
-- 
2.39.5



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


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

* [pdm-devel] applied: [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable
  2025-01-14 11:21 [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dominik Csapak
                   ` (3 preceding siblings ...)
  2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 4/4] ui: migrate: make target endpoint selectable for remote migration Dominik Csapak
@ 2025-01-14 13:25 ` Dietmar Maurer
  4 siblings, 0 replies; 6+ messages in thread
From: Dietmar Maurer @ 2025-01-14 13:25 UTC (permalink / raw)
  To: Proxmox Datacenter Manager development discussion, Dominik Csapak

applied


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


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

end of thread, other threads:[~2025-01-14 13:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-01-14 11:21 [pdm-devel] [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dominik Csapak
2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 1/4] pdm-client: add cluster status method Dominik Csapak
2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 2/4] pdm-client: add target-endpoint parameter to remote migration methods Dominik Csapak
2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 3/4] ui: widget: add remote endpoint selector Dominik Csapak
2025-01-14 11:21 ` [pdm-devel] [PATCH datacenter-manager v2 4/4] ui: migrate: make target endpoint selectable for remote migration Dominik Csapak
2025-01-14 13:25 ` [pdm-devel] applied: [PATCH datacenter-manager v2 0/4] remote migration: make target endpoint selectable Dietmar Maurer

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