From: Stefan Reiter <s.reiter@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [RFC manager 8/9] gui: add basic custom CPU model editor
Date: Thu, 28 Oct 2021 13:41:49 +0200 [thread overview]
Message-ID: <20211028114150.3245864-9-s.reiter@proxmox.com> (raw)
In-Reply-To: <20211028114150.3245864-1-s.reiter@proxmox.com>
Supports viewing, deleting, adding and editing existing custom CPU
models. All properties except CPU flags are supported in the editor.
A new control for selecting between different Phys-Bits (default, host,
custom number) is added.
This also seems to be the first use of a non-fa icon in the sidebar, so
add a new class that correctly aligns pve-itype-icon-* elements.
Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
www/css/ext6-pve.css | 4 +
www/manager6/Makefile | 3 +
www/manager6/dc/CPUTypeEdit.js | 81 ++++++++++++++
www/manager6/dc/CPUTypeView.js | 148 ++++++++++++++++++++++++++
www/manager6/dc/Config.js | 6 ++
www/manager6/form/PhysBitsSelector.js | 128 ++++++++++++++++++++++
6 files changed, 370 insertions(+)
create mode 100644 www/manager6/dc/CPUTypeEdit.js
create mode 100644 www/manager6/dc/CPUTypeView.js
create mode 100644 www/manager6/form/PhysBitsSelector.js
diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index e5d792f8..bb9bd8c0 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -447,6 +447,10 @@
padding: 0;
}
+.pve-icon-sidebar {
+ margin-top: 5px;
+}
+
/* displayfield minheight is wrong */
.x-form-display-field-default {
min-height: 20px;
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index e5e85aed..abca0a05 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -48,6 +48,7 @@ JSSRC= \
form/NodeSelector.js \
form/PCISelector.js \
form/PermPathSelector.js \
+ form/PhysBitsSelector.js \
form/PoolSelector.js \
form/PreallocationSelector.js \
form/PrivilegesSelector.js \
@@ -131,6 +132,8 @@ JSSRC= \
dc/ClusterEdit.js \
dc/Config.js \
dc/CorosyncLinkEdit.js \
+ dc/CPUTypeView.js \
+ dc/CPUTypeEdit.js \
dc/GroupEdit.js \
dc/GroupView.js \
dc/Guests.js \
diff --git a/www/manager6/dc/CPUTypeEdit.js b/www/manager6/dc/CPUTypeEdit.js
new file mode 100644
index 00000000..3ad527c4
--- /dev/null
+++ b/www/manager6/dc/CPUTypeEdit.js
@@ -0,0 +1,81 @@
+Ext.define('PVE.dc.CPUTypeEdit', {
+ extend: 'Proxmox.window.Edit',
+ alias: ['widget.pveCpuTypeEdit'],
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ subject: gettext('CPU Type'),
+
+ cbindData: {
+ cputype: '',
+ isCreate: (cfg) => !cfg.cputype,
+ },
+
+ cbind: {
+ autoLoad: get => !get('isCreate'),
+ url: get => `/api2/extjs/nodes/localhost/capabilities/qemu/cpu/model/${get('cputype')}`,
+ method: get => get('isCreate') ? 'POST' : 'PUT',
+ isCreate: get => get('isCreate'),
+ },
+
+ getValues: function() {
+ let me = this;
+ let values = me.callParent();
+
+ PVE.Utils.delete_if_default(values, 'reported-model', '', me.isCreate);
+ PVE.Utils.delete_if_default(values, 'hv-vendor-id', '', me.isCreate);
+ PVE.Utils.delete_if_default(values, 'phys-bits', '', me.isCreate);
+ PVE.Utils.delete_if_default(values, 'flags', '', me.isCreate);
+
+ if (me.isCreate && !values.cputype.match(/^custom-/)) {
+ values.cputype = 'custom-' + values.cputype;
+ }
+
+ return values;
+ },
+
+ items: [
+ {
+ xtype: 'inputpanel',
+ column1: [
+ {
+ xtype: 'pmxDisplayEditField',
+ fieldLabel: gettext('Name'),
+ cbind: {
+ editable: '{isCreate}',
+ value: '{cputype}',
+ },
+ name: 'cputype',
+ allowBlank: false,
+ },
+ {
+ xtype: 'CPUModelSelector',
+ fieldLabel: gettext('Reported Model'),
+ allowCustom: false,
+ name: 'reported-model',
+ },
+ {
+ xtype: 'textfield',
+ fieldLabel: gettext('Hyper-V Vendor'),
+ name: 'hv-vendor-id',
+ allowBlank: true,
+ emptyText: gettext('None'),
+ maxLength: 12,
+ },
+ ],
+ column2: [
+ {
+ xtype: 'checkbox',
+ fieldLabel: gettext('Hidden'),
+ name: 'hidden',
+ inputValue: 1,
+ uncheckedValue: 0,
+ },
+ {
+ xtype: 'PhysBitsSelector',
+ fieldLabel: gettext('Phys-Bits'),
+ name: 'phys-bits',
+ },
+ ],
+ },
+ ],
+});
diff --git a/www/manager6/dc/CPUTypeView.js b/www/manager6/dc/CPUTypeView.js
new file mode 100644
index 00000000..0d560369
--- /dev/null
+++ b/www/manager6/dc/CPUTypeView.js
@@ -0,0 +1,148 @@
+Ext.define('PVE.dc.CPUTypeView', {
+ extend: 'Ext.grid.GridPanel',
+ alias: ['widget.pveCPUTypeView'],
+
+ onlineHelp: 'qm_cpu',
+
+ store: {
+ model: 'pve-custom-cpu-type',
+ proxy: {
+ type: 'proxmox',
+ url: "/api2/json/nodes/localhost/capabilities/qemu/cpu/model",
+ root: 'data.ids',
+ },
+ autoLoad: true,
+ sorters: ['cputype'],
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ getSelection: function() {
+ let me = this;
+ let grid = me.getView();
+ let selection = grid.getSelection();
+ if (selection.length === 1) {
+ return selection[0].data;
+ }
+ return null;
+ },
+
+ showEditor: function(cputype) {
+ let me = this;
+ let param = {};
+ if (cputype) {
+ Ext.apply(param, { cputype: cputype });
+ }
+ let win = Ext.create('PVE.dc.CPUTypeEdit', param);
+ win.on('destroy', () => me.reload());
+ win.show();
+ },
+
+ onAdd: function() {
+ let me = this;
+ me.showEditor();
+ },
+
+ onEdit: function() {
+ let me = this;
+ let selection = me.getSelection();
+ me.showEditor(selection.cputype);
+ },
+
+ reload: function() {
+ let me = this;
+ let grid = me.getView();
+ let store = grid.store;
+ store.reload();
+ },
+ },
+
+ columns: [
+ {
+ header: 'Name',
+ flex: 1,
+ sortable: true,
+ dataIndex: 'cputype',
+ renderer: val => val.replace(/^custom-/, ''),
+ },
+ {
+ header: 'Reported Model',
+ width: '80px',
+ sortable: true,
+ dataIndex: 'reported-model',
+ },
+ {
+ header: 'Phys-Bits',
+ width: '40px',
+ sortable: true,
+ dataIndex: 'phys-bits',
+ },
+ {
+ header: 'Hidden',
+ width: '40px',
+ sortable: true,
+ dataIndex: 'hidden',
+ },
+ {
+ header: 'HyperV-Vendor',
+ width: '80px',
+ sortable: true,
+ dataIndex: 'hv-vendor-id',
+ },
+ {
+ header: 'Flags',
+ flex: 2,
+ sortable: true,
+ dataIndex: 'flags',
+ },
+ ],
+
+ tbar: [
+ {
+ text: gettext('Add'),
+ handler: 'onAdd',
+ },
+ '-',
+ {
+ xtype: 'proxmoxStdRemoveButton',
+ baseurl: '/api2/extjs/nodes/localhost/capabilities/qemu/cpu/model/',
+ getRecordName: (rec) => rec.data.cputype,
+ getUrl: function(rec) {
+ let me = this;
+ return me.baseurl + rec.data.cputype;
+ },
+ callback: 'reload',
+ },
+ {
+ text: gettext('Edit'),
+ handler: 'onEdit',
+ },
+ ],
+
+ selModel: {
+ xtype: 'Ext.selection.RowModel',
+ },
+
+ listeners: {
+ itemdblclick: function(_, rec) {
+ let me = this;
+ me.getController().showEditor(rec.data.cputype);
+ },
+ },
+
+ initComponent: function() {
+ let me = this;
+ me.callParent();
+ Proxmox.Utils.monStoreErrors(me, me.store);
+ },
+
+}, function() {
+ Ext.define('pve-custom-cpu-type', {
+ extend: 'Ext.data.Model',
+ fields: [
+ 'cputype', 'reported-model', 'hv-vendor-id', 'flags', 'phys-bits',
+ { name: 'hidden', type: 'boolean' },
+ ],
+ });
+});
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 934952d9..cbaabb9e 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -70,6 +70,12 @@ Ext.define('PVE.dc.Config', {
title: gettext('Replication'),
itemId: 'replication',
},
+ {
+ xtype: 'pveCPUTypeView',
+ iconCls: 'pve-itype-icon-processor pve-icon pve-icon-sidebar',
+ title: gettext('CPU Types'),
+ itemId: 'cputypes',
+ },
{
xtype: 'pveACLView',
title: gettext('Permissions'),
diff --git a/www/manager6/form/PhysBitsSelector.js b/www/manager6/form/PhysBitsSelector.js
new file mode 100644
index 00000000..a18b675f
--- /dev/null
+++ b/www/manager6/form/PhysBitsSelector.js
@@ -0,0 +1,128 @@
+Ext.define('PVE.form.PhysBitsSelector', {
+ extend: 'Ext.form.FieldContainer',
+ alias: 'widget.PhysBitsSelector',
+ mixins: ['Ext.form.field.Field'],
+
+ layout: 'vbox',
+ initialValue: '',
+ originalValue: '',
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ updateNumberField: function() {
+ let me = this;
+ let modeCustom = me.lookupReference('modeCustom');
+ let customNum = me.lookupReference('customNum');
+
+ customNum.setDisabled(!modeCustom.getValue());
+ me.getView().validate();
+ },
+
+ listen: {
+ component: {
+ '*': {
+ change: function() {
+ let me = this;
+ me.getView().checkChange();
+ },
+ },
+ },
+ },
+ },
+
+ getValue: function() {
+ let me = this;
+ let ctrl = me.getController();
+ if (ctrl.lookupReference('modeDefault').getValue()) {
+ return '';
+ } else if (ctrl.lookupReference('modeHost').getValue()) {
+ return 'host';
+ } else if (ctrl.lookupReference('modeCustom').getValue()) {
+ return ctrl.lookupReference('customNum').getValue();
+ }
+ return ''; // shouldn't happen
+ },
+
+ setValue: function(value) {
+ let me = this;
+ let ctrl = me.getController();
+ let modeField;
+
+ if (!value) {
+ modeField = ctrl.lookupReference('modeDefault');
+ } else if (value === 'host') {
+ modeField = ctrl.lookupReference('modeHost');
+ } else {
+ let customNum = me.lookupReference('customNum');
+ customNum.setValue(value);
+ modeField = ctrl.lookupReference('modeCustom');
+ }
+
+ modeField.setValue(true);
+ me.checkChange();
+
+ return value;
+ },
+
+ getErrors: function() {
+ let me = this;
+ let ctrl = me.getController();
+ if (ctrl.lookupReference('modeCustom').getValue()) {
+ return ctrl.lookupReference('customNum').getErrors();
+ }
+ return [];
+ },
+
+ isValid: function() {
+ let me = this;
+ let ctrl = me.getController();
+ return ctrl.lookupReference('customNum').isValid();
+ },
+
+ items: [
+ {
+ xtype: 'radiofield',
+ boxLabel: gettext('Default'),
+ inputValue: 'default',
+ checked: true,
+ reference: 'modeDefault',
+ isFormField: false,
+ },
+ {
+ xtype: 'radiofield',
+ boxLabel: gettext('Host'),
+ inputValue: 'host',
+ reference: 'modeHost',
+ isFormField: false,
+ },
+ {
+ xtype: 'fieldcontainer',
+ layout: 'hbox',
+ items: [
+ {
+ xtype: 'radiofield',
+ boxLabel: gettext('Custom'),
+ inputValue: 'custom',
+ listeners: {
+ change: 'updateNumberField',
+ },
+ reference: 'modeCustom',
+ isFormField: false,
+ },
+ {
+ xtype: 'numberfield',
+ width: '60px',
+ margin: '0 0 0 10px',
+ minValue: 8,
+ maxValue: 64,
+ reference: 'customNum',
+ allowBlank: false,
+ isFormField: false,
+ disabled: true,
+ },
+ ],
+ },
+ ],
+});
+
--
2.30.2
next prev parent reply other threads:[~2021-10-28 11:42 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-28 11:41 [pve-devel] [RFC 0/9] Unfinished: Custom CPU type API and GUI Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC pve-qemu 1/9] Add -list-flags command line option Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 2/9] api: add recognized-flags and supported-flags endpoints Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 3/9] api: add /cpu/model/* get endpoint Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC qemu-server 4/9] api: add /cpu/model/* get/create/delete/update endpoints Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 5/9] gui: VMCPUFlagSelector: fix unknownFlags behaviour Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 6/9] gui: CPUModelSelector: fix dirty state on default Stefan Reiter
2021-10-28 11:41 ` [pve-devel] [RFC manager 7/9] gui: CPUModelSelector: add 'allowCustom' Stefan Reiter
2021-10-28 11:41 ` Stefan Reiter [this message]
2021-10-28 11:41 ` [pve-devel] [RFC manager 9/9] Initial attempt at CPU flag editor for custom models Stefan Reiter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211028114150.3245864-9-s.reiter@proxmox.com \
--to=s.reiter@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox