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