Compare commits

...

29 Commits

Author SHA1 Message Date
jiangcuo
f851f8195e
Merge branch 'proxmox:master' into master 2025-04-14 14:22:41 +08:00
Thomas Lamprecht
0c6234f0bc bump version to 8.3.6
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 22:18:17 +02:00
Dominik Csapak
76f695f2e6 import: allow upload of guest images files into import storage
so users can upload qcow2/raw/vmdk files directly in the UI
Check the uploaded file with 'file_size_info' and the untrusted flag.
This checks the file format, existence of backing files, etc.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20250407101310.3196974-3-d.csapak@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 22:13:16 +02:00
Dominik Csapak
551bad9d47 api: rename 'isOva' to 'is_ova' to adhere to style guide
see https://pve.proxmox.com/wiki/Perl_Style_Guide#Casing

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Link: https://lore.proxmox.com/20250407101310.3196974-2-d.csapak@proxmox.com
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-07 22:12:51 +02:00
Fiona Ebner
5a73bac343 btrfs: die early for broken subvolume resize
In the BTRFS plugin, resize_volume() for a subovlume currently fails
with "failed to get btrfs subvolume ID from: ". This is because the
btrfs 'subvol show' command is invoked with '-q', so there is no
output.

As btrfs quotas are currently not implemented, die early with a clean
error instead and comment out the unused code for now.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250303092445.13873-6-f.ebner@proxmox.com
2025-04-06 21:57:58 +02:00
Fiona Ebner
0426aa3165 api: volume info: do not fail for zero-sized subvolumes
The special case of size being zero is supported if the volume is of
format 'subvol' is a special use case supported in Proxmox VE.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250303092445.13873-5-f.ebner@proxmox.com
2025-04-06 21:57:58 +02:00
Fiona Ebner
d97d7ff676 plugin: volume export formats: avoid superfluous file_size_info() call
The result from the file_size_info() call is not used by
volume_export_formats() and most failure scenarios of file_size_info()
lead to an undefined return value rather than a failure. This includes
the case for a non-existent file. The default path() implementation
doesn't do any existence check either.

An interesting scenario where file_size_info() does fail, is when the
volume is corrupted or not in the queried format. But this is a rare
edge case, so an early check doesn't seem worth it. It will be caught
by volume_export() itself, or in case of VM migration, also when
querying the size during scanning of local volumes.

While checking for the definedness of $size could serve as an early
sanity check, it is not currently done and other plugins don't do such
early checks in their implementation of volume_export_formats()
either. Keep the implementation abstract in Plugin.pm too and avoid
doing IO. Callers that want to do early existence checks or similar
can do so themselves explicitly, covering all plugins.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250303092445.13873-4-f.ebner@proxmox.com
2025-04-06 21:57:58 +02:00
Fiona Ebner
00105c8003 btrfs: fix volume size info for subvolumes in scalar context
Return the same size as in list context. See also commit "plugin: file
size info: be consistent about size of directory subvol".

Fixes cloning containers with unsized subvolumes on BTRFS. Before the
change, this would fail with "mkfs.ext4: Device size reported to be
zero.". That is because with non-zero size, the allocation of the
volume for the clone will be done with 'raw' format by the
alloc_disk() helper in LXC.pm rather than 'subvol'. This change will
make cloning containers with unsized subvol directories possible.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250303092445.13873-3-f.ebner@proxmox.com
2025-04-06 21:57:58 +02:00
Fiona Ebner
544dc0e893 plugin: file size info: be consistent about size of directory subvol
In list context, the file_size_info() function in Plugin.pm would
return 0 for the size of a subvol directory, but in scalar context 1.
As reported in the community forum [0], the change in commit e50dde0
("volume export: rely on storage plugin's format"), changing the
caller in volume_export() to scalar context exposed the inconsistency
in the return value for the size. This led to breakage of migration
with unsized btrfs subvolumes.

Align the returned values to avoid such surprises. Caller that would
treat 0 as an error should use list context and check if it is a
subvol, if they are able to handle them.

NOTE: it is not possible to attach either a subvol, or a volume with
zero size to a VM.

Existing callers of file_size_info() do not break with this change:
GuestImport/OVF.pm - parse_ovf() +
API/Qemu.pm - create_disks():
  Doesn't support subvol directories, dies if size is 0.
Storage/Plugin.pm - create_base() +
Storage/{BTRFS,}Plugin.pm - list_images():
  Checks for definedness of $size.
Storage/{BTRFS,ESXi,}Plugin.pm - volume_size_info():
  Transitive, see below.
Storage/Plugin.pm - volume_export():
  Regressed by commit e50dde0, this change will restore previous
  behavior.
Storage/Plugin.pm - volume_export_formats() +
GuestImport.pm - extract_disk_from_import_file() +
Storage.pm - assert_iso_content():
  Doesn't use the result.
CLI/qm.pm - importdisk:
  Dies early if source is a directory.
CLI/qm.pm - importovf:
  Calls parse_ovf() earlier.

Existing callers of volume_size_info() do not break with this change:
API2/Storage/Content.pm - info:
  Uses list context, not affected by change.
QemuMigrate.pm - scan_local_volumes() +
API2/Qemu.pm - create_disks(), first call:
  Uses list context, not affected by change, does not support subvol
  directories.
API2/Qemu.pm - create_disks(), second call +
API2/Qemu.pm - import_from_volid():
  Doesn't support subvol directories, dies if size is 0.
API2/Qemu.pm - resize_vm +
VZDump/QemuServer.pm - prepare() +
QemuServer.pm - clone_disk() +
QemuServer.pm - create_efidisk():
  Doesn't support subvol directories (see NOTE above), but would not
  die directly if size is 0. (In case of create_efidisk(), the size of
  the just created disk is queried.)
API2/LXC.pm - resize_vm:
  For directory plugins, the subsequent call to resize_volume() fails
  with "can't resize this image format".
  For BTRFS, quotas are currently not supported and the call to
  resize_volume() fails with "failed to get btrfs subvolume ID from:".
  This is because the btrfs 'subvol show' command is invoked with
  '-q', so there is no output. Even if it would work, it would be more
  correct to use 0 as the current size to add to the new quota rather
  than 1.
LXC/Config.pm - rescan_volume():
  This will happily use size=1 from before this change, but that is
  not correct. A subsequent 'pct rescan' will correct the size to 0.
  It is only used when hotplugging an existing subvol directory.
LXC.pm - copy_volume():
  This will happily use size=1 from before this change, but that is
  not correct and will lead to failure "mkfs.ext4: Device size
  reported to be zero.". That is because with non-zero size, the
  allocation of the volume for the clone will be done with 'raw'
  format by the alloc_disk() helper in LXC.pm rather than 'subvol'.
  This change will make cloning containers with unsized subvol
  directories possible.
QemuServer/Cloudinit.pm - commit_cloudinit_disk():
  Doesn't support subvol directories (see NOTE above), will allocate
  a new volume when size is 0.

[0]: https://forum.proxmox.com/threads/162943/

Fixes: e50dde0 ("volume export: rely on storage plugin's format")
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250303092445.13873-2-f.ebner@proxmox.com
2025-04-06 21:57:58 +02:00
Thomas Lamprecht
d0eb7df3af bump version to 8.3.5
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-06 21:18:43 +02:00
Daniel Herzig
a9a2782d77 fix #3986: directory plugin: remove trailing slashes
Currently, setting up a directory storage with trailing slashes in the
path results in log messages with double slashes, if this path gets
expanded by an action like vzdump. While this is just a cosmetic
issue, it looks odd and some users might think this is a bug if they
currently investigate some other issue and see such paths.

This patch removes those trailing slashes once the directory storage
class config gets updated.

Signed-off-by: Daniel Herzig <d.herzig@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-06 21:18:20 +02:00
Fiona Ebner
32f55f8c0d btrfs: volume export: fix command for incremental stream
The subvolume itself cannot be included if there is a base snapshot
or the command would fail with e.g.

> ERROR: subvolume /mnt/btrfs/images/400/vm-400-disk-0 is not read-only

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-04-06 21:18:20 +02:00
Fiona Ebner
2ef1d256e7 btrfs: volume import: fix check for presence of base snapshot
For a 'raw' volume, the path includes the '/disk.raw' suffix, but the
check expects the containing subvolume directory.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-04-06 21:18:20 +02:00
Fiona Ebner
0e58fffcc2 volume export: fix handling of snapshot list
The split_list() helper will return a list, and assignment in scalar
context would result in the number of elements, instead of having the
desired array reference, that the BTRFS plugin expects.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-04-06 21:18:20 +02:00
Shannon Sterz
8a183b5db2 disk management: account for leading white space in serial number
some manufacturer seem to report leading white space in the
`ID_SERIAL_SHORT` field. the regex failed here, as it just didn't
match the whitespace at all.

reported on the forum:
https://forum.proxmox.com/threads/nvme-drive-serial-unknown.163480/#post-754953

Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Link: https://lore.proxmox.com/20250312083819.15379-1-s.sterz@proxmox.com
2025-04-06 21:18:14 +02:00
Fiona Ebner
afc58d4d95 extract backup config: delegate to backup provider for storages that support it
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Link: https://lore.proxmox.com/20250404133204.239783-8-f.ebner@proxmox.com
2025-04-06 20:58:27 +02:00
Fiona Ebner
e2dc01ac9f plugin api: bump api version and age
Changes for version 11:

* Allow declaring storage features via plugin data.
* Introduce new_backup_provider() plugin method.
* Allow declaring sensitive properties via plugin data.

See the api changelog file for details.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Link: https://lore.proxmox.com/20250404133204.239783-7-f.ebner@proxmox.com
2025-04-06 20:57:40 +02:00
Fiona Ebner
db5c50c079 config api/plugins: let plugins define sensitive properties themselves
Hard-coding a list of sensitive properties means that custom plugins
cannot define their own sensitive properties for the on_add/on_update
hooks.

Have plugins declare the list of their sensitive properties in the
plugin data. For backwards compatibility, return the previously
hard-coded list if no such declaration is present.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Link: https://lore.proxmox.com/20250404133204.239783-6-f.ebner@proxmox.com
2025-04-06 20:57:40 +02:00
Fiona Ebner
ff971aefc0 plugin: introduce method for native external backup provider support
The new_backup_provider() method can be used by storage plugins for
external backup providers. If the method returns a provider, Proxmox
VE will use callbacks to that provider for backups and restore instead
of using its usual backup/restore mechanisms.

The backup provider API is split into two parts, both of which again
need different implementations for VM and LXC guests:

1. Backup API

In Proxmox VE, a backup job consists of backup tasks for individual
guests. There are methods for initialization and cleanup of the job,
i.e. job_init() and job_cleanup() and for each guest backup, i.e.
backup_init() and backup_cleanup().

The backup_get_mechanism() method is used to decide on the backup
mechanism. Currently, 'file-handle' or 'nbd' for VMs, and 'directory'
for containers is possible. The method also let's the plugin indicate
whether to use a bitmap for incremental VM backup or not. It is enough
to implement one mechanism for VMs and one mechanism for containers.

Next, there are methods for backing up the guest's configuration and
data, backup_vm() for VM backup and backup_container() for container
backup, with the latter running

Finally, some helpers like getting the provider name or volume ID for
the backup target, as well as for handling the backup log.

The backup transaction looks as follows:

First, job_init() is called that can be used to check backup server
availability and prepare the connection. Then for each guest
backup_init() followed by backup_vm() or backup_container() and finally
backup_cleanup(). Afterwards job_cleanup() is called. For containers,
there is an additional backup_container_prepare() call while still
privileged. The actual backup_container() call happens as the
(unprivileged) container root user, so that the file owner and group IDs
match the container's perspective.

1.1 Backup Mechanisms

VM:

Access to the data on the VM's disk from the time the backup started
is made available via a so-called "snapshot access". This is either
the full image, or in case a bitmap is used, the dirty parts of the
image since the last time the bitmap was used for a successful backup.
Reading outside of the dirty parts will result in an error. After
backing up each part of the disk, it should be discarded in the export
to avoid unnecessary space usage on the Proxmox VE side (there is an
associated fleecing image).

VM mechanism 'file-handle':

The snapshot access is exposed via a file descriptor. A subroutine to
read the dirty regions for incremental backup is provided as well.

VM mechanism 'nbd':

The snapshot access and, if used, bitmap are exported via NBD.

Container mechanism 'directory':

A copy or snapshot of the container's filesystem state is made
available as a directory. The method is executed inside the user
namespace associated to the container.

2. Restore API

The restore_get_mechanism() method is used to decide on the restore
mechanism. Currently, 'qemu-img' for VMs, and 'directory' or 'tar' for
containers are possible. It is enough to implement one mechanism for
VMs and one mechanism for containers.

Next, methods for extracting the guest and firewall configuration and
the implementations of the restore mechanism via a pair of methods: an
init method, for making the data available to Proxmox VE and a cleanup
method that is called after restore.

2.1. Restore Mechanisms

VM mechanism 'qemu-img':

The backup provider gives a path to the disk image that will be
restored. The path needs to be something 'qemu-img' can deal with,
e.g. can also be an NBD URI or similar.

Container mechanism 'directory':

The backup provider gives the path to a directory with the full
filesystem structure of the container.

Container mechanism 'tar':

The backup provider gives the path to a (potentially compressed) tar
archive with the full filesystem structure of the container.

See the PVE::BackupProvider::Plugin module for the full API
documentation.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
 [WB: replace backup_vm_available_bitmaps with
  backup_vm_query_incremental, which instead of a bitmap name provides
  a bitmap mode that is 'new' (create or *recreate* a bitmap) or 'use'
  (use an existing bitmap, or create one if none exists)]
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Link: https://lore.proxmox.com/20250404133204.239783-5-f.ebner@proxmox.com
2025-04-06 20:55:50 +02:00
Fiona Ebner
bbedead5fc common: add deallocate helper function
For punching holes via fallocate. This will be useful for the external
backup provider API to discard parts of the source. The 'file-handle'
mechanism there uses a fuse mount, which does not implement the
BLKDISCARD ioctl, but does implement fallocate.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Link: https://lore.proxmox.com/20250404133204.239783-4-f.ebner@proxmox.com
2025-04-06 20:55:50 +02:00
Fiona Ebner
0066560da4 add storage_has_feature() helper function
Which looks up whether a storage supports a given feature in its
'plugindata'. This is intentionally kept simple and not implemented
as a plugin method for now. Should it ever become more complex
requiring plugins to override the default implementation, it can
later be changed to a method.

Suggested-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Link: https://lore.proxmox.com/20250404133204.239783-3-f.ebner@proxmox.com
2025-04-06 20:52:55 +02:00
Friedrich Weber
7c62215e48 fix #3716: api: download from url: use proxy option for https
The web UI uses the download-url endpoint for downloading an ISO, VZ
template, or OVA file via wget. In a setup where this request has to
go over a proxy (configured in the http_proxy datacenter option), the
download only works for http:// URLs, not https:// URLs. The reason is
that the download-url handler does not pass the https_proxy option to
the download_file_from_url helper, hence the helper only sets the
http_proxy environment variable for wget, not the https_proxy one.

Fix this by also passing the https_proxy option to the
download_file_from_url helper.

This will break setups that rely on http_proxy not being respected for
https:// URLs. For example, setups that have a proxy for external
connections, but download e.g. ISO files (only) via https from an
internal repository that the proxy doesn't serve.

Signed-off-by: Friedrich Weber <f.weber@proxmox.com>
Reviewed-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Tested-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Link: https://lore.kernel.org/r/20250326105108.34911-2-f.weber@proxmox.com
2025-04-05 18:24:39 +02:00
Thomas Lamprecht
22ac96a00b bump version to 8.3.4
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-04-03 19:20:21 +02:00
Fiona Ebner
2569cb4604 introduce 'pve-storage-image-format' standard option for image format
The new 'pve-storage-image-format' standard option uses a simple enum
instead of a subroutine verifier. Since the 'pve-storage-format'
format that is replaced by it was used in pve-guest-common's
StorageTunnel, the format cannot be removed without a versioned
breaks.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Acked-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-03 18:51:18 +02:00
Fiona Ebner
559867f79b api: alloc: allow 'vmdk' for the 'format' option
The API endpoint will automatically detect the format from the
extension for raw, qcow2 and vmdk, but it was not yet possible to
specify the format explicitly via the parameter. This could be
annoying/surprising to users. There also might be third-party plugins
that want to use vmdk, but not require a suffix in the name. Add
'vmdk' as an allowed format to avoid these issues and for consistency
by using the 'pve-storage-format' format.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Acked-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-03 18:51:18 +02:00
Fiona Ebner
1b38ad0e27 schema: anchor regex for 'pve-storage-format'
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Acked-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-03 18:51:18 +02:00
Fiona Ebner
bba0b3ae46 api: code cleanup: remove unused Data::Dumper include
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Acked-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-03 18:51:18 +02:00
Fiona Ebner
43ad71bf30 plugin: file size info: remove ancient 'cow' from formats
The format was dropped in QEMU binary version 2.2 with commit
550830f935 ("block: delete cow block driver").

This follows qemu-server commit "drive: remove ancient 'cow' from
formats".

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Acked-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
2025-04-03 18:51:18 +02:00
Victor Seva
32e482a617 fix #957: iscsi: improve iscsi_test_portal logic
Check if there is already a logged session present and fall back to
previous TCP check port connection.

pvestatd is calling check_connection every 10 seconds.  This check
produces a lot of noise at the iscsi server logging.

Signed-off-by: Victor Seva <linuxmaniac@torreviejawireless.org>
Tested-by: Mira Limbeck <m.limbeck@proxmox.com>
Reviewed-by: Mira Limbeck <m.limbeck@proxmox.com>
Tested-by: Friedrich Weber <f.weber@proxmox.com>
Reviewed-by: Friedrich Weber <f.weber@proxmox.com>
2025-03-25 19:35:09 +01:00
29 changed files with 1473 additions and 55 deletions

View File

@ -6,6 +6,38 @@ without breaking anything unaware of it.)
Future changes should be documented in here.
## Version 11:
* Allow declaring storage features via plugin data
A new `storage_has_feature()` helper function was added that checks a storage plugin's features.
Plugins can indicate support for certain features in their `plugindata`. The first such feature is
`backup-provider`, see below for more details. To declare support for this feature, return
`features => { 'backup-provider' => 1 }` as part of the plugin data.
* Introduce new_backup_provider() plugin method
Proxmox VE now supports a `Backup Provider API` that can be used to implement custom backup
solutions tightly integrated in the Proxmox VE stack. See the `PVE::BackupProvider::Plugin::Base`
module for detailed documentation. A backup provider also needs to implement an associated storage
plugin for user-facing integration in Proxmox VE. Such a plugin needds to opt-in to the
`backup-provider` feature (see above) and implement the new_backup_provider() method, returning a
blessed reference to the backup provider class. The rest of the plugin methods, e.g. listing
content, providing usage information, etc., follow the same API as usual.
* Allow declaring sensitive properties via plugin data
A new `sensitive_properties()` helper function was added to get the list of sensitive properties
a plugin uses via the plugin's `plugindata`. The sensitive properties are passed separately from
other properties to the `on_add_hook()` and `on_update_hook()` methods and should not be written
to the storage configuration file directly, but stored in the more restricted
`/etc/pve/priv/storage` directory on the Proxmox Cluster File System. For example, to declare that
a `ssh-private-key` property used by the plugin is sensitive, return
`'sensitive-properties' => { 'ssh-private-key' => 1 }` as part of the plugin data. The list of
sensitive properties was hard-coded previously, as `encryption-key`, `keyring`, `master-pubkey`,
`password`. For backwards compatibility, this list is still used if a plugin doesn't declare its
own sensitive properties.
## Version 10:
* a new `rename_volume` method has been added

64
debian/changelog vendored
View File

@ -1,3 +1,67 @@
libpve-storage-perl (8.3.6) bookworm; urgency=medium
* plugin: file size info: be consistent about size of directory subvol to
avoid breakage at use sites, like storage migration with btrfs.
* plugin: volume export formats: avoid superfluous file_size_info() call.
* api: volume info: do not fail for zero-sized subvolumes.
* btrfs: die early for broken subvolume resize.
* import: allow upload of guest images files into import storage.
-- Proxmox Support Team <support@proxmox.com> Mon, 07 Apr 2025 22:18:14 +0200
libpve-storage-perl (8.3.5) bookworm; urgency=medium
* fix #3716: api: download from url: use proxy option for https, not just
http.
* extend the storage plugin interface providing:
+ helper to query features that plugins can declare to support
+ add new_backup_provider plugin method to allow creating a storage that
implements backup functionality that natively integrates into PVE.
+ declare which properties are sensitive or not centrally in a plugin.
* disk management: allow leading white space in the serial number property.
* btrs: various small fixes in preparation for fully supported volume
export.
* fix #3986: directory plugin: remove trailing slashes to avoid multiple
consecutive slashes; while they do not have any effect they rather look
odd and users might misinterpret them as causing problems.
-- Proxmox Support Team <support@proxmox.com> Sun, 06 Apr 2025 21:18:38 +0200
libpve-storage-perl (8.3.4) bookworm; urgency=medium
* rbd plugin: drop broken cache for pool specific information in list image.
* iSCSI direct: drop broken cache for storeid-specific information in list
image.
* fix #3873: btrfs: volume export: avoid that snapshots of other disks get
picked up to fix storage migration with multiple volumes and snapshots.
* btrfs: volume import: forcefully set image to readwrite to be able to
recreate snapshots.
* fix #957: iscsi: improve check for already available sessions to a portal,
reducing log-noise on the iSCSI server.
* plugin: file size info: remove ancient 'cow' from formats, dropped
upstream in QEMU version 2.2.
* api: alloc: allow explicitly passing 'vmdk' for the 'format' option
instead of just detecting that format indirectly.
* introduce 'pve-storage-image-format' standard option for image schema
format
-- Proxmox Support Team <support@proxmox.com> Thu, 03 Apr 2025 19:20:17 +0200
libpve-storage-perl (8.3.3) bookworm; urgency=medium
* plugin: export/import: fix calls to path() method

View File

@ -190,8 +190,6 @@ __PACKAGE__->register_method ({
return &$api_storage_config($cfg, $param->{storage});
}});
my $sensitive_params = [qw(password encryption-key master-pubkey keyring)];
__PACKAGE__->register_method ({
name => 'create',
protected => 1,
@ -239,6 +237,7 @@ __PACKAGE__->register_method ({
# fix me in section config create never need an empty entity.
delete $param->{nodes} if !$param->{nodes};
my $sensitive_params = PVE::Storage::Plugin::sensitive_properties($type);
my $sensitive = extract_sensitive_params($param, $sensitive_params, []);
my $plugin = PVE::Storage::Plugin->lookup($type);
@ -344,6 +343,7 @@ __PACKAGE__->register_method ({
my $scfg = PVE::Storage::storage_config($cfg, $storeid);
$type = $scfg->{type};
my $sensitive_params = PVE::Storage::Plugin::sensitive_properties($type);
my $sensitive = extract_sensitive_params($param, $sensitive_params, $delete);
my $plugin = PVE::Storage::Plugin->lookup($type);

View File

@ -2,11 +2,11 @@ package PVE::API2::Storage::Content;
use strict;
use warnings;
use Data::Dumper;
use PVE::SafeSyslog;
use PVE::Cluster;
use PVE::Storage;
use PVE::Storage::Common; # for 'pve-storage-image-format' standard option
use PVE::INotify;
use PVE::Exception qw(raise_param_exc);
use PVE::RPCEnvironment;
@ -178,12 +178,10 @@ __PACKAGE__->register_method ({
type => 'string',
pattern => '\d+[MG]?',
},
'format' => {
type => 'string',
enum => ['raw', 'qcow2', 'subvol'],
format => get_standard_option('pve-storage-image-format', {
requires => 'size',
optional => 1,
},
}),
},
},
returns => {
@ -324,7 +322,9 @@ __PACKAGE__->register_method ({
my $path = PVE::Storage::path($cfg, $volid);
my ($size, $format, $used, $parent) = PVE::Storage::volume_size_info($cfg, $volid);
die "volume_size_info on '$volid' failed\n" if !($format && $size);
die "volume_size_info on '$volid' failed - no format\n" if !$format;
die "volume_size_info on '$volid' failed - no size\n" if !defined($size);
die "volume '$volid' has size zero\n" if !$size && $format ne 'subvol';
my $entry = {
path => $path,

View File

@ -387,7 +387,7 @@ __PACKAGE__->register_method ({
name => 'upload',
path => '{storage}/upload',
method => 'POST',
description => "Upload templates, ISO images and OVAs.",
description => "Upload templates, ISO images, OVAs and VM images.",
permissions => {
check => ['perm', '/storage/{storage}', ['Datastore.AllocateTemplate']],
},
@ -455,7 +455,8 @@ __PACKAGE__->register_method ({
my $filename = PVE::Storage::normalize_content_filename($param->{filename});
my $path;
my $isOva = 0;
my $is_ova = 0;
my $image_format;
if ($content eq 'iso') {
if ($filename !~ m![^/]+$PVE::Storage::ISO_EXT_RE_0$!) {
@ -471,8 +472,14 @@ __PACKAGE__->register_method ({
if ($filename !~ m!${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::UPLOAD_IMPORT_EXT_RE_1$!) {
raise_param_exc({ filename => "invalid filename or wrong extension" });
}
my $format = $1;
if ($format eq 'ova') {
$is_ova = 1;
} else {
$image_format = $format;
}
$isOva = 1;
$path = PVE::Storage::get_import_dir($cfg, $storage);
} else {
raise_param_exc({ content => "upload content type '$content' not allowed" });
@ -541,8 +548,11 @@ __PACKAGE__->register_method ({
PVE::Storage::assert_iso_content($tmpfilename);
}
if ($isOva) {
if ($is_ova) {
assert_ova_contents($tmpfilename);
} elsif (defined($image_format)) {
# checks untrusted image
PVE::Storage::file_size_info($tmpfilename, 10, $image_format, 1);
}
};
if (my $err = $@) {
@ -578,7 +588,7 @@ __PACKAGE__->register_method({
name => 'download_url',
path => '{storage}/download-url',
method => 'POST',
description => "Download templates, ISO images and OVAs by using an URL.",
description => "Download templates, ISO images, OVAs and VM images by using an URL.",
proxyto => 'node',
permissions => {
description => 'Requires allocation access on the storage and as this allows one to probe'
@ -666,7 +676,8 @@ __PACKAGE__->register_method({
my $filename = PVE::Storage::normalize_content_filename($param->{filename});
my $path;
my $isOva = 0;
my $is_ova = 0;
my $image_format;
if ($content eq 'iso') {
if ($filename !~ m![^/]+$PVE::Storage::ISO_EXT_RE_0$!) {
@ -682,9 +693,12 @@ __PACKAGE__->register_method({
if ($filename !~ m!${PVE::Storage::SAFE_CHAR_CLASS_RE}+$PVE::Storage::UPLOAD_IMPORT_EXT_RE_1$!) {
raise_param_exc({ filename => "invalid filename or wrong extension" });
}
my $format = $1;
if ($filename =~ m/\.ova$/) {
$isOva = 1;
if ($format eq 'ova') {
$is_ova = 1;
} else {
$image_format = $format;
}
$path = PVE::Storage::get_import_dir($cfg, $storage);
@ -700,6 +714,7 @@ __PACKAGE__->register_method({
hash_required => 0,
verify_certificates => $param->{'verify-certificates'} // 1,
http_proxy => $dccfg->{http_proxy},
https_proxy => $dccfg->{http_proxy},
};
my ($checksum, $checksum_algorithm) = $param->@{'checksum', 'checksum-algorithm'};
@ -715,8 +730,11 @@ __PACKAGE__->register_method({
PVE::Storage::assert_iso_content($tmp_path);
}
if ($isOva) {
if ($is_ova) {
assert_ova_contents($tmp_path);
} elsif (defined($image_format)) {
# checks untrusted image
PVE::Storage::file_size_info($tmp_path, 10, $image_format, 1);
}
};

View File

@ -0,0 +1,3 @@
.PHONY: install
install:
make -C Plugin install

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
SOURCES = Base.pm
.PHONY: install
install:
for i in ${SOURCES}; do install -D -m 0644 $$i ${DESTDIR}${PERLDIR}/PVE/BackupProvider/Plugin/$$i; done

View File

@ -308,7 +308,7 @@ __PACKAGE__->register_method ({
my $with_snapshots = $param->{'with-snapshots'};
if (defined(my $list = $param->{'snapshot-list'})) {
$with_snapshots = PVE::Tools::split_list($list);
$with_snapshots = [PVE::Tools::split_list($list)];
}
my $filename = $param->{filename};

View File

@ -328,7 +328,7 @@ sub get_udev_info {
return if !defined($data->{devpath});
$data->{serial} = 'unknown';
$data->{serial} = $1 if $info =~ m/^E: ID_SERIAL_SHORT=(\S+)$/m;
$data->{serial} = $1 if $info =~ m/^E: ID_SERIAL_SHORT=\s*(\S+)$/m;
$data->{gpt} = $info =~ m/^E: ID_PART_TABLE_TYPE=gpt$/m ? 1 : 0;

View File

@ -9,6 +9,7 @@ install:
make -C Storage install
make -C GuestImport install
make -C API2 install
make -C BackupProvider install
make -C CLI install
.PHONY: test

View File

@ -42,11 +42,11 @@ use PVE::Storage::BTRFSPlugin;
use PVE::Storage::ESXiPlugin;
# Storage API version. Increment it on changes in storage API interface.
use constant APIVER => 10;
use constant APIVER => 11;
# Age is the number of versions we're backward compatible with.
# This is like having 'current=APIVER' and age='APIAGE' in libtool,
# see https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
use constant APIAGE => 1;
use constant APIAGE => 2;
our $KNOWN_EXPORT_FORMATS = ['raw+size', 'tar+size', 'qcow2+size', 'vmdk+size', 'zfs', 'btrfs'];
@ -116,7 +116,7 @@ our $BACKUP_EXT_RE_2 = qr/\.(tgz|(?:tar|vma)(?:\.(${\PVE::Storage::Plugin::COMPR
our $IMPORT_EXT_RE_1 = qr/\.(ova|ovf|qcow2|raw|vmdk)/;
our $UPLOAD_IMPORT_EXT_RE_1 = qr/\.(ova)/;
our $UPLOAD_IMPORT_EXT_RE_1 = qr/\.(ova|qcow2|raw|vmdk)/;
our $SAFE_CHAR_CLASS_RE = qr/[a-zA-Z0-9\-\.\+\=\_]/;
our $SAFE_CHAR_WITH_WHITESPACE_CLASS_RE = qr/[ a-zA-Z0-9\-\.\+\=\_]/;
@ -213,6 +213,14 @@ sub storage_check_enabled {
return storage_check_node($cfg, $storeid, $node, $noerr);
}
sub storage_has_feature {
my ($cfg, $storeid, $feature) = @_;
my $scfg = storage_config($cfg, $storeid);
return PVE::Storage::Plugin::storage_has_feature($scfg->{type}, $feature);
}
# storage_can_replicate:
# return true if storage supports replication
# (volumes allocated with vdisk_alloc() has replication feature)
@ -1479,7 +1487,7 @@ sub scan_iscsi {
die "unable to parse/resolve portal address '${portal_in}'\n";
}
return PVE::Storage::ISCSIPlugin::iscsi_discovery([ $portal ]);
return PVE::Storage::ISCSIPlugin::iscsi_discovery(undef, [ $portal ]);
}
sub storage_default_format {
@ -1751,6 +1759,17 @@ sub extract_vzdump_config {
storage_check_enabled($cfg, $storeid);
return PVE::Storage::PBSPlugin->extract_vzdump_config($scfg, $volname, $storeid);
}
if (storage_has_feature($cfg, $storeid, 'backup-provider')) {
my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
my $log_function = sub {
my ($log_level, $message) = @_;
my $prefix = $log_level eq 'err' ? 'ERROR' : uc($log_level);
print "$prefix: $message\n";
};
my $backup_provider = $plugin->new_backup_provider($scfg, $storeid, $log_function);
return $backup_provider->archive_get_guest_config($volname, $storeid);
}
}
my $archive = abs_filesystem_path($cfg, $volid);
@ -2019,6 +2038,14 @@ sub volume_export_start {
PVE::Tools::run_command($cmds, %$run_command_params);
}
sub new_backup_provider {
my ($cfg, $storeid, $log_function) = @_;
my $scfg = storage_config($cfg, $storeid);
my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
return $plugin->new_backup_provider($scfg, $storeid, $log_function);
}
# bash completion helper
sub complete_storage {

View File

@ -45,6 +45,7 @@ sub plugindata {
{ images => 1, rootdir => 1 },
],
format => [ { raw => 1, subvol => 1 }, 'raw', ],
'sensitive-properties' => {},
};
}
@ -232,14 +233,15 @@ sub btrfs_cmd {
return $msg;
}
sub btrfs_get_subvol_id {
my ($class, $path) = @_;
my $info = $class->btrfs_cmd(['subvolume', 'show', '--', $path]);
if ($info !~ /^\s*(?:Object|Subvolume) ID:\s*(\d+)$/m) {
die "failed to get btrfs subvolume ID from: $info\n";
}
return $1;
}
# NOTE: this function is currently boken, because btrfs_cmd uses '-q' so there will be no output.
#sub btrfs_get_subvol_id {
# my ($class, $path) = @_;
# my $info = $class->btrfs_cmd(['subvolume', 'show', '--', $path]);
# if ($info !~ /^\s*(?:Object|Subvolume) ID:\s*(\d+)$/m) {
# die "failed to get btrfs subvolume ID from: $info\n";
# }
# return $1;
#}
my sub chattr : prototype($$$) {
my ($fh, $mask, $xor) = @_;
@ -486,7 +488,7 @@ sub volume_size_info {
my $ctime = (stat($path))[10];
my ($used, $size) = (0, 0);
#my ($used, $size) = btrfs_subvol_quota($class, $path); # uses wantarray
return wantarray ? ($size, 'subvol', $used, undef, $ctime) : 1;
return wantarray ? ($size, 'subvol', $used, undef, $ctime) : $size;
}
return PVE::Storage::Plugin::file_size_info($path, $timeout, $format);
@ -497,10 +499,13 @@ sub volume_resize {
my $format = ($class->parse_volname($volname))[6];
if ($format eq 'subvol') {
my $path = $class->filesystem_path($scfg, $volname);
my $id = '0/' . $class->btrfs_get_subvol_id($path);
$class->btrfs_cmd(['qgroup', 'limit', '--', "${size}k", "0/$id", $path]);
return undef;
# NOTE: `btrfs send/recv` actually drops quota information so supporting subvolumes with
# quotas doesn't play nice with send/recv.
die "cannot resize subvolume - btrfs quotas are currently not supported\n";
# my $path = $class->filesystem_path($scfg, $volname);
# my $id = '0/' . $class->btrfs_get_subvol_id($path);
# $class->btrfs_cmd(['qgroup', 'limit', '--', "${size}k", "0/$id", $path]);
# return undef;
}
return PVE::Storage::Plugin::volume_resize(@_);
@ -779,7 +784,8 @@ sub volume_export {
}
push @$cmd, '--';
if (ref($with_snapshots) eq 'ARRAY') {
push @$cmd, (map { "$path\@$_" } ($with_snapshots // [])->@*), $path;
push @$cmd, (map { "$path\@$_" } ($with_snapshots // [])->@*);
push @$cmd, $path if !defined($base_snapshot);
} else {
foreach_snapshot_of_subvol($path, sub {
my ($snap_name) = @_;
@ -823,6 +829,7 @@ sub volume_import {
if (defined($base_snapshot)) {
my $path = $class->path($scfg, $volname, $storeid, $base_snapshot);
$path = raw_file_to_subvol($path) if $volume_format eq 'raw';
die "base snapshot '$base_snapshot' not found - no such directory '$path'\n"
if !path_is_subvolume($path);
}

View File

@ -101,6 +101,7 @@ sub plugindata {
content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1,
backup => 1, snippets => 1, import => 1}, { images => 1 }],
format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
'sensitive-properties' => { password => 1 },
};
}

View File

@ -118,6 +118,7 @@ sub plugindata {
return {
content => [ { vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 },
{ backup => 1 }],
'sensitive-properties' => { keyring => 1 },
};
}

View File

@ -3,6 +3,14 @@ package PVE::Storage::Common;
use strict;
use warnings;
use PVE::JSONSchema;
use PVE::Syscall;
use constant {
FALLOC_FL_KEEP_SIZE => 0x01, # see linux/falloc.h
FALLOC_FL_PUNCH_HOLE => 0x02, # see linux/falloc.h
};
=pod
=head1 NAME
@ -25,6 +33,31 @@ be grouped in a submodule can also be found here.
=back
=head1 STANDARD OPTIONS FOR JSON SCHEMA
=over
=back
=head3 pve-storage-image-format
Possible formats a guest image can have.
=cut
# TODO PVE 9 - Note that currently, qemu-server allows more formats for VM images, so third party
# storage plugins might potentially allow more too, but none of the plugins we are aware of do that.
# Those formats should either be allowed here or support for them should be phased out (at least in
# the storage layer). Can still be added again in the future, should any plugin provider request it.
PVE::JSONSchema::register_standard_option('pve-storage-image-format', {
type => 'string',
enum => ['raw', 'qcow2', 'subvol', 'vmdk'],
description => "Format of the image.",
});
=pod
=head1 FUNCTIONS
=cut
@ -51,4 +84,27 @@ sub align_size_up : prototype($$) {
return $aligned_size;
}
=pod
=head3 deallocate
deallocate($file_handle, $offset, $length)
Deallocates the range with C<$length> many bytes starting from offset C<$offset>
for the file associated to the file handle C<$file_handle>. Dies on failure.
=cut
sub deallocate : prototype($$$) {
my ($file_handle, $offset, $length) = @_;
my $mode = FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE;
$offset = int($offset);
$length = int($length);
if (syscall(PVE::Syscall::fallocate, fileno($file_handle), $mode, $offset, $length) != 0) {
die "fallocate: punch hole failed (offset: $offset, length: $length) - $!\n";
}
}
1;

View File

@ -6,6 +6,7 @@ use warnings;
use Cwd;
use Encode qw(decode encode);
use File::Path;
use File::Spec;
use IO::File;
use POSIX;
@ -26,6 +27,7 @@ sub plugindata {
content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, none => 1, import => 1 },
{ images => 1, rootdir => 1 }],
format => [ { raw => 1, qcow2 => 1, vmdk => 1, subvol => 1 } , 'raw' ],
'sensitive-properties' => {},
};
}
@ -245,6 +247,8 @@ sub check_config {
if ($opts->{path} !~ m|^/[-/a-zA-Z0-9_.@]+$|) {
die "illegal path for directory storage: $opts->{path}\n";
}
# remove trailing slashes from path
$opts->{path} = File::Spec->canonpath($opts->{path});
return $opts;
}

View File

@ -31,6 +31,7 @@ sub plugindata {
return {
content => [ { import => 1 }, { import => 1 }],
format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
'sensitive-properties' => { password => 1 },
};
}

View File

@ -100,6 +100,7 @@ sub plugindata {
content => [ { images => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1},
{ images => 1 }],
format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
'sensitive-properties' => {},
};
}

View File

@ -60,6 +60,7 @@ sub plugindata {
return {
content => [ {images => 1, none => 1}, { images => 1 }],
select_existing => 1,
'sensitive-properties' => {},
};
}

View File

@ -58,9 +58,30 @@ sub iscsi_session_list {
return $res;
}
sub iscsi_test_portal {
my ($portal) = @_;
sub iscsi_test_session {
my ($sid) = @_;
if ($sid !~ m/^[0-9]+$/) {
die "session_id: '$sid' is not a number\n";
}
my $state = file_read_firstline("/sys/class/iscsi_session/session${sid}/state");
return defined($state) && $state eq 'LOGGED_IN';
}
sub iscsi_test_portal {
my ($target, $portal, $cache) = @_;
$cache //= {};
if (defined($target)) {
# check session state instead if available
my $sessions = iscsi_session($cache, $target);
for my $session ($sessions->@*) {
next if $session->{portal} ne $portal;
my $state = iscsi_test_session($session->{session_id});
return $state if $state;
}
}
# check portal via tcp
my ($server, $port) = PVE::Tools::parse_host_and_port($portal);
return 0 if !$server;
return PVE::Network::tcp_ping($server, $port || 3260, 2);
@ -97,13 +118,13 @@ sub iscsi_portals {
}
sub iscsi_discovery {
my ($portals) = @_;
my ($target_in, $portals, $cache) = @_;
assert_iscsi_support();
my $res = {};
for my $portal ($portals->@*) {
next if !iscsi_test_portal($portal); # fixme: raise exception here?
next if !iscsi_test_portal($target_in, $portal, $cache); # fixme: raise exception here?
my $cmd = [$ISCSIADM, '--mode', 'discovery', '--type', 'sendtargets', '--portal', $portal];
eval {
@ -127,11 +148,11 @@ sub iscsi_discovery {
}
sub iscsi_login {
my ($target, $portals) = @_;
my ($target, $portals, $cache) = @_;
assert_iscsi_support();
eval { iscsi_discovery($portals); };
eval { iscsi_discovery($target, $portals, $cache); };
warn $@ if $@;
# Disable retries to avoid blocking pvestatd for too long, next iteration will retry anyway
@ -284,6 +305,7 @@ sub plugindata {
return {
content => [ {images => 1, none => 1}, { images => 1 }],
select_existing => 1,
'sensitive-properties' => {},
};
}
@ -445,7 +467,7 @@ sub activate_storage {
}
if ($do_login) {
eval { iscsi_login($scfg->{target}, $portals); };
eval { iscsi_login($scfg->{target}, $portals, $cache); };
warn $@ if $@;
} else {
# make sure we get all devices
@ -559,11 +581,11 @@ sub activate_volume {
sub check_connection {
my ($class, $storeid, $scfg) = @_;
my $cache = {};
my $portals = iscsi_portals($scfg->{target}, $scfg->{portal});
for my $portal (@$portals) {
my $result = iscsi_test_portal($portal);
my $result = iscsi_test_portal($scfg->{target}, $portal, $cache);
return $result if $result;
}

View File

@ -218,6 +218,7 @@ sub type {
sub plugindata {
return {
content => [ {images => 1, rootdir => 1}, { images => 1 }],
'sensitive-properties' => {},
};
}

View File

@ -31,6 +31,7 @@ sub type {
sub plugindata {
return {
content => [ {images => 1, rootdir => 1}, { images => 1, rootdir => 1}],
'sensitive-properties' => {},
};
}

View File

@ -56,6 +56,7 @@ sub plugindata {
content => [ { images => 1, rootdir => 1, vztmpl => 1, iso => 1, backup => 1, snippets => 1, import => 1 },
{ images => 1 }],
format => [ { raw => 1, qcow2 => 1, vmdk => 1 } , 'raw' ],
'sensitive-properties' => {},
};
}

View File

@ -30,6 +30,11 @@ sub type {
sub plugindata {
return {
content => [ {backup => 1, none => 1}, { backup => 1 }],
'sensitive-properties' => {
'encryption-key' => 1,
'master-pubkey' => 1,
password => 1,
},
};
}

View File

@ -184,11 +184,10 @@ my $defaultData = {
type => 'string', format => 'pve-storage-path',
optional => 1,
},
'format' => {
format => get_standard_option('pve-storage-image-format', {
description => "Default image format.",
type => 'string', format => 'pve-storage-format',
optional => 1,
},
}),
preallocation => {
description => "Preallocation mode for raw and qcow2 images. " .
"Using 'metadata' on raw images results in preallocation=off.",
@ -246,6 +245,28 @@ sub dirs_hash_to_string {
return join(',', map { "$_=$hash->{$_}" } sort keys %$hash);
}
sub sensitive_properties {
my ($type) = @_;
my $data = $defaultData->{plugindata}->{$type};
if (my $sensitive_properties = $data->{'sensitive-properties'}) {
return [sort keys $sensitive_properties->%*];
}
# For backwards compatibility. This list was hardcoded in the API module previously.
return [qw(encryption-key keyring master-pubkey password)];
}
sub storage_has_feature {
my ($type, $feature) = @_;
my $data = $defaultData->{plugindata}->{$type};
if (my $features = $data->{features}) {
return $features->{$feature};
}
return;
}
sub default_format {
my ($scfg) = @_;
@ -342,11 +363,15 @@ sub verify_content {
return $ct;
}
# NOTE the 'pve-storage-format' is deprecated, use the 'pve-storage-image-format' standard option
# from Storage/Common.pm instead
# TODO PVE 9 - remove after doing a versioned breaks for pve-guest-common, which was using this
# format.
PVE::JSONSchema::register_format('pve-storage-format', \&verify_format);
sub verify_format {
my ($fmt, $noerr) = @_;
if ($fmt !~ m/(raw|qcow2|vmdk|subvol)/) {
if ($fmt !~ m/^(raw|qcow2|vmdk|subvol)$/) {
return undef if $noerr;
die "invalid format '$fmt'\n";
}
@ -953,7 +978,7 @@ sub free_image {
}
# TODO taken from PVE/QemuServer/Drive.pm, avoiding duplication would be nice
my @checked_qemu_img_formats = qw(raw cow qcow qcow2 qed vmdk cloop);
my @checked_qemu_img_formats = qw(raw qcow qcow2 qed vmdk cloop);
# set $untrusted if the file in question might be malicious since it isn't
# created by our stack
@ -1007,7 +1032,7 @@ sub file_size_info {
if (S_ISDIR($st->mode)) {
$handle_error->("expected format '$file_format', but '$filename' is a directory\n")
if $file_format && $file_format ne 'subvol';
return wantarray ? (0, 'subvol', 0, undef, $st->ctime) : 1;
return wantarray ? (0, 'subvol', 0, undef, $st->ctime) : 0;
} elsif ($file_format && $file_format eq 'subvol') {
$handle_error->("expected format '$file_format', but '$filename' is not a directory\n");
}
@ -1733,10 +1758,7 @@ sub volume_export {
sub volume_export_formats {
my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_;
if ($scfg->{path} && !defined($snapshot) && !defined($base_snapshot)) {
my ($file) = $class->path($scfg, $volname, $storeid)
or return;
my $format = ($class->parse_volname($volname))[6];
my $size = file_size_info($file, undef, $format);
if ($with_snapshots) {
return ($format.'+size') if ($format eq 'qcow2' || $format eq 'vmdk');
@ -1858,6 +1880,21 @@ sub rename_volume {
return "${storeid}:${base}${target_vmid}/${target_volname}";
}
# Used by storage plugins for external backup providers. See PVE::BackupProvider::Plugin for the API
# the provider needs to implement.
#
# $scfg - the storage configuration
# $storeid - the storage ID
# $log_function($log_level, $message) - this log function can be used to write to the backup task
# log in Proxmox VE. $log_level is 'info', 'warn' or 'err', $message is the message to be printed.
#
# Returns a blessed reference to the backup provider class.
sub new_backup_provider {
my ($class, $scfg, $storeid, $log_function) = @_;
die "implement me if enabling the feature 'backup-provider' in plugindata()->{features}\n";
}
sub config_aware_base_mkdir {
my ($class, $scfg, $path) = @_;

View File

@ -380,6 +380,7 @@ sub type {
sub plugindata {
return {
content => [ {images => 1, rootdir => 1}, { images => 1 }],
'sensitive-properties' => { keyring => 1 },
};
}

View File

@ -175,6 +175,7 @@ sub type {
sub plugindata {
return {
content => [ {images => 1}, { images => 1 }],
'sensitive-properties' => {},
};
}

View File

@ -22,6 +22,7 @@ sub plugindata {
return {
content => [ {images => 1, rootdir => 1}, {images => 1 , rootdir => 1}],
format => [ { raw => 1, subvol => 1 } , 'raw' ],
'sensitive-properties' => {},
};
}