]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/grid/FirewallRules.js
ext6migrate: adapt to new KVComboBox interface
[pve-manager.git] / www / manager6 / grid / FirewallRules.js
CommitLineData
435cce27
DM
1Ext.define('PVE.form.FWMacroSelector', {
2 extend: 'PVE.form.ComboGrid',
3 alias: 'widget.pveFWMacroSelector',
4
5 initComponent: function() {
6 var me = this;
7
8 var store = Ext.create('Ext.data.Store', {
9 autoLoad: true,
10 fields: [ 'macro', 'descr' ],
11 idProperty: 'macro',
12 proxy: {
13 type: 'pve',
14 url: "/api2/json/cluster/firewall/macros"
15 },
16 sorters: {
17 property: 'macro',
18 order: 'DESC'
19 }
20 });
21
22 Ext.apply(me, {
23 store: store,
24 allowBlank: true,
25 autoSelect: false,
26 valueField: 'macro',
27 displayField: 'macro',
28 listConfig: {
29 columns: [
30 {
31 header: gettext('Macro'),
32 dataIndex: 'macro',
33 hideable: false,
34 width: 100
35 },
36 {
37 header: gettext('Description'),
38 flex: 1,
39 dataIndex: 'descr'
40 }
41 ]
42 }
43 });
44
45 me.callParent();
46 }
47});
48
49Ext.define('PVE.FirewallRulePanel', {
50 extend: 'PVE.panel.InputPanel',
51
52 allow_iface: false,
53
54 list_refs_url: undefined,
55
56 onGetValues: function(values) {
57 var me = this;
58
59 // hack: editable ComboGrid returns nothing when empty, so we need to set ''
60 // Also, disabled text fields return nothing, so we need to set ''
61
62 Ext.Array.each(['source', 'dest', 'proto', 'sport', 'dport'], function(key) {
63 if (values[key] === undefined) {
64 values[key] = '';
65 }
66 });
67
68 delete values.modified_marker;
69
70 return values;
71 },
72
73 initComponent : function() {
74 var me = this;
75
76 if (!me.list_refs_url) {
77 throw "no list_refs_url specified";
78 }
79
80 me.column1 = [
81 {
82 // hack: we use this field to mark the form 'dirty' when the
83 // record has errors- so that the user can safe the unmodified
84 // form again.
85 xtype: 'hiddenfield',
86 name: 'modified_marker',
87 value: '',
88 },
89 {
90 xtype: 'pveKVComboBox',
91 name: 'type',
92 value: 'in',
e7bc7f31 93 comboItems: [['in', 'in'], ['out', 'out']],
435cce27
DM
94 fieldLabel: gettext('Direction'),
95 allowBlank: false
96 },
97 {
98 xtype: 'pveKVComboBox',
99 name: 'action',
100 value: 'ACCEPT',
e7bc7f31 101 comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
435cce27
DM
102 fieldLabel: gettext('Action'),
103 allowBlank: false
104 }
105 ];
106
107 if (me.allow_iface) {
108 me.column1.push({
109 xtype: 'pvetextfield',
110 name: 'iface',
111 deleteEmpty: !me.create,
112 value: '',
113 fieldLabel: gettext('Interface')
114 });
115 } else {
116 me.column1.push({
117 xtype: 'displayfield',
118 fieldLabel: '',
119 height: 22, // hack: set same height as text fields
120 value: ''
121 });
122 }
123
124 me.column1.push([
125 {
126 xtype: 'displayfield',
127 fieldLabel: '',
128 height: 7,
129 value: ''
130 },
131 {
132 xtype: 'pveIPRefSelector',
133 name: 'source',
134 autoSelect: false,
135 editable: true,
136 base_url: me.list_refs_url,
137 value: '',
138 fieldLabel: gettext('Source')
139
140 },
141 {
142 xtype: 'pveIPRefSelector',
143 name: 'dest',
144 autoSelect: false,
145 editable: true,
146 base_url: me.list_refs_url,
147 value: '',
148 fieldLabel: gettext('Destination')
149 }
150 ]);
151
152
153 me.column2 = [
154 {
155 xtype: 'pvecheckbox',
156 name: 'enable',
157 checked: false,
158 height: 22, // hack: set same height as text fields
159 uncheckedValue: 0,
160 fieldLabel: gettext('Enable')
161 },
162 {
163 xtype: 'pveFWMacroSelector',
164 name: 'macro',
165 value: '',
166 fieldLabel: gettext('Macro'),
167 allowBlank: true,
168 listeners: {
169 change: function(f, value) {
170 if (value === '') {
171 me.down('field[name=proto]').setDisabled(false);
172 me.down('field[name=sport]').setDisabled(false);
173 me.down('field[name=dport]').setDisabled(false);
174 } else {
175 me.down('field[name=proto]').setDisabled(true);
176 me.down('field[name=proto]').setValue('');
177 me.down('field[name=sport]').setDisabled(true);
178 me.down('field[name=sport]').setValue('');
179 me.down('field[name=dport]').setDisabled(true);
180 me.down('field[name=dport]').setValue('');
181 }
182 }
183 }
184 },
185 {
186 xtype: 'pveIPProtocolSelector',
187 name: 'proto',
188 autoSelect: false,
189 editable: true,
190 value: '',
191 fieldLabel: gettext('Protocol')
192 },
193 {
194 xtype: 'displayfield',
195 fieldLabel: '',
196 height: 7,
197 value: ''
198 },
199 {
200 xtype: 'textfield',
201 name: 'sport',
202 value: '',
203 fieldLabel: gettext('Source port')
204 },
205 {
206 xtype: 'textfield',
207 name: 'dport',
208 height: 22, // hack: set same height as text fields
209 value: '',
210 fieldLabel: gettext('Dest. port')
211 }
212 ];
213
214 me.columnB = [
215 {
216 xtype: 'textfield',
217 name: 'comment',
218 value: '',
219 fieldLabel: gettext('Comment')
220 }
221 ];
222
223 me.callParent();
224 }
225});
226
227Ext.define('PVE.FirewallRuleEdit', {
228 extend: 'PVE.window.Edit',
229
230 base_url: undefined,
231 list_refs_url: undefined,
232
233 allow_iface: false,
234
235 initComponent : function() {
236 /*jslint confusion: true */
237 var me = this;
238
239 if (!me.base_url) {
240 throw "no base_url specified";
241 }
242 if (!me.list_refs_url) {
243 throw "no list_refs_url specified";
244 }
245
246 me.create = (me.rule_pos === undefined);
247
248 if (me.create) {
249 me.url = '/api2/extjs' + me.base_url;
250 me.method = 'POST';
251 } else {
252 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
253 me.method = 'PUT';
254 }
255
256 var ipanel = Ext.create('PVE.FirewallRulePanel', {
257 create: me.create,
258 list_refs_url: me.list_refs_url,
259 allow_iface: me.allow_iface,
260 rule_pos: me.rule_pos
261 });
262
263 Ext.apply(me, {
264 subject: gettext('Rule'),
265 isAdd: true,
266 items: [ ipanel ]
267 });
268
269 me.callParent();
270
271 if (!me.create) {
272 me.load({
273 success: function(response, options) {
274 var values = response.result.data;
275 ipanel.setValues(values);
276 if (values.errors) {
277 var field = me.query('[isFormField][name=modified_marker]')[0];
278 field.setValue(1);
279 Ext.Function.defer(function() {
280 var form = ipanel.up('form').getForm();
281 form.markInvalid(values.errors)
282 }, 100);
283 }
284 }
285 });
286 }
287 }
288});
289
290Ext.define('PVE.FirewallGroupRuleEdit', {
291 extend: 'PVE.window.Edit',
292
293 base_url: undefined,
294
295 allow_iface: false,
296
297 initComponent : function() {
298 /*jslint confusion: true */
299 var me = this;
300
301 me.create = (me.rule_pos === undefined);
302
303 if (me.create) {
304 me.url = '/api2/extjs' + me.base_url;
305 me.method = 'POST';
306 } else {
307 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
308 me.method = 'PUT';
309 }
310
311 var column1 = [
312 {
313 xtype: 'hiddenfield',
314 name: 'type',
315 value: 'group'
316 },
317 {
318 xtype: 'pveSecurityGroupsSelector',
319 name: 'action',
320 value: '',
321 fieldLabel: gettext('Security Group'),
322 allowBlank: false
323 }
324 ];
325
326 if (me.allow_iface) {
327 column1.push({
328 xtype: 'pvetextfield',
329 name: 'iface',
330 deleteEmpty: !me.create,
331 value: '',
332 fieldLabel: gettext('Interface')
333 });
334 }
335
336 var ipanel = Ext.create('PVE.panel.InputPanel', {
337 create: me.create,
338 column1: column1,
339 column2: [
340 {
341 xtype: 'pvecheckbox',
342 name: 'enable',
343 checked: false,
344 height: 22, // hack: set same height as text fields
345 uncheckedValue: 0,
346 fieldLabel: gettext('Enable')
347 }
348 ],
349 columnB: [
350 {
351 xtype: 'textfield',
352 name: 'comment',
353 value: '',
354 fieldLabel: gettext('Comment')
355 }
356 ]
357 });
358
359 Ext.apply(me, {
360 subject: gettext('Rule'),
361 isAdd: true,
362 items: [ ipanel ]
363 });
364
365 me.callParent();
366
367 if (!me.create) {
368 me.load({
369 success: function(response, options) {
370 var values = response.result.data;
371 ipanel.setValues(values);
372 }
373 });
374 }
375 }
376});
377
378Ext.define('PVE.FirewallRules', {
379 extend: 'Ext.grid.Panel',
380 alias: 'widget.pveFirewallRules',
381
382 base_url: undefined,
383 list_refs_url: undefined,
384
385 addBtn: undefined,
386 removeBtn: undefined,
387 editBtn: undefined,
388 groupBtn: undefined,
389
390 tbar_prefix: undefined,
391
392 allow_groups: true,
393 allow_iface: false,
394
395 setBaseUrl: function(url) {
396 var me = this;
397
398 me.base_url = url;
399
400 if (url === undefined) {
401 me.addBtn.setDisabled(true);
402 if (me.groupBtn) {
403 me.groupBtn.setDisabled(true);
404 }
405 me.store.removeAll();
406 } else {
407 me.addBtn.setDisabled(false);
408 if (me.groupBtn) {
409 me.groupBtn.setDisabled(false);
410 }
411 me.store.setProxy({
412 type: 'pve',
413 url: '/api2/json' + url
414 });
415
416 me.store.load();
417 }
418 },
419
420 moveRule: function(from, to) {
421 var me = this;
422
423 if (!me.base_url) {
424 return;
425 }
426
427 PVE.Utils.API2Request({
428 url: me.base_url + "/" + from,
429 method: 'PUT',
430 params: { moveto: to },
431 waitMsgTarget: me,
432 failure: function(response, options) {
433 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
434 },
435 callback: function() {
436 me.store.load();
437 }
438 });
439 },
440
441 updateRule: function(rule) {
442 var me = this;
443
444 if (!me.base_url) {
445 return;
446 }
447
448 rule.enable = rule.enable ? 1 : 0;
449
450 var pos = rule.pos;
451 delete rule.pos;
452 delete rule.errors;
453
454 PVE.Utils.API2Request({
455 url: me.base_url + '/' + pos.toString(),
456 method: 'PUT',
457 params: rule,
458 waitMsgTarget: me,
459 failure: function(response, options) {
460 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
461 },
462 callback: function() {
463 me.store.load();
464 }
465 });
466 },
467
468 deleteRule: function(rule) {
469 var me = this;
470
471 if (!me.base_url) {
472 return;
473 }
474
475 PVE.Utils.API2Request({
476 url: me.base_url + '/' + rule.pos.toString() +
477 '?digest=' + encodeURIComponent(rule.digest),
478 method: 'DELETE',
479 waitMsgTarget: me,
480 failure: function(response, options) {
481 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
482 },
483 callback: function() {
484 me.store.load();
485 }
486 });
487 },
488
489 initComponent: function() {
490 /*jslint confusion: true */
491 var me = this;
492
493 if (!me.list_refs_url) {
494 throw "no list_refs_url specified";
495 }
496
f3578e26 497 var store = Ext.create('Ext.data.Store',{
435cce27
DM
498 model: 'pve-fw-rule'
499 });
500
501 var reload = function() {
502 store.load();
503 };
504
505 var sm = Ext.create('Ext.selection.RowModel', {});
506
507 var run_editor = function() {
508 var rec = sm.getSelection()[0];
509 if (!rec) {
510 return;
511 }
512 var type = rec.data.type;
513
514 var editor;
515 if (type === 'in' || type === 'out') {
516 editor = 'PVE.FirewallRuleEdit';
517 } else if (type === 'group') {
518 editor = 'PVE.FirewallGroupRuleEdit';
519 } else {
520 return;
521 }
522
523 var win = Ext.create(editor, {
524 digest: rec.data.digest,
525 allow_iface: me.allow_iface,
526 base_url: me.base_url,
527 list_refs_url: me.list_refs_url,
528 rule_pos: rec.data.pos
529 });
530
531 win.show();
532 win.on('destroy', reload);
533 };
534
f3578e26 535 me.editBtn = Ext.create('PVE.button.Button',{
435cce27
DM
536 text: gettext('Edit'),
537 disabled: true,
538 selModel: sm,
539 handler: run_editor
540 });
541
542 me.addBtn = Ext.create('Ext.Button', {
543 text: gettext('Add'),
544 disabled: true,
545 handler: function() {
546 var win = Ext.create('PVE.FirewallRuleEdit', {
547 allow_iface: me.allow_iface,
548 base_url: me.base_url,
549 list_refs_url: me.list_refs_url
550 });
551 win.on('destroy', reload);
552 win.show();
553 }
554 });
555
556 if (me.allow_groups) {
557 me.groupBtn = Ext.create('Ext.Button', {
558 text: gettext('Insert') + ': ' +
559 gettext('Security Group'),
560 disabled: true,
561 handler: function() {
562 var win = Ext.create('PVE.FirewallGroupRuleEdit', {
563 allow_iface: me.allow_iface,
564 base_url: me.base_url
565 });
566 win.on('destroy', reload);
567 win.show();
568 }
569 });
570 }
571
f3578e26 572 me.removeBtn = Ext.create('PVE.button.Button',{
435cce27
DM
573 text: gettext('Remove'),
574 selModel: sm,
575 disabled: true,
576 handler: function() {
577 var rec = sm.getSelection()[0];
578 if (!rec) {
579 return;
580 }
581 me.deleteRule(rec.data);
582 }
583 });
584
585 var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : [];
586 tbar.push(me.addBtn);
587 if (me.groupBtn) {
588 tbar.push(me.groupBtn);
589 }
590 tbar.push([ me.removeBtn, me.editBtn ]);
591
592 var render_errors = function(name, value, metaData, record) {
593 var errors = record.data.errors;
594 if (errors && errors[name]) {
595 metaData.tdCls = 'x-form-invalid-field';
596 var html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
597 metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
598 html.replace(/\"/g,'&quot;') + '"';
599 }
600 return value;
601 };
602
603 var columns = [
604 {
605 // similar to xtype: 'rownumberer',
606 dataIndex: 'pos',
607 resizable: false,
608 width: 23,
609 sortable: false,
610 align: 'right',
611 hideable: false,
612 menuDisabled: true,
613 renderer: function(value, metaData, record, rowIdx, colIdx, store) {
614 metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
615 if (value >= 0) {
616 return value;
617 }
618 return '';
619 }
620 },
621 {
622 xtype: 'checkcolumn',
623 header: gettext('Enable'),
624 dataIndex: 'enable',
625 listeners: {
7a4c3133
EK
626 checkchange: function(column, recordIndex, checked) {
627 var record = me.getStore().getData().items[recordIndex];
435cce27
DM
628 record.commit();
629 var data = {};
7a4c3133 630 Ext.Array.forEach(record.getFields(), function(field) {
435cce27
DM
631 data[field.name] = record.get(field.name);
632 });
633 if (!me.allow_iface || !data.iface) {
634 delete data.iface;
635 }
636 me.updateRule(data);
637 }
638 },
639 width: 50
640 },
641 {
642 header: gettext('Type'),
643 dataIndex: 'type',
644 renderer: function(value, metaData, record) {
645 return render_errors('type', value, metaData, record);
646 },
647 width: 50
648 },
649 {
650 header: gettext('Action'),
651 dataIndex: 'action',
652 renderer: function(value, metaData, record) {
653 return render_errors('action', value, metaData, record);
654 },
655 width: 80
656 },
657 {
658 header: gettext('Macro'),
659 dataIndex: 'macro',
660 renderer: function(value, metaData, record) {
661 return render_errors('macro', value, metaData, record);
662 },
663 width: 80
664 }
665 ];
666
667 if (me.allow_iface) {
668 columns.push({
669 header: gettext('Interface'),
670 dataIndex: 'iface',
671 renderer: function(value, metaData, record) {
672 return render_errors('iface', value, metaData, record);
673 },
674 width: 80
675 });
676 }
677
678 columns.push([
679 {
680 header: gettext('Source'),
681 dataIndex: 'source',
682 renderer: function(value, metaData, record) {
683 return render_errors('source', value, metaData, record);
684 },
685 width: 100
686 },
687 {
688 header: gettext('Destination'),
689 dataIndex: 'dest',
690 renderer: function(value, metaData, record) {
691 return render_errors('dest', value, metaData, record);
692 },
693 width: 100
694 },
695 {
696 header: gettext('Protocol'),
697 dataIndex: 'proto',
698 renderer: function(value, metaData, record) {
699 return render_errors('proto', value, metaData, record);
700 },
701 width: 100
702 },
703 {
704 header: gettext('Dest. port'),
705 dataIndex: 'dport',
706 renderer: function(value, metaData, record) {
707 return render_errors('dport', value, metaData, record);
708 },
709 width: 100
710 },
711 {
712 header: gettext('Source port'),
713 dataIndex: 'sport',
714 renderer: function(value, metaData, record) {
715 return render_errors('sport', value, metaData, record);
716 },
717 width: 100
718 },
719 {
720 header: gettext('Comment'),
721 dataIndex: 'comment',
722 flex: 1,
723 renderer: function(value, metaData, record) {
724 return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record);
725 }
726 }
727 ]);
728
729 Ext.apply(me, {
730 store: store,
731 selModel: sm,
732 tbar: tbar,
733 viewConfig: {
734 plugins: [
735 {
736 ptype: 'gridviewdragdrop',
737 dragGroup: 'FWRuleDDGroup',
738 dropGroup: 'FWRuleDDGroup'
739 }
740 ],
741 listeners: {
742 beforedrop: function(node, data, dropRec, dropPosition) {
743 if (!dropRec) {
744 return false; // empty view
745 }
746 var moveto = dropRec.get('pos');
747 if (dropPosition === 'after') {
748 moveto++;
749 }
750 var pos = data.records[0].get('pos');
751 me.moveRule(pos, moveto);
752 return 0;
753 },
754 itemdblclick: run_editor
755 }
756 },
757 sortableColumns: false,
758 columns: columns
759 });
760
761 me.callParent();
762
763 if (me.base_url) {
764 me.setBaseUrl(me.base_url); // load
765 }
766 }
767}, function() {
768
769 Ext.define('pve-fw-rule', {
770 extend: 'Ext.data.Model',
771 fields: [ { name: 'enable', type: 'boolean' },
772 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface',
773 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ],
774 idProperty: 'pos'
775 });
776
777});