diff --git a/src/node/NetworkView.js b/src/node/NetworkView.js new file mode 100644 index 0000000..0ff9649 --- /dev/null +++ b/src/node/NetworkView.js @@ -0,0 +1,449 @@ +Ext.define('proxmox-networks', { + extend: 'Ext.data.Model', + fields: [ + 'active', + 'address', + 'address6', + 'altnames', + 'autostart', + 'bridge_ports', + 'cidr', + 'cidr6', + 'comments', + 'gateway', + 'gateway6', + 'iface', + 'netmask', + 'netmask6', + 'slaves', + 'type', + 'vlan-id', + 'vlan-raw-device', + ], + idProperty: 'iface', +}); + +Ext.define('Proxmox.node.NetworkView', { + extend: 'Ext.panel.Panel', + + alias: ['widget.proxmoxNodeNetworkView'], + + // defines what types of network devices we want to create + // order is always the same + types: ['bridge', 'bond', 'vlan', 'ovs'], + + showApplyBtn: false, + + // if true, the altnames column will be shown by default + showAltNames: false, + + // for options passed down to the network edit window + editOptions: {}, + + initComponent: function () { + let me = this; + + if (!me.nodename) { + throw 'no node name specified'; + } + + let baseUrl = `/nodes/${me.nodename}/network`; + + let store = Ext.create('Ext.data.Store', { + model: 'proxmox-networks', + proxy: { + type: 'proxmox', + url: '/api2/json' + baseUrl, + }, + sorters: [ + { + property: 'iface', + direction: 'ASC', + }, + ], + }); + + let reload = function () { + let changeitem = me.down('#changes'); + let apply_btn = me.down('#apply'); + let revert_btn = me.down('#revert'); + Proxmox.Utils.API2Request({ + url: baseUrl, + failure: function (response, opts) { + store.loadData({}); + Proxmox.Utils.setErrorMask(me, response.htmlStatus); + changeitem.update(''); + changeitem.setHidden(true); + }, + success: function (response, opts) { + let result = Ext.decode(response.responseText); + store.loadData(result.data); + let changes = result.changes; + if (changes === undefined || changes === '') { + changes = gettext('No changes'); + changeitem.setHidden(true); + apply_btn.setDisabled(true); + revert_btn.setDisabled(true); + } else { + changeitem.update('
' + Ext.htmlEncode(changes) + ''); + changeitem.setHidden(false); + apply_btn.setDisabled(false); + revert_btn.setDisabled(false); + } + }, + }); + }; + + let run_editor = function () { + let grid = me.down('gridpanel'); + let sm = grid.getSelectionModel(); + let rec = sm.getSelection()[0]; + if (!rec) { + return; + } + + Ext.create('Proxmox.node.NetworkEdit', { + autoShow: true, + nodename: me.nodename, + iface: rec.data.iface, + iftype: rec.data.type, + showAltNames: me.showAltNames, + ...me.editOptions, + listeners: { + destroy: () => reload(), + }, + }); + }; + + let edit_btn = new Ext.Button({ + text: gettext('Edit'), + disabled: true, + handler: run_editor, + }); + + let sm = Ext.create('Ext.selection.RowModel', {}); + + let del_btn = new Proxmox.button.StdRemoveButton({ + selModel: sm, + getUrl: ({ data }) => `${baseUrl}/${data.iface}`, + callback: () => reload(), + }); + + let apply_btn = Ext.create('Proxmox.button.Button', { + text: gettext('Apply Configuration'), + itemId: 'apply', + disabled: true, + confirmMsg: 'Do you want to apply pending network changes?', + hidden: !me.showApplyBtn, + handler: function () { + Proxmox.Utils.API2Request({ + url: baseUrl, + method: 'PUT', + waitMsgTarget: me, + success: function ({ result }, opts) { + Ext.create('Proxmox.window.TaskProgress', { + autoShow: true, + taskDone: reload, + upid: result.data, + }); + }, + failure: (response) => Ext.Msg.alert(gettext('Error'), response.htmlStatus), + }); + }, + }); + + let set_button_status = function () { + let rec = sm.getSelection()[0]; + + edit_btn.setDisabled(!rec); + del_btn.setDisabled(!rec); + }; + + let findNextFreeInterfaceId = function (prefix) { + for (let next = 0; next <= 9999; next++) { + let id = `${prefix}${next.toString()}`; + if (!store.getById(id)) { + return id; + } + } + Ext.Msg.alert('Error', `No free ID for ${prefix} found!`); + return ''; + }; + + let menu_items = []; + let addEditWindowToMenu = (iType, iDefault) => { + menu_items.push({ + text: Proxmox.Utils.render_network_iface_type(iType), + handler: () => + Ext.create('Proxmox.node.NetworkEdit', { + autoShow: true, + nodename: me.nodename, + iftype: iType, + iface_default: findNextFreeInterfaceId(iDefault ?? iType), + ...me.editOptions, + onlineHelp: 'sysadmin_network_configuration', + listeners: { + destroy: () => reload(), + }, + }), + }); + }; + + if (me.types.indexOf('bridge') !== -1) { + addEditWindowToMenu('bridge', 'vmbr'); + } + + if (me.types.indexOf('bond') !== -1) { + addEditWindowToMenu('bond'); + } + + if (me.types.indexOf('vlan') !== -1) { + addEditWindowToMenu('vlan'); + } + + if (me.types.indexOf('ovs') !== -1) { + if (menu_items.length > 0) { + menu_items.push({ xtype: 'menuseparator' }); + } + + addEditWindowToMenu('OVSBridge', 'vmbr'); + addEditWindowToMenu('OVSBond', 'bond'); + + menu_items.push({ + text: Proxmox.Utils.render_network_iface_type('OVSIntPort'), + handler: () => + Ext.create('Proxmox.node.NetworkEdit', { + autoShow: true, + nodename: me.nodename, + iftype: 'OVSIntPort', + listeners: { + destroy: () => reload(), + }, + }), + }); + } + + let renderer_generator = function (fieldname) { + return function (val, metaData, rec) { + let tmp = []; + if (rec.data[fieldname]) { + tmp.push(rec.data[fieldname]); + } + if (rec.data[fieldname + '6']) { + tmp.push(rec.data[fieldname + '6']); + } + return tmp.join('