]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame_incremental - src/node/NetworkView.js
bump version to 4.2.3
[proxmox-widget-toolkit.git] / src / node / NetworkView.js
... / ...
CommitLineData
1Ext.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
25Ext.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 Ext.create('Proxmox.node.NetworkEdit', {
99 autoShow: true,
100 nodename: me.nodename,
101 iface: rec.data.iface,
102 iftype: rec.data.type,
103 listeners: {
104 destroy: () => reload(),
105 },
106 });
107 };
108
109 let edit_btn = new Ext.Button({
110 text: gettext('Edit'),
111 disabled: true,
112 handler: run_editor,
113 });
114
115 let sm = Ext.create('Ext.selection.RowModel', {});
116
117 let del_btn = new Proxmox.button.StdRemoveButton({
118 selModel: sm,
119 getUrl: ({ data }) => `${baseUrl}/${data.iface}`,
120 callback: () => reload(),
121 });
122
123 let apply_btn = Ext.create('Proxmox.button.Button', {
124 text: gettext('Apply Configuration'),
125 itemId: 'apply',
126 disabled: true,
127 confirmMsg: 'Do you want to apply pending network changes?',
128 hidden: !me.showApplyBtn,
129 handler: function() {
130 Proxmox.Utils.API2Request({
131 url: baseUrl,
132 method: 'PUT',
133 waitMsgTarget: me,
134 success: function({ result }, opts) {
135 Ext.create('Proxmox.window.TaskProgress', {
136 autoShow: true,
137 taskDone: reload,
138 upid: result.data,
139 });
140 },
141 failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
142 });
143 },
144 });
145
146 let set_button_status = function() {
147 let rec = sm.getSelection()[0];
148
149 edit_btn.setDisabled(!rec);
150 del_btn.setDisabled(!rec);
151 };
152
153 let findNextFreeInterfaceId = function(prefix) {
154 for (let next = 0; next <= 9999; next++) {
155 let id = `${prefix}${next.toString()}`;
156 if (!store.getById(id)) {
157 return id;
158 }
159 }
160 Ext.Msg.alert('Error', `No free ID for ${prefix} found!`);
161 return '';
162 };
163
164 let menu_items = [];
165 let addEditWindowToMenu = (iType, iDefault) => {
166 menu_items.push({
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 }),
178 });
179 };
180
181 if (me.types.indexOf('bridge') !== -1) {
182 addEditWindowToMenu('bridge', 'vmbr');
183 }
184
185 if (me.types.indexOf('bond') !== -1) {
186 addEditWindowToMenu('bond');
187 }
188
189 if (me.types.indexOf('vlan') !== -1) {
190 addEditWindowToMenu('vlan');
191 }
192
193 if (me.types.indexOf('ovs') !== -1) {
194 if (menu_items.length > 0) {
195 menu_items.push({ xtype: 'menuseparator' });
196 }
197
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(),
209 },
210 }),
211 });
212 }
213
214 let renderer_generator = function(fieldname) {
215 return function(val, metaData, rec) {
216 let tmp = [];
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
227 Ext.apply(me, {
228 layout: 'border',
229 tbar: [
230 {
231 text: gettext('Create'),
232 menu: {
233 plain: true,
234 items: menu_items,
235 },
236 }, '-',
237 {
238 text: gettext('Revert'),
239 itemId: 'revert',
240 handler: function() {
241 Proxmox.Utils.API2Request({
242 url: baseUrl,
243 method: 'DELETE',
244 waitMsgTarget: me,
245 callback: function() {
246 reload();
247 },
248 failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
249 });
250 },
251 },
252 edit_btn,
253 del_btn,
254 '-',
255 apply_btn,
256 ],
257 items: [
258 {
259 xtype: 'gridpanel',
260 stateful: true,
261 stateId: 'grid-node-network',
262 store: store,
263 selModel: sm,
264 region: 'center',
265 border: false,
266 columns: [
267 {
268 header: gettext('Name'),
269 sortable: true,
270 dataIndex: 'iface',
271 },
272 {
273 header: gettext('Type'),
274 sortable: true,
275 width: 120,
276 renderer: Proxmox.Utils.render_network_iface_type,
277 dataIndex: 'type',
278 },
279 {
280 xtype: 'booleancolumn',
281 header: gettext('Active'),
282 width: 80,
283 sortable: true,
284 dataIndex: 'active',
285 trueText: Proxmox.Utils.yesText,
286 falseText: Proxmox.Utils.noText,
287 undefinedText: Proxmox.Utils.noText,
288 },
289 {
290 xtype: 'booleancolumn',
291 header: gettext('Autostart'),
292 width: 80,
293 sortable: true,
294 dataIndex: 'autostart',
295 trueText: Proxmox.Utils.yesText,
296 falseText: Proxmox.Utils.noText,
297 undefinedText: Proxmox.Utils.noText,
298 },
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,
307 undefinedText: Proxmox.Utils.noText,
308 },
309 {
310 header: gettext('Ports/Slaves'),
311 dataIndex: 'type',
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 },
324 },
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 },
335 {
336 header: gettext('IP address'),
337 sortable: true,
338 width: 120,
339 hidden: true,
340 dataIndex: 'address',
341 renderer: renderer_generator('address'),
342 },
343 {
344 header: gettext('Subnet mask'),
345 width: 120,
346 sortable: true,
347 hidden: true,
348 dataIndex: 'netmask',
349 renderer: renderer_generator('netmask'),
350 },
351 {
352 header: gettext('CIDR'),
353 width: 150,
354 sortable: true,
355 dataIndex: 'cidr',
356 renderer: renderer_generator('cidr'),
357 },
358 {
359 header: gettext('Gateway'),
360 width: 150,
361 sortable: true,
362 dataIndex: 'gateway',
363 renderer: renderer_generator('gateway'),
364 },
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 },
377 {
378 header: 'MTU',
379 hidden: true,
380 sortable: true,
381 dataIndex: 'mtu',
382 },
383 {
384 header: gettext('Comment'),
385 dataIndex: 'comments',
386 flex: 1,
387 renderer: Ext.String.htmlEncode,
388 },
389 ],
390 listeners: {
391 selectionchange: set_button_status,
392 itemdblclick: run_editor,
393 },
394 },
395 {
396 border: false,
397 region: 'south',
398 autoScroll: true,
399 hidden: true,
400 itemId: 'changes',
401 tbar: [
402 gettext('Pending changes') + ' (' +
403 gettext("Either reboot or use 'Apply Configuration' (needs ifupdown2) to activate") + ')',
404 ],
405 split: true,
406 bodyPadding: 5,
407 flex: 0.6,
408 html: gettext("No changes"),
409 },
410 ],
411 });
412
413 me.callParent();
414 reload();
415 },
416});