]> git.proxmox.com Git - pve-manager.git/blob - www/manager/qemu/HardwareView.js
89f2f4e53cf9d9a1720ee979a814a496205ee2ad
[pve-manager.git] / www / manager / qemu / HardwareView.js
1 // fixme: howto avoid jslint type confusion?
2 /*jslint confusion: true */
3 Ext.define('PVE.qemu.HardwareView', {
4 extend: 'PVE.grid.PendingObjectGrid',
5 alias: ['widget.PVE.qemu.HardwareView'],
6
7 renderKey: function(key, metaData, rec, rowIndex, colIndex, store) {
8 var me = this;
9 var rows = me.rows;
10 var rowdef = rows[key] || {};
11
12 metaData.tdAttr = "valign=middle";
13
14 if (rowdef.tdCls) {
15 metaData.tdCls = rowdef.tdCls;
16 if (rowdef.tdCls == 'pve-itype-icon-storage') {
17 var value = me.getObjectValue(key, '', true);
18 if (value.match(/media=cdrom/)) {
19 metaData.tdCls = 'pve-itype-icon-cdrom';
20 return rowdef.cdheader;
21 }
22 }
23 }
24 return rowdef.header || key;
25 },
26
27 initComponent : function() {
28 var me = this;
29 var i, confid;
30
31 var nodename = me.pveSelNode.data.node;
32 if (!nodename) {
33 throw "no node name specified";
34 }
35
36 var vmid = me.pveSelNode.data.vmid;
37 if (!vmid) {
38 throw "no VM ID specified";
39 }
40
41 var caps = Ext.state.Manager.get('GuiCap');
42
43 var rows = {
44 memory: {
45 header: gettext('Memory'),
46 editor: caps.vms['VM.Config.Memory'] ? 'PVE.qemu.MemoryEdit' : undefined,
47 never_delete: true,
48 defaultValue: 512,
49 tdCls: 'pve-itype-icon-memory',
50 renderer: function(value, metaData, record) {
51 var balloon = me.getObjectValue('balloon');
52 if (balloon) {
53 return PVE.Utils.format_size(balloon*1024*1024) + "/" +
54 PVE.Utils.format_size(value*1024*1024);
55
56 }
57 return PVE.Utils.format_size(value*1024*1024);
58 }
59 },
60 sockets: {
61 header: gettext('Processors'),
62 never_delete: true,
63 editor: (caps.vms['VM.Config.CPU'] || caps.vms['VM.Config.HWType']) ?
64 'PVE.qemu.ProcessorEdit' : undefined,
65 tdCls: 'pve-itype-icon-processor',
66 defaultValue: 1,
67 multiKey: ['sockets', 'cpu', 'cores', 'numa', 'vcpus', 'cpulimit', 'cpuunits'],
68 renderer: function(value, metaData, record, rowIndex, colIndex, store, pending) {
69
70 var sockets = me.getObjectValue('sockets', 1, pending);
71 var model = me.getObjectValue('cpu', undefined, pending);
72 var cores = me.getObjectValue('cores', 1, pending);
73 var numa = me.getObjectValue('numa', undefined, pending);
74 var vcpus = me.getObjectValue('vcpus', undefined, pending);
75 var cpulimit = me.getObjectValue('cpulimit', undefined, pending);
76 var cpuunits = me.getObjectValue('cpuunits', undefined, pending);
77
78 var res = (sockets*cores) + ' (' + sockets + ' sockets, ' + cores + ' cores)';
79
80 if (model) {
81 res += ' [' + model + ']';
82 }
83
84 if (numa) {
85 res += ' [numa=' + numa +']';
86 }
87
88 if (vcpus) {
89 res += ' [vcpus=' + vcpus +']';
90 }
91
92 if (cpulimit) {
93 res += ' [cpulimit=' + cpulimit +']';
94 }
95
96 if (cpuunits) {
97 res += ' [cpuunits=' + cpuunits +']';
98 }
99
100 return res;
101 }
102 },
103 keyboard: {
104 header: gettext('Keyboard Layout'),
105 never_delete: true,
106 editor: caps.vms['VM.Config.Options'] ? 'PVE.qemu.KeyboardEdit' : undefined,
107 tdCls: 'pve-itype-icon-keyboard',
108 defaultValue: '',
109 renderer: PVE.Utils.render_kvm_language
110 },
111 vga: {
112 header: gettext('Display'),
113 editor: caps.vms['VM.Config.HWType'] ? 'PVE.qemu.DisplayEdit' : undefined,
114 never_delete: true,
115 tdCls: 'pve-itype-icon-display',
116 defaultValue: '',
117 renderer: PVE.Utils.render_kvm_vga_driver
118 },
119 cores: {
120 visible: false
121 },
122 cpu: {
123 visible: false
124 },
125 numa: {
126 visible: false
127 },
128 balloon: {
129 visible: false
130 },
131 hotplug: {
132 visible: false
133 },
134 vcpus: {
135 visible: false
136 },
137 cpuunits: {
138 visible: false
139 },
140 cpulimit: {
141 visible: false
142 }
143
144 };
145
146 for (i = 0; i < 4; i++) {
147 confid = "ide" + i;
148 rows[confid] = {
149 group: 1,
150 tdCls: 'pve-itype-icon-storage',
151 editor: 'PVE.qemu.HDEdit',
152 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
153 header: gettext('Hard Disk') + ' (' + confid +')',
154 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
155 };
156 }
157 for (i = 0; i < 6; i++) {
158 confid = "sata" + i;
159 rows[confid] = {
160 group: 1,
161 tdCls: 'pve-itype-icon-storage',
162 editor: 'PVE.qemu.HDEdit',
163 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
164 header: gettext('Hard Disk') + ' (' + confid +')',
165 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
166 };
167 }
168 for (i = 0; i < 16; i++) {
169 confid = "scsi" + i;
170 rows[confid] = {
171 group: 1,
172 tdCls: 'pve-itype-icon-storage',
173 editor: 'PVE.qemu.HDEdit',
174 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
175 header: gettext('Hard Disk') + ' (' + confid +')',
176 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
177 };
178 }
179 for (i = 0; i < 16; i++) {
180 confid = "virtio" + i;
181 rows[confid] = {
182 group: 1,
183 tdCls: 'pve-itype-icon-storage',
184 editor: 'PVE.qemu.HDEdit',
185 never_delete: caps.vms['VM.Config.Disk'] ? false : true,
186 header: gettext('Hard Disk') + ' (' + confid +')',
187 cdheader: gettext('CD/DVD Drive') + ' (' + confid +')'
188 };
189 }
190 for (i = 0; i < 32; i++) {
191 confid = "net" + i;
192 rows[confid] = {
193 group: 2,
194 tdCls: 'pve-itype-icon-network',
195 editor: caps.vms['VM.Config.Network'] ? 'PVE.qemu.NetworkEdit' : undefined,
196 never_delete: caps.vms['VM.Config.Network'] ? false : true,
197 header: gettext('Network Device') + ' (' + confid +')'
198 };
199 }
200 for (i = 0; i < 8; i++) {
201 rows["unused" + i] = {
202 group: 3,
203 tdCls: 'pve-itype-icon-storage',
204 editor: caps.vms['VM.Config.Disk'] ? 'PVE.qemu.HDEdit' : undefined,
205 header: gettext('Unused Disk') + ' ' + i
206 };
207 }
208
209 var sorterFn = function(rec1, rec2) {
210 var v1 = rec1.data.key;
211 var v2 = rec2.data.key;
212 var g1 = rows[v1].group || 0;
213 var g2 = rows[v2].group || 0;
214
215 return (g1 !== g2) ?
216 (g1 > g2 ? 1 : -1) : (v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
217 };
218
219 var reload = function() {
220 me.rstore.load();
221 };
222
223 var baseurl = 'nodes/' + nodename + '/qemu/' + vmid + '/config';
224
225 var sm = Ext.create('Ext.selection.RowModel', {});
226
227 var run_editor = function() {
228 var rec = sm.getSelection()[0];
229 if (!rec) {
230 return;
231 }
232
233 var rowdef = rows[rec.data.key];
234 if (!rowdef.editor) {
235 return;
236 }
237
238 var editor = rowdef.editor;
239 if (rowdef.tdCls == 'pve-itype-icon-storage') {
240 var value = me.getObjectValue(rec.data.key, '', true);
241 if (value.match(/media=cdrom/)) {
242 editor = 'PVE.qemu.CDEdit';
243 }
244 }
245
246 var win;
247
248 if (Ext.isString(editor)) {
249 win = Ext.create(editor, {
250 pveSelNode: me.pveSelNode,
251 confid: rec.data.key,
252 url: '/api2/extjs/' + baseurl
253 });
254 } else {
255 var config = Ext.apply({
256 pveSelNode: me.pveSelNode,
257 confid: rec.data.key,
258 url: '/api2/extjs/' + baseurl
259 }, rowdef.editor);
260 win = Ext.createWidget(rowdef.editor.xtype, config);
261 win.load();
262 }
263
264 win.show();
265 win.on('destroy', reload);
266 };
267
268 var run_diskthrottle = function() {
269 var rec = sm.getSelection()[0];
270 if (!rec) {
271 return;
272 }
273
274 var win = Ext.create('PVE.qemu.HDThrottle', {
275 pveSelNode: me.pveSelNode,
276 confid: rec.data.key,
277 url: '/api2/extjs/' + baseurl
278 });
279
280 win.show();
281 win.on('destroy', reload);
282 };
283
284 var run_resize = function() {
285 var rec = sm.getSelection()[0];
286 if (!rec) {
287 return;
288 }
289
290 var win = Ext.create('PVE.window.HDResize', {
291 disk: rec.data.key,
292 nodename: nodename,
293 vmid: vmid
294 });
295
296 win.show();
297
298 win.on('destroy', reload);
299 };
300
301 var run_cpuoptions = function() {
302 var sockets = me.getObjectValue('sockets', 1);
303 var cores = me.getObjectValue('cores', 1);
304
305 var win = Ext.create('PVE.qemu.CPUOptions', {
306 maxvcpus: sockets * cores,
307 vmid: vmid,
308 pveSelNode: me.pveSelNode,
309 url: '/api2/extjs/' + baseurl
310 });
311
312 win.show();
313
314 win.on('destroy', reload);
315 };
316
317 var run_move = function() {
318 var rec = sm.getSelection()[0];
319 if (!rec) {
320 return;
321 }
322
323 var win = Ext.create('PVE.window.HDMove', {
324 disk: rec.data.key,
325 nodename: nodename,
326 vmid: vmid
327 });
328
329 win.show();
330
331 win.on('destroy', reload);
332 };
333
334 var edit_btn = new PVE.button.Button({
335 text: gettext('Edit'),
336 selModel: sm,
337 disabled: true,
338 handler: run_editor
339 });
340
341 var resize_btn = new PVE.button.Button({
342 text: gettext('Resize disk'),
343 selModel: sm,
344 disabled: true,
345 handler: run_resize
346 });
347
348 var move_btn = new PVE.button.Button({
349 text: gettext('Move disk'),
350 selModel: sm,
351 disabled: true,
352 handler: run_move
353 });
354
355 var diskthrottle_btn = new PVE.button.Button({
356 text: gettext('Disk Throttle'),
357 selModel: sm,
358 disabled: true,
359 handler: run_diskthrottle
360 });
361
362 var cpuoptions_btn = new Ext.Button({
363 text: gettext('CPU options'),
364 handler: run_cpuoptions
365 });
366
367 var remove_btn = new PVE.button.Button({
368 text: gettext('Remove'),
369 selModel: sm,
370 disabled: true,
371 dangerous: true,
372 confirmMsg: function(rec) {
373 var msg = Ext.String.format(gettext('Are you sure you want to remove entry {0}'),
374 "'" + me.renderKey(rec.data.key, {}, rec) + "'");
375 if (rec.data.key.match(/^unused\d+$/)) {
376 msg += " " + gettext('This will permanently erase all image data.');
377 }
378
379 return msg;
380 },
381 handler: function(b, e, rec) {
382 PVE.Utils.API2Request({
383 url: '/api2/extjs/' + baseurl,
384 waitMsgTarget: me,
385 method: 'PUT',
386 params: {
387 'delete': rec.data.key
388 },
389 callback: function() {
390 reload();
391 },
392 failure: function (response, opts) {
393 Ext.Msg.alert('Error', response.htmlStatus);
394 }
395 });
396 }
397 });
398
399 var revert_btn = new PVE.button.Button({
400 text: gettext('Revert'),
401 selModel: sm,
402 disabled: true,
403 handler: function(b, e, rec) {
404 var rowdef = me.rows[rec.data.key] || {};
405 var keys = rowdef.multiKey || [ rec.data.key ];
406 var revert = keys.join(',');
407 PVE.Utils.API2Request({
408 url: '/api2/extjs/' + baseurl,
409 waitMsgTarget: me,
410 method: 'PUT',
411 params: {
412 'revert': revert
413 },
414 callback: function() {
415 reload();
416 },
417 failure: function (response, opts) {
418 Ext.Msg.alert('Error',response.htmlStatus);
419 }
420 });
421 }
422 });
423
424 var set_button_status = function() {
425 var sm = me.getSelectionModel();
426 var rec = sm.getSelection()[0];
427
428 if (!rec) {
429 remove_btn.disable();
430 edit_btn.disable();
431 resize_btn.disable();
432 move_btn.disable();
433 diskthrottle_btn.disable();
434 revert_btn.disable();
435 return;
436 }
437 var key = rec.data.key;
438 var value = rec.data.value;
439 var rowdef = rows[key];
440
441 var pending = rec.data['delete'] || me.hasPendingChanges(key);
442 var isDisk = !key.match(/^unused\d+/) &&
443 (rowdef.tdCls == 'pve-itype-icon-storage' && !value.match(/media=cdrom/));
444
445 remove_btn.setDisabled(rec.data['delete'] || (rowdef.never_delete === true));
446
447 edit_btn.setDisabled(rec.data['delete'] || !rowdef.editor);
448
449 resize_btn.setDisabled(pending || !isDisk);
450
451 move_btn.setDisabled(pending || !isDisk);
452
453 diskthrottle_btn.setDisabled(pending || !isDisk);
454
455 revert_btn.setDisabled(!pending);
456
457 };
458
459 Ext.applyIf(me, {
460 url: '/api2/json/' + 'nodes/' + nodename + '/qemu/' + vmid + '/pending',
461 interval: 5000,
462 selModel: sm,
463 cwidth1: 170,
464 tbar: [
465 {
466 text: gettext('Add'),
467 menu: new Ext.menu.Menu({
468 items: [
469 {
470 text: gettext('Hard Disk'),
471 iconCls: 'pve-itype-icon-storage',
472 disabled: !caps.vms['VM.Config.Disk'],
473 handler: function() {
474 var win = Ext.create('PVE.qemu.HDEdit', {
475 url: '/api2/extjs/' + baseurl,
476 pveSelNode: me.pveSelNode
477 });
478 win.on('destroy', reload);
479 win.show();
480 }
481 },
482 {
483 text: gettext('CD/DVD Drive'),
484 iconCls: 'pve-itype-icon-cdrom',
485 disabled: !caps.vms['VM.Config.Disk'],
486 handler: function() {
487 var win = Ext.create('PVE.qemu.CDEdit', {
488 url: '/api2/extjs/' + baseurl,
489 pveSelNode: me.pveSelNode
490 });
491 win.on('destroy', reload);
492 win.show();
493 }
494 },
495 {
496 text: gettext('Network Device'),
497 iconCls: 'pve-itype-icon-network',
498 disabled: !caps.vms['VM.Config.Network'],
499 handler: function() {
500 var win = Ext.create('PVE.qemu.NetworkEdit', {
501 url: '/api2/extjs/' + baseurl,
502 pveSelNode: me.pveSelNode
503 });
504 win.on('destroy', reload);
505 win.show();
506 }
507 }
508 ]
509 })
510 },
511 remove_btn,
512 edit_btn,
513 resize_btn,
514 move_btn,
515 diskthrottle_btn,
516 cpuoptions_btn,
517 revert_btn
518 ],
519 rows: rows,
520 sorterFn: sorterFn,
521 listeners: {
522 itemdblclick: run_editor,
523 selectionchange: set_button_status
524 }
525 });
526
527 me.callParent();
528
529 me.on('show', me.rstore.startUpdate);
530 me.on('hide', me.rstore.stopUpdate);
531 me.on('destroy', me.rstore.stopUpdate);
532
533 me.rstore.on('datachanged', function() {
534 set_button_status();
535 });
536 }
537 });