* [PATCH pve-network 1/2] api: sdn: ipam: add optional vmid to POST endpoint
2026-08-19 12:44 [PATCH manager/network 0/2] sdn: add guest/nic selectors to IPAM create window Hannes Laimer
@ 2026-08-19 12:44 ` Hannes Laimer
2026-08-19 12:44 ` [PATCH pve-manager 2/2] ui: sdn: ipam: allow picking the nic instead of typing its MAC Hannes Laimer
1 sibling, 0 replies; 3+ messages in thread
From: Hannes Laimer @ 2026-08-19 12:44 UTC (permalink / raw)
To: pve-devel
The update PUT endpoint allows an optional vmid, this alligns the POST
one. And allows creating new assignments with a vmid associated.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
src/PVE/API2/Network/SDN/Ips.pm | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/PVE/API2/Network/SDN/Ips.pm b/src/PVE/API2/Network/SDN/Ips.pm
index 5ff05e7..d0902f5 100644
--- a/src/PVE/API2/Network/SDN/Ips.pm
+++ b/src/PVE/API2/Network/SDN/Ips.pm
@@ -64,6 +64,9 @@ __PACKAGE__->register_method({
properties => {
zone => get_standard_option('pve-sdn-zone-id'),
vnet => get_standard_option('pve-sdn-vnet-id'),
+ vmid => get_standard_option('pve-vmid', {
+ optional => 1,
+ }),
mac => get_standard_option('mac-addr'),
ip => {
type => 'string',
@@ -78,9 +81,10 @@ __PACKAGE__->register_method({
my $vnet = extract_param($param, 'vnet');
my $mac = extract_param($param, 'mac');
+ my $vmid = extract_param($param, 'vmid');
my $ip = extract_param($param, 'ip');
- PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, undef);
+ PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, $vmid);
return undef;
},
--
2.47.3
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH pve-manager 2/2] ui: sdn: ipam: allow picking the nic instead of typing its MAC
2026-08-19 12:44 [PATCH manager/network 0/2] sdn: add guest/nic selectors to IPAM create window Hannes Laimer
2026-08-19 12:44 ` [PATCH pve-network 1/2] api: sdn: ipam: add optional vmid to POST endpoint Hannes Laimer
@ 2026-08-19 12:44 ` Hannes Laimer
1 sibling, 0 replies; 3+ messages in thread
From: Hannes Laimer @ 2026-08-19 12:44 UTC (permalink / raw)
To: pve-devel
Instead of hand-copying a MAC from the guest's hardware panel, take a
guest and one of its NICs on the assignments's VNet, and fill the MAC in
from there.
Setting a MAC without a guest stays possible, so this is purely a UX
improvement. We could also restrict this, so our IPAM only manages IPs
of guests in our SDN.
Signed-off-by: Hannes Laimer <h.laimer@proxmox.com>
---
www/manager6/sdn/IpamEdit.js | 112 ++++++++++++++++++++++++++++++++++-
1 file changed, 109 insertions(+), 3 deletions(-)
diff --git a/www/manager6/sdn/IpamEdit.js b/www/manager6/sdn/IpamEdit.js
index cdefe436..aeb21b2b 100644
--- a/www/manager6/sdn/IpamEdit.js
+++ b/www/manager6/sdn/IpamEdit.js
@@ -16,18 +16,48 @@ Ext.define('PVE.sdn.IpamEditInputPanel', {
items: [
{
- xtype: 'pmxDisplayEditField',
name: 'vmid',
- fieldLabel: 'VMID',
+ fieldLabel: gettext('Guest'),
+ allowBlank: true,
+ notFoundIsValid: true,
+ emptyText: Proxmox.Utils.NoneText,
+ cbind: {
+ xtype: (get) => (get('isCreate') ? 'vmComboSelector' : 'displayfield'),
+ },
+ listeners: {
+ change: 'onGuestChange',
+ },
+ },
+ {
+ xtype: 'combobox',
+ reference: 'netid',
+ fieldLabel: gettext('Network Device'),
+ emptyText: gettext('Select a guest first'),
+ submitValue: false,
allowBlank: false,
editable: false,
+ disabled: true,
+ queryMode: 'local',
+ valueField: 'netid',
+ displayField: 'netid',
+ listConfig: {
+ itemTpl: '{netid} <i>{mac:htmlEncode}</i>',
+ },
+ store: {
+ fields: ['netid', 'mac'],
+ data: [],
+ },
cbind: {
- hidden: '{isCreate}',
+ hidden: '{!isCreate}',
+ },
+ listeners: {
+ select: 'onDeviceSelect',
},
},
{
xtype: 'pmxDisplayEditField',
name: 'mac',
+ reference: 'mac',
fieldLabel: 'MAC',
allowBlank: false,
cbind: {
@@ -58,11 +88,87 @@ Ext.define('PVE.sdn.IpamEdit', {
return `${url}/${values.vnet}/ips`;
},
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ onGuestChange: function (f, vmid) {
+ if (!this.getView().isCreate) {
+ return;
+ }
+ this.setGuest(vmid ? f.getSelection() : undefined);
+ },
+
+ onDeviceSelect: function (f, record) {
+ this.lookup('mac').setValue(record?.data.mac ?? '');
+ },
+
+ setGuest: function (record) {
+ let me = this;
+ let view = me.getView();
+
+ let selector = me.lookup('netid');
+ let mac = me.lookup('mac');
+
+ selector.clearValue();
+ selector.getStore().removeAll();
+ selector.setDisabled(!record);
+
+ mac.setValue('');
+ mac.setEditable(!record);
+
+ if (!record) {
+ selector.setEmptyText(gettext('Select a guest first'));
+ return;
+ }
+
+ Proxmox.Utils.API2Request({
+ url: `/nodes/${record.data.node}/${record.data.type}/${record.data.vmid}/config`,
+ method: 'GET',
+ failure: (response) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
+ success: function ({ result }) {
+ if (selector.destroyed) {
+ return;
+ }
+
+ let vnet = view.extraRequestParams?.vnet;
+ let nics = [];
+
+ for (const [key, value] of Object.entries(result.data)) {
+ if (!key.match(/^net\d+$/)) {
+ continue;
+ }
+
+ let net =
+ record.data.type === 'qemu'
+ ? PVE.Parser.parseQemuNetwork(key, value)
+ : PVE.Parser.parseLxcNetwork(value);
+
+ let mac = net?.macaddr ?? net?.hwaddr;
+ if (!mac || net.bridge !== vnet) {
+ continue;
+ }
+
+ nics.push({ netid: key, mac });
+ }
+
+ selector.getStore().setData(nics);
+ selector.setEmptyText(
+ nics.length
+ ? gettext('Select a network device')
+ : Ext.String.format(gettext('No network device on {0}'), vnet),
+ );
+ },
+ });
+ },
+ },
+
initComponent: function () {
var me = this;
me.method = me.isCreate ? 'POST' : 'PUT';
+ me.defaultFocus = me.isCreate ? 'textfield[name=mac]' : 'textfield[name=ip]';
+
let ipanel = Ext.create('PVE.sdn.IpamEditInputPanel', {
isCreate: me.isCreate,
});
--
2.47.3
^ permalink raw reply related [flat|nested] 3+ messages in thread