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 [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 5DC0A1FF15C for <inbox@lore.proxmox.com>; Wed, 26 Mar 2025 15:21:59 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 188C43943D; Wed, 26 Mar 2025 15:21:49 +0100 (CET) From: Max Carrara <m.carrara@proxmox.com> To: pve-devel@lists.proxmox.com Date: Wed, 26 Mar 2025 15:20:58 +0100 Message-Id: <20250326142059.261938-8-m.carrara@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250326142059.261938-1-m.carrara@proxmox.com> References: <20250326142059.261938-1-m.carrara@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.074 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: [pve-devel] [PATCH v1 pve-storage 7/8] pluginbase: document volume operations 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> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com> 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@proxmox.com> Co-authored-by: Maximiliano Sandoval <m.sandoval@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 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel