use PVE::Storage::DRBDPlugin;
# Storage API version. Icrement it on changes in storage API interface.
-use constant APIVER => 1;
+use constant APIVER => 2;
+# 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;
# load standard plugins
PVE::Storage::DirPlugin->register();
eval {
require $file;
+
+ # Check perl interface:
+ die "not derived from PVE::Storage::Plugin\n"
+ if !$modname->isa('PVE::Storage::Plugin');
+ die "does not provide an api() method\n"
+ if !$modname->can('api');
+ # Check storage API version and that file is really storage plugin.
+ my $version = $modname->api();
+ die "implements an API version newer than current ($version > " . APIVER . ")\n"
+ if $version > APIVER;
+ my $min_version = (APIVER - APIAGE);
+ die "API version too old, please update the plugin ($version < $min_version)\n"
+ if $version < $min_version;
+ import $file;
+ $modname->register();
+
+ # If we got this far and the API version is not the same, make some
+ # noise:
+ warn "Plugin \"$modname\" is implementing an older storage API, an upgrade is recommended\n"
+ if $version != APIVER;
};
if ($@) {
- warn $@;
- # Check storage API version and that file is really storage plugin.
- } elsif ($modname->isa('PVE::Storage::Plugin') && $modname->can('api') && $modname->api() == APIVER) {
- eval {
- import $file;
- $modname->register();
- };
- warn $@ if $@;
- } else {
- warn "Error loading storage plugin \"$modname\" because of API version mismatch. Please, update it.\n"
+ warn "Error loading storage plugin \"$modname\": $@";
}
});
}
if ($sid) {
my ($vtype, undef, $ownervm) = parse_volname($cfg, $volid);
if ($vtype eq 'iso' || $vtype eq 'vztmpl') {
- # we simply allow access
+ # require at least read access to storage, (custom) templates/ISOs could be sensitive
+ $rpcenv->check_any($user, "/storage/$sid", ['Datastore.AllocateSpace', 'Datastore.Audit']);
} elsif (defined($ownervm) && defined($vmid) && ($ownervm == $vmid)) {
# we are owner - allow access
} elsif ($vtype eq 'backup' && $ownervm) {
});
}
+sub map_volume {
+ my ($cfg, $volid, $snapname) = @_;
+
+ my ($storeid, $volname) = parse_volume_id($volid);
+
+ my $scfg = storage_config($cfg, $storeid);
+
+ my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+
+ return $plugin->map_volume($storeid, $scfg, $volname, $snapname);
+}
+
+sub unmap_volume {
+ my ($cfg, $volid, $snapname) = @_;
+
+ my ($storeid, $volname) = parse_volume_id($volid);
+
+ my $scfg = storage_config($cfg, $storeid);
+
+ my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+
+ return $plugin->unmap_volume($storeid, $scfg, $volname, $snapname);
+}
+
sub vdisk_alloc {
my ($cfg, $storeid, $vmid, $fmt, $name, $size) = @_;
$rpcenv->fork_worker('imgdel', undef, $authuser, $cleanup_worker);
}
+# lists all files in the snippets directory
+sub snippets_list {
+ my ($cfg, $storeid) = @_;
+
+ my $ids = $cfg->{ids};
+
+ storage_check_enabled($cfg, $storeid) if ($storeid);
+
+ my $res = {};
+
+ foreach my $sid (keys %$ids) {
+ next if $storeid && $storeid ne $sid;
+ next if !storage_check_enabled($cfg, $sid, undef, 1);
+
+ my $scfg = $ids->{$sid};
+ next if !$scfg->{content}->{snippets};
+
+ activate_storage($cfg, $sid);
+
+ if ($scfg->{path}) {
+ my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
+ my $path = $plugin->get_subdir($scfg, 'snippets');
+
+ foreach my $fn (<$path/*>) {
+ next if -d $fn;
+
+ push @{$res->{$sid}}, {
+ volid => "$sid:snippets/". basename($fn),
+ format => 'snippet',
+ size => -s $fn,
+ };
+ }
+ }
+
+ if ($res->{$sid}) {
+ @{$res->{$sid}} = sort {$a->{volid} cmp $b->{volid} } @{$res->{$sid}};
+ }
+ }
+
+ return $res;
+}
+
#list iso or openvz template ($tt = <iso|vztmpl|backup>)
sub template_list {
my ($cfg, $storeid, $tt) = @_;
sub volume_list {
my ($cfg, $storeid, $vmid, $content) = @_;
- my @ctypes = qw(images vztmpl iso backup);
+ my @ctypes = qw(images vztmpl iso backup snippets);
my $cts = $content ? [ $content ] : [ @ctypes ];
@{$data->{$storeid}} = grep { $_->{volid} =~ m/\S+-$vmid-\S+/ } @{$data->{$storeid}};
}
}
+ } elsif ($ct eq 'snippets') {
+ $data = snippets_list($cfg, $storeid);
}
next if !$data || !$data->{$storeid};
raise_param_exc({ portal => "unable to resolve portal address '$portal'" });
}
-# idea is from usbutils package (/usr/bin/usb-devices) script
-sub __scan_usb_device {
- my ($res, $devpath, $parent, $level) = @_;
-
- return if ! -d $devpath;
- return if $level && $devpath !~ m/^.*[-.](\d+)$/;
- my $port = $level ? int($1 - 1) : 0;
-
- my $busnum = int(file_read_firstline("$devpath/busnum"));
- my $devnum = int(file_read_firstline("$devpath/devnum"));
-
- my $d = {
- port => $port,
- level => $level,
- busnum => $busnum,
- devnum => $devnum,
- speed => file_read_firstline("$devpath/speed"),
- class => hex(file_read_firstline("$devpath/bDeviceClass")),
- vendid => file_read_firstline("$devpath/idVendor"),
- prodid => file_read_firstline("$devpath/idProduct"),
- };
-
- if ($level) {
- my $usbpath = $devpath;
- $usbpath =~ s|^.*/\d+\-||;
- $d->{usbpath} = $usbpath;
- }
-
- my $product = file_read_firstline("$devpath/product");
- $d->{product} = $product if $product;
-
- my $manu = file_read_firstline("$devpath/manufacturer");
- $d->{manufacturer} = $manu if $manu;
-
- my $serial => file_read_firstline("$devpath/serial");
- $d->{serial} = $serial if $serial;
-
- push @$res, $d;
-
- foreach my $subdev (<$devpath/$busnum-*>) {
- next if $subdev !~ m|/$busnum-[0-9]+(\.[0-9]+)*$|;
- __scan_usb_device($res, $subdev, $devnum, $level + 1);
- }
-
-};
-
-sub scan_usb {
-
- my $devlist = [];
-
- foreach my $device (</sys/bus/usb/devices/usb*>) {
- __scan_usb_device($devlist, $device, 0, 0);
- }
-
- return $devlist;
-}
sub scan_iscsi {
my ($portal_in) = @_;
sub complete_content_type {
my ($cmdname, $pname, $cvalue) = @_;
- return [qw(rootdir images vztmpl iso backup)];
+ return [qw(rootdir images vztmpl iso backup snippets)];
}
sub complete_volume {
}
# Apply per-storage limits - if there are storages involved.
- if (@$storage_list) {
+ if (defined($storage_list) && @$storage_list) {
my $config = config();
# The Datastore.Allocate permission allows us to modify the per-storage