]> git.proxmox.com Git - pve-manager.git/blobdiff - www/manager6/qemu/BootOrderEdit.js
update shipped appliance info index
[pve-manager.git] / www / manager6 / qemu / BootOrderEdit.js
index 783a3aa8910ccfcd33394a0aab1f28b3a72b5599..c1f24ff4c9494d359d7c11478a1109dcfe37e245 100644 (file)
+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: 'PVE.panel.InputPanel',
+    extend: 'Proxmox.panel.InputPanel',
     alias: 'widget.pveQemuBootOrderPanel',
-    vmconfig: {}, // store loaded vm config
 
-    bootdisk: undefined,
-    selection: [],
-    list: [],
-    comboboxes: [],
+    onlineHelp: 'qm_bootorder',
 
-    setVMConfig: function(vmconfig) {
-       var me = this;
-       me.vmconfig = vmconfig;
-       var order = me.vmconfig.boot || 'cdn';
-       me.bootdisk = me.vmconfig.bootdisk || undefined;
+    vmconfig: {}, // store loaded vm config
+    store: undefined,
 
-       // get the first 3 characters
-       // ignore the rest (there should never be more than 3)
-       me.selection = order.split('').slice(0,3);
+    inUpdate: false,
+    controller: {
+       xclass: 'Ext.app.ViewController',
 
-       // build bootdev list
-       me.list = [];
-       Ext.Object.each(me.vmconfig, function(key, value) {
-           if ((/^(ide|sata|scsi|virtio)\d+$/).test(key) &&
-               !(/media=cdrom/).test(value)) {
-               me.list.push([key, "Disk '" + key + "'"]);
-           }
-       });
+       init: function(view) {
+           let me = this;
 
-       me.list.push(['d', 'CD-ROM']);
-       me.list.push(['n', gettext('Network')]);
-       me.list.push(['__none__', PVE.Utils.noneText]);
+           let grid = me.lookup('grid');
+           let marker = me.lookup('marker');
+           let emptyWarning = me.lookup('emptyWarning');
 
-       me.recomputeList();
+           marker.originalValue = undefined;
 
-       me.comboboxes.forEach(function(box) {
-           box.resetOriginalValue();
-       });
+           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);
+       },
     },
 
-    onGetValues: function(values) {
-       var me = this;
-       var order = me.selection.join('');
-       var res = { boot: order };
+    isCloudinit: (v) => v.match(/media=cdrom/) && v.match(/[:/]vm-\d+-cloudinit/),
 
-       if  (me.bootdisk && order.indexOf('c') !== -1) {
-           res['bootdisk'] = me.bootdisk;
-       } else {
-           res['delete'] = 'bootdisk';
-       }
+    isDisk: function(value) {
+       return PVE.Utils.bus_match.test(value);
+    },
 
-       return res;
+    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));
     },
 
-    recomputeSelection: function(combobox, newVal, oldVal) {
-       var me = this.up('#inputpanel');
-       me.selection = [];
-       me.comboboxes.forEach(function(item) {
-           var val = item.getValue();
-
-           // when selecting an already selected item,
-           // switch it around
-           if (val === newVal &&
-               item.name !== combobox.name &&
-               newVal !== '__none__') {
-               // swap items
-               val = oldVal;
+    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 }));
            }
+       }
 
-           // push 'c','d' or 'n' in the array
-           if ((/^(ide|sata|scsi|virtio)\d+$/).test(val)) {
-               me.selection.push('c');
-               me.bootdisk = val;
-           } else if (val === 'd' ||
-                      val === 'n') {
-               me.selection.push(val);
+       // 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.recomputeList();
+       me.store.insert(0, bootorder);
+       me.store.fireEvent("update");
     },
 
-    recomputeList: function(){
-       var me = this;
-       // set the correct values in the kvcomboboxes
-       var cnt = 0;
-       me.comboboxes.forEach(function(item) {
-           if (cnt === 0) {
-               // never show 'none' on first combobox
-               item.store.loadData(me.list.slice(0, me.list.length-1));
-           } else {
-               item.store.loadData(me.list);
-           }
-           item.suspendEvent('change');
-           if (cnt < me.selection.length) {
-               item.setValue((me.selection[cnt] !== 'c')?me.selection[cnt]:me.bootdisk);
-           } else if (cnt === 0){
-               item.setValue('');
-           } else {
-               item.setValue('__none__');
-           }
-           cnt++;
-           item.resumeEvent('change');
-           item.validate();
-       });
+    calculateValue: function() {
+       let me = this;
+       return me.store.getData().items
+           .filter(x => x.data.enabled)
+           .map(x => x.data.name)
+           .join(';');
     },
 
-    initComponent : function() {
-       var me = this;
-
-       // this has to be done here, because of
-       // the way our inputPanel class handles items
-       me.comboboxes = [
-               Ext.createWidget('pveKVComboBox', {
-               fieldLabel: gettext('Boot device') + " 1",
-               labelWidth: 120,
-               name: 'bd1',
-               allowBlank: false,
-               listeners: {
-                   change: me.recomputeSelection,
-               }
-           }),
-               Ext.createWidget('pveKVComboBox', {
-               fieldLabel: gettext('Boot device') + " 2",
-               labelWidth: 120,
-               name: 'bd2',
-               allowBlank: false,
-               listeners: {
-                   change: me.recomputeSelection,
-               }
-           }),
-               Ext.createWidget('pveKVComboBox', {
-               fieldLabel: gettext('Boot device') + " 3",
-               labelWidth: 120,
-               name: 'bd3',
-               allowBlank: false,
-               listeners: {
-                   change: me.recomputeSelection,
+    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);
                }
-           }),
-       ];
-       Ext.apply(me, { items: me.comboboxes });
-       me.callParent();
-    }
+
+               // 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: 'PVE.window.Edit',
+    extend: 'Proxmox.window.Edit',
 
-    items: {
+    items: [{
        xtype: 'pveQemuBootOrderPanel',
        itemId: 'inputpanel',
-    },
+    }],
 
     subject: gettext('Boot Order'),
+    width: 640,
 
-    initComponent : function() {
-       var me = this;
+    initComponent: function() {
+       let me = this;
        me.callParent();
        me.load({
-           success: function(response, options) {
-               me.down('#inputpanel').setVMConfig(response.result.data);
-           }
+           success: ({ result }) => me.down('#inputpanel').setVMConfig(result.data),
        });
-    }
+    },
 });