]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/window/GuestImport.js
ui: guest import: update text for cdrom-image-ignored warning
[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
bb3fa9de
DC
95 control: {
96 'grid field': {
97 // update records from widgetcolumns
98 change: function(widget, value) {
99 let rec = widget.getWidgetRecord();
100 rec.set(widget.name, value);
101 rec.commit();
102 },
103 },
b7583d45
DC
104 'grid[reference=diskGrid] pveStorageSelector': {
105 change: 'diskStorageChange',
106 },
107 'grid[reference=cdGrid] pveStorageSelector': {
108 change: 'isoStorageChange',
bb3fa9de 109 },
4fb223f7
DC
110 'field[name=osbase]': {
111 change: 'onOSBaseChange',
112 },
c52eb476
DC
113 'panel[reference=summaryTab]': {
114 activate: 'calculateConfig',
115 },
bb3fa9de
DC
116 },
117 },
118
119 viewModel: {
120 data: {
121 coreCount: 1,
122 socketCount: 1,
123 warnings: [],
124 },
125
126 formulas: {
127 totalCoreCount: get => get('socketCount') * get('coreCount'),
128 hideWarnings: get => get('warnings').length === 0,
03d8d7d2
TL
129 warningsText: get => '<ul style="margin: 0; padding-left: 20px;">'
130 + get('warnings').map(w => `<li>${w}</li>`).join('') + '</ul>',
bb3fa9de
DC
131 },
132 },
133
11236006 134 width: 700,
b0eeb862
DC
135 bodyPadding: 0,
136
bb3fa9de
DC
137 items: [
138 {
b0eeb862
DC
139 xtype: 'tabpanel',
140 defaults: {
141 bodyPadding: 10,
bb3fa9de 142 },
b0eeb862 143 items: [
bb3fa9de 144 {
b0eeb862
DC
145 title: gettext('General'),
146 xtype: 'inputpanel',
147 reference: 'mainInputPanel',
148 onGetValues: function(values) {
149 let me = this;
150 let grid = me.up('pveGuestImportWindow');
151
152 // from pveDiskStorageSelector
153 let defaultStorage = values.hdstorage;
154 let defaultFormat = values.diskformat;
155 delete values.hdstorage;
156 delete values.diskformat;
157
158 let defaultBridge = values.defaultBridge;
159 delete values.defaultBridge;
160
161 let config = Ext.apply(grid.vmConfig, values);
162
163 if (config.scsi0) {
164 config.scsi0 = config.scsi0.replace('local:0,', 'local:0,format=qcow2,');
165 }
166
167 grid.lookup('diskGrid').getStore().each((rec) => {
168 if (!rec.data.enable) {
169 return;
170 }
171 let id = rec.data.id;
172 let data = {
173 ...rec.data,
174 };
175 delete data.enable;
176 delete data.id;
a5e56799 177 delete data.size;
b0eeb862
DC
178 if (!data.file) {
179 data.file = defaultStorage;
180 data.format = defaultFormat;
181 }
182 data.file += ':0'; // for our special api format
183 if (id === 'efidisk0') {
184 delete data['import-from'];
185 }
186 config[id] = PVE.Parser.printQemuDrive(data);
187 });
188
189 grid.lookup('netGrid').getStore().each((rec) => {
190 if (!rec.data.enable) {
191 return;
192 }
193 let id = rec.data.id;
194 let data = {
195 ...rec.data,
196 };
197 delete data.enable;
198 delete data.id;
199 if (!data.bridge) {
200 data.bridge = defaultBridge;
201 }
202 config[id] = PVE.Parser.printQemuNetwork(data);
203 });
204
205 grid.lookup('cdGrid').getStore().each((rec) => {
206 if (!rec.data.enable) {
207 return;
208 }
209 let id = rec.data.id;
210 let cd = {
211 media: 'cdrom',
212 file: rec.data.file ? rec.data.file : 'none',
213 };
214 config[id] = PVE.Parser.printPropertyString(cd);
215 });
216
217 if (grid.lookup('liveimport').getValue()) {
218 config['live-restore'] = 1;
219 }
220
221 return config;
bb3fa9de 222 },
bb3fa9de 223
b0eeb862 224 column1: [
bb3fa9de 225 {
b0eeb862
DC
226 xtype: 'pveGuestIDSelector',
227 name: 'vmid',
228 fieldLabel: 'VM',
229 guestType: 'qemu',
230 loadNextFreeID: true,
bb3fa9de
DC
231 },
232 {
b0eeb862
DC
233 xtype: 'proxmoxintegerfield',
234 fieldLabel: gettext('Sockets'),
235 name: 'sockets',
236 reference: 'socketsField',
237 value: 1,
238 minValue: 1,
239 maxValue: 4,
240 allowBlank: true,
241 bind: {
242 value: '{socketCount}',
243 },
bb3fa9de
DC
244 },
245 {
b0eeb862
DC
246 xtype: 'proxmoxintegerfield',
247 fieldLabel: gettext('Cores'),
248 name: 'cores',
249 reference: 'coresField',
250 value: 1,
251 minValue: 1,
252 maxValue: 128,
253 allowBlank: true,
254 bind: {
255 value: '{coreCount}',
bb3fa9de
DC
256 },
257 },
258 {
b0eeb862
DC
259 xtype: 'pveMemoryField',
260 fieldLabel: gettext('Memory'),
261 name: 'memory',
262 reference: 'memoryField',
263 value: 512,
264 allowBlank: true,
bb3fa9de
DC
265 },
266 {
b0eeb862
DC
267 //spacer
268 xtype: 'displayfield',
269 },
270 {
271 xtype: 'pveDiskStorageSelector',
272 reference: 'defaultStorage',
273 storageLabel: gettext('Default Storage'),
274 storageContent: 'images',
275 autoSelect: true,
276 hideSize: true,
277 name: 'defaultStorage',
bb3fa9de
DC
278 },
279 ],
b0eeb862
DC
280
281 column2: [
b7583d45 282 {
b0eeb862
DC
283 xtype: 'textfield',
284 fieldLabel: gettext('Name'),
285 name: 'name',
286 vtype: 'DnsName',
287 reference: 'nameField',
288 allowBlank: true,
289 },
290 {
291 xtype: 'CPUModelSelector',
292 name: 'cpu',
293 reference: 'cputype',
294 value: 'x86-64-v2-AES',
295 fieldLabel: gettext('Type'),
296 },
297 {
298 xtype: 'displayfield',
299 fieldLabel: gettext('Total cores'),
300 name: 'totalcores',
301 isFormField: false,
302 bind: {
303 value: '{totalCoreCount}',
304 },
305 },
306 {
307 xtype: 'combobox',
308 submitValue: false,
309 name: 'osbase',
310 fieldLabel: gettext('OS Type'),
311 editable: false,
312 queryMode: 'local',
313 value: 'Linux',
314 store: Object.keys(PVE.Utils.kvm_ostypes),
315 },
316 {
317 xtype: 'combobox',
318 name: 'ostype',
319 reference: 'ostype',
320 fieldLabel: gettext('Version'),
321 value: 'l26',
322 allowBlank: false,
323 editable: false,
324 queryMode: 'local',
325 valueField: 'val',
326 displayField: 'desc',
327 store: {
328 fields: ['desc', 'val'],
329 data: PVE.Utils.kvm_ostypes.Linux,
b7583d45
DC
330 },
331 },
332 {
b0eeb862
DC
333 xtype: 'PVE.form.BridgeSelector',
334 reference: 'defaultBridge',
335 name: 'defaultBridge',
336 allowBlank: false,
337 fieldLabel: gettext('Default Bridge'),
b7583d45 338 },
b0eeb862
DC
339 ],
340
341 columnB: [
1c2c2f69
DC
342 {
343 xtype: 'proxmoxcheckbox',
344 fieldLabel: gettext('Live Import'),
345 reference: 'liveimport',
346 isFormField: false,
347 boxLabel: gettext('Experimental'),
348 },
b7583d45 349 {
b0eeb862
DC
350 xtype: 'displayfield',
351 fieldLabel: gettext('Warnings'),
352 labelWidth: 200,
353 hidden: true,
354 bind: {
355 hidden: '{hideWarnings}',
b7583d45 356 },
b7583d45
DC
357 },
358 {
b0eeb862
DC
359 xtype: 'displayfield',
360 reference: 'warningText',
361 userCls: 'pmx-hint',
362 hidden: true,
363 bind: {
364 hidden: '{hideWarnings}',
365 value: '{warningsText}',
b7583d45 366 },
b7583d45
DC
367 },
368 ],
369 },
bb3fa9de 370 {
b0eeb862
DC
371 title: gettext('Advanced'),
372 xtype: 'inputpanel',
373 items: [
bb3fa9de 374 {
b0eeb862
DC
375 xtype: 'displayfield',
376 fieldLabel: gettext('Disks'),
377 labelWidth: 200,
bb3fa9de
DC
378 },
379 {
b0eeb862
DC
380 xtype: 'grid',
381 reference: 'diskGrid',
382 minHeight: 58,
383 maxHeight: 150,
384 store: {
385 data: [],
386 sorters: [
387 'id',
388 ],
389 },
390 columns: [
391 {
392 xtype: 'checkcolumn',
393 header: gettext('Use'),
394 width: 50,
395 dataIndex: 'enable',
396 listeners: {
397 checkchange: function(_column, _rowIndex, _checked, record) {
398 record.commit();
399 },
400 },
401 },
402 {
403 text: gettext('Disk'),
404 dataIndex: 'id',
405 },
406 {
407 text: gettext('Source'),
408 dataIndex: 'import-from',
409 flex: 1,
410 renderer: function(value) {
411 return value.replace(/^.*\//, '');
412 },
413 },
a5e56799
DC
414 {
415 text: gettext('Size'),
416 dataIndex: 'size',
417 renderer: (value) => {
418 if (Ext.isNumeric(value)) {
419 return Proxmox.Utils.render_size(value);
420 }
421 return value ?? Proxmox.Utils.unknownText;
422 },
423 },
b0eeb862
DC
424 {
425 text: gettext('Storage'),
426 dataIndex: 'file',
427 xtype: 'widgetcolumn',
428 width: 150,
429 widget: {
430 xtype: 'pveStorageSelector',
431 isFormField: false,
432 autoSelect: false,
433 allowBlank: true,
434 emptyText: gettext('From Default'),
435 name: 'file',
436 storageContent: 'images',
437 },
438 onWidgetAttach: 'setNodename',
439 },
440 {
441 text: gettext('Format'),
442 dataIndex: 'format',
443 xtype: 'widgetcolumn',
444 width: 150,
445 widget: {
446 xtype: 'pveDiskFormatSelector',
447 name: 'format',
448 disabled: true,
449 isFormField: false,
450 matchFieldWidth: false,
451 },
452 },
453 ],
bb3fa9de
DC
454 },
455 {
b0eeb862
DC
456 xtype: 'displayfield',
457 fieldLabel: gettext('CD/DVD Drives'),
458 labelWidth: 200,
bb3fa9de
DC
459 },
460 {
b0eeb862
DC
461 xtype: 'grid',
462 reference: 'cdGrid',
463 minHeight: 58,
464 maxHeight: 150,
465 store: {
466 data: [],
467 sorters: [
468 'id',
469 ],
81c46efc 470 },
b0eeb862
DC
471 columns: [
472 {
473 xtype: 'checkcolumn',
474 header: gettext('Use'),
475 width: 50,
476 dataIndex: 'enable',
477 listeners: {
478 checkchange: function(_column, _rowIndex, _checked, record) {
479 record.commit();
480 },
481 },
482 },
483 {
484 text: gettext('Slot'),
485 dataIndex: 'id',
486 sorted: true,
487 },
488 {
489 text: gettext('Storage'),
490 xtype: 'widgetcolumn',
491 width: 150,
492 widget: {
493 xtype: 'pveStorageSelector',
494 isFormField: false,
495 autoSelect: false,
496 allowBlank: true,
497 emptyText: Proxmox.Utils.noneText,
498 storageContent: 'iso',
499 },
500 onWidgetAttach: 'setNodename',
501 },
502 {
503 text: gettext('ISO'),
504 dataIndex: 'file',
505 xtype: 'widgetcolumn',
506 flex: 1,
507 widget: {
508 xtype: 'pveFileSelector',
509 name: 'file',
510 isFormField: false,
511 allowBlank: true,
512 emptyText: Proxmox.Utils.noneText,
513 storageContent: 'iso',
514 },
515 onWidgetAttach: 'setNodename',
516 },
517 ],
518 },
519 {
520 xtype: 'displayfield',
521 fieldLabel: gettext('Network Interfaces'),
522 labelWidth: 200,
bb3fa9de
DC
523 },
524 {
b0eeb862
DC
525 xtype: 'grid',
526 minHeight: 58,
527 maxHeight: 150,
528 reference: 'netGrid',
529 store: {
530 data: [],
531 sorters: [
532 'id',
533 ],
bb3fa9de 534 },
b0eeb862
DC
535 columns: [
536 {
537 xtype: 'checkcolumn',
538 header: gettext('Use'),
539 width: 50,
540 dataIndex: 'enable',
541 listeners: {
542 checkchange: function(_column, _rowIndex, _checked, record) {
543 record.commit();
544 },
545 },
546 },
547 {
548 text: gettext('ID'),
549 dataIndex: 'id',
550 },
551 {
552 text: gettext('MAC address'),
553 flex: 1,
554 dataIndex: 'macaddr',
555 renderer: value => value ?? 'auto',
556 },
557 {
558 text: gettext('Model'),
559 flex: 1,
560 dataIndex: 'model',
561 xtype: 'widgetcolumn',
562 widget: {
563 xtype: 'pveNetworkCardSelector',
564 name: 'model',
565 isFormField: false,
566 allowBlank: false,
567 },
568 },
569 {
570 text: gettext('Bridge'),
571 dataIndex: 'bridge',
572 xtype: 'widgetcolumn',
573 flex: 1,
574 widget: {
575 xtype: 'PVE.form.BridgeSelector',
576 name: 'bridge',
577 isFormField: false,
578 autoSelect: false,
579 allowBlank: true,
580 emptyText: gettext('From Default'),
581 },
582 onWidgetAttach: 'setNodename',
583 },
584 ],
bb3fa9de
DC
585 },
586 ],
587 },
c52eb476
DC
588 {
589 title: gettext('Resulting Config'),
590 reference: 'summaryTab',
591 items: [
592 {
593 xtype: 'grid',
594 reference: 'summaryGrid',
595 maxHeight: 400,
596 scrollable: true,
597 store: {
598 model: 'KeyValue',
599 sorters: [{
600 property: 'key',
601 direction: 'ASC',
602 }],
603 },
604 columns: [
605 { header: 'Key', width: 150, dataIndex: 'key' },
606 { header: 'Value', flex: 1, dataIndex: 'value' },
607 ],
608 },
609 ],
610 },
bb3fa9de
DC
611 ],
612 },
613 ],
614
615 initComponent: function() {
616 let me = this;
617
618 if (!me.volumeName) {
619 throw "no volumeName given";
620 }
621
622 if (!me.storage) {
623 throw "no storage given";
624 }
625
626 if (!me.nodename) {
627 throw "no nodename given";
628 }
629
630 me.callParent();
631
bb3fa9de
DC
632 me.setTitle(Ext.String.format(gettext('Import Guest - {0}'), `${me.storage}:${me.volumeName}`));
633
b0eeb862
DC
634 me.lookup('defaultStorage').setNodename(me.nodename);
635 me.lookup('defaultBridge').setNodename(me.nodename);
636
03d8d7d2
TL
637 let renderWarning = w => {
638 const warningsCatalogue = {
463b9b82 639 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, if required you can reconfigure the '{0}' drive in the 'Advanced' tab."),
03d8d7d2
TL
640 'nvme-unsupported': gettext("NVMe disks are currently not supported, '{0}' will get attaced as SCSI"),
641 'ovmf-with-lsi-unsupported': gettext("OVMF is built without LSI drivers, scsi hardware was set to '{1}'"),
642 'serial-port-socket-only': gettext("Serial socket '{0}' will be mapped to a socket"),
643 };
644 let message = warningsCatalogue[w.type];
645 if (!w.type || !message) {
646 return w.message ?? w.type ?? gettext('Unknown warning');
647 }
648 return Ext.String.format(message, w.key ?? 'unknown', w.value ?? 'unknown');
649 };
650
bb3fa9de
DC
651 me.load({
652 success: function(response) {
653 let data = response.result.data;
654 me.vmConfig = data['create-args'];
655
656 let disks = [];
657 for (const [id, value] of Object.entries(data.disks ?? {})) {
a5e56799
DC
658 let volid = Ext.htmlEncode('<none>');
659 let size = 'auto';
660 if (Ext.isObject(value)) {
661 volid = value.volid;
662 size = value.size;
663 }
bb3fa9de
DC
664 disks.push({
665 id,
666 enable: true,
a5e56799
DC
667 size,
668 'import-from': volid,
bb3fa9de
DC
669 format: 'raw',
670 });
671 }
672
673 let nets = [];
674 for (const [id, parsed] of Object.entries(data.net ?? {})) {
675 parsed.id = id;
676 parsed.enable = true;
677 nets.push(parsed);
678 }
b7583d45
DC
679
680 let cdroms = [];
681 for (const [id, value] of Object.entries(me.vmConfig)) {
682 if (!Ext.isString(value) || !value.match(/media=cdrom/)) {
683 continue;
684 }
685 cdroms.push({
686 enable: true,
687 id,
688 });
689 delete me.vmConfig[id];
690 }
bb3fa9de
DC
691 me.lookup('diskGrid').getStore().setData(disks);
692 me.lookup('netGrid').getStore().setData(nets);
b7583d45 693 me.lookup('cdGrid').getStore().setData(cdroms);
bb3fa9de 694
03d8d7d2 695 me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
bb3fa9de 696
4fb223f7
DC
697 let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');
698
699 me.setValues({
700 osbase: osinfo.base,
701 ...me.vmConfig,
702 });
bb3fa9de
DC
703 },
704 });
705 },
706});