]>
Commit | Line | Data |
---|---|---|
5a691a50 | 1 | Ext.define('PVE.lxc.NetworkInputPanel', { |
ef4ef788 | 2 | extend: 'Proxmox.panel.InputPanel', |
5a691a50 DM |
3 | alias: 'widget.pveLxcNetworkInputPanel', |
4 | ||
5 | insideWizard: false, | |
6 | ||
c8802a60 | 7 | onlineHelp: 'pct_container_network', |
7f1ac74c | 8 | |
5a691a50 DM |
9 | setNodename: function(nodename) { |
10 | var me = this; | |
99541dcc | 11 | |
5a691a50 DM |
12 | if (!nodename || (me.nodename === nodename)) { |
13 | return; | |
14 | } | |
15 | ||
16 | me.nodename = nodename; | |
17 | ||
18 | var bridgesel = me.query("[isFormField][name=bridge]")[0]; | |
19 | bridgesel.setNodename(nodename); | |
20 | }, | |
99541dcc | 21 | |
5a691a50 DM |
22 | onGetValues: function(values) { |
23 | var me = this; | |
24 | ||
25 | var id; | |
d5e771ce | 26 | if (me.isCreate) { |
5a691a50 DM |
27 | id = values.id; |
28 | delete values.id; | |
29 | } else { | |
30 | id = me.ifname; | |
31 | } | |
32 | ||
33 | if (!id) { | |
34 | return {}; | |
35 | } | |
36 | ||
37 | var newdata = {}; | |
38 | ||
84de645d DC |
39 | if (values.ipv6mode !== 'static') { |
40 | values.ip6 = values.ipv6mode; | |
41 | } | |
42 | if (values.ipv4mode !== 'static') { | |
43 | values.ip = values.ipv4mode; | |
44 | } | |
5a691a50 DM |
45 | newdata[id] = PVE.Parser.printLxcNetwork(values); |
46 | return newdata; | |
47 | }, | |
48 | ||
8058410f | 49 | initComponent: function() { |
5a691a50 DM |
50 | var me = this; |
51 | ||
5a691a50 DM |
52 | var cdata = {}; |
53 | ||
54 | if (me.insideWizard) { | |
55 | me.ifname = 'net0'; | |
56 | cdata.name = 'eth0'; | |
d8e3d77c | 57 | me.dataCache = {}; |
5a691a50 | 58 | } |
8058410f | 59 | cdata.firewall = (me.insideWizard || me.isCreate); |
d8e3d77c TL |
60 | |
61 | if (!me.dataCache) { | |
62 | throw "no dataCache specified"; | |
63 | } | |
64 | ||
d5e771ce | 65 | if (!me.isCreate) { |
5a691a50 DM |
66 | if (!me.ifname) { |
67 | throw "no interface name specified"; | |
68 | } | |
69 | if (!me.dataCache[me.ifname]) { | |
70 | throw "no such interface '" + me.ifname + "'"; | |
71 | } | |
72 | ||
73 | cdata = PVE.Parser.parseLxcNetwork(me.dataCache[me.ifname]); | |
74 | } | |
75 | ||
2517c76b | 76 | var i; |
e08f07de | 77 | for (i = 0; i < 32; i++) { |
d5e771ce | 78 | if (me.isCreate && !me.dataCache['net'+i.toString()]) { |
2517c76b DC |
79 | me.ifname = 'net' + i.toString(); |
80 | break; | |
81 | } | |
5a691a50 | 82 | } |
5a691a50 | 83 | |
2517c76b DC |
84 | var idselector = { |
85 | xtype: 'hidden', | |
5a691a50 | 86 | name: 'id', |
f6710aac | 87 | value: me.ifname, |
5a691a50 | 88 | }; |
2517c76b | 89 | |
7c7ae44f | 90 | me.column1 = [ |
2517c76b | 91 | idselector, |
5a691a50 DM |
92 | { |
93 | xtype: 'textfield', | |
94 | name: 'name', | |
160673c1 TL |
95 | fieldLabel: gettext('Name'), |
96 | emptyText: '(e.g., eth0)', | |
5a691a50 DM |
97 | allowBlank: false, |
98 | value: cdata.name, | |
99 | validator: function(value) { | |
ec0bd652 | 100 | var result = ''; |
5a691a50 DM |
101 | Ext.Object.each(me.dataCache, function(key, netstr) { |
102 | if (!key.match(/^net\d+/) || key === me.ifname) { | |
103 | return; // continue | |
104 | } | |
105 | var net = PVE.Parser.parseLxcNetwork(netstr); | |
106 | if (net.name === value) { | |
107 | result = "interface name already in use"; | |
108 | return false; | |
109 | } | |
110 | }); | |
ec0bd652 DC |
111 | if (result !== '') { |
112 | return result; | |
113 | } | |
114 | // validator can return bool/string | |
ec0bd652 | 115 | return true; |
f6710aac | 116 | }, |
5a691a50 DM |
117 | }, |
118 | { | |
119 | xtype: 'textfield', | |
120 | name: 'hwaddr', | |
121 | fieldLabel: gettext('MAC address'), | |
122 | vtype: 'MacAddress', | |
123 | value: cdata.hwaddr, | |
41bfbd0a | 124 | allowBlank: true, |
f6710aac | 125 | emptyText: 'auto', |
5a691a50 DM |
126 | }, |
127 | { | |
128 | xtype: 'PVE.form.BridgeSelector', | |
129 | name: 'bridge', | |
130 | nodename: me.nodename, | |
131 | fieldLabel: gettext('Bridge'), | |
132 | value: cdata.bridge, | |
f6710aac | 133 | allowBlank: false, |
5a691a50 DM |
134 | }, |
135 | { | |
136 | xtype: 'pveVlanField', | |
137 | name: 'tag', | |
f6710aac | 138 | value: cdata.tag, |
5a691a50 DM |
139 | }, |
140 | { | |
519ca7fa DM |
141 | xtype: 'numberfield', |
142 | name: 'rate', | |
143 | fieldLabel: gettext('Rate limit') + ' (MB/s)', | |
144 | minValue: 0, | |
145 | maxValue: 10*1024, | |
146 | value: cdata.rate, | |
147 | emptyText: 'unlimited', | |
f6710aac | 148 | allowBlank: true, |
519ca7fa DM |
149 | }, |
150 | { | |
896c0d50 | 151 | xtype: 'proxmoxcheckbox', |
5a691a50 DM |
152 | fieldLabel: gettext('Firewall'), |
153 | name: 'firewall', | |
f6710aac TL |
154 | value: cdata.firewall, |
155 | }, | |
5a691a50 DM |
156 | ]; |
157 | ||
158 | var dhcp4 = (cdata.ip === 'dhcp'); | |
159 | if (dhcp4) { | |
160 | cdata.ip = ''; | |
161 | cdata.gw = ''; | |
162 | } | |
163 | ||
164 | var auto6 = (cdata.ip6 === 'auto'); | |
165 | var dhcp6 = (cdata.ip6 === 'dhcp'); | |
166 | if (auto6 || dhcp6) { | |
167 | cdata.ip6 = ''; | |
168 | cdata.gw6 = ''; | |
169 | } | |
2a4971d8 | 170 | |
5a691a50 DM |
171 | me.column2 = [ |
172 | { | |
173 | layout: { | |
174 | type: 'hbox', | |
f6710aac | 175 | align: 'middle', |
5a691a50 DM |
176 | }, |
177 | border: false, | |
178 | margin: '0 0 5 0', | |
5a691a50 DM |
179 | items: [ |
180 | { | |
181 | xtype: 'label', | |
f6710aac | 182 | text: 'IPv4:', // do not localize |
5a691a50 DM |
183 | }, |
184 | { | |
185 | xtype: 'radiofield', | |
186 | boxLabel: gettext('Static'), | |
187 | name: 'ipv4mode', | |
188 | inputValue: 'static', | |
189 | checked: !dhcp4, | |
190 | margin: '0 0 0 10', | |
191 | listeners: { | |
192 | change: function(cb, value) { | |
a2163454 | 193 | me.down('field[name=ip]').setEmptyText( |
f6710aac | 194 | !!value ? Proxmox.Utils.NoneText : "", |
a2163454 | 195 | ); |
5a691a50 DM |
196 | me.down('field[name=ip]').setDisabled(!value); |
197 | me.down('field[name=gw]').setDisabled(!value); | |
f6710aac TL |
198 | }, |
199 | }, | |
5a691a50 DM |
200 | }, |
201 | { | |
202 | xtype: 'radiofield', | |
2ce6111f | 203 | boxLabel: 'DHCP', // do not localize |
5a691a50 DM |
204 | name: 'ipv4mode', |
205 | inputValue: 'dhcp', | |
206 | checked: dhcp4, | |
f6710aac TL |
207 | margin: '0 0 0 10', |
208 | }, | |
209 | ], | |
5a691a50 DM |
210 | }, |
211 | { | |
212 | xtype: 'textfield', | |
213 | name: 'ip', | |
214 | vtype: 'IPCIDRAddress', | |
215 | value: cdata.ip, | |
b552db18 | 216 | emptyText: dhcp4 ? '' : Proxmox.Utils.NoneText, |
5a691a50 | 217 | disabled: dhcp4, |
f6710aac | 218 | fieldLabel: 'IPv4/CIDR', // do not localize |
5a691a50 DM |
219 | }, |
220 | { | |
221 | xtype: 'textfield', | |
222 | name: 'gw', | |
223 | value: cdata.gw, | |
224 | vtype: 'IPAddress', | |
225 | disabled: dhcp4, | |
2ce6111f | 226 | fieldLabel: gettext('Gateway') + ' (IPv4)', |
f6710aac | 227 | margin: '0 0 3 0', // override bottom margin to account for the menuseparator |
5a691a50 DM |
228 | }, |
229 | { | |
230 | xtype: 'menuseparator', | |
231 | height: '3', | |
f6710aac | 232 | margin: '0', |
5a691a50 DM |
233 | }, |
234 | { | |
235 | layout: { | |
236 | type: 'hbox', | |
f6710aac | 237 | align: 'middle', |
5a691a50 DM |
238 | }, |
239 | border: false, | |
240 | margin: '0 0 5 0', | |
5a691a50 DM |
241 | items: [ |
242 | { | |
243 | xtype: 'label', | |
f6710aac | 244 | text: 'IPv6:', // do not localize |
5a691a50 DM |
245 | }, |
246 | { | |
247 | xtype: 'radiofield', | |
248 | boxLabel: gettext('Static'), | |
249 | name: 'ipv6mode', | |
250 | inputValue: 'static', | |
251 | checked: !(auto6 || dhcp6), | |
252 | margin: '0 0 0 10', | |
253 | listeners: { | |
254 | change: function(cb, value) { | |
a2163454 | 255 | me.down('field[name=ip6]').setEmptyText( |
f6710aac | 256 | !!value ? Proxmox.Utils.NoneText : "", |
a2163454 | 257 | ); |
5a691a50 DM |
258 | me.down('field[name=ip6]').setDisabled(!value); |
259 | me.down('field[name=gw6]').setDisabled(!value); | |
f6710aac TL |
260 | }, |
261 | }, | |
5a691a50 DM |
262 | }, |
263 | { | |
264 | xtype: 'radiofield', | |
2ce6111f | 265 | boxLabel: 'DHCP', // do not localize |
5a691a50 DM |
266 | name: 'ipv6mode', |
267 | inputValue: 'dhcp', | |
268 | checked: dhcp6, | |
f6710aac | 269 | margin: '0 0 0 10', |
5a691a50 DM |
270 | }, |
271 | { | |
272 | xtype: 'radiofield', | |
2ce6111f | 273 | boxLabel: 'SLAAC', // do not localize |
5a691a50 DM |
274 | name: 'ipv6mode', |
275 | inputValue: 'auto', | |
276 | checked: auto6, | |
f6710aac TL |
277 | margin: '0 0 0 10', |
278 | }, | |
279 | ], | |
5a691a50 DM |
280 | }, |
281 | { | |
282 | xtype: 'textfield', | |
283 | name: 'ip6', | |
284 | value: cdata.ip6, | |
b552db18 | 285 | emptyText: dhcp6 || auto6 ? '' : Proxmox.Utils.NoneText, |
5a691a50 DM |
286 | vtype: 'IP6CIDRAddress', |
287 | disabled: (dhcp6 || auto6), | |
f6710aac | 288 | fieldLabel: 'IPv6/CIDR', // do not localize |
5a691a50 DM |
289 | }, |
290 | { | |
291 | xtype: 'textfield', | |
292 | name: 'gw6', | |
293 | vtype: 'IP6Address', | |
294 | value: cdata.gw6, | |
295 | disabled: (dhcp6 || auto6), | |
f6710aac TL |
296 | fieldLabel: gettext('Gateway') + ' (IPv6)', |
297 | }, | |
5a691a50 DM |
298 | ]; |
299 | ||
300 | me.callParent(); | |
f6710aac | 301 | }, |
5a691a50 | 302 | }); |
d5e771ce | 303 | |
5a691a50 | 304 | Ext.define('PVE.lxc.NetworkEdit', { |
9fccc702 | 305 | extend: 'Proxmox.window.Edit', |
5a691a50 DM |
306 | |
307 | isAdd: true, | |
308 | ||
8058410f | 309 | initComponent: function() { |
5a691a50 DM |
310 | var me = this; |
311 | ||
312 | if (!me.dataCache) { | |
313 | throw "no dataCache specified"; | |
314 | } | |
315 | ||
316 | if (!me.nodename) { | |
317 | throw "no node name specified"; | |
318 | } | |
319 | ||
320 | var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', { | |
321 | ifname: me.ifname, | |
322 | nodename: me.nodename, | |
323 | dataCache: me.dataCache, | |
f6710aac | 324 | isCreate: me.isCreate, |
5a691a50 | 325 | }); |
99541dcc | 326 | |
5a691a50 DM |
327 | Ext.apply(me, { |
328 | subject: gettext('Network Device') + ' (veth)', | |
329 | digest: me.dataCache.digest, | |
8058410f | 330 | items: [ipanel], |
5a691a50 DM |
331 | }); |
332 | ||
333 | me.callParent(); | |
f6710aac | 334 | }, |
5a691a50 DM |
335 | }); |
336 | ||
337 | Ext.define('PVE.lxc.NetworkView', { | |
338 | extend: 'Ext.grid.GridPanel', | |
d5e771ce | 339 | alias: 'widget.pveLxcNetworkView', |
5a691a50 | 340 | |
ba93a9c6 DC |
341 | onlineHelp: 'pct_container_network', |
342 | ||
5a691a50 DM |
343 | dataCache: {}, // used to store result of last load |
344 | ||
af603c15 DC |
345 | stateful: true, |
346 | stateId: 'grid-lxc-network', | |
347 | ||
5a691a50 DM |
348 | load: function() { |
349 | var me = this; | |
350 | ||
e7ade592 | 351 | Proxmox.Utils.setErrorMask(me, true); |
5a691a50 | 352 | |
e7ade592 | 353 | Proxmox.Utils.API2Request({ |
5a691a50 DM |
354 | url: me.url, |
355 | failure: function(response, opts) { | |
e7ade592 | 356 | Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus); |
5a691a50 DM |
357 | }, |
358 | success: function(response, opts) { | |
e7ade592 | 359 | Proxmox.Utils.setErrorMask(me, false); |
5a691a50 DM |
360 | var result = Ext.decode(response.responseText); |
361 | var data = result.data || {}; | |
362 | me.dataCache = data; | |
363 | var records = []; | |
364 | Ext.Object.each(data, function(key, value) { | |
365 | if (!key.match(/^net\d+/)) { | |
366 | return; // continue | |
367 | } | |
368 | var net = PVE.Parser.parseLxcNetwork(value); | |
369 | net.id = key; | |
370 | records.push(net); | |
371 | }); | |
372 | me.store.loadData(records); | |
e08f07de | 373 | me.down('button[name=addButton]').setDisabled((records.length >= 32)); |
f6710aac | 374 | }, |
5a691a50 DM |
375 | }); |
376 | }, | |
377 | ||
8058410f | 378 | initComponent: function() { |
5a691a50 DM |
379 | var me = this; |
380 | ||
381 | var nodename = me.pveSelNode.data.node; | |
382 | if (!nodename) { | |
383 | throw "no node name specified"; | |
384 | } | |
385 | ||
386 | var vmid = me.pveSelNode.data.vmid; | |
387 | if (!vmid) { | |
388 | throw "no VM ID specified"; | |
389 | } | |
390 | ||
391 | var caps = Ext.state.Manager.get('GuiCap'); | |
392 | ||
393 | me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config'; | |
394 | ||
395 | var store = new Ext.data.Store({ | |
396 | model: 'pve-lxc-network', | |
397 | sorters: [ | |
398 | { | |
8058410f | 399 | property: 'id', |
f6710aac TL |
400 | direction: 'ASC', |
401 | }, | |
402 | ], | |
5a691a50 DM |
403 | }); |
404 | ||
405 | var sm = Ext.create('Ext.selection.RowModel', {}); | |
406 | ||
5720fafa | 407 | var remove_btn = new Proxmox.button.Button({ |
5a691a50 DM |
408 | text: gettext('Remove'), |
409 | disabled: true, | |
410 | selModel: sm, | |
411 | enableFn: function(rec) { | |
412 | return !!caps.vms['VM.Config.Network']; | |
413 | }, | |
8058410f | 414 | confirmMsg: function(rec) { |
5a691a50 DM |
415 | return Ext.String.format(gettext('Are you sure you want to remove entry {0}'), |
416 | "'" + rec.data.id + "'"); | |
417 | }, | |
418 | handler: function(btn, event, rec) { | |
e7ade592 | 419 | Proxmox.Utils.API2Request({ |
5a691a50 DM |
420 | url: me.url, |
421 | waitMsgTarget: me, | |
422 | method: 'PUT', | |
8058410f | 423 | params: { 'delete': rec.data.id, digest: me.dataCache.digest }, |
5a691a50 DM |
424 | callback: function() { |
425 | me.load(); | |
426 | }, | |
8058410f | 427 | failure: function(response, opts) { |
5a691a50 | 428 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); |
f6710aac | 429 | }, |
5a691a50 | 430 | }); |
f6710aac | 431 | }, |
5a691a50 DM |
432 | }); |
433 | ||
434 | var run_editor = function() { | |
435 | var rec = sm.getSelection()[0]; | |
436 | if (!rec) { | |
437 | return; | |
438 | } | |
439 | ||
440 | if (!caps.vms['VM.Config.Network']) { | |
441 | return false; | |
442 | } | |
443 | ||
444 | var win = Ext.create('PVE.lxc.NetworkEdit', { | |
445 | url: me.url, | |
446 | nodename: nodename, | |
447 | dataCache: me.dataCache, | |
f6710aac | 448 | ifname: rec.data.id, |
5a691a50 DM |
449 | }); |
450 | win.on('destroy', me.load, me); | |
451 | win.show(); | |
452 | }; | |
453 | ||
5720fafa | 454 | var edit_btn = new Proxmox.button.Button({ |
5a691a50 DM |
455 | text: gettext('Edit'), |
456 | selModel: sm, | |
457 | disabled: true, | |
458 | enableFn: function(rec) { | |
459 | if (!caps.vms['VM.Config.Network']) { | |
460 | return false; | |
461 | } | |
462 | return true; | |
463 | }, | |
f6710aac | 464 | handler: run_editor, |
5a691a50 DM |
465 | }); |
466 | ||
f7993618 | 467 | Ext.apply(me, { |
5a691a50 DM |
468 | store: store, |
469 | selModel: sm, | |
5a691a50 DM |
470 | tbar: [ |
471 | { | |
472 | text: gettext('Add'), | |
2517c76b | 473 | name: 'addButton', |
5a691a50 DM |
474 | disabled: !caps.vms['VM.Config.Network'], |
475 | handler: function() { | |
476 | var win = Ext.create('PVE.lxc.NetworkEdit', { | |
477 | url: me.url, | |
478 | nodename: nodename, | |
d5e771ce | 479 | isCreate: true, |
f6710aac | 480 | dataCache: me.dataCache, |
5a691a50 DM |
481 | }); |
482 | win.on('destroy', me.load, me); | |
483 | win.show(); | |
f6710aac | 484 | }, |
5a691a50 DM |
485 | }, |
486 | remove_btn, | |
f6710aac | 487 | edit_btn, |
5a691a50 DM |
488 | ], |
489 | columns: [ | |
490 | { | |
a29ff1bc | 491 | header: 'ID', |
5a691a50 | 492 | width: 50, |
f6710aac | 493 | dataIndex: 'id', |
5a691a50 DM |
494 | }, |
495 | { | |
496 | header: gettext('Name'), | |
497 | width: 80, | |
f6710aac | 498 | dataIndex: 'name', |
5a691a50 DM |
499 | }, |
500 | { | |
501 | header: gettext('Bridge'), | |
502 | width: 80, | |
f6710aac | 503 | dataIndex: 'bridge', |
5a691a50 DM |
504 | }, |
505 | { | |
506 | header: gettext('Firewall'), | |
507 | width: 80, | |
508 | dataIndex: 'firewall', | |
f6710aac | 509 | renderer: Proxmox.Utils.format_boolean, |
5a691a50 DM |
510 | }, |
511 | { | |
512 | header: gettext('VLAN Tag'), | |
513 | width: 80, | |
f6710aac | 514 | dataIndex: 'tag', |
5a691a50 DM |
515 | }, |
516 | { | |
517 | header: gettext('MAC address'), | |
518 | width: 110, | |
f6710aac | 519 | dataIndex: 'hwaddr', |
5a691a50 DM |
520 | }, |
521 | { | |
522 | header: gettext('IP address'), | |
523 | width: 150, | |
524 | dataIndex: 'ip', | |
525 | renderer: function(value, metaData, rec) { | |
526 | if (rec.data.ip && rec.data.ip6) { | |
527 | return rec.data.ip + "<br>" + rec.data.ip6; | |
528 | } else if (rec.data.ip6) { | |
529 | return rec.data.ip6; | |
530 | } else { | |
531 | return rec.data.ip; | |
532 | } | |
f6710aac | 533 | }, |
5a691a50 DM |
534 | }, |
535 | { | |
536 | header: gettext('Gateway'), | |
537 | width: 150, | |
538 | dataIndex: 'gw', | |
539 | renderer: function(value, metaData, rec) { | |
540 | if (rec.data.gw && rec.data.gw6) { | |
541 | return rec.data.gw + "<br>" + rec.data.gw6; | |
542 | } else if (rec.data.gw6) { | |
543 | return rec.data.gw6; | |
544 | } else { | |
545 | return rec.data.gw; | |
546 | } | |
f6710aac TL |
547 | }, |
548 | }, | |
5a691a50 DM |
549 | ], |
550 | listeners: { | |
4b488565 | 551 | activate: me.load, |
f6710aac TL |
552 | itemdblclick: run_editor, |
553 | }, | |
5a691a50 DM |
554 | }); |
555 | ||
556 | me.callParent(); | |
f6710aac | 557 | }, |
5a691a50 | 558 | }, function() { |
5a691a50 DM |
559 | Ext.define('pve-lxc-network', { |
560 | extend: "Ext.data.Model", | |
561 | proxy: { type: 'memory' }, | |
8058410f TL |
562 | fields: ['id', 'name', 'hwaddr', 'bridge', |
563 | 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall'], | |
5a691a50 | 564 | }); |
5a691a50 DM |
565 | }); |
566 |