[pve-devel] [PATCH manager v3 2/3] ui: migrate: lxc: display precondition messages for ha resource affinity

Daniel Kral d.kral at proxmox.com
Fri Jul 4 20:20:59 CEST 2025


Extend the container precondition check to show whether a migration of a
container results in any additional migrations because of positive HA
resource affinity rules or if any migrations cannot be completed because
of any negative resource affinity rules.

In the latter case these migrations would be blocked when executing the
migrations anyway by the HA Manager's CLI and it state machine, but this
gives a better heads-up about this. However, additional migrations are
not reported in advance by the CLI yet, so these warnings are crucial to
warn users about the comigrated HA resources.

Signed-off-by: Daniel Kral <d.kral at proxmox.com>
---
 www/manager6/window/Migrate.js | 87 ++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 3 deletions(-)

diff --git a/www/manager6/window/Migrate.js b/www/manager6/window/Migrate.js
index 4103ad13..dff6af08 100644
--- a/www/manager6/window/Migrate.js
+++ b/www/manager6/window/Migrate.js
@@ -195,7 +195,7 @@ Ext.define('PVE.window.Migrate', {
             if (vm.get('vmtype') === 'qemu') {
                 await me.checkQemuPreconditions(resetMigrationPossible);
             } else {
-                me.checkLxcPreconditions(resetMigrationPossible);
+                await me.checkLxcPreconditions(resetMigrationPossible);
             }
 
             // Only allow nodes where the local storage is available in case of offline migration
@@ -363,11 +363,92 @@ Ext.define('PVE.window.Migrate', {
 
             vm.set('migration', migration);
         },
-        checkLxcPreconditions: function (resetMigrationPossible) {
-            let vm = this.getViewModel();
+        checkLxcPreconditions: async function (resetMigrationPossible) {
+            let me = this;
+            let vm = me.getViewModel();
+            let migrateStats;
+
             if (vm.get('running')) {
                 vm.set('migration.mode', 'restart');
             }
+
+            try {
+                if (
+                    me.fetchingNodeMigrateInfo &&
+                    me.fetchingNodeMigrateInfo === vm.get('nodename')
+                ) {
+                    return;
+                }
+                me.fetchingNodeMigrateInfo = vm.get('nodename');
+                let { result } = await Proxmox.Async.api2({
+                    url: `/nodes/${vm.get('nodename')}/${vm.get('vmtype')}/${vm.get('vmid')}/migrate`,
+                    method: 'GET',
+                });
+                migrateStats = result.data;
+                me.fetchingNodeMigrateInfo = false;
+            } catch (error) {
+                Ext.Msg.alert(Proxmox.Utils.errorText, error.htmlStatus);
+                return;
+            }
+
+            if (migrateStats.running) {
+                vm.set('running', true);
+            }
+
+            // Get migration object from viewmodel to prevent to many bind callbacks
+            let migration = vm.get('migration');
+            if (resetMigrationPossible) {
+                migration.possible = true;
+            }
+            migration.preconditions = [];
+            let targetNode = me.lookup('pveNodeSelector').value;
+            let disallowed = migrateStats['not-allowed-nodes']?.[targetNode] ?? {};
+
+            let blockingHAResources = disallowed['blocking-ha-resources'] ?? [];
+            if (blockingHAResources.length) {
+                migration.possible = false;
+
+                for (const { sid, cause } of blockingHAResources) {
+                    let reasonText;
+                    if (cause === 'resource-affinity') {
+                        reasonText = Ext.String.format(
+                            gettext(
+                                'HA resource {0} with negative affinity to container on selected target node',
+                            ),
+                            sid,
+                        );
+                    } else {
+                        reasonText = Ext.String.format(
+                            gettext('blocking HA resource {0} on selected target node'),
+                            sid,
+                        );
+                    }
+
+                    migration.preconditions.push({
+                        severity: 'error',
+                        text: Ext.String.format(
+                            gettext('Cannot migrate container, because {0}.'),
+                            reasonText,
+                        ),
+                    });
+                }
+            }
+
+            let comigratedHAResources = migrateStats['comigrated-ha-resources'];
+            if (comigratedHAResources !== undefined) {
+                for (const sid of comigratedHAResources) {
+                    const text = Ext.String.format(
+                        gettext(
+                            'HA resource {0} with positive affinity to container is also migrated to selected target node.',
+                        ),
+                        sid,
+                    );
+
+                    migration.preconditions.push({ text, severity: 'warning' });
+                }
+            }
+
+            vm.set('migration', migration);
         },
     },
 
-- 
2.39.5





More information about the pve-devel mailing list