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