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