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