From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id C02051FF187 for ; Mon, 8 Sep 2025 20:01:03 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C8EA61881A; Mon, 8 Sep 2025 20:01:04 +0200 (CEST) From: "Max R. Carrara" To: pve-devel@lists.proxmox.com Date: Mon, 8 Sep 2025 20:00:45 +0200 Message-ID: <20250908180058.530119-2-m.carrara@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250908180058.530119-1-m.carrara@proxmox.com> References: <20250908180058.530119-1-m.carrara@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1757354438145 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.087 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 Subject: [pve-devel] [RFC pve-storage master v1 01/12] plugin: meta: add package PVE::Storage::Plugin::Meta 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: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" This package is used to retrieve general metadata about plugins. Add this package in order to keep code concerning the retrieval of storage plugin metadata in one place instead of mixing the code into `PVE::Storage` and `PVE::Storage::Plugin`. At the moment, plugin metadata includes the plugin's kind (inbuilt or custom), its supported content types and formats, and what properties it declares as sensitive. Since plugin metadata (such as the returned hash by the `plugindata()` method, for example) is static, cache the metadata of all plugins after the first call to either `get_plugin_metadata()` or `get_plugin_metadata_all()`. The public subroutines (deep-)copy their returned data to prevent any accidental modification, as hashrefs aren't supported by the 'use constant' Perl core pragma. This isn't the most optimal way to do this; as a potential alternative `Readonly` [0] could be used instead, but I didn't want to pull in another dependency at the moment. [0]: https://metacpan.org/pod/Readonly Signed-off-by: Max R. Carrara --- src/PVE/Storage/Makefile | 1 + src/PVE/Storage/Plugin/Makefile | 10 ++ src/PVE/Storage/Plugin/Meta.pm | 168 ++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) create mode 100644 src/PVE/Storage/Plugin/Makefile create mode 100644 src/PVE/Storage/Plugin/Meta.pm diff --git a/src/PVE/Storage/Makefile b/src/PVE/Storage/Makefile index a67dc25..ca687b6 100644 --- a/src/PVE/Storage/Makefile +++ b/src/PVE/Storage/Makefile @@ -19,5 +19,6 @@ SOURCES= \ .PHONY: install install: make -C Common install + make -C Plugin install for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/Storage/$$i; done make -C LunCmd install diff --git a/src/PVE/Storage/Plugin/Makefile b/src/PVE/Storage/Plugin/Makefile new file mode 100644 index 0000000..ca82517 --- /dev/null +++ b/src/PVE/Storage/Plugin/Makefile @@ -0,0 +1,10 @@ +SOURCES = Meta.pm \ + + +INSTALL_PATH = ${DESTDIR}${PERLDIR}/PVE/Storage/Plugin + +.PHONY: install +install: + set -e && for SOURCE in ${SOURCES}; \ + do install -D -m 0644 $$SOURCE ${INSTALL_PATH}/$$SOURCE; \ + done diff --git a/src/PVE/Storage/Plugin/Meta.pm b/src/PVE/Storage/Plugin/Meta.pm new file mode 100644 index 0000000..6d0cb51 --- /dev/null +++ b/src/PVE/Storage/Plugin/Meta.pm @@ -0,0 +1,168 @@ +package PVE::Storage::Plugin::Meta; + +use v5.36; + +use Carp qw(croak confess); +use Storable qw(dclone); + +use PVE::Storage; +use PVE::Storage::Plugin; + +use Exporter qw(import); + +our @EXPORT_OK = qw( + plugin_kinds + plugin_content_types + plugin_formats + get_plugin_metadata + get_plugin_metadata_all +); + +=head1 NAME + +PVE::Storage::Plugin::Meta - Retrieving Storage Plugin Metadata + +=head1 DESCRIPTION + +=for comment +TODO + +=cut + +my $PLUGIN_KINDS = [ + 'builtin', 'custom', +]; + +# Note: 'none' isn't included here since it's an internal marker content type. +my $PLUGIN_CONTENT_TYPES = [ + 'images', 'rootdir', 'vztmpl', 'iso', 'backup', 'snippets', 'import', +]; + +my $PLUGIN_FORMATS = [ + 'raw', 'qcow2', 'vmdk', 'subvol', +]; + +my $DEFAULT_PLUGIN_FORMAT = 'raw'; + +sub plugin_kinds() { + return [$PLUGIN_KINDS->@*]; +} + +sub plugin_content_types() { + return [$PLUGIN_CONTENT_TYPES->@*]; +} + +sub plugin_formats() { + return [$PLUGIN_FORMATS->@*]; +} + +my $plugin_metadata = undef; + +my sub assemble_plugin_metadata_content($plugin) { + confess '$plugin is undef' if !defined($plugin); + + my $content_metadata = { + supported => [], + default => [], + }; + + my $plugindata = $plugin->plugindata(); + + return $content_metadata if !defined($plugindata->{content}); + + my $supported = $plugindata->{content}->[0]; + my $default = $plugindata->{content}->[1]; + + for my $content_type ($PLUGIN_CONTENT_TYPES->@*) { + if (defined($supported->{$content_type})) { + push($content_metadata->{supported}->@*, $content_type); + } + + if (defined($default->{$content_type})) { + push($content_metadata->{default}->@*, $content_type); + } + } + + return $content_metadata; +} + +my sub assemble_plugin_metadata_format($plugin) { + confess '$plugin is undef' if !defined($plugin); + + my $plugindata = $plugin->plugindata(); + + if (!defined($plugindata->{format})) { + return { + supported => [$DEFAULT_PLUGIN_FORMAT], + default => $DEFAULT_PLUGIN_FORMAT, + }; + } + + my $format_metadata = { + supported => [], + default => $plugindata->{format}->[1], + }; + + my $supported = $plugindata->{format}->[0]; + + for my $format ($PLUGIN_FORMATS->@*) { + if (defined($supported->{$format})) { + push($format_metadata->{supported}->@*, $format); + } + } + + return $format_metadata; +} + +my sub assemble_plugin_metadata() { + return if defined($plugin_metadata); + + $plugin_metadata = {}; + my $all_types = PVE::Storage::Plugin->lookup_types(); + + for my $type ($all_types->@*) { + my $plugin = PVE::Storage::Plugin->lookup($type); + + $plugin = "$plugin"; + + my $kind = 'builtin'; + $kind = 'custom' if $plugin =~ m/^PVE::Storage::Custom::/; + + my $metadata = { + type => $type, + module => $plugin, + kind => $kind, + }; + + $metadata->{content} = assemble_plugin_metadata_content($plugin); + $metadata->{format} = assemble_plugin_metadata_format($plugin); + + my $sensitive_properties = $plugin->plugindata()->{'sensitive-properties'} // {}; + + $metadata->{'sensitive-properties'} = + [grep { $sensitive_properties->{$_} } sort keys $sensitive_properties->%*]; + + $plugin_metadata->{$type} = $metadata; + } + + return; +} + +sub get_plugin_metadata { + my ($plugin_type) = @_; + + croak "\$plugin_type is undef" if !defined($plugin_type); + + assemble_plugin_metadata() if !defined($plugin_metadata); + + return dclone($plugin_metadata->{$plugin_type}) if exists($plugin_metadata->{$plugin_type}); + return undef; +} + +sub get_plugin_metadata_all { + assemble_plugin_metadata() if !defined($plugin_metadata); + + return dclone($plugin_metadata); +} + +1; -- 2.47.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel