From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <pve-devel-bounces@lists.proxmox.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68])
	by lore.proxmox.com (Postfix) with ESMTPS id 973D61FF164
	for <inbox@lore.proxmox.com>; Fri, 11 Apr 2025 15:49:18 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id 6CB7E1B7E6;
	Fri, 11 Apr 2025 15:49:11 +0200 (CEST)
Date: Fri, 11 Apr 2025 15:49:06 +0200
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: Fabian =?utf-8?Q?Gr=C3=BCnbichler?= <f.gruenbichler@proxmox.com>
Message-ID: <wkzj4l7kxucljczf4342mlazkf2wlbzculbs4q6g4mtjsvrs6f@qujn7bv6vp4a>
References: <20250326142059.261938-1-m.carrara@proxmox.com>
 <20250326142059.261938-4-m.carrara@proxmox.com>
 <1743423551.a0519q1x3m.astroid@yuna.none>
MIME-Version: 1.0
Content-Disposition: inline
In-Reply-To: <1743423551.a0519q1x3m.astroid@yuna.none>
X-SPAM-LEVEL: Spam detection results:  0
 AWL 0.081 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: Re: [pve-devel] [PATCH v1 pve-storage 3/8] pluginbase: document
 SectionConfig methods
X-BeenThere: pve-devel@lists.proxmox.com
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/>
List-Post: <mailto:pve-devel@lists.proxmox.com>
List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help>
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe>
Reply-To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Cc: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Errors-To: pve-devel-bounces@lists.proxmox.com
Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com>

On Mon, Mar 31, 2025 at 05:13:10PM +0200, Fabian Gr=FCnbichler wrote:
> On March 26, 2025 3:20 pm, Max Carrara wrote:
> > This commit adds docstrings for the relevant PVE::SectionConfig
> > methods in the context of the storage plugin API.
> > =

> > Signed-off-by: Max Carrara <m.carrara@proxmox.com>
> > ---
> >  src/PVE/Storage/PluginBase.pm | 194 +++++++++++++++++++++++++++++++++-
> >  1 file changed, 192 insertions(+), 2 deletions(-)
> > =

> > diff --git a/src/PVE/Storage/PluginBase.pm b/src/PVE/Storage/PluginBase=
.pm
> > index 16977f3..5f7e6fd 100644
> > --- a/src/PVE/Storage/PluginBase.pm
> > +++ b/src/PVE/Storage/PluginBase.pm
> > @@ -88,7 +88,7 @@ package PVE::Storage::PluginBase;
> >  use strict;
> >  use warnings;
> >  =

> > -use Carp qw(croak);
> > +use Carp qw(croak confess);
> >  =

> >  use parent qw(PVE::SectionConfig);
> >  =

> > @@ -100,27 +100,217 @@ use parent qw(PVE::SectionConfig);
> >  =

> >  =3Dcut
> >  =

> > +=3Dhead3 $plugin->type()
> > +
> > +B<REQUIRED:> Must be implemented in every storage plugin.
> =

> I am not sure whether we should do
> =

> s/implemented in/implemented by
> =

> for this whole thing and SectionConfig, but I won't note it for every
> instance ;)
> =

> > +
> > +This should return a string with which the plugin can be uniquely iden=
tified.
> =

> a string which uniquely identifies the plugin.
> =

> > +Any string is acceptable, as long as it's descriptive and you're sure =
it won't
> > +conflict with another plugin. In the cases of built-in plugins, you wi=
ll often
> > +find the filesystem name or something similar being used.
> =

> I'd replace that with something like:
> =

> Commonly, the name of the storage technology or filesystem supported by
> the plugin is used. If it is necessary to differentiate multiple
> plugins for the same storage technology/filesystem, add a suffix
> denoting the supported variant or feature set.

While in perl we seem to parse this via `/\S+/` (greedily), I'd argue we
should request to limit this to `[a-z\-]`...

Technically, `PVE::SectionConfig->parse_section_header()` would parse
this line:

    My:Plugin"with-a-BAD:name

as a header of type `My:Plugin"with-a-BAD` and section id `name`...

We may want to fix that...

> =

> > +
> > +See C<L<PVE::SectionConfig/type>> for more information.
> > +
> > +=3Dcut
> > +
> >  sub type {
> >      croak "implement me in sub-class\n";
> >  }
> >  =

> > +=3Dhead3 $plugin->properties()
> > +
> > +B<OPTIONAL:> May be implemented in a storage plugin.
> > +
> > +This method should be implemented if there are additional properties t=
o be used
> > +by your plugin. Since properties are global and may be reused across p=
lugins,
> =

> s/used/registered
> =

> IMHO that conveys better what the difference between properties and
> options are..
> =

> "global and may be reused across plugins" is kinda superfluous, maybe
> =

> "Since properties share a global namespace across all plugins"?
> =

> > +the names of properties must not collide with one another.
> =

> each property can only be registered once by a single plugin, but used
> as an option by many.
> =

> > +
> > +When implementing a third-party plugin, it is recommended to prefix pr=
operties
> > +with some kind of identifier, like so:
> =

> Third-party plugins should therefore prefix any properties they register
> with their plugin type, for example:
> =

> > +
> > +    sub properties {
> > +	return {
> > +	    'example-storage-address' =3D> {
> > +		description =3D> 'Host address of the ExampleStorage to connect to.',
> > +		type =3D> 'string',
> > +	    },
> > +	    'example-storage-pool' =3D> {
> > +		description =3D> 'Name of the ExampleStorage pool to use.',
> > +		type =3D> 'string',
> > +	    },
> > +	    # [...]
> > +	};
> > +    }
> > +
> > +However, it is encouraged to reuse properties of inbuilt plugins whene=
ver
> > +possible. There are a few provided properties that are regarded as I<s=
ensitive>
> > +and will be treated differently in order to not expose them or write t=
hem as
> > +plain text into configuration files. One such property fit for externa=
l use is
> > +C<password>, which you can use to provide a password, API secret, or s=
imilar.
> =

> If possible, generic properties registered by built-in plugins should be
> reused.
> =

> (inbuilt is a very weird, very British word ;))
> =

> the rest of that is kinda misplaced here and should be its own section
> somewhere (it affects options just as much as properties). we should
> probably allow registering sensitive properties for plugins?
> =

> > +
> > +See C<L<PVE::SectionConfig/properties>> for more information.
> > +
> > +=3Dcut
> > +
> >  sub properties {
> >      my ($class) =3D @_;
> >      return $class->SUPER::properties();
> >  }
> >  =

> > +=3Dhead3 $plugin->options()
> > +
> > +B<REQUIRED:> Must be implemented in every storage plugin.
> > +
> > +This method returns a hash of the properties used by the plugin. Becau=
se
> > +properties are shared among plugins, it is recommended to reuse any ex=
isting
> > +ones of inbuilt plugins and only define custom properties via
> > +C<L<< properties()|/"$plugin->properties()" >>> if necessary.
> =

> Everything but the first sentence can be a reference to the properties
> part above..
> =

> > +
> > +The properties a plugin uses are then declared as follows:
> =

> or just, "For example:" ? ;)
> =

> > +
> > +    sub options {
> > +	return {
> > +	    nodes =3D> { optional =3D> 1 },
> > +	    content =3D> { optional =3D> 1 },
> > +	    disable =3D> { optional =3D> 1 },

^ Currently these 3 need to actually be listed by the plugin, but we
probably want to enforce that they exist?

If the storage doesn't list the `disable` flag you, well, cannot disable
it...

> > +	    'example-storage-pool' =3D> { fixed =3D> 1 },
> > +	    'example-storage-address' =3D> { fixed =3D> 1 },
> > +	    password =3D> { optional =3D> 1 },
> > +	};
> > +    }
> > +
> > +C<optional> properties are not required to be set. It is recommended t=
o set
> =

> s/set/make or "declare"?
> =

> > +most properties optional by default, unless it I<really> is required t=
o always
> > +exist.
> =

> s/it/they
> s/exist/be set
> =

> > +
> > +C<fixed> properties can only be set when creating a new storage via th=
e plugin
> > +and cannot be changed afterwards.
> =

> s/creating a new storage/adding a new storage config entry/ ? a bit more
> precise to differentiate it from actually "creating a storage" as in
> formatting a disk/..
> =

> I am not sure what "via the plugin" is supposed to mean there, I think
> it can just be dropped.
> =

> > +
> > +See C<L<PVE::SectionConfig/options>> for more information.
> > +
> > +=3Dcut
> > +
> >  sub options {
> >      my ($class) =3D @_;
> >      return $class->SUPER::options();
> >  }
> >  =

> > +=3Dhead3 $plugin->plugindata()
> > +
> > +B<REQUIRED:> Must be implemented in every storage plugin.
> > +
> > +This method returns a hash that specifies additional capabilities of t=
he storage
> > +plugin, such as what kinds of data may be stored on it or what VM disk=
 formats
> =

> s/data/content or s/data/volumes
> =

> > +the storage supports. Additionally, defaults may also be set. This is =
done
> > +through the C<content> and C<format> keys.
> =

> but how about re-organizing it a bit?
> =

> This method returns a hash declaring which content types and image
> formats this plugin (or the underlying storage) supports.
> =

> and then describe each key?
> =

> > +
> > +The C<content> key is used to declare B<supported content types> and t=
heir
> > +defaults, while the C<format> key declares B<supported disk formats> a=
nd the
> > +default disk format of the storage:
> =

> "and their defaults" sounds weird.
> =

> The 'content' key stores a list containing two hashes. The first list
> element is a hash declaring which content types are supported by the
> plugin. The second list element is a hash declaring which content types
> are used by default if no explicit 'content' option is set on a plugin
> instance.
> =

> and maybe link somewhere else for a description of the content type
> values, so that there is a single place that various parts that need it
> can link to?
> =

> and then
> =

> The 'format' key stores a list containing two hashes. The first list
> element is a hash declaring which image formats are supported by the
> storage plugin. The second list element is the default image format that
> should be used for images on the storage if non is explicitly provided.
> =

> > +
> > +    sub plugindata {
> > +	return {
> > +	    content =3D> [
> > +		# possible content types
> > +		{
> > +		    images =3D> 1,   # disk images
> > +		    rootdir =3D> 1,  # container root directories

Side note...
`rootdir` is one we may need to rethink in the future.

The actual volume type for a container is still actually an "image".
(pve-container uses `vdisk_alloc` to get a `raw` file and then runs
`mkfs` on it or uses a *subvol* type (zfs, btrfs, or dirplugin with
size=3D0)

> > +		    vztmpl =3D> 1,   # container templates
> > +		    iso =3D> 1,      # iso images
> > +		    backup =3D> 1,   # vzdump backup files
> > +		    snippets =3D> 1, # snippets
> > +		    import =3D> 1,   # imports; see explanation below
> > +		    none =3D> 1,     # no content; see explanation below
> > +		},
> > +		# defaults if 'content' isn't explicitly set
> > +		{
> > +		    images =3D> 1,   # store disk images by default
> > +		    rootdir =3D> 1   # store containers by default
> > +		    # possible to add more or have no defaults
> > +		}
> > +	    ],
> > +	    format =3D> [
> > +		# possible disk formats
> > +		{
> > +		    raw =3D> 1,   # raw disk image
> > +		    qcow2 =3D> 1, # QEMU image format
> > +		    vmdk =3D> 1,  # VMware image format
> > +		    subvol =3D> 1 # subvolumes; see explanation below
> > +		},
> > +		"qcow2" # default if 'format' isn't explicitly set
> > +	    ]
> > +	    # [...]
> > +	};
> > +    }
> > +
> > +While the example above depicts a rather capable storage, the following
> > +shows a simpler storage that can only be used for VM disks:
> =

> raw-formatted VM disks
> =

> > +
> > +    sub plugindata {
> > +	return {
> > +	    content =3D> [
> > +		{ images =3D> 1 },
> > +	    ],
> > +	    format =3D> [
> > +		{ raw =3D> 1 },
> > +		"raw",
> > +	    ]
> > +	};
> > +    }
> > +
> > +Which content types and formats are supported depends on the underlyin=
g storage
> > +implementation.
> > +
> > +B<Regarding C<import>:> The C<import> content type is used internally =
to
> > +interface with virtual guests of foreign sources or formats. The corre=
sponding
> > +functionality has not yet been published to the public parts of the st=
orage
> > +API. Third-party plugins therefore should not declare this content typ=
e.
> =

> this should live somewhere where content types are described
> =

> > +
> > +B<Regarding C<none>:> The C<none> content type denotes the I<absence> =
of other
> > +types of content, i.e. this content type may only be set on a storage =
if no
> > +other content type is set. This is used internally for storages that s=
upport
> > +adding another storage "on top" of them; at the moment, this makes it =
possible
> > +to set up an LVM (thin) pool on top of an iSCSI LUN. The corresponding
> > +functionality has not yet been published to the public parts of the st=
orage
> > +API. Third-party plugins therefore should not declare this content typ=
e.
> =

> same
> =

> > +
> > +B<Regarding C<subvol>:> The C<subvol> format is used internally to all=
ow the
> > +root directories of containers to use ZFS subvolumes (also known as
> > +I<ZFS datasets>, not to be confused with I<ZVOLs>). Third-party plugin=
s should
> > +not declare this format type.
> =

> this is incomplete, but same.

(btrfs subvolumes and size=3D0 dirs)

> =

> > +
> > +There is one more key, C<select_existing>, which is used internally for
> > +ISCSI-related GUI functionality. Third-party plugins should not declar=
e this
> > +key.
> =

> this is okay here :)
> =

> > +
> > +=3Dcut
> > +
> >  sub plugindata {
> >      my ($class) =3D @_;
> >      return $class->SUPER::plugindata();
> >  }
> >  =

> > +=3Dhead3 $plugin->private()
> > +
> > +B<WARNING:> This method is provided by C<L<PVE::Storage::Plugin>> and
> > +must be used as-is. It is merely documented here for informal purposes
> > +and B<must not be overridden.>
> > +
> > +Returns a hash containing the tracked plugin metadata, most notably the
> > +C<propertyList>, which contains all known properties of all plugins.
> > +
> > +C<L<PVE::Storage::Plugin>> uses this to predefine a lot of useful prop=
erties
> > +that are relevant for all plugins. Core functionality such as defining
> > +whether a storage is shared, which nodes may use it, whether a storage
> > +is enabled or not, etc. are implemented via these properties.
> =

> this is not true and sounds like it is very interesting to look at/mess
> with ;) in reality, it is part of SectionConfig machinery, so just say
> that and that it must not be overridden and be done with it?
> =

> if PluginBase becomes the real base plugin then it and some other
> helpers would need to move here anyway..
> =

> > +
> > +See C<L<PVE::SectionConfig/private>> for more information.
> > +
> > +=3Dcut
> > +
> >  sub private {
> > -    croak "implement me in sub-class\n";
> > +    confess "private() is provided by PVE::Storage::Plugin and must no=
t be overridden";
> >  }
> >  =

> >  =3Dhead2 GENERAL
> > -- =

> > 2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel