[pve-devel] [PATCH storage v5 15/51] plugin: qemu blockdev options: parse protocol paths in default implementation

Fabian Grünbichler f.gruenbichler at proxmox.com
Thu Jul 3 11:38:50 CEST 2025


Quoting Fiona Ebner (2025-07-02 18:27:48)
> for better backwards compatibility. This also means using path()
> rather than filesystem_path() as the latter does not return protocol
> paths.

I guess I should have read until here before replying to the other patch, sorry
for the noise ;)

> 
> Some protocol paths are not implemented (considered all that are
> listed by grepping for '\.protocol_name' in QEMU):
> - ftp(s)/http(s), which would access web servers via curl. This one
>   could be added if there is enough interest.
> - nvme://XXXX:XX:XX.X/X, which would access a host NVME device.
> - null-{aio,co}, which are mainly useful for debugging.
> - pbs, because path-based access is not used anymore for PBS,
>   live-restore in qemu-server already defines a driver-based device.
> - nfs and ssh, because the QEMU build script used by Proxmox VE does
>   not enable them.
> - blk{debug,verify}, because they are for debugging.
> - the ones used by blkio, i.e. io_uring, nvme-io_uring,
>   virtio-blk-vfio-pci, virtio-blk-vhost-user and
>   virtio-blk-vhost-vdpa, because the QEMU build script used by Proxmox
>   VE does not enable blkio.
> - backup-dump and zeroinit, because they should not be used by the
>   storage layer directly.
> - gluster, because support is dropped in Proxmox VE 9.
> - host_cdrom, because the storage layer should not access host CD-ROM
>   devices.
> - fat, because it hopefully isn't used by any third-party plugin here.
> 
> Co-developed-by: Alexandre Derumier <alexandre.derumier at groupe-cyllene.com>
> Signed-off-by: Fiona Ebner <f.ebner at proxmox.com>
> ---
> 
> New in v5.
> 
>  src/PVE/Storage/Plugin.pm | 95 ++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 94 insertions(+), 1 deletion(-)
> 
> diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
> index 3f2c638..c2f376b 100644
> --- a/src/PVE/Storage/Plugin.pm
> +++ b/src/PVE/Storage/Plugin.pm
> @@ -1961,6 +1961,34 @@ sub rename_volume {
>      return "${storeid}:${base}${target_vmid}/${target_volname}";
>  }
>  
> +my sub blockdev_options_nbd_tcp {
> +    my ($host, $port, $export) = @_;
> +
> +    die "blockdev_options_nbd_tcp: no host" if !defined($host);
> +
> +    my $blockdev = {};
> +
> +    my $server = { type => 'inet', host => "$host" };
> +    # port is also a string in QAPI, not optional, default for NBD is 10809
> +    $server->{port} = defined($port) ? "$port" : "10809";
> +    $blockdev = { driver => 'nbd', server => $server };
> +    $blockdev->{export} = "$export" if defined($export);
> +
> +    return $blockdev;
> +}
> +
> +my sub blockdev_options_nbd_unix {
> +    my ($socket_path, $export) = @_;
> +
> +    my $blockdev = {};
> +
> +    my $server = { type => 'unix', path => "$socket_path" };
> +    $blockdev = { driver => 'nbd', server => $server };
> +    $blockdev->{export} = "$export" if defined($export);
> +
> +    return $blockdev;
> +}
> +
>  =pod
>  
>  =head3 qemu_blockdev_options
> @@ -2031,7 +2059,7 @@ sub qemu_blockdev_options {
>  
>      my $blockdev = {};
>  
> -    my ($path) = $class->filesystem_path($scfg, $volname, $options->{'snapshot-name'});
> +    my ($path) = $class->path($scfg, $volname, $storeid, $options->{'snapshot-name'});
>  
>      if ($path =~ m|^/|) {
>          # For qcow2 and qed the path of a snapshot will be the same, but it's not possible to attach
> @@ -2046,6 +2074,71 @@ sub qemu_blockdev_options {
>          my $st = File::stat::stat($path) or die "stat for '$path' failed - $!\n";
>          my $driver = (S_ISCHR($st->mode) || S_ISBLK($st->mode)) ? 'host_device' : 'file';
>          $blockdev = { driver => $driver, filename => $path };
> +    } elsif ($path =~ m|^file:(\S+)|) {
> +        $blockdev = { driver => 'file', filename => "$1" };
> +    } elsif ($path =~ m|^host_device:(\S+)|) {
> +        $blockdev = { driver => 'host_device', filename => "$1" };
> +    } elsif ($path =~ m|^iscsi://(\S+)/(\S+)/(\d+)$|) {
> +        $blockdev =
> +            { driver => 'iscsi', portal => "$1", target => "$2", lun => "$3", transport => "tcp" };
> +    } elsif ($path =~ m|^iser://(\S+)/(\S+)/(\d+)$|) {
> +        $blockdev =
> +            { driver => 'iscsi', portal => "$1", target => "$2", lun => "$3", transport => "iser" };
> +    } elsif ($path =~ m|^nbd(?:\+tcp)?://(\S+?)(?::(\d+))?/(\S+)?$|) { # new style NBD TCP URI
> +        $blockdev = blockdev_options_nbd_tcp($1, $2, $3);
> +    } elsif ($path =~ m|^nbd(?:\+tcp)?:(\S+):(\d+)(?::exportname=(\S+))?$|) {
> +        # old style NBD TCP URI
> +        $blockdev = blockdev_options_nbd_tcp($1, $2, $3);
> +    } elsif ($path =~ m|^nbd\+unix:///(\S+)?\?socket=(\S+)$|) { # new style NBD unix URI
> +        $blockdev = blockdev_options_nbd_unix($2, $1); # note the order!
> +    } elsif ($path =~ m|^nbd:unix:(\S+?)(?::exportname=(\S+))?$|) { # old style NBD unix URI
> +        $blockdev = blockdev_options_nbd_unix($1, $2);
> +    } elsif ($path =~ m/^rbd:(\S+)$/) {
> +        my $rbd_options = $1;
> +        $blockdev->{driver} = 'rbd';
> +
> +        #map options to key=value pair (if not key is provided, this is the image)
> +        #options are seprated with : but we need to exclude \: used for ipv6 address
> +        my $options = {
> +            map {
> +                s/\\:/:/g;
> +                /^(.*?)=(.*)/ ? ($1 => $2) : (image => $_)
> +            } $rbd_options =~ /(?:\\:|\[[^\]]*\]|[^:\\])+/g
> +        };
> +
> +        $blockdev->{'auth-client-required'} = [$options->{'auth_supported'}]
> +            if $options->{'auth_supported'};
> +        $blockdev->{'conf'} = $options->{'conf'} if $options->{'conf'};
> +        $blockdev->{'user'} = $options->{'id'} if $options->{'id'};
> +
> +        if ($options->{'mon_host'}) {
> +            my $server = [];
> +            my @mons = split(';', $options->{'mon_host'});
> +            for my $mon (@mons) {
> +                $mon =~ s/[\[\]]//g;
> +                my ($host, $port) = PVE::Tools::parse_host_and_port($mon);
> +                $port = '3300' if !$port;
> +                push @$server, { host => $host, port => $port };
> +            }
> +            $blockdev->{server} = $server;
> +        }
> +
> +        if ($options->{'image'} =~ m|^(\S+)/(\S+)$|) {
> +            $blockdev->{pool} = $1;
> +            $blockdev->{image} = $2;
> +            if ($blockdev->{image} =~ m|^(\S+)/(\S+)$|) {
> +                $blockdev->{namespace} = $1;
> +                $blockdev->{image} = $2;
> +            }
> +        }
> +
> +        delete($options->@{qw(auth_supported conf id mon_host image)});
> +
> +        # Map rest directly. With -drive, it was possible to use arbitrary key-value-pairs. Like
> +        # this, there will be warnings for those that are not allowed via blockdev.
> +        for my $opt (keys $options->%*) {
> +            $blockdev->{$opt} = $options->{$opt};
> +        }
>      } else {
>          die "storage plugin doesn't implement qemu_blockdev_options() method\n";
>      }
> -- 
> 2.47.2
> 
> 
> 
> _______________________________________________
> pve-devel mailing list
> pve-devel at lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
> 
>




More information about the pve-devel mailing list