From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 456AE98899 for ; Tue, 25 Apr 2023 12:22:25 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 2609D3A664 for ; Tue, 25 Apr 2023 12:21:55 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Tue, 25 Apr 2023 12:21:54 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B33A745438 for ; Tue, 25 Apr 2023 12:21:53 +0200 (CEST) From: Markus Frank To: pve-devel@lists.proxmox.com Date: Tue, 25 Apr 2023 12:21:33 +0200 Message-Id: <20230425102136.85334-4-m.frank@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20230425102136.85334-1-m.frank@proxmox.com> References: <20230425102136.85334-1-m.frank@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.055 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record T_SCC_BODY_TEXT_LINE -0.01 - Subject: [pve-devel] [PATCH manager v4 3/6] added Config for Shared Filesystem Directories X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 25 Apr 2023 10:22:25 -0000 and made an API Endpoint for getting, adding and removing directories to the config. Signed-off-by: Markus Frank --- PVE/API2/DirConfig.pm | 129 +++++++++++++++++++++++++++++++++++ PVE/API2/Makefile | 1 + PVE/API2/Nodes.pm | 6 ++ PVE/DirConfig.pm | 155 ++++++++++++++++++++++++++++++++++++++++++ PVE/Makefile | 1 + 5 files changed, 292 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..56796029 --- /dev/null +++ b/PVE/DirConfig.pm @@ -0,0 +1,155 @@ +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; +} + +sub extract_dir_path { + my ($nodename, $dirid) = @_; + my $dir_config = 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; +} + +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