]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/GuestImport.js
ui: guest import: add field for scsi controller
[pve-manager.git] / www / manager6 / window / GuestImport.js
CommitLineData
bb3fa9de
DC
1Ext.define('PVE.window.GuestImport', {
2 extend: 'Proxmox.window.Edit', // fixme: Proxmox.window.Edit?
3 alias: 'widget.pveGuestImportWindow',
4
5 title: gettext('Import Guest'),
6
7 submitUrl: function() {
8 let me = this;
9 return `/nodes/${me.nodename}/qemu`;
10 },
11
12 isAdd: true,
13 isCreate: true,
14 submitText: gettext('Import'),
15 showTaskViewer: true,
16 method: 'POST',
17
18 loadUrl: function(_url, { storage, nodename, volumeName }) {
19 let args = Ext.Object.toQueryString({ volume: volumeName });
20 return `/nodes/${nodename}/storage/${storage}/import-metadata?${args}`;
21 },
22
23 controller: {
24 xclass: 'Ext.app.ViewController',
25
26 setNodename: function(_column, widget) {
27 let me = this;
28 let view = me.getView();
29 widget.setNodename(view.nodename);
30 },
31
b7583d45 32 diskStorageChange: function(storageSelector, value) {
bb3fa9de
DC
33 let me = this;
34
35 let grid = me.lookup('diskGrid');
36 let rec = storageSelector.getWidgetRecord();
b7583d45 37 let validFormats = storageSelector.store.getById(value)?.data.format;
bb3fa9de
DC
38 grid.query('pveDiskFormatSelector').some((selector) => {
39 if (selector.getWidgetRecord().data.id !== rec.data.id) {
40 return false;
41 }
42
43 if (validFormats?.[0]?.qcow2) {
44 selector.setDisabled(false);
45 selector.setValue('qcow2');
46 } else {
47 selector.setValue('raw');
48 selector.setDisabled(true);
49 }
50
51 return true;
52 });
53 },
54
b7583d45
DC
55 isoStorageChange: function(storageSelector, value) {
56 let me = this;
57
58 let grid = me.lookup('cdGrid');
59 let rec = storageSelector.getWidgetRecord();
60 grid.query('pveFileSelector').some((selector) => {
61 if (selector.getWidgetRecord().data.id !== rec.data.id) {
62 return false;
63 }
64
65 selector.setStorage(value);
66 if (!value) {
67 selector.setValue('');
68 }
69
70 return true;
71 });
72 },
73
4fb223f7
DC
74 onOSBaseChange: function(_field, value) {
75 let me = this;
76 let ostype = me.lookup('ostype');
77 let store = ostype.getStore();
78 store.setData(PVE.Utils.kvm_ostypes[value]);
79 let old_val = ostype.getValue();
80 if (old_val && store.find('val', old_val) !== -1) {
81 ostype.setValue(old_val);
82 } else {
83 ostype.setValue(store.getAt(0));
84 }
85 },
86
c52eb476
DC
87 calculateConfig: function() {
88 let me = this;
89 let inputPanel = me.lookup('mainInputPanel');
90 let summaryGrid = me.lookup('summaryGrid');
91 let values = inputPanel.getValues();
92 summaryGrid.getStore().setData(Object.entries(values).map(([key, value]) => ({ key, value })));
93 },
94
d39e2492
DC
95 // assume assigned sata disks indices are continuous, so without holes
96 getMaxSata: function() {
97 let me = this;
98 let view = me.getView();
99 if (view.maxSata !== undefined) {
100 return view.maxSata;
101 }
102
103 view.maxSata = -1;
104 for (const key of Object.keys(me.getView().vmConfig)) {
105 if (!key.toLowerCase().startsWith('sata')) {
106 continue;
107 }
108 let idx = parseInt(key.slice(4), 10);
109 if (idx > view.maxSata) {
110 view.maxSata = idx;
111 }
112 }
113 me.lookup('diskGrid').getStore().each(rec => {
114 if (!rec.data.id.toLowerCase().startsWith('sata')) {
115 return;
116 }
117 let idx = parseInt(rec.data.id.slice(4), 10);
118 if (idx > view.maxSata) {
119 view.maxSata = idx;
120 }
121 });
122 me.lookup('cdGrid').getStore().each(rec => {
123 if (!rec.data.id.toLowerCase().startsWith('sata')) {
124 return;
125 }
126 let idx = parseInt(rec.data.id.slice(4), 10);
127 if (idx > view.maxSata) {
128 view.maxSata = idx;
129 }
130 });
131
132 return view.maxSata;
133 },
134
135 mapDisk: function(value, metaData) {
136 let me = this;
137 let mapSata = me.lookup('mapSata');
138 if (mapSata.isDisabled() || !mapSata.getValue()) {
139 return value;
140 }
141 if (!value.toLowerCase().startsWith('scsi')) {
142 return value;
143 }
144 let offset = parseInt(value.slice(4), 10);
145 let newIdx = offset + me.getMaxSata() + 1;
146 if (newIdx > PVE.Utils.diskControllerMaxIDs.sata) {
147 let prefix = '';
148 if (metaData !== undefined) {
149 // we're in the renderer so put a warning here
150 let warning = gettext('Too many disks, could not map to SATA.');
151 prefix = `<i data-qtip="${warning}" class="fa fa-exclamation-triangle warning"></i> `;
152 }
153 return `${prefix}${value}`;
154 }
155 return `sata${newIdx}`;
156 },
157
158 refreshDiskGrid: function() {
159 this.lookup('diskGrid').reconfigure();
160 },
161
bb3fa9de
DC
162 control: {
163 'grid field': {
164 // update records from widgetcolumns
165 change: function(widget, value) {
166 let rec = widget.getWidgetRecord();
167 rec.set(widget.name, value);
168 rec.commit();
169 },
170 },
b7583d45
DC
171 'grid[reference=diskGrid] pveStorageSelector': {
172 change: 'diskStorageChange',
173 },
174 'grid[reference=cdGrid] pveStorageSelector': {
175 change: 'isoStorageChange',
bb3fa9de 176 },
4fb223f7
DC
177 'field[name=osbase]': {
178 change: 'onOSBaseChange',
179 },
c52eb476
DC
180 'panel[reference=summaryTab]': {
181 activate: 'calculateConfig',
182 },
d39e2492
DC
183 'proxmoxcheckbox[reference=mapSata]': {
184 change: 'refreshDiskGrid',
185 },
186 'combobox[name=ostype]': {
187 change: 'refreshDiskGrid',
188 },
bb3fa9de
DC
189 },
190 },
191
192 viewModel: {
193 data: {
194 coreCount: 1,
195 socketCount: 1,
226f01cb 196 liveImport: false,
d39e2492 197 os: '',
bb3fa9de
DC
198 warnings: [],
199 },
200
201 formulas: {
202 totalCoreCount: get => get('socketCount') * get('coreCount'),
203 hideWarnings: get => get('warnings').length === 0,
03d8d7d2
TL
204 warningsText: get => '<ul style="margin: 0; padding-left: 20px;">'
205 + get('warnings').map(w => `<li>${w}</li>`).join('') + '</ul>',
226f01cb
TL
206 liveImportNote: get => !get('liveImport') ? ''
207 : gettext('Note: If anything goes wrong during the live-import, new data written by the VM may be lost.'),
d39e2492 208 isWindows: get => (get('os') ?? '').startsWith('win'),
bb3fa9de
DC
209 },
210 },
211
11236006 212 width: 700,
b0eeb862
DC
213 bodyPadding: 0,
214
bb3fa9de
DC
215 items: [
216 {
b0eeb862
DC
217 xtype: 'tabpanel',
218 defaults: {
219 bodyPadding: 10,
bb3fa9de 220 },
b0eeb862 221 items: [
bb3fa9de 222 {
b0eeb862
DC
223 title: gettext('General'),
224 xtype: 'inputpanel',
225 reference: 'mainInputPanel',
226 onGetValues: function(values) {
227 let me = this;
228 let grid = me.up('pveGuestImportWindow');
229
230 // from pveDiskStorageSelector
231 let defaultStorage = values.hdstorage;
232 let defaultFormat = values.diskformat;
233 delete values.hdstorage;
234 delete values.diskformat;
235
236 let defaultBridge = values.defaultBridge;
237 delete values.defaultBridge;
238
239 let config = Ext.apply(grid.vmConfig, values);
240
241 if (config.scsi0) {
242 config.scsi0 = config.scsi0.replace('local:0,', 'local:0,format=qcow2,');
243 }
244
d39e2492
DC
245 let parsedBoot = PVE.Parser.parsePropertyString(config.boot ?? '');
246 if (parsedBoot.order) {
247 parsedBoot.order = parsedBoot.order.split(';');
248 }
249
b0eeb862
DC
250 grid.lookup('diskGrid').getStore().each((rec) => {
251 if (!rec.data.enable) {
252 return;
253 }
d39e2492
DC
254 let id = grid.getController().mapDisk(rec.data.id);
255 if (id !== rec.data.id && parsedBoot?.order) {
256 let idx = parsedBoot.order.indexOf(rec.data.id);
257 if (idx !== -1) {
258 parsedBoot.order[idx] = id;
259 }
260 }
b0eeb862
DC
261 let data = {
262 ...rec.data,
263 };
264 delete data.enable;
265 delete data.id;
a5e56799 266 delete data.size;
b0eeb862
DC
267 if (!data.file) {
268 data.file = defaultStorage;
269 data.format = defaultFormat;
270 }
271 data.file += ':0'; // for our special api format
272 if (id === 'efidisk0') {
273 delete data['import-from'];
274 }
275 config[id] = PVE.Parser.printQemuDrive(data);
276 });
277
d39e2492
DC
278 if (parsedBoot.order) {
279 parsedBoot.order = parsedBoot.order.join(';');
280 }
281 config.boot = PVE.Parser.printPropertyString(parsedBoot);
282
b0eeb862
DC
283 grid.lookup('netGrid').getStore().each((rec) => {
284 if (!rec.data.enable) {
285 return;
286 }
287 let id = rec.data.id;
288 let data = {
289 ...rec.data,
290 };
291 delete data.enable;
292 delete data.id;
293 if (!data.bridge) {
294 data.bridge = defaultBridge;
295 }
296 config[id] = PVE.Parser.printQemuNetwork(data);
297 });
298
299 grid.lookup('cdGrid').getStore().each((rec) => {
300 if (!rec.data.enable) {
301 return;
302 }
303 let id = rec.data.id;
304 let cd = {
305 media: 'cdrom',
306 file: rec.data.file ? rec.data.file : 'none',
307 };
308 config[id] = PVE.Parser.printPropertyString(cd);
309 });
310
8a0dd9d6
DC
311 config.scsihw = grid.lookup('scsihw').getValue();
312
b0eeb862
DC
313 if (grid.lookup('liveimport').getValue()) {
314 config['live-restore'] = 1;
315 }
316
8a0dd9d6
DC
317 // remove __default__ values
318 for (const [key, value] of Object.entries(config)) {
319 if (value === '__default__') {
320 delete config[key];
321 }
322 }
323
b0eeb862 324 return config;
bb3fa9de 325 },
bb3fa9de 326
b0eeb862 327 column1: [
bb3fa9de 328 {
b0eeb862
DC
329 xtype: 'pveGuestIDSelector',
330 name: 'vmid',
331 fieldLabel: 'VM',
332 guestType: 'qemu',
333 loadNextFreeID: true,
bb3fa9de
DC
334 },
335 {
b0eeb862
DC
336 xtype: 'proxmoxintegerfield',
337 fieldLabel: gettext('Sockets'),
338 name: 'sockets',
339 reference: 'socketsField',
340 value: 1,
341 minValue: 1,
342 maxValue: 4,
343 allowBlank: true,
344 bind: {
345 value: '{socketCount}',
346 },
bb3fa9de
DC
347 },
348 {
b0eeb862
DC
349 xtype: 'proxmoxintegerfield',
350 fieldLabel: gettext('Cores'),
351 name: 'cores',
352 reference: 'coresField',
353 value: 1,
354 minValue: 1,
355 maxValue: 128,
356 allowBlank: true,
357 bind: {
358 value: '{coreCount}',
bb3fa9de
DC
359 },
360 },
361 {
b0eeb862
DC
362 xtype: 'pveMemoryField',
363 fieldLabel: gettext('Memory'),
364 name: 'memory',
365 reference: 'memoryField',
366 value: 512,
367 allowBlank: true,
bb3fa9de
DC
368 },
369 {
b0eeb862
DC
370 //spacer
371 xtype: 'displayfield',
372 },
373 {
374 xtype: 'pveDiskStorageSelector',
375 reference: 'defaultStorage',
376 storageLabel: gettext('Default Storage'),
377 storageContent: 'images',
378 autoSelect: true,
379 hideSize: true,
380 name: 'defaultStorage',
bb3fa9de
DC
381 },
382 ],
b0eeb862
DC
383
384 column2: [
b7583d45 385 {
b0eeb862
DC
386 xtype: 'textfield',
387 fieldLabel: gettext('Name'),
388 name: 'name',
389 vtype: 'DnsName',
390 reference: 'nameField',
391 allowBlank: true,
392 },
393 {
394 xtype: 'CPUModelSelector',
395 name: 'cpu',
396 reference: 'cputype',
397 value: 'x86-64-v2-AES',
398 fieldLabel: gettext('Type'),
399 },
400 {
401 xtype: 'displayfield',
402 fieldLabel: gettext('Total cores'),
403 name: 'totalcores',
404 isFormField: false,
405 bind: {
406 value: '{totalCoreCount}',
407 },
408 },
409 {
410 xtype: 'combobox',
411 submitValue: false,
412 name: 'osbase',
413 fieldLabel: gettext('OS Type'),
414 editable: false,
415 queryMode: 'local',
416 value: 'Linux',
417 store: Object.keys(PVE.Utils.kvm_ostypes),
418 },
419 {
420 xtype: 'combobox',
421 name: 'ostype',
422 reference: 'ostype',
423 fieldLabel: gettext('Version'),
424 value: 'l26',
425 allowBlank: false,
426 editable: false,
427 queryMode: 'local',
428 valueField: 'val',
429 displayField: 'desc',
d39e2492
DC
430 bind: {
431 value: '{os}',
432 },
b0eeb862
DC
433 store: {
434 fields: ['desc', 'val'],
435 data: PVE.Utils.kvm_ostypes.Linux,
b7583d45
DC
436 },
437 },
438 {
b0eeb862
DC
439 xtype: 'PVE.form.BridgeSelector',
440 reference: 'defaultBridge',
441 name: 'defaultBridge',
442 allowBlank: false,
443 fieldLabel: gettext('Default Bridge'),
b7583d45 444 },
b0eeb862
DC
445 ],
446
447 columnB: [
1c2c2f69
DC
448 {
449 xtype: 'proxmoxcheckbox',
450 fieldLabel: gettext('Live Import'),
451 reference: 'liveimport',
452 isFormField: false,
226f01cb
TL
453 boxLabelCls: 'pmx-hint black x-form-cb-label',
454 bind: {
455 value: '{liveImport}',
456 boxLabel: '{liveImportNote}',
457 },
1c2c2f69 458 },
b7583d45 459 {
b0eeb862
DC
460 xtype: 'displayfield',
461 fieldLabel: gettext('Warnings'),
462 labelWidth: 200,
463 hidden: true,
464 bind: {
465 hidden: '{hideWarnings}',
b7583d45 466 },
b7583d45
DC
467 },
468 {
b0eeb862
DC
469 xtype: 'displayfield',
470 reference: 'warningText',
471 userCls: 'pmx-hint',
472 hidden: true,
473 bind: {
474 hidden: '{hideWarnings}',
475 value: '{warningsText}',
b7583d45 476 },
b7583d45
DC
477 },
478 ],
479 },
bb3fa9de 480 {
b0eeb862
DC
481 title: gettext('Advanced'),
482 xtype: 'inputpanel',
d39e2492
DC
483
484 column1: [
8a0dd9d6
DC
485 {
486 xtype: 'pveScsiHwSelector',
487 reference: 'scsihw',
488 name: 'scsihw',
489 submitValue: false,
490 fieldLabel: gettext('SCSI Controller'),
491 },
492 ],
493
494 column2: [
d39e2492
DC
495 {
496 xtype: 'proxmoxcheckbox',
497 fieldLabel: gettext('Map SCSI to SATA'),
498 labelWidth: 120,
499 reference: 'mapSata',
500 isFormField: false,
501 hidden: true,
502 disabled: true,
503 bind: {
504 hidden: '{!isWindows}',
505 disabled: '{!isWindows}',
506 },
507 autoEl: {
508 tag: 'div',
509 'data-qtip': gettext('Useful when wanting to use VirtIO-SCSI'),
510 },
511 },
512 ],
513
514 columnB: [
bb3fa9de 515 {
b0eeb862
DC
516 xtype: 'displayfield',
517 fieldLabel: gettext('Disks'),
518 labelWidth: 200,
bb3fa9de
DC
519 },
520 {
b0eeb862
DC
521 xtype: 'grid',
522 reference: 'diskGrid',
163868e1 523 minHeight: 60,
b0eeb862
DC
524 maxHeight: 150,
525 store: {
526 data: [],
527 sorters: [
528 'id',
529 ],
530 },
531 columns: [
532 {
533 xtype: 'checkcolumn',
534 header: gettext('Use'),
535 width: 50,
536 dataIndex: 'enable',
537 listeners: {
538 checkchange: function(_column, _rowIndex, _checked, record) {
539 record.commit();
540 },
541 },
542 },
543 {
544 text: gettext('Disk'),
545 dataIndex: 'id',
d39e2492 546 renderer: 'mapDisk',
b0eeb862
DC
547 },
548 {
549 text: gettext('Source'),
550 dataIndex: 'import-from',
551 flex: 1,
552 renderer: function(value) {
553 return value.replace(/^.*\//, '');
554 },
555 },
a5e56799
DC
556 {
557 text: gettext('Size'),
558 dataIndex: 'size',
559 renderer: (value) => {
560 if (Ext.isNumeric(value)) {
561 return Proxmox.Utils.render_size(value);
562 }
563 return value ?? Proxmox.Utils.unknownText;
564 },
565 },
b0eeb862
DC
566 {
567 text: gettext('Storage'),
568 dataIndex: 'file',
569 xtype: 'widgetcolumn',
570 width: 150,
571 widget: {
572 xtype: 'pveStorageSelector',
573 isFormField: false,
574 autoSelect: false,
575 allowBlank: true,
576 emptyText: gettext('From Default'),
577 name: 'file',
578 storageContent: 'images',
579 },
580 onWidgetAttach: 'setNodename',
581 },
582 {
583 text: gettext('Format'),
584 dataIndex: 'format',
585 xtype: 'widgetcolumn',
586 width: 150,
587 widget: {
588 xtype: 'pveDiskFormatSelector',
589 name: 'format',
590 disabled: true,
591 isFormField: false,
592 matchFieldWidth: false,
593 },
594 },
595 ],
bb3fa9de
DC
596 },
597 {
b0eeb862
DC
598 xtype: 'displayfield',
599 fieldLabel: gettext('CD/DVD Drives'),
600 labelWidth: 200,
163868e1
TL
601 style: {
602 paddingTop: '10px',
603 },
bb3fa9de
DC
604 },
605 {
b0eeb862
DC
606 xtype: 'grid',
607 reference: 'cdGrid',
163868e1 608 minHeight: 60,
b0eeb862
DC
609 maxHeight: 150,
610 store: {
611 data: [],
612 sorters: [
613 'id',
614 ],
81c46efc 615 },
b0eeb862
DC
616 columns: [
617 {
618 xtype: 'checkcolumn',
619 header: gettext('Use'),
620 width: 50,
621 dataIndex: 'enable',
622 listeners: {
623 checkchange: function(_column, _rowIndex, _checked, record) {
624 record.commit();
625 },
626 },
627 },
628 {
629 text: gettext('Slot'),
630 dataIndex: 'id',
631 sorted: true,
632 },
633 {
634 text: gettext('Storage'),
635 xtype: 'widgetcolumn',
636 width: 150,
637 widget: {
638 xtype: 'pveStorageSelector',
639 isFormField: false,
640 autoSelect: false,
641 allowBlank: true,
642 emptyText: Proxmox.Utils.noneText,
643 storageContent: 'iso',
644 },
645 onWidgetAttach: 'setNodename',
646 },
647 {
648 text: gettext('ISO'),
649 dataIndex: 'file',
650 xtype: 'widgetcolumn',
651 flex: 1,
652 widget: {
653 xtype: 'pveFileSelector',
654 name: 'file',
655 isFormField: false,
656 allowBlank: true,
657 emptyText: Proxmox.Utils.noneText,
658 storageContent: 'iso',
659 },
660 onWidgetAttach: 'setNodename',
661 },
662 ],
663 },
664 {
665 xtype: 'displayfield',
666 fieldLabel: gettext('Network Interfaces'),
667 labelWidth: 200,
163868e1
TL
668 style: {
669 paddingTop: '10px',
670 },
bb3fa9de
DC
671 },
672 {
b0eeb862
DC
673 xtype: 'grid',
674 minHeight: 58,
675 maxHeight: 150,
676 reference: 'netGrid',
677 store: {
678 data: [],
679 sorters: [
680 'id',
681 ],
bb3fa9de 682 },
b0eeb862
DC
683 columns: [
684 {
685 xtype: 'checkcolumn',
686 header: gettext('Use'),
687 width: 50,
688 dataIndex: 'enable',
689 listeners: {
690 checkchange: function(_column, _rowIndex, _checked, record) {
691 record.commit();
692 },
693 },
694 },
695 {
696 text: gettext('ID'),
697 dataIndex: 'id',
698 },
699 {
700 text: gettext('MAC address'),
701 flex: 1,
702 dataIndex: 'macaddr',
703 renderer: value => value ?? 'auto',
704 },
705 {
706 text: gettext('Model'),
707 flex: 1,
708 dataIndex: 'model',
709 xtype: 'widgetcolumn',
710 widget: {
711 xtype: 'pveNetworkCardSelector',
712 name: 'model',
713 isFormField: false,
714 allowBlank: false,
715 },
716 },
717 {
718 text: gettext('Bridge'),
719 dataIndex: 'bridge',
720 xtype: 'widgetcolumn',
721 flex: 1,
722 widget: {
723 xtype: 'PVE.form.BridgeSelector',
724 name: 'bridge',
725 isFormField: false,
726 autoSelect: false,
727 allowBlank: true,
728 emptyText: gettext('From Default'),
729 },
730 onWidgetAttach: 'setNodename',
731 },
732 ],
bb3fa9de
DC
733 },
734 ],
735 },
c52eb476
DC
736 {
737 title: gettext('Resulting Config'),
738 reference: 'summaryTab',
739 items: [
740 {
741 xtype: 'grid',
742 reference: 'summaryGrid',
743 maxHeight: 400,
744 scrollable: true,
745 store: {
746 model: 'KeyValue',
747 sorters: [{
748 property: 'key',
749 direction: 'ASC',
750 }],
751 },
752 columns: [
753 { header: 'Key', width: 150, dataIndex: 'key' },
754 { header: 'Value', flex: 1, dataIndex: 'value' },
755 ],
756 },
757 ],
758 },
bb3fa9de
DC
759 ],
760 },
761 ],
762
763 initComponent: function() {
764 let me = this;
765
766 if (!me.volumeName) {
767 throw "no volumeName given";
768 }
769
770 if (!me.storage) {
771 throw "no storage given";
772 }
773
774 if (!me.nodename) {
775 throw "no nodename given";
776 }
777
778 me.callParent();
779
bb3fa9de
DC
780 me.setTitle(Ext.String.format(gettext('Import Guest - {0}'), `${me.storage}:${me.volumeName}`));
781
b0eeb862
DC
782 me.lookup('defaultStorage').setNodename(me.nodename);
783 me.lookup('defaultBridge').setNodename(me.nodename);
784
03d8d7d2
TL
785 let renderWarning = w => {
786 const warningsCatalogue = {
463b9b82 787 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, if required you can reconfigure the '{0}' drive in the 'Advanced' tab."),
03d8d7d2
TL
788 'nvme-unsupported': gettext("NVMe disks are currently not supported, '{0}' will get attaced as SCSI"),
789 'ovmf-with-lsi-unsupported': gettext("OVMF is built without LSI drivers, scsi hardware was set to '{1}'"),
790 'serial-port-socket-only': gettext("Serial socket '{0}' will be mapped to a socket"),
b185bbad 791 'guest-is-running': gettext('Virtual guest seems to be running on source host. Import might fail or have inconsistent state!'),
03d8d7d2
TL
792 };
793 let message = warningsCatalogue[w.type];
794 if (!w.type || !message) {
795 return w.message ?? w.type ?? gettext('Unknown warning');
796 }
797 return Ext.String.format(message, w.key ?? 'unknown', w.value ?? 'unknown');
798 };
799
bb3fa9de
DC
800 me.load({
801 success: function(response) {
802 let data = response.result.data;
803 me.vmConfig = data['create-args'];
804
805 let disks = [];
806 for (const [id, value] of Object.entries(data.disks ?? {})) {
a5e56799
DC
807 let volid = Ext.htmlEncode('<none>');
808 let size = 'auto';
809 if (Ext.isObject(value)) {
810 volid = value.volid;
811 size = value.size;
812 }
bb3fa9de
DC
813 disks.push({
814 id,
815 enable: true,
a5e56799
DC
816 size,
817 'import-from': volid,
bb3fa9de
DC
818 format: 'raw',
819 });
820 }
821
822 let nets = [];
823 for (const [id, parsed] of Object.entries(data.net ?? {})) {
824 parsed.id = id;
825 parsed.enable = true;
826 nets.push(parsed);
827 }
b7583d45
DC
828
829 let cdroms = [];
830 for (const [id, value] of Object.entries(me.vmConfig)) {
831 if (!Ext.isString(value) || !value.match(/media=cdrom/)) {
832 continue;
833 }
834 cdroms.push({
835 enable: true,
836 id,
837 });
838 delete me.vmConfig[id];
839 }
bb3fa9de
DC
840 me.lookup('diskGrid').getStore().setData(disks);
841 me.lookup('netGrid').getStore().setData(nets);
b7583d45 842 me.lookup('cdGrid').getStore().setData(cdroms);
bb3fa9de 843
03d8d7d2 844 me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
bb3fa9de 845
4fb223f7
DC
846 let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');
847
848 me.setValues({
849 osbase: osinfo.base,
850 ...me.vmConfig,
851 });
bb3fa9de
DC
852 },
853 });
854 },
855});