]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/node/NetworkView.js
node: network: do not gettext MTU
[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 sm = Ext.create('Ext.selection.RowModel', {});
114
115 let del_btn = new Proxmox.button.StdRemoveButton({
116 selModel: sm,
117 getUrl: ({ data }) => `${baseUrl}/${data.iface}`,
118 callback: () => reload(),
119 });
120
121 let apply_btn = Ext.create('Proxmox.button.Button', {
122 text: gettext('Apply Configuration'),
123 itemId: 'apply',
124 disabled: true,
125 confirmMsg: 'Do you want to apply pending network changes?',
126 hidden: !me.showApplyBtn,
127 handler: function() {
128 Proxmox.Utils.API2Request({
129 url: baseUrl,
130 method: 'PUT',
131 waitMsgTarget: me,
132 success: function(response, opts) {
133 let upid = response.result.data;
134
135 let win = Ext.create('Proxmox.window.TaskProgress', {
136 taskDone: reload,
137 upid: upid,
138 });
139 win.show();
140 },
141 failure: function(response, opts) {
142 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
143 },
144 });
145 },
146 });
147
148 let set_button_status = function() {
149 let rec = sm.getSelection()[0];
150
151 edit_btn.setDisabled(!rec);
152 del_btn.setDisabled(!rec);
153 };
154
155 let find_next_iface_id = function(prefix) {
156 let next;
157 for (next = 0; next <= 9999; next++) {
158 if (!store.getById(prefix + next.toString())) {
159 break;
160 }
161 }
162 return prefix + next.toString();
163 };
164
165 let menu_items = [];
166
167 if (me.types.indexOf('bridge') !== -1) {
168 menu_items.push({
169 text: Proxmox.Utils.render_network_iface_type('bridge'),
170 handler: function() {
171 let win = Ext.create('Proxmox.node.NetworkEdit', {
172 nodename: me.nodename,
173 iftype: 'bridge',
174 iface_default: find_next_iface_id('vmbr'),
175 onlineHelp: 'sysadmin_network_configuration',
176 });
177 win.on('destroy', reload);
178 win.show();
179 },
180 });
181 }
182
183 if (me.types.indexOf('bond') !== -1) {
184 menu_items.push({
185 text: Proxmox.Utils.render_network_iface_type('bond'),
186 handler: function() {
187 let win = Ext.create('Proxmox.node.NetworkEdit', {
188 nodename: me.nodename,
189 iftype: 'bond',
190 iface_default: find_next_iface_id('bond'),
191 onlineHelp: 'sysadmin_network_configuration',
192 });
193 win.on('destroy', reload);
194 win.show();
195 },
196 });
197 }
198
199 if (me.types.indexOf('vlan') !== -1) {
200 menu_items.push({
201 text: Proxmox.Utils.render_network_iface_type('vlan'),
202 handler: function() {
203 let win = Ext.create('Proxmox.node.NetworkEdit', {
204 nodename: me.nodename,
205 iftype: 'vlan',
206 iface_default: find_next_iface_id('vlan'),
207 onlineHelp: 'sysadmin_network_configuration',
208 });
209 win.on('destroy', reload);
210 win.show();
211 },
212 });
213 }
214
215 if (me.types.indexOf('ovs') !== -1) {
216 if (menu_items.length > 0) {
217 menu_items.push({ xtype: 'menuseparator' });
218 }
219
220 menu_items.push(
221 {
222 text: Proxmox.Utils.render_network_iface_type('OVSBridge'),
223 handler: function() {
224 let win = Ext.create('Proxmox.node.NetworkEdit', {
225 nodename: me.nodename,
226 iftype: 'OVSBridge',
227 iface_default: find_next_iface_id('vmbr'),
228 });
229 win.on('destroy', reload);
230 win.show();
231 },
232 },
233 {
234 text: Proxmox.Utils.render_network_iface_type('OVSBond'),
235 handler: function() {
236 let win = Ext.create('Proxmox.node.NetworkEdit', {
237 nodename: me.nodename,
238 iftype: 'OVSBond',
239 iface_default: find_next_iface_id('bond'),
240 });
241 win.on('destroy', reload);
242 win.show();
243 },
244 },
245 {
246 text: Proxmox.Utils.render_network_iface_type('OVSIntPort'),
247 handler: function() {
248 let win = Ext.create('Proxmox.node.NetworkEdit', {
249 nodename: me.nodename,
250 iftype: 'OVSIntPort',
251 });
252 win.on('destroy', reload);
253 win.show();
254 },
255 },
256 );
257 }
258
259 let renderer_generator = function(fieldname) {
260 return function(val, metaData, rec) {
261 let tmp = [];
262 if (rec.data[fieldname]) {
263 tmp.push(rec.data[fieldname]);
264 }
265 if (rec.data[fieldname + '6']) {
266 tmp.push(rec.data[fieldname + '6']);
267 }
268 return tmp.join('<br>') || '';
269 };
270 };
271
272 Ext.apply(me, {
273 layout: 'border',
274 tbar: [
275 {
276 text: gettext('Create'),
277 menu: {
278 plain: true,
279 items: menu_items,
280 },
281 }, '-',
282 {
283 text: gettext('Revert'),
284 itemId: 'revert',
285 handler: function() {
286 Proxmox.Utils.API2Request({
287 url: baseUrl,
288 method: 'DELETE',
289 waitMsgTarget: me,
290 callback: function() {
291 reload();
292 },
293 failure: function(response, opts) {
294 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
295 },
296 });
297 },
298 },
299 edit_btn,
300 del_btn,
301 '-',
302 apply_btn,
303 ],
304 items: [
305 {
306 xtype: 'gridpanel',
307 stateful: true,
308 stateId: 'grid-node-network',
309 store: store,
310 selModel: sm,
311 region: 'center',
312 border: false,
313 columns: [
314 {
315 header: gettext('Name'),
316 sortable: true,
317 dataIndex: 'iface',
318 },
319 {
320 header: gettext('Type'),
321 sortable: true,
322 width: 120,
323 renderer: Proxmox.Utils.render_network_iface_type,
324 dataIndex: 'type',
325 },
326 {
327 xtype: 'booleancolumn',
328 header: gettext('Active'),
329 width: 80,
330 sortable: true,
331 dataIndex: 'active',
332 trueText: Proxmox.Utils.yesText,
333 falseText: Proxmox.Utils.noText,
334 undefinedText: Proxmox.Utils.noText,
335 },
336 {
337 xtype: 'booleancolumn',
338 header: gettext('Autostart'),
339 width: 80,
340 sortable: true,
341 dataIndex: 'autostart',
342 trueText: Proxmox.Utils.yesText,
343 falseText: Proxmox.Utils.noText,
344 undefinedText: Proxmox.Utils.noText,
345 },
346 {
347 xtype: 'booleancolumn',
348 header: gettext('VLAN aware'),
349 width: 80,
350 sortable: true,
351 dataIndex: 'bridge_vlan_aware',
352 trueText: Proxmox.Utils.yesText,
353 falseText: Proxmox.Utils.noText,
354 undefinedText: Proxmox.Utils.noText,
355 },
356 {
357 header: gettext('Ports/Slaves'),
358 dataIndex: 'type',
359 renderer: (value, metaData, { data }) => {
360 if (value === 'bridge') {
361 return data.bridge_ports;
362 } else if (value === 'bond') {
363 return data.slaves;
364 } else if (value === 'OVSBridge') {
365 return data.ovs_ports;
366 } else if (value === 'OVSBond') {
367 return data.ovs_bonds;
368 }
369 return '';
370 },
371 },
372 {
373 header: gettext('Bond Mode'),
374 dataIndex: 'bond_mode',
375 renderer: Proxmox.Utils.render_bond_mode,
376 },
377 {
378 header: gettext('Hash Policy'),
379 hidden: true,
380 dataIndex: 'bond_xmit_hash_policy',
381 },
382 {
383 header: gettext('IP address'),
384 sortable: true,
385 width: 120,
386 hidden: true,
387 dataIndex: 'address',
388 renderer: renderer_generator('address'),
389 },
390 {
391 header: gettext('Subnet mask'),
392 width: 120,
393 sortable: true,
394 hidden: true,
395 dataIndex: 'netmask',
396 renderer: renderer_generator('netmask'),
397 },
398 {
399 header: gettext('CIDR'),
400 width: 150,
401 sortable: true,
402 dataIndex: 'cidr',
403 renderer: renderer_generator('cidr'),
404 },
405 {
406 header: gettext('Gateway'),
407 width: 150,
408 sortable: true,
409 dataIndex: 'gateway',
410 renderer: renderer_generator('gateway'),
411 },
412 {
413 header: gettext('VLAN ID'),
414 hidden: true,
415 sortable: true,
416 dataIndex: 'vlan-id',
417 },
418 {
419 header: gettext('VLAN raw device'),
420 hidden: true,
421 sortable: true,
422 dataIndex: 'vlan-raw-device',
423 },
424 {
425 header: 'MTU',
426 hidden: true,
427 sortable: true,
428 dataIndex: 'mtu',
429 },
430 {
431 header: gettext('Comment'),
432 dataIndex: 'comments',
433 flex: 1,
434 renderer: Ext.String.htmlEncode,
435 },
436 ],
437 listeners: {
438 selectionchange: set_button_status,
439 itemdblclick: run_editor,
440 },
441 },
442 {
443 border: false,
444 region: 'south',
445 autoScroll: true,
446 hidden: true,
447 itemId: 'changes',
448 tbar: [
449 gettext('Pending changes') + ' (' +
450 gettext("Either reboot or use 'Apply Configuration' (needs ifupdown2) to activate") + ')',
451 ],
452 split: true,
453 bodyPadding: 5,
454 flex: 0.6,
455 html: gettext("No changes"),
456 },
457 ],
458 });
459
460 me.callParent();
461 reload();
462 },
463 });