From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 765C51FF14F for ; Fri, 08 May 2026 18:33:05 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 3FEA01E941; Fri, 8 May 2026 18:32:16 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Subject: [PATCH pve-manager v6 19/24] ui: sdn: prefix list: adapt to changed api structure Date: Fri, 8 May 2026 18:31:28 +0200 Message-ID: <20260508163134.481912-20-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260508163134.481912-1-s.hanreich@proxmox.com> References: <20260508163134.481912-1-s.hanreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1778257795508 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.368 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 KAM_MAILER 2 Automated Mailer Tag Left in Email 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: K7CLYTLXKEM6PK7DQAM3JAZ42NCV2OPD X-Message-ID-Hash: K7CLYTLXKEM6PK7DQAM3JAZ42NCV2OPD X-MailFrom: s.hanreich@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: From: Dominik Csapak instead of having to parse out the prefix list entries from the overall prefix list GET api call, the entries are now queried below: /sdn/cluster/prefix-list/{id}/entries so the entries grid can be simplified to use it's own store + url. This makes it possible to move the stores out of the viewmodel and into the respective grid views. Also use proxmoxStdRemoveButtons here to simplify the removal code. Signed-off-by: Dominik Csapak [SH: fixed wrong url in edit window, made sequence number optional and added empty text] Signed-off-by: Stefan Hanreich --- www/manager6/sdn/PrefixListPanel.js | 279 ++++++++++------------------ 1 file changed, 96 insertions(+), 183 deletions(-) diff --git a/www/manager6/sdn/PrefixListPanel.js b/www/manager6/sdn/PrefixListPanel.js index cdb860f4f..0e5cc11b8 100644 --- a/www/manager6/sdn/PrefixListPanel.js +++ b/www/manager6/sdn/PrefixListPanel.js @@ -1,16 +1,11 @@ Ext.define('PVE.sdn.PrefixList', { extend: 'Ext.data.Model', - fields: ['id', 'entries', 'pending'], - - getId: function () { - let me = this; - return me.data.pending?.[me.idProperty] ?? me.data[me.idProperty]; - }, + fields: ['id', 'state'], }); Ext.define('PVE.sdn.PrefixListEntry', { extend: 'Ext.data.Model', - fields: ['id', 'action', 'prefix', 'le', 'ge', 'pending'], + fields: ['id', 'action', 'seq', 'prefix', 'le', 'ge', 'pending'], }); Ext.define('PVE.sdn.EditPrefixListWindow', { @@ -45,10 +40,11 @@ Ext.define('PVE.sdn.EditPrefixListWindow', { Ext.define('PVE.sdn.EditPrefixListEntryWindow', { extend: 'Proxmox.window.Edit', + mixins: ['Proxmox.Mixin.CBind'], subject: gettext('Prefix List Entry'), - url: '/cluster/sdn/prefix-lists', + baseUrl: '/cluster/sdn/prefix-lists', config: { entry: null, @@ -57,6 +53,15 @@ Ext.define('PVE.sdn.EditPrefixListEntryWindow', { isCreate: false, items: [ + { + xtype: 'proxmoxintegerfield', + name: 'seq', + fieldLabel: gettext('Sequence Nr.'), + emptyText: gettext('autogenerated'), + cbind: { + deleteEmpty: '{!isCreate}', + } + }, { xtype: 'proxmoxKVComboBox', fieldLabel: gettext('Action'), @@ -88,6 +93,17 @@ Ext.define('PVE.sdn.EditPrefixListEntryWindow', { initComponent: function () { let me = this; me.method = me.isCreate ? 'POST' : 'PUT'; + + if (!me.prefixList) { + throw new 'no prefixList given'(); + } + + if (me.entry) { + me.url = `${me.baseUrl}/${me.prefixList}/entries/${me.entry.seq}`; + } else { + me.url = `${me.baseUrl}/${me.prefixList}/entries`; + } + me.callParent(); me.setValues(me.getEntry()); @@ -108,19 +124,28 @@ Ext.define('PVE.sdn.PrefixListView', { }, { text: gettext('Remove'), - xtype: 'button', - handler: 'removePrefixList', - bind: { - disabled: '{!prefixListGrid.selection}', - }, + xtype: 'proxmoxStdRemoveButton', + baseurl: '/cluster/sdn/prefix-lists/', + dangerous: true, + callback: 'reloadPrefixList', }, + '->', { text: gettext('Reload'), xtype: 'button', - handler: 'reload', + handler: 'reloadPrefixList', }, ], + store: { + autoLoad: true, + model: 'PVE.sdn.PrefixList', + proxy: { + type: 'proxmox', + url: '/api2/json/cluster/sdn/prefix-lists?pending=1', + }, + }, + columns: [ { text: gettext('Name'), @@ -139,6 +164,12 @@ Ext.define('PVE.sdn.PrefixListView', { }, }, ], + + initComponent: function () { + let me = this; + me.callParent(); + Proxmox.Utils.monStoreErrors(me, me.getStore()); + }, }); Ext.define('PVE.sdn.PrefixListEntriesView', { @@ -151,30 +182,22 @@ Ext.define('PVE.sdn.PrefixListEntriesView', { prefixList: null, }, - viewConfig: { - plugins: [ - { - ptype: 'gridviewdragdrop', - }, - ], - }, - listeners: { - drop: 'saveEntries', itemdblclick: 'editPrefixListEntry', }, + store: { + model: 'PVE.sdn.PrefixListEntry', + proxy: { + type: 'proxmox', + }, + }, + columns: [ { - width: 40, - resizable: false, - sortable: false, - hideable: false, - menuDisabled: true, - renderer: function (value, metaData, record, rowIdx, colIdx) { - metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special'; - return ""; - }, + text: gettext('Sequence Nr.'), + dataIndex: 'seq', + flex: 1, }, { text: gettext('Action'), @@ -209,21 +232,32 @@ Ext.define('PVE.sdn.PrefixListEntriesView', { }, { text: gettext('Edit'), - xtype: 'button', + xtype: 'proxmoxButton', + disabled: true, handler: 'editPrefixListEntry', - bind: { - disabled: '{!prefixListEntriesGrid.selection}', - }, }, { text: gettext('Remove'), - xtype: 'button', - handler: 'removePrefixListEntry', - bind: { - disabled: '{!prefixListEntriesGrid.selection}', + xtype: 'proxmoxStdRemoveButton', + customConfirmationMessage: gettext( + 'Are you sure you want to remove entry with sequence {0}', + ), + getRecordName: (rec) => rec.data.seq, + getUrl: function (rec) { + let grid = this.up('grid'); + let prefixList = grid.prefixList; + let id = prefixList.getId(); + let seq = rec.data.seq; + return `/cluster/sdn/prefix-lists/${id}/entries/${seq}`; }, }, ], + + initComponent: function () { + let me = this; + me.callParent(); + Proxmox.Utils.monStoreErrors(me, me.getStore()); + }, }); Ext.define('PVE.sdn.PrefixListPanel', { @@ -233,30 +267,6 @@ Ext.define('PVE.sdn.PrefixListPanel', { emptyText: gettext('No prefix list configured'), viewModel: { - stores: { - prefixLists: { - autoLoad: true, - model: 'PVE.sdn.PrefixList', - proxy: { - type: 'proxmox', - url: '/api2/json/cluster/sdn/prefix-lists?pending=1', - }, - }, - prefixListEntries: { - model: 'PVE.sdn.PrefixListEntry', - proxy: { - type: 'proxmox', - reader: { - transform: { - fn: function (response) { - let entries = response.data.entries ?? []; - return entries.map(PVE.Parser.parsePropertyString); - }, - }, - }, - }, - }, - }, formulas: { entryGridEmptyText: function (get) { let selection = get('prefixListGrid.selection'); @@ -269,64 +279,24 @@ Ext.define('PVE.sdn.PrefixListPanel', { }, controller: { - reload: function () { + reloadPrefixList: function () { let me = this; - - let prefixList = me.getViewModel().get('prefixListGrid.selection'); - - me.getViewModel() - .getStore('prefixLists') - .load((records, _operation, success) => { - if (!success || !prefixList) { - return; - } - - let newPrefixList = records.find((record) => { - return record.getId() === prefixList.getId(); - }); - - me.lookupReference('prefixListGrid').setSelection(newPrefixList); - }); + me.lookup('prefixListGrid').getStore().load(); + // reset entries grid + let entriesGrid = me.lookup('prefixListEntriesGrid'); + entriesGrid.getStore().setData([]); + Proxmox.Utils.setErrorMask(entriesGrid, false); }, - saveEntries: function () { + reloadPrefixEntries: function () { let me = this; - - let prefixList = me.getViewModel().get('prefixListGrid.selection'); - - let entries = me - .getViewModel() - .getStore('prefixListEntries') - .getData() - .items.map((item) => { - let data = item.data; - delete data.id; - - return PVE.Parser.printPropertyString(data); - }); - - let params = {}; - - if (entries.length > 0) { - params.entries = entries; - } else { - params = { delete: ['entries'] }; - } - - Proxmox.Async.api2({ - url: `/api2/extjs/cluster/sdn/prefix-lists/${prefixList.getId()}`, - params, - method: 'PUT', - }) - .catch(Proxmox.Utils.alertResponseFailure) - .finally(() => { - me.reload(prefixList); - }); + me.lookup('prefixListEntriesGrid').getStore().load(); }, selectPrefixList: function (gridPanel, record, index, options) { let me = this; - let url = `/api2/extjs/cluster/sdn/prefix-lists/${record.getId()}`; - let entryStore = me.getViewModel().getStore('prefixListEntries'); + let prefixEntriesGrid = me.lookup('prefixListEntriesGrid'); + let entryStore = prefixEntriesGrid.getStore(); + let url = `/api2/json/cluster/sdn/prefix-lists/${record.getId()}/entries`; entryStore.getProxy().setUrl(url); entryStore.load(); @@ -338,56 +308,28 @@ Ext.define('PVE.sdn.PrefixListPanel', { autoShow: true, isCreate: true, listeners: { - close: () => me.reload(), + close: () => me.reloadPrefixList(), }, }); }, - removePrefixList: function () { + addPrefixListEntry: function () { let me = this; let prefixList = me.getViewModel().get('prefixListGrid.selection'); - Ext.Msg.show({ - title: gettext('Confirm'), - icon: Ext.Msg.WARNING, - message: Ext.String.format(gettext('Remove prefix list "{0}"?'), prefixList.getId()), - buttons: Ext.Msg.YESNO, - defaultFocus: 'no', - callback: function (btn) { - if (btn !== 'yes') { - return; - } - - Proxmox.Async.api2({ - url: `/api2/extjs/cluster/sdn/prefix-lists/${prefixList.getId()}`, - method: 'DELETE', - }) - .catch(Proxmox.Utils.alertResponseFailure) - .finally(() => { - me.reload(prefixList); - }); - }, - }); - }, - addPrefixListEntry: function () { - let panel = this; - Ext.create('PVE.sdn.EditPrefixListEntryWindow', { autoShow: true, isCreate: true, - submit: function () { - let me = this; - - panel.getViewModel().getStore('prefixListEntries').add(me.getValues()); - panel.saveEntries(); - - me.close(); + prefixList: prefixList.getId(), + listeners: { + close: () => me.reloadPrefixEntries(), }, }); }, editPrefixListEntry: function () { - let panel = this; + let me = this; - let entry = panel.getViewModel().get('prefixListEntriesGrid.selection'); + let entry = me.getViewModel().get('prefixListEntriesGrid.selection'); + let prefixList = me.getViewModel().get('prefixListGrid.selection'); if (!entry) { console.warn('no prefix list entry selected!'); @@ -397,35 +339,10 @@ Ext.define('PVE.sdn.PrefixListPanel', { Ext.create('PVE.sdn.EditPrefixListEntryWindow', { autoShow: true, isCreate: false, + prefixList: prefixList.getId(), entry: entry.data, - submit: function () { - let me = this; - entry.set(me.getValues()); - - panel.saveEntries(); - - me.close(); - }, - }); - }, - removePrefixListEntry: function () { - let me = this; - - let entry = me.getViewModel().get('prefixListEntriesGrid.selection'); - - Ext.Msg.show({ - title: gettext('Confirm'), - icon: Ext.Msg.WARNING, - message: gettext('Remove prefix list entry?'), - buttons: Ext.Msg.YESNO, - defaultFocus: 'no', - callback: function (btn) { - if (btn !== 'yes') { - return; - } - - me.getViewModel().getStore('prefixListEntries').remove(entry); - me.saveEntries(); + listeners: { + close: () => me.reloadPrefixEntries(), }, }); }, @@ -441,9 +358,6 @@ Ext.define('PVE.sdn.PrefixListPanel', { border: false, split: true, reference: 'prefixListGrid', - bind: { - store: '{prefixLists}', - }, listeners: { select: 'selectPrefixList', }, @@ -454,7 +368,6 @@ Ext.define('PVE.sdn.PrefixListPanel', { border: false, bind: { prefixList: '{prefixListGrid.selection}', - store: '{prefixListEntries}', emptyText: '{entryGridEmptyText}', }, reference: 'prefixListEntriesGrid', -- 2.47.3