[pve-devel] [RFC pve-storage master v1 01/12] plugin: meta: add package PVE::Storage::Plugin::Meta

Max R. Carrara m.carrara at proxmox.com
Mon Sep 8 20:00:45 CEST 2025


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 <m.carrara at proxmox.com>
---
 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





More information about the pve-devel mailing list