From: "Max R. Carrara" <m.carrara@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [RFC pve-storage, pve-manager master v1 00/12] GUI Support for Custom Storage Plugins
Date: Mon, 8 Sep 2025 20:00:44 +0200 [thread overview]
Message-ID: <20250908180058.530119-1-m.carrara@proxmox.com> (raw)
GUI Support for Custom Storage Plugins
======================================
tl;dr:
Add an API method to PVE::Storage::Plugin that returns the definition
for the form view of custom storage plugins. This definition is used by
the frontend to build the form view for creating / editing the storage
config entry of the plugin. The ultimate goal here is that custom
storage plugin devs don't have to (and also *must not*) ever touch
JavaScript to make their plugins show up in the GUI.
Overview
--------
This RFC implements GUI support for custom storage plugins.
To achieve this, four new paths are added to the API:
- plugins/storage
Returns the metadata of all plugins.
Plugin metadata includes supported content types and formats (as well
as their defaults), the plugin's short name, the views a plugin
defines, and more.
- plugins/storage/{plugin}
Returns the metadata of a single plugin.
- plugins/storage/{plugin}/views
Returns a list of all view declarations of a plugin. If the plugin
defines no views, the list is empty.
- plugins/storage/{plugin}/views/form
Returns the form view definition of a plugin.
How Custom Views Work
---------------------
A view is what defines how data should be displayed to users. Views are
specified via a nested hash in Perl and converted to JSON via the API.
The view definition inside the JSON object is then taken by the frontend
and built into a view for the user.
In particular, this RFC adds support for custom form views for storage
plugin config entries (Datacenter > Storage). A plugin may define a form
view by implementing the new `get_form_view()` plugin API method and
specifying in `plugindata()` that it has declared such a view.
Additionally, the JSON schema for form views is versioned to make
forward- and backward-compat easier.
The form view currently only allows customization of the "General" tab.
However, if a config entry has the "backup" content type selected, the
"Backup Retention" tab becomes unmasked, just like with inbuilt plugins.
The JSON schema for the form view mainly consists of the columns of the
"General" tab and the fields those columns may include.
The supported columns reflect those that are currently used in our
frontend:
- A list of "regular" columns
- A "bottom" column (the wide column below the regular ones)
- Columns in the advanced subsection
- A "bottom" column in the advanced subsection
Every column may contain a list of fields. Fields are typed and
correspond to a SectionConfig property of the storage plugin returning
the view. This means that defining the form view is enough, no
additional API methods need to be implemented otherwise. Ext.JS will
then use the property names in the field definitions for the regular
storage API calls.
This means that custom storage plugin authors don't have to write a
single line of JavaScript when implementing GUI support for their
plugin.
The currently supported field types in the form view schema are:
- boolean
- integer
- number
- string
- selection
Fields have common attributes as well as attributes specific to its
particular type. For example, the 'string' field may have the additional
'display-mode' attribute, with which the field may be displayed as
regular text field (the default), as text area, or as a password field.
How these field definitions are interpreted depends on the frontend.
In this RFC, the properties of the corresponding Ext.JS field are
determined and stitched together dynamically.
The four fields for the storage ID, nodes, content types and enable /
disable checkbox are always added by default and cannot be declared in
the form view.
Example Implementation
----------------------
To show the custom form view in action, the whole thing described here
is implemented for the ZFS pool plugin. You should notice only minor
differences from the original form view.
Current Limitations
-------------------
- The "default text" is currently not set. Didn't want to give in to the
ever-lingering feature creep surrounding this RFC.
- The same probably goes for other minor particularities that Ext.JS
supports. If the reader has any additional ideas, please send them
my way.
- There is no support for cluster setups yet.
- This is only *really* an issue for node-local storages. The example
implementation for the ZFS pool storage in this RFC works for single
node setups, but there's no node selector or anything of the sort
for custom selections. Would highly appreciate any ideas in that
regard, as we might have to deviate from the "standard look" that
our storage config forms currently have when it comes to that.
- For some reason the checkbox for the advanced section doesn't show up
even if fields exist inside its columns.
- The fields still show up as expected, it's just that they can't be
hidden with the "Advanced [ ]" checkbox.
- No idea why that happens. Would appreciate any Ext.JS lore / help in
that regard.
- Docstrings for the new stuff will be added once this becomes a proper
series.
Further Ideas
-------------
- While this only aims to implement GUI support for custom storage
plugins, there's nothing that's really stopping us from using this for
our own plugins once all the rough edges have been smoothed out.
- The only thing that might hinder us from *fully* switching over to
declaring our inbuilt plugins' form views according to this series
is the fact that some plugins define custom dialogues and such.
- E.g. the PBS plugin has a custom "Encryption" tab with whole
dialogues for auto-generating / uploading encryption keys.
- As of right now, the already existing field types in Ext.JS
(meaning 'xtypes' here) are used.
- What we could do is add custom field types in Ext.JS that correspond
to the five types that the form view schema allows, in order to
provide a more uniform way of building the fields and columns in
Ext.JS. Right now the Ext.JS fields are just made up on the spot,
which is a bit convoluted.
- Not sure if this is strictly necessary though, but might be nice to
have.
- This whole concept in the RFC can theoretically be generalized so that
it may be used throughout other places in PVE as well.
- The JSON schemas for columns and fields in particular technically
aren't really limited to storage plugin stuff.
- It might therefore be beneficial overall to pull the smaller pieces
out and define them in a separate module (debian package) so that
the rest of the backend can also benefit from this.
- I don't know of any other use cases as of right now though, which
is why I confined the schemas to PVE::Storage::Plugin::Views at the
moment.
- If we do want to generalize field / column / row / etc. schemas
eventually, we can just add a new schema version for the storage
plugin form view that uses the altered schemas when that happens.
:^)
Closing Thoughts
----------------
If you read this far, thanks a lot for your attention. 🙏 I hope I
haven't missed anything. I'd appreciate any feedback.
Also, thanks a lot to Aaron L. for brainstorming this through with me in
the beginning!
Summary of Changes
------------------
pve-storage:
Max R. Carrara (8):
plugin: meta: add package PVE::Storage::Plugin::Meta
api: Add 'plugins/storage' and 'plugins/storage/{plugin}' paths
plugin: meta: introduce 'short-name'
plugin: views: add package PVE::Storage::Plugin::Views
plugin: add new plugin API method `get_form_view()`
plugin: meta: add metadata regarding views in API
api: views: add paths regarding storage plugin views
plugin: zfspool: add 'short-name' and form view for ZFS pool plugin
src/PVE/API2/Makefile | 1 +
src/PVE/API2/Plugins/Makefile | 18 ++
src/PVE/API2/Plugins/Storage/Config.pm | 188 +++++++++++++++++++
src/PVE/API2/Plugins/Storage/Makefile | 18 ++
src/PVE/API2/Plugins/Storage/Views.pm | 172 ++++++++++++++++++
src/PVE/Storage/Makefile | 1 +
src/PVE/Storage/Plugin.pm | 8 +
src/PVE/Storage/Plugin/Makefile | 11 ++
src/PVE/Storage/Plugin/Meta.pm | 211 +++++++++++++++++++++
src/PVE/Storage/Plugin/Views.pm | 242 +++++++++++++++++++++++++
src/PVE/Storage/ZFSPoolPlugin.pm | 67 +++++++
11 files changed, 937 insertions(+)
create mode 100644 src/PVE/API2/Plugins/Makefile
create mode 100644 src/PVE/API2/Plugins/Storage/Config.pm
create mode 100644 src/PVE/API2/Plugins/Storage/Makefile
create mode 100644 src/PVE/API2/Plugins/Storage/Views.pm
create mode 100644 src/PVE/Storage/Plugin/Makefile
create mode 100644 src/PVE/Storage/Plugin/Meta.pm
create mode 100644 src/PVE/Storage/Plugin/Views.pm
pve-manager:
Max R. Carrara (4):
api: handle path 'plugins/storage' through its package
ui: storage: add CustomBase.js
ui: storage: support custom storage plugins in Datacenter > Storage
ui: storage: use `Ext.Msg.alert()` instead of throwing an exception
PVE/API2.pm | 6 +
www/manager6/Makefile | 1 +
www/manager6/dc/StorageView.js | 137 ++++++++--
www/manager6/storage/CustomBase.js | 402 +++++++++++++++++++++++++++++
4 files changed, 524 insertions(+), 22 deletions(-)
create mode 100644 www/manager6/storage/CustomBase.js
--
2.47.2
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next reply other threads:[~2025-09-08 18:01 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-08 18:00 Max R. Carrara [this message]
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 01/12] plugin: meta: add package PVE::Storage::Plugin::Meta Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 02/12] api: Add 'plugins/storage' and 'plugins/storage/{plugin}' paths Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 03/12] plugin: meta: introduce 'short-name' Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 04/12] plugin: views: add package PVE::Storage::Plugin::Views Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 05/12] plugin: add new plugin API method `get_form_view()` Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 06/12] plugin: meta: add metadata regarding views in API Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 07/12] api: views: add paths regarding storage plugin views Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-storage master v1 08/12] plugin: zfspool: add 'short-name' and form view for ZFS pool plugin Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-manager master v1 09/12] api: handle path 'plugins/storage' through its package Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-manager master v1 10/12] ui: storage: add CustomBase.js Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-manager master v1 11/12] ui: storage: support custom storage plugins in Datacenter > Storage Max R. Carrara
2025-09-08 18:00 ` [pve-devel] [RFC pve-manager master v1 12/12] ui: storage: use `Ext.Msg.alert()` instead of throwing an exception Max R. Carrara
2025-09-08 19:23 ` [pve-devel] [RFC pve-storage, pve-manager master v1 00/12] GUI Support for Custom Storage Plugins Thomas Lamprecht
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250908180058.530119-1-m.carrara@proxmox.com \
--to=m.carrara@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox