]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/window/GuestImport.js
ui: guest import: avoid one indentation level in deeply nested widget tree
[pve-manager.git] / www / manager6 / window / GuestImport.js
1 Ext.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
32 diskStorageChange: function(storageSelector, value) {
33 let me = this;
34
35 let grid = me.lookup('diskGrid');
36 let rec = storageSelector.getWidgetRecord();
37 let validFormats = storageSelector.store.getById(value)?.data.format;
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
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
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
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
95 calculateAdditionalCDIdx: function() {
96 let me = this;
97
98 let maxIde = me.getMaxControllerId('ide');
99 let maxSata = me.getMaxControllerId('sata');
100 // only ide0 and ide2 can be used reliably for isos (e.g. for q35)
101 if (maxIde < 0) {
102 return 'ide0';
103 }
104 if (maxIde < 2) {
105 return 'ide2';
106 }
107 if (maxSata < PVE.Utils.diskControllerMaxIDs.sata - 1) {
108 return `sata${maxSata+1}`;
109 }
110
111 return '';
112 },
113
114 // assume assigned sata disks indices are continuous, so without holes
115 getMaxControllerId: function(controller) {
116 let me = this;
117 let view = me.getView();
118 if (!controller) {
119 return -1;
120 }
121
122 let max = view[`max${controller}`];
123 if (max !== undefined) {
124 return max;
125 }
126
127 max = -1;
128 for (const key of Object.keys(me.getView().vmConfig)) {
129 if (!key.toLowerCase().startsWith(controller)) {
130 continue;
131 }
132 let idx = parseInt(key.slice(controller.length), 10);
133 if (idx > max) {
134 max = idx;
135 }
136 }
137 me.lookup('diskGrid').getStore().each(rec => {
138 if (!rec.data.id.toLowerCase().startsWith(controller)) {
139 return;
140 }
141 let idx = parseInt(rec.data.id.slice(controller.length), 10);
142 if (idx > max) {
143 max = idx;
144 }
145 });
146 me.lookup('cdGrid').getStore().each(rec => {
147 if (!rec.data.id.toLowerCase().startsWith(controller) || rec.data.hidden) {
148 return;
149 }
150 let idx = parseInt(rec.data.id.slice(controller.length), 10);
151 if (idx > max) {
152 max = idx;
153 }
154 });
155
156 view[`max${controller}`] = max;
157 return max;
158 },
159
160 mapDisk: function(value, metaData) {
161 let me = this;
162 let mapSata = me.lookup('mapSata');
163 if (mapSata.isDisabled() || !mapSata.getValue()) {
164 return value;
165 }
166 if (!value.toLowerCase().startsWith('scsi')) {
167 return value;
168 }
169 let offset = parseInt(value.slice(4), 10);
170 let newIdx = offset + me.getMaxControllerId('sata') + 1;
171 if (me.getViewModel().get('isWindows') && me.getView().additionalCdIdx?.startsWith('sata')) {
172 // additionalCdIdx takes the highest sata port
173 newIdx++;
174 }
175 if (newIdx >= PVE.Utils.diskControllerMaxIDs.sata) {
176 let prefix = '';
177 if (metaData !== undefined) {
178 // we're in the renderer so put a warning here
179 let warning = gettext('Too many disks, could not map to SATA.');
180 prefix = `<i data-qtip="${warning}" class="fa fa-exclamation-triangle warning"></i> `;
181 }
182 return `${prefix}${value}`;
183 }
184 return `sata${newIdx}`;
185 },
186
187 refreshGrids: function() {
188 this.lookup('diskGrid').reconfigure();
189 this.lookup('cdGrid').reconfigure();
190 this.lookup('netGrid').reconfigure();
191 },
192
193 onOSTypeChange: function(_cb, value) {
194 let me = this;
195 if (!value) {
196 return;
197 }
198 let store = me.lookup('cdGrid').getStore();
199 let collection = store.getData().getSource() ?? store.getData();
200 let rec = collection.find('autogenerated', true);
201
202 let isWindows = (value ?? '').startsWith('w');
203 if (rec) {
204 rec.set('hidden', !isWindows);
205 rec.commit();
206 }
207 let prepareVirtio = me.lookup('mapSata').getValue();
208 let defaultScsiHw = me.getView().vmConfig.scsihw ?? '__default__';
209 me.lookup('scsihw').setValue(prepareVirtio && isWindows ? 'virtio-scsi-single' : defaultScsiHw);
210
211 me.refreshGrids();
212 },
213
214 onPrepareVirtioChange: function(_cb, value) {
215 let me = this;
216
217 let scsihw = me.lookup('scsihw');
218 scsihw.suspendEvents();
219 scsihw.setValue(value ? 'virtio-scsi-single' : me.getView().vmConfig.scsihw);
220 scsihw.resumeEvents();
221
222 me.refreshGrids();
223 },
224
225 onScsiHwChange: function(_field, value) {
226 let me = this;
227 me.getView().vmConfig.scsihw = value;
228 },
229
230 onUniqueMACChange: function(_cb, value) {
231 let me = this;
232
233 me.getViewModel().set('uniqueMACAdresses', value);
234
235 me.lookup('netGrid').reconfigure();
236 },
237
238 renderMacAddress: function(value, metaData, record, rowIndex, colIndex, store, view) {
239 let me = this;
240 let vm = me.getViewModel();
241
242 return !vm.get('uniqueMACAdresses') && value ? value : 'auto';
243 },
244
245 control: {
246 'grid field': {
247 // update records from widgetcolumns
248 change: function(widget, value) {
249 let rec = widget.getWidgetRecord();
250 rec.set(widget.name, value);
251 rec.commit();
252 },
253 },
254 'grid[reference=diskGrid] pveStorageSelector': {
255 change: 'diskStorageChange',
256 },
257 'grid[reference=cdGrid] pveStorageSelector': {
258 change: 'isoStorageChange',
259 },
260 'field[name=osbase]': {
261 change: 'onOSBaseChange',
262 },
263 'panel[reference=summaryTab]': {
264 activate: 'calculateConfig',
265 },
266 'proxmoxcheckbox[reference=mapSata]': {
267 change: 'onPrepareVirtioChange',
268 },
269 'combobox[name=ostype]': {
270 change: 'onOSTypeChange',
271 },
272 'pveScsiHwSelector': {
273 change: 'onScsiHwChange',
274 },
275 'proxmoxcheckbox[name=uniqueMACs]': {
276 change: 'onUniqueMACChange',
277 },
278 },
279 },
280
281 viewModel: {
282 data: {
283 coreCount: 1,
284 socketCount: 1,
285 liveImport: false,
286 os: 'l26',
287 maxCdDrives: false,
288 uniqueMACAdresses: false,
289 warnings: [],
290 },
291
292 formulas: {
293 totalCoreCount: get => get('socketCount') * get('coreCount'),
294 hideWarnings: get => get('warnings').length === 0,
295 warningsText: get => '<ul style="margin: 0; padding-left: 20px;">'
296 + get('warnings').map(w => `<li>${w}</li>`).join('') + '</ul>',
297 liveImportNote: get => !get('liveImport') ? ''
298 : gettext('Note: If anything goes wrong during the live-import, new data written by the VM may be lost.'),
299 isWindows: get => (get('os') ?? '').startsWith('w'),
300 },
301 },
302
303 width: 700,
304 bodyPadding: 0,
305
306 items: [{
307 xtype: 'tabpanel',
308 defaults: {
309 bodyPadding: 10,
310 },
311 items: [
312 {
313 title: gettext('General'),
314 xtype: 'inputpanel',
315 reference: 'mainInputPanel',
316 onGetValues: function(values) {
317 let me = this;
318 let grid = me.up('pveGuestImportWindow');
319 let vm = grid.getViewModel();
320
321 // from pveDiskStorageSelector
322 let defaultStorage = values.hdstorage;
323 let defaultFormat = values.diskformat;
324 delete values.hdstorage;
325 delete values.diskformat;
326
327 let defaultBridge = values.defaultBridge;
328 delete values.defaultBridge;
329
330 let config = Ext.apply(grid.vmConfig, values);
331
332 if (config.scsi0) {
333 config.scsi0 = config.scsi0.replace('local:0,', 'local:0,format=qcow2,');
334 }
335
336 let parsedBoot = PVE.Parser.parsePropertyString(config.boot ?? '');
337 if (parsedBoot.order) {
338 parsedBoot.order = parsedBoot.order.split(';');
339 }
340
341 grid.lookup('diskGrid').getStore().each((rec) => {
342 if (!rec.data.enable) {
343 return;
344 }
345 let id = grid.getController().mapDisk(rec.data.id);
346 if (id !== rec.data.id && parsedBoot?.order) {
347 let idx = parsedBoot.order.indexOf(rec.data.id);
348 if (idx !== -1) {
349 parsedBoot.order[idx] = id;
350 }
351 }
352 let data = {
353 ...rec.data,
354 };
355 delete data.enable;
356 delete data.id;
357 delete data.size;
358 if (!data.file) {
359 data.file = defaultStorage;
360 data.format = defaultFormat;
361 }
362 data.file += ':0'; // for our special api format
363 if (id === 'efidisk0') {
364 delete data['import-from'];
365 }
366 config[id] = PVE.Parser.printQemuDrive(data);
367 });
368
369 if (parsedBoot.order) {
370 parsedBoot.order = parsedBoot.order.join(';');
371 }
372 config.boot = PVE.Parser.printPropertyString(parsedBoot);
373
374 grid.lookup('netGrid').getStore().each((rec) => {
375 if (!rec.data.enable) {
376 return;
377 }
378 let id = rec.data.id;
379 let data = {
380 ...rec.data,
381 };
382 delete data.enable;
383 delete data.id;
384 if (!data.bridge) {
385 data.bridge = defaultBridge;
386 }
387 if (vm.get('uniqueMACAdresses')) {
388 data.macaddr = undefined;
389 }
390 config[id] = PVE.Parser.printQemuNetwork(data);
391 });
392
393 grid.lookup('cdGrid').getStore().each((rec) => {
394 if (!rec.data.enable) {
395 return;
396 }
397 let id = rec.data.id;
398 let cd = {
399 media: 'cdrom',
400 file: rec.data.file ? rec.data.file : 'none',
401 };
402 config[id] = PVE.Parser.printPropertyString(cd);
403 });
404
405 config.scsihw = grid.lookup('scsihw').getValue();
406
407 if (grid.lookup('liveimport').getValue()) {
408 config['live-restore'] = 1;
409 }
410
411 // remove __default__ values
412 for (const [key, value] of Object.entries(config)) {
413 if (value === '__default__') {
414 delete config[key];
415 }
416 }
417
418 return config;
419 },
420
421 column1: [
422 {
423 xtype: 'pveGuestIDSelector',
424 name: 'vmid',
425 fieldLabel: 'VM',
426 guestType: 'qemu',
427 loadNextFreeID: true,
428 },
429 {
430 xtype: 'proxmoxintegerfield',
431 fieldLabel: gettext('Sockets'),
432 name: 'sockets',
433 reference: 'socketsField',
434 value: 1,
435 minValue: 1,
436 maxValue: 128,
437 allowBlank: true,
438 bind: {
439 value: '{socketCount}',
440 },
441 },
442 {
443 xtype: 'proxmoxintegerfield',
444 fieldLabel: gettext('Cores'),
445 name: 'cores',
446 reference: 'coresField',
447 value: 1,
448 minValue: 1,
449 maxValue: 1024,
450 allowBlank: true,
451 bind: {
452 value: '{coreCount}',
453 },
454 },
455 {
456 xtype: 'pveMemoryField',
457 fieldLabel: gettext('Memory') + ' (MiB)',
458 name: 'memory',
459 reference: 'memoryField',
460 value: 512,
461 allowBlank: true,
462 },
463 { xtype: 'displayfield' }, // spacer
464 { xtype: 'displayfield' }, // spacer
465 {
466 xtype: 'pveDiskStorageSelector',
467 reference: 'defaultStorage',
468 storageLabel: gettext('Default Storage'),
469 storageContent: 'images',
470 autoSelect: true,
471 hideSize: true,
472 name: 'defaultStorage',
473 },
474 ],
475
476 column2: [
477 {
478 xtype: 'textfield',
479 fieldLabel: gettext('Name'),
480 name: 'name',
481 vtype: 'DnsName',
482 reference: 'nameField',
483 allowBlank: true,
484 },
485 {
486 xtype: 'CPUModelSelector',
487 name: 'cpu',
488 reference: 'cputype',
489 value: 'x86-64-v2-AES',
490 fieldLabel: gettext('CPU Type'),
491 },
492 {
493 xtype: 'displayfield',
494 fieldLabel: gettext('Total cores'),
495 name: 'totalcores',
496 isFormField: false,
497 bind: {
498 value: '{totalCoreCount}',
499 },
500 },
501 {
502 xtype: 'combobox',
503 submitValue: false,
504 name: 'osbase',
505 fieldLabel: gettext('OS Type'),
506 editable: false,
507 queryMode: 'local',
508 value: 'Linux',
509 store: Object.keys(PVE.Utils.kvm_ostypes),
510 },
511 {
512 xtype: 'combobox',
513 name: 'ostype',
514 reference: 'ostype',
515 fieldLabel: gettext('Version'),
516 value: 'l26',
517 allowBlank: false,
518 editable: false,
519 queryMode: 'local',
520 valueField: 'val',
521 displayField: 'desc',
522 bind: {
523 value: '{os}',
524 },
525 store: {
526 fields: ['desc', 'val'],
527 data: PVE.Utils.kvm_ostypes.Linux,
528 },
529 },
530 { xtype: 'displayfield' }, // spacer
531 {
532 xtype: 'PVE.form.BridgeSelector',
533 reference: 'defaultBridge',
534 name: 'defaultBridge',
535 allowBlank: false,
536 fieldLabel: gettext('Default Bridge'),
537 },
538 ],
539
540 columnB: [
541 {
542 xtype: 'proxmoxcheckbox',
543 fieldLabel: gettext('Live Import'),
544 reference: 'liveimport',
545 isFormField: false,
546 boxLabelCls: 'pmx-hint black x-form-cb-label',
547 bind: {
548 value: '{liveImport}',
549 boxLabel: '{liveImportNote}',
550 },
551 },
552 {
553 xtype: 'displayfield',
554 fieldLabel: gettext('Warnings'),
555 labelWidth: 200,
556 hidden: true,
557 bind: {
558 hidden: '{hideWarnings}',
559 },
560 },
561 {
562 xtype: 'displayfield',
563 reference: 'warningText',
564 userCls: 'pmx-hint',
565 hidden: true,
566 bind: {
567 hidden: '{hideWarnings}',
568 value: '{warningsText}',
569 },
570 },
571 ],
572 },
573 {
574 title: gettext('Advanced'),
575 xtype: 'inputpanel',
576
577 // the first inputpanel handles all values, so prevent value leakage here
578 onGetValues: () => ({}),
579
580 columnT: [
581 {
582 xtype: 'displayfield',
583 fieldLabel: gettext('Disks'),
584 labelWidth: 200,
585 },
586 {
587 xtype: 'grid',
588 reference: 'diskGrid',
589 minHeight: 60,
590 maxHeight: 150,
591 store: {
592 data: [],
593 sorters: [
594 'id',
595 ],
596 },
597 columns: [
598 {
599 xtype: 'checkcolumn',
600 header: gettext('Use'),
601 width: 50,
602 dataIndex: 'enable',
603 listeners: {
604 checkchange: function(_column, _rowIndex, _checked, record) {
605 record.commit();
606 },
607 },
608 },
609 {
610 text: gettext('Disk'),
611 dataIndex: 'id',
612 renderer: 'mapDisk',
613 },
614 {
615 text: gettext('Source'),
616 dataIndex: 'import-from',
617 flex: 1,
618 renderer: function(value) {
619 return value.replace(/^.*\//, '');
620 },
621 },
622 {
623 text: gettext('Size'),
624 dataIndex: 'size',
625 renderer: (value) => {
626 if (Ext.isNumeric(value)) {
627 return Proxmox.Utils.render_size(value);
628 }
629 return value ?? Proxmox.Utils.unknownText;
630 },
631 },
632 {
633 text: gettext('Storage'),
634 dataIndex: 'file',
635 xtype: 'widgetcolumn',
636 width: 150,
637 widget: {
638 xtype: 'pveStorageSelector',
639 isFormField: false,
640 autoSelect: false,
641 allowBlank: true,
642 emptyText: gettext('From Default'),
643 name: 'file',
644 storageContent: 'images',
645 },
646 onWidgetAttach: 'setNodename',
647 },
648 {
649 text: gettext('Format'),
650 dataIndex: 'format',
651 xtype: 'widgetcolumn',
652 width: 150,
653 widget: {
654 xtype: 'pveDiskFormatSelector',
655 name: 'format',
656 disabled: true,
657 isFormField: false,
658 matchFieldWidth: false,
659 },
660 },
661 ],
662 },
663 ],
664
665 column1: [
666 {
667 xtype: 'proxmoxcheckbox',
668 fieldLabel: gettext('Prepare for VirtIO-SCSI'),
669 labelWidth: 200,
670 reference: 'mapSata',
671 name: 'mapSata',
672 submitValue: false,
673 disabled: true,
674 bind: {
675 disabled: '{!isWindows}',
676 },
677 autoEl: {
678 tag: 'div',
679 'data-qtip': gettext('Maps SCSI disks to SATA and changes the SCSI Controller. Useful for a quicker switch to VirtIO-SCSI attached disks'),
680 },
681 },
682 ],
683
684 column2: [
685 {
686 xtype: 'pveScsiHwSelector',
687 reference: 'scsihw',
688 name: 'scsihw',
689 value: '__default__',
690 submitValue: false,
691 fieldLabel: gettext('SCSI Controller'),
692 },
693 ],
694
695 columnB: [
696 {
697 xtype: 'displayfield',
698 fieldLabel: gettext('CD/DVD Drives'),
699 labelWidth: 200,
700 },
701 {
702 xtype: 'grid',
703 reference: 'cdGrid',
704 minHeight: 60,
705 maxHeight: 150,
706 store: {
707 data: [],
708 sorters: [
709 'id',
710 ],
711 filters: [
712 function(rec) {
713 return !rec.data.hidden;
714 },
715 ],
716 },
717 columns: [
718 {
719 xtype: 'checkcolumn',
720 header: gettext('Use'),
721 width: 50,
722 dataIndex: 'enable',
723 listeners: {
724 checkchange: function(_column, _rowIndex, _checked, record) {
725 record.commit();
726 },
727 },
728 },
729 {
730 text: gettext('Slot'),
731 dataIndex: 'id',
732 sorted: true,
733 },
734 {
735 text: gettext('Storage'),
736 xtype: 'widgetcolumn',
737 width: 150,
738 widget: {
739 xtype: 'pveStorageSelector',
740 isFormField: false,
741 autoSelect: false,
742 allowBlank: true,
743 emptyText: Proxmox.Utils.noneText,
744 storageContent: 'iso',
745 },
746 onWidgetAttach: 'setNodename',
747 },
748 {
749 text: gettext('ISO'),
750 dataIndex: 'file',
751 xtype: 'widgetcolumn',
752 flex: 1,
753 widget: {
754 xtype: 'pveFileSelector',
755 name: 'file',
756 isFormField: false,
757 allowBlank: true,
758 emptyText: Proxmox.Utils.noneText,
759 storageContent: 'iso',
760 },
761 onWidgetAttach: 'setNodename',
762 },
763 ],
764 },
765 {
766 xtype: 'displayfield',
767 fieldLabel: gettext('Network Interfaces'),
768 labelWidth: 200,
769 style: {
770 paddingTop: '10px',
771 },
772 },
773 {
774 xtype: 'grid',
775 minHeight: 58,
776 maxHeight: 150,
777 reference: 'netGrid',
778 store: {
779 data: [],
780 sorters: [
781 'id',
782 ],
783 },
784 columns: [
785 {
786 xtype: 'checkcolumn',
787 header: gettext('Use'),
788 width: 50,
789 dataIndex: 'enable',
790 listeners: {
791 checkchange: function(_column, _rowIndex, _checked, record) {
792 record.commit();
793 },
794 },
795 },
796 {
797 text: gettext('ID'),
798 dataIndex: 'id',
799 },
800 {
801 text: gettext('MAC address'),
802 flex: 1,
803 dataIndex: 'macaddr',
804 renderer: 'renderMacAddress',
805 },
806 {
807 text: gettext('Model'),
808 flex: 1,
809 dataIndex: 'model',
810 xtype: 'widgetcolumn',
811 widget: {
812 xtype: 'pveNetworkCardSelector',
813 name: 'model',
814 isFormField: false,
815 allowBlank: false,
816 },
817 },
818 {
819 text: gettext('Bridge'),
820 dataIndex: 'bridge',
821 xtype: 'widgetcolumn',
822 flex: 1,
823 widget: {
824 xtype: 'PVE.form.BridgeSelector',
825 name: 'bridge',
826 isFormField: false,
827 autoSelect: false,
828 allowBlank: true,
829 emptyText: gettext('From Default'),
830 },
831 onWidgetAttach: 'setNodename',
832 },
833 ],
834 },
835 {
836 xtype: 'proxmoxcheckbox',
837 name: 'uniqueMACs',
838 boxLabel: gettext('Unique MAC addresses'),
839 uncheckedValue: false,
840 value: false,
841 },
842 ],
843 },
844 {
845 title: gettext('Resulting Config'),
846 reference: 'summaryTab',
847 items: [
848 {
849 xtype: 'grid',
850 reference: 'summaryGrid',
851 maxHeight: 400,
852 scrollable: true,
853 store: {
854 model: 'KeyValue',
855 sorters: [{
856 property: 'key',
857 direction: 'ASC',
858 }],
859 },
860 columns: [
861 { header: 'Key', width: 150, dataIndex: 'key' },
862 { header: 'Value', flex: 1, dataIndex: 'value' },
863 ],
864 },
865 ],
866 },
867 ],
868 }],
869
870 initComponent: function() {
871 let me = this;
872
873 if (!me.volumeName) {
874 throw "no volumeName given";
875 }
876
877 if (!me.storage) {
878 throw "no storage given";
879 }
880
881 if (!me.nodename) {
882 throw "no nodename given";
883 }
884
885 me.callParent();
886
887 me.setTitle(Ext.String.format(gettext('Import Guest - {0}'), `${me.storage}:${me.volumeName}`));
888
889 me.lookup('defaultStorage').setNodename(me.nodename);
890 me.lookup('defaultBridge').setNodename(me.nodename);
891
892 let renderWarning = w => {
893 const warningsCatalogue = {
894 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, if required you can reconfigure the '{0}' drive in the 'Advanced' tab."),
895 'nvme-unsupported': gettext("NVMe disks are currently not supported, '{0}' will get attaced as SCSI"),
896 'ovmf-with-lsi-unsupported': gettext("OVMF is built without LSI drivers, scsi hardware was set to '{1}'"),
897 'serial-port-socket-only': gettext("Serial socket '{0}' will be mapped to a socket"),
898 'guest-is-running': gettext('Virtual guest seems to be running on source host. Import might fail or have inconsistent state!'),
899 'efi-state-lost': Ext.String.format(
900 gettext('EFI state cannot be imported, you may need to reconfigure the boot order (see {0})'),
901 '<a href="https://pve.proxmox.com/wiki/OVMF/UEFI_Boot_Entries">OVMF/UEFI Boot Entries</a>',
902 ),
903 };
904 let message = warningsCatalogue[w.type];
905 if (!w.type || !message) {
906 return w.message ?? w.type ?? gettext('Unknown warning');
907 }
908 return Ext.String.format(message, w.key ?? 'unknown', w.value ?? 'unknown');
909 };
910
911 me.load({
912 success: function(response) {
913 let data = response.result.data;
914 me.vmConfig = data['create-args'];
915
916 let disks = [];
917 for (const [id, value] of Object.entries(data.disks ?? {})) {
918 let volid = Ext.htmlEncode('<none>');
919 let size = 'auto';
920 if (Ext.isObject(value)) {
921 volid = value.volid;
922 size = value.size;
923 }
924 disks.push({
925 id,
926 enable: true,
927 size,
928 'import-from': volid,
929 format: 'raw',
930 });
931 }
932
933 let nets = [];
934 for (const [id, parsed] of Object.entries(data.net ?? {})) {
935 parsed.id = id;
936 parsed.enable = true;
937 nets.push(parsed);
938 }
939
940 let cdroms = [];
941 for (const [id, value] of Object.entries(me.vmConfig)) {
942 if (!Ext.isString(value) || !value.match(/media=cdrom/)) {
943 continue;
944 }
945 cdroms.push({
946 enable: true,
947 hidden: false,
948 id,
949 });
950 delete me.vmConfig[id];
951 }
952
953 me.lookup('diskGrid').getStore().setData(disks);
954 me.lookup('netGrid').getStore().setData(nets);
955 me.lookup('cdGrid').getStore().setData(cdroms);
956
957 let additionalCdIdx = me.getController().calculateAdditionalCDIdx();
958 if (additionalCdIdx === '') {
959 me.getViewModel().set('maxCdDrives', true);
960 } else if (cdroms.length === 0) {
961 me.additionalCdIdx = additionalCdIdx;
962 me.lookup('cdGrid').getStore().add({
963 enable: true,
964 hidden: !(me.vmConfig.ostype ?? '').startsWith('w'),
965 id: additionalCdIdx,
966 autogenerated: true,
967 });
968 }
969
970 me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
971
972 let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');
973 let mapSata = (me.vmConfig.ostype ?? '').startsWith('w') && (me.vmConfig.bios ?? '').indexOf('ovmf') !== -1;
974
975 me.setValues({
976 osbase: osinfo.base,
977 ...me.vmConfig,
978 });
979
980
981 me.lookup('mapSata').setValue(mapSata);
982 },
983 });
984 },
985 });