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