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