]>
Commit | Line | Data |
---|---|---|
1cc7c672 SR |
1 | Ext.define('pve-boot-order-entry', { |
2 | extend: 'Ext.data.Model', | |
3 | fields: [ | |
eb4b9246 TL |
4 | { name: 'name', type: 'string' }, |
5 | { name: 'enabled', type: 'bool' }, | |
6 | { name: 'desc', type: 'string' }, | |
7 | ], | |
1cc7c672 SR |
8 | }); |
9 | ||
638a57e7 | 10 | Ext.define('PVE.qemu.BootOrderPanel', { |
ef4ef788 | 11 | extend: 'Proxmox.panel.InputPanel', |
adaac36f | 12 | alias: 'widget.pveQemuBootOrderPanel', |
1cc7c672 | 13 | |
638a57e7 | 14 | vmconfig: {}, // store loaded vm config |
1cc7c672 | 15 | store: undefined, |
638a57e7 | 16 | |
1cc7c672 SR |
17 | inUpdate: false, |
18 | controller: { | |
19 | xclass: 'Ext.app.ViewController', | |
20 | }, | |
638a57e7 | 21 | |
1cc7c672 | 22 | isDisk: function(value) { |
98a01af2 | 23 | return PVE.Utils.bus_match.test(value); |
4edfc518 DC |
24 | }, |
25 | ||
1cc7c672 SR |
26 | isBootdev: function(dev, value) { |
27 | return this.isDisk(dev) || | |
28 | (/^net\d+/).test(dev) || | |
29 | (/^hostpci\d+/).test(dev) || | |
30 | ((/^usb\d+/).test(dev) && !(/spice/).test(value)); | |
31 | }, | |
32 | ||
638a57e7 | 33 | setVMConfig: function(vmconfig) { |
1cc7c672 | 34 | let me = this; |
638a57e7 | 35 | me.vmconfig = vmconfig; |
adaac36f | 36 | |
1cc7c672 | 37 | me.store.removeAll(); |
638a57e7 | 38 | |
1cc7c672 | 39 | let boot = PVE.Parser.parsePropertyString(me.vmconfig.boot, "legacy"); |
638a57e7 | 40 | |
1cc7c672 SR |
41 | let bootorder = []; |
42 | if (boot.order) { | |
eb4b9246 | 43 | bootorder = boot.order.split(';').map(dev => ({ name: dev, enabled: true })); |
1cc7c672 SR |
44 | } else if (!(/^\s*$/).test(me.vmconfig.boot)) { |
45 | // legacy style, transform to new bootorder | |
46 | let order = boot.legacy || 'cdn'; | |
47 | let bootdisk = me.vmconfig.bootdisk || undefined; | |
9e7b4d8d | 48 | |
1cc7c672 SR |
49 | // get the first 4 characters (acdn) |
50 | // ignore the rest (there should never be more than 4) | |
eb4b9246 | 51 | let orderList = order.split('').slice(0, 4); |
638a57e7 | 52 | |
1cc7c672 SR |
53 | // build bootdev list |
54 | for (let i = 0; i < orderList.length; i++) { | |
55 | let list = []; | |
56 | if (orderList[i] === 'c') { | |
57 | if (bootdisk !== undefined && me.vmconfig[bootdisk]) { | |
58 | list.push(bootdisk); | |
59 | } | |
60 | } else if (orderList[i] === 'd') { | |
61 | Ext.Object.each(me.vmconfig, function(key, value) { | |
62 | if (me.isDisk(key) && (/media=cdrom/).test(value)) { | |
63 | list.push(key); | |
64 | } | |
65 | }); | |
66 | } else if (orderList[i] === 'n') { | |
67 | Ext.Object.each(me.vmconfig, function(key, value) { | |
68 | if ((/^net\d+/).test(key)) { | |
69 | list.push(key); | |
70 | } | |
71 | }); | |
72 | } | |
638a57e7 | 73 | |
1cc7c672 SR |
74 | // Object.each iterates in random order, sort alphabetically |
75 | list.sort(); | |
eb4b9246 | 76 | list.forEach(dev => bootorder.push({ name: dev, enabled: true })); |
1cc7c672 | 77 | } |
638a57e7 DM |
78 | } |
79 | ||
1cc7c672 SR |
80 | // add disabled devices as well |
81 | let disabled = []; | |
82 | Ext.Object.each(me.vmconfig, function(key, value) { | |
83 | if (me.isBootdev(key, value) && | |
eb4b9246 | 84 | !Ext.Array.some(bootorder, x => x.name === key)) { |
1cc7c672 | 85 | disabled.push(key); |
adaac36f | 86 | } |
1cc7c672 SR |
87 | }); |
88 | disabled.sort(); | |
eb4b9246 | 89 | disabled.forEach(dev => bootorder.push({ name: dev, enabled: false })); |
638a57e7 | 90 | |
1cc7c672 SR |
91 | // add descriptions |
92 | bootorder.forEach(entry => { | |
93 | entry.desc = me.vmconfig[entry.name]; | |
adaac36f | 94 | }); |
638a57e7 | 95 | |
1cc7c672 SR |
96 | me.store.insert(0, bootorder); |
97 | me.store.fireEvent("update"); | |
638a57e7 DM |
98 | }, |
99 | ||
1cc7c672 SR |
100 | calculateValue: function() { |
101 | let me = this; | |
102 | return me.store.getData().items | |
103 | .filter(x => x.data.enabled) | |
104 | .map(x => x.data.name) | |
105 | .join(';'); | |
adaac36f | 106 | }, |
638a57e7 | 107 | |
1cc7c672 SR |
108 | onGetValues: function() { |
109 | let me = this; | |
110 | // Note: we allow an empty value, so no 'delete' option | |
111 | let val = { order: me.calculateValue() }; | |
112 | let res = { boot: PVE.Parser.printPropertyString(val) }; | |
113 | return res; | |
114 | }, | |
115 | ||
116 | items: [ | |
117 | { | |
118 | xtype: 'grid', | |
119 | reference: 'grid', | |
120 | margin: '0 0 5 0', | |
eacd918e | 121 | minHeight: 150, |
93775406 TL |
122 | defaults: { |
123 | sortable: false, | |
124 | hideable: false, | |
125 | draggable: false, | |
126 | }, | |
1cc7c672 | 127 | columns: [ |
1cc7c672 SR |
128 | { |
129 | header: '#', | |
595c30aa | 130 | flex: 4, |
1cc7c672 | 131 | renderer: (value, metaData, record, rowIndex) => { |
595c30aa | 132 | let dragHandle = "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>"; |
1cc7c672 SR |
133 | let idx = (rowIndex + 1).toString(); |
134 | if (record.get('enabled')) { | |
595c30aa | 135 | return dragHandle + idx; |
1cc7c672 | 136 | } else { |
595c30aa | 137 | return dragHandle + "<span class='faded'>" + idx + "</span>"; |
1cc7c672 SR |
138 | } |
139 | }, | |
140 | }, | |
141 | { | |
142 | xtype: 'checkcolumn', | |
143 | header: gettext('Enabled'), | |
144 | dataIndex: 'enabled', | |
595c30aa | 145 | flex: 4, |
1cc7c672 SR |
146 | }, |
147 | { | |
148 | header: gettext('Device'), | |
149 | dataIndex: 'name', | |
595c30aa | 150 | flex: 6, |
dd27ec6d TL |
151 | renderer: (value, metaData, record, rowIndex) => { |
152 | let desc = record.get('desc'); | |
153 | ||
154 | let icon = '', iconCls; | |
155 | if (value.match(/^net\d+$/)) { | |
156 | iconCls = 'exchange'; | |
157 | } else if (desc.match(/media=cdrom/)) { | |
158 | metaData.tdCls = 'pve-itype-icon-cdrom'; | |
159 | } else { | |
160 | iconCls = 'hdd-o'; | |
161 | } | |
162 | if (iconCls !== undefined) { | |
163 | metaData.tdCls += 'pve-itype-fa'; | |
164 | icon = `<i class="pve-grid-fa fa fa-fw fa-${iconCls}"></i>`; | |
165 | } | |
166 | ||
167 | return icon + value; | |
168 | }, | |
1cc7c672 SR |
169 | }, |
170 | { | |
171 | header: gettext('Description'), | |
172 | dataIndex: 'desc', | |
595c30aa | 173 | flex: 20, |
1cc7c672 SR |
174 | }, |
175 | ], | |
176 | viewConfig: { | |
177 | plugins: { | |
178 | ptype: 'gridviewdragdrop', | |
179 | dragText: gettext('Drag and drop to reorder'), | |
eb4b9246 | 180 | }, |
1cc7c672 SR |
181 | }, |
182 | listeners: { | |
183 | drop: function() { | |
184 | // doesn't fire automatically on reorder | |
185 | this.getStore().fireEvent("update"); | |
eb4b9246 | 186 | }, |
1cc7c672 SR |
187 | }, |
188 | }, | |
189 | { | |
190 | xtype: 'component', | |
191 | html: gettext('Drag and drop to reorder'), | |
192 | }, | |
193 | { | |
194 | xtype: 'displayfield', | |
195 | reference: 'emptyWarning', | |
196 | userCls: 'pmx-hint', | |
197 | value: gettext('Warning: No devices selected, the VM will probably not boot!'), | |
198 | }, | |
199 | { | |
200 | // for dirty marking and 'reset' function | |
201 | xtype: 'field', | |
202 | reference: 'marker', | |
203 | hidden: true, | |
204 | setValue: function(val) { | |
205 | let me = this; | |
206 | let panel = me.up('pveQemuBootOrderPanel'); | |
207 | ||
208 | // on form reset, go back to original state | |
209 | if (!panel.inUpdate) { | |
210 | panel.setVMConfig(panel.vmconfig); | |
adaac36f | 211 | } |
1cc7c672 SR |
212 | |
213 | // not a subclass, so no callParent; just do it manually | |
214 | me.setRawValue(me.valueToRaw(val)); | |
215 | return me.mixins.field.setValue.call(me, val); | |
eb4b9246 | 216 | }, |
1cc7c672 SR |
217 | }, |
218 | ], | |
219 | ||
220 | initComponent: function() { | |
221 | let me = this; | |
222 | ||
638a57e7 | 223 | me.callParent(); |
1cc7c672 SR |
224 | |
225 | let controller = me.getController(); | |
226 | ||
227 | let grid = controller.lookup('grid'); | |
228 | let marker = controller.lookup('marker'); | |
229 | let emptyWarning = controller.lookup('emptyWarning'); | |
230 | ||
231 | marker.originalValue = undefined; | |
232 | ||
233 | me.store = Ext.create('Ext.data.Store', { | |
234 | model: 'pve-boot-order-entry', | |
235 | listeners: { | |
236 | update: function() { | |
237 | this.commitChanges(); | |
238 | let val = me.calculateValue(); | |
239 | if (marker.originalValue === undefined) { | |
240 | marker.originalValue = val; | |
241 | } | |
242 | me.inUpdate = true; | |
243 | marker.setValue(val); | |
244 | me.inUpdate = false; | |
245 | marker.checkDirty(); | |
246 | emptyWarning.setHidden(val !== ''); | |
247 | grid.getView().refresh(); | |
eb4b9246 TL |
248 | }, |
249 | }, | |
1cc7c672 SR |
250 | }); |
251 | grid.setStore(me.store); | |
eb4b9246 | 252 | }, |
638a57e7 DM |
253 | }); |
254 | ||
255 | Ext.define('PVE.qemu.BootOrderEdit', { | |
9fccc702 | 256 | extend: 'Proxmox.window.Edit', |
638a57e7 | 257 | |
ec0bd652 | 258 | items: [{ |
adaac36f | 259 | xtype: 'pveQemuBootOrderPanel', |
eb4b9246 | 260 | itemId: 'inputpanel', |
ec0bd652 | 261 | }], |
638a57e7 | 262 | |
adaac36f | 263 | subject: gettext('Boot Order'), |
595c30aa | 264 | width: 640, |
638a57e7 | 265 | |
eb4b9246 | 266 | initComponent: function() { |
1cc7c672 | 267 | let me = this; |
638a57e7 | 268 | me.callParent(); |
638a57e7 DM |
269 | me.load({ |
270 | success: function(response, options) { | |
adaac36f | 271 | me.down('#inputpanel').setVMConfig(response.result.data); |
eb4b9246 | 272 | }, |
638a57e7 | 273 | }); |
eb4b9246 | 274 | }, |
638a57e7 | 275 | }); |