[pve-devel] [PATCH 2/2] Add plugin for Netapp storage over multipath media.

Dmitry Petuhov mityapetuhov at gmail.com
Sat Aug 6 09:23:16 CEST 2016


Signed-off-by: Dmitry Petuhov <mityapetuhov at gmail.com>
---
 PVE/Storage.pm                |   2 +
 PVE/Storage/MPNetappPlugin.pm | 602 ++++++++++++++++++++++++++++++++++++++++++
 PVE/Storage/Makefile          |   2 +-
 PVE/Storage/Plugin.pm         |   2 +-
 control.in                    |   2 +-
 5 files changed, 607 insertions(+), 3 deletions(-)
 create mode 100644 PVE/Storage/MPNetappPlugin.pm

diff --git a/PVE/Storage.pm b/PVE/Storage.pm
index 25ff545..59bbc49 100755
--- a/PVE/Storage.pm
+++ b/PVE/Storage.pm
@@ -32,6 +32,7 @@ use PVE::Storage::GlusterfsPlugin;
 use PVE::Storage::ZFSPoolPlugin;
 use PVE::Storage::ZFSPlugin;
 use PVE::Storage::DRBDPlugin;
+use PVE::Storage::MPNetappPlugin;
 
 # load and initialize all plugins
 PVE::Storage::DirPlugin->register();
@@ -46,6 +47,7 @@ PVE::Storage::GlusterfsPlugin->register();
 PVE::Storage::ZFSPoolPlugin->register();
 PVE::Storage::ZFSPlugin->register();
 PVE::Storage::DRBDPlugin->register();
+PVE::Storage::MPNetappPlugin->register();
 PVE::Storage::Plugin->init();
 
 my $UDEVADM = '/sbin/udevadm';
diff --git a/PVE/Storage/MPNetappPlugin.pm b/PVE/Storage/MPNetappPlugin.pm
new file mode 100644
index 0000000..d1a80af
--- /dev/null
+++ b/PVE/Storage/MPNetappPlugin.pm
@@ -0,0 +1,602 @@
+package PVE::Storage::MPNetappPlugin;
+
+use strict;
+use warnings;
+use Data::Dumper;
+use IO::File;
+use PVE::Tools qw(run_command trim file_read_firstline dir_glob_foreach);
+use PVE::Storage::Plugin;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use LWP::UserAgent;
+use HTTP::Request;
+use XML::Simple;
+
+use base qw(PVE::Storage::Plugin);
+
+sub netapp_request {
+    my ($scfg, $vserver, $params) = @_;
+
+	my $vfiler = $vserver ? "vfiler='$vserver'" : "";
+
+	my $content = "<?xml version='1.0' encoding='UTF-8' ?>\n";
+	$content .= "<!DOCTYPE netapp SYSTEM 'file:/etc/netapp_filer.dtd'>\n";
+	$content .= "<netapp $vfiler version='1.19' xmlns='http://www.netapp.com/filer/admin'>\n";
+	$content .= $params;
+	$content .= "</netapp>\n";
+	my $url = "http://".$scfg->{adminserver}."/servlets/netapp.servlets.admin.XMLrequest_filer";
+	my $request = HTTP::Request->new('POST',"$url");
+	$request->authorization_basic($scfg->{login},$scfg->{password});
+
+	$request->content($content);
+	$request->content_length(length($content));
+	my $ua = LWP::UserAgent->new;
+	my $response = $ua->request($request);
+	my $xmlparser = XML::Simple->new( KeepRoot => 1 );
+	my $xmlresponse = $xmlparser->XMLin($response->{_content});
+
+	if(ref $xmlresponse->{netapp}->{results} eq 'ARRAY'){
+	    foreach my $result (@{$xmlresponse->{netapp}->{results}}) {
+		if($result->{status} ne 'passed'){
+		    die "netapp api error : ".$result->{reason};
+		}
+	    }
+	}
+	elsif ($xmlresponse->{netapp}->{results}->{status} ne 'passed') {
+	    die "netapp api error : ".$content.$xmlresponse->{netapp}->{results}->{reason};
+	}
+
+	return $xmlresponse;
+}
+
+sub netapp_build_params {
+    my ($execute, %params) = @_;
+
+    my $xml = "<$execute>\n";
+    while (my ($property, $value) = each(%params)){
+	$xml.="<$property>$value</$property>\n";
+    }
+    $xml.="</$execute>\n";
+
+    return $xml;
+
+}
+
+sub _name2vol {
+	my ($vol) = @_;
+	$vol =~ s/-/_/g;
+	$vol .= '_vol';
+	return $vol;
+}
+
+
+sub netapp_create_volume {
+    my ($scfg, $name, $size) = @_;
+
+    my $volume = _name2vol($name);
+
+    my $aggregate = $scfg->{aggregate};
+    my $xmlparams = ($scfg->{api} == 8 && $scfg->{vserver})?netapp_build_params("volume-create", "containing-aggr-name" => $aggregate, "volume" => $volume, "size" => "$size", "junction-path" => "/images/$volume", "space-reserve" => "none"):
+		    netapp_build_params("volume-create", "containing-aggr-name" => $aggregate, "volume" => $volume, "size" => "$size", "space-reserve" => "none");
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+
+}
+
+sub netapp_sisenable_volume {
+    my ($scfg, $name) = @_;
+
+    my $volume = _name2vol($name);
+    my $xmlparams = netapp_build_params("sis-enable", "path" => "/vol/$volume");
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_sissetconfig_volume {
+    my ($scfg, $name) = @_;
+
+    my $volume = _name2vol($name);
+    my $xmlparams = netapp_build_params("sis-set-config", "enable-compression" => "true", "enable-inline-compression" => "false", "schedule" => "-", "path" => "/vol/$volume");
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_autosize_volume {
+    my ($scfg, $name) = @_;
+
+    my $volume = _name2vol($name);
+    my $xmlparams = ($scfg->{api} == 8)?
+		netapp_build_params('volume-autosize-set', 'mode' => 'grow_shrink', 'volume' => _name2vol($name)):
+		netapp_build_params('volume-autosize-set', 'is-enabled' => 'true', 'volume' => _name2vol($name));
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_snapshotsetreserve_volume {
+    my ($scfg, $name) = @_;
+
+    my $volume = _name2vol($name);
+    my $xmlparams = netapp_build_params("snapshot-set-reserve", "volume" => _name2vol($name), "percentage" => "0");
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_resize_volume {
+    my ($scfg, $name, $size) = @_;
+
+    netapp_request($scfg, $scfg->{vserver}, netapp_build_params("volume-size", "volume" => _name2vol($name), "new-size" => "$size" ));
+}
+
+sub netapp_snapshot_create {
+    my ($scfg, $name, $snapname) = @_;
+
+    netapp_request($scfg, $scfg->{vserver}, netapp_build_params("snapshot-create", "volume" => _name2vol($name), "snapshot" => "$snapname" ));
+}
+
+sub netapp_snapshot_exist {
+    my ($scfg, $name, $snap) = @_;
+
+    my $volume = _name2vol($name);
+    my $snapshotslist = netapp_request($scfg, $scfg->{vserver}, netapp_build_params("snapshot-list-info", "volume" => _name2vol($name)));
+    my $snapshotexist = undef;
+    $snapshotexist = 1 if (defined($snapshotslist->{"netapp"}->{"results"}->{"snapshots"}->{"snapshot-info"}->{"$snap"}));
+    $snapshotexist = 1 if (defined($snapshotslist->{netapp}->{results}->{"snapshots"}->{"snapshot-info"}->{name}) && $snapshotslist->{netapp}->{results}->{"snapshots"}->{"snapshot-info"}->{name} eq $snap);
+    return $snapshotexist;
+}
+
+sub netapp_snapshot_rollback {
+    my ($scfg, $name, $snapname) = @_;
+
+    netapp_request($scfg, $scfg->{vserver}, netapp_build_params("snapshot-restore-volume", "volume" => _name2vol($name), "snapshot" => "$snapname" ));
+}
+
+sub netapp_snapshot_delete {
+    my ($scfg, $name, $snapname) = @_;
+
+    netapp_request($scfg, $scfg->{vserver}, netapp_build_params("snapshot-delete", "volume" => _name2vol($name), "snapshot" => "$snapname" ));
+}
+
+sub netapp_unmount_volume {
+    my ($scfg, $name) = @_;
+
+    my $xmlparams = netapp_build_params("volume-unmount", "volume-name" => _name2vol($name), "force" => "true");
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_offline_volume {
+    my ($scfg, $name) = @_;
+
+    my $xmlparams = netapp_build_params("volume-offline", "name" => _name2vol($name));
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_destroy_volume {
+    my ($scfg, $name) = @_;
+
+    my $xmlparams = netapp_build_params("volume-destroy", "name" => _name2vol($name));
+    netapp_request($scfg, $scfg->{vserver}, $xmlparams);
+}
+
+sub netapp_list_luns {
+    my ($scfg, $vmid) = @_;
+    my $list = {};
+
+    if ($scfg->{api} == 8) {
+	my $xmlresponse = netapp_request($scfg, $scfg->{'vserver'},
+		'<lun-get-iter><desired-attributes><lun-attributes><path></path><serial-number></serial-number>'.
+		'<size></size><state></state><mapped></mapped></lun-attributes></desired-attributes>'.
+		'<max-records>5000</max-records></lun-get-iter>');
+
+	# For first LUN there may be not-array reference. So, turn it into single-element array.
+	my $luns = (ref($xmlresponse->{'netapp'}->{'results'}->{'attributes-list'}->{'lun-attributes'}) eq 'ARRAY')?
+		    $xmlresponse->{'netapp'}->{'results'}->{'attributes-list'}->{'lun-attributes'}:
+		    [$xmlresponse->{'netapp'}->{'results'}->{'attributes-list'}->{'lun-attributes'}];
+
+	foreach my $lun (@$luns) {
+	    next unless $lun->{'path'} =~  m!/vol/(\S+)/(vm-(\d+)-disk-\d+)$!;
+	    next if defined($vmid) and $3 != $vmid;
+
+	    my $name = $2;
+	    $list->{$name}->{'vol'} = $1;
+	    # Get wwn from LUN's serial number
+	    $list->{$name}->{'wwn'} = $lun->{'serial-number'};
+	    $list->{$name}->{'wwn'} =~ s/(.)/sprintf("%x",ord($1))/eg; #convert to hex
+	    $list->{$name}->{'wwn'} = '60a98000' . $list->{$name}->{'wwn'}; # Add netapp-specific prefix
+	    $list->{$name}->{'path'} = $lun->{'path'};
+	    $list->{$name}->{'size'} = $lun->{'size'};
+	    $list->{$name}->{'online'} = ($lun->{'state'} eq 'online')?'true':'false';
+	    $list->{$name}->{'mapped'} = $lun->{'mapped'};
+	}
+    } elsif ($scfg->{api} == 7) {
+
+	my $xmlresponse = netapp_request($scfg, $scfg->{'vserver'}, netapp_build_params('lun-list-info'));
+
+	# For first LUN there may be not-array reference. So, turn it into single-element array.
+	my $luns = (ref($xmlresponse->{'netapp'}->{'results'}->{'luns'}->{'lun-info'}) eq 'ARRAY')?
+		    $xmlresponse->{'netapp'}->{'results'}->{'luns'}->{'lun-info'}:
+		    [$xmlresponse->{'netapp'}->{'results'}->{'luns'}->{'lun-info'}];
+
+	foreach my $lun (@$luns) {
+	    next unless $lun->{'path'} =~  m!/vol/(\S+)/(vm-(\d+)-disk-\d+)$!;
+	    next if defined($vmid) and $3 != $vmid;
+
+	    my $name = $2;
+	    $list->{$name}->{'vol'} = $1;
+	    # Get wwn from LUN's serial number
+	    $list->{$name}->{'wwn'} = $lun->{'serial-number'};
+	    $list->{$name}->{'wwn'} =~ s/(.)/sprintf("%x",ord($1))/eg; #convert to hex
+	    $list->{$name}->{'wwn'} = '60a98000' . $list->{$name}->{'wwn'}; # Add netapp-specific prefix
+	    $list->{$name}->{'path'} = $lun->{'path'};
+	    $list->{$name}->{'size'} = $lun->{'size'};
+	    $list->{$name}->{'online'} = $lun->{'online'};
+	    $list->{$name}->{'mapped'} = $lun->{'mapped'};
+	}
+    }
+
+    return $list;
+}
+
+sub netapp_create_lun {
+    my ($scfg, $name, $size) = @_;
+
+    my $vol = _name2vol($name);
+    my $xmlparams = ($scfg->{api} == 8)?
+		netapp_build_params('lun-create-by-size', 'ostype' => 'linux', 'path' => "/vol/$vol/$name" , 'size' => $size,
+			'space-allocation-enabled' => 'true', 'space-reservation-enabled' => 'false'):
+		netapp_build_params('lun-create-by-size', 'ostype' => 'linux', 'path' => "/vol/$vol/$name" , 'size' => $size,
+			'space-reservation-enabled' => 'false');
+
+    netapp_request($scfg, $scfg->{'vserver'}, $xmlparams);
+}
+
+sub netapp_map_lun {
+    my ($scfg, $name) = @_;
+
+    my $vol = _name2vol($name);
+    my $xmlparams = netapp_build_params('lun-map', 'initiator-group' => $scfg->{igroup}, 'path' => "/vol/$vol/$name");
+
+    netapp_request($scfg, $scfg->{'vserver'}, $xmlparams);
+}
+
+sub netapp_resize_lun {
+    my ($scfg, $name, $newsize) = @_;
+
+    my $vol = _name2vol($name);
+    my $xmlparams = netapp_build_params('lun-resize', 'path' => "/vol/$vol/$name", 'size' => $newsize);
+    netapp_request($scfg, $scfg->{'vserver'}, $xmlparams);
+}
+
+sub netapp_unmap_lun {
+    my ($scfg, $name) = @_;
+
+    my $vol = _name2vol($name);
+    my $xmlparams = netapp_build_params('lun-unmap', 'initiator-group' => $scfg->{'igroup'}, 'path' => "/vol/$vol/$name");
+
+    netapp_request($scfg, $scfg->{'vserver'}, $xmlparams);
+}
+
+sub netapp_delete_lun {
+    my ($scfg, $name) = @_;
+    my $vol = _name2vol($name);
+
+    # First, get lun offline
+    netapp_request($scfg, $scfg->{'vserver'}, netapp_build_params('lun-offline', 'path' => "/vol/$vol/$name"));
+
+    # Then delete it
+    netapp_request($scfg, $scfg->{'vserver'}, netapp_build_params('lun-destroy', 'path' => "/vol/$vol/$name"));
+}
+
+sub netapp_aggregate_size {
+    my ($scfg) = @_;
+
+    if ($scfg->{api} == 8) {
+	my $list = netapp_request($scfg, undef, netapp_build_params("aggr-get-iter", "desired-attributes" => "" ));
+
+        foreach my $aggregate (@{$list->{netapp}->{results}->{"attributes-list"}->{"aggr-attributes"}}) {
+	    if($aggregate->{"aggregate-name"} eq $scfg->{aggregate}){
+	        my $used = $aggregate->{"aggr-space-attributes"}->{"size-used"};
+	        my $total = $aggregate->{"aggr-space-attributes"}->{"size-total"};
+	        my $free = $aggregate->{"aggr-space-attributes"}->{"size-available"};
+	        return ($total, $free, $used, 1);
+	    }
+	}
+    } elsif ($scfg->{api} == 7) {
+	my $xmlresponse = netapp_request($scfg, undef, netapp_build_params("aggr-space-list-info"));
+
+        foreach my $aggregate (@{$xmlresponse->{netapp}->{results}->{"aggregates"}->{"aggr-space-info"}}) {
+	    if($aggregate->{"aggregate-name"} eq $scfg->{aggregate}){
+		my $used = $aggregate->{"size-used"};
+		my $total = $aggregate->{"size-nominal"};
+		my $free = $aggregate->{"size-free"};
+		return [$total, $free, $used, 1];
+	    }
+	}
+    }
+}
+
+# Utility functions
+sub mp_get_name {
+    my ($class, $scfg, $wwn) = @_;
+
+    my $luns = netapp_list_luns($scfg, undef);
+
+    foreach my $name (keys %$luns) {
+	return $name if $luns->{$name}->{'wwn'} eq $wwn;
+    }
+    die "cannot get name for wwn $wwn\n";
+}
+
+sub mp_get_wwn {
+    my ($class, $scfg, $name) = @_;
+
+    my $luns = netapp_list_luns($scfg, undef);
+
+    return $luns->{$name}->{'wwn'};
+}
+
+# Configuration
+
+sub type {
+    return 'mpnetapp';
+}
+
+sub plugindata {
+    return {
+	content => [ {images => 1, rootdir => 1, none => 1}, { images => 1 }],
+    };
+}
+
+sub properties {
+    return {
+        vserver => {
+            description => "Vserver name.",
+            type => 'string',
+        },
+        aggregate => {
+            description => "Array/Pool/Aggregate name.",
+            type => 'string',
+        },
+	adminserver => {
+	    description => "Management IP or DNS name of storage.",
+	    type => 'string', format => 'pve-storage-server',
+	},
+	login => {
+	    description => "login",
+	    type => 'string',
+	},
+	password => {
+	    description => "password",
+	    type => 'string',
+	},
+	igroup => {
+	    description => "Initiator group name",
+	    type => 'string',
+	},
+	api => {
+	    description => "API version (7 or 8)",
+	    type => 'string',
+	},
+    };
+}
+
+sub options {
+    return {
+	adminserver => { fixed => 1 },
+	login => { fixed => 1 },
+	password => { optional => 1 },
+	vserver => { optional => 1 },
+	aggregate => { fixed => 1 },
+        nodes => { optional => 1 },
+	disable => { optional => 1 },
+	content => { optional => 1 },
+	igroup => { optional => 1 },
+	api => { optional => 1 },
+    }
+}
+
+# Storage implementation
+
+sub parse_volname {
+    my ($class, $volname) = @_;
+
+    if ($volname =~ m/vm-(\d+)-disk-\S+/) {
+	return ('images', $volname, $1, undef, undef, undef, 'raw');
+    } else {
+	die "Invalid volume $volname";
+    }
+}
+
+sub filesystem_path {
+    my ($class, $scfg, $volname, $snapname) = @_;
+
+    die "Direct attached device snapshot is not implemented" if defined($snapname);
+
+    my ($vtype, $name, $vmid) = $class->parse_volname($volname);
+    die "Cannot find WWN for volume $volname" unless my $wwn = $class->mp_get_wwn($scfg, $name);
+
+    my $path = "/dev/disk/by-id/dm-uuid-mpath-0x$wwn";
+
+    return wantarray ? ($path, $vmid, $vtype) : $path;
+}
+
+sub create_base {
+    my ($class, $storeid, $scfg, $volname) = @_;
+
+    die "Creating base image is currently unimplemented";
+}
+
+sub clone_image {
+    my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
+
+    die "Cloning image is currently unimplemented";
+}
+
+# Seems like this method gets size in kilobytes somehow,
+# while listing methost return bytes. That's strange.
+sub alloc_image {
+    my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
+
+    my $luns = netapp_list_luns($scfg, $vmid);
+
+    unless ($name) {
+	for (my $i = 1; $i < 100; $i++) {
+	    if (!$luns->{"vm-$vmid-disk-$i"}) {
+		$name = "vm-$vmid-disk-$i";
+		last;
+	    }
+	}
+    }
+
+    # Netapp's GUI reserves 5% for snapshots by default, reproduce it.
+    netapp_create_volume($scfg, $name, int($size*1.05)*1024);
+    netapp_sisenable_volume($scfg, $name);
+    netapp_sissetconfig_volume($scfg, $name);
+    netapp_autosize_volume($scfg, $name);
+    netapp_snapshotsetreserve_volume($scfg, $name);
+    netapp_create_lun($scfg, $name, $size*1024);
+
+    netapp_map_lun($scfg, $name);
+
+    return $name;
+}
+
+sub free_image {
+    my ($class, $storeid, $scfg, $volname, $isBase) = @_;
+
+    my $wwn = $class->mp_get_wwn($scfg, $volname);
+
+    netapp_unmap_lun($scfg, $volname);
+
+    netapp_delete_lun($scfg, $volname);
+    netapp_unmount_volume($scfg,$volname) if ($scfg->{api} == 8 && $scfg->{vserver});
+    netapp_offline_volume($scfg,$volname);
+    netapp_destroy_volume($scfg,$volname);
+
+    dir_glob_foreach('/etc/pve/nodes', '\w+', sub {
+	my ($node) = @_;
+	run_command(['/usr/bin/ssh', $node, '/usr/sbin/scsi-scan.sh --remove-offline']);
+    });
+}
+
+sub list_images {
+    my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
+
+    my $res = [];
+
+    my $luns = netapp_list_luns($scfg, $vmid);
+
+    foreach my $name (keys %$luns) {
+
+	my $volid = "$storeid:$name";
+
+	if ($vollist) {
+	    my $found = grep { $_ eq $volid } @$vollist;
+	    next if !$found;
+	} else {
+	    next if defined($vmid);
+	}
+
+	push @$res, {
+	    'volid' => $volid, 'format' => 'raw', 'size' => $luns->{$name}->{'size'},
+	};
+
+    }
+
+    return $res;
+}
+
+sub status {
+    my ($class, $storeid, $scfg, $cache) = @_;
+
+    return @{netapp_aggregate_size($scfg)};
+}
+
+sub activate_storage {
+    my ($class, $storeid, $scfg, $cache) = @_;
+
+    # Server's SCSI subsystem is always up, so there's nothing to do
+    return 1;
+}
+
+sub deactivate_storage {
+    my ($class, $storeid, $scfg, $cache) = @_;
+
+    return 1;
+}
+
+sub activate_volume {
+    my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
+
+    die "volume snapshot [de]activation not possible on multipath device" if $snapname;
+
+    warn "Activating '$volname'\n";
+    $class->multipath_enable($scfg, $class->mp_get_wwn($scfg,$volname));
+
+    return 1;
+}
+
+sub deactivate_volume {
+    my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
+
+    die "volume snapshot [de]activation not possible on multipath device" if $snapname;
+
+    warn "Deactivating '$volname'\n";
+    $class->multipath_disable($scfg, $class->mp_get_wwn($scfg,$volname));
+
+    return 1;
+}
+
+sub volume_resize {
+    my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
+
+    netapp_resize_volume($scfg, $volname, $size);
+    netapp_resize_lun($scfg, $volname, $volname, $size);
+
+    my $wwn = $class->mp_get_wwn($scfg,$volname);
+    run_command(['/usr/sbin/scsi-scan.sh', '--rescan-wwid', "0x$wwn"]);
+    return 1;
+}
+
+sub volume_snapshot {
+    my ($class, $scfg, $storeid, $volname, $snap) = @_;
+
+    netapp_snapshot_create($scfg, $volname, $snap);
+    return 1;
+}
+
+sub volume_snapshot_rollback {
+    my ($class, $scfg, $storeid, $volname, $snap) = @_;
+
+    netapp_snapshot_rollback($scfg, $volname, $snap);
+
+    #size could be changed here? Check for device changes.
+    my $wwn = $class->mp_get_wwn($scfg,$volname);
+    run_command(['/usr/sbin/scsi-scan.sh', '--rescan-wwid', "0x$wwn"]);
+    return 1;
+}
+
+sub volume_snapshot_delete {
+    my ($class, $scfg, $storeid, $volname, $snap) = @_;
+
+    netapp_snapshot_delete($scfg, $volname, $snap);
+    return 1;
+}
+
+sub volume_has_feature {
+    my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
+
+    my $features = {
+	snapshot => { current => 1, snap => 1 },
+	sparseinit => { current => 1 },
+    };
+
+    my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
+	$class->parse_volname($volname);
+
+    my $key = undef;
+    if($snapname) {
+	$key = 'snap';
+    } else {
+	$key =  $isBase ? 'base' : 'current';
+    }
+    return 1 if $features->{$feature}->{$key};
+
+    return undef;
+}
+
+1;
diff --git a/PVE/Storage/Makefile b/PVE/Storage/Makefile
index b924f21..3ac8518 100644
--- a/PVE/Storage/Makefile
+++ b/PVE/Storage/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Plugin.pm DirPlugin.pm LVMPlugin.pm NFSPlugin.pm ISCSIPlugin.pm RBDPlugin.pm SheepdogPlugin.pm ISCSIDirectPlugin.pm GlusterfsPlugin.pm ZFSPoolPlugin.pm ZFSPlugin.pm DRBDPlugin.pm LvmThinPlugin.pm
+SOURCES=Plugin.pm DirPlugin.pm LVMPlugin.pm NFSPlugin.pm ISCSIPlugin.pm RBDPlugin.pm SheepdogPlugin.pm ISCSIDirectPlugin.pm GlusterfsPlugin.pm ZFSPoolPlugin.pm ZFSPlugin.pm DRBDPlugin.pm LvmThinPlugin.pm MPNetappPlugin.pm
 
 .PHONY: install
 install:
diff --git a/PVE/Storage/Plugin.pm b/PVE/Storage/Plugin.pm
index e4f1a08..82d6cce 100644
--- a/PVE/Storage/Plugin.pm
+++ b/PVE/Storage/Plugin.pm
@@ -369,7 +369,7 @@ sub parse_config {
 	    $d->{content} = $def->{content}->[1] if !$d->{content};
 	}
 
-	if ($type eq 'iscsi' || $type eq 'nfs' || $type eq 'rbd' || $type eq 'sheepdog' || $type eq 'iscsidirect' || $type eq 'glusterfs' || $type eq 'zfs' || $type eq 'drbd') {
+	if ($type eq 'iscsi' || $type eq 'nfs' || $type eq 'rbd' || $type eq 'sheepdog' || $type eq 'iscsidirect' || $type eq 'glusterfs' || $type eq 'zfs' || $type eq 'drbd' || $type eq 'mpnetapp') {
 	    $d->{shared} = 1;
 	}
     }
diff --git a/control.in b/control.in
index 9722be7..3cdaab4 100644
--- a/control.in
+++ b/control.in
@@ -3,7 +3,7 @@ Version: @@VERSION@@-@@PKGRELEASE@@
 Section: perl
 Priority: optional
 Architecture: @@ARCH@@
-Depends: perl (>= 5.6.0-16), nfs-common, udev, libpve-common-perl, lvm2, thin-provisioning-tools, libfile-chdir-perl, glusterfs-client (>= 3.4.0-2), cstream, libnet-dbus-perl, sg3-utils, multipath-tools
+Depends: perl (>= 5.6.0-16), nfs-common, udev, libpve-common-perl, lvm2, thin-provisioning-tools, libfile-chdir-perl, glusterfs-client (>= 3.4.0-2), cstream, libnet-dbus-perl, sg3-utils, multipath-tools, libxml-simple-perl
 Maintainer: Proxmox Support Team <support at proxmox.com>
 Description: Proxmox VE storage management library
  This package contains the storage management library used by Proxmox VE.
-- 
2.1.4




More information about the pve-devel mailing list