36e97aa41d
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
274 lines
6.5 KiB
JavaScript
274 lines
6.5 KiB
JavaScript
Ext.define('pve-boot-order-entry', {
|
|
extend: 'Ext.data.Model',
|
|
fields: [
|
|
{ name: 'name', type: 'string' },
|
|
{ name: 'enabled', type: 'bool' },
|
|
{ name: 'desc', type: 'string' },
|
|
],
|
|
});
|
|
|
|
Ext.define('PVE.qemu.BootOrderPanel', {
|
|
extend: 'Proxmox.panel.InputPanel',
|
|
alias: 'widget.pveQemuBootOrderPanel',
|
|
|
|
onlineHelp: 'qm_bootorder',
|
|
|
|
vmconfig: {}, // store loaded vm config
|
|
store: undefined,
|
|
|
|
inUpdate: false,
|
|
controller: {
|
|
xclass: 'Ext.app.ViewController',
|
|
|
|
init: function(view) {
|
|
let me = this;
|
|
|
|
let grid = me.lookup('grid');
|
|
let marker = me.lookup('marker');
|
|
let emptyWarning = me.lookup('emptyWarning');
|
|
|
|
marker.originalValue = undefined;
|
|
|
|
view.store = Ext.create('Ext.data.Store', {
|
|
model: 'pve-boot-order-entry',
|
|
listeners: {
|
|
update: function() {
|
|
this.commitChanges();
|
|
let val = view.calculateValue();
|
|
if (marker.originalValue === undefined) {
|
|
marker.originalValue = val;
|
|
}
|
|
view.inUpdate = true;
|
|
marker.setValue(val);
|
|
view.inUpdate = false;
|
|
marker.checkDirty();
|
|
emptyWarning.setHidden(val !== '');
|
|
grid.getView().refresh();
|
|
},
|
|
},
|
|
});
|
|
grid.setStore(view.store);
|
|
},
|
|
},
|
|
|
|
isCloudinit: (v) => v.match(/media=cdrom/) && v.match(/[:/]vm-\d+-cloudinit/),
|
|
|
|
isDisk: function(value) {
|
|
return PVE.Utils.bus_match.test(value);
|
|
},
|
|
|
|
isBootdev: function(dev, value) {
|
|
return (this.isDisk(dev) && !this.isCloudinit(value)) ||
|
|
(/^net\d+/).test(dev) ||
|
|
(/^hostpci\d+/).test(dev) ||
|
|
((/^usb\d+/).test(dev) && !(/spice/).test(value));
|
|
},
|
|
|
|
setVMConfig: function(vmconfig) {
|
|
let me = this;
|
|
me.vmconfig = vmconfig;
|
|
|
|
me.store.removeAll();
|
|
|
|
let boot = PVE.Parser.parsePropertyString(me.vmconfig.boot, "legacy");
|
|
|
|
let bootorder = [];
|
|
if (boot.order) {
|
|
bootorder = boot.order.split(';').map(dev => ({ name: dev, enabled: true }));
|
|
} else if (!(/^\s*$/).test(me.vmconfig.boot)) {
|
|
// legacy style, transform to new bootorder
|
|
let order = boot.legacy || 'cdn';
|
|
let bootdisk = me.vmconfig.bootdisk || undefined;
|
|
|
|
// get the first 4 characters (acdn)
|
|
// ignore the rest (there should never be more than 4)
|
|
let orderList = order.split('').slice(0, 4);
|
|
|
|
// build bootdev list
|
|
for (let i = 0; i < orderList.length; i++) {
|
|
let list = [];
|
|
if (orderList[i] === 'c') {
|
|
if (bootdisk !== undefined && me.vmconfig[bootdisk]) {
|
|
list.push(bootdisk);
|
|
}
|
|
} else if (orderList[i] === 'd') {
|
|
Ext.Object.each(me.vmconfig, function(key, value) {
|
|
if (me.isDisk(key) && value.match(/media=cdrom/) && !me.isCloudinit(value)) {
|
|
list.push(key);
|
|
}
|
|
});
|
|
} else if (orderList[i] === 'n') {
|
|
Ext.Object.each(me.vmconfig, function(key, value) {
|
|
if ((/^net\d+/).test(key)) {
|
|
list.push(key);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Object.each iterates in random order, sort alphabetically
|
|
list.sort();
|
|
list.forEach(dev => bootorder.push({ name: dev, enabled: true }));
|
|
}
|
|
}
|
|
|
|
// add disabled devices as well
|
|
let disabled = [];
|
|
Ext.Object.each(me.vmconfig, function(key, value) {
|
|
if (me.isBootdev(key, value) &&
|
|
!Ext.Array.some(bootorder, x => x.name === key)) {
|
|
disabled.push(key);
|
|
}
|
|
});
|
|
disabled.sort();
|
|
disabled.forEach(dev => bootorder.push({ name: dev, enabled: false }));
|
|
|
|
// add descriptions
|
|
bootorder.forEach(entry => {
|
|
entry.desc = me.vmconfig[entry.name];
|
|
});
|
|
|
|
me.store.insert(0, bootorder);
|
|
me.store.fireEvent("update");
|
|
},
|
|
|
|
calculateValue: function() {
|
|
let me = this;
|
|
return me.store.getData().items
|
|
.filter(x => x.data.enabled)
|
|
.map(x => x.data.name)
|
|
.join(';');
|
|
},
|
|
|
|
onGetValues: function() {
|
|
let me = this;
|
|
// Note: we allow an empty value, so no 'delete' option
|
|
let val = { order: me.calculateValue() };
|
|
let res = { boot: PVE.Parser.printPropertyString(val) };
|
|
return res;
|
|
},
|
|
|
|
items: [
|
|
{
|
|
xtype: 'grid',
|
|
reference: 'grid',
|
|
margin: '0 0 5 0',
|
|
minHeight: 150,
|
|
defaults: {
|
|
sortable: false,
|
|
hideable: false,
|
|
draggable: false,
|
|
},
|
|
columns: [
|
|
{
|
|
header: '#',
|
|
flex: 4,
|
|
renderer: (value, metaData, record, rowIndex) => {
|
|
let dragHandle = "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
|
|
let idx = (rowIndex + 1).toString();
|
|
if (record.get('enabled')) {
|
|
return dragHandle + idx;
|
|
} else {
|
|
return dragHandle + "<span class='faded'>" + idx + "</span>";
|
|
}
|
|
},
|
|
},
|
|
{
|
|
xtype: 'checkcolumn',
|
|
header: gettext('Enabled'),
|
|
dataIndex: 'enabled',
|
|
flex: 4,
|
|
},
|
|
{
|
|
header: gettext('Device'),
|
|
dataIndex: 'name',
|
|
flex: 6,
|
|
renderer: (value, metaData, record, rowIndex) => {
|
|
let desc = record.get('desc');
|
|
|
|
let icon = '', iconCls;
|
|
if (value.match(/^net\d+$/)) {
|
|
iconCls = 'exchange';
|
|
} else if (desc.match(/media=cdrom/)) {
|
|
metaData.tdCls = 'pve-itype-icon-cdrom';
|
|
} else {
|
|
iconCls = 'hdd-o';
|
|
}
|
|
if (iconCls !== undefined) {
|
|
metaData.tdCls += 'pve-itype-fa';
|
|
icon = `<i class="pve-grid-fa fa fa-fw fa-${iconCls}"></i>`;
|
|
}
|
|
|
|
return icon + value;
|
|
},
|
|
},
|
|
{
|
|
header: gettext('Description'),
|
|
dataIndex: 'desc',
|
|
flex: 20,
|
|
},
|
|
],
|
|
viewConfig: {
|
|
plugins: {
|
|
ptype: 'gridviewdragdrop',
|
|
dragText: gettext('Drag and drop to reorder'),
|
|
},
|
|
},
|
|
listeners: {
|
|
drop: function() {
|
|
// doesn't fire automatically on reorder
|
|
this.getStore().fireEvent("update");
|
|
},
|
|
},
|
|
},
|
|
{
|
|
xtype: 'component',
|
|
html: gettext('Drag and drop to reorder'),
|
|
},
|
|
{
|
|
xtype: 'displayfield',
|
|
reference: 'emptyWarning',
|
|
userCls: 'pmx-hint',
|
|
value: gettext('Warning: No devices selected, the VM will probably not boot!'),
|
|
},
|
|
{
|
|
// for dirty marking and 'reset' function
|
|
xtype: 'field',
|
|
reference: 'marker',
|
|
hidden: true,
|
|
setValue: function(val) {
|
|
let me = this;
|
|
let panel = me.up('pveQemuBootOrderPanel');
|
|
|
|
// on form reset, go back to original state
|
|
if (!panel.inUpdate) {
|
|
panel.setVMConfig(panel.vmconfig);
|
|
}
|
|
|
|
// not a subclass, so no callParent; just do it manually
|
|
me.setRawValue(me.valueToRaw(val));
|
|
return me.mixins.field.setValue.call(me, val);
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
Ext.define('PVE.qemu.BootOrderEdit', {
|
|
extend: 'Proxmox.window.Edit',
|
|
|
|
items: [{
|
|
xtype: 'pveQemuBootOrderPanel',
|
|
itemId: 'inputpanel',
|
|
}],
|
|
|
|
subject: gettext('Boot Order'),
|
|
width: 640,
|
|
|
|
initComponent: function() {
|
|
let me = this;
|
|
me.callParent();
|
|
me.load({
|
|
success: ({ result }) => me.down('#inputpanel').setVMConfig(result.data),
|
|
});
|
|
},
|
|
});
|