]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/qemu/HDEdit.js
ui: ceph: osd: handle edge case with dead node
[pve-manager.git] / www / manager6 / qemu / HDEdit.js
CommitLineData
d5e771ce 1/* 'change' property is assigned a string and then a function */
df15b3da 2Ext.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
c2233c8f
DC
180 var labelWidth = 140;
181
df15b3da
DM
182 me.drive = {};
183
74c0b9a9
DC
184 let column1 = [];
185 let column2 = [];
df15b3da 186
74c0b9a9
DC
187 let advancedColumn1 = [];
188 let advancedColumn2 = [];
546d09c2 189
df15b3da 190 if (!me.confid || me.unused) {
42902182 191 me.bussel = Ext.create('PVE.form.ControllerSelector', {
9dbc4dfa
DC
192 vmconfig: me.vmconfig,
193 selectFree: true,
df15b3da 194 });
74c0b9a9 195 column1.push(me.bussel);
2414ef15 196
42bd6556
EK
197 me.scsiController = Ext.create('Ext.form.field.Display', {
198 fieldLabel: gettext('SCSI Controller'),
199 reference: 'scsiController',
471d03aa 200 bind: me.insideWizard ? {
f6710aac 201 value: '{current.scsihw}',
3f543d7f
TL
202 visible: '{isSCSI}',
203 } : {
204 visible: '{isSCSI}',
205 },
42bd6556 206 renderer: PVE.Utils.render_scsihw,
471d03aa 207 submitValue: false,
f6710aac 208 hidden: true,
42bd6556 209 });
74c0b9a9 210 column1.push(me.scsiController);
df15b3da
DM
211 }
212
213 if (me.unused) {
09cacce7 214 me.unusedDisks = Ext.create('Proxmox.form.KVComboBox', {
7a6e0635 215 name: 'unusedId',
df15b3da
DM
216 fieldLabel: gettext('Disk image'),
217 matchFieldWidth: false,
218 listConfig: {
f6710aac 219 width: 350,
df15b3da
DM
220 },
221 data: [],
f6710aac 222 allowBlank: false,
df15b3da 223 });
74c0b9a9 224 column1.push(me.unusedDisks);
d5e771ce 225 } else if (me.isCreate) {
74c0b9a9 226 column1.push({
771aec96 227 xtype: 'pveDiskStorageSelector',
df15b3da 228 storageContent: 'images',
771aec96 229 name: 'disk',
df15b3da 230 nodename: me.nodename,
f6710aac 231 autoSelect: me.insideWizard,
df15b3da 232 });
df15b3da 233 } else {
74c0b9a9 234 column1.push({
df15b3da
DM
235 xtype: 'textfield',
236 disabled: true,
237 submitValue: false,
238 fieldLabel: gettext('Disk image'),
f6710aac 239 name: 'hdimage',
df15b3da
DM
240 });
241 }
242
74c0b9a9 243 column2.push(
f9261fde
NC
244 {
245 xtype: 'CacheTypeSelector',
246 name: 'cache',
247 value: '__default__',
f6710aac 248 fieldLabel: gettext('Cache'),
f9261fde 249 },
546d09c2
DC
250 {
251 xtype: 'proxmoxcheckbox',
252 fieldLabel: gettext('Discard'),
546d09c2 253 reference: 'discard',
f6710aac
TL
254 name: 'discard',
255 },
f9261fde
NC
256 );
257
74c0b9a9 258 advancedColumn1.push(
f9261fde
NC
259 {
260 xtype: 'proxmoxcheckbox',
f9261fde
NC
261 fieldLabel: gettext('SSD emulation'),
262 labelWidth: labelWidth,
263 name: 'ssd',
3f543d7f
TL
264 clearOnDisable: true,
265 bind: {
d9d96299 266 disabled: '{isVirtIO}',
3f543d7f 267 },
546d09c2
DC
268 },
269 {
270 xtype: 'proxmoxcheckbox',
3f543d7f 271 name: 'iothread',
546d09c2
DC
272 fieldLabel: 'IO thread',
273 labelWidth: labelWidth,
3f543d7f
TL
274 clearOnDisable: true,
275 bind: {
276 disabled: '{!isVirtIO && !isSCSI}',
277 },
c2233c8f 278 },
e4d84378
TL
279 {
280 xtype: 'proxmoxcheckbox',
281 name: 'readOnly', // `ro` in the config, we map in get/set values
282 defaultValue: 0,
283 fieldLabel: gettext('Read-only'),
284 labelWidth: labelWidth,
285 clearOnDisable: true,
286 bind: {
287 disabled: '{!isVirtIO && !isSCSI}',
288 },
289 },
74c0b9a9
DC
290 );
291
292 advancedColumn2.push(
293 {
294 xtype: 'proxmoxcheckbox',
295 fieldLabel: gettext('Backup'),
296 autoEl: {
297 tag: 'div',
298 'data-qtip': gettext('Include volume in backup job'),
299 },
300 labelWidth: labelWidth,
301 name: 'backup',
302 bind: {
303 value: '{isIncludedInBackup}',
304 },
305 },
306 {
307 xtype: 'proxmoxcheckbox',
308 fieldLabel: gettext('Skip replication'),
309 labelWidth: labelWidth,
310 name: 'noreplicate',
311 },
f68d8b2c
DC
312 {
313 xtype: 'proxmoxKVComboBox',
314 name: 'aio',
315 fieldLabel: gettext('Async IO'),
316 allowBlank: false,
317 value: '__default__',
318 labelWidth: labelWidth,
319 comboItems: [
320 ['__default__', Proxmox.Utils.defaultText + ' (io_uring)'],
321 ['io_uring', 'io_uring'],
322 ['native', 'native'],
323 ['threads', 'threads'],
324 ],
325 },
74c0b9a9
DC
326 );
327
328 let bwColumn1 = [
c2233c8f
DC
329 {
330 xtype: 'numberfield',
331 name: 'mbps_rd',
332 minValue: 1,
333 step: 1,
334 fieldLabel: gettext('Read limit') + ' (MB/s)',
335 labelWidth: labelWidth,
f6710aac 336 emptyText: gettext('unlimited'),
c2233c8f
DC
337 },
338 {
339 xtype: 'numberfield',
340 name: 'mbps_wr',
341 minValue: 1,
342 step: 1,
343 fieldLabel: gettext('Write limit') + ' (MB/s)',
344 labelWidth: labelWidth,
f6710aac 345 emptyText: gettext('unlimited'),
c2233c8f
DC
346 },
347 {
348 xtype: 'proxmoxintegerfield',
349 name: 'iops_rd',
350 minValue: 10,
351 step: 10,
352 fieldLabel: gettext('Read limit') + ' (ops/s)',
353 labelWidth: labelWidth,
f6710aac 354 emptyText: gettext('unlimited'),
c2233c8f
DC
355 },
356 {
357 xtype: 'proxmoxintegerfield',
358 name: 'iops_wr',
359 minValue: 10,
360 step: 10,
361 fieldLabel: gettext('Write limit') + ' (ops/s)',
362 labelWidth: labelWidth,
f6710aac
TL
363 emptyText: gettext('unlimited'),
364 },
74c0b9a9 365 ];
546d09c2 366
74c0b9a9 367 let bwColumn2 = [
c2233c8f
DC
368 {
369 xtype: 'numberfield',
370 name: 'mbps_rd_max',
371 minValue: 1,
372 step: 1,
373 fieldLabel: gettext('Read max burst') + ' (MB)',
374 labelWidth: labelWidth,
f6710aac 375 emptyText: gettext('default'),
c2233c8f
DC
376 },
377 {
378 xtype: 'numberfield',
379 name: 'mbps_wr_max',
380 minValue: 1,
381 step: 1,
382 fieldLabel: gettext('Write max burst') + ' (MB)',
383 labelWidth: labelWidth,
f6710aac 384 emptyText: gettext('default'),
c2233c8f
DC
385 },
386 {
387 xtype: 'proxmoxintegerfield',
388 name: 'iops_rd_max',
389 minValue: 10,
390 step: 10,
391 fieldLabel: gettext('Read max burst') + ' (ops)',
392 labelWidth: labelWidth,
f6710aac 393 emptyText: gettext('default'),
c2233c8f
DC
394 },
395 {
396 xtype: 'proxmoxintegerfield',
397 name: 'iops_wr_max',
398 minValue: 10,
399 step: 10,
400 fieldLabel: gettext('Write max burst') + ' (ops)',
401 labelWidth: labelWidth,
f6710aac
TL
402 emptyText: gettext('default'),
403 },
74c0b9a9
DC
404 ];
405
406 me.items = [
407 {
408 xtype: 'tabpanel',
409 plain: true,
410 bodyPadding: 10,
411 border: 0,
412 items: [
413 {
414 title: gettext('Disk'),
415 xtype: 'inputpanel',
416 reference: 'diskpanel',
417 column1,
418 column2,
419 advancedColumn1,
420 advancedColumn2,
421 showAdvanced: me.showAdvanced,
422 getValues: () => ({}),
423 },
424 {
425 title: gettext('Bandwidth'),
426 xtype: 'inputpanel',
427 reference: 'bwpanel',
428 column1: bwColumn1,
429 column2: bwColumn2,
430 showAdvanced: me.showAdvanced,
431 getValues: () => ({}),
432 },
433 ],
434 },
435 ];
546d09c2 436
df15b3da 437 me.callParent();
f6710aac 438 },
74c0b9a9
DC
439
440 setAdvancedVisible: function(visible) {
441 this.lookup('diskpanel').setAdvancedVisible(visible);
442 this.lookup('bwpanel').setAdvancedVisible(visible);
443 },
df15b3da
DM
444});
445
446Ext.define('PVE.qemu.HDEdit', {
9fccc702 447 extend: 'Proxmox.window.Edit',
df15b3da
DM
448
449 isAdd: true,
450
7cb345b8
WL
451 backgroundDelay: 5,
452
74c0b9a9
DC
453 width: 600,
454 bodyPadding: 0,
455
8058410f 456 initComponent: function() {
df15b3da
DM
457 var me = this;
458
459 var nodename = me.pveSelNode.data.node;
7a6e0635
TL
460 if (!nodename) {
461 throw "no node name specified";
df15b3da
DM
462 }
463
464 var unused = me.confid && me.confid.match(/^unused\d+$/);
465
d5e771ce 466 me.isCreate = me.confid ? unused : true;
df15b3da
DM
467
468 var ipanel = Ext.create('PVE.qemu.HDInputPanel', {
469 confid: me.confid,
470 nodename: nodename,
471 unused: unused,
f6710aac 472 isCreate: me.isCreate,
df15b3da
DM
473 });
474
df15b3da
DM
475 if (unused) {
476 me.subject = gettext('Unused Disk');
d5e771ce 477 } else if (me.isCreate) {
df15b3da
DM
478 me.subject = gettext('Hard Disk');
479 } else {
480 me.subject = gettext('Hard Disk') + ' (' + me.confid + ')';
481 }
482
8058410f 483 me.items = [ipanel];
df15b3da
DM
484
485 me.callParent();
d5e771ce
EK
486 /* 'data' is assigned an empty array in same file, and here we
487 * use it like an object
488 */
df15b3da
DM
489 me.load({
490 success: function(response, options) {
491 ipanel.setVMConfig(response.result.data);
492 if (me.confid) {
493 var value = response.result.data[me.confid];
494 var drive = PVE.Parser.parseQemuDrive(me.confid, value);
495 if (!drive) {
185a77e5 496 Ext.Msg.alert(gettext('Error'), 'Unable to parse drive options');
df15b3da
DM
497 me.close();
498 return;
499 }
500 ipanel.setDrive(drive);
501 me.isValid(); // trigger validation
502 }
f6710aac 503 },
df15b3da 504 });
f6710aac 505 },
df15b3da 506});