[PATCH pve-manager 1/1] ui: sdn: add ipv6 options for subnets in evpns zones

Hannes Laimer
2026-02-18T10:23:54Z

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