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