]> git.proxmox.com Git - pve-manager.git/blob - www/manager/lxc/Network.js
933a22d1a88e8d31d704e18123ba0c860b2bec4b
[pve-manager.git] / www / manager / lxc / Network.js
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,
129 allowBlank: true,
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
451 Ext.applyIf(me, {
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