[pve-devel] [PATCH v1 pve-storage 7/8] pluginbase: document volume operations
Max Carrara
m.carrara at proxmox.com
Wed Mar 26 15:20:58 CET 2025
Add docstrings for the following methods:
- list_volumes
- get_volume_attribute
- update_volume_attribute
- volume_size_info
- volume_resize
- volume_snapshot
- volume_snapshot_info
- volume_rollback_is_possible
- volume_snapshot_rollback
- volume_snapshot_delete
- volume_snapshot_needs_fsfreeze
- storage_can_replicate
- volume_has_feature
- map_volume
- unmap_volume
- activate_volume
- deactivate_volume
- rename_volume
- prune_backups
Signed-off-by: Max Carrara <m.carrara at proxmox.com>
Co-authored-by: Maximiliano Sandoval <m.sandoval at proxmox.com>
---
src/PVE/Storage/PluginBase.pm | 401 ++++++++++++++++++++++++++++++++--
1 file changed, 388 insertions(+), 13 deletions(-)
diff --git a/src/PVE/Storage/PluginBase.pm b/src/PVE/Storage/PluginBase.pm
index 37b1471..a1bfc8d 100644
--- a/src/PVE/Storage/PluginBase.pm
+++ b/src/PVE/Storage/PluginBase.pm
@@ -861,108 +861,483 @@ sub free_image {
=cut
+=head3 $plugin->list_volumes($storeid, \%scfg, $vmid, \@content_types)
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Returns a list of volumes for the given guest whose content type is within the
+given C<\@content_types>. If C<\@content_types> is empty, no volumes will be
+returned. See C<L<< plugindata()|/"$plugin->plugindata()" >>> for all content types.
+
+ # List all backups for a guest
+ my $backups = $plugin->list_volumes($storeid, $scfg, $vmid, ['backup']);
+
+ # List all containers and virtual machines on the storage
+ my $guests = $plugin->list_volumes($storeid, $scfg, undef, ['rootdir', 'images'])
+
+The returned listref of hashrefs has the following structure:
+
+ [
+ {
+ content => "images",
+ ctime => "1702298038", # creation time as unix timestamp
+ format => "raw",
+ size => 9663676416, # in bytes!
+ vmid => 102,
+ volid => "local-lvm:vm-102-disk-0",
+ },
+ # [...]
+ ]
+
+Backups will also contain additional keys:
+
+ [
+ {
+ content => "backup",
+ ctime => 1742405070, # creation time as unix timestamp
+ format => "tar.zst",
+ notes => "...", # comment that was entered when backup was created
+ size => 328906840, # in bytes!
+ subtype => "lxc", # "lxc" for containers, "qemu" for VMs
+ vmid => 106,
+ volid => "local:backup/vzdump-lxc-106-2025_03_19-18_24_30.tar.zst",
+ },
+ # [...]
+ ]
+
+=cut
+
sub list_volumes {
my ($class, $storeid, $scfg, $vmid, $content_types) = @_;
croak "implement me in sub-class\n";
}
-# Returns undef if the attribute is not supported for the volume.
-# Should die if there is an error fetching the attribute.
-# Possible attributes:
-# notes - user-provided comments/notes.
-# protected - not to be removed by free_image, and for backups, ignored when pruning.
+=head3 $plugin->get_volume_attribute(\%scfg, $storeid, $volname, $attribute)
+
+=head3 $plugin->get_volume_attribute(...)
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Returns the value of the given C<$attribute> for a volume. If the attribute isn't
+supported for the volume, returns C<undef>.
+
+C<die>s if there is an error fetching the attribute.
+
+B<Possible attributes:>
+
+=over
+
+=item * C<notes> (returns scalar)
+
+User-provided comments / notes.
+
+=item * C<protected> (returns scalar)
+
+When set to C<1>, the volume must not be removed by C<L<< free_image()|/"$plugin->free_image(...)" >>>.
+Backups with C<protected> set to C<1> are ignored when pruning.
+
+=back
+
+=cut
+
sub get_volume_attribute {
my ($class, $scfg, $storeid, $volname, $attribute) = @_;
croak "implement me in sub-class\n";
}
-# Dies if the attribute is not supported for the volume.
+=head3 $plugin->update_volume_attribute(\%scfg, $storeid, $volname, $attribute, $value)
+
+=head3 $plugin->update_volume_attribute(...)
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Sets the value of the given C<$attribute> for a volume to C<$value>.
+
+C<die>s if the attribute isn't supported for the volume (or storage).
+
+For a list of supported attributes, see C<L<< get_volume_attribute()|/"$plugin->get_volume_attribute(...)" >>>.
+
+=cut
+
sub update_volume_attribute {
my ($class, $scfg, $storeid, $volname, $attribute, $value) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_size_info(\%scfg, $storeid, $volname [, $timeout])
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Returns information about the given volume's size. In scalar context, this returns
+just the volume's size in bytes:
+
+ my $size = $plugin->volume_size_info($scfg, $storeid, $volname, $timeout)
+
+In list context, returns an array with the following structure:
+
+ my ($size, $format, $used, $parent, $ctime) = $plugin->volume_size_info(
+ $scfg, $storeid, $volname, $timeout
+ )
+
+where C<$size> is the size of the volume in bytes, C<$format> one of the possible
+L<< formats listed in C<plugindata()>|/"$plugin->plugindata()" >>, C<$used> the
+amount of space used in bytes, C<$parent> the (optional) name of the base volume
+and C<$ctime> the creation time as unix timestamp.
+
+Optionally, a C<$timeout> may be provided after which the method should C<die>.
+This timeout is best passed to other helpers which already support timeouts,
+such as C<L<< PVE::Tools::run_command|PVE::Tools/run_command >>>.
+
+See also the C<L<< PVE::Storage::Plugin::file_size_info|PVE::Storage::Plugin/file_size_info >>> helper.
+
+=cut
+
sub volume_size_info {
my ($class, $scfg, $storeid, $volname, $timeout) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_resize(\%scfg, $storeid, $volname, $size [, $running])
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Resizes a volume to the new C<$size> in bytes. Optionally, the guest that owns
+the volume may be C<$running> (= C<1>).
+
+C<die>s in case of errors, or if the underlying storage implementation or the
+volume's format doesn't support resizing.
+
+This function should not return any value. In previous versions the returned
+value would be used to determine the new size of the volume or whether the
+operation succeeded.
+
+=cut
+
sub volume_resize {
my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_snapshot(\%scfg, $storeid, $volname, $snap)
+
+B<OPTIONAL:> May be implemented if the underlying storage supports snapshots.
+
+Takes a snapshot of a volume and gives it the name provided by C<$snap>.
+
+C<die>s if the underlying storrage doesn't support snapshots or an error
+occurs while taking a snapshot.
+
+=cut
+
sub volume_snapshot {
my ($class, $scfg, $storeid, $volname, $snap) = @_;
croak "implement me in sub-class\n";
}
-# Returns a hash with the snapshot names as keys and the following data:
-# id - Unique id to distinguish different snapshots even if the have the same name.
-# timestamp - Creation time of the snapshot (seconds since epoch).
-# Returns an empty hash if the volume does not exist.
+=head3 $plugin->volume_snapshot_info(\%scfg, $storeid, $volname)
+
+Returns a hashref with the snapshot names as keys and the following structure:
+
+ {
+ my_snapshot => {
+ id => "01b7e668-58b4-6f42-9a5e-1944c2855c84", # Unique id to distinguish different snapshots with the same name.
+ timestamp => "1729841807", # Creation time of the snapshot (seconds since epoch).
+ },
+ # [...]
+ }
+
+Returns an empty hashref if the volume does not exist.
+
+=cut
+
sub volume_snapshot_info {
my ($class, $scfg, $storeid, $volname) = @_;
croak "implement me in sub-class\n";
}
-# Asserts that a rollback to $snap on $volname is possible.
-# If certain snapshots are preventing the rollback and $blockers is an array
-# reference, the snapshot names can be pushed onto $blockers prior to dying.
+=head3 $plugin->volume_rollback_is_possible(\%scfg, $storeid, $volname, $snap [, \@blockers])
+
+=head3 $plugin->volume_rollback_is_possible(...)
+
+B<OPTIONAL:> May be implemented if the underlying storage supports snapshots.
+
+Asserts whether a rollback to C<$snap> on C<$volname> is possible, C<die>s if
+it isn't.
+
+Optionally, C<\@blockers> may be provided, which may contain the names of
+snapshots that are preventing the rollback. Should any such snapshots exist,
+they should be pushed to this listref pior to C<die>-ing. The caller may then
+use this listref when handling errors.
+
+=cut
+
sub volume_rollback_is_possible {
my ($class, $scfg, $storeid, $volname, $snap, $blockers) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_snapshot_rollback(\%scfg, $storeid, $volname, $snap)
+
+Performs a rollback to the given C<$snap>shot on C<$volname>.
+
+C<die>s in case of errors.
+
+=cut
+
sub volume_snapshot_rollback {
my ($class, $scfg, $storeid, $volname, $snap) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_snapshot_delete(\%scfg, $storeid, $volname, $snap [, $running])
+
+Deletes the C<$snap>shot of C<$volname>.
+
+C<die>s in case of errors.
+
+Optionally, the guest that owns the given volume may be C<$running> (= C<1>).
+
+B<Deprecated:> The C<$running> parameter is deprecated and will be removed on the
+next C<APIAGE> reset.
+
+=cut
+
sub volume_snapshot_delete {
my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_snapshot_needs_fsfreeze()
+
+Returns whether filesystems on top of the volume need to flush their journal for
+consistency before a snapshot is taken. See L<fsfreeze(8)>.
+
+This is needed for container mountpoints.
+
+=cut
+
sub volume_snapshot_needs_fsfreeze {
croak "implement me in sub-class\n";
}
+=head3 $plugin->storage_can_replicate(\%scfg, $storeid, $format)
+
+Returns whether volumes in a given C<$format> support replication.
+
+See C<L<< plugindata()|/"$plugin->plugindata()" >>> for all disk formats.
+
+=cut
+
sub storage_can_replicate {
my ($class, $scfg, $storeid, $format) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->volume_has_feature(\%scfg, $feature, $storeid, $volname, $snapname [, $running, \%opts])
+
+=head3 $plugin->volume_has_feature(...)
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Checks whether a volume C<$volname> or its snapshot C<$snapname> supports the
+given C<$feature>, returning C<1> if it does and C<undef> otherwise. The guest
+owning the volume may optionally be C<$running>.
+
+C<$feature> may be one of the following:
+
+ clone # linked clone is possible
+ copy # full clone is possible
+ replicate # replication is possible
+ snapshot # taking a snapshot is possible
+ sparseinit # volume is sparsely initialized
+ template # conversion to base image is possible
+ rename # renaming volumes is possible
+
+Which features are available under which circumstances depends on multiple
+factors, such as the underlying storage implementation, the format used, etc.
+It's best to check out C<L<PVE::Storage::Plugin>> or C<L<PVE::Storage::ZFSPoolPlugin>>
+for examples on how to handle features.
+
+Additional keys are given in C<\%opts>:
+
+=over
+
+=item * C<< valid_target_formats => [...] >>
+
+Listref of formats for the target of a copy/clone operation that the caller
+could work with. The format of the given volume is always considered valid and
+if no list is specified, all formats are considered valid.
+
+=back
+
+=cut
+
sub volume_has_feature {
my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running, $opts) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->map_volume($storeid, \%scfg, $volname [, $snapname])
+
+B<OPTIONAL:> May be implemented in a storage plugin.
+
+Maps the device asscoiated with a volume or a volume's snapshot to a filesystem
+path, returning the path on completion. This method is used by containers.
+
+C<die>s in case of errors.
+
+C<L<< unmap_volume()|/"$plugin->unmap_volume(...)" >>> can be used to declare
+how the device should be unmapped.
+
+=cut
+
sub map_volume {
my ($class, $storeid, $scfg, $volname, $snapname) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->unmap_volume($storeid, \%scfg, $volname [, $snapname])
+
+B<OPTIONAL:> May be implemented in a storage plugin.
+
+Unmaps the device associated to a volume or a volume's snapshot.
+
+C<die>s in case of errors.
+
+=cut
+
sub unmap_volume {
my ($class, $storeid, $scfg, $volname, $snapname) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->activate_volume($storeid, \%scfg, $volname, $snapname [, \%cache])
+
+=head3 $plugin->activate_volume(...)
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Activates a volume or its associated snapshot, making it available to the
+system for further use. For example, this could mean activating an LVM volume,
+mounting a ZFS dataset, checking whether the volume's file path exists, etc.
+
+C<die>s in case of errors or if an operation is not supported.
+
+If this isn't needed, the method should simply be a no-op.
+
+This method may reuse L<< cached information via C<\%cache>|/"CACHING EXPENSIVE OPERATIONS" >>.
+
+=cut
+
sub activate_volume {
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->deactivate_volume($storeid, \%scfg, $volname, $snapname [, \%cache])
+
+=head3 deactivate_volume(...)
+
+B<REQUIRED:> Must be implemented in every storage plugin.
+
+Deactivates a volume or its associated snapshot, making it unavailable to
+the system. For example, this could mean deactivating an LVM volume,
+unmapping a Ceph/RBD device, etc.
+
+If this isn't needed, the method should simply be a no-op.
+
+This method may reuse L<< cached information via C<\%cache>|/"CACHING EXPENSIVE OPERATIONS" >>.
+
+=cut
+
sub deactivate_volume {
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->rename_volume(...)
+
+=head3 $plugin->rename_volume(\%scfg, $storeid, $source_volname, $target_vmid, $target_volname)
+
+B<OPTIONAL:> May be implemented in a storage plugin.
+
+Renames the volume given by C<$source_volname> to C<$target_volname> and assigns
+it to the guest C<$target_vmid>. Returns the volume ID of the renamed volume.
+
+This method is needed for the I<Change Owner> feature.
+
+C<die>s if the rename failed.
+
+=cut
+
sub rename_volume {
my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
croak "implement me in sub-class\n";
}
+=head3 $plugin->prune_backups(\%scfg, $storeid [, \%keep, $vmid, $type, $dryrun, \&logfunc])
+
+=head3 $plugin->prune_backups(...)
+
+Export a volume into a file handle as a stream with a desired format.
+
+C<die>s if there are (grave) problems while pruning.
+
+This method may take several optional parameters:
+
+=over
+
+=item * C<< \%keep >>
+
+A hashref containing backup retention policies. It has the following structure:
+
+ {
+ 'keep-all' => 1 # (optional) Whether to keep all backups.
+ # Conflicts with the other options when true.
+ 'keep-last' => N # (optional) Keep the last N backups.
+ 'keep-hourly' => N # (optional) Keep backups for the last N different hours.
+ # If there is more than one backup for a single
+ # hour, only the latest one is kept.
+ 'keep-daily' => N # (optional) Keep backups for the last N different days.
+ # If there is more than one backup for a single
+ # day, only the latest one is kept.
+ 'keep-weekly' => N # (optional) Keep backups for the last N different weeks.
+ # If there is more than one backup for a single
+ # week, only the latest one is kept.
+ 'keep-monthly' => N # (optional) Keep backups for the last N different months.
+ # If there is more than one backup for a single
+ # month, only the latest one is kept.
+ 'keep-yearly' => N # (optional) Keep backups for the last N different years.
+ # If there is more than one backup for a single
+ # year, only the latest one is kept.
+ }
+
+=item * C<< $vmid >>
+
+The guest's ID.
+
+=item * C<< $type >>
+
+The type of guest. When C<defined>, it can be one of C<"qemu"> or C<"lxc">.
+If C<undefined>, both types of backups will be pruned.
+
+=item * C<< $dry_run >>
+
+Whether this is a dry run. If set to C<1> there won't be any change on the
+underlying storage.
+
+=item * C<< \&logfunc >>
+
+A subroutine ref that can be used to log messages with the following signature:
+
+ $logfunc->($severity, $message)
+
+where $severity can be one of C<"info">, C<"err">, or C<"warn">.
+
+=back
+
+=cut
+
sub prune_backups {
my ($class, $scfg, $storeid, $keep, $vmid, $type, $dryrun, $logfunc) = @_;
croak "implement me in sub-class\n";
--
2.39.5
More information about the pve-devel
mailing list