]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/grid/FirewallRules.js
ui: firwall: change icmp type selector to a combogrid
[pve-manager.git] / www / manager6 / grid / FirewallRules.js
index 5a2241a045178b816bbc66c966d33831e2487ced..dbc595c6572f2717aebc6ff151501106073cd440 100644 (file)
@@ -34,7 +34,7 @@ Ext.define('PVE.form.FWMacroSelector', {
            },
            sorters: {
                property: 'macro',
-               order: 'DESC',
+               direction: 'ASC',
            },
        });
 
@@ -46,6 +46,107 @@ Ext.define('PVE.form.FWMacroSelector', {
     },
 });
 
+Ext.define('PVE.form.ICMPTypeSelector', {
+    extend: 'Proxmox.form.ComboGrid',
+    alias: 'widget.pveICMPTypeSelector',
+    allowBlank: true,
+    autoSelect: false,
+    valueField: 'name',
+    displayField: 'name',
+    listConfig: {
+       columns: [
+           {
+               header: gettext('Type'),
+               dataIndex: 'type',
+               hideable: false,
+               sortable: false,
+               width: 50,
+           },
+           {
+               header: gettext('Name'),
+               dataIndex: 'name',
+               hideable: false,
+               sortable: false,
+               flex: 1,
+           },
+       ],
+    },
+    setName: function(value) {
+       this.name = value;
+    }
+});
+
+let ICMP_TYPE_NAMES_STORE = Ext.create('Ext.data.Store', {
+    field: ['type', 'name'],
+    data: [
+       { type: 'any', name: 'any' },
+       { type: '0', name: 'echo-reply' },
+       { type: '3', name: 'destination-unreachable' },
+       { type: '3/0', name: 'network-unreachable' },
+       { type: '3/1', name: 'host-unreachable' },
+       { type: '3/2', name: 'protocol-unreachable' },
+       { type: '3/3', name: 'port-unreachable' },
+       { type: '3/4', name: 'fragmentation-needed' },
+       { type: '3/5', name: 'source-route-failed' },
+       { type: '3/6', name: 'network-unknown' },
+       { type: '3/7', name: 'host-unknown' },
+       { type: '3/9', name: 'network-prohibited' },
+       { type: '3/10', name: 'host-prohibited' },
+       { type: '3/11', name: 'TOS-network-unreachable' },
+       { type: '3/12', name: 'TOS-host-unreachable' },
+       { type: '3/13', name: 'communication-prohibited' },
+       { type: '3/14', name: 'host-precedence-violation' },
+       { type: '3/15', name: 'precedence-cutoff' },
+       { type: '4', name: 'source-quench' },
+       { type: '5', name: 'redirect' },
+       { type: '5/0', name: 'network-redirect' },
+       { type: '5/1', name: 'host-redirect' },
+       { type: '5/2', name: 'TOS-network-redirect' },
+       { type: '5/3', name: 'TOS-host-redirect' },
+       { type: '8', name: 'echo-request' },
+       { type: '9', name: 'router-advertisement' },
+       { type: '10', name: 'router-solicitation' },
+       { type: '11', name: 'time-exceeded' },
+       { type: '11/0', name: 'ttl-zero-during-transit' },
+       { type: '11/1', name: 'ttl-zero-during-reassembly' },
+       { type: '12', name: 'parameter-problem' },
+       { type: '12/0', name: 'ip-header-bad' },
+       { type: '12/1', name: 'required-option-missing' },
+       { type: '13', name: 'timestamp-request' },
+       { type: '14', name: 'timestamp-reply' },
+       { type: '17', name: 'address-mask-request' },
+       { type: '18', name: 'address-mask-reply' },
+    ],
+});
+let ICMPV6_TYPE_NAMES_STORE = Ext.create('Ext.data.Store', {
+    field: ['type', 'name'],
+    data: [
+       { type: '1', name: 'destination-unreachable' },
+       { type: '1/0', name: 'no-route' },
+       { type: '1/1', name: 'communication-prohibited' },
+       { type: '1/2', name: 'beyond-scope' },
+       { type: '1/3', name: 'address-unreachable' },
+       { type: '1/4', name: 'port-unreachable' },
+       { type: '1/5', name: 'failed-policy' },
+       { type: '1/6', name: 'reject-route', },
+       { type: '2', name: 'packet-too-big' },
+       { type: '3', name: 'time-exceeded' },
+       { type: '3/0', name: 'ttl-zero-during-transit' },
+       { type: '3/1', name: 'ttl-zero-during-reassembly' },
+       { type: '4', name: 'parameter-problem' },
+       { type: '4/0', name: 'bad-header' },
+       { type: '4/1', name: 'unknown-header-type' },
+       { type: '4/2', name: 'unknown-option' },
+       { type: '128', name: 'echo-request' },
+       { type: '129', name: 'echo-reply' },
+       { type: '133', name: 'router-solicitation' },
+       { type: '134', name: 'router-advertisement' },
+       { type: '135', name: 'neighbour-solicitation' },
+       { type: '136', name: 'neighbour-advertisement' },
+       { type: '137', name: 'redirect' },
+    ],
+});
+
 Ext.define('PVE.FirewallRulePanel', {
     extend: 'Proxmox.panel.InputPanel',
 
@@ -59,7 +160,7 @@ Ext.define('PVE.FirewallRulePanel', {
        // hack: editable ComboGrid returns nothing when empty, so we need to set ''
        // Also, disabled text fields return nothing, so we need to set ''
 
-       Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) {
+       Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'icmp-type', 'log'], function(key) {
            if (values[key] === undefined) {
                values[key] = '';
            }
@@ -135,7 +236,8 @@ Ext.define('PVE.FirewallRulePanel', {
                base_url: me.list_refs_url,
                value: '',
                fieldLabel: gettext('Source'),
-
+               maxLength: 512,
+               maxLengthText: gettext('Too long, consider using IP sets.'),
            },
            {
                xtype: 'pveIPRefSelector',
@@ -145,6 +247,8 @@ Ext.define('PVE.FirewallRulePanel', {
                base_url: me.list_refs_url,
                value: '',
                fieldLabel: gettext('Destination'),
+               maxLength: 512,
+               maxLengthText: gettext('Too long, consider using IP sets.'),
            },
        );
 
@@ -187,6 +291,32 @@ Ext.define('PVE.FirewallRulePanel', {
                editable: true,
                value: '',
                fieldLabel: gettext('Protocol'),
+               listeners: {
+                   change: function(f, value) {
+                       if (value === 'icmp' || value === 'icmpv6' || value === 'ipv6-icmp') {
+                           me.down('field[name=dport]').setHidden(true);
+                           me.down('field[name=dport]').setDisabled(true);
+                           if (value === 'icmp') {
+                               me.down('#icmpv4-type').setHidden(false);
+                               me.down('#icmpv4-type').setDisabled(false);
+                               me.down('#icmpv6-type').setHidden(true);
+                               me.down('#icmpv6-type').setDisabled(true);
+                           } else {
+                               me.down('#icmpv6-type').setHidden(false);
+                               me.down('#icmpv6-type').setDisabled(false);
+                               me.down('#icmpv4-type').setHidden(true);
+                               me.down('#icmpv4-type').setDisabled(true);
+                           }
+                       } else {
+                           me.down('#icmpv4-type').setHidden(true);
+                           me.down('#icmpv4-type').setDisabled(true);
+                           me.down('#icmpv6-type').setHidden(true);
+                           me.down('#icmpv6-type').setDisabled(true);
+                           me.down('field[name=dport]').setHidden(false);
+                           me.down('field[name=dport]').setDisabled(false);
+                       }
+                   }
+               },
            },
            {
                xtype: 'displayfield',
@@ -206,6 +336,30 @@ Ext.define('PVE.FirewallRulePanel', {
                value: '',
                fieldLabel: gettext('Dest. port'),
            },
+           {
+               xtype: 'pveICMPTypeSelector',
+               name: 'icmp-type',
+               id: 'icmpv4-type',
+               autoSelect: false,
+               editable: true,
+               hidden: true,
+               disabled: true,
+               value: '',
+               fieldLabel: gettext('ICMP type'),
+               store: ICMP_TYPE_NAMES_STORE,
+           },
+           {
+               xtype: 'pveICMPTypeSelector',
+               name: 'icmp-type',
+               id: 'icmpv6-type',
+               autoSelect: false,
+               editable: true,
+               hidden: true,
+               disabled: true,
+               value: '',
+               fieldLabel: gettext('ICMP type'),
+               store: ICMPV6_TYPE_NAMES_STORE,
+           },
        ];
 
        me.advancedColumn1 = [
@@ -275,6 +429,10 @@ Ext.define('PVE.FirewallRuleEdit', {
                success: function(response, options) {
                    var values = response.result.data;
                    ipanel.setValues(values);
+                   // set icmp-type again after protocol has been set
+                   if (values["icmp-type"] !== undefined) {
+                       ipanel.setValues({"icmp-type": values["icmp-type"]});
+                   }
                    if (values.errors) {
                        var field = me.query('[isFormField][name=modified_marker]')[0];
                        field.setValue(1);
@@ -541,25 +699,21 @@ Ext.define('PVE.FirewallRules', {
        });
 
        var run_copy_editor = function() {
-           var rec = sm.getSelection()[0];
-
+           let rec = sm.getSelection()[0];
            if (!rec) {
                return;
            }
-           var type = rec.data.type;
-
-
+           let type = rec.data.type;
            if (!(type === 'in' || type === 'out')) {
                return;
            }
 
-           var win = Ext.create('PVE.FirewallRuleEdit', {
+           let win = Ext.create('PVE.FirewallRuleEdit', {
                allow_iface: me.allow_iface,
                base_url: me.base_url,
                list_refs_url: me.list_refs_url,
                rec: rec,
            });
-
            win.show();
            win.on('destroy', reload);
        };
@@ -567,9 +721,7 @@ Ext.define('PVE.FirewallRules', {
        me.copyBtn = Ext.create('Proxmox.button.Button', {
            text: gettext('Copy'),
            selModel: sm,
-           enableFn: function(rec) {
-               return rec.data.type === 'in' || rec.data.type === 'out';
-           },
+           enableFn: ({ data }) => data.type === 'in' || data.type === 'out',
            disabled: true,
            handler: run_copy_editor,
        });
@@ -604,46 +756,46 @@ Ext.define('PVE.FirewallRules', {
            },
        });
 
-       var tbar = me.tbar_prefix ? [me.tbar_prefix] : [];
+       let tbar = me.tbar_prefix ? [me.tbar_prefix] : [];
        tbar.push(me.addBtn, me.copyBtn);
        if (me.groupBtn) {
            tbar.push(me.groupBtn);
        }
        tbar.push(me.removeBtn, me.editBtn);
 
-       var render_errors = function(name, value, metaData, record) {
-           var errors = record.data.errors;
+       let render_errors = function(name, value, metaData, record) {
+           let errors = record.data.errors;
            if (errors && errors[name]) {
                metaData.tdCls = 'proxmox-invalid-row';
-               var html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
-               metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
-                   html.replace(/\"/g, '&quot;') + '"';
+               let html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
+               metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + html + '"';
            }
            return value;
        };
 
-       var columns = [
+       let columns = [
            {
                // similar to xtype: 'rownumberer',
                dataIndex: 'pos',
                resizable: false,
-               minWidth: 42,
+               minWidth: 65,
+               maxWidth: 83,
                flex: 1,
                sortable: false,
-               align: 'right',
                hideable: false,
                menuDisabled: true,
-               renderer: function(value, metaData, record, rowIdx, colIdx, store) {
+               renderer: function(value, metaData, record, rowIdx, colIdx) {
                    metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
+                   let dragHandle = "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
                    if (value >= 0) {
-                       return value;
+                       return dragHandle + value;
                    }
-                   return '';
+                   return dragHandle;
                },
            },
            {
                xtype: 'checkcolumn',
-               header: gettext('Enable'),
+               header: gettext('On'),
                dataIndex: 'enable',
                listeners: {
                    checkchange: function(column, recordIndex, checked) {
@@ -659,8 +811,7 @@ Ext.define('PVE.FirewallRules', {
                        me.updateRule(data);
                    },
                },
-               minWidth: 50,
-               flex: 2,
+               width: 40,
            },
            {
                header: gettext('Type'),
@@ -668,7 +819,8 @@ Ext.define('PVE.FirewallRules', {
                renderer: function(value, metaData, record) {
                    return render_errors('type', value, metaData, record);
                },
-               minWidth: 50,
+               minWidth: 60,
+               maxWidth: 80,
                flex: 2,
            },
            {
@@ -678,7 +830,8 @@ Ext.define('PVE.FirewallRules', {
                    return render_errors('action', value, metaData, record);
                },
                minWidth: 80,
-               flex: 3,
+               maxWidth: 200,
+               flex: 2,
            },
            {
                header: gettext('Macro'),
@@ -687,7 +840,7 @@ Ext.define('PVE.FirewallRules', {
                    return render_errors('macro', value, metaData, record);
                },
                minWidth: 80,
-               flex: 3,
+               flex: 2,
            },
        ];
 
@@ -699,11 +852,19 @@ Ext.define('PVE.FirewallRules', {
                    return render_errors('iface', value, metaData, record);
                },
                minWidth: 80,
-               flex: 3,
+               flex: 2,
            });
        }
 
        columns.push(
+           {
+               header: gettext('Protocol'),
+               dataIndex: 'proto',
+               renderer: function(value, metaData, record) {
+                   return render_errors('proto', value, metaData, record);
+               },
+               width: 75,
+           },
            {
                header: gettext('Source'),
                dataIndex: 'source',
@@ -711,43 +872,32 @@ Ext.define('PVE.FirewallRules', {
                    return render_errors('source', value, metaData, record);
                },
                minWidth: 100,
-               flex: 4,
+               flex: 2,
            },
            {
-               header: gettext('Destination'),
-               dataIndex: 'dest',
+               header: gettext('S.Port'),
+               dataIndex: 'sport',
                renderer: function(value, metaData, record) {
-                   return render_errors('dest', value, metaData, record);
+                   return render_errors('sport', value, metaData, record);
                },
-               minWidth: 100,
-               flex: 4,
+               width: 75,
            },
            {
-               header: gettext('Protocol'),
-               dataIndex: 'proto',
+               header: gettext('Destination'),
+               dataIndex: 'dest',
                renderer: function(value, metaData, record) {
-                   return render_errors('proto', value, metaData, record);
+                   return render_errors('dest', value, metaData, record);
                },
                minWidth: 100,
-               flex: 3,
+               flex: 2,
            },
            {
-               header: gettext('Dest. port'),
+               header: gettext('D.Port'),
                dataIndex: 'dport',
                renderer: function(value, metaData, record) {
                    return render_errors('dport', value, metaData, record);
                },
-               minWidth: 100,
-               flex: 4,
-           },
-           {
-               header: gettext('Source port'),
-               dataIndex: 'sport',
-               renderer: function(value, metaData, record) {
-                   return render_errors('sport', value, metaData, record);
-               },
-               minWidth: 100,
-               flex: 3,
+               width: 75,
            },
            {
                header: gettext('Log level'),
@@ -755,15 +905,19 @@ Ext.define('PVE.FirewallRules', {
                renderer: function(value, metaData, record) {
                    return render_errors('log', value, metaData, record);
                },
-               minWidth: 100,
-               flex: 3,
+               width: 100,
            },
            {
                header: gettext('Comment'),
                dataIndex: 'comment',
-               flex: 6,
+               flex: 10,
+               minWidth: 75,
                renderer: function(value, metaData, record) {
-                   return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record);
+                   let comment = render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record) || '';
+                   if (comment.length * 12 > metaData.column.cellWidth) {
+                       comment = `<span data-qtip="${comment}">${comment}</span>`;
+                   }
+                   return comment;
                },
            },
        );
@@ -772,7 +926,7 @@ Ext.define('PVE.FirewallRules', {
            store: store,
            selModel: sm,
            tbar: tbar,
-            viewConfig: {
+           viewConfig: {
                plugins: [
                    {
                        ptype: 'gridviewdragdrop',
@@ -781,15 +935,15 @@ Ext.define('PVE.FirewallRules', {
                    },
                ],
                listeners: {
-                    beforedrop: function(node, data, dropRec, dropPosition) {
+                   beforedrop: function(node, data, dropRec, dropPosition) {
                        if (!dropRec) {
                            return false; // empty view
                        }
-                       var moveto = dropRec.get('pos');
+                       let moveto = dropRec.get('pos');
                        if (dropPosition === 'after') {
                            moveto++;
                        }
-                       var pos = data.records[0].get('pos');
+                       let pos = data.records[0].get('pos');
                        me.moveRule(pos, moveto);
                        return 0;
                     },
@@ -809,9 +963,22 @@ Ext.define('PVE.FirewallRules', {
 }, function() {
     Ext.define('pve-fw-rule', {
        extend: 'Ext.data.Model',
-       fields: [{ name: 'enable', type: 'boolean' },
-                 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface',
-                 'dport', 'sport', 'comment', 'pos', 'digest', 'errors'],
+       fields: [
+           { name: 'enable', type: 'boolean' },
+           'type',
+           'action',
+           'macro',
+           'source',
+           'dest',
+           'proto',
+           'iface',
+           'dport',
+           'sport',
+           'comment',
+           'pos',
+           'digest',
+           'errors',
+       ],
        idProperty: 'pos',
     });
 });