]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/node/NetworkView.js
ui: network: add columns for vlan-id and vlan-raw-device
[proxmox-widget-toolkit.git] / src / node / NetworkView.js
1 Ext.define('proxmox-networks', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'active',
5 'address',
6 'address6',
7 'autostart',
8 'bridge_ports',
9 'cidr',
10 'cidr6',
11 'comments',
12 'gateway',
13 'gateway6',
14 'iface',
15 'netmask',
16 'netmask6',
17 'slaves',
18 'type',
19 'vlan-id',
20 'vlan-raw-device',
21 ],
22 idProperty: 'iface',
23 });
24
25 Ext.define('Proxmox.node.NetworkView', {
26 extend: 'Ext.panel.Panel',
27
28 alias: ['widget.proxmoxNodeNetworkView'],
29
30 // defines what types of network devices we want to create
31 // order is always the same
32 types: ['bridge', 'bond', 'vlan', 'ovs'],
33
34 showApplyBtn: false,
35
36 initComponent: function() {
37 let me = this;
38
39 if (!me.nodename) {
40 throw "no node name specified";
41 }
42
43 let baseUrl = '/nodes/' + me.nodename + '/network';
44
45 let store = Ext.create('Ext.data.Store', {
46 model: 'proxmox-networks',
47 proxy: {
48 type: 'proxmox',
49 url: '/api2/json' + baseUrl,
50 },
51 sorters: [
52 {
53 property: 'iface',
54 direction: 'ASC',
55 },
56 ],
57 });
58
59 let reload = function() {
60 let changeitem = me.down('#changes');
61 let apply_btn = me.down('#apply');
62 let revert_btn = me.down('#revert');
63 Proxmox.Utils.API2Request({
64 url: baseUrl,
65 failure: function(response, opts) {
66 store.loadData({});
67 Proxmox.Utils.setErrorMask(me, response.htmlStatus);
68 changeitem.update('');
69 changeitem.setHidden(true);
70 },
71 success: function(response, opts) {
72 let result = Ext.decode(response.responseText);
73 store.loadData(result.data);
74 let changes = result.changes;
75 if (changes === undefined || changes === '') {
76 changes = gettext("No changes");
77 changeitem.setHidden(true);
78 apply_btn.setDisabled(true);
79 revert_btn.setDisabled(true);
80 } else {
81 changeitem.update("<pre>" + Ext.htmlEncode(changes) + "</pre>");
82 changeitem.setHidden(false);
83 apply_btn.setDisabled(false);
84 revert_btn.setDisabled(false);
85 }
86 },
87 });
88 };
89
90 let run_editor = function() {
91 let grid = me.down('gridpanel');
92 let sm = grid.getSelectionModel();
93 let rec = sm.getSelection()[0];
94 if (!rec) {
95 return;
96 }
97
98 let win = Ext.create('Proxmox.node.NetworkEdit', {
99 nodename: me.nodename,
100 iface: rec.data.iface,
101 iftype: rec.data.type,
102 });
103 win.show();
104 win.on('destroy', reload);
105 };
106
107 let edit_btn = new Ext.Button({
108 text: gettext('Edit'),
109 disabled: true,
110 handler: run_editor,
111 });
112
113 let del_btn = new Ext.Button({
114 text: gettext('Remove'),
115 disabled: true,
116 handler: function() {
117 let grid = me.down('gridpanel');
118 let sm = grid.getSelectionModel();
119 let rec = sm.getSelection()[0];
120 if (!rec) {
121 return;
122 }
123
124 let iface = rec.data.iface;
125
126 Proxmox.Utils.API2Request({
127 url: baseUrl + '/' + iface,
128 method: 'DELETE',
129 waitMsgTarget: me,
130 callback: function() {
131 reload();
132 },
133 failure: function(response, opts) {
134 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
135 },
136 });
137 },
138 });
139
140 let apply_btn = Ext.create('Proxmox.button.Button', {
141 text: gettext('Apply Configuration'),
142 itemId: 'apply',
143 disabled: true,
144 confirmMsg: 'Do you want to apply pending network changes?',
145 hidden: !me.showApplyBtn,
146 handler: function() {
147 Proxmox.Utils.API2Request({
148 url: baseUrl,
149 method: 'PUT',
150 waitMsgTarget: me,
151 success: function(response, opts) {
152 let upid = response.result.data;
153
154 let win = Ext.create('Proxmox.window.TaskProgress', {
155 taskDone: reload,
156 upid: upid,
157 });
158 win.show();
159 },
160 failure: function(response, opts) {
161 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
162 },
163 });
164 },
165 });
166
167 let set_button_status = function() {
168 let grid = me.down('gridpanel');
169 let sm = grid.getSelectionModel();
170 let rec = sm.getSelection()[0];
171
172 edit_btn.setDisabled(!rec);
173 del_btn.setDisabled(!rec);
174 };
175
176 let render_ports = function(value, metaData, record) {
177 if (value === 'bridge') {
178 return record.data.bridge_ports;
179 } else if (value === 'bond') {
180 return record.data.slaves;
181 } else if (value === 'OVSBridge') {
182 return record.data.ovs_ports;
183 } else if (value === 'OVSBond') {
184 return record.data.ovs_bonds;
185 }
186 return '';
187 };
188
189 let find_next_iface_id = function(prefix) {
190 let next;
191 for (next = 0; next <= 9999; next++) {
192 if (!store.getById(prefix + next.toString())) {
193 break;
194 }
195 }
196 return prefix + next.toString();
197 };
198
199 let menu_items = [];
200
201 if (me.types.indexOf('bridge') !== -1) {
202 menu_items.push({
203 text: Proxmox.Utils.render_network_iface_type('bridge'),
204 handler: function() {
205 let win = Ext.create('Proxmox.node.NetworkEdit', {
206 nodename: me.nodename,
207 iftype: 'bridge',
208 iface_default: find_next_iface_id('vmbr'),
209 onlineHelp: 'sysadmin_network_configuration',
210 });
211 win.on('destroy', reload);
212 win.show();
213 },
214 });
215 }
216
217 if (me.types.indexOf('bond') !== -1) {
218 menu_items.push({
219 text: Proxmox.Utils.render_network_iface_type('bond'),
220 handler: function() {
221 let win = Ext.create('Proxmox.node.NetworkEdit', {
222 nodename: me.nodename,
223 iftype: 'bond',
224 iface_default: find_next_iface_id('bond'),
225 onlineHelp: 'sysadmin_network_configuration',
226 });
227 win.on('destroy', reload);
228 win.show();
229 },
230 });
231 }
232
233 if (me.types.indexOf('vlan') !== -1) {
234 menu_items.push({
235 text: Proxmox.Utils.render_network_iface_type('vlan'),
236 handler: function() {
237 let win = Ext.create('Proxmox.node.NetworkEdit', {
238 nodename: me.nodename,
239 iftype: 'vlan',
240 iface_default: find_next_iface_id('vlan'),
241 onlineHelp: 'sysadmin_network_configuration',
242 });
243 win.on('destroy', reload);
244 win.show();
245 },
246 });
247 }
248
249 if (me.types.indexOf('ovs') !== -1) {
250 if (menu_items.length > 0) {
251 menu_items.push({ xtype: 'menuseparator' });
252 }
253
254 menu_items.push(
255 {
256 text: Proxmox.Utils.render_network_iface_type('OVSBridge'),
257 handler: function() {
258 let win = Ext.create('Proxmox.node.NetworkEdit', {
259 nodename: me.nodename,
260 iftype: 'OVSBridge',
261 iface_default: find_next_iface_id('vmbr'),
262 });
263 win.on('destroy', reload);
264 win.show();
265 },
266 },
267 {
268 text: Proxmox.Utils.render_network_iface_type('OVSBond'),
269 handler: function() {
270 let win = Ext.create('Proxmox.node.NetworkEdit', {
271 nodename: me.nodename,
272 iftype: 'OVSBond',
273 iface_default: find_next_iface_id('bond'),
274 });
275 win.on('destroy', reload);
276 win.show();
277 },
278 },
279 {
280 text: Proxmox.Utils.render_network_iface_type('OVSIntPort'),
281 handler: function() {
282 let win = Ext.create('Proxmox.node.NetworkEdit', {
283 nodename: me.nodename,
284 iftype: 'OVSIntPort',
285 });
286 win.on('destroy', reload);
287 win.show();
288 },
289 },
290 );
291 }
292
293 let renderer_generator = function(fieldname) {
294 return function(val, metaData, rec) {
295 let tmp = [];
296 if (rec.data[fieldname]) {
297 tmp.push(rec.data[fieldname]);
298 }
299 if (rec.data[fieldname + '6']) {
300 tmp.push(rec.data[fieldname + '6']);
301 }
302 return tmp.join('<br>') || '';
303 };
304 };
305
306 Ext.apply(me, {
307 layout: 'border',
308 tbar: [
309 {
310 text: gettext('Create'),
311 menu: {
312 plain: true,
313 items: menu_items,
314 },
315 }, '-',
316 {
317 text: gettext('Revert'),
318 itemId: 'revert',
319 handler: function() {
320 Proxmox.Utils.API2Request({
321 url: baseUrl,
322 method: 'DELETE',
323 waitMsgTarget: me,
324 callback: function() {
325 reload();
326 },
327 failure: function(response, opts) {
328 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
329 },
330 });
331 },
332 },
333 edit_btn,
334 del_btn,
335 '-',
336 apply_btn,
337 ],
338 items: [
339 {
340 xtype: 'gridpanel',
341 stateful: true,
342 stateId: 'grid-node-network',
343 store: store,
344 region: 'center',
345 border: false,
346 columns: [
347 {
348 header: gettext('Name'),
349 sortable: true,
350 dataIndex: 'iface',
351 },
352 {
353 header: gettext('Type'),
354 sortable: true,
355 width: 120,
356 renderer: Proxmox.Utils.render_network_iface_type,
357 dataIndex: 'type',
358 },
359 {
360 xtype: 'booleancolumn',
361 header: gettext('Active'),
362 width: 80,
363 sortable: true,
364 dataIndex: 'active',
365 trueText: Proxmox.Utils.yesText,
366 falseText: Proxmox.Utils.noText,
367 undefinedText: Proxmox.Utils.noText,
368 },
369 {
370 xtype: 'booleancolumn',
371 header: gettext('Autostart'),
372 width: 80,
373 sortable: true,
374 dataIndex: 'autostart',
375 trueText: Proxmox.Utils.yesText,
376 falseText: Proxmox.Utils.noText,
377 undefinedText: Proxmox.Utils.noText,
378 },
379 {
380 xtype: 'booleancolumn',
381 header: gettext('VLAN aware'),
382 width: 80,
383 sortable: true,
384 dataIndex: 'bridge_vlan_aware',
385 trueText: Proxmox.Utils.yesText,
386 falseText: Proxmox.Utils.noText,
387 undefinedText: Proxmox.Utils.noText,
388 },
389 {
390 header: gettext('Ports/Slaves'),
391 dataIndex: 'type',
392 renderer: render_ports,
393 },
394 {
395 header: gettext('Bond Mode'),
396 dataIndex: 'bond_mode',
397 renderer: Proxmox.Utils.render_bond_mode,
398 },
399 {
400 header: gettext('Hash Policy'),
401 hidden: true,
402 dataIndex: 'bond_xmit_hash_policy',
403 },
404 {
405 header: gettext('IP address'),
406 sortable: true,
407 width: 120,
408 hidden: true,
409 dataIndex: 'address',
410 renderer: renderer_generator('address'),
411 },
412 {
413 header: gettext('Subnet mask'),
414 width: 120,
415 sortable: true,
416 hidden: true,
417 dataIndex: 'netmask',
418 renderer: renderer_generator('netmask'),
419 },
420 {
421 header: gettext('CIDR'),
422 width: 150,
423 sortable: true,
424 dataIndex: 'cidr',
425 renderer: renderer_generator('cidr'),
426 },
427 {
428 header: gettext('Gateway'),
429 width: 150,
430 sortable: true,
431 dataIndex: 'gateway',
432 renderer: renderer_generator('gateway'),
433 },
434 {
435 header: gettext('VLAN ID'),
436 hidden: true,
437 sortable: true,
438 dataIndex: 'vlan-id',
439 },
440 {
441 header: gettext('VLAN raw device'),
442 hidden: true,
443 sortable: true,
444 dataIndex: 'vlan-raw-device',
445 },
446 {
447 header: gettext('Comment'),
448 dataIndex: 'comments',
449 flex: 1,
450 renderer: Ext.String.htmlEncode,
451 },
452 ],
453 listeners: {
454 selectionchange: set_button_status,
455 itemdblclick: run_editor,
456 },
457 },
458 {
459 border: false,
460 region: 'south',
461 autoScroll: true,
462 hidden: true,
463 itemId: 'changes',
464 tbar: [
465 gettext('Pending changes') + ' (' +
466 gettext("Either reboot or use 'Apply Configuration' (needs ifupdown2) to activate") + ')',
467 ],
468 split: true,
469 bodyPadding: 5,
470 flex: 0.6,
471 html: gettext("No changes"),
472 },
473 ],
474 });
475
476 me.callParent();
477 reload();
478 },
479 });