]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/qemu/BootOrderEdit.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / qemu / BootOrderEdit.js
1 Ext.define('pve-boot-order-entry', {
2 extend: 'Ext.data.Model',
3 fields: [
4 { name: 'name', type: 'string' },
5 { name: 'enabled', type: 'bool' },
6 { name: 'desc', type: 'string' },
7 ],
8 });
9
10 Ext.define('PVE.qemu.BootOrderPanel', {
11 extend: 'Proxmox.panel.InputPanel',
12 alias: 'widget.pveQemuBootOrderPanel',
13
14 onlineHelp: 'qm_bootorder',
15
16 vmconfig: {}, // store loaded vm config
17 store: undefined,
18
19 inUpdate: false,
20 controller: {
21 xclass: 'Ext.app.ViewController',
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 },
52 },
53
54 isCloudinit: (v) => v.match(/media=cdrom/) && v.match(/[:/]vm-\d+-cloudinit/),
55
56 isDisk: function(value) {
57 return PVE.Utils.bus_match.test(value);
58 },
59
60 isBootdev: function(dev, value) {
61 return (this.isDisk(dev) && !this.isCloudinit(value)) ||
62 (/^net\d+/).test(dev) ||
63 (/^hostpci\d+/).test(dev) ||
64 ((/^usb\d+/).test(dev) && !(/spice/).test(value));
65 },
66
67 setVMConfig: function(vmconfig) {
68 let me = this;
69 me.vmconfig = vmconfig;
70
71 me.store.removeAll();
72
73 let boot = PVE.Parser.parsePropertyString(me.vmconfig.boot, "legacy");
74
75 let bootorder = [];
76 if (boot.order) {
77 bootorder = boot.order.split(';').map(dev => ({ name: dev, enabled: true }));
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;
82
83 // get the first 4 characters (acdn)
84 // ignore the rest (there should never be more than 4)
85 let orderList = order.split('').slice(0, 4);
86
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) {
96 if (me.isDisk(key) && value.match(/media=cdrom/) && !me.isCloudinit(value)) {
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 }
107
108 // Object.each iterates in random order, sort alphabetically
109 list.sort();
110 list.forEach(dev => bootorder.push({ name: dev, enabled: true }));
111 }
112 }
113
114 // add disabled devices as well
115 let disabled = [];
116 Ext.Object.each(me.vmconfig, function(key, value) {
117 if (me.isBootdev(key, value) &&
118 !Ext.Array.some(bootorder, x => x.name === key)) {
119 disabled.push(key);
120 }
121 });
122 disabled.sort();
123 disabled.forEach(dev => bootorder.push({ name: dev, enabled: false }));
124
125 // add descriptions
126 bootorder.forEach(entry => {
127 entry.desc = me.vmconfig[entry.name];
128 });
129
130 me.store.insert(0, bootorder);
131 me.store.fireEvent("update");
132 },
133
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(';');
140 },
141
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',
155 minHeight: 150,
156 defaults: {
157 sortable: false,
158 hideable: false,
159 draggable: false,
160 },
161 columns: [
162 {
163 header: '#',
164 flex: 4,
165 renderer: (value, metaData, record, rowIndex) => {
166 let dragHandle = "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
167 let idx = (rowIndex + 1).toString();
168 if (record.get('enabled')) {
169 return dragHandle + idx;
170 } else {
171 return dragHandle + "<span class='faded'>" + idx + "</span>";
172 }
173 },
174 },
175 {
176 xtype: 'checkcolumn',
177 header: gettext('Enabled'),
178 dataIndex: 'enabled',
179 flex: 4,
180 },
181 {
182 header: gettext('Device'),
183 dataIndex: 'name',
184 flex: 6,
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 },
203 },
204 {
205 header: gettext('Description'),
206 dataIndex: 'desc',
207 flex: 20,
208 },
209 ],
210 viewConfig: {
211 plugins: {
212 ptype: 'gridviewdragdrop',
213 dragText: gettext('Drag and drop to reorder'),
214 },
215 },
216 listeners: {
217 drop: function() {
218 // doesn't fire automatically on reorder
219 this.getStore().fireEvent("update");
220 },
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);
245 }
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);
250 },
251 },
252 ],
253 });
254
255 Ext.define('PVE.qemu.BootOrderEdit', {
256 extend: 'Proxmox.window.Edit',
257
258 items: [{
259 xtype: 'pveQemuBootOrderPanel',
260 itemId: 'inputpanel',
261 }],
262
263 subject: gettext('Boot Order'),
264 width: 640,
265
266 initComponent: function() {
267 let me = this;
268 me.callParent();
269 me.load({
270 success: ({ result }) => me.down('#inputpanel').setVMConfig(result.data),
271 });
272 },
273 });