* [pve-devel] [PATCH docs v3 1/6] added shared filesystem doc for virtio-fs & virtio-9p
2023-03-31 11:16 [pve-devel] [PATCH docs/acces-control/manager/qemu-server v3 0/6] feature #1027 virtio-9p/virtio-fs Markus Frank
@ 2023-03-31 11:16 ` Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH access-control v3 2/6] added acls for Shared Files Directories Markus Frank
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Markus Frank @ 2023-03-31 11:16 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
qm.adoc | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/qm.adoc b/qm.adoc
index 0726b29..8cc1678 100644
--- a/qm.adoc
+++ b/qm.adoc
@@ -934,6 +934,67 @@ recommended to always use a limiter to avoid guests using too many host
resources. If desired, a value of '0' for `max_bytes` can be used to disable
all limits.
+[[qm_sharedfiles]]
+Shared Filesystems
+~~~~~~~~~~~~~~~~~~
+
+add directories for Shared Filesystems
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To add a directory either add a directory to the "Shared Files" list in the node
+config in the WebUI or add it to the /etc/pve/nodes/<nodename>/dirs config file
+like this:
+
+----
+<dirid>: /path/to/share
+----
+
+9pfs (virtio-9p)
+^^^^^^^^^^^^^^^^
+
+QEMU's 9pfs uses the Plan 9 Filesystem Protocol to share a directory on the host
+with a guest VM.
+
+To share a directory with 9p, run the following command:
+
+----
+qm set <vmid> -sharedfiles0 virtio-9p,dirid=<dirid>,tag=<mount tag>
+----
+
+To mount QEMU's 9pfs in a guest VM with the Linux kernel 9p driver, run the
+following command:
+
+----
+mount -t 9p -o trans=virtio,version=9p2000.L <mount tag> <mount point>
+----
+
+https://www.linux-kvm.org/page/9p_virtio
+
+https://wiki.qemu.org/Documentation/9psetup
+
+virtio-fs
+^^^^^^^^^
+
+Virtio-fs is a shared file system, that enables sharing a directory between
+host and guest VM while taking advantage of the locality of virtual machines
+and the hypervisor to get a higher throughput than 9p.
+Numa must be disabled to use virtio-fs.
+
+To share a directory with virtio-fs, run the following command:
+
+----
+qm set <vmid> -sharedfiles0 virtio-fs,dirid=<dirid>,tag=<mount tag>
+----
+
+To mount virtio-fs in a guest VM with the Linux kernel virtiofs driver, run the
+following command:
+
+----
+mount -t virtiofs <mount tag> <mount point>
+----
+
+https://virtio-fs.gitlab.io/howto-qemu.html
+
[[qm_bootorder]]
Device Boot Order
~~~~~~~~~~~~~~~~~
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pve-devel] [PATCH access-control v3 2/6] added acls for Shared Files Directories
2023-03-31 11:16 [pve-devel] [PATCH docs/acces-control/manager/qemu-server v3 0/6] feature #1027 virtio-9p/virtio-fs Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH docs v3 1/6] added shared filesystem doc for virtio-fs & virtio-9p Markus Frank
@ 2023-03-31 11:16 ` Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH manager v3 3/6] added Config for Shared Filesystem Directories Markus Frank
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Markus Frank @ 2023-03-31 11:16 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
src/PVE/API2/Directory.pm | 68 +++++++++++++++++++++++++++++++++++++++
src/PVE/AccessControl.pm | 16 +++++++++
src/PVE/RPCEnvironment.pm | 12 ++++++-
3 files changed, 95 insertions(+), 1 deletion(-)
create mode 100644 src/PVE/API2/Directory.pm
diff --git a/src/PVE/API2/Directory.pm b/src/PVE/API2/Directory.pm
new file mode 100644
index 0000000..b44ba9d
--- /dev/null
+++ b/src/PVE/API2/Directory.pm
@@ -0,0 +1,68 @@
+package PVE::API2::Directory;
+
+use strict;
+use warnings;
+
+use PVE::Exception qw(raise raise_perm_exc raise_param_exc);
+use PVE::Cluster qw (cfs_read_file cfs_write_file);
+use PVE::Tools qw(split_list extract_param);
+use PVE::JSONSchema qw(get_standard_option register_standard_option);
+use PVE::SafeSyslog;
+
+use PVE::AccessControl;
+use PVE::Auth::Plugin;
+use PVE::TokenConfig;
+
+use PVE::RESTHandler;
+use PVE::DirConfig;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+ name => 'index',
+ path => '',
+ method => 'GET',
+ description => "simple return value of parameter 'text'",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => {
+ type => 'string',
+ }
+ },
+ },
+ returns => {
+ type => 'string',
+ },
+ code => sub {
+ my ($param) = @_;
+ return $param->{node};
+ }
+});
+
+
+__PACKAGE__->register_method ({
+ name => 'add_dir',
+ path => 'add',
+ method => 'POST',
+ description => "simple return value of parameter 'text'",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ directory => {
+ type => 'string',
+ },
+ node => {
+ type => 'string',
+ }
+ },
+ },
+ returns => {
+ type => 'string',
+ },
+ code => sub {
+ my ($param) = @_;
+
+ return $param->{node};
+ }
+});
diff --git a/src/PVE/AccessControl.pm b/src/PVE/AccessControl.pm
index a95d072..b1271d3 100644
--- a/src/PVE/AccessControl.pm
+++ b/src/PVE/AccessControl.pm
@@ -1092,6 +1092,18 @@ my $privgroups = {
'Pool.Audit',
],
},
+ Map => {
+ root => [],
+ admin => [
+ 'Map.Modify',
+ ],
+ user => [
+ 'Map.Use',
+ ],
+ audit => [
+ 'Map.Audit',
+ ],
+ },
};
my $valid_privs = {};
@@ -1125,6 +1137,8 @@ sub create_roles {
}
$special_roles->{"PVETemplateUser"} = { 'VM.Clone' => 1, 'VM.Audit' => 1 };
+
+ delete($special_roles->{"PVEAdmin"}->{'Map.Modify'});
};
create_roles();
@@ -1221,6 +1235,8 @@ sub check_path {
|/storage/[[:alnum:]\.\-\_]+
|/vms
|/vms/[1-9][0-9]{2,}
+ |/map/dirs
+ |/map/dirs/[[:alnum:]\.\-\_]+
)$!xs;
}
diff --git a/src/PVE/RPCEnvironment.pm b/src/PVE/RPCEnvironment.pm
index 0ee2346..9c88ac6 100644
--- a/src/PVE/RPCEnvironment.pm
+++ b/src/PVE/RPCEnvironment.pm
@@ -187,10 +187,11 @@ sub compute_api_permission {
nodes => qr/Sys\.|Permissions\.Modify/,
sdn => qr/SDN\.|Permissions\.Modify/,
dc => qr/Sys\.Audit|SDN\./,
+ 'map' => qr/Map\.Modify/
};
map { $res->{$_} = {} } keys %$priv_re_map;
- my $required_paths = ['/', '/nodes', '/access/groups', '/vms', '/storage', '/sdn'];
+ my $required_paths = ['/', '/nodes', '/access/groups', '/vms', '/storage', '/sdn', '/map'];
my $checked_paths = {};
foreach my $path (@$required_paths, keys %{$usercfg->{acl}}) {
@@ -240,6 +241,7 @@ sub get_effective_permissions {
'/sdn' => 1,
'/storage' => 1,
'/vms' => 1,
+ '/map' => 1,
};
my $cfg = $self->{user_cfg};
@@ -355,6 +357,14 @@ sub check_vm_perm {
return $self->check_full($user, "/vms/$vmid", $privs, $any, $noerr);
};
+sub check_dir_perm {
+ my ($self, $user, $dirid, $privs, $any, $noerr) = @_;
+
+ my $cfg = $self->{user_cfg};
+
+ return $self->check_full($user, "/map/dirs/$dirid", $privs, $any, $noerr);
+};
+
sub is_group_member {
my ($self, $group, $user) = @_;
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pve-devel] [PATCH manager v3 3/6] added Config for Shared Filesystem Directories
2023-03-31 11:16 [pve-devel] [PATCH docs/acces-control/manager/qemu-server v3 0/6] feature #1027 virtio-9p/virtio-fs Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH docs v3 1/6] added shared filesystem doc for virtio-fs & virtio-9p Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH access-control v3 2/6] added acls for Shared Files Directories Markus Frank
@ 2023-03-31 11:16 ` Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH manager v3 4/6] added Shared Files tab in Node Settings Markus Frank
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Markus Frank @ 2023-03-31 11:16 UTC (permalink / raw)
To: pve-devel
and made an API Endpoint for getting, adding and removing
directories to the config.
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
PVE/API2/DirConfig.pm | 129 +++++++++++++++++++++++++++++++++++++++
PVE/API2/Makefile | 1 +
PVE/API2/Nodes.pm | 6 ++
PVE/DirConfig.pm | 139 ++++++++++++++++++++++++++++++++++++++++++
PVE/Makefile | 1 +
5 files changed, 276 insertions(+)
create mode 100644 PVE/API2/DirConfig.pm
create mode 100644 PVE/DirConfig.pm
diff --git a/PVE/API2/DirConfig.pm b/PVE/API2/DirConfig.pm
new file mode 100644
index 00000000..0cbc6f96
--- /dev/null
+++ b/PVE/API2/DirConfig.pm
@@ -0,0 +1,129 @@
+package PVE::API2::DirConfig;
+
+use strict;
+use warnings;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::DirConfig;
+use PVE::Tools qw(extract_param);
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method({
+ name => 'get_config',
+ path => '',
+ method => 'GET',
+ description => "Get Directories for Host Directory Sharing.",
+ permissions => {
+ check => ['perm', '/map/dirs', [ 'Map.Audit' ]],
+ },
+ proxyto => 'node',
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => 'object',
+ properties => {
+ dirid => {
+ type => 'string',
+ description => 'Directory ID',
+ },
+ path => {
+ type => 'string',
+ description => 'Host Directory Path',
+ },
+ },
+ },
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $config = PVE::DirConfig::load_config($param->{node});
+ delete $config->{description};
+ my $result = [];
+ foreach my $key (keys %{$config}) {
+ push @$result, {
+ dirid => $key,
+ path => $config->{$key},
+ };
+ }
+
+ return $result;
+ }
+});
+
+__PACKAGE__->register_method({
+ name => 'add_dir',
+ path => '',
+ method => 'POST',
+ description => "Add Directories for Host Directory Sharing.",
+ permissions => {
+ check => ['perm', '/map/dirs', [ 'Map.Modify' ]],
+ },
+ protected => 1,
+ proxyto => 'node',
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ dirid => {
+ type => 'string',
+ pattern => '[a-zA-Z0-9\-]+',
+ },
+ path => {
+ type => 'string',
+ maxLength => 4096,
+ format => 'pve-storage-path',
+ },
+ },
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+ my $node = extract_param($param, 'node');
+ my $dirid = extract_param($param, 'dirid');
+ my $path = extract_param($param, 'path');
+ PVE::DirConfig::add_dir_config($node, $dirid, $path);
+ return undef;
+ },
+});
+
+
+__PACKAGE__->register_method({
+ name => 'del_dir',
+ path => '',
+ method => 'DELETE',
+ description => "Remove Directory from Host Directory Sharing.",
+ permissions => {
+ check => ['perm', '/map/dirs', [ 'Map.Modify' ]],
+ },
+ protected => 1,
+ proxyto => 'node',
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ dirid => {
+ type => 'string',
+ pattern => '[a-zA-Z0-9\-]+',
+ },
+ },
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+
+ my $node = extract_param($param, 'node');
+ my $dirid = extract_param($param, 'dirid');
+ PVE::DirConfig::del_dir_config($node, $dirid);
+ return undef;
+ },
+});
+
+
+1;
diff --git a/PVE/API2/Makefile b/PVE/API2/Makefile
index 5c08ebe0..1b96223c 100644
--- a/PVE/API2/Makefile
+++ b/PVE/API2/Makefile
@@ -12,6 +12,7 @@ PERLSOURCE = \
Ceph.pm \
Certificates.pm \
Cluster.pm \
+ DirConfig.pm \
HAConfig.pm \
Hardware.pm \
Network.pm \
diff --git a/PVE/API2/Nodes.pm b/PVE/API2/Nodes.pm
index bfe5c40a..b4e2992f 100644
--- a/PVE/API2/Nodes.pm
+++ b/PVE/API2/Nodes.pm
@@ -48,6 +48,7 @@ use PVE::API2::LXC::Status;
use PVE::API2::LXC;
use PVE::API2::Network;
use PVE::API2::NodeConfig;
+use PVE::API2::DirConfig;
use PVE::API2::Qemu::CPU;
use PVE::API2::Qemu;
use PVE::API2::Replication;
@@ -199,6 +200,11 @@ __PACKAGE__->register_method ({
path => 'config',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::DirConfig",
+ path => 'dirs',
+});
+
if ($have_sdn) {
__PACKAGE__->register_method ({
subclass => "PVE::API2::Network::SDN::Zones::Status",
diff --git a/PVE/DirConfig.pm b/PVE/DirConfig.pm
new file mode 100644
index 00000000..26e7803e
--- /dev/null
+++ b/PVE/DirConfig.pm
@@ -0,0 +1,139 @@
+package PVE::DirConfig;
+
+use strict;
+use warnings;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(file_get_contents file_set_contents lock_file);
+
+my $dir_config_lock = '/var/lock/dirs.lock';
+
+sub config_file {
+ my ($node) = @_;
+
+ return "/etc/pve/nodes/${node}/dirs";
+}
+
+sub load_config {
+ my ($node) = @_;
+
+ my $filename = config_file($node);
+ my $raw = eval { PVE::Tools::file_get_contents($filename); };
+ return {} if !$raw;
+
+ return parse_file_config($raw, $filename);
+}
+
+sub write_config {
+ my ($node, $conf) = @_;
+
+ my $filename = config_file($node);
+
+ my $raw = write_file_config($conf);
+
+ PVE::Tools::file_set_contents($filename, $raw);
+}
+
+sub lock_config {
+ my ($node, $realcode, @param) = @_;
+
+ # make sure configuration file is up-to-date
+ my $code = sub {
+ PVE::Cluster::cfs_update();
+ $realcode->(@_);
+ };
+
+ my $res = lock_file($dir_config_lock, 10, $code, @param);
+
+ die $@ if $@;
+
+ return $res;
+}
+
+my $descr = " Description for Shared Files Directory Config.\n"
+ ." Add Directories with:\n dirid: /path/to/share";
+
+my $dir_desc = {
+ path => {
+ type => 'string',
+ format_description => 'path',
+ description => 'path of Directory ID',
+ default_key => 1,
+ },
+};
+
+my $conf_schema = {
+ type => 'object',
+ properties => {},
+};
+
+sub parse_file_config : prototype($$) {
+ my ($content, $filename) = @_;
+ return undef if !defined($content);
+
+ my $conf = PVE::JSONSchema::parse_config($conf_schema, $filename, $content, 'description');
+
+ return $conf;
+}
+
+sub parse_dir_config {
+ my ($data) = @_;
+
+ return PVE::JSONSchema::parse_property_string($dir_desc, $data);
+}
+
+sub print_dir_config {
+ my ($data) = @_;
+
+ return PVE::JSONSchema::print_property_string($data, $dir_desc);
+}
+
+sub add_dir_config {
+ my ($node, $dirid, $path) = @_;
+ my $code = sub {
+ my $conf = load_config($node);
+ if (! -e $path) {
+ mkdir $path;
+ }
+ if ((-e $path) && (! -d $path)) {
+ die "Path $path exists but is not a directory\n"
+ }
+ $conf->{$dirid} = $path;
+ write_config($node, $conf);
+ };
+ lock_config($node, $code);
+ die $@ if $@;
+}
+
+sub del_dir_config {
+ my ($node, $dirid) = @_;
+ my $code = sub {
+ my $conf = load_config($node);
+ delete $conf->{$dirid};
+ write_config($node, $conf);
+ };
+ lock_config($node, $code);
+ die $@ if $@;
+}
+
+sub write_file_config {
+ my ($conf) = @_;
+
+ my $raw = '';
+ # add description as comment to top of file
+ foreach my $cl (split(/\n/, $descr)) {
+ $raw .= '#' . $cl . "\n";
+ }
+
+ for my $key (sort keys %$conf) {
+ next if ($key eq 'description');
+ my $value = $conf->{$key};
+ die "detected invalid newline inside property '$key'\n"
+ if $value =~ m/\n/;
+ $raw .= "$key: $value\n";
+ }
+
+ return $raw;
+}
+
+1;
diff --git a/PVE/Makefile b/PVE/Makefile
index 48b85d33..c11066bd 100644
--- a/PVE/Makefile
+++ b/PVE/Makefile
@@ -9,6 +9,7 @@ PERLSOURCE = \
AutoBalloon.pm \
CertCache.pm \
CertHelpers.pm \
+ DirConfig.pm \
ExtMetric.pm \
HTTPServer.pm \
Jobs.pm \
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pve-devel] [PATCH manager v3 4/6] added Shared Files tab in Node Settings
2023-03-31 11:16 [pve-devel] [PATCH docs/acces-control/manager/qemu-server v3 0/6] feature #1027 virtio-9p/virtio-fs Markus Frank
` (2 preceding siblings ...)
2023-03-31 11:16 ` [pve-devel] [PATCH manager v3 3/6] added Config for Shared Filesystem Directories Markus Frank
@ 2023-03-31 11:16 ` Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH manager v3 5/6] added options to add virtio-9p & virtio-fs Shared Filesystems to qemu config Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH qemu-server v3 6/6] feature #1027: virtio-9p & virtio-fs support Markus Frank
5 siblings, 0 replies; 7+ messages in thread
From: Markus Frank @ 2023-03-31 11:16 UTC (permalink / raw)
To: pve-devel
to add/remove/show directories that are available for shared
filesystems.
and added /map/dirs/ path to PermPathStore.
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
www/manager6/Makefile | 1 +
www/manager6/data/PermPathStore.js | 3 +
www/manager6/node/Config.js | 12 ++
www/manager6/node/SharedFiles.js | 177 +++++++++++++++++++++++++++++
4 files changed, 193 insertions(+)
create mode 100644 www/manager6/node/SharedFiles.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 2b577c8e..bce14fdb 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -199,6 +199,7 @@ JSSRC= \
node/CmdMenu.js \
node/Config.js \
node/Directory.js \
+ node/SharedFiles.js \
node/LVM.js \
node/LVMThin.js \
node/StatusView.js \
diff --git a/www/manager6/data/PermPathStore.js b/www/manager6/data/PermPathStore.js
index cf702c03..d83f9e12 100644
--- a/www/manager6/data/PermPathStore.js
+++ b/www/manager6/data/PermPathStore.js
@@ -13,6 +13,7 @@ Ext.define('PVE.data.PermPathStore', {
{ 'value': '/sdn/zones' },
{ 'value': '/storage' },
{ 'value': '/vms' },
+ { 'value': '/map/dirs' },
],
constructor: function(config) {
@@ -39,6 +40,8 @@ Ext.define('PVE.data.PermPathStore', {
break;
case 'pool': path = '/pool/' + record.get('pool');
break;
+ case 'dirs': path = '/map/dirs/' + record.get('dirs');
+ break;
}
if (path !== undefined && !donePaths[path]) {
me.add({ value: path });
diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js
index 0cc23fb4..b35d5abc 100644
--- a/www/manager6/node/Config.js
+++ b/www/manager6/node/Config.js
@@ -410,6 +410,18 @@ Ext.define('PVE.node.Config', {
});
}
+ if (caps.map['Map.Modify']) {
+ me.items.push(
+ {
+ xtype: 'pveSharedFilesList',
+ title: gettext('Shared Files'),
+ iconCls: 'fa fa-folder',
+ onlineHelp: 'qm_sharedfiles',
+ itemId: 'sharedFiles',
+ },
+ );
+ }
+
me.items.push(
{
title: gettext('Task History'),
diff --git a/www/manager6/node/SharedFiles.js b/www/manager6/node/SharedFiles.js
new file mode 100644
index 00000000..4152de1b
--- /dev/null
+++ b/www/manager6/node/SharedFiles.js
@@ -0,0 +1,177 @@
+Ext.define('PVE.node.CreateSharedFiles', {
+ extend: 'Proxmox.window.Edit',
+ xtype: 'pveCreateSharedFiles',
+
+ subject: "Shared Directory",
+
+ onlineHelp: 'qm_sharedfiles',
+
+ initComponent: function() {
+ var me = this;
+
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ me.isCreate = true;
+
+ Ext.applyIf(me, {
+ url: "/nodes/" + me.nodename + "/dirs",
+ method: 'POST',
+ items: [
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'dirid',
+ fieldLabel: gettext('Directory ID'),
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'path',
+ fieldLabel: gettext('Directory Path'),
+ allowBlank: false,
+ },
+ ],
+ });
+
+ me.callParent();
+ },
+});
+
+Ext.define('PVE.node.SharedFilesList', {
+ extend: 'Ext.grid.Panel',
+ xtype: 'pveSharedFilesList',
+
+ viewModel: {
+ data: {
+ path: '',
+ },
+ formulas: {
+ dirid: (get) => get('dirid'),
+ },
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ removeDirectory: function() {
+ let me = this;
+ let vm = me.getViewModel();
+ let view = me.getView();
+
+ const dirid = vm.get('dirid');
+
+ if (!view.nodename) {
+ throw "no node name specified";
+ }
+
+ if (!dirid) {
+ throw "no directory name specified";
+ }
+ Ext.create('Proxmox.window.SafeDestroy', {
+ url: `/nodes/${view.nodename}/dirs`,
+ item: { id: dirid },
+ params: { dirid: dirid },
+ taskName: 'Remove Directory from Shared Files',
+ autoShow: true,
+ listeners: {
+ destroy: () => view.reload(),
+ },
+ }).show();
+ },
+ },
+
+ stateful: true,
+ stateId: 'grid-node-directory',
+ columns: [
+ {
+ text: gettext('Directory ID'),
+ dataIndex: 'dirid',
+ flex: 1,
+ },
+ {
+ text: gettext('Directory Path'),
+ dataIndex: 'path',
+ flex: 1,
+ },
+ ],
+
+ rootVisible: false,
+ useArrows: true,
+
+ tbar: [
+ {
+ text: gettext('Reload'),
+ iconCls: 'fa fa-refresh',
+ handler: function() {
+ this.up('panel').reload();
+ },
+ },
+ {
+ text: `${gettext('Create')}: ${gettext('Directory')}`,
+ handler: function() {
+ let view = this.up('panel');
+ Ext.create('PVE.node.CreateSharedFiles', {
+ nodename: view.nodename,
+ listeners: {
+ destroy: () => view.reload(),
+ },
+ autoShow: true,
+ });
+ },
+ },
+ {
+ text: gettext('Remove'),
+ itemId: 'removeDir',
+ handler: 'removeDirectory',
+ disabled: true,
+ bind: {
+ disabled: '{!dirid}',
+ },
+ },
+ ],
+
+ reload: function() {
+ let me = this;
+ me.store.load();
+ me.store.sort();
+ },
+
+ listeners: {
+ activate: function() {
+ this.reload();
+ },
+ selectionchange: function(model, selected) {
+ let me = this;
+ let vm = me.getViewModel();
+
+ vm.set('dirid', selected[0]?.data.dirid || '');
+ },
+ },
+
+ initComponent: function() {
+ let me = this;
+
+ me.nodename = me.pveSelNode.data.node;
+ if (!me.nodename) {
+ throw "no node name specified";
+ }
+
+ Ext.apply(me, {
+ store: {
+ fields: ['dirid', 'path'],
+ proxy: {
+ type: 'proxmox',
+ url: `/api2/json/nodes/${me.nodename}/dirs`,
+ },
+ sorters: 'dirid',
+ },
+ });
+
+ me.callParent();
+
+ Proxmox.Utils.monStoreErrors(me, me.getStore(), true);
+ me.reload();
+ },
+});
+
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pve-devel] [PATCH manager v3 5/6] added options to add virtio-9p & virtio-fs Shared Filesystems to qemu config
2023-03-31 11:16 [pve-devel] [PATCH docs/acces-control/manager/qemu-server v3 0/6] feature #1027 virtio-9p/virtio-fs Markus Frank
` (3 preceding siblings ...)
2023-03-31 11:16 ` [pve-devel] [PATCH manager v3 4/6] added Shared Files tab in Node Settings Markus Frank
@ 2023-03-31 11:16 ` Markus Frank
2023-03-31 11:16 ` [pve-devel] [PATCH qemu-server v3 6/6] feature #1027: virtio-9p & virtio-fs support Markus Frank
5 siblings, 0 replies; 7+ messages in thread
From: Markus Frank @ 2023-03-31 11:16 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
www/manager6/Makefile | 1 +
www/manager6/Utils.js | 1 +
www/manager6/qemu/HardwareView.js | 19 +++++
www/manager6/qemu/SharedFilesEdit.js | 101 +++++++++++++++++++++++++++
4 files changed, 122 insertions(+)
create mode 100644 www/manager6/qemu/SharedFilesEdit.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index bce14fdb..5900c9a4 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -241,6 +241,7 @@ JSSRC= \
qemu/QemuBiosEdit.js \
qemu/RNGEdit.js \
qemu/SSHKey.js \
+ qemu/SharedFilesEdit.js \
qemu/ScsiHwEdit.js \
qemu/SerialEdit.js \
qemu/Smbios1Edit.js \
diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index 94e75d5c..a8e749b5 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -1596,6 +1596,7 @@ Ext.define('PVE.Utils', {
serial: 4,
rng: 1,
tpmstate: 1,
+ sharedfiles: 10,
},
// we can have usb6 and up only for specific machine/ostypes
diff --git a/www/manager6/qemu/HardwareView.js b/www/manager6/qemu/HardwareView.js
index af35a980..e1ceb32a 100644
--- a/www/manager6/qemu/HardwareView.js
+++ b/www/manager6/qemu/HardwareView.js
@@ -309,6 +309,16 @@ Ext.define('PVE.qemu.HardwareView', {
never_delete: !caps.nodes['Sys.Console'],
header: gettext("VirtIO RNG"),
};
+ for (let i = 0; i < PVE.Utils.hardware_counts.sharedfiles; i++) {
+ let confid = "sharedfiles" + i.toString();
+ rows[confid] = {
+ group: 50,
+ order: i,
+ iconCls: 'folder',
+ editor: 'PVE.qemu.SharedFilesEdit',
+ header: gettext('Shared FS') + ' (' + confid +')',
+ };
+ }
var sorterFn = function(rec1, rec2) {
var v1 = rec1.data.key;
@@ -582,6 +592,7 @@ Ext.define('PVE.qemu.HardwareView', {
const noVMConfigDiskPerm = !caps.vms['VM.Config.Disk'];
const noVMConfigCDROMPerm = !caps.vms['VM.Config.CDROM'];
const noVMConfigCloudinitPerm = !caps.vms['VM.Config.Cloudinit'];
+ const noVMConfigOptionsPerm = !caps.vms['VM.Config.Options'];
me.down('#addUsb').setDisabled(noSysConsolePerm || isAtUsbLimit());
me.down('#addPci').setDisabled(noSysConsolePerm || isAtLimit('hostpci'));
@@ -591,6 +602,7 @@ Ext.define('PVE.qemu.HardwareView', {
me.down('#addRng').setDisabled(noSysConsolePerm || isAtLimit('rng'));
efidisk_menuitem.setDisabled(noVMConfigDiskPerm || isAtLimit('efidisk'));
me.down('#addTpmState').setDisabled(noSysConsolePerm || isAtLimit('tpmstate'));
+ me.down('#addFileshare').setDisabled(noVMConfigOptionsPerm || isAtLimit('sharedfiles'));
me.down('#addCloudinitDrive').setDisabled(noVMConfigCDROMPerm || noVMConfigCloudinitPerm || hasCloudInit);
if (!rec) {
@@ -735,6 +747,13 @@ Ext.define('PVE.qemu.HardwareView', {
disabled: !caps.nodes['Sys.Console'],
handler: editorFactory('RNGEdit'),
},
+ {
+ text: gettext("Shared Filesystem"),
+ itemId: 'addFileshare',
+ iconCls: 'fa fa-folder',
+ disabled: !caps.nodes['Sys.Console'],
+ handler: editorFactory('SharedFilesEdit'),
+ },
],
}),
},
diff --git a/www/manager6/qemu/SharedFilesEdit.js b/www/manager6/qemu/SharedFilesEdit.js
new file mode 100644
index 00000000..b4d0a253
--- /dev/null
+++ b/www/manager6/qemu/SharedFilesEdit.js
@@ -0,0 +1,101 @@
+Ext.define('PVE.qemu.SharedFilesInputPanel', {
+ extend: 'Proxmox.panel.InputPanel',
+ xtype: 'pveSharedFilesInputPanel',
+ onlineHelp: 'qm_sharedfiles',
+
+ insideWizard: false,
+
+ onGetValues: function(values) {
+ var me = this;
+ var confid = me.confid;
+ var params = {};
+ params[confid] = PVE.Parser.printPropertyString(values, 'type');
+ return params;
+ },
+
+ setSharedfiles: function(confid, data) {
+ var me = this;
+ me.confid = confid;
+ me.sharedfiles = data;
+ me.setValues(me.sharedfiles);
+ },
+ items: [
+ {
+ name: 'type',
+ xtype: 'proxmoxKVComboBox',
+ fieldLabel: gettext('Shared FS Type'),
+ comboItems: [['virtio-9p', 'virtio-9p'], ['virtio-fs', 'virtio-fs']],
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ emptyText: 'dirid',
+ fieldLabel: gettext('Directory ID'),
+ name: 'dirid',
+ allowBlank: false,
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ emptyText: 'tag name',
+ fieldLabel: gettext('tag'),
+ name: 'tag',
+ allowBlank: false,
+ },
+ ],
+ initComponent: function() {
+ var me = this;
+
+ me.sharedfiles = {};
+ me.confid = 'sharedfiles0';
+ me.callParent();
+ },
+});
+
+Ext.define('PVE.qemu.SharedFilesEdit', {
+ extend: 'Proxmox.window.Edit',
+
+ subject: gettext('Filesystem Passthrough'),
+
+ initComponent: function() {
+ var me = this;
+
+ me.isCreate = !me.confid;
+
+ var ipanel = Ext.create('PVE.qemu.SharedFilesInputPanel', {
+ confid: me.confid,
+ isCreate: me.isCreate,
+ });
+
+ Ext.applyIf(me, {
+ items: ipanel,
+ });
+
+ me.callParent();
+
+ me.load({
+ success: function(response) {
+ me.conf = response.result.data;
+ var i, confid;
+ if (!me.isCreate) {
+ var value = me.conf[me.confid];
+ var sharedfiles = PVE.Parser.parsePropertyString(value, "type");
+ if (!sharedfiles) {
+ Ext.Msg.alert(gettext('Error'), 'Unable to parse sharedfiles options');
+ me.close();
+ return;
+ }
+ ipanel.setSharedfiles(me.confid, sharedfiles);
+ } else {
+ for (i = 0; i < PVE.Utils.hardware_counts.sharedfiles; i++) {
+ confid = 'sharedfiles' + i.toString();
+ if (!Ext.isDefined(me.conf[confid])) {
+ me.confid = confid;
+ break;
+ }
+ }
+ ipanel.setSharedfiles(me.confid, {});
+ }
+ },
+ });
+ },
+});
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread
* [pve-devel] [PATCH qemu-server v3 6/6] feature #1027: virtio-9p & virtio-fs support
2023-03-31 11:16 [pve-devel] [PATCH docs/acces-control/manager/qemu-server v3 0/6] feature #1027 virtio-9p/virtio-fs Markus Frank
` (4 preceding siblings ...)
2023-03-31 11:16 ` [pve-devel] [PATCH manager v3 5/6] added options to add virtio-9p & virtio-fs Shared Filesystems to qemu config Markus Frank
@ 2023-03-31 11:16 ` Markus Frank
5 siblings, 0 replies; 7+ messages in thread
From: Markus Frank @ 2023-03-31 11:16 UTC (permalink / raw)
To: pve-devel
adds support for sharing directorys with a guest vm
virtio-9p can be simply started with qemu
virtio-fs needs virtiofsd to be started
Signed-off-by: Markus Frank <m.frank@proxmox.com>
---
PVE/API2/Qemu.pm | 20 +++++-
PVE/QemuServer.pm | 158 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 587bb22..aaeefe2 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -639,6 +639,8 @@ my $check_vm_modify_config_perm = sub {
# the user needs Disk and PowerMgmt privileges to change the vmstate
# also needs privileges on the storage, that will be checked later
$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk', 'VM.PowerMgmt' ]);
+ } elsif ($opt =~ m/^sharedfiles\d$/) {
+ # needs $param->{$opt} so checkout $check_vm_dir_perm
} else {
# catches hostpci\d+, args, lock, etc.
# new options will be checked here
@@ -649,6 +651,20 @@ my $check_vm_modify_config_perm = sub {
return 1;
};
+my $check_vm_dir_perm = sub {
+ my ($rpcenv, $authuser, $param) = @_;
+
+ return 1 if $authuser eq 'root@pam';
+
+ foreach my $opt (keys %{$param}) {
+ if ($opt =~ m/^sharedfiles\d$/) {
+ my $sharedfiles = PVE::QemuServer::parse_sharedfiles($param->{$opt});
+ $rpcenv->check_dir_perm($authuser, $sharedfiles->{dirid}, ['Map.Use']);
+ }
+ }
+ return 1;
+};
+
__PACKAGE__->register_method({
name => 'vmlist',
path => '',
@@ -875,7 +891,7 @@ __PACKAGE__->register_method({
&$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param, $storage);
&$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, $pool, [ keys %$param]);
-
+ &$check_vm_dir_perm($rpcenv, $authuser, $param);
&$check_vm_create_serial_perm($rpcenv, $authuser, $vmid, $pool, $param);
&$check_vm_create_usb_perm($rpcenv, $authuser, $vmid, $pool, $param);
@@ -1576,6 +1592,8 @@ my $update_vm_api = sub {
&$check_vm_modify_config_perm($rpcenv, $authuser, $vmid, undef, [keys %$param]);
+ &$check_vm_dir_perm($rpcenv, $authuser, $param);
+
&$check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param);
my $updatefn = sub {
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index c1d0fd2..af47311 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -57,6 +57,7 @@ use PVE::QemuServer::Memory;
use PVE::QemuServer::Monitor qw(mon_cmd);
use PVE::QemuServer::PCI qw(print_pci_addr print_pcie_addr print_pcie_root_port parse_hostpci);
use PVE::QemuServer::USB qw(parse_usb_device);
+use PVE::DirConfig;
my $have_sdn;
eval {
@@ -274,6 +275,31 @@ my $rng_fmt = {
},
};
+my $sharedfiles_fmt = {
+ type => {
+ type => 'string',
+ default_key => 1,
+ enum => ['virtio-9p', 'virtio-fs'],
+ description => "sharedfiles via"
+ ." virtio-9p (https://www.linux-kvm.org/page/9p_virtio)"
+ ." or virtio-fs (https://virtio-fs.gitlab.io/howto-qemu.html)",
+ format_description => "virtio-sharedfiles-type",
+ optional => 1,
+ },
+ dirid => {
+ type => 'string',
+ description => "dirid of directory you want to share with the guest VM",
+ format_description => "virtio-sharedfiles-dirid",
+ optional => 1,
+ },
+ tag => {
+ type => 'string',
+ description => "tag name for mounting in the guest VM",
+ format_description => "virtio-sharedfiles-tag",
+ optional => 1,
+ },
+};
+
my $meta_info_fmt = {
'ctime' => {
type => 'integer',
@@ -832,6 +858,7 @@ while (my ($k, $v) = each %$confdesc) {
my $MAX_USB_DEVICES = 14;
my $MAX_NETS = 32;
+my $MAX_SHAREDFILES = 10;
my $MAX_SERIAL_PORTS = 4;
my $MAX_PARALLEL_PORTS = 3;
my $MAX_NUMA = 8;
@@ -974,6 +1001,12 @@ my $netdesc = {
description => "Specify network devices.",
};
+my $sharedfilesdesc = {
+ optional => 1,
+ type => 'string', format => $sharedfiles_fmt,
+ description => "share files between host and guest",
+};
+
PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
my $ipconfig_fmt = {
@@ -1035,6 +1068,10 @@ for (my $i = 0; $i < $MAX_NETS; $i++) {
$confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
}
+for (my $i = 0; $i < $MAX_SHAREDFILES; $i++) {
+ $confdesc->{"sharedfiles$i"} = $sharedfilesdesc;
+}
+
foreach my $key (keys %$confdesc_cloudinit) {
$confdesc->{$key} = $confdesc_cloudinit->{$key};
}
@@ -1988,6 +2025,16 @@ sub parse_net {
return $res;
}
+sub parse_sharedfiles {
+ my ($value) = @_;
+
+ return if !$value;
+ my $res = eval { parse_property_string($sharedfiles_fmt, $value) };
+
+ warn $@ if $@;
+ return $res;
+}
+
# ipconfigX ip=cidr,gw=ip,ip6=cidr,gw6=ip
sub parse_ipconfig {
my ($data) = @_;
@@ -4105,6 +4152,44 @@ sub config_to_command {
push @$devices, '-device', $netdevicefull;
}
+ my $onevirtiofs = 0;
+ for (my $i = 0; $i < $MAX_SHAREDFILES; $i++) {
+ my $sharedfilesstr = "sharedfiles$i";
+
+ next if !$conf->{$sharedfilesstr};
+ my $sharedfiles = parse_sharedfiles($conf->{$sharedfilesstr});
+ next if !$sharedfiles;
+
+ die $sharedfilesstr.' needs a type (virtio-9p or virtio-fs)' if !$sharedfiles->{type};
+ die $sharedfilesstr.' needs a dirid of a directory to share' if !$sharedfiles->{dirid};
+ die $sharedfilesstr.' needs a mount tag' if !$sharedfiles->{tag};
+
+ if ($sharedfiles->{type} eq 'virtio-fs' && $conf->{numa}) {
+ die "disable numa to use virtio-fs or use virtio-9p instead";
+ }
+
+ my $path = extract_dir_path($nodename, $sharedfiles->{dirid});
+
+ if ($sharedfiles->{type} eq 'virtio-9p') {
+ push @$devices, '-fsdev', "local,security_model=passthrough,id=fsdev$i"
+ .",path=$path";
+ push @$devices, '-device', "virtio-9p-pci,id=fs9p$i,fsdev=fsdev$i"
+ .",mount_tag=$sharedfiles->{tag}";
+ }
+ if ($sharedfiles->{type} eq 'virtio-fs') {
+ push @$devices, '-chardev', "socket,id=virtfs$i,path=/var/run/virtiofsd/vm$vmid-fs$i";
+ push @$devices, '-device', 'vhost-user-fs-pci,queue-size=1024'
+ .",chardev=virtfs$i,tag=$sharedfiles->{tag}";
+ $onevirtiofs = 1;
+ }
+ }
+
+ if ($onevirtiofs) {
+ push @$devices, '-object', 'memory-backend-file,id=mem'
+ .",size=$conf->{memory}M,mem-path=/dev/shm,share=on";
+ push @$devices, '-numa', 'node,memdev=mem';
+ }
+
if ($conf->{ivshmem}) {
my $ivshmem = parse_property_string($ivshmem_fmt, $conf->{ivshmem});
@@ -4190,6 +4275,57 @@ sub config_to_command {
return wantarray ? ($cmd, $vollist, $spice_port) : $cmd;
}
+sub extract_dir_path {
+ my ($nodename, $dirid) = @_;
+ my $dir_config = PVE::DirConfig::load_config($nodename);
+ my $path = $dir_config->{$dirid};
+ if (!$path) {
+ die "Directory ID $dirid does not exist\n";
+ }
+ if (! -e $path) {
+ die "Path $path does not exist\n";
+ }
+ if ((-e $path) && (! -d $path)) {
+ die "Path $path exists but is not a directory\n"
+ }
+ return $path;
+}
+
+sub start_virtiofs {
+ my ($vmid, $path, $fsid, $dirid) = @_;
+
+ my $socket_path_root = "/var/run/virtiofsd";
+ mkdir $socket_path_root;
+ my $socket_path = "$socket_path_root/vm$vmid-fs$fsid";
+ unlink($socket_path);
+ my $socket = IO::Socket::UNIX->new(
+ Type => SOCK_STREAM,
+ Local => $socket_path,
+ Listen => 1,
+ ) or die "cannot create socket - $!\n";
+
+ my $flags = fcntl($socket, F_GETFD, 0)
+ or die "failed to get file descriptor flags: $!\n";
+ fcntl($socket, F_SETFD, $flags & ~FD_CLOEXEC)
+ or die "failed to remove FD_CLOEXEC from file descriptor\n";
+
+ my $fd = $socket->fileno();
+
+ my $pid = fork();
+ if ($pid == 0) {
+ # TODO replace with rust implementation in bookworm
+ run_command(['/usr/lib/kvm/virtiofsd', "--fd=$fd",
+ '-o', "source=$path", '-o', 'cache=always']);
+ POSIX::_exit(0);
+ } elsif (!defined($pid)) {
+ die "could not fork to start virtiofsd";
+ }
+
+ # return socket to keep it alive,
+ # so that qemu will wait for virtiofsd to start
+ return $socket;
+}
+
sub check_rng_source {
my ($source) = @_;
@@ -5747,6 +5883,23 @@ sub vm_start_nolock {
my ($cmd, $vollist, $spice_port) = config_to_command($storecfg, $vmid,
$conf, $defaults, $forcemachine, $forcecpu, $params->{'pbs-backing'});
+ my $nodename = nodename();
+ my @sockets;
+ for (my $i = 0; $i < $MAX_SHAREDFILES; $i++) {
+ my $sharedfilesstr = "sharedfiles$i";
+
+ next if !$conf->{$sharedfilesstr};
+ my $sharedfiles = parse_sharedfiles($conf->{$sharedfilesstr});
+ next if !$sharedfiles;
+
+ my $path = extract_dir_path($nodename, $sharedfiles->{dirid});
+
+ if ($sharedfiles && $sharedfiles->{type} eq 'virtio-fs' && !$conf->{numa}) {
+ my $socket = start_virtiofs($vmid, $path, $i, $sharedfiles->{dirid});
+ push @sockets, $socket;
+ }
+ }
+
my $migration_ip;
my $get_migration_ip = sub {
my ($nodename) = @_;
@@ -6094,6 +6247,11 @@ sub vm_start_nolock {
PVE::GuestHelpers::exec_hookscript($conf, $vmid, 'post-start');
+ foreach my $socket (@sockets) {
+ shutdown($socket, 2);
+ close($socket);
+ }
+
return $res;
}
--
2.30.2
^ permalink raw reply [flat|nested] 7+ messages in thread