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