]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/qemu/HDEdit.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / qemu / HDEdit.js
1 /* 'change' property is assigned a string and then a function */
2 Ext.define('PVE.qemu.HDInputPanel', {
3 extend: 'Proxmox.panel.InputPanel',
4 alias: 'widget.pveQemuHDInputPanel',
5 onlineHelp: 'qm_hard_disk',
6
7 insideWizard: false,
8
9 unused: false, // ADD usused disk imaged
10
11 vmconfig: {}, // used to select usused disks
12
13 viewModel: {
14 data: {
15 isSCSI: false,
16 isVirtIO: false,
17 isSCSISingle: false,
18 },
19 },
20
21 controller: {
22 xclass: 'Ext.app.ViewController',
23
24 onControllerChange: function(field) {
25 let me = this;
26 let vm = this.getViewModel();
27
28 let value = field.getValue();
29 vm.set('isSCSI', value.match(/^scsi/));
30 vm.set('isVirtIO', value.match(/^virtio/));
31
32 me.fireIdChange();
33 },
34
35 fireIdChange: function() {
36 let view = this.getView();
37 view.fireEvent('diskidchange', view, view.bussel.getConfId());
38 },
39
40 control: {
41 'field[name=controller]': {
42 change: 'onControllerChange',
43 afterrender: 'onControllerChange',
44 },
45 'field[name=deviceid]': {
46 change: 'fireIdChange',
47 },
48 'field[name=scsiController]': {
49 change: function(f, value) {
50 let vm = this.getViewModel();
51 vm.set('isSCSISingle', value === 'virtio-scsi-single');
52 },
53 },
54 },
55
56 init: function(view) {
57 var vm = this.getViewModel();
58 if (view.isCreate) {
59 vm.set('isIncludedInBackup', true);
60 }
61 if (view.confid) {
62 vm.set('isSCSI', view.confid.match(/^scsi/));
63 vm.set('isVirtIO', view.confid.match(/^virtio/));
64 }
65 },
66 },
67
68 onGetValues: function(values) {
69 var me = this;
70
71 var params = {};
72 var confid = me.confid || values.controller + values.deviceid;
73
74 if (me.unused) {
75 me.drive.file = me.vmconfig[values.unusedId];
76 confid = values.controller + values.deviceid;
77 } else if (me.isCreate) {
78 if (values.hdimage) {
79 me.drive.file = values.hdimage;
80 } else {
81 me.drive.file = values.hdstorage + ":" + values.disksize;
82 }
83 me.drive.format = values.diskformat;
84 }
85
86 PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0');
87 PVE.Utils.propertyStringSet(me.drive, values.noreplicate, 'replicate', 'no');
88 PVE.Utils.propertyStringSet(me.drive, values.discard, 'discard', 'on');
89 PVE.Utils.propertyStringSet(me.drive, values.ssd, 'ssd', 'on');
90 PVE.Utils.propertyStringSet(me.drive, values.iothread, 'iothread', 'on');
91 PVE.Utils.propertyStringSet(me.drive, values.readOnly, 'ro', 'on');
92 PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache');
93 PVE.Utils.propertyStringSet(me.drive, values.aio, 'aio');
94
95 ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr'].forEach(name => {
96 let burst_name = `${name}_max`;
97 PVE.Utils.propertyStringSet(me.drive, values[name], name);
98 PVE.Utils.propertyStringSet(me.drive, values[burst_name], burst_name);
99 });
100
101 params[confid] = PVE.Parser.printQemuDrive(me.drive);
102
103 return params;
104 },
105
106 updateVMConfig: function(vmconfig) {
107 var me = this;
108 me.vmconfig = vmconfig;
109 me.bussel?.updateVMConfig(vmconfig);
110 },
111
112 setVMConfig: function(vmconfig) {
113 var me = this;
114
115 me.vmconfig = vmconfig;
116
117 if (me.bussel) {
118 me.bussel.setVMConfig(vmconfig);
119 me.scsiController.setValue(vmconfig.scsihw);
120 }
121 if (me.unusedDisks) {
122 var disklist = [];
123 Ext.Object.each(vmconfig, function(key, value) {
124 if (key.match(/^unused\d+$/)) {
125 disklist.push([key, value]);
126 }
127 });
128 me.unusedDisks.store.loadData(disklist);
129 me.unusedDisks.setValue(me.confid);
130 }
131 },
132
133 setDrive: function(drive) {
134 var me = this;
135
136 me.drive = drive;
137
138 var values = {};
139 var match = drive.file.match(/^([^:]+):/);
140 if (match) {
141 values.hdstorage = match[1];
142 }
143
144 values.hdimage = drive.file;
145 values.backup = PVE.Parser.parseBoolean(drive.backup, 1);
146 values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1);
147 values.diskformat = drive.format || 'raw';
148 values.cache = drive.cache || '__default__';
149 values.discard = drive.discard === 'on';
150 values.ssd = PVE.Parser.parseBoolean(drive.ssd);
151 values.iothread = PVE.Parser.parseBoolean(drive.iothread);
152 values.readOnly = PVE.Parser.parseBoolean(drive.ro);
153 values.aio = drive.aio || '__default__';
154
155 values.mbps_rd = drive.mbps_rd;
156 values.mbps_wr = drive.mbps_wr;
157 values.iops_rd = drive.iops_rd;
158 values.iops_wr = drive.iops_wr;
159 values.mbps_rd_max = drive.mbps_rd_max;
160 values.mbps_wr_max = drive.mbps_wr_max;
161 values.iops_rd_max = drive.iops_rd_max;
162 values.iops_wr_max = drive.iops_wr_max;
163
164 me.setValues(values);
165 },
166
167 setNodename: function(nodename) {
168 var me = this;
169 me.down('#hdstorage').setNodename(nodename);
170 me.down('#hdimage').setStorage(undefined, nodename);
171 },
172
173 hasAdvanced: true,
174
175 initComponent: function() {
176 var me = this;
177
178 me.drive = {};
179
180 let column1 = [];
181 let column2 = [];
182
183 let advancedColumn1 = [];
184 let advancedColumn2 = [];
185
186 if (!me.confid || me.unused) {
187 me.bussel = Ext.create('PVE.form.ControllerSelector', {
188 vmconfig: me.vmconfig,
189 selectFree: true,
190 });
191 column1.push(me.bussel);
192
193 me.scsiController = Ext.create('Ext.form.field.Display', {
194 fieldLabel: gettext('SCSI Controller'),
195 reference: 'scsiController',
196 name: 'scsiController',
197 bind: me.insideWizard ? {
198 value: '{current.scsihw}',
199 visible: '{isSCSI}',
200 } : {
201 visible: '{isSCSI}',
202 },
203 renderer: PVE.Utils.render_scsihw,
204 submitValue: false,
205 hidden: true,
206 });
207 column1.push(me.scsiController);
208 }
209
210 if (me.unused) {
211 me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', {
212 name: 'unusedId',
213 fieldLabel: gettext('Disk image'),
214 matchFieldWidth: false,
215 listConfig: {
216 width: 350,
217 },
218 data: [],
219 allowBlank: false,
220 });
221 column1.push(me.unusedDisks);
222 } else if (me.isCreate) {
223 column1.push({
224 xtype: 'pveDiskStorageSelector',
225 storageContent: 'images',
226 name: 'disk',
227 nodename: me.nodename,
228 autoSelect: me.insideWizard,
229 });
230 } else {
231 column1.push({
232 xtype: 'textfield',
233 disabled: true,
234 submitValue: false,
235 fieldLabel: gettext('Disk image'),
236 name: 'hdimage',
237 });
238 }
239
240 column2.push(
241 {
242 xtype: 'CacheTypeSelector',
243 name: 'cache',
244 value: '__default__',
245 fieldLabel: gettext('Cache'),
246 },
247 {
248 xtype: 'proxmoxcheckbox',
249 fieldLabel: gettext('Discard'),
250 reference: 'discard',
251 name: 'discard',
252 },
253 {
254 xtype: 'proxmoxcheckbox',
255 name: 'iothread',
256 fieldLabel: 'IO thread',
257 clearOnDisable: true,
258 bind: me.insideWizard || me.isCreate ? {
259 disabled: '{!isVirtIO && !isSCSI}',
260 // Checkbox.setValue handles Arrays in a different way, therefore cast to bool
261 value: '{!!isVirtIO || (isSCSI && isSCSISingle)}',
262 } : {
263 disabled: '{!isVirtIO && !isSCSI}',
264 },
265 },
266 );
267
268 advancedColumn1.push(
269 {
270 xtype: 'proxmoxcheckbox',
271 fieldLabel: gettext('SSD emulation'),
272 name: 'ssd',
273 clearOnDisable: true,
274 bind: {
275 disabled: '{isVirtIO}',
276 },
277 },
278 {
279 xtype: 'proxmoxcheckbox',
280 name: 'readOnly', // `ro` in the config, we map in get/set values
281 defaultValue: 0,
282 fieldLabel: gettext('Read-only'),
283 clearOnDisable: true,
284 bind: {
285 disabled: '{!isVirtIO && !isSCSI}',
286 },
287 },
288 );
289
290 advancedColumn2.push(
291 {
292 xtype: 'proxmoxcheckbox',
293 fieldLabel: gettext('Backup'),
294 autoEl: {
295 tag: 'div',
296 'data-qtip': gettext('Include volume in backup job'),
297 },
298 name: 'backup',
299 bind: {
300 value: '{isIncludedInBackup}',
301 },
302 },
303 {
304 xtype: 'proxmoxcheckbox',
305 fieldLabel: gettext('Skip replication'),
306 name: 'noreplicate',
307 },
308 {
309 xtype: 'proxmoxKVComboBox',
310 name: 'aio',
311 fieldLabel: gettext('Async IO'),
312 allowBlank: false,
313 value: '__default__',
314 comboItems: [
315 ['__default__', Proxmox.Utils.defaultText + ' (io_uring)'],
316 ['io_uring', 'io_uring'],
317 ['native', 'native'],
318 ['threads', 'threads'],
319 ],
320 },
321 );
322
323 let labelWidth = 140;
324
325 let bwColumn1 = [
326 {
327 xtype: 'numberfield',
328 name: 'mbps_rd',
329 minValue: 1,
330 step: 1,
331 fieldLabel: gettext('Read limit') + ' (MB/s)',
332 labelWidth: labelWidth,
333 emptyText: gettext('unlimited'),
334 },
335 {
336 xtype: 'numberfield',
337 name: 'mbps_wr',
338 minValue: 1,
339 step: 1,
340 fieldLabel: gettext('Write limit') + ' (MB/s)',
341 labelWidth: labelWidth,
342 emptyText: gettext('unlimited'),
343 },
344 {
345 xtype: 'proxmoxintegerfield',
346 name: 'iops_rd',
347 minValue: 10,
348 step: 10,
349 fieldLabel: gettext('Read limit') + ' (ops/s)',
350 labelWidth: labelWidth,
351 emptyText: gettext('unlimited'),
352 },
353 {
354 xtype: 'proxmoxintegerfield',
355 name: 'iops_wr',
356 minValue: 10,
357 step: 10,
358 fieldLabel: gettext('Write limit') + ' (ops/s)',
359 labelWidth: labelWidth,
360 emptyText: gettext('unlimited'),
361 },
362 ];
363
364 let bwColumn2 = [
365 {
366 xtype: 'numberfield',
367 name: 'mbps_rd_max',
368 minValue: 1,
369 step: 1,
370 fieldLabel: gettext('Read max burst') + ' (MB)',
371 labelWidth: labelWidth,
372 emptyText: gettext('default'),
373 },
374 {
375 xtype: 'numberfield',
376 name: 'mbps_wr_max',
377 minValue: 1,
378 step: 1,
379 fieldLabel: gettext('Write max burst') + ' (MB)',
380 labelWidth: labelWidth,
381 emptyText: gettext('default'),
382 },
383 {
384 xtype: 'proxmoxintegerfield',
385 name: 'iops_rd_max',
386 minValue: 10,
387 step: 10,
388 fieldLabel: gettext('Read max burst') + ' (ops)',
389 labelWidth: labelWidth,
390 emptyText: gettext('default'),
391 },
392 {
393 xtype: 'proxmoxintegerfield',
394 name: 'iops_wr_max',
395 minValue: 10,
396 step: 10,
397 fieldLabel: gettext('Write max burst') + ' (ops)',
398 labelWidth: labelWidth,
399 emptyText: gettext('default'),
400 },
401 ];
402
403 me.items = [
404 {
405 xtype: 'tabpanel',
406 plain: true,
407 bodyPadding: 10,
408 border: 0,
409 items: [
410 {
411 title: gettext('Disk'),
412 xtype: 'inputpanel',
413 reference: 'diskpanel',
414 column1,
415 column2,
416 advancedColumn1,
417 advancedColumn2,
418 showAdvanced: me.showAdvanced,
419 getValues: () => ({}),
420 },
421 {
422 title: gettext('Bandwidth'),
423 xtype: 'inputpanel',
424 reference: 'bwpanel',
425 column1: bwColumn1,
426 column2: bwColumn2,
427 showAdvanced: me.showAdvanced,
428 getValues: () => ({}),
429 },
430 ],
431 },
432 ];
433
434 me.callParent();
435 },
436
437 setAdvancedVisible: function(visible) {
438 this.lookup('diskpanel').setAdvancedVisible(visible);
439 this.lookup('bwpanel').setAdvancedVisible(visible);
440 },
441 });
442
443 Ext.define('PVE.qemu.HDEdit', {
444 extend: 'Proxmox.window.Edit',
445
446 isAdd: true,
447
448 backgroundDelay: 5,
449
450 width: 600,
451 bodyPadding: 0,
452
453 initComponent: function() {
454 var me = this;
455
456 var nodename = me.pveSelNode.data.node;
457 if (!nodename) {
458 throw "no node name specified";
459 }
460
461 var unused = me.confid && me.confid.match(/^unused\d+$/);
462
463 me.isCreate = me.confid ? unused : true;
464
465 var ipanel = Ext.create('PVE.qemu.HDInputPanel', {
466 confid: me.confid,
467 nodename: nodename,
468 unused: unused,
469 isCreate: me.isCreate,
470 });
471
472 if (unused) {
473 me.subject = gettext('Unused Disk');
474 } else if (me.isCreate) {
475 me.subject = gettext('Hard Disk');
476 } else {
477 me.subject = gettext('Hard Disk') + ' (' + me.confid + ')';
478 }
479
480 me.items = [ipanel];
481
482 me.callParent();
483 /* 'data' is assigned an empty array in same file, and here we
484 * use it like an object
485 */
486 me.load({
487 success: function(response, options) {
488 ipanel.setVMConfig(response.result.data);
489 if (me.confid) {
490 var value = response.result.data[me.confid];
491 var drive = PVE.Parser.parseQemuDrive(me.confid, value);
492 if (!drive) {
493 Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options');
494 me.close();
495 return;
496 }
497 ipanel.setDrive(drive);
498 me.isValid(); // trigger validation
499 }
500 },
501 });
502 },
503 });