]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/lxc/Network.js
gui: lxc/Network: add 'None' EmptyText for static mode
[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]').setEmptyText(
195 !!value ? Proxmox.Utils.NoneText : ""
196 );
197 me.down('field[name=ip]').setDisabled(!value);
198 me.down('field[name=gw]').setDisabled(!value);
199 }
200 }
201 },
202 {
203 xtype: 'radiofield',
204 boxLabel: 'DHCP', // do not localize
205 name: 'ipv4mode',
206 inputValue: 'dhcp',
207 checked: dhcp4,
208 margin: '0 0 0 10'
209 }
210 ]
211 },
212 {
213 xtype: 'textfield',
214 name: 'ip',
215 vtype: 'IPCIDRAddress',
216 value: cdata.ip,
217 emptyText: Proxmox.Utils.NoneText,
218 disabled: dhcp4,
219 fieldLabel: 'IPv4/CIDR' // do not localize
220 },
221 {
222 xtype: 'textfield',
223 name: 'gw',
224 value: cdata.gw,
225 vtype: 'IPAddress',
226 disabled: dhcp4,
227 fieldLabel: gettext('Gateway') + ' (IPv4)',
228 margin: '0 0 3 0' // override bottom margin to account for the menuseparator
229 },
230 {
231 xtype: 'menuseparator',
232 height: '3',
233 margin: '0'
234 },
235 {
236 layout: {
237 type: 'hbox',
238 align: 'middle'
239 },
240 border: false,
241 margin: '0 0 5 0',
242 items: [
243 {
244 xtype: 'label',
245 text: 'IPv6:' // do not localize
246 },
247 {
248 xtype: 'radiofield',
249 boxLabel: gettext('Static'),
250 name: 'ipv6mode',
251 inputValue: 'static',
252 checked: !(auto6 || dhcp6),
253 margin: '0 0 0 10',
254 listeners: {
255 change: function(cb, value) {
256 me.down('field[name=ip6]').setEmptyText(
257 !!value ? Proxmox.Utils.NoneText : ""
258 );
259 me.down('field[name=ip6]').setDisabled(!value);
260 me.down('field[name=gw6]').setDisabled(!value);
261 }
262 }
263 },
264 {
265 xtype: 'radiofield',
266 boxLabel: 'DHCP', // do not localize
267 name: 'ipv6mode',
268 inputValue: 'dhcp',
269 checked: dhcp6,
270 margin: '0 0 0 10'
271 },
272 {
273 xtype: 'radiofield',
274 boxLabel: 'SLAAC', // do not localize
275 name: 'ipv6mode',
276 inputValue: 'auto',
277 checked: auto6,
278 margin: '0 0 0 10'
279 }
280 ]
281 },
282 {
283 xtype: 'textfield',
284 name: 'ip6',
285 value: cdata.ip6,
286 emptyText: Proxmox.Utils.NoneText,
287 vtype: 'IP6CIDRAddress',
288 disabled: (dhcp6 || auto6),
289 fieldLabel: 'IPv6/CIDR' // do not localize
290 },
291 {
292 xtype: 'textfield',
293 name: 'gw6',
294 vtype: 'IP6Address',
295 value: cdata.gw6,
296 disabled: (dhcp6 || auto6),
297 fieldLabel: gettext('Gateway') + ' (IPv6)'
298 }
299 ];
300
301 me.callParent();
302 }
303 });
304
305 Ext.define('PVE.lxc.NetworkEdit', {
306 extend: 'Proxmox.window.Edit',
307
308 isAdd: true,
309
310 initComponent : function() {
311 var me = this;
312
313 if (!me.dataCache) {
314 throw "no dataCache specified";
315 }
316
317 if (!me.nodename) {
318 throw "no node name specified";
319 }
320
321 var ipanel = Ext.create('PVE.lxc.NetworkInputPanel', {
322 ifname: me.ifname,
323 nodename: me.nodename,
324 dataCache: me.dataCache,
325 isCreate: me.isCreate
326 });
327
328 Ext.apply(me, {
329 subject: gettext('Network Device') + ' (veth)',
330 digest: me.dataCache.digest,
331 items: [ ipanel ]
332 });
333
334 me.callParent();
335 }
336 });
337
338 Ext.define('PVE.lxc.NetworkView', {
339 extend: 'Ext.grid.GridPanel',
340 alias: 'widget.pveLxcNetworkView',
341
342 onlineHelp: 'pct_container_network',
343
344 dataCache: {}, // used to store result of last load
345
346 stateful: true,
347 stateId: 'grid-lxc-network',
348
349 load: function() {
350 var me = this;
351
352 Proxmox.Utils.setErrorMask(me, true);
353
354 Proxmox.Utils.API2Request({
355 url: me.url,
356 failure: function(response, opts) {
357 Proxmox.Utils.setErrorMask(me, gettext('Error') + ': ' + response.htmlStatus);
358 },
359 success: function(response, opts) {
360 Proxmox.Utils.setErrorMask(me, false);
361 var result = Ext.decode(response.responseText);
362 var data = result.data || {};
363 me.dataCache = data;
364 var records = [];
365 Ext.Object.each(data, function(key, value) {
366 if (!key.match(/^net\d+/)) {
367 return; // continue
368 }
369 var net = PVE.Parser.parseLxcNetwork(value);
370 net.id = key;
371 records.push(net);
372 });
373 me.store.loadData(records);
374 me.down('button[name=addButton]').setDisabled((records.length >= 10));
375 }
376 });
377 },
378
379 initComponent : function() {
380 var me = this;
381
382 var nodename = me.pveSelNode.data.node;
383 if (!nodename) {
384 throw "no node name specified";
385 }
386
387 var vmid = me.pveSelNode.data.vmid;
388 if (!vmid) {
389 throw "no VM ID specified";
390 }
391
392 var caps = Ext.state.Manager.get('GuiCap');
393
394 me.url = '/nodes/' + nodename + '/lxc/' + vmid + '/config';
395
396 var store = new Ext.data.Store({
397 model: 'pve-lxc-network',
398 sorters: [
399 {
400 property : 'id',
401 direction: 'ASC'
402 }
403 ]
404 });
405
406 var sm = Ext.create('Ext.selection.RowModel', {});
407
408 var remove_btn = new Proxmox.button.Button({
409 text: gettext('Remove'),
410 disabled: true,
411 selModel: sm,
412 enableFn: function(rec) {
413 return !!caps.vms['VM.Config.Network'];
414 },
415 confirmMsg: function (rec) {
416 return Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
417 "'" + rec.data.id + "'");
418 },
419 handler: function(btn, event, rec) {
420 Proxmox.Utils.API2Request({
421 url: me.url,
422 waitMsgTarget: me,
423 method: 'PUT',
424 params: { 'delete': rec.data.id, digest: me.dataCache.digest },
425 callback: function() {
426 me.load();
427 },
428 failure: function (response, opts) {
429 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
430 }
431 });
432 }
433 });
434
435 var run_editor = function() {
436 var rec = sm.getSelection()[0];
437 if (!rec) {
438 return;
439 }
440
441 if (!caps.vms['VM.Config.Network']) {
442 return false;
443 }
444
445 var win = Ext.create('PVE.lxc.NetworkEdit', {
446 url: me.url,
447 nodename: nodename,
448 dataCache: me.dataCache,
449 ifname: rec.data.id
450 });
451 win.on('destroy', me.load, me);
452 win.show();
453 };
454
455 var edit_btn = new Proxmox.button.Button({
456 text: gettext('Edit'),
457 selModel: sm,
458 disabled: true,
459 enableFn: function(rec) {
460 if (!caps.vms['VM.Config.Network']) {
461 return false;
462 }
463 return true;
464 },
465 handler: run_editor
466 });
467
468 Ext.apply(me, {
469 store: store,
470 selModel: sm,
471 tbar: [
472 {
473 text: gettext('Add'),
474 name: 'addButton',
475 disabled: !caps.vms['VM.Config.Network'],
476 handler: function() {
477 var win = Ext.create('PVE.lxc.NetworkEdit', {
478 url: me.url,
479 nodename: nodename,
480 isCreate: true,
481 dataCache: me.dataCache
482 });
483 win.on('destroy', me.load, me);
484 win.show();
485 }
486 },
487 remove_btn,
488 edit_btn
489 ],
490 columns: [
491 {
492 header: 'ID',
493 width: 50,
494 dataIndex: 'id'
495 },
496 {
497 header: gettext('Name'),
498 width: 80,
499 dataIndex: 'name'
500 },
501 {
502 header: gettext('Bridge'),
503 width: 80,
504 dataIndex: 'bridge'
505 },
506 {
507 header: gettext('Firewall'),
508 width: 80,
509 dataIndex: 'firewall',
510 renderer: Proxmox.Utils.format_boolean
511 },
512 {
513 header: gettext('VLAN Tag'),
514 width: 80,
515 dataIndex: 'tag'
516 },
517 {
518 header: gettext('MAC address'),
519 width: 110,
520 dataIndex: 'hwaddr'
521 },
522 {
523 header: gettext('IP address'),
524 width: 150,
525 dataIndex: 'ip',
526 renderer: function(value, metaData, rec) {
527 if (rec.data.ip && rec.data.ip6) {
528 return rec.data.ip + "<br>" + rec.data.ip6;
529 } else if (rec.data.ip6) {
530 return rec.data.ip6;
531 } else {
532 return rec.data.ip;
533 }
534 }
535 },
536 {
537 header: gettext('Gateway'),
538 width: 150,
539 dataIndex: 'gw',
540 renderer: function(value, metaData, rec) {
541 if (rec.data.gw && rec.data.gw6) {
542 return rec.data.gw + "<br>" + rec.data.gw6;
543 } else if (rec.data.gw6) {
544 return rec.data.gw6;
545 } else {
546 return rec.data.gw;
547 }
548 }
549 }
550 ],
551 listeners: {
552 activate: me.load,
553 itemdblclick: run_editor
554 }
555 });
556
557 me.callParent();
558 }
559 }, function() {
560
561 Ext.define('pve-lxc-network', {
562 extend: "Ext.data.Model",
563 proxy: { type: 'memory' },
564 fields: [ 'id', 'name', 'hwaddr', 'bridge',
565 'ip', 'gw', 'ip6', 'gw6', 'tag', 'firewall' ]
566 });
567
568 });
569