From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 9DC7E1FF133 for ; Mon, 27 Apr 2026 14:18:54 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 285E11BD3A; Mon, 27 Apr 2026 14:18:46 +0200 (CEST) From: Markus Frank To: pve-devel@lists.proxmox.com Subject: [PATCH manager v3 11/11] directory mapping: add live-migration-method option for virtiofs Date: Mon, 27 Apr 2026 14:17:00 +0200 Message-ID: <20260427121746.270544-12-m.frank@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260427121746.270544-1-m.frank@proxmox.com> References: <20260427121746.270544-1-m.frank@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1777292195172 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.008 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: QRYKMG5CGWX2IAM4FRUR5AVCBKN3PKD6 X-Message-ID-Hash: QRYKMG5CGWX2IAM4FRUR5AVCBKN3PKD6 X-MailFrom: m.frank@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Display warnings indicating that this feature is experimental. Add the Messages.js file to reuse warnings, hints, and other messages in different locations. Display the virtiofs warnings from the Messages.js file in the migration and directory edit windows. Requires the qemu-server commit named "virtiofs: add live migration support". Signed-off-by: Markus Frank --- v3 changes: * remove severity option from Messages.js * renamed hideComment to hideClusterOptions * moved all live-migration related fields to columnB * added has_virtiofs_live_migration variable * improved the warning messages www/manager6/Makefile | 1 + www/manager6/Messages.js | 15 ++++++++ www/manager6/dc/DirMapView.js | 8 ++++ www/manager6/window/DirMapEdit.js | 63 +++++++++++++++++++++++++++++-- www/manager6/window/Migrate.js | 13 +++++++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 www/manager6/Messages.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index b506849d..80ce4a7c 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -7,6 +7,7 @@ JSSRC= \ StateProvider.js \ Utils.js \ UIOptions.js \ + Messages.js \ Toolkit.js \ VNCConsole.js \ button/ConsoleButton.js \ diff --git a/www/manager6/Messages.js b/www/manager6/Messages.js new file mode 100644 index 00000000..8e412a45 --- /dev/null +++ b/www/manager6/Messages.js @@ -0,0 +1,15 @@ +Ext.define('PVE.Messages', { + singleton: true, + + virtiofs: { + prerequisites: gettext( + 'Before migrating with a virtiofs device, ensure that virtiofsd is installed on all hosts and that they are using the same shared directory on the same file system (e.g. CephFS).', + ), + umount: gettext( + 'The safest way to migrate a VM with a virtiofs device is to either unmount or mount the device as read-only.', + ), + migration_method_change: gettext( + 'Changes to the migration method will only take effect once a VM has been stopped and restarted, or migrated. When changing this from disabled, please restart VMs to ensure that additional migration checks are enabled.', + ), + }, +}); diff --git a/www/manager6/dc/DirMapView.js b/www/manager6/dc/DirMapView.js index f0dfe28f..26f938e9 100644 --- a/www/manager6/dc/DirMapView.js +++ b/www/manager6/dc/DirMapView.js @@ -26,6 +26,14 @@ Ext.define('PVE.dc.DirMapView', { dataIndex: 'text', width: 200, }, + { + header: gettext('Live Migration Method'), + dataIndex: 'live-migration', + renderer: function (value, _meta, record) { + return Ext.String.htmlEncode(value ?? record.data['live-migration-method']); + }, + width: 150, + }, { header: gettext('Comment'), dataIndex: 'description', diff --git a/www/manager6/window/DirMapEdit.js b/www/manager6/window/DirMapEdit.js index 841ff1fe..ac34d1a4 100644 --- a/www/manager6/window/DirMapEdit.js +++ b/www/manager6/window/DirMapEdit.js @@ -8,7 +8,7 @@ Ext.define('PVE.window.DirMapEditWindow', { me.isCreate = !me.name; me.method = me.isCreate ? 'POST' : 'PUT'; me.hideMapping = !!me.entryOnly; - me.hideComment = me.name && !me.entryOnly; + me.hideClusterOptions = me.name && !me.entryOnly; me.hideNodeSelector = me.nodename || me.entryOnly; me.hideNode = !me.nodename || !me.hideNodeSelector; return { @@ -17,6 +17,15 @@ Ext.define('PVE.window.DirMapEditWindow', { }; }, + viewModel: { + data: { + migrationMethod: '__default__', + }, + formulas: { + hideMigrationHints: (get) => get('migrationMethod') === '__default__', + }, + }, + submitUrl: function (_url, data) { let me = this; let name = me.isCreate ? '' : me.name; @@ -39,8 +48,10 @@ Ext.define('PVE.window.DirMapEditWindow', { let name = values.name; let description = values.description; + let liveMigration = values['live-migration-method']; let deletes = values.delete; + delete values['live-migration-method']; delete values.description; delete values.name; delete values.delete; @@ -69,6 +80,9 @@ Ext.define('PVE.window.DirMapEditWindow', { if (description) { values.description = description; } + if (liveMigration) { + values['live-migration-method'] = liveMigration; + } if (deletes && !view.isCreate) { values.delete = deletes; } @@ -184,13 +198,56 @@ Ext.define('PVE.window.DirMapEditWindow', { ], columnB: [ + { + xtype: 'proxmoxKVComboBox', + fieldLabel: ` ${gettext('Experimental')}: + ${gettext('Live Migration Method')}`, + reference: 'live-migration-method', + name: 'live-migration-method', + comboItems: [ + [ + '__default__', + Proxmox.Utils.defaultText + ' (' + Proxmox.Utils.disabledText + ')', + ], + ['find-paths', 'find-paths'], + ['file-handles', 'file-handles'], + ], + cbind: { + deleteEmpty: '{!isCreate}', + disabled: '{hideClusterOptions}', + hidden: '{hideClusterOptions}', + }, + bind: { + value: '{migrationMethod}', + }, + }, + { + xtype: 'displayfield', + reference: 'migration-method-change-hint', + columnWidth: 1, + value: PVE.Messages.virtiofs.migration_method_change, + bind: { + hidden: '{hideMigrationHints}', + }, + userCls: 'pmx-hint', + }, + { + xtype: 'displayfield', + reference: 'migration-hint', + columnWidth: 1, + value: PVE.Messages.virtiofs.prerequisites + ' ' + PVE.Messages.virtiofs.umount, + bind: { + hidden: '{hideMigrationHints}', + }, + userCls: 'pmx-hint', + }, { xtype: 'fieldcontainer', defaultType: 'radiofield', layout: 'fit', cbind: { - disabled: '{hideComment}', - hidden: '{hideComment}', + disabled: '{hideClusterOptions}', + hidden: '{hideClusterOptions}', }, items: [ { diff --git a/www/manager6/window/Migrate.js b/www/manager6/window/Migrate.js index ff80c70c..1bc0315d 100644 --- a/www/manager6/window/Migrate.js +++ b/www/manager6/window/Migrate.js @@ -335,6 +335,19 @@ Ext.define('PVE.window.Migrate', { if (vm.get('running')) { let allowed = []; let notAllowed = []; + let has_virtiofs_live_migration = Object.entries(mappedResources).some( + ([key, resource]) => key.match(/^virtiofs\d+/) && resource['live-migration'], + ); + if (has_virtiofs_live_migration) { + migration.preconditions.push({ + text: PVE.Messages.virtiofs.prerequisites, + severity: 'warning', + }); + migration.preconditions.push({ + text: PVE.Messages.virtiofs.umount, + severity: 'warning', + }); + } for (const [key, resource] of Object.entries(mappedResources)) { if (resource['live-migration']) { allowed.push(key); -- 2.47.3