From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 35AED1FF0E0 for ; Thu, 09 Jul 2026 10:21:13 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id D3921214C9; Thu, 09 Jul 2026 10:20:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783366866; x=1783971666; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=056OWqSYOi10nOvpVVGM1WmdvLKF6x7HiQ+0f6bQ0Vw=; b=Dn+toz2ybUvxp/9cJ/vRcTYlYasEZypn2SsZr2MhO+P9cf5qvFvUWLUdKtua/GGIwu lPP7kaSmtmaiZHA6eSF0kDVbLdRnXBPbUY+a1xRAHJpS74ahS08CtWHmFwpUwmzl3EZW BjBgevuKfqTsqrSXJkdFt53IrRhf+GHQQX6Qd5tNR2fwV0+USesCePKL2yCOirOcfZia SLvvCar70xZZdi3566B0AHCZRRbepdYcsyyemJr14plp7+JFzD0Kk/B2qxOYaeGfxIjH 3S4YuX5AwKt4zexrfPPLoP/pwPYWVO+nh3fRuK6Z3DYEUN7qomfYU+RBup/jeZKAa6H5 UV8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783366866; x=1783971666; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=056OWqSYOi10nOvpVVGM1WmdvLKF6x7HiQ+0f6bQ0Vw=; b=W0P8POoZb3c+dlYF8JRd2ck0j/vUxoN2V8rTP320hL0sqIJRTr3s5zG0To/6nh0NiN UUEjNr1LaLJOGEaboKqbsWPfRQUgv/y0n0Eb27PLGh8/baD+yZphsiskFBmx7O1gtds4 Ak3lakHBGF+M2vZBE17IaaDv15m8w6W6OG6vWDHDnr+NceBk6ABpu/fT3RzV9A5C8jx9 G3dnEtDEo9V9iY9CnTzfPpKpE01XiGHjacUiPHhF7bfIPD50ghLJLpgr/G4jIWwEY7kQ IFVGIYVNMujwXcM2Gx0Q/KeAqkuiNBWlYUlTRF0bBpW1NZw/A462hQU8qJcFUK6DPYDe RAJw== X-Gm-Message-State: AOJu0YxtEBkT5MQ/ENrZ+n3Qo05mKf+6k/tdguUBllpNvjIfFsBUXeZ6 P/JC3ppRi0EcevA/rGPwH/EhchGeRCkpvgbQspLkpken5OhmDS3VzXGcgYNwG35x X-Gm-Gg: AfdE7cnUr2rgZ2aHstmlql+tDaV9tLrqcrFz+GLJuBDlMpv0Mxnb0o6CpZZCSxJZyCX Z5LSE6DszBC6vH4Cfduw0G+eu/pHjOghsSwpNIqqYVd+XB3M8gh7nNMuw7ppW3rj472M0tqi5mg Fas7VH/qoGezfgl7R2f8wflm3+L1L8I1nuaCAB0yczanwP1I/18JmX7tYVnepbbXQT1YmGzxc4U MKMm7SwbWLJgnK0PjQy7Ht9vFTu1lT9/C1nPFDyJqVZwoZLMam63ks66okkKAv2bS/nwyTDZDrV hRuRg6RwTRiIiiWumLUVtK0Ayh59oxyCh8p0IFqHVUqyAZ2KHJlihg0/bvWJYRVy3jT+5F5moKm HgnJBYxL96zCDZtiR0jIcRFXO/V7aof0AJxtoM4nWeAgymUrvvnITLmAXlRgB9ECNrykbJJQr19 zt4/+XbXaIjOcQJA== X-Received: by 2002:a05:6000:29c8:b0:460:3233:beee with SMTP id ffacd0b85a97d-47de66ac941mr1309481f8f.42.1783366865643; Mon, 06 Jul 2026 12:41:05 -0700 (PDT) From: copystring To: pve-devel@lists.proxmox.com subject: SPAM: [RFC PATCH pve-manager] lxc: add OCI rootfs replacement window Date: Mon, 6 Jul 2026 21:40:58 +0200 Message-ID: <20260706194059.280257-6-copystring@gmail.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260706194059.280257-1-copystring@gmail.com> References: <20260706194059.280257-1-copystring@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 3 AWL 0.307 Adjusted score from AWL reputation of From: address DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy FREEMAIL_FROM 0.001 Sender email is commonly abused enduser mail provider RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust RCVD_IN_SBL_CSS 3.558 Received via a relay in Spamhaus SBL-CSS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-MailFrom: copystring@gmail.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation Message-ID-Hash: RHDXRLRKZ4EKBBNY76FNBGZQHEFS76IU X-Message-ID-Hash: RHDXRLRKZ4EKBBNY76FNBGZQHEFS76IU X-Mailman-Approved-At: Thu, 09 Jul 2026 10:20:40 +0200 CC: copystring X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Add a guarded UI flow for replacing a stopped container rootfs from an OCI image archive. The action requires VM.Config.Disk, keeps the submit button disabled until the current config digest is loaded and the user confirms the rootfs replacement, and calls the new backend endpoint. Signed-off-by: copystring --- www/manager6/Makefile | 1 + www/manager6/lxc/CmdMenu.js | 14 +++ www/manager6/lxc/Config.js | 22 ++++ www/manager6/lxc/RootfsOciReplace.js | 149 +++++++++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 www/manager6/lxc/RootfsOciReplace.js diff --git a/www/manager6/Makefile b/www/manager6/Makefile index d4dd3f35..4f7206b9 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -207,6 +207,7 @@ JSSRC= \ dc/PCIMapView.js \ dc/USBMapView.js \ dc/DirMapView.js \ + lxc/RootfsOciReplace.js \ lxc/CmdMenu.js \ lxc/Config.js \ lxc/CreateWizard.js \ diff --git a/www/manager6/lxc/CmdMenu.js b/www/manager6/lxc/CmdMenu.js index a7940e04..93bea07f 100644 --- a/www/manager6/lxc/CmdMenu.js +++ b/www/manager6/lxc/CmdMenu.js @@ -134,6 +134,20 @@ Ext.define('PVE.lxc.CmdMenu', { }, }, { xtype: 'menuseparator' }, + { + text: gettext('Replace Rootfs from OCI'), + iconCls: 'fa fa-fw fa-refresh', + hidden: !caps.vms['VM.Config.Disk'], + disabled: me.isTemplate || !stopped, + handler: () => { + Ext.create('PVE.lxc.RootfsOciReplace', { + nodename: info.node, + vmid: info.vmid, + canUpdateOptions: caps.vms['VM.Config.Options'], + autoShow: true, + }); + }, + }, { text: gettext('Take Snapshot'), iconCls: 'fa fa-fw fa-history', diff --git a/www/manager6/lxc/Config.js b/www/manager6/lxc/Config.js index 3a99d6a7..9058a287 100644 --- a/www/manager6/lxc/Config.js +++ b/www/manager6/lxc/Config.js @@ -147,6 +147,22 @@ Ext.define('PVE.lxc.Config', { }); }, }, + { + text: gettext('Replace Rootfs from OCI'), + itemId: 'replaceRootfsFromOciBtn', + iconCls: 'fa fa-fw fa-refresh', + hidden: !caps.vms['VM.Config.Disk'], + disabled: template || running, + handler: function () { + Ext.create('PVE.lxc.RootfsOciReplace', { + nodename: nodename, + vmid: vmid, + canUpdateOptions: caps.vms['VM.Config.Options'], + reload: () => me.statusStore.load(), + autoShow: true, + }); + }, + }, { iconCls: 'fa fa-heartbeat ', hidden: !caps.nodes['Sys.Console'], @@ -424,6 +440,12 @@ Ext.define('PVE.lxc.Config', { startBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status === 'running' || template); shutdownBtn.setDisabled(!caps.vms['VM.PowerMgmt'] || status !== 'running'); me.down('#removeBtn').setDisabled(!caps.vms['VM.Allocate'] || status !== 'stopped'); + let replaceRootfsBtn = me.down('#replaceRootfsFromOciBtn'); + if (replaceRootfsBtn) { + replaceRootfsBtn.setDisabled( + !caps.vms['VM.Config.Disk'] || template || status !== 'stopped', + ); + } consoleBtn.setDisabled(template); if (prevStatus === 'stopped' && status === 'running') { diff --git a/www/manager6/lxc/RootfsOciReplace.js b/www/manager6/lxc/RootfsOciReplace.js new file mode 100644 index 00000000..2b6ffe78 --- /dev/null +++ b/www/manager6/lxc/RootfsOciReplace.js @@ -0,0 +1,149 @@ +Ext.define('PVE.lxc.RootfsOciReplace', { + extend: 'Proxmox.window.Edit', + alias: 'widget.pveLxcRootfsOciReplace', + + title: gettext('Replace Rootfs from OCI Image'), + isCreate: true, + method: 'POST', + submitText: gettext('Replace Rootfs'), + showTaskViewer: true, + autoLoad: true, + width: 760, + fieldDefaults: { + labelWidth: 130, + }, + + initComponent: function () { + let me = this; + + if (!me.nodename) { + throw 'no node name specified'; + } + if (!me.vmid) { + throw 'no VM ID specified'; + } + + me.url = '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/replace_rootfs_from_oci'; + me.loadUrl = '/nodes/' + me.nodename + '/lxc/' + me.vmid + '/config'; + + let canUpdateOptions = !!me.canUpdateOptions; + let warningHtml = [ + `${Ext.String.htmlEncode(gettext('Rootfs replacement is destructive.'))}`, + Ext.String.htmlEncode(gettext('The current rootfs / will be replaced by the OCI image.')), + Ext.String.htmlEncode(gettext('Separate mount points such as mp0, mp1, ... are preserved.')), + Ext.String.htmlEncode( + gettext( + 'Data stored only on / will not be part of the new active rootfs. The old rootfs is kept as an unused volume.', + ), + ), + ].join('
'); + + let templateStorageSelector = Ext.create('PVE.form.StorageSelector', { + name: 'tmplstorage', + fieldLabel: gettext('Image Storage'), + nodename: me.nodename, + storageContent: 'vztmpl', + autoSelect: true, + allowBlank: false, + submitValue: false, + }); + + let templateSelector = Ext.create('PVE.form.FileSelector', { + name: 'ostemplate', + fieldLabel: gettext('OCI Image'), + nodename: me.nodename, + storageContent: 'vztmpl', + allowBlank: false, + }); + + templateStorageSelector.on('change', function (_field, storage) { + templateSelector.setStorage(storage, me.nodename); + }); + + me.items = [ + { + xtype: 'inputpanel', + border: false, + onGetValues: function (values) { + delete values.tmplstorage; + delete values.confirmRootfsReplace; + + values['confirm-rootfs-replace'] = 1; + values['update-oci-config'] = values['update-oci-config'] ? 1 : 0; + + if (!values.storage) { + delete values.storage; + } + if (!values.size) { + delete values.size; + } + + return values; + }, + columnT: [ + { + xtype: 'displayfield', + fieldLabel: gettext('Warning'), + userCls: 'pmx-hint', + value: warningHtml, + }, + templateStorageSelector, + templateSelector, + ], + column1: [ + { + xtype: 'pveStorageSelector', + name: 'storage', + fieldLabel: gettext('Target Storage'), + nodename: me.nodename, + storageContent: 'rootdir', + allowBlank: true, + autoSelect: false, + emptyText: gettext('Current rootfs storage'), + }, + { + xtype: 'proxmoxtextfield', + name: 'size', + fieldLabel: gettext('Root Disk Size'), + allowBlank: true, + emptyText: gettext('Current size'), + regex: /^\d+(\.\d+)?[KMGT]?$/, + regexText: gettext('Invalid size'), + }, + ], + column2: [ + { + xtype: 'proxmoxcheckbox', + name: 'update-oci-config', + fieldLabel: gettext('Update OCI config'), + checked: canUpdateOptions, + disabled: !canUpdateOptions, + uncheckedValue: 0, + }, + { + xtype: 'proxmoxcheckbox', + name: 'confirmRootfsReplace', + fieldLabel: gettext('Confirmation'), + boxLabel: gettext( + 'I understand that the current rootfs will be replaced and that data stored only on / will no longer be active.', + ), + submitValue: false, + uncheckedValue: 0, + getErrors: function () { + return this.getValue() ? [] : [gettext('Confirmation required')]; + }, + listeners: { + change: function (field) { + field.validate(); + }, + }, + }, + ], + }, + ]; + + me.callParent(); + + me.on('destroy', () => Ext.isFunction(me.reload) && me.reload()); + }, +}); -- 2.55.0.windows.2