]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/GuestImport.js
ui: guest import: implement 'map-to-sata' checkbox
[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
311 if (grid.lookup('liveimport').getValue()) {
312 config['live-restore'] = 1;
313 }
314
315 return config;
bb3fa9de 316 },
bb3fa9de 317
b0eeb862 318 column1: [
bb3fa9de 319 {
b0eeb862
DC
320 xtype: 'pveGuestIDSelector',
321 name: 'vmid',
322 fieldLabel: 'VM',
323 guestType: 'qemu',
324 loadNextFreeID: true,
bb3fa9de
DC
325 },
326 {
b0eeb862
DC
327 xtype: 'proxmoxintegerfield',
328 fieldLabel: gettext('Sockets'),
329 name: 'sockets',
330 reference: 'socketsField',
331 value: 1,
332 minValue: 1,
333 maxValue: 4,
334 allowBlank: true,
335 bind: {
336 value: '{socketCount}',
337 },
bb3fa9de
DC
338 },
339 {
b0eeb862
DC
340 xtype: 'proxmoxintegerfield',
341 fieldLabel: gettext('Cores'),
342 name: 'cores',
343 reference: 'coresField',
344 value: 1,
345 minValue: 1,
346 maxValue: 128,
347 allowBlank: true,
348 bind: {
349 value: '{coreCount}',
bb3fa9de
DC
350 },
351 },
352 {
b0eeb862
DC
353 xtype: 'pveMemoryField',
354 fieldLabel: gettext('Memory'),
355 name: 'memory',
356 reference: 'memoryField',
357 value: 512,
358 allowBlank: true,
bb3fa9de
DC
359 },
360 {
b0eeb862
DC
361 //spacer
362 xtype: 'displayfield',
363 },
364 {
365 xtype: 'pveDiskStorageSelector',
366 reference: 'defaultStorage',
367 storageLabel: gettext('Default Storage'),
368 storageContent: 'images',
369 autoSelect: true,
370 hideSize: true,
371 name: 'defaultStorage',
bb3fa9de
DC
372 },
373 ],
b0eeb862
DC
374
375 column2: [
b7583d45 376 {
b0eeb862
DC
377 xtype: 'textfield',
378 fieldLabel: gettext('Name'),
379 name: 'name',
380 vtype: 'DnsName',
381 reference: 'nameField',
382 allowBlank: true,
383 },
384 {
385 xtype: 'CPUModelSelector',
386 name: 'cpu',
387 reference: 'cputype',
388 value: 'x86-64-v2-AES',
389 fieldLabel: gettext('Type'),
390 },
391 {
392 xtype: 'displayfield',
393 fieldLabel: gettext('Total cores'),
394 name: 'totalcores',
395 isFormField: false,
396 bind: {
397 value: '{totalCoreCount}',
398 },
399 },
400 {
401 xtype: 'combobox',
402 submitValue: false,
403 name: 'osbase',
404 fieldLabel: gettext('OS Type'),
405 editable: false,
406 queryMode: 'local',
407 value: 'Linux',
408 store: Object.keys(PVE.Utils.kvm_ostypes),
409 },
410 {
411 xtype: 'combobox',
412 name: 'ostype',
413 reference: 'ostype',
414 fieldLabel: gettext('Version'),
415 value: 'l26',
416 allowBlank: false,
417 editable: false,
418 queryMode: 'local',
419 valueField: 'val',
420 displayField: 'desc',
d39e2492
DC
421 bind: {
422 value: '{os}',
423 },
b0eeb862
DC
424 store: {
425 fields: ['desc', 'val'],
426 data: PVE.Utils.kvm_ostypes.Linux,
b7583d45
DC
427 },
428 },
429 {
b0eeb862
DC
430 xtype: 'PVE.form.BridgeSelector',
431 reference: 'defaultBridge',
432 name: 'defaultBridge',
433 allowBlank: false,
434 fieldLabel: gettext('Default Bridge'),
b7583d45 435 },
b0eeb862
DC
436 ],
437
438 columnB: [
1c2c2f69
DC
439 {
440 xtype: 'proxmoxcheckbox',
441 fieldLabel: gettext('Live Import'),
442 reference: 'liveimport',
443 isFormField: false,
226f01cb
TL
444 boxLabelCls: 'pmx-hint black x-form-cb-label',
445 bind: {
446 value: '{liveImport}',
447 boxLabel: '{liveImportNote}',
448 },
1c2c2f69 449 },
b7583d45 450 {
b0eeb862
DC
451 xtype: 'displayfield',
452 fieldLabel: gettext('Warnings'),
453 labelWidth: 200,
454 hidden: true,
455 bind: {
456 hidden: '{hideWarnings}',
b7583d45 457 },
b7583d45
DC
458 },
459 {
b0eeb862
DC
460 xtype: 'displayfield',
461 reference: 'warningText',
462 userCls: 'pmx-hint',
463 hidden: true,
464 bind: {
465 hidden: '{hideWarnings}',
466 value: '{warningsText}',
b7583d45 467 },
b7583d45
DC
468 },
469 ],
470 },
bb3fa9de 471 {
b0eeb862
DC
472 title: gettext('Advanced'),
473 xtype: 'inputpanel',
d39e2492
DC
474
475 column1: [
476 {
477 xtype: 'proxmoxcheckbox',
478 fieldLabel: gettext('Map SCSI to SATA'),
479 labelWidth: 120,
480 reference: 'mapSata',
481 isFormField: false,
482 hidden: true,
483 disabled: true,
484 bind: {
485 hidden: '{!isWindows}',
486 disabled: '{!isWindows}',
487 },
488 autoEl: {
489 tag: 'div',
490 'data-qtip': gettext('Useful when wanting to use VirtIO-SCSI'),
491 },
492 },
493 ],
494
495 columnB: [
bb3fa9de 496 {
b0eeb862
DC
497 xtype: 'displayfield',
498 fieldLabel: gettext('Disks'),
499 labelWidth: 200,
bb3fa9de
DC
500 },
501 {
b0eeb862
DC
502 xtype: 'grid',
503 reference: 'diskGrid',
163868e1 504 minHeight: 60,
b0eeb862
DC
505 maxHeight: 150,
506 store: {
507 data: [],
508 sorters: [
509 'id',
510 ],
511 },
512 columns: [
513 {
514 xtype: 'checkcolumn',
515 header: gettext('Use'),
516 width: 50,
517 dataIndex: 'enable',
518 listeners: {
519 checkchange: function(_column, _rowIndex, _checked, record) {
520 record.commit();
521 },
522 },
523 },
524 {
525 text: gettext('Disk'),
526 dataIndex: 'id',
d39e2492 527 renderer: 'mapDisk',
b0eeb862
DC
528 },
529 {
530 text: gettext('Source'),
531 dataIndex: 'import-from',
532 flex: 1,
533 renderer: function(value) {
534 return value.replace(/^.*\//, '');
535 },
536 },
a5e56799
DC
537 {
538 text: gettext('Size'),
539 dataIndex: 'size',
540 renderer: (value) => {
541 if (Ext.isNumeric(value)) {
542 return Proxmox.Utils.render_size(value);
543 }
544 return value ?? Proxmox.Utils.unknownText;
545 },
546 },
b0eeb862
DC
547 {
548 text: gettext('Storage'),
549 dataIndex: 'file',
550 xtype: 'widgetcolumn',
551 width: 150,
552 widget: {
553 xtype: 'pveStorageSelector',
554 isFormField: false,
555 autoSelect: false,
556 allowBlank: true,
557 emptyText: gettext('From Default'),
558 name: 'file',
559 storageContent: 'images',
560 },
561 onWidgetAttach: 'setNodename',
562 },
563 {
564 text: gettext('Format'),
565 dataIndex: 'format',
566 xtype: 'widgetcolumn',
567 width: 150,
568 widget: {
569 xtype: 'pveDiskFormatSelector',
570 name: 'format',
571 disabled: true,
572 isFormField: false,
573 matchFieldWidth: false,
574 },
575 },
576 ],
bb3fa9de
DC
577 },
578 {
b0eeb862
DC
579 xtype: 'displayfield',
580 fieldLabel: gettext('CD/DVD Drives'),
581 labelWidth: 200,
163868e1
TL
582 style: {
583 paddingTop: '10px',
584 },
bb3fa9de
DC
585 },
586 {
b0eeb862
DC
587 xtype: 'grid',
588 reference: 'cdGrid',
163868e1 589 minHeight: 60,
b0eeb862
DC
590 maxHeight: 150,
591 store: {
592 data: [],
593 sorters: [
594 'id',
595 ],
81c46efc 596 },
b0eeb862
DC
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('Slot'),
611 dataIndex: 'id',
612 sorted: true,
613 },
614 {
615 text: gettext('Storage'),
616 xtype: 'widgetcolumn',
617 width: 150,
618 widget: {
619 xtype: 'pveStorageSelector',
620 isFormField: false,
621 autoSelect: false,
622 allowBlank: true,
623 emptyText: Proxmox.Utils.noneText,
624 storageContent: 'iso',
625 },
626 onWidgetAttach: 'setNodename',
627 },
628 {
629 text: gettext('ISO'),
630 dataIndex: 'file',
631 xtype: 'widgetcolumn',
632 flex: 1,
633 widget: {
634 xtype: 'pveFileSelector',
635 name: 'file',
636 isFormField: false,
637 allowBlank: true,
638 emptyText: Proxmox.Utils.noneText,
639 storageContent: 'iso',
640 },
641 onWidgetAttach: 'setNodename',
642 },
643 ],
644 },
645 {
646 xtype: 'displayfield',
647 fieldLabel: gettext('Network Interfaces'),
648 labelWidth: 200,
163868e1
TL
649 style: {
650 paddingTop: '10px',
651 },
bb3fa9de
DC
652 },
653 {
b0eeb862
DC
654 xtype: 'grid',
655 minHeight: 58,
656 maxHeight: 150,
657 reference: 'netGrid',
658 store: {
659 data: [],
660 sorters: [
661 'id',
662 ],
bb3fa9de 663 },
b0eeb862
DC
664 columns: [
665 {
666 xtype: 'checkcolumn',
667 header: gettext('Use'),
668 width: 50,
669 dataIndex: 'enable',
670 listeners: {
671 checkchange: function(_column, _rowIndex, _checked, record) {
672 record.commit();
673 },
674 },
675 },
676 {
677 text: gettext('ID'),
678 dataIndex: 'id',
679 },
680 {
681 text: gettext('MAC address'),
682 flex: 1,
683 dataIndex: 'macaddr',
684 renderer: value => value ?? 'auto',
685 },
686 {
687 text: gettext('Model'),
688 flex: 1,
689 dataIndex: 'model',
690 xtype: 'widgetcolumn',
691 widget: {
692 xtype: 'pveNetworkCardSelector',
693 name: 'model',
694 isFormField: false,
695 allowBlank: false,
696 },
697 },
698 {
699 text: gettext('Bridge'),
700 dataIndex: 'bridge',
701 xtype: 'widgetcolumn',
702 flex: 1,
703 widget: {
704 xtype: 'PVE.form.BridgeSelector',
705 name: 'bridge',
706 isFormField: false,
707 autoSelect: false,
708 allowBlank: true,
709 emptyText: gettext('From Default'),
710 },
711 onWidgetAttach: 'setNodename',
712 },
713 ],
bb3fa9de
DC
714 },
715 ],
716 },
c52eb476
DC
717 {
718 title: gettext('Resulting Config'),
719 reference: 'summaryTab',
720 items: [
721 {
722 xtype: 'grid',
723 reference: 'summaryGrid',
724 maxHeight: 400,
725 scrollable: true,
726 store: {
727 model: 'KeyValue',
728 sorters: [{
729 property: 'key',
730 direction: 'ASC',
731 }],
732 },
733 columns: [
734 { header: 'Key', width: 150, dataIndex: 'key' },
735 { header: 'Value', flex: 1, dataIndex: 'value' },
736 ],
737 },
738 ],
739 },
bb3fa9de
DC
740 ],
741 },
742 ],
743
744 initComponent: function() {
745 let me = this;
746
747 if (!me.volumeName) {
748 throw "no volumeName given";
749 }
750
751 if (!me.storage) {
752 throw "no storage given";
753 }
754
755 if (!me.nodename) {
756 throw "no nodename given";
757 }
758
759 me.callParent();
760
bb3fa9de
DC
761 me.setTitle(Ext.String.format(gettext('Import Guest - {0}'), `${me.storage}:${me.volumeName}`));
762
b0eeb862
DC
763 me.lookup('defaultStorage').setNodename(me.nodename);
764 me.lookup('defaultBridge').setNodename(me.nodename);
765
03d8d7d2
TL
766 let renderWarning = w => {
767 const warningsCatalogue = {
463b9b82 768 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, if required you can reconfigure the '{0}' drive in the 'Advanced' tab."),
03d8d7d2
TL
769 'nvme-unsupported': gettext("NVMe disks are currently not supported, '{0}' will get attaced as SCSI"),
770 'ovmf-with-lsi-unsupported': gettext("OVMF is built without LSI drivers, scsi hardware was set to '{1}'"),
771 'serial-port-socket-only': gettext("Serial socket '{0}' will be mapped to a socket"),
b185bbad 772 'guest-is-running': gettext('Virtual guest seems to be running on source host. Import might fail or have inconsistent state!'),
03d8d7d2
TL
773 };
774 let message = warningsCatalogue[w.type];
775 if (!w.type || !message) {
776 return w.message ?? w.type ?? gettext('Unknown warning');
777 }
778 return Ext.String.format(message, w.key ?? 'unknown', w.value ?? 'unknown');
779 };
780
bb3fa9de
DC
781 me.load({
782 success: function(response) {
783 let data = response.result.data;
784 me.vmConfig = data['create-args'];
785
786 let disks = [];
787 for (const [id, value] of Object.entries(data.disks ?? {})) {
a5e56799
DC
788 let volid = Ext.htmlEncode('<none>');
789 let size = 'auto';
790 if (Ext.isObject(value)) {
791 volid = value.volid;
792 size = value.size;
793 }
bb3fa9de
DC
794 disks.push({
795 id,
796 enable: true,
a5e56799
DC
797 size,
798 'import-from': volid,
bb3fa9de
DC
799 format: 'raw',
800 });
801 }
802
803 let nets = [];
804 for (const [id, parsed] of Object.entries(data.net ?? {})) {
805 parsed.id = id;
806 parsed.enable = true;
807 nets.push(parsed);
808 }
b7583d45
DC
809
810 let cdroms = [];
811 for (const [id, value] of Object.entries(me.vmConfig)) {
812 if (!Ext.isString(value) || !value.match(/media=cdrom/)) {
813 continue;
814 }
815 cdroms.push({
816 enable: true,
817 id,
818 });
819 delete me.vmConfig[id];
820 }
bb3fa9de
DC
821 me.lookup('diskGrid').getStore().setData(disks);
822 me.lookup('netGrid').getStore().setData(nets);
b7583d45 823 me.lookup('cdGrid').getStore().setData(cdroms);
bb3fa9de 824
03d8d7d2 825 me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
bb3fa9de 826
4fb223f7
DC
827 let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');
828
829 me.setValues({
830 osbase: osinfo.base,
831 ...me.vmConfig,
832 });
bb3fa9de
DC
833 },
834 });
835 },
836});