]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/window/GuestImport.js
ui: guest import: add ostype selector
[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 storageChange: 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 onOSBaseChange: function(_field, value) {
56 let me = this;
57 let ostype = me.lookup('ostype');
58 let store = ostype.getStore();
59 store.setData(PVE.Utils.kvm_ostypes[value]);
60 let old_val = ostype.getValue();
61 if (old_val && store.find('val', old_val) !== -1) {
62 ostype.setValue(old_val);
63 } else {
64 ostype.setValue(store.getAt(0));
65 }
66 },
67
68 control: {
69 'grid field': {
70 // update records from widgetcolumns
71 change: function(widget, value) {
72 let rec = widget.getWidgetRecord();
73 rec.set(widget.name, value);
74 rec.commit();
75 },
76 },
77 'pveStorageSelector': {
78 change: 'storageChange',
79 },
80 'field[name=osbase]': {
81 change: 'onOSBaseChange',
82 },
83 },
84 },
85
86 viewModel: {
87 data: {
88 coreCount: 1,
89 socketCount: 1,
90 warnings: [],
91 },
92
93 formulas: {
94 totalCoreCount: get => get('socketCount') * get('coreCount'),
95 hideWarnings: get => get('warnings').length === 0,
96 warningsText: get => '<ul style="margin: 0; padding-left: 20px;">'
97 + get('warnings').map(w => `<li>${w}</li>`).join('') + '</ul>',
98 },
99 },
100
101 items: [
102 {
103 xtype: 'inputpanel',
104 onGetValues: function(values) {
105 let me = this;
106 let grid = me.up('pveGuestImportWindow');
107
108 let config = Ext.apply(grid.vmConfig, values);
109
110 if (config.scsi0) {
111 config.scsi0 = config.scsi0.replace('local:0,', 'local:0,format=qcow2,');
112 }
113
114 grid.lookup('diskGrid').getStore().each((rec) => {
115 if (!rec.data.enable) {
116 return;
117 }
118 let id = rec.data.id;
119 delete rec.data.enable;
120 delete rec.data.id;
121 rec.data.file += ':0'; // for our special api format
122 if (id === 'efidisk0') {
123 delete rec.data['import-from'];
124 }
125 config[id] = PVE.Parser.printQemuDrive(rec.data);
126 });
127
128 grid.lookup('netGrid').getStore().each((rec) => {
129 if (!rec.data.enable) {
130 return;
131 }
132 let id = rec.data.id;
133 delete rec.data.enable;
134 delete rec.data.id;
135 config[id] = PVE.Parser.printQemuNetwork(rec.data);
136 });
137
138 if (grid.lookup('liveimport').getValue()) {
139 config['live-restore'] = 1;
140 }
141
142 return config;
143 },
144
145 column1: [
146 {
147 xtype: 'pveGuestIDSelector',
148 name: 'vmid',
149 fieldLabel: 'VM',
150 guestType: 'qemu',
151 loadNextFreeID: true,
152 },
153 {
154 xtype: 'proxmoxintegerfield',
155 fieldLabel: gettext('Sockets'),
156 name: 'sockets',
157 reference: 'socketsField',
158 value: 1,
159 minValue: 1,
160 maxValue: 4,
161 allowBlank: true,
162 bind: {
163 value: '{socketCount}',
164 },
165 },
166 {
167 xtype: 'proxmoxintegerfield',
168 fieldLabel: gettext('Cores'),
169 name: 'cores',
170 reference: 'coresField',
171 value: 1,
172 minValue: 1,
173 maxValue: 128,
174 allowBlank: true,
175 bind: {
176 value: '{coreCount}',
177 },
178 },
179 {
180 xtype: 'pveMemoryField',
181 fieldLabel: gettext('Memory'),
182 name: 'memory',
183 reference: 'memoryField',
184 value: 512,
185 allowBlank: true,
186 },
187 ],
188
189 column2: [
190 {
191 xtype: 'textfield',
192 fieldLabel: gettext('Name'),
193 name: 'name',
194 vtype: 'DnsName',
195 reference: 'nameField',
196 allowBlank: true,
197 },
198 {
199 xtype: 'CPUModelSelector',
200 name: 'cpu',
201 reference: 'cputype',
202 value: 'x86-64-v2-AES',
203 fieldLabel: gettext('Type'),
204 },
205 {
206 xtype: 'displayfield',
207 fieldLabel: gettext('Total cores'),
208 name: 'totalcores',
209 isFormField: false,
210 bind: {
211 value: '{totalCoreCount}',
212 },
213 },
214 {
215 xtype: 'combobox',
216 submitValue: false,
217 name: 'osbase',
218 fieldLabel: gettext('OS Type'),
219 editable: false,
220 queryMode: 'local',
221 value: 'Linux',
222 store: Object.keys(PVE.Utils.kvm_ostypes),
223 },
224 {
225 xtype: 'combobox',
226 name: 'ostype',
227 reference: 'ostype',
228 fieldLabel: gettext('Version'),
229 value: 'l26',
230 allowBlank: false,
231 editable: false,
232 queryMode: 'local',
233 valueField: 'val',
234 displayField: 'desc',
235 store: {
236 fields: ['desc', 'val'],
237 data: PVE.Utils.kvm_ostypes.Linux,
238 },
239 },
240 ],
241 columnB: [
242 {
243 xtype: 'displayfield',
244 fieldLabel: gettext('Disks'),
245 labelWidth: 200,
246 },
247 {
248 xtype: 'grid',
249 reference: 'diskGrid',
250 maxHeight: 150,
251 store: { data: [] },
252 columns: [
253 {
254 xtype: 'checkcolumn',
255 header: gettext('Use'),
256 width: 50,
257 dataIndex: 'enable',
258 listeners: {
259 checkchange: function(_column, _rowIndex, _checked, record) {
260 record.commit();
261 },
262 },
263 },
264 {
265 text: gettext('Disk'),
266 dataIndex: 'id',
267 },
268 {
269 text: gettext('Source'),
270 dataIndex: 'import-from',
271 flex: 1,
272 renderer: function(value) {
273 return value.replace(/^.*\//, '');
274 },
275 },
276 {
277 text: gettext('Storage'),
278 dataIndex: 'file',
279 xtype: 'widgetcolumn',
280 width: 150,
281 widget: {
282 xtype: 'pveStorageSelector',
283 isFormField: false,
284 name: 'file',
285 storageContent: 'images',
286 },
287 onWidgetAttach: 'setNodename',
288 },
289 {
290 text: gettext('Format'),
291 dataIndex: 'format',
292 xtype: 'widgetcolumn',
293 width: 150,
294 widget: {
295 xtype: 'pveDiskFormatSelector',
296 name: 'format',
297 isFormField: false,
298 matchFieldWidth: false,
299 },
300 },
301 ],
302 },
303 {
304 xtype: 'displayfield',
305 fieldLabel: gettext('Network Interfaces'),
306 labelWidth: 200,
307 },
308 {
309 xtype: 'grid',
310 maxHeight: 150,
311 reference: 'netGrid',
312 store: { data: [] },
313 columns: [
314 {
315 xtype: 'checkcolumn',
316 header: gettext('Use'),
317 width: 50,
318 dataIndex: 'enable',
319 listeners: {
320 checkchange: function(_column, _rowIndex, _checked, record) {
321 record.commit();
322 },
323 },
324 },
325 {
326 text: gettext('ID'),
327 dataIndex: 'id',
328 },
329 {
330 text: gettext('MAC address'),
331 flex: 1,
332 dataIndex: 'macaddr',
333 },
334 {
335 text: gettext('Model'),
336 flex: 1,
337 dataIndex: 'model',
338 },
339 {
340 text: gettext('Bridge'),
341 dataIndex: 'bridge',
342 xtype: 'widgetcolumn',
343 widget: {
344 xtype: 'PVE.form.BridgeSelector',
345 name: 'bridge',
346 isFormField: false,
347 allowBlank: false,
348 },
349 onWidgetAttach: 'setNodename',
350 },
351 ],
352 },
353 {
354 xtype: 'displayfield',
355 fieldLabel: gettext('Warnings'),
356 labelWidth: 200,
357 hidden: true,
358 bind: {
359 hidden: '{hideWarnings}',
360 },
361 },
362 {
363 xtype: 'displayfield',
364 reference: 'warningText',
365 userCls: 'pmx-hint',
366 hidden: true,
367 bind: {
368 hidden: '{hideWarnings}',
369 value: '{warningsText}',
370 },
371 },
372 ],
373 },
374 ],
375
376 initComponent: function() {
377 let me = this;
378
379 if (!me.volumeName) {
380 throw "no volumeName given";
381 }
382
383 if (!me.storage) {
384 throw "no storage given";
385 }
386
387 if (!me.nodename) {
388 throw "no nodename given";
389 }
390
391 me.callParent();
392
393 me.query('toolbar')?.[0]?.insert(0, {
394 xtype: 'proxmoxcheckbox',
395 reference: 'liveimport',
396 boxLabelAlign: 'before',
397 boxLabel: gettext('Live Import'),
398 });
399
400 me.setTitle(Ext.String.format(gettext('Import Guest - {0}'), `${me.storage}:${me.volumeName}`));
401
402 let renderWarning = w => {
403 const warningsCatalogue = {
404 'cdrom-image-ignored': gettext("CD-ROM images cannot get imported, please reconfigure the '{0}' drive after the import"),
405 'nvme-unsupported': gettext("NVMe disks are currently not supported, '{0}' will get attaced as SCSI"),
406 'ovmf-with-lsi-unsupported': gettext("OVMF is built without LSI drivers, scsi hardware was set to '{1}'"),
407 'serial-port-socket-only': gettext("Serial socket '{0}' will be mapped to a socket"),
408 };
409 let message = warningsCatalogue[w.type];
410 if (!w.type || !message) {
411 return w.message ?? w.type ?? gettext('Unknown warning');
412 }
413 return Ext.String.format(message, w.key ?? 'unknown', w.value ?? 'unknown');
414 };
415
416 me.load({
417 success: function(response) {
418 let data = response.result.data;
419 me.vmConfig = data['create-args'];
420
421 let disks = [];
422 for (const [id, value] of Object.entries(data.disks ?? {})) {
423 disks.push({
424 id,
425 enable: true,
426 'import-from': id === 'efidisk0' ? Ext.htmlEncode('<none>') : value,
427 format: 'raw',
428 });
429 }
430
431 let nets = [];
432 for (const [id, parsed] of Object.entries(data.net ?? {})) {
433 parsed.id = id;
434 parsed.enable = true;
435 nets.push(parsed);
436 }
437 me.lookup('diskGrid').getStore().setData(disks);
438 me.lookup('netGrid').getStore().setData(nets);
439
440 me.getViewModel().set('warnings', data.warnings.map(w => renderWarning(w)));
441
442 let osinfo = PVE.Utils.get_kvm_osinfo(me.vmConfig.ostype ?? '');
443
444 me.setValues({
445 osbase: osinfo.base,
446 ...me.vmConfig,
447 });
448 },
449 });
450 },
451 });