[pve-devel] [PATCH manager] ui: lxc: add edit window for device passthrough

Filip Schauer f.schauer at proxmox.com
Tue Nov 21 11:23:26 CET 2023


Patch v2 available:

https://lists.proxmox.com/pipermail/pve-devel/2023-November/060583.html

On 30/10/2023 12:27, Filip Schauer wrote:
> Signed-off-by: Filip Schauer <f.schauer at proxmox.com>
> ---
> Depends on:
> https://lists.proxmox.com/pipermail/pve-devel/2023-October/059616.html
>
>   www/manager6/Makefile          |   1 +
>   www/manager6/Utils.js          |  11 +++
>   www/manager6/lxc/DeviceEdit.js | 158 +++++++++++++++++++++++++++++++++
>   www/manager6/lxc/Resources.js  |  28 +++++-
>   4 files changed, 197 insertions(+), 1 deletion(-)
>   create mode 100644 www/manager6/lxc/DeviceEdit.js
>
> diff --git a/www/manager6/Makefile b/www/manager6/Makefile
> index 57e1b48f..373c8f6d 100644
> --- a/www/manager6/Makefile
> +++ b/www/manager6/Makefile
> @@ -185,6 +185,7 @@ JSSRC= 							\
>   	lxc/CmdMenu.js					\
>   	lxc/Config.js					\
>   	lxc/CreateWizard.js				\
> +	lxc/DeviceEdit.js				\
>   	lxc/DNS.js					\
>   	lxc/FeaturesEdit.js				\
>   	lxc/MPEdit.js					\
> diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
> index 8f46c07e..1ea7b42a 100644
> --- a/www/manager6/Utils.js
> +++ b/www/manager6/Utils.js
> @@ -1605,6 +1605,17 @@ Ext.define('PVE.Utils', {
>   	}
>       },
>   
> +    dev_count: 256,
> +
> +    forEachDev: function(func) {
> +	for (let i = 0; i < PVE.Utils.dev_count; i++) {
> +	    let cont = func(i);
> +	    if (!cont && cont !== undefined) {
> +		return;
> +	    }
> +	}
> +    },
> +
>       hardware_counts: {
>   	net: 32,
>   	usb: 14,
> diff --git a/www/manager6/lxc/DeviceEdit.js b/www/manager6/lxc/DeviceEdit.js
> new file mode 100644
> index 00000000..e4790c4e
> --- /dev/null
> +++ b/www/manager6/lxc/DeviceEdit.js
> @@ -0,0 +1,158 @@
> +Ext.define('PVE.lxc.DeviceInputPanel', {
> +    extend: 'Proxmox.panel.InputPanel',
> +    mixins: ['Proxmox.Mixin.CBind'],
> +
> +    autoComplete: false,
> +
> +    cbindData: function(initialConfig) {
> +	let me = this;
> +	if (!me.pveSelNode) {
> +	    throw "no pveSelNode given";
> +	}
> +
> +	return { nodename: me.pveSelNode.data.node };
> +    },
> +
> +    viewModel: {
> +	data: {},
> +    },
> +
> +    setVMConfig: function(vmconfig) {
> +	var me = this;
> +	me.vmconfig = vmconfig;
> +    },
> +
> +    onGetValues: function(values) {
> +	var me = this;
> +	if (!me.confid) {
> +	    let max_devices = 256;
> +	    for (let i = 0; i < max_devices; i++) {
> +		let id = 'dev' + i.toString();
> +		if (!me.vmconfig[id]) {
> +		    me.confid = id;
> +		    break;
> +		}
> +	    }
> +	}
> +	var val = "";
> +	var type = me.down('radiofield').getGroupValue();
> +	switch (type) {
> +	    case 'path':
> +		val = values[type];
> +		delete values[type];
> +		break;
> +	    case 'usbmapped':
> +		val = 'usbmapping=' + values[type];
> +		delete values[type];
> +		break;
> +	    default:
> +		throw "invalid type selected";
> +	}
> +
> +	values[me.confid] = val;
> +	return values;
> +    },
> +
> +    items: [
> +	{
> +	    xtype: 'fieldcontainer',
> +	    defaultType: 'radiofield',
> +	    layout: 'fit',
> +	    items: [
> +		{
> +		    name: 'dev',
> +		    inputValue: 'usbmapped',
> +		    boxLabel: gettext('Use mapped USB device'),
> +		    reference: 'usbmapped',
> +		    submitValue: false,
> +		    checked: true,
> +		},
> +		{
> +		    xtype: 'pveUSBMapSelector',
> +		    disabled: true,
> +		    name: 'usbmapped',
> +		    cbind: { nodename: '{nodename}' },
> +		    bind: { disabled: '{!usbmapped.checked}' },
> +		    allowBlank: false,
> +		    fieldLabel: gettext('Choose Device'),
> +		    labelAlign: 'right',
> +		},
> +		{
> +		    name: 'dev',
> +		    inputValue: 'path',
> +		    boxLabel: gettext('Use Device Path'),
> +		    reference: 'path',
> +		    submitValue: false,
> +		},
> +		{
> +		    xtype: 'textfield',
> +		    disabled: true,
> +		    type: 'device',
> +		    name: 'path',
> +		    cbind: { pveSelNode: '{pveSelNode}' },
> +		    bind: { disabled: '{!path.checked}' },
> +		    editable: true,
> +		    allowBlank: false,
> +		    fieldLabel: gettext('Device Path'),
> +		    labelAlign: 'right',
> +		},
> +	    ],
> +	},
> +    ],
> +});
> +
> +Ext.define('PVE.lxc.DeviceEdit', {
> +    extend: 'Proxmox.window.Edit',
> +
> +    vmconfig: undefined,
> +
> +    isAdd: true,
> +    width: 400,
> +    subject: gettext('Device'),
> +
> +    initComponent: function() {
> +	var me = this;
> +
> +	me.isCreate = !me.confid;
> +
> +	var ipanel = Ext.create('PVE.lxc.DeviceInputPanel', {
> +	    confid: me.confid,
> +	    pveSelNode: me.pveSelNode,
> +	});
> +
> +	Ext.apply(me, {
> +	    items: [ipanel],
> +	});
> +
> +	me.callParent();
> +
> +	me.load({
> +	    success: function(response, options) {
> +		ipanel.setVMConfig(response.result.data);
> +		if (me.isCreate) {
> +		    return;
> +		}
> +
> +		let data = PVE.Parser.parsePropertyString(response.result.data[me.confid], 'path');
> +		let path, usbmapped;
> +		let dev;
> +
> +		if (data.path) {
> +		    path = data.path;
> +		    dev = 'path';
> +		} else if (data.usbmapping) {
> +		    usbmapped = data.usbmapping;
> +		    dev = 'usbmapped';
> +		}
> +
> +		var values = {
> +		    dev,
> +		    path,
> +		    usbmapped,
> +		};
> +
> +		ipanel.setValues(values);
> +	    },
> +	});
> +    },
> +});
> diff --git a/www/manager6/lxc/Resources.js b/www/manager6/lxc/Resources.js
> index 85112345..9dcb74eb 100644
> --- a/www/manager6/lxc/Resources.js
> +++ b/www/manager6/lxc/Resources.js
> @@ -135,6 +135,17 @@ Ext.define('PVE.lxc.RessourceView', {
>   	    };
>   	}, true);
>   
> +	PVE.Utils.forEachDev(function(i) {
> +	    confid = 'dev' + i;
> +	    rows[confid] = {
> +		group: 7,
> +		order: i,
> +		tdCls: 'pve-itype-icon-pci',
> +		editor: 'PVE.lxc.DeviceEdit',
> +		header: gettext('Device') + ' (' + confid + ')',
> +	    };
> +	});
> +
>   	var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config';
>   
>   	me.selModel = Ext.create('Ext.selection.RowModel', {});
> @@ -311,6 +322,7 @@ Ext.define('PVE.lxc.RessourceView', {
>   	    let isDisk = isRootFS || key.match(/^(mp|unused)\d+/);
>   	    let isUnusedDisk = key.match(/^unused\d+/);
>   	    let isUsedDisk = isDisk && !isUnusedDisk;
> +	    let isDevice = key.match(/^dev\d+/);
>   
>   	    let noedit = isDelete || !rowdef.editor;
>   	    if (!noedit && Proxmox.UserName !== 'root at pam' && key.match(/^mp\d+$/)) {
> @@ -326,7 +338,7 @@ Ext.define('PVE.lxc.RessourceView', {
>   	    reassign_menuitem.setDisabled(isRootFS);
>   	    resize_menuitem.setDisabled(isUnusedDisk);
>   
> -	    remove_btn.setDisabled(!isDisk || isRootFS || !diskCap || pending);
> +	    remove_btn.setDisabled(!(isDisk || isDevice) || isRootFS || !diskCap || pending);
>   	    revert_btn.setDisabled(!pending);
>   
>   	    remove_btn.setText(isUsedDisk ? remove_btn.altText : remove_btn.defaultText);
> @@ -380,6 +392,20 @@ Ext.define('PVE.lxc.RessourceView', {
>   				    });
>   				},
>   			    },
> +			    {
> +				text: gettext('Device Passthrough'),
> +				iconCls: 'pve-itype-icon-pci',
> +				handler: function() {
> +				    Ext.create('PVE.lxc.DeviceEdit', {
> +					autoShow: true,
> +					url: `/api2/extjs/${baseurl}`,
> +					pveSelNode: me.pveSelNode,
> +					listeners: {
> +					    destroy: () => me.reload(),
> +					},
> +				    });
> +				},
> +			    },
>   			],
>   		    }),
>   		},





More information about the pve-devel mailing list