]>
Commit | Line | Data |
---|---|---|
c59fb4ee DC |
1 | Ext.define('PVE.qemu.PCIInputPanel', { |
2 | extend: 'Proxmox.panel.InputPanel', | |
3 | ||
4f0d4294 | 4 | onlineHelp: 'qm_pci_passthrough_vm_config', |
c59fb4ee | 5 | |
b1d42186 DC |
6 | controller: { |
7 | xclass: 'Ext.app.ViewController', | |
8 | ||
9 | setVMConfig: function(vmconfig) { | |
10 | let me = this; | |
11 | let view = me.getView(); | |
12 | me.vmconfig = vmconfig; | |
c59fb4ee | 13 | |
b1d42186 | 14 | let hostpci = me.vmconfig[view.confid] || ''; |
c59fb4ee | 15 | |
b1d42186 DC |
16 | let values = PVE.Parser.parsePropertyString(hostpci, 'host'); |
17 | if (values.host) { | |
18 | if (!values.host.match(/^[0-9a-f]{4}:/i)) { // add optional domain | |
19 | values.host = "0000:" + values.host; | |
20 | } | |
21 | if (values.host.length < 11) { // 0000:00:00 format not 0000:00:00.0 | |
22 | values.host += ".0"; | |
23 | values.multifunction = true; | |
24 | } | |
25 | values.type = 'raw'; | |
26 | } else if (values.mapping) { | |
27 | values.type = 'mapped'; | |
5b23d683 | 28 | } |
b1d42186 DC |
29 | |
30 | values['x-vga'] = PVE.Parser.parseBoolean(values['x-vga'], 0); | |
31 | values.pcie = PVE.Parser.parseBoolean(values.pcie, 0); | |
32 | values.rombar = PVE.Parser.parseBoolean(values.rombar, 1); | |
33 | ||
34 | view.setValues(values); | |
35 | if (!me.vmconfig.machine || me.vmconfig.machine.indexOf('q35') === -1) { | |
36 | // machine is not set to some variant of q35, so we disable pcie | |
37 | let pcie = me.lookup('pcie'); | |
38 | pcie.setDisabled(true); | |
39 | pcie.setBoxLabel(gettext('Q35 only')); | |
5b23d683 | 40 | } |
5b23d683 | 41 | |
b1d42186 DC |
42 | if (values.romfile) { |
43 | me.lookup('romfile').setVisible(true); | |
44 | } | |
45 | }, | |
c59fb4ee | 46 | |
b1d42186 DC |
47 | selectorEnable: function(selector) { |
48 | let me = this; | |
49 | me.pciDevChange(selector, selector.getValue()); | |
50 | }, | |
c59fb4ee | 51 | |
b1d42186 DC |
52 | pciDevChange: function(pcisel, value) { |
53 | let me = this; | |
54 | let mdevfield = me.lookup('mdev'); | |
55 | if (!value) { | |
56 | if (!pcisel.isDisabled()) { | |
57 | mdevfield.setDisabled(true); | |
58 | } | |
59 | return; | |
60 | } | |
61 | let pciDev = pcisel.getStore().getById(value); | |
c59fb4ee | 62 | |
b1d42186 DC |
63 | mdevfield.setDisabled(!pciDev || !pciDev.data.mdev); |
64 | if (!pciDev) { | |
65 | return; | |
66 | } | |
67 | ||
68 | let path = value; | |
69 | if (pciDev.data.map) { | |
70 | // find local mapping | |
71 | for (const entry of pciDev.data.map) { | |
72 | let mapping = PVE.Parser.parsePropertyString(entry); | |
73 | if (mapping.node === pcisel.up('inputpanel').nodename) { | |
74 | path = mapping.path.split(';')[0]; | |
75 | break; | |
76 | } | |
77 | } | |
78 | if (path.indexOf('.') === -1) { | |
79 | path += '.0'; | |
c59fb4ee DC |
80 | } |
81 | } | |
a7ec3666 | 82 | |
b1d42186 DC |
83 | if (pciDev.data.mdev) { |
84 | mdevfield.setPciID(path); | |
85 | } | |
86 | if (pcisel.reference === 'selector') { | |
87 | let iommu = pciDev.data.iommugroup; | |
88 | if (iommu === -1) { | |
89 | return; | |
90 | } | |
91 | // try to find out if there are more devices in that iommu group | |
92 | let id = path.substring(0, 5); // 00:00 | |
93 | let count = 0; | |
94 | pcisel.getStore().each(({ data }) => { | |
95 | if (data.iommugroup === iommu && data.id.substring(0, 5) !== id) { | |
96 | count++; | |
97 | return false; | |
98 | } | |
99 | return true; | |
100 | }); | |
101 | me.lookup('group_warning').setVisible(count > 0); | |
102 | } | |
103 | }, | |
c59fb4ee | 104 | |
b1d42186 DC |
105 | onGetValues: function(values) { |
106 | let me = this; | |
107 | let view = me.getView(); | |
108 | if (!view.confid) { | |
109 | for (let i = 0; i < PVE.Utils.hardware_counts.hostpci; i++) { | |
110 | if (!me.vmconfig['hostpci' + i.toString()]) { | |
111 | view.confid = 'hostpci' + i.toString(); | |
112 | break; | |
113 | } | |
114 | } | |
115 | // FIXME: what if no confid was found?? | |
116 | } | |
c59fb4ee | 117 | |
b1d42186 | 118 | values.host?.replace(/^0000:/, ''); // remove optional '0000' domain |
2b25a905 | 119 | |
b1d42186 DC |
120 | if (values.multifunction && values.host) { |
121 | values.host = values.host.substring(0, values.host.indexOf('.')); // skip the '.X' | |
122 | delete values.multifunction; | |
123 | } | |
124 | ||
125 | if (values.rombar) { | |
126 | delete values.rombar; | |
127 | } else { | |
128 | values.rombar = 0; | |
129 | } | |
130 | ||
131 | if (!values.romfile) { | |
132 | delete values.romfile; | |
133 | } | |
134 | ||
135 | delete values.type; | |
136 | ||
137 | let ret = {}; | |
138 | ret[view.confid] = PVE.Parser.printPropertyString(values, 'host'); | |
139 | return ret; | |
140 | }, | |
141 | }, | |
142 | ||
143 | viewModel: { | |
144 | data: { | |
145 | isMapped: true, | |
146 | }, | |
147 | }, | |
148 | ||
149 | setVMConfig: function(vmconfig) { | |
150 | return this.getController().setVMConfig(vmconfig); | |
151 | }, | |
152 | ||
153 | onGetValues: function(values) { | |
154 | return this.getController().onGetValues(values); | |
c59fb4ee DC |
155 | }, |
156 | ||
157 | initComponent: function() { | |
a7ec3666 | 158 | let me = this; |
c59fb4ee DC |
159 | |
160 | me.nodename = me.pveSelNode.data.node; | |
161 | if (!me.nodename) { | |
162 | throw "no node name specified"; | |
163 | } | |
164 | ||
b1d42186 DC |
165 | me.columnT = [ |
166 | { | |
167 | xtype: 'displayfield', | |
168 | reference: 'iommu_warning', | |
169 | hidden: true, | |
170 | columnWidth: 1, | |
171 | padding: '0 0 10 0', | |
172 | value: 'No IOMMU detected, please activate it.' + | |
173 | 'See Documentation for further information.', | |
174 | userCls: 'pmx-hint', | |
175 | }, | |
176 | { | |
177 | xtype: 'displayfield', | |
178 | reference: 'group_warning', | |
179 | hidden: true, | |
180 | columnWidth: 1, | |
181 | padding: '0 0 10 0', | |
182 | itemId: 'iommuwarning', | |
183 | value: 'The selected Device is not in a seperate IOMMU group, make sure this is intended.', | |
184 | userCls: 'pmx-hint', | |
185 | }, | |
186 | ]; | |
187 | ||
c59fb4ee | 188 | me.column1 = [ |
b1d42186 DC |
189 | { |
190 | xtype: 'radiofield', | |
191 | name: 'type', | |
192 | inputValue: 'mapped', | |
193 | boxLabel: gettext('Mapped Device'), | |
194 | bind: { | |
195 | value: '{isMapped}', | |
196 | }, | |
197 | }, | |
198 | { | |
199 | xtype: 'pvePCIMapSelector', | |
200 | fieldLabel: gettext('Device'), | |
201 | reference: 'mapped_selector', | |
202 | name: 'mapping', | |
203 | labelAlign: 'right', | |
204 | nodename: me.nodename, | |
205 | allowBlank: false, | |
206 | bind: { | |
207 | disabled: '{!isMapped}', | |
208 | }, | |
209 | listeners: { | |
210 | change: 'pciDevChange', | |
211 | enable: 'selectorEnable', | |
212 | }, | |
213 | }, | |
214 | { | |
215 | xtype: 'radiofield', | |
216 | name: 'type', | |
217 | inputValue: 'raw', | |
218 | checked: true, | |
219 | boxLabel: gettext('Raw Device'), | |
220 | }, | |
c59fb4ee DC |
221 | { |
222 | xtype: 'pvePCISelector', | |
223 | fieldLabel: gettext('Device'), | |
224 | name: 'host', | |
b1d42186 | 225 | reference: 'selector', |
c59fb4ee | 226 | nodename: me.nodename, |
b1d42186 | 227 | labelAlign: 'right', |
c59fb4ee | 228 | allowBlank: false, |
b1d42186 DC |
229 | disabled: true, |
230 | bind: { | |
231 | disabled: '{isMapped}', | |
232 | }, | |
c59fb4ee DC |
233 | onLoadCallBack: function(store, records, success) { |
234 | if (!success || !records.length) { | |
235 | return; | |
236 | } | |
b1d42186 DC |
237 | me.lookup('iommu_warning').setVisible( |
238 | records.every((val) => val.data.iommugroup === -1), | |
239 | ); | |
c59fb4ee DC |
240 | }, |
241 | listeners: { | |
b1d42186 DC |
242 | change: 'pciDevChange', |
243 | enable: 'selectorEnable', | |
f6710aac | 244 | }, |
c59fb4ee DC |
245 | }, |
246 | { | |
247 | xtype: 'proxmoxcheckbox', | |
248 | fieldLabel: gettext('All Functions'), | |
b1d42186 DC |
249 | reference: 'all_functions', |
250 | disabled: true, | |
251 | labelAlign: 'right', | |
f6710aac | 252 | name: 'multifunction', |
b1d42186 DC |
253 | bind: { |
254 | disabled: '{isMapped}', | |
255 | }, | |
f6710aac | 256 | }, |
c59fb4ee DC |
257 | ]; |
258 | ||
259 | me.column2 = [ | |
260 | { | |
261 | xtype: 'pveMDevSelector', | |
262 | name: 'mdev', | |
b1d42186 | 263 | reference: 'mdev', |
c59fb4ee DC |
264 | disabled: true, |
265 | fieldLabel: gettext('MDev Type'), | |
266 | nodename: me.nodename, | |
267 | listeners: { | |
268 | change: function(field, value) { | |
a7ec3666 | 269 | let multiFunction = me.down('field[name=multifunction]'); |
d2021707 | 270 | if (value) { |
a7ec3666 | 271 | multiFunction.setValue(false); |
c59fb4ee | 272 | } |
a7ec3666 | 273 | multiFunction.setDisabled(!!value); |
f6710aac TL |
274 | }, |
275 | }, | |
c59fb4ee DC |
276 | }, |
277 | { | |
278 | xtype: 'proxmoxcheckbox', | |
279 | fieldLabel: gettext('Primary GPU'), | |
f6710aac TL |
280 | name: 'x-vga', |
281 | }, | |
c59fb4ee DC |
282 | ]; |
283 | ||
284 | me.advancedColumn1 = [ | |
285 | { | |
286 | xtype: 'proxmoxcheckbox', | |
287 | fieldLabel: 'ROM-Bar', | |
f6710aac | 288 | name: 'rombar', |
c59fb4ee DC |
289 | }, |
290 | { | |
291 | xtype: 'displayfield', | |
292 | submitValue: true, | |
293 | hidden: true, | |
294 | fieldLabel: 'ROM-File', | |
b1d42186 | 295 | reference: 'romfile', |
f6710aac TL |
296 | name: 'romfile', |
297 | }, | |
9a9be673 NS |
298 | { |
299 | xtype: 'textfield', | |
300 | name: 'vendor-id', | |
d20113ef TL |
301 | fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Vendor')), |
302 | emptyText: gettext('From Device'), | |
9a9be673 NS |
303 | vtype: 'PciId', |
304 | allowBlank: true, | |
9a9be673 NS |
305 | submitEmpty: false, |
306 | }, | |
307 | { | |
308 | xtype: 'textfield', | |
309 | name: 'device-id', | |
d20113ef TL |
310 | fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Device')), |
311 | emptyText: gettext('From Device'), | |
9a9be673 NS |
312 | vtype: 'PciId', |
313 | allowBlank: true, | |
9a9be673 NS |
314 | submitEmpty: false, |
315 | }, | |
c59fb4ee DC |
316 | ]; |
317 | ||
318 | me.advancedColumn2 = [ | |
319 | { | |
320 | xtype: 'proxmoxcheckbox', | |
321 | fieldLabel: 'PCI-Express', | |
b1d42186 | 322 | reference: 'pcie', |
f6710aac TL |
323 | name: 'pcie', |
324 | }, | |
9a9be673 NS |
325 | { |
326 | xtype: 'textfield', | |
327 | name: 'sub-vendor-id', | |
d20113ef TL |
328 | fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Sub-Vendor')), |
329 | emptyText: gettext('From Device'), | |
9a9be673 NS |
330 | vtype: 'PciId', |
331 | allowBlank: true, | |
9a9be673 NS |
332 | submitEmpty: false, |
333 | }, | |
334 | { | |
335 | xtype: 'textfield', | |
336 | name: 'sub-device-id', | |
d20113ef TL |
337 | fieldLabel: Ext.String.format(gettext('{0} ID'), gettext('Sub-Device')), |
338 | emptyText: gettext('From Device'), | |
9a9be673 NS |
339 | vtype: 'PciId', |
340 | allowBlank: true, | |
9a9be673 NS |
341 | submitEmpty: false, |
342 | }, | |
c59fb4ee DC |
343 | ]; |
344 | ||
345 | me.callParent(); | |
f6710aac | 346 | }, |
c59fb4ee DC |
347 | }); |
348 | ||
349 | Ext.define('PVE.qemu.PCIEdit', { | |
350 | extend: 'Proxmox.window.Edit', | |
351 | ||
c59fb4ee DC |
352 | subject: gettext('PCI Device'), |
353 | ||
a7ec3666 TL |
354 | vmconfig: undefined, |
355 | isAdd: true, | |
c59fb4ee | 356 | |
8058410f | 357 | initComponent: function() { |
a7ec3666 | 358 | let me = this; |
c59fb4ee DC |
359 | |
360 | me.isCreate = !me.confid; | |
361 | ||
a7ec3666 | 362 | let ipanel = Ext.create('PVE.qemu.PCIInputPanel', { |
c59fb4ee | 363 | confid: me.confid, |
f6710aac | 364 | pveSelNode: me.pveSelNode, |
c59fb4ee DC |
365 | }); |
366 | ||
367 | Ext.apply(me, { | |
8058410f | 368 | items: [ipanel], |
c59fb4ee DC |
369 | }); |
370 | ||
371 | me.callParent(); | |
372 | ||
373 | me.load({ | |
a7ec3666 | 374 | success: ({ result }) => ipanel.setVMConfig(result.data), |
c59fb4ee | 375 | }); |
f6710aac | 376 | }, |
c59fb4ee | 377 | }); |