]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/lxc/Network.js
ui/vnc: reload IFrame-embedded xtermjs on LXC startup
[pve-manager.git] / www / manager6 / lxc / Network.js
1 Ext.define('PVE.lxc.NetworkInputPanel', {
2 extend: 'Proxmox.panel.InputPanel',
3 alias: 'widget.pveLxcNetworkInputPanel',
4
5 insideWizard: false,
6
7 onlineHelp: 'pct_container_network',
8
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;
26 if (me.isCreate) {
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
39 if (values.ipv6mode !== 'static') {
40 values.ip6 = values.ipv6mode;
41 }
42 if (values.ipv4mode !== 'static') {
43 values.ip = values.ipv4mode;
44 }
45 newdata[id] = PVE.Parser.printLxcNetwork(values);
46 return newdata;
47 },
48
49 initComponent : function() {
50 var me = this;
51
52 var cdata = {};
53
54 if (me.insideWizard) {
55 me.ifname = 'net0';
56 cdata.name = 'eth0';
57 me.dataCache = {};
58 }
59 cdata.firewall = (me.insideWizard || me.isCreate);
60
61 if (!me.dataCache) {
62 throw "no dataCache specified";
63 }
64
65 if (!me.isCreate) {
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
76 var i;
77 for (i = 0; i < 10; i++) {
78 if (me.isCreate && !me.dataCache['net'+i.toString()]) {
79 me.ifname = 'net' + i.toString();
80 break;
81 }
82 }
83
84 var idselector = {
85 xtype: 'hidden',
86 name: 'id',
87 value: me.ifname
88 };
89
90 me.column1 = [
91 idselector,
92 {
93 xtype: 'textfield',
94 name: 'name',
95 fieldLabel: gettext('Name'),
96 emptyText: '(e.g., eth0)',
97 allowBlank: false,
98 value: cdata.name,
99 validator: function(value) {
100 var result = '';
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 });
111 if (result !== '') {
112 return result;
113 }
114 // validator can return bool/string
115 /*jslint confusion:true*/
116 return true;
117 }
118 },
119 {
120 xtype: 'textfield',
121 name: 'hwaddr',
122 fieldLabel: gettext('MAC address'),
123 vtype: 'MacAddress',
124 value: cdata.hwaddr,
125 allowBlank: true,
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',
139 value: cdata.tag
140 },
141 {
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 {
152 xtype: 'proxmoxcheckbox',
153 fieldLabel: gettext('Firewall'),
154 name: 'firewall',
155 value: cdata.firewall
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',
180 items: [
181 {
182 xtype: 'label',
183 text: 'IPv4:' // do not localize
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',
201 boxLabel: 'DHCP', // do not localize
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,
215 fieldLabel: 'IPv4/CIDR' // do not localize
216 },
217 {
218 xtype: 'textfield',
219 name: 'gw',
220 value: cdata.gw,
221 vtype: 'IPAddress',
222 disabled: dhcp4,
223 fieldLabel: gettext('Gateway') + ' (IPv4)',
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',
238 items: [
239 {
240 xtype: 'label',
241 text: 'IPv6:' // do not localize
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',
259 boxLabel: 'DHCP', // do not localize
260 name: 'ipv6mode',
261 inputValue: 'dhcp',
262 checked: dhcp6,
263 margin: '0 0 0 10'
264 },
265 {
266 xtype: 'radiofield',
267 boxLabel: 'SLAAC', // do not localize
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),
281 fieldLabel: 'IPv6/CIDR' // do not localize
282 },
283 {
284 xtype: 'textfield',
285 name: 'gw6',
286 vtype: 'IP6Address',
287 value: cdata.gw6,
288 disabled: (dhcp6 || auto6),
289 fieldLabel: gettext('Gateway') + ' (IPv6)'
290 }
291 ];
292
293 me.callParent();
294 }
295 });
296
297
298 Ext.define('PVE.lxc.NetworkEdit', {
299 extend: 'Proxmox.window.Edit',
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,
318 isCreate: me.isCreate
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',
333 alias: 'widget.pveLxcNetworkView',
334
335 onlineHelp: 'pct_container_network',
336
337 dataCache: {}, // used to store result of last load
338
339 stateful: true,
340 stateId: 'grid-lxc-network',
341
342 load: function() {
343 var me = this;
344
345 Proxmox.Utils.setErrorMask(me, true);
346
347 Proxmox.Utils.API2Request({
348 url: me.url,
349 failure: function(response, opts) {
350 Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus);
351 },
352 success: function(response, opts) {
353 Proxmox.Utils.setErrorMask(me, false);
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);
367 me.down('button[name=addButton]').setDisabled((records.length >= 10));
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
401 var remove_btn = new Proxmox.button.Button({
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) {
413 Proxmox.Utils.API2Request({
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
448 var edit_btn = new Proxmox.button.Button({
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
461 Ext.apply(me, {
462 store: store,
463 selModel: sm,
464 tbar: [
465 {
466 text: gettext('Add'),
467 name: 'addButton',
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,
473 isCreate: true,
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 {
485 header: 'ID',
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',
503 renderer: Proxmox.Utils.format_boolean
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: {
545 activate: me.load,
546 itemdblclick: run_editor
547 }
548 });
549
550 me.callParent();
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