]>
Commit | Line | Data |
---|---|---|
d5e771ce | 1 | /* 'change' property is assigned a string and then a function */ |
df15b3da | 2 | Ext.define('PVE.qemu.HDInputPanel', { |
ef4ef788 | 3 | extend: 'Proxmox.panel.InputPanel', |
85806e47 | 4 | alias: 'widget.pveQemuHDInputPanel', |
c8802a60 | 5 | onlineHelp: 'qm_hard_disk', |
df15b3da DM |
6 | |
7 | insideWizard: false, | |
8 | ||
9 | unused: false, // ADD usused disk imaged | |
10 | ||
11 | vmconfig: {}, // used to select usused disks | |
12 | ||
3f543d7f TL |
13 | viewModel: { |
14 | data: { | |
0fe712de | 15 | isSCSI: false, |
3f543d7f | 16 | isVirtIO: false, |
94b5e53a | 17 | isSCSISingle: false, |
3f543d7f TL |
18 | }, |
19 | }, | |
8e1df0ac | 20 | |
180d7ef5 | 21 | controller: { |
180d7ef5 DM |
22 | xclass: 'Ext.app.ViewController', |
23 | ||
24 | onControllerChange: function(field) { | |
ef82a0b1 | 25 | let me = this; |
3f543d7f | 26 | let vm = this.getViewModel(); |
f9261fde | 27 | |
3f543d7f TL |
28 | let value = field.getValue(); |
29 | vm.set('isSCSI', value.match(/^scsi/)); | |
30 | vm.set('isVirtIO', value.match(/^virtio/)); | |
9d0fc155 DC |
31 | |
32 | me.fireIdChange(); | |
33 | }, | |
34 | ||
35 | fireIdChange: function() { | |
36 | let view = this.getView(); | |
37 | view.fireEvent('diskidchange', view, view.bussel.getConfId()); | |
180d7ef5 DM |
38 | }, |
39 | ||
40 | control: { | |
9cac6b75 | 41 | 'field[name=controller]': { |
180d7ef5 | 42 | change: 'onControllerChange', |
f6710aac | 43 | afterrender: 'onControllerChange', |
9cac6b75 | 44 | }, |
9d0fc155 DC |
45 | 'field[name=deviceid]': { |
46 | change: 'fireIdChange', | |
47 | }, | |
94b5e53a | 48 | 'field[name=scsiController]': { |
2414ef15 | 49 | change: function(f, value) { |
94b5e53a MH |
50 | let vm = this.getViewModel(); |
51 | vm.set('isSCSISingle', value === 'virtio-scsi-single'); | |
f6710aac TL |
52 | }, |
53 | }, | |
8e1df0ac AL |
54 | }, |
55 | ||
56 | init: function(view) { | |
57 | var vm = this.getViewModel(); | |
58 | if (view.isCreate) { | |
59 | vm.set('isIncludedInBackup', true); | |
60 | } | |
3f543d7f TL |
61 | if (view.confid) { |
62 | vm.set('isSCSI', view.confid.match(/^scsi/)); | |
63 | vm.set('isVirtIO', view.confid.match(/^virtio/)); | |
64 | } | |
f6710aac | 65 | }, |
180d7ef5 DM |
66 | }, |
67 | ||
df15b3da DM |
68 | onGetValues: function(values) { |
69 | var me = this; | |
70 | ||
9a8a5735 | 71 | var params = {}; |
53e3ea84 | 72 | var confid = me.confid || values.controller + values.deviceid; |
7a6e0635 | 73 | |
df15b3da DM |
74 | if (me.unused) { |
75 | me.drive.file = me.vmconfig[values.unusedId]; | |
76 | confid = values.controller + values.deviceid; | |
d5e771ce | 77 | } else if (me.isCreate) { |
df15b3da DM |
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 | } | |
7a6e0635 | 85 | |
87178934 | 86 | PVE.Utils.propertyStringSet(me.drive, !values.backup, 'backup', '0'); |
e4e36020 DC |
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'); | |
e4d84378 | 91 | PVE.Utils.propertyStringSet(me.drive, values.readOnly, 'ro', 'on'); |
e4e36020 | 92 | PVE.Utils.propertyStringSet(me.drive, values.cache, 'cache'); |
f68d8b2c | 93 | PVE.Utils.propertyStringSet(me.drive, values.aio, 'aio'); |
df15b3da | 94 | |
731c3253 TL |
95 | ['mbps_rd', 'mbps_wr', 'iops_rd', 'iops_wr'].forEach(name => { |
96 | let burst_name = `${name}_max`; | |
e4e36020 DC |
97 | PVE.Utils.propertyStringSet(me.drive, values[name], name); |
98 | PVE.Utils.propertyStringSet(me.drive, values[burst_name], burst_name); | |
731c3253 | 99 | }); |
c2233c8f | 100 | |
df15b3da | 101 | params[confid] = PVE.Parser.printQemuDrive(me.drive); |
7a6e0635 TL |
102 | |
103 | return params; | |
df15b3da DM |
104 | }, |
105 | ||
9dbc4dfa DC |
106 | updateVMConfig: function(vmconfig) { |
107 | var me = this; | |
108 | me.vmconfig = vmconfig; | |
109 | me.bussel?.updateVMConfig(vmconfig); | |
110 | }, | |
111 | ||
df15b3da DM |
112 | setVMConfig: function(vmconfig) { |
113 | var me = this; | |
114 | ||
115 | me.vmconfig = vmconfig; | |
116 | ||
117 | if (me.bussel) { | |
7206af4b | 118 | me.bussel.setVMConfig(vmconfig); |
42bd6556 | 119 | me.scsiController.setValue(vmconfig.scsihw); |
df15b3da DM |
120 | } |
121 | if (me.unusedDisks) { | |
7a6e0635 | 122 | var disklist = []; |
df15b3da DM |
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; | |
87178934 | 145 | values.backup = PVE.Parser.parseBoolean(drive.backup, 1); |
1b6f87ab | 146 | values.noreplicate = !PVE.Parser.parseBoolean(drive.replicate, 1); |
df15b3da | 147 | values.diskformat = drive.format || 'raw'; |
ac7db8f7 | 148 | values.cache = drive.cache || '__default__'; |
53e3ea84 | 149 | values.discard = drive.discard === 'on'; |
f9261fde | 150 | values.ssd = PVE.Parser.parseBoolean(drive.ssd); |
df15b3da | 151 | values.iothread = PVE.Parser.parseBoolean(drive.iothread); |
e4d84378 | 152 | values.readOnly = PVE.Parser.parseBoolean(drive.ro); |
f68d8b2c | 153 | values.aio = drive.aio || '__default__'; |
df15b3da | 154 | |
c2233c8f DC |
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 | ||
df15b3da DM |
164 | me.setValues(values); |
165 | }, | |
166 | ||
167 | setNodename: function(nodename) { | |
168 | var me = this; | |
771aec96 DC |
169 | me.down('#hdstorage').setNodename(nodename); |
170 | me.down('#hdimage').setStorage(undefined, nodename); | |
df15b3da DM |
171 | }, |
172 | ||
74c0b9a9 DC |
173 | hasAdvanced: true, |
174 | ||
8058410f | 175 | initComponent: function() { |
df15b3da DM |
176 | var me = this; |
177 | ||
178 | me.drive = {}; | |
179 | ||
74c0b9a9 DC |
180 | let column1 = []; |
181 | let column2 = []; | |
df15b3da | 182 | |
74c0b9a9 DC |
183 | let advancedColumn1 = []; |
184 | let advancedColumn2 = []; | |
546d09c2 | 185 | |
df15b3da | 186 | if (!me.confid || me.unused) { |
42902182 | 187 | me.bussel = Ext.create('PVE.form.ControllerSelector', { |
9dbc4dfa DC |
188 | vmconfig: me.vmconfig, |
189 | selectFree: true, | |
df15b3da | 190 | }); |
74c0b9a9 | 191 | column1.push(me.bussel); |
2414ef15 | 192 | |
42bd6556 EK |
193 | me.scsiController = Ext.create('Ext.form.field.Display', { |
194 | fieldLabel: gettext('SCSI Controller'), | |
195 | reference: 'scsiController', | |
94b5e53a | 196 | name: 'scsiController', |
471d03aa | 197 | bind: me.insideWizard ? { |
f6710aac | 198 | value: '{current.scsihw}', |
3f543d7f TL |
199 | visible: '{isSCSI}', |
200 | } : { | |
201 | visible: '{isSCSI}', | |
202 | }, | |
42bd6556 | 203 | renderer: PVE.Utils.render_scsihw, |
471d03aa | 204 | submitValue: false, |
f6710aac | 205 | hidden: true, |
42bd6556 | 206 | }); |
74c0b9a9 | 207 | column1.push(me.scsiController); |
df15b3da DM |
208 | } |
209 | ||
210 | if (me.unused) { | |
09cacce7 | 211 | me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', { |
7a6e0635 | 212 | name: 'unusedId', |
df15b3da DM |
213 | fieldLabel: gettext('Disk image'), |
214 | matchFieldWidth: false, | |
215 | listConfig: { | |
f6710aac | 216 | width: 350, |
df15b3da DM |
217 | }, |
218 | data: [], | |
f6710aac | 219 | allowBlank: false, |
df15b3da | 220 | }); |
74c0b9a9 | 221 | column1.push(me.unusedDisks); |
d5e771ce | 222 | } else if (me.isCreate) { |
74c0b9a9 | 223 | column1.push({ |
771aec96 | 224 | xtype: 'pveDiskStorageSelector', |
df15b3da | 225 | storageContent: 'images', |
771aec96 | 226 | name: 'disk', |
df15b3da | 227 | nodename: me.nodename, |
f6710aac | 228 | autoSelect: me.insideWizard, |
df15b3da | 229 | }); |
df15b3da | 230 | } else { |
74c0b9a9 | 231 | column1.push({ |
df15b3da DM |
232 | xtype: 'textfield', |
233 | disabled: true, | |
234 | submitValue: false, | |
235 | fieldLabel: gettext('Disk image'), | |
f6710aac | 236 | name: 'hdimage', |
df15b3da DM |
237 | }); |
238 | } | |
239 | ||
74c0b9a9 | 240 | column2.push( |
f9261fde NC |
241 | { |
242 | xtype: 'CacheTypeSelector', | |
243 | name: 'cache', | |
244 | value: '__default__', | |
f6710aac | 245 | fieldLabel: gettext('Cache'), |
f9261fde | 246 | }, |
546d09c2 DC |
247 | { |
248 | xtype: 'proxmoxcheckbox', | |
249 | fieldLabel: gettext('Discard'), | |
546d09c2 | 250 | reference: 'discard', |
f6710aac TL |
251 | name: 'discard', |
252 | }, | |
f9261fde NC |
253 | { |
254 | xtype: 'proxmoxcheckbox', | |
94b5e53a MH |
255 | name: 'iothread', |
256 | fieldLabel: 'IO thread', | |
3f543d7f | 257 | clearOnDisable: true, |
00e4bd2e | 258 | bind: me.insideWizard || me.isCreate ? { |
94b5e53a MH |
259 | disabled: '{!isVirtIO && !isSCSI}', |
260 | // Checkbox.setValue handles Arrays in a different way, therefore cast to bool | |
261 | value: '{!!isVirtIO || (isSCSI && isSCSISingle)}', | |
00e4bd2e DC |
262 | } : { |
263 | disabled: '{!isVirtIO && !isSCSI}', | |
3f543d7f | 264 | }, |
546d09c2 | 265 | }, |
94b5e53a MH |
266 | ); |
267 | ||
268 | advancedColumn1.push( | |
546d09c2 DC |
269 | { |
270 | xtype: 'proxmoxcheckbox', | |
94b5e53a MH |
271 | fieldLabel: gettext('SSD emulation'), |
272 | name: 'ssd', | |
3f543d7f TL |
273 | clearOnDisable: true, |
274 | bind: { | |
94b5e53a | 275 | disabled: '{isVirtIO}', |
3f543d7f | 276 | }, |
c2233c8f | 277 | }, |
e4d84378 TL |
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'), | |
e4d84378 TL |
283 | clearOnDisable: true, |
284 | bind: { | |
285 | disabled: '{!isVirtIO && !isSCSI}', | |
286 | }, | |
287 | }, | |
74c0b9a9 DC |
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 | }, | |
74c0b9a9 DC |
298 | name: 'backup', |
299 | bind: { | |
300 | value: '{isIncludedInBackup}', | |
301 | }, | |
302 | }, | |
303 | { | |
304 | xtype: 'proxmoxcheckbox', | |
305 | fieldLabel: gettext('Skip replication'), | |
74c0b9a9 DC |
306 | name: 'noreplicate', |
307 | }, | |
f68d8b2c DC |
308 | { |
309 | xtype: 'proxmoxKVComboBox', | |
310 | name: 'aio', | |
311 | fieldLabel: gettext('Async IO'), | |
312 | allowBlank: false, | |
313 | value: '__default__', | |
f68d8b2c DC |
314 | comboItems: [ |
315 | ['__default__', Proxmox.Utils.defaultText + ' (io_uring)'], | |
316 | ['io_uring', 'io_uring'], | |
317 | ['native', 'native'], | |
318 | ['threads', 'threads'], | |
319 | ], | |
320 | }, | |
74c0b9a9 DC |
321 | ); |
322 | ||
b48c6665 TL |
323 | let labelWidth = 140; |
324 | ||
74c0b9a9 | 325 | let bwColumn1 = [ |
c2233c8f DC |
326 | { |
327 | xtype: 'numberfield', | |
328 | name: 'mbps_rd', | |
329 | minValue: 1, | |
330 | step: 1, | |
331 | fieldLabel: gettext('Read limit') + ' (MB/s)', | |
332 | labelWidth: labelWidth, | |
f6710aac | 333 | emptyText: gettext('unlimited'), |
c2233c8f DC |
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, | |
f6710aac | 342 | emptyText: gettext('unlimited'), |
c2233c8f DC |
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, | |
f6710aac | 351 | emptyText: gettext('unlimited'), |
c2233c8f DC |
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, | |
f6710aac TL |
360 | emptyText: gettext('unlimited'), |
361 | }, | |
74c0b9a9 | 362 | ]; |
546d09c2 | 363 | |
74c0b9a9 | 364 | let bwColumn2 = [ |
c2233c8f DC |
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, | |
f6710aac | 372 | emptyText: gettext('default'), |
c2233c8f DC |
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, | |
f6710aac | 381 | emptyText: gettext('default'), |
c2233c8f DC |
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, | |
f6710aac | 390 | emptyText: gettext('default'), |
c2233c8f DC |
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, | |
f6710aac TL |
399 | emptyText: gettext('default'), |
400 | }, | |
74c0b9a9 DC |
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 | ]; | |
546d09c2 | 433 | |
df15b3da | 434 | me.callParent(); |
f6710aac | 435 | }, |
74c0b9a9 DC |
436 | |
437 | setAdvancedVisible: function(visible) { | |
438 | this.lookup('diskpanel').setAdvancedVisible(visible); | |
439 | this.lookup('bwpanel').setAdvancedVisible(visible); | |
440 | }, | |
df15b3da DM |
441 | }); |
442 | ||
443 | Ext.define('PVE.qemu.HDEdit', { | |
9fccc702 | 444 | extend: 'Proxmox.window.Edit', |
df15b3da DM |
445 | |
446 | isAdd: true, | |
447 | ||
7cb345b8 WL |
448 | backgroundDelay: 5, |
449 | ||
74c0b9a9 DC |
450 | width: 600, |
451 | bodyPadding: 0, | |
452 | ||
8058410f | 453 | initComponent: function() { |
df15b3da DM |
454 | var me = this; |
455 | ||
456 | var nodename = me.pveSelNode.data.node; | |
7a6e0635 TL |
457 | if (!nodename) { |
458 | throw "no node name specified"; | |
df15b3da DM |
459 | } |
460 | ||
461 | var unused = me.confid && me.confid.match(/^unused\d+$/); | |
462 | ||
d5e771ce | 463 | me.isCreate = me.confid ? unused : true; |
df15b3da DM |
464 | |
465 | var ipanel = Ext.create('PVE.qemu.HDInputPanel', { | |
466 | confid: me.confid, | |
467 | nodename: nodename, | |
468 | unused: unused, | |
f6710aac | 469 | isCreate: me.isCreate, |
df15b3da DM |
470 | }); |
471 | ||
df15b3da DM |
472 | if (unused) { |
473 | me.subject = gettext('Unused Disk'); | |
d5e771ce | 474 | } else if (me.isCreate) { |
df15b3da DM |
475 | me.subject = gettext('Hard Disk'); |
476 | } else { | |
477 | me.subject = gettext('Hard Disk') + ' (' + me.confid + ')'; | |
478 | } | |
479 | ||
8058410f | 480 | me.items = [ipanel]; |
df15b3da DM |
481 | |
482 | me.callParent(); | |
d5e771ce EK |
483 | /* 'data' is assigned an empty array in same file, and here we |
484 | * use it like an object | |
485 | */ | |
df15b3da DM |
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) { | |
185a77e5 | 493 | Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options'); |
df15b3da DM |
494 | me.close(); |
495 | return; | |
496 | } | |
497 | ipanel.setDrive(drive); | |
498 | me.isValid(); // trigger validation | |
499 | } | |
f6710aac | 500 | }, |
df15b3da | 501 | }); |
f6710aac | 502 | }, |
df15b3da | 503 | }); |