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 9F96C1FF138 for ; Wed, 18 Feb 2026 11:23:54 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 13F55182CF; Wed, 18 Feb 2026 11:24:42 +0100 (CET) From: Hannes Laimer To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager 1/1] ui: sdn: add ipv6 options for subnets in evpns zones Date: Wed, 18 Feb 2026 11:23:48 +0100 Message-ID: <20260218102350.211294-5-h.laimer@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260218102350.211294-1-h.laimer@proxmox.com> References: <20260218102350.211294-1-h.laimer@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1771410235950 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.061 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: P4FDKKBEHW7B74PQYFTXAQI3HLNW67CT X-Message-ID-Hash: P4FDKKBEHW7B74PQYFTXAQI3HLNW67CT X-MailFrom: h.laimer@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: Signed-off-by: Hannes Laimer --- www/manager6/form/SDNVnetSelector.js | 2 +- www/manager6/sdn/SubnetEdit.js | 132 ++++++++++++++++++++++++++- www/manager6/sdn/SubnetView.js | 6 +- www/manager6/sdn/VnetView.js | 5 +- 4 files changed, 140 insertions(+), 5 deletions(-) diff --git a/www/manager6/form/SDNVnetSelector.js b/www/manager6/form/SDNVnetSelector.js index 9e54159c..5ccc3cfb 100644 --- a/www/manager6/form/SDNVnetSelector.js +++ b/www/manager6/form/SDNVnetSelector.js @@ -52,7 +52,7 @@ Ext.define( function () { Ext.define('pve-sdn-vnet', { extend: 'Ext.data.Model', - fields: ['alias', 'tag', 'type', 'vnet', 'zone'], + fields: ['alias', 'tag', 'type', 'vnet', 'zone', 'zone-type'], proxy: { type: 'proxmox', url: '/api2/json/cluster/sdn/vnets', diff --git a/www/manager6/sdn/SubnetEdit.js b/www/manager6/sdn/SubnetEdit.js index a3608428..89cabce4 100644 --- a/www/manager6/sdn/SubnetEdit.js +++ b/www/manager6/sdn/SubnetEdit.js @@ -2,6 +2,21 @@ Ext.define('PVE.sdn.SubnetInputPanel', { extend: 'Proxmox.panel.InputPanel', mixins: ['Proxmox.Mixin.CBind'], + updateSnatState: function (cidr) { + let me = this; + let snatField = me.down('[name=snat]'); + if (!snatField) { + return; + } + + let addr = cidr ? cidr.split('/')[0] : ''; + let isV6 = !!addr && Proxmox.Utils.IP6_match.test(addr); + snatField.setDisabled(isV6); + if (isV6) { + snatField.setValue(false); + } + }, + onGetValues: function (values) { let me = this; @@ -24,6 +39,14 @@ Ext.define('PVE.sdn.SubnetInputPanel', { flex: 1, allowBlank: false, fieldLabel: gettext('Subnet'), + listeners: { + change: function (field, value) { + let panel = field.up('inputpanel'); + if (panel) { + panel.updateSnatState(value); + } + }, + }, }, { xtype: 'proxmoxtextfield', @@ -59,6 +82,100 @@ Ext.define('PVE.sdn.SubnetInputPanel', { ], }); +Ext.define('PVE.sdn.SubnetIPv6Panel', { + extend: 'Proxmox.panel.InputPanel', + mixins: ['Proxmox.Mixin.CBind'], + + items: [ + { + xtype: 'fieldset', + title: 'Router Advertisement', + items: [ + { + xtype: 'proxmoxcheckbox', + name: 'nd-ra-enable', + uncheckedValue: null, + checked: false, + fieldLabel: 'Enable RA', + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxtextfield', + name: 'nd-ra-rdnss', + fieldLabel: 'RDNSS', + vtype: 'IP64Address', + allowBlank: true, + skipEmptyText: true, + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxcheckbox', + name: 'nd-ra-flag-managed', + uncheckedValue: null, + checked: false, + fieldLabel: 'DHCP Managed (M)', + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxcheckbox', + name: 'nd-ra-flag-other', + uncheckedValue: null, + checked: false, + fieldLabel: 'DHCP Other (O)', + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxcheckbox', + name: 'nd-ra-flag-auto', + uncheckedValue: null, + checked: false, + fieldLabel: 'SLAAC (A)', + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + ], + }, + { + xtype: 'fieldset', + itemId: 'slaacFieldset', + title: 'SLAAC', + items: [ + { + xtype: 'proxmoxintegerfield', + name: 'nd-prefix-valid-lifetime', + fieldLabel: gettext('Valid Prefix Lifetime'), + minValue: 0, + allowBlank: true, + emptyText: '2592000 (seconds)', + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + { + xtype: 'proxmoxintegerfield', + name: 'nd-prefix-preferred-lifetime', + fieldLabel: gettext('Preferred Prefix Lifetime'), + minValue: 0, + allowBlank: true, + emptyText: '604800 (seconds)', + cbind: { + deleteEmpty: '{!isCreate}', + }, + }, + ], + }, + ], +}); + Ext.define('PVE.sdn.SubnetDhcpRangePanel', { extend: 'Ext.form.FieldContainer', mixins: ['Ext.form.field.Field'], @@ -238,6 +355,7 @@ Ext.define('PVE.sdn.SubnetDhcpRangePanel', { Ext.define('PVE.sdn.SubnetEdit', { extend: 'Proxmox.window.Edit', + onlineHelp: 'pvesdn_config_subnet_ipv6', subject: gettext('Subnet'), subnet: undefined, @@ -245,6 +363,7 @@ Ext.define('PVE.sdn.SubnetEdit', { width: 350, base_url: undefined, + zoneType: undefined, bodyPadding: 0, @@ -272,12 +391,22 @@ Ext.define('PVE.sdn.SubnetEdit', { name: 'dhcp-range', }); + let tabItems = [ipanel, dhcpPanel]; + if (me.zoneType === 'evpn') { + let ipv6Panel = Ext.create('PVE.sdn.SubnetIPv6Panel', { + isCreate: me.isCreate, + itemId: 'ipv6Panel', + title: gettext('IPv6 Options'), + }); + tabItems.push(ipv6Panel); + } + Ext.apply(me, { items: [ { xtype: 'tabpanel', bodyPadding: 10, - items: [ipanel, dhcpPanel], + items: tabItems, }, ], }); @@ -288,6 +417,7 @@ Ext.define('PVE.sdn.SubnetEdit', { me.load({ success: function (response, options) { me.setValues(response.result.data); + ipanel.updateSnatState(response.result.data.cidr); }, }); } diff --git a/www/manager6/sdn/SubnetView.js b/www/manager6/sdn/SubnetView.js index c61458e0..1eee33d4 100644 --- a/www/manager6/sdn/SubnetView.js +++ b/www/manager6/sdn/SubnetView.js @@ -8,13 +8,15 @@ Ext.define( stateId: 'grid-sdn-subnet', base_url: undefined, + zone_type: undefined, remove_btn: undefined, - setBaseUrl: function (url) { + setBaseUrl: function (url, zoneType) { let me = this; me.base_url = url; + me.zone_type = zoneType; if (url === undefined) { me.store.removeAll(); @@ -50,6 +52,7 @@ Ext.define( autoShow: true, subnet: rec.data.subnet, base_url: me.base_url, + zoneType: me.zone_type, }); win.on('destroy', reload); }; @@ -62,6 +65,7 @@ Ext.define( autoShow: true, base_url: me.base_url, type: 'subnet', + zoneType: me.zone_type, }); win.on('destroy', reload); }, diff --git a/www/manager6/sdn/VnetView.js b/www/manager6/sdn/VnetView.js index 1c576db6..4ba97a2f 100644 --- a/www/manager6/sdn/VnetView.js +++ b/www/manager6/sdn/VnetView.js @@ -141,10 +141,11 @@ Ext.define('PVE.sdn.VnetView', { show: reload, select: function (_sm, rec) { let url = `/cluster/sdn/vnets/${rec.data.vnet}/subnets`; - me.subnetview_panel.setBaseUrl(url); + let zoneType = rec.data['zone-type']; + me.subnetview_panel.setBaseUrl(url, zoneType); }, deselect: function () { - me.subnetview_panel.setBaseUrl(undefined); + me.subnetview_panel.setBaseUrl(undefined, undefined); }, }, }); -- 2.47.3