[pve-devel] [RFC pve-storage master v1 07/12] api: views: add paths regarding storage plugin views
Max R. Carrara
m.carrara at proxmox.com
Mon Sep 8 20:00:51 CEST 2025
This commit adds the following paths:
- 'plugins/storage/{plugin}/views': Returns an array of objects
describing the views that a plugin currently supports. If no views
are supported, the array is empty.
- 'plugins/storage/{plugin}/views/form': Returns the form view
definition of the given plugin.
Like in an earlier commit, the form view is always validated against
its JSON schema after it was fetched. This is rather suboptimal at the
moment and will be done within tests in the future. Right now this is
left in so as to keep the RFC smaller.
Signed-off-by: Max R. Carrara <m.carrara at proxmox.com>
---
src/PVE/API2/Plugins/Storage/Config.pm | 7 +
src/PVE/API2/Plugins/Storage/Makefile | 1 +
src/PVE/API2/Plugins/Storage/Views.pm | 172 +++++++++++++++++++++++++
3 files changed, 180 insertions(+)
create mode 100644 src/PVE/API2/Plugins/Storage/Views.pm
diff --git a/src/PVE/API2/Plugins/Storage/Config.pm b/src/PVE/API2/Plugins/Storage/Config.pm
index 60c0515..3f57784 100644
--- a/src/PVE/API2/Plugins/Storage/Config.pm
+++ b/src/PVE/API2/Plugins/Storage/Config.pm
@@ -18,9 +18,16 @@ use PVE::Storage::Plugin::Meta qw(
);
use PVE::Tools qw(extract_param);
+use PVE::API2::Plugins::Storage::Views;
+
use PVE::RESTHandler;
use base qw(PVE::RESTHandler);
+__PACKAGE__->register_method({
+ subclass => 'PVE::API2::Plugins::Storage::Views',
+ path => '{plugin}/views',
+});
+
my $PLUGIN_METADATA_SCHEMA = {
type => 'object',
properties => {
diff --git a/src/PVE/API2/Plugins/Storage/Makefile b/src/PVE/API2/Plugins/Storage/Makefile
index 73875cf..83fab2e 100644
--- a/src/PVE/API2/Plugins/Storage/Makefile
+++ b/src/PVE/API2/Plugins/Storage/Makefile
@@ -1,4 +1,5 @@
SOURCES = Config.pm \
+ Views.pm \
SUBDIRS =
diff --git a/src/PVE/API2/Plugins/Storage/Views.pm b/src/PVE/API2/Plugins/Storage/Views.pm
new file mode 100644
index 0000000..7419fb5
--- /dev/null
+++ b/src/PVE/API2/Plugins/Storage/Views.pm
@@ -0,0 +1,172 @@
+package PVE::API2::Plugins::Storage::Views;
+
+use v5.36;
+
+use List::Util qw(any);
+
+use HTTP::Status qw(:constants);
+
+use PVE::Exception qw(raise);
+use PVE::Storage;
+use PVE::Storage::Plugin;
+use PVE::Storage::Plugin::Meta qw(
+ plugin_view_types
+ plugin_view_modes
+ get_plugin_metadata
+);
+use PVE::Storage::Plugin::Views qw(
+ get_form_view_schema
+);
+use PVE::Tools qw(extract_param);
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+# plugins/storage/{plugin}/views
+
+__PACKAGE__->register_method({
+ name => 'index',
+ path => '',
+ method => 'GET',
+ description => "Return available views for a plugin.",
+ permissions => {
+ # TODO: perms
+ description => "",
+ user => 'all',
+ },
+ parameters => {
+ additionalProperties => 0,
+ },
+ # NOTE: Intentionally returning an array of objects here for forward compat
+ returns => {
+ type => 'array',
+ items => {
+ type => 'object',
+ properties => {
+ 'view-type' => {
+ type => 'string',
+ enum => plugin_view_types(),
+ optional => 0,
+ },
+ },
+ },
+ },
+ code => sub($param) {
+ my $param_type = extract_param($param, 'plugin');
+
+ my $metadata = get_plugin_metadata($param_type)
+ or raise("Plugin '$param_type' not found", code => HTTP_NOT_FOUND);
+
+ my $result = [];
+
+ for my $view_type ($metadata->{views}->@*) {
+ my $view_spec = {
+ 'view-type' => $view_type,
+ };
+
+ push($result->@*, $view_spec);
+ }
+
+ return $result;
+ },
+});
+
+# plugins/storage/{plugin}/views/form
+
+__PACKAGE__->register_method({
+ name => 'form',
+ path => 'form',
+ method => 'GET',
+ description => "Return a plugin's form view.",
+ permissions => {
+ # TODO: perms
+ description => "",
+ user => 'all',
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ plugin => {
+ type => 'string',
+ optional => 0,
+ },
+ mode => {
+ description => "The mode for which to return the view."
+ . " Can be either 'create' or 'update', depending on whether"
+ . " the storage is being created or updated (edited).",
+ type => 'string',
+ enum => plugin_view_modes(),
+ optional => 1,
+ default => 'create',
+ },
+ },
+ },
+ returns => get_form_view_schema(),
+ code => sub($param) {
+ my $param_type = extract_param($param, 'plugin');
+ my $param_mode = extract_param($param, 'mode') // 'create';
+
+ my $metadata = get_plugin_metadata($param_type)
+ or raise("Plugin '$param_type' not found", code => HTTP_NOT_FOUND);
+
+ my $views = $metadata->{views} // [];
+ raise("Plugin '$param_type' defines no views", code => HTTP_BAD_REQUEST)
+ if !scalar($views->@*);
+
+ my $has_form_view = any { $_ eq 'form' } $views->@*;
+
+ raise("Plugin '$param_type' has no form view", code => HTTP_BAD_REQUEST)
+ if !$has_form_view;
+
+ my $plugin = PVE::Storage::Plugin->lookup($param_type);
+
+ my $context = {
+ mode => $param_mode,
+ };
+
+ my $view = eval { $plugin->get_form_view($context) };
+ if (my $err = $@) {
+ raise(
+ "Error while fetching form view for plugin '$param_type': $err",
+ code => HTTP_INTERNAL_SERVER_ERROR,
+ );
+ }
+
+ if (!defined($view)) {
+ raise(
+ "Form view for plugin '$param_type' is undefined",
+ code => HTTP_INTERNAL_SERVER_ERROR,
+ );
+ }
+
+ # TODO: run in tests instead?
+ # --> test with different contexts (mode only right now)
+ eval { PVE::JSONSchema::validate($view, get_form_view_schema()); };
+ if (my $err = $@) {
+ # NOTE: left in only for debugging purposes at the moment
+ require Data::Dumper;
+
+ local $Data::Dumper::Terse = 1;
+ local $Data::Dumper::Indent = 1;
+ local $Data::Dumper::Useqq = 1;
+ local $Data::Dumper::Deparse = 1;
+ local $Data::Dumper::Quotekeys = 0;
+ local $Data::Dumper::Sortkeys = 1;
+ local $Data::Dumper::Trailingcomma = 1;
+
+ warn "Failed to validate form view of plugin '$param_type':\n$err\n";
+ warn '$context = ' . Dumper($context) . "\n";
+ warn '$view = ' . Dumper($view) . "\n";
+
+ raise(
+ "Failed to validate form view of plugin '$param_type':\n$err\n",
+ code => HTTP_INTERNAL_SERVER_ERROR,
+ );
+ }
+
+ return $view;
+ },
+});
+
+1;
--
2.47.2
More information about the pve-devel
mailing list