From: Michael Rasmussen <m...@datanom.net> snapshot backup of CT broke general snapshot handling. Handle snapshot creation and deletion properly.
Signed-off-by: Michael Rasmussen <m...@datanom.net> --- PVE/Storage/FreeNASPlugin.pm | 1570 +++++++++++++++++++++--------------------- 1 file changed, 782 insertions(+), 788 deletions(-) diff --git a/PVE/Storage/FreeNASPlugin.pm b/PVE/Storage/FreeNASPlugin.pm index fee3d27..9fe64d6 100644 --- a/PVE/Storage/FreeNASPlugin.pm +++ b/PVE/Storage/FreeNASPlugin.pm @@ -58,7 +58,6 @@ sub freenas_request { $req->authorization_basic($scfg->{username}, $scfg->{password}); my $res = $ua->request($req); - #print Dumper($res); return $res->code unless $res->is_success; return $res->content; @@ -66,43 +65,43 @@ sub freenas_request { sub freenas_get_version { my ($scfg) = @_; - - my $response = freenas_request($scfg, 'GET', "system/version"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $info = decode_json($response); - $fullversion = $info->{fullversion}; - if ($fullversion =~ /^\w+-(\d+)\.(\d*)\.(\d*)/) { - my $minor = $2; - my $micro = $3; - - if ($minor) { - $minor = "0$minor" unless $minor > 9; - } else { - $minor = '00'; - } - - if ($micro) { - $micro = "0$micro" unless $micro > 9; - } else { - $micro = '00'; - } - - $version = "$1$minor$micro"; - } else { - $version = '90200'; - } + + my $response = freenas_request($scfg, 'GET', "system/version"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $info = decode_json($response); + $fullversion = $info->{fullversion}; + if ($fullversion =~ /^\w+-(\d+)\.(\d*)\.(\d*)/) { + my $minor = $2; + my $micro = $3; + + if ($minor) { + $minor = "0$minor" unless $minor > 9; + } else { + $minor = '00'; + } + + if ($micro) { + $micro = "0$micro" unless $micro > 9; + } else { + $micro = '00'; + } + + $version = "$1$minor$micro"; + } else { + $version = '90200'; + } } sub freenas_list_zvol { my ($scfg) = @_; - freenas_get_version($scfg) unless $version; - - my $response = freenas_request($scfg, 'GET', "storage/volume/$scfg->{pool}/zvols?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + freenas_get_version($scfg) unless $version; + + my $response = freenas_request($scfg, 'GET', "storage/volume/$scfg->{pool}/zvols?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; my $zvols = decode_json($response); - $response = freenas_request($scfg, 'GET', "storage/snapshot?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + $response = freenas_request($scfg, 'GET', "storage/snapshot?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; my $snapshots = decode_json($response); my $list = (); @@ -110,13 +109,13 @@ sub freenas_list_zvol { my $vmid; my $parent; foreach my $zvol (@$zvols) { - next unless $zvol->{name} =~ /^(base|vm)-(\d+)-disk-\d+$/; - $vmid = $2; - $parent = undef; - foreach my $snap (@$snapshots) { - next unless $snap->{name} eq "__base__$vmid"; - $parent = $snap->{filesystem} =~ /^$scfg->{pool}\/(.+)$/ ? $1 : undef; - } + next unless $zvol->{name} =~ /^(base|vm)-(\d+)-disk-\d+$/; + $vmid = $2; + $parent = undef; + foreach my $snap (@$snapshots) { + next unless $snap->{name} eq "__base__$vmid"; + $parent = $snap->{filesystem} =~ /^$scfg->{pool}\/(.+)$/ ? $1 : undef; + } $list->{$scfg->{pool}}->{$zvol->{name}} = { name => $zvol->{name}, size => $zvol->{volsize}, @@ -125,280 +124,280 @@ sub freenas_list_zvol { format => 'raw', }; if ($zvol->{name} =~ /^base-(.*)/) { - $hide->{"vm-$1"} = 1; + $hide->{"vm-$1"} = 1; } } delete @{$list->{$scfg->{pool}}}{keys %$hide}; - + return $list; } sub freenas_no_more_extents { - my ($scfg, $target) = @_; - - my $response = freenas_request($scfg, 'GET', "services/iscsi/targettoextent?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $extents = decode_json($response); - foreach my $extent (@$extents) { - return 0 if $extent->{iscsi_target} == $target; - } - - return 1; + my ($scfg, $target) = @_; + + my $response = freenas_request($scfg, 'GET', "services/iscsi/targettoextent?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $extents = decode_json($response); + foreach my $extent (@$extents) { + return 0 if $extent->{iscsi_target} == $target; + } + + return 1; } - + sub freenas_get_target { - my ($scfg, $vmid) = @_; - my $target = undef; - - my $response = freenas_request($scfg, 'GET', "services/iscsi/target?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $targets = decode_json($response); - foreach my $t (@$targets) { - if ($t->{iscsi_target_name} eq "vm-$vmid") { - $target = $t->{id}; - last; - } - } - - return $target; + my ($scfg, $vmid) = @_; + my $target = undef; + + my $response = freenas_request($scfg, 'GET', "services/iscsi/target?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $targets = decode_json($response); + foreach my $t (@$targets) { + if ($t->{iscsi_target_name} eq "vm-$vmid") { + $target = $t->{id}; + last; + } + } + + return $target; } sub freenas_create_target { - my ($scfg, $vmid, $valid_code) = @_; - my $data; - - freenas_get_version($scfg) unless $version; - - if ($version < 110000) { - $data = { - iscsi_target_alias => "vm-$vmid", - iscsi_target_name => "vm-$vmid", - }; - } elsif ($version < 110100) { - $data = { - iscsi_target_alias => "vm-$vmid", - iscsi_target_name => "vm-$vmid", - }; - } else { - die "FreeNAS-$version: Unsupported!"; - } - my $response = freenas_request($scfg, 'POST', "services/iscsi/target", encode_json($data), $valid_code); - if ($response =~ /^\d+$/) { - return freenas_get_target($scfg, $vmid) if $valid_code && $response == $valid_code; - die HTTP::Status::status_message($response); - } - my $target = decode_json($response); - - die "Creating target for 'vm-$vmid' failed" unless $target->{id}; - - return $target->{id}; + my ($scfg, $vmid, $valid_code) = @_; + my $data; + + freenas_get_version($scfg) unless $version; + + if ($version < 110000) { + $data = { + iscsi_target_alias => "vm-$vmid", + iscsi_target_name => "vm-$vmid", + }; + } elsif ($version < 110100) { + $data = { + iscsi_target_alias => "vm-$vmid", + iscsi_target_name => "vm-$vmid", + }; + } else { + die "FreeNAS-$version: Unsupported!"; + } + my $response = freenas_request($scfg, 'POST', "services/iscsi/target", encode_json($data), $valid_code); + if ($response =~ /^\d+$/) { + return freenas_get_target($scfg, $vmid) if $valid_code && $response == $valid_code; + die HTTP::Status::status_message($response); + } + my $target = decode_json($response); + + die "Creating target for 'vm-$vmid' failed" unless $target->{id}; + + return $target->{id}; } sub freenas_delete_target { - my ($scfg, $target) = @_; + my ($scfg, $target) = @_; - my $response = freenas_request($scfg, 'DELETE', "services/iscsi/target/$target"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $response = freenas_request($scfg, 'DELETE', "services/iscsi/target/$target"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub freenas_get_target_name { - my ($scfg, $volname) = @_; - my $name = undef; - - if ($volname =~ /^(vm|base)-(\d+)-/) { - $name = "vm-$2"; - return "$target_prefix\:$name"; - } - - return undef; + my ($scfg, $volname) = @_; + my $name = undef; + + if ($volname =~ /^(vm|base)-(\d+)-/) { + $name = "vm-$2"; + return "$target_prefix\:$name"; + } + + return undef; } sub freenas_create_target_group { - my ($scfg, $target, $valid_code) = @_; - my $data; - - freenas_get_version($scfg) unless $version; - - # Trying to create a target group which already exists will cause and internal - # server error so if creating an existing target group should be allowed (return - # existing target group number we must search prior to create - if ($valid_code && $valid_code == 409) { - my $tg = freenas_get_target_group($scfg, $target); - return $tg if $tg; - } - - if ($version < 110000) { - $data = { - iscsi_target => $target, - iscsi_target_authgroup => $scfg->{auth_group} ? $scfg->{auth_group} : undef, - iscsi_target_portalgroup => $scfg->{portal_group}, - iscsi_target_initiatorgroup => $scfg->{initiator_group}, - iscsi_target_authtype => $scfg->{auth_type} ? $scfg->{auth_type} : 'None', - iscsi_target_initialdigest => "Auto", - }; - } elsif ($version < 110100) { - $data = { - iscsi_target => $target, - iscsi_target_authgroup => $scfg->{auth_group} ? $scfg->{auth_group} : undef, - iscsi_target_portalgroup => $scfg->{portal_group}, - iscsi_target_initiatorgroup => $scfg->{initiator_group}, - iscsi_target_authtype => $scfg->{auth_type} ? $scfg->{auth_type} : 'None', - iscsi_target_initialdigest => "Auto", - }; - } else { - die "FreeNAS-$version: Unsupported!"; - } - - my $response = freenas_request($scfg, 'POST', "services/iscsi/targetgroup", encode_json($data), $valid_code); - if ($response =~ /^\d+$/) { - if ($valid_code != 409) { - return freenas_get_target_group($scfg, $target) if $valid_code && $response == $valid_code; - } - die HTTP::Status::status_message($response); - } - my $tg = decode_json($response); - - die "Creating target group for target '$target' failed" unless $tg->{id}; - - return $tg->{id}; + my ($scfg, $target, $valid_code) = @_; + my $data; + + freenas_get_version($scfg) unless $version; + + # Trying to create a target group which already exists will cause and internal + # server error so if creating an existing target group should be allowed (return + # existing target group number we must search prior to create + if ($valid_code && $valid_code == 409) { + my $tg = freenas_get_target_group($scfg, $target); + return $tg if $tg; + } + + if ($version < 110000) { + $data = { + iscsi_target => $target, + iscsi_target_authgroup => $scfg->{auth_group} ? $scfg->{auth_group} : undef, + iscsi_target_portalgroup => $scfg->{portal_group}, + iscsi_target_initiatorgroup => $scfg->{initiator_group}, + iscsi_target_authtype => $scfg->{auth_type} ? $scfg->{auth_type} : 'None', + iscsi_target_initialdigest => "Auto", + }; + } elsif ($version < 110100) { + $data = { + iscsi_target => $target, + iscsi_target_authgroup => $scfg->{auth_group} ? $scfg->{auth_group} : undef, + iscsi_target_portalgroup => $scfg->{portal_group}, + iscsi_target_initiatorgroup => $scfg->{initiator_group}, + iscsi_target_authtype => $scfg->{auth_type} ? $scfg->{auth_type} : 'None', + iscsi_target_initialdigest => "Auto", + }; + } else { + die "FreeNAS-$version: Unsupported!"; + } + + my $response = freenas_request($scfg, 'POST', "services/iscsi/targetgroup", encode_json($data), $valid_code); + if ($response =~ /^\d+$/) { + if ($valid_code != 409) { + return freenas_get_target_group($scfg, $target) if $valid_code && $response == $valid_code; + } + die HTTP::Status::status_message($response); + } + my $tg = decode_json($response); + + die "Creating target group for target '$target' failed" unless $tg->{id}; + + return $tg->{id}; } sub freenas_delete_target_group { - my ($scfg, $tg) = @_; + my ($scfg, $tg) = @_; - my $response = freenas_request($scfg, 'DELETE', "services/iscsi/targetgroup/$tg"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $response = freenas_request($scfg, 'DELETE', "services/iscsi/targetgroup/$tg"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub freenas_get_target_group { - my ($scfg, $target) = @_; - my $targetgroup = undef; - - my $response = freenas_request($scfg, 'GET', "services/iscsi/targetgroup?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $targetgroups = decode_json($response); - - foreach my $tgroup (@$targetgroups) { - if ($tgroup->{iscsi_target} == $target && - $tgroup->{iscsi_target_portalgroup} == $scfg->{portal_group} && - $tgroup->{iscsi_target_initiatorgroup} == $scfg->{initiator_group}) { - $targetgroup = $tgroup->{id}; - last; - } - } - - return $targetgroup; + my ($scfg, $target) = @_; + my $targetgroup = undef; + + my $response = freenas_request($scfg, 'GET', "services/iscsi/targetgroup?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $targetgroups = decode_json($response); + + foreach my $tgroup (@$targetgroups) { + if ($tgroup->{iscsi_target} == $target && + $tgroup->{iscsi_target_portalgroup} == $scfg->{portal_group} && + $tgroup->{iscsi_target_initiatorgroup} == $scfg->{initiator_group}) { + $targetgroup = $tgroup->{id}; + last; + } + } + + return $targetgroup; } sub freenas_create_extent { - my ($scfg, $zvol) = @_; - my $data; - - freenas_get_version($scfg) unless $version; - - if ($version < 110000) { - $data = { - iscsi_target_extent_type => 'Disk', - iscsi_target_extent_name => $zvol, - iscsi_target_extent_disk => "zvol/$scfg->{pool}/$zvol", - }; - } elsif ($version < 110100) { - $data = { - iscsi_target_extent_type => 'Disk', - iscsi_target_extent_name => $zvol, - iscsi_target_extent_disk => "zvol/$scfg->{pool}/$zvol", - }; - } else { - die "FreeNAS-$version: Unsupported!"; - } - - my $response = freenas_request($scfg, 'POST', "services/iscsi/extent", encode_json($data)); - die HTTP::Status::status_message($response) if ($response =~ /^\d+$/); - my $extent = decode_json($response); - - die "Creating LUN for volume '$zvol' failed" unless $extent->{id}; - - return $extent->{id}; + my ($scfg, $zvol) = @_; + my $data; + + freenas_get_version($scfg) unless $version; + + if ($version < 110000) { + $data = { + iscsi_target_extent_type => 'Disk', + iscsi_target_extent_name => $zvol, + iscsi_target_extent_disk => "zvol/$scfg->{pool}/$zvol", + }; + } elsif ($version < 110100) { + $data = { + iscsi_target_extent_type => 'Disk', + iscsi_target_extent_name => $zvol, + iscsi_target_extent_disk => "zvol/$scfg->{pool}/$zvol", + }; + } else { + die "FreeNAS-$version: Unsupported!"; + } + + my $response = freenas_request($scfg, 'POST', "services/iscsi/extent", encode_json($data)); + die HTTP::Status::status_message($response) if ($response =~ /^\d+$/); + my $extent = decode_json($response); + + die "Creating LUN for volume '$zvol' failed" unless $extent->{id}; + + return $extent->{id}; } sub freenas_delete_extent { - my ($scfg, $extent) = @_; + my ($scfg, $extent) = @_; - my $response = freenas_request($scfg, 'DELETE', "services/iscsi/extent/$extent"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $response = freenas_request($scfg, 'DELETE', "services/iscsi/extent/$extent"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub freenas_get_extent { - my ($scfg, $volname) = @_; - my $extent = undef; - - my $response = freenas_request($scfg, 'GET', "services/iscsi/extent?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $extents = decode_json($response); - foreach my $ext (@$extents) { - if ($ext->{iscsi_target_extent_path} =~ /$scfg->{pool}\/$volname$/) { - $extent = $ext->{id}; - last; - } - } - - return $extent; + my ($scfg, $volname) = @_; + my $extent = undef; + + my $response = freenas_request($scfg, 'GET', "services/iscsi/extent?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $extents = decode_json($response); + foreach my $ext (@$extents) { + if ($ext->{iscsi_target_extent_path} =~ /$scfg->{pool}\/$volname$/) { + $extent = $ext->{id}; + last; + } + } + + return $extent; } sub freenas_create_target_to_exent { - my ($scfg, $target, $extent, $lunid) = @_; - my $data; - - freenas_get_version($scfg) unless $version; - - if ($version < 110000) { - $data = { - iscsi_target => $target, - iscsi_extent => $extent, - iscsi_lunid => $lunid, - }; - } elsif ($version < 110100) { - $data = { - iscsi_target => $target, - iscsi_extent => $extent, - iscsi_lunid => $lunid, - }; - } else { - die "FreeNAS-$version: Unsupported!"; - } - - my $response = freenas_request($scfg, 'POST', "services/iscsi/targettoextent", encode_json($data)); - die HTTP::Status::status_message($response) if ($response =~ /^\d+$/); - my $tg2extent = decode_json($response); - - die "Creating view for LUN '$extent' failed" unless $tg2extent->{id}; - - return $tg2extent->{id}; + my ($scfg, $target, $extent, $lunid) = @_; + my $data; + + freenas_get_version($scfg) unless $version; + + if ($version < 110000) { + $data = { + iscsi_target => $target, + iscsi_extent => $extent, + iscsi_lunid => $lunid, + }; + } elsif ($version < 110100) { + $data = { + iscsi_target => $target, + iscsi_extent => $extent, + iscsi_lunid => $lunid, + }; + } else { + die "FreeNAS-$version: Unsupported!"; + } + + my $response = freenas_request($scfg, 'POST', "services/iscsi/targettoextent", encode_json($data)); + die HTTP::Status::status_message($response) if ($response =~ /^\d+$/); + my $tg2extent = decode_json($response); + + die "Creating view for LUN '$extent' failed" unless $tg2extent->{id}; + + return $tg2extent->{id}; } sub freenas_delete_target_to_exent { - my ($scfg, $tg2exent) = @_; + my ($scfg, $tg2exent) = @_; - my $response = freenas_request($scfg, 'DELETE', "services/iscsi/targettoextent/$tg2exent"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $response = freenas_request($scfg, 'DELETE', "services/iscsi/targettoextent/$tg2exent"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub freenas_get_target_to_exent { - my ($scfg, $extent, $target) = @_; - my $t2extent = undef; - - my $response = freenas_request($scfg, 'GET', "services/iscsi/targettoextent?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $t2extents = decode_json($response); - foreach my $t2ext (@$t2extents) { - if ($t2ext->{iscsi_target} == $target && $t2ext->{iscsi_extent} == $extent) { - $t2extent = $t2ext->{id}; - last; - } - } - - return $t2extent; + my ($scfg, $extent, $target) = @_; + my $t2extent = undef; + + my $response = freenas_request($scfg, 'GET', "services/iscsi/targettoextent?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $t2extents = decode_json($response); + foreach my $t2ext (@$t2extents) { + if ($t2ext->{iscsi_target} == $target && $t2ext->{iscsi_extent} == $extent) { + $t2extent = $t2ext->{id}; + last; + } + } + + return $t2extent; } sub freenas_find_free_diskname { @@ -410,20 +409,20 @@ sub freenas_find_free_diskname { my $disk_ids = {}; my $dat = $volumes->{$scfg->{pool}}; - foreach my $image (keys %$dat) { - my $volname = $dat->{$image}->{name}; - if ($volname =~ m/(vm|base)-$vmid-disk-(\d+)/){ - $disk_ids->{$2} = 1; - } - } - - for (my $i = 1; $i < $max_luns + 1; $i++) { - if (!$disk_ids->{$i}) { - return "vm-$vmid-disk-$i"; - } - } - - die "Maximum number of LUNs($max_luns) for this VM $vmid in storage '$storeid' is reached."; + foreach my $image (keys %$dat) { + my $volname = $dat->{$image}->{name}; + if ($volname =~ m/(vm|base)-$vmid-disk-(\d+)/){ + $disk_ids->{$2} = 1; + } + } + + for (my $i = 1; $i < $max_luns + 1; $i++) { + if (!$disk_ids->{$i}) { + return "vm-$vmid-disk-$i"; + } + } + + die "Maximum number of LUNs($max_luns) for this VM $vmid in storage '$storeid' is reached."; } sub freenas_get_lun_number { @@ -431,31 +430,31 @@ sub freenas_get_lun_number { my $lunid = undef; if ($volname =~ /^(vm|base)-\d+-disk-(\d+)$/) { - $lunid = $2 - 1; + $lunid = $2 - 1; } elsif ($volname =~ /^vm-(\d+)-state/) { - # Find id for temporary LUN - my $target = freenas_get_target($scfg, $1); - my $id = $max_luns; - my $response = freenas_request($scfg, 'GET', "services/iscsi/targettoextent?limit=$limit"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $t2extents = decode_json($response); - - foreach my $t2extent (@$t2extents) { - next unless $t2extent->{iscsi_target} == $target && - $t2extent->{iscsi_lunid} + 1 > $max_luns && - $t2extent->{iscsi_lunid} < $max_luns + $active_snaps; - my $eid = freenas_get_extent($scfg, $volname); - if ($eid) { - $response = freenas_request($scfg, 'GET', "services/iscsi/extent/$eid"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $extent = decode_json($response); - # Request to get lunid for an existing lun - last if $t2extent->{iscsi_extent} eq $eid; - } - $id++; - } - die "Max snapshots (4) is reached" unless ($id - $max_luns) < $active_snaps; - $lunid = $id; + # Find id for temporary LUN + my $target = freenas_get_target($scfg, $1); + my $id = $max_luns; + my $response = freenas_request($scfg, 'GET', "services/iscsi/targettoextent?limit=$limit"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $t2extents = decode_json($response); + + foreach my $t2extent (@$t2extents) { + next unless $t2extent->{iscsi_target} == $target && + $t2extent->{iscsi_lunid} + 1 > $max_luns && + $t2extent->{iscsi_lunid} < $max_luns + $active_snaps; + my $eid = freenas_get_extent($scfg, $volname); + if ($eid) { + $response = freenas_request($scfg, 'GET', "services/iscsi/extent/$eid"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $extent = decode_json($response); + # Request to get lunid for an existing lun + last if $t2extent->{iscsi_extent} eq $eid; + } + $id++; + } + die "Max snapshots (4) is reached" unless ($id - $max_luns) < $active_snaps; + $lunid = $id; } elsif ($volname =~ /^(vm|base)-\d+-disk-\d+\@vzdump$/) { # Required to be able to exposed read-only LUNs for snapshot backup CT $lunid = $max_luns + $active_snaps; @@ -465,67 +464,67 @@ sub freenas_get_lun_number { } sub freenas_create_lun { - my ($scfg, $vmid, $zvol) = @_; - my ($target, $tg, $extent, $tg2exent) = (undef, undef, undef, undef); - - eval { - $target = freenas_create_target($scfg, $vmid, 409); - die "create_lun-> Could not create target for VM '$vmid'" unless $target; - $tg = freenas_create_target_group($scfg, $target, 409); - die "create_lun-> Could not create target group for VM '$vmid'" unless $tg; - $extent = freenas_create_extent($scfg, $zvol); - die "create_lun-> Could not create extent for VM '$vmid'" unless $extent; - my $lunid = freenas_get_lun_number($scfg, $zvol); - die "create_lun-> $zvol: Bad name format for VM '$vmid'" unless defined $lunid; - $tg2exent = freenas_create_target_to_exent($scfg, $target, $extent, $lunid); - die "create_lun-> Could not create target to extend for VM '$vmid'" unless defined $tg2exent; - }; - if ($@) { - my $err = $@; - if ($tg2exent) { - freenas_delete_target_to_exent($scfg, $tg2exent); - } - if ($extent) { - freenas_delete_extent($scfg, $extent); - } - if ($target && freenas_no_more_extents($scfg, $target)) { - if ($tg) { - freenas_delete_target_group($scfg, $tg); - } - freenas_delete_target($scfg, $target); - } - die $err; - } + my ($scfg, $vmid, $zvol) = @_; + my ($target, $tg, $extent, $tg2exent) = (undef, undef, undef, undef); + + eval { + $target = freenas_create_target($scfg, $vmid, 409); + die "create_lun-> Could not create target for VM '$vmid'" unless $target; + $tg = freenas_create_target_group($scfg, $target, 409); + die "create_lun-> Could not create target group for VM '$vmid'" unless $tg; + $extent = freenas_create_extent($scfg, $zvol); + die "create_lun-> Could not create extent for VM '$vmid'" unless $extent; + my $lunid = freenas_get_lun_number($scfg, $zvol); + die "create_lun-> $zvol: Bad name format for VM '$vmid'" unless defined $lunid; + $tg2exent = freenas_create_target_to_exent($scfg, $target, $extent, $lunid); + die "create_lun-> Could not create target to extend for VM '$vmid'" unless defined $tg2exent; + }; + if ($@) { + my $err = $@; + if ($tg2exent) { + freenas_delete_target_to_exent($scfg, $tg2exent); + } + if ($extent) { + freenas_delete_extent($scfg, $extent); + } + if ($target && freenas_no_more_extents($scfg, $target)) { + if ($tg) { + freenas_delete_target_group($scfg, $tg); + } + freenas_delete_target($scfg, $target); + } + die $err; + } } sub freenas_create_zvol { - my ($scfg, $volname, $size) = @_; - - my $data = { - name => $volname, - volsize => $size, - }; - my $response = freenas_request($scfg, 'POST', "storage/volume/$scfg->{pool}/zvols", encode_json($data)); - die HTTP::Status::status_message($response) if ($response =~ /^\d+$/); - my $zvol = decode_json($response); - - die "$volname: Failed creating volume" unless $zvol && $zvol->{name}; - - return $zvol->{name}; + my ($scfg, $volname, $size) = @_; + + my $data = { + name => $volname, + volsize => $size, + }; + my $response = freenas_request($scfg, 'POST', "storage/volume/$scfg->{pool}/zvols", encode_json($data)); + die HTTP::Status::status_message($response) if ($response =~ /^\d+$/); + my $zvol = decode_json($response); + + die "$volname: Failed creating volume" unless $zvol && $zvol->{name}; + + return $zvol->{name}; } sub freenas_delete_zvol { - my ($scfg, $volname) = @_; + my ($scfg, $volname) = @_; - my $response = freenas_request($scfg, 'DELETE', "storage/volume/$scfg->{pool}/zvols/$volname"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $response = freenas_request($scfg, 'DELETE', "storage/volume/$scfg->{pool}/zvols/$volname"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub os_request { - my ($cmd, $noerr, $timeout) = @_; + my ($cmd, $noerr, $timeout) = @_; - $timeout = PVE::RPCEnvironment::is_worker() ? 60*60 : 5 if !$timeout; - $noerr = 0 if !$noerr; + $timeout = PVE::RPCEnvironment::is_worker() ? 60*60 : 5 if !$timeout; + $noerr = 0 if !$noerr; my $text = ''; @@ -534,200 +533,200 @@ sub os_request { $text .= "$line\n"; }; - my $exit_code = run_command($cmd, noerr => $noerr, errfunc => $output, outfunc => $output, timeout => $timeout); + my $exit_code = run_command($cmd, noerr => $noerr, errfunc => $output, outfunc => $output, timeout => $timeout); return wantarray ? ($exit_code, $text) : $exit_code; } sub bail_out { - my ($class, $storeid, $scfg, $volname, $err) = @_; - - $class->free_image($storeid, $scfg, $volname); - die $err; + my ($class, $storeid, $scfg, $volname, $err) = @_; + + $class->free_image($storeid, $scfg, $volname); + die $err; } sub disk_by_path { - my ($scfg, $volname) = @_; + my ($scfg, $volname) = @_; - my $target = freenas_get_target_name($scfg, $volname); - my $lun = freenas_get_lun_number($scfg, $volname); - my $path = "/dev/disk/by-path/ip-$scfg->{portal}\:3260-iscsi-$target-lun-$lun"; + my $target = freenas_get_target_name($scfg, $volname); + my $lun = freenas_get_lun_number($scfg, $volname); + my $path = "/dev/disk/by-path/ip-$scfg->{portal}\:3260-iscsi-$target-lun-$lun"; - return $path; + return $path; } sub build_lun_list { - my ($scfg, $sid, $lun) = @_; - - my $luns = {}; - my $text = ''; - my $exit = 0; - - eval { - ($exit, $text) = os_request("iscsiadm -m session -r $sid -P3", 1, 60); - }; - if ($@) { - # An exist code of 22 means no active session otherwise an error - if ($exit != 22) { - die "$@"; - } - } - if ($text =~ /.*Host Number:\s*(\d+)\s+State:\s+running(.*)/s) { - my $host = $1; - my $found = 0; - for (split /^/, $2) { - if ($_ =~ /Channel\s+(\d+)\s+Id\s+(\d+)\s+Lun:\s+(\d+)/) { - if (defined $lun && $lun == $3) { - $luns = {}; - $found = 1; - } - $luns->{$3} = "$host:".int($1).":$2:$3"; - last if $found; - } - } - } - - return $luns; + my ($scfg, $sid, $lun) = @_; + + my $luns = {}; + my $text = ''; + my $exit = 0; + + eval { + ($exit, $text) = os_request("iscsiadm -m session -r $sid -P3", 1, 60); + }; + if ($@) { + # An exist code of 22 means no active session otherwise an error + if ($exit != 22) { + die "$@"; + } + } + if ($text =~ /.*Host Number:\s*(\d+)\s+State:\s+running(.*)/s) { + my $host = $1; + my $found = 0; + for (split /^/, $2) { + if ($_ =~ /Channel\s+(\d+)\s+Id\s+(\d+)\s+Lun:\s+(\d+)/) { + if (defined $lun && $lun == $3) { + $luns = {}; + $found = 1; + } + $luns->{$3} = "$host:".int($1).":$2:$3"; + last if $found; + } + } + } + + return $luns; } sub get_sid { - my ($scfg, $volname) = @_; - my $sid = -1; - my $text = ''; - my $exit = 0; - - my $target = freenas_get_target_name($scfg, $volname); - - eval { - ($exit, $text) = os_request("iscsiadm -m node -T $target -p $scfg->{portal} -s", 1, 60); - }; - if ($@) { - # An exist code of 21 or 22 means no active session otherwise an error - if ($exit != 21 || $exit != 22) { - die "$@"; - } - } - if ($text =~ /.*\[sid\:\s*(\d+),\s*.*/) { - $sid = $1; - } - - return $sid; + my ($scfg, $volname) = @_; + my $sid = -1; + my $text = ''; + my $exit = 0; + + my $target = freenas_get_target_name($scfg, $volname); + + eval { + ($exit, $text) = os_request("iscsiadm -m node -T $target -p $scfg->{portal} -s", 1, 60); + }; + if ($@) { + # An exist code of 21 or 22 means no active session otherwise an error + if ($exit != 21 || $exit != 22) { + die "$@"; + } + } + if ($text =~ /.*\[sid\:\s*(\d+),\s*.*/) { + $sid = $1; + } + + return $sid; } sub create_session { - my ($scfg, $volname) = @_; - my $sid = -1; - my $exit = undef; - - my $target = freenas_get_target_name($scfg, $volname); - - eval { - $exit = os_request("iscsiadm -m node -T $target -p $scfg->{portal} --login", 1, 60); - if ($exit == 21) { - eval { - os_request("iscsiadm -m discovery -t sendtargets -p $scfg->{portal}", 0, 60); - os_request("iscsiadm -m node -T $target -p $scfg->{portal} --login", 0, 60); - }; - } - }; - if ($@) { - if ($exit == 21) { - eval { - os_request("iscsiadm -m discovery -t sendtargets -p $scfg->{portal}", 0, 60); - os_request("iscsiadm -m node -T $target -p $scfg->{portal} --login", 0, 60); - }; - } else { - die $@; - } - } - eval { - $sid = get_sid($scfg, $volname); - }; - die "$@" if $@; - die "Could not create session" if $sid < 0; - - return $sid; + my ($scfg, $volname) = @_; + my $sid = -1; + my $exit = undef; + + my $target = freenas_get_target_name($scfg, $volname); + + eval { + $exit = os_request("iscsiadm -m node -T $target -p $scfg->{portal} --login", 1, 60); + if ($exit == 21) { + eval { + os_request("iscsiadm -m discovery -t sendtargets -p $scfg->{portal}", 0, 60); + os_request("iscsiadm -m node -T $target -p $scfg->{portal} --login", 0, 60); + }; + } + }; + if ($@) { + if ($exit == 21) { + eval { + os_request("iscsiadm -m discovery -t sendtargets -p $scfg->{portal}", 0, 60); + os_request("iscsiadm -m node -T $target -p $scfg->{portal} --login", 0, 60); + }; + } else { + die $@; + } + } + eval { + $sid = get_sid($scfg, $volname); + }; + die "$@" if $@; + die "Could not create session" if $sid < 0; + + return $sid; } sub delete_session { - my ($scfg, $sid) = @_; - - eval { - os_request("iscsiadm -m session -r $sid --logout", 0, 60); - }; + my ($scfg, $sid) = @_; + + eval { + os_request("iscsiadm -m session -r $sid --logout", 0, 60); + }; } sub remove_local_lun { - my ($id) = @_; - - os_request("echo 1 > /sys/bus/scsi/devices/$id/delete", 0, 60); + my ($id) = @_; + + os_request("echo 1 > /sys/bus/scsi/devices/$id/delete", 0, 60); } sub deactivate_luns { - # $luns contains a hash of luns to keep - my ($scfg, $volname, $luns) = @_; - - $luns = {} if !$luns; - my $sid; - my $list = {}; - - eval { - $sid = get_sid($scfg, $volname); - }; - die "$@" if $@; - - eval { - $list = build_lun_list($scfg, $sid); - - foreach my $key (keys %$list) { - next if exists($luns->{$key}); - remove_local_lun($list->{$key}); - } - }; - die "$@" if $@; + # $luns contains a hash of luns to keep + my ($scfg, $volname, $luns) = @_; + + $luns = {} if !$luns; + my $sid; + my $list = {}; + + eval { + $sid = get_sid($scfg, $volname); + }; + die "$@" if $@; + + eval { + $list = build_lun_list($scfg, $sid); + + foreach my $key (keys %$list) { + next if exists($luns->{$key}); + remove_local_lun($list->{$key}); + } + }; + die "$@" if $@; } sub get_active_luns { - my ($class, $storeid, $scfg, $volname) = @_; - - my $sid = 0; - my $luns = {}; - - eval { - $sid = get_sid($scfg, $volname); - }; - die "$@" if $@; - if ($sid < 0) { - # We have no active sessions so make one - eval { - $sid = create_session($scfg, $volname); - }; - die "$@" if $@; - # Since no session existed prior to this call deactivate all LUN's found - deactivate_luns($scfg, $volname); - } else { - eval { - $luns = build_lun_list($scfg, $sid); - }; - die "$@" if $@; - } - - return $luns; + my ($class, $storeid, $scfg, $volname) = @_; + + my $sid = 0; + my $luns = {}; + + eval { + $sid = get_sid($scfg, $volname); + }; + die "$@" if $@; + if ($sid < 0) { + # We have no active sessions so make one + eval { + $sid = create_session($scfg, $volname); + }; + die "$@" if $@; + # Since no session existed prior to this call deactivate all LUN's found + deactivate_luns($scfg, $volname); + } else { + eval { + $luns = build_lun_list($scfg, $sid); + }; + die "$@" if $@; + } + + return $luns; } sub rescan_session { - my ($class, $storeid, $scfg, $volname, $exclude_lun) = @_; - - eval { - my $active_luns = get_active_luns($class, $storeid, $scfg, $volname); - delete $active_luns->{$exclude_lun} if defined $exclude_lun; - my $sid = get_sid($scfg, $volname); - die "Missing session" if $sid < 0; - os_request("iscsiadm -m session -r $sid -R", 0, 60); - deactivate_luns($scfg, $volname, $active_luns); - delete_session($scfg, $sid) if !%$active_luns; - }; - die "$@" if $@; + my ($class, $storeid, $scfg, $volname, $exclude_lun) = @_; + + eval { + my $active_luns = get_active_luns($class, $storeid, $scfg, $volname); + delete $active_luns->{$exclude_lun} if defined $exclude_lun; + my $sid = get_sid($scfg, $volname); + die "Missing session" if $sid < 0; + os_request("iscsiadm -m session -r $sid -R", 0, 60); + deactivate_luns($scfg, $volname, $active_luns); + delete_session($scfg, $sid) if !%$active_luns; + }; + die "$@" if $@; } sub freenas_get_latest_snapshot { @@ -736,13 +735,13 @@ sub freenas_get_latest_snapshot { my $vname = ($class->parse_volname($volname))[1]; # abort rollback if snapshot is not the latest - my $response = freenas_request($scfg, 'GET', "storage/snapshot"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $snapshots = decode_json($response); - + my $response = freenas_request($scfg, 'GET', "storage/snapshot"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $snapshots = decode_json($response); + my $recentsnap; foreach my $snapshot (@$snapshots) { - next unless $snapshot->{filesystem} =~ /$scfg->{pool}\/$vname/ && $snapshot->{mostrecent}; + next unless $snapshot->{filesystem} =~ /$scfg->{pool}\/$vname/ && $snapshot->{mostrecent}; $recentsnap = $snapshot->{name}; last; } @@ -758,42 +757,42 @@ sub type { sub plugindata { return { - content => [ {images => 1, rootdir => 1}, {images => 1 , rootdir => 1} ], - format => [ { raw => 1 } , 'raw' ], + content => [ {images => 1, rootdir => 1}, {images => 1 , rootdir => 1} ], + format => [ { raw => 1 } , 'raw' ], }; } sub properties { return { password => { - description => "password", - type => "string", + description => "password", + type => "string", }, portal_group => { - description => "Portal Group ID", - type => "integer", + description => "Portal Group ID", + type => "integer", }, initiator_group => { - description => "Initiator Group ID", - type => "integer", + description => "Initiator Group ID", + type => "integer", }, }; } sub options { return { - portal => { fixed => 1 }, - pool => { fixed => 1 }, - portal_group => { fixed => 1 }, - initiator_group => { fixed => 1 }, - blocksize => { optional => 1 }, - username => { optional => 1 }, - password => { optional => 1 }, -# sparse => { optional => 1 }, not available in 9.2.x. Appear in 11.x + portal => { fixed => 1 }, + pool => { fixed => 1 }, + portal_group => { fixed => 1 }, + initiator_group => { fixed => 1 }, + blocksize => { optional => 1 }, + username => { optional => 1 }, + password => { optional => 1 }, +# sparse => { optional => 1 }, not available in 9.2.x. Appear in 11.x # in 9.2.x all zvols are created sparse! - nodes => { optional => 1 }, - disable => { optional => 1 }, - content => { optional => 1 }, + nodes => { optional => 1 }, + disable => { optional => 1 }, + content => { optional => 1 }, }; } @@ -802,14 +801,14 @@ sub options { sub volume_size_info { my ($class, $scfg, $storeid, $volname, $timeout) = @_; - my (undef, $vname) = $class->parse_volname($volname); + my (undef, $vname) = $class->parse_volname($volname); - my $response = freenas_request($scfg, 'GET', "storage/volume/$scfg->{pool}/zvols/$vname"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $zvol = decode_json($response); - - return $zvol->{volsize} if $zvol && $zvol->{volsize}; - + my $response = freenas_request($scfg, 'GET', "storage/volume/$scfg->{pool}/zvols/$vname"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $zvol = decode_json($response); + + return $zvol->{volsize} if $zvol && $zvol->{volsize}; + die "Could not get zfs volume size\n"; } @@ -817,9 +816,9 @@ sub parse_volname { my ($class, $volname) = @_; if ($volname =~ m/^(((base)-(\d+)-\S+)\/)?((base|vm)-(\d+)-\S+)$/) { - my $format = 'raw'; - my $isBase = ($6 eq 'base'); - return ('images', $5, $7, $2, $4, $isBase, $format); + my $format = 'raw'; + my $isBase = ($6 eq 'base'); + return ('images', $5, $7, $2, $4, $isBase, $format); } die "unable to parse freenas volume name '$volname'\n"; @@ -833,9 +832,9 @@ sub status { my $active = 0; eval { - my $response = freenas_request($scfg, 'GET', "storage/volume/$scfg->{pool}"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $vol = decode_json($response); + my $response = freenas_request($scfg, 'GET', "storage/volume/$scfg->{pool}"); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $vol = decode_json($response); my $children = $vol->{children}; if (@$children) { $used = $children->[0]{used}; @@ -861,27 +860,27 @@ sub list_images { if (my $dat = $cache->{freenas}->{$zfspool}) { - foreach my $image (keys %$dat) { - - my $info = $dat->{$image}; - my $volname = $info->{name}; - my $parent = $info->{parent}; - my $owner = $info->{vmid}; - - if ($parent) { - $info->{volid} = "$storeid:$parent/$volname"; - } else { - $info->{volid} = "$storeid:$volname"; - } - - if ($vollist) { - my $found = grep { $_ eq $info->{volid} } @$vollist; - next if !$found; - } else { - next if defined ($vmid) && ($owner ne $vmid); - } - push @$res, $info; - } + foreach my $image (keys %$dat) { + + my $info = $dat->{$image}; + my $volname = $info->{name}; + my $parent = $info->{parent}; + my $owner = $info->{vmid}; + + if ($parent) { + $info->{volid} = "$storeid:$parent/$volname"; + } else { + $info->{volid} = "$storeid:$volname"; + } + + if ($vollist) { + my $found = grep { $_ eq $info->{volid} } @$vollist; + next if !$found; + } else { + next if defined ($vmid) && ($owner ne $vmid); + } + push @$res, $info; + } } return $res; @@ -889,16 +888,14 @@ sub list_images { sub path { my ($class, $scfg, $volname, $storeid, $snapname) = @_; - #die "direct access to snapshots not implemented" - #if defined($snapname); my ($vtype, $vname, $vmid) = $class->parse_volname($volname); - $vname = "$vname\@$snapname" if $snapname; + $vname = "$vname\@$snapname" if $snapname; - my $luns = get_active_luns($class, $storeid, $scfg, $vname); - my $path = disk_by_path($scfg, $vname); - + my $luns = get_active_luns($class, $storeid, $scfg, $vname); + my $path = disk_by_path($scfg, $vname); + return ($path, $vmid, $vtype); } @@ -915,42 +912,42 @@ sub create_base { my $newname = $name; $newname =~ s/^vm-/base-/; - $target = freenas_get_target($scfg, $vmid); - die "create_base-> missing target" unless $target; - my $extent = freenas_get_extent($scfg, $name); - die "create_base-> missing extent" unless $extent; - my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); - die "create_base-> missing target to extent" unless $tg2exent; - $lun = freenas_get_lun_number($scfg, $name); - die "create_base-> missing LUN" unless defined $lun; - freenas_delete_target_to_exent($scfg, $tg2exent); - freenas_delete_extent($scfg, $extent); - my $sid = get_sid($scfg, $name); - if ($sid >= 0) { - my $lid = build_lun_list($scfg, $sid, $lun); - if ($lid && $lid->{$lun}) { - remove_local_lun($lid->{$lun}); - } - } - - eval { - # FreeNAS API does not support renaming a zvol so create a snapshot - # and make a clone of the snapshot instead - $class->volume_snapshot($scfg, $storeid, $name, $snap); - - my $data = { - name => "$scfg->{pool}/$newname" - }; - my $response = freenas_request($scfg, 'POST', "storage/snapshot/$scfg->{pool}/$name\@$snap/clone/", encode_json($data)); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - - freenas_create_lun($scfg, $vmid, $newname); + $target = freenas_get_target($scfg, $vmid); + die "create_base-> missing target" unless $target; + my $extent = freenas_get_extent($scfg, $name); + die "create_base-> missing extent" unless $extent; + my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); + die "create_base-> missing target to extent" unless $tg2exent; + $lun = freenas_get_lun_number($scfg, $name); + die "create_base-> missing LUN" unless defined $lun; + freenas_delete_target_to_exent($scfg, $tg2exent); + freenas_delete_extent($scfg, $extent); + my $sid = get_sid($scfg, $name); + if ($sid >= 0) { + my $lid = build_lun_list($scfg, $sid, $lun); + if ($lid && $lid->{$lun}) { + remove_local_lun($lid->{$lun}); + } + } + + eval { + # FreeNAS API does not support renaming a zvol so create a snapshot + # and make a clone of the snapshot instead + $class->volume_snapshot($scfg, $storeid, $name, $snap); + + my $data = { + name => "$scfg->{pool}/$newname" + }; + my $response = freenas_request($scfg, 'POST', "storage/snapshot/$scfg->{pool}/$name\@$snap/clone/", encode_json($data)); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + + freenas_create_lun($scfg, $vmid, $newname); }; if ($@) { - $extent = freenas_create_extent($scfg, $name); - die "create_base-> Could not create extent for VM '$vmid'" unless $extent; - $tg2exent = freenas_create_target_to_exent($scfg, $target, $extent, $lun); - die "create_base-> Could not create target to extend for VM '$vmid'" unless defined $tg2exent; + $extent = freenas_create_extent($scfg, $name); + die "create_base-> Could not create extent for VM '$vmid'" unless $extent; + $tg2exent = freenas_create_target_to_exent($scfg, $target, $extent, $lun); + die "create_base-> Could not create target to extend for VM '$vmid'" unless defined $tg2exent; } return $newname; @@ -966,30 +963,30 @@ sub clone_image { die "clone_image only works on base images" if !$isBase; - my $run = PVE::QemuServer::check_running($basevmid); - if (!$run) { - $run = PVE::LXC::check_running($basevmid); - } - + my $run = PVE::QemuServer::check_running($basevmid); + if (!$run) { + $run = PVE::LXC::check_running($basevmid); + } + my $name = freenas_find_free_diskname($storeid, $scfg, $vmid, $format); - $class->volume_snapshot($scfg, $storeid, $basename, $snap); - - my $data = { - name => "$scfg->{pool}/$name" - }; - my $response = freenas_request($scfg, 'POST', "storage/snapshot/$scfg->{pool}/$basename\@$snap/clone/", encode_json($data)); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + $class->volume_snapshot($scfg, $storeid, $basename, $snap); + + my $data = { + name => "$scfg->{pool}/$name" + }; + my $response = freenas_request($scfg, 'POST', "storage/snapshot/$scfg->{pool}/$basename\@$snap/clone/", encode_json($data)); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - $name = "$basename/$name"; + $name = "$basename/$name"; # get ZFS dataset name from PVE volname my (undef, $clonedname) = $class->parse_volname($name); - freenas_create_lun($scfg, $vmid, $clonedname); - - my $res = $class->deactivate_volume($storeid, $scfg, $basename) unless $run; - warn "Could not deactivate volume '$basename'" unless $res; - + freenas_create_lun($scfg, $vmid, $clonedname); + + my $res = $class->deactivate_volume($storeid, $scfg, $basename) unless $run; + warn "Could not deactivate volume '$basename'" unless $res; + return $name; } @@ -1009,15 +1006,15 @@ sub alloc_image { my $zvol = freenas_create_zvol($scfg, $volname, $size); eval { - freenas_create_lun($scfg, $vmid, $zvol) if $zvol; + freenas_create_lun($scfg, $vmid, $zvol) if $zvol; }; if ($@) { - my $err = $@; - eval { - freenas_delete_zvol($scfg, $volname); - }; - $err .= "\n$@" if $@; - die $err; + my $err = $@; + eval { + freenas_delete_zvol($scfg, $volname); + }; + $err .= "\n$@" if $@; + die $err; } return $volname; @@ -1028,39 +1025,39 @@ sub free_image { my ($vtype, $name, $vmid, $basename) = $class->parse_volname($volname); - eval { - my $target = freenas_get_target($scfg, $vmid); - die "free_image-> missing target" unless $target; - my $extent = freenas_get_extent($scfg, $name); - die "free_image-> missing extent" unless $extent; - my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); - die "free_image-> missing target to extent" unless $tg2exent; - my $target_group = freenas_get_target_group($scfg, $target); - die "free_image-> missing target group" unless $target_group; - my $lun = freenas_get_lun_number($scfg, $name); - die "free_image-> missing LUN" unless defined $lun; - - my $res = $class->deactivate_volume($storeid, $scfg, $volname); - warn "Could not deactivate volume '$volname'" unless $res; - freenas_delete_target_to_exent($scfg, $tg2exent); - freenas_delete_extent($scfg, $extent); - if ($target && freenas_no_more_extents($scfg, $target)) { - if ($target_group) { - freenas_delete_target_group($scfg, $target_group); - } - freenas_delete_target($scfg, $target); - } - freenas_delete_zvol($scfg, $name); - $class->volume_snapshot_delete($scfg, $storeid, $basename, "__base__$vmid") if $basename; - if ($isBase) { - $basename = $name; - $basename =~ s/^base-/vm-/; - $class->volume_snapshot_delete($scfg, $storeid, $basename, '__base__') if $basename; - freenas_delete_zvol($scfg, $basename); - } - }; + eval { + my $target = freenas_get_target($scfg, $vmid); + die "free_image-> missing target" unless $target; + my $extent = freenas_get_extent($scfg, $name); + die "free_image-> missing extent" unless $extent; + my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); + die "free_image-> missing target to extent" unless $tg2exent; + my $target_group = freenas_get_target_group($scfg, $target); + die "free_image-> missing target group" unless $target_group; + my $lun = freenas_get_lun_number($scfg, $name); + die "free_image-> missing LUN" unless defined $lun; + + my $res = $class->deactivate_volume($storeid, $scfg, $volname); + warn "Could not deactivate volume '$volname'" unless $res; + freenas_delete_target_to_exent($scfg, $tg2exent); + freenas_delete_extent($scfg, $extent); + if ($target && freenas_no_more_extents($scfg, $target)) { + if ($target_group) { + freenas_delete_target_group($scfg, $target_group); + } + freenas_delete_target($scfg, $target); + } + freenas_delete_zvol($scfg, $name); + $class->volume_snapshot_delete($scfg, $storeid, $basename, "__base__$vmid") if $basename; + if ($isBase) { + $basename = $name; + $basename =~ s/^base-/vm-/; + $class->volume_snapshot_delete($scfg, $storeid, $basename, '__base__') if $basename; + freenas_delete_zvol($scfg, $basename); + } + }; if ($@) { - my $err = $@; + my $err = $@; freenas_create_lun($scfg, $vmid, $name) unless $isBase; die $err; } @@ -1079,36 +1076,36 @@ sub volume_resize { } die 'mode failure - unable to resize disk(s) on a running system due to FreeNAS bug.<br /> - See bug report: <a href="https://bugs.freenas.org/issues/24432" target="_blank">#24432</a><br />' if $run; + See bug report: <a href="https://bugs.freenas.org/issues/24432" target="_blank">#24432</a><br />' if $run; my $data = { - volsize => $size, + volsize => $size, }; - my $response = freenas_request($scfg, 'PUT', "storage/volume/$scfg->{pool}/zvols/$name", encode_json($data)); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - my $vol = decode_json($response); + my $response = freenas_request($scfg, 'PUT', "storage/volume/$scfg->{pool}/zvols/$name", encode_json($data)); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + my $vol = decode_json($response); - my $sid = get_sid($scfg, $name); - if ($sid >= 0) { - eval { + my $sid = get_sid($scfg, $name); + if ($sid >= 0) { + eval { #### Required because of a bug in FreeNAS: https://bugs.freenas.org/issues/24432 - my $targetname = freenas_get_target_name($scfg, $name); - die "volume_resize-> Missing target name" unless $targetname; - my $target = freenas_get_target($scfg, $vmid); - die "volume_resize-> Missing target" unless $target; - my $extent = freenas_get_extent($scfg, $name); - die "volume_resize-> Missing extent" unless $extent; - my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); - die "volume_resize-> Missing target to extent" unless $tg2exent; - my $lunid = freenas_get_lun_number($scfg, $name); - die "volume_resize-> Missing LUN" unless defined $lunid; - freenas_delete_target_to_exent($scfg, $tg2exent); - freenas_create_target_to_exent($scfg, $target, $extent, $lunid); + my $targetname = freenas_get_target_name($scfg, $name); + die "volume_resize-> Missing target name" unless $targetname; + my $target = freenas_get_target($scfg, $vmid); + die "volume_resize-> Missing target" unless $target; + my $extent = freenas_get_extent($scfg, $name); + die "volume_resize-> Missing extent" unless $extent; + my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); + die "volume_resize-> Missing target to extent" unless $tg2exent; + my $lunid = freenas_get_lun_number($scfg, $name); + die "volume_resize-> Missing LUN" unless defined $lunid; + freenas_delete_target_to_exent($scfg, $tg2exent); + freenas_create_target_to_exent($scfg, $target, $extent, $lunid); #### Required because of a bug in FreeNAS: https://bugs.freenas.org/issues/24432 - rescan_session($class, $storeid, $scfg, $name, $lunid); - }; - die "$name: Resize with $size failed. ($@)\n" if $@; - } + rescan_session($class, $storeid, $scfg, $name, $lunid); + }; + die "$name: Resize with $size failed. ($@)\n" if $@; + } return int($vol->{volsize}/1024); } @@ -1116,24 +1113,14 @@ sub volume_resize { sub volume_snapshot { my ($class, $scfg, $storeid, $volname, $snap) = @_; - my (undef, $vname, $vmid) = $class->parse_volname($volname); - + my $vname = ($class->parse_volname($volname))[1]; + my $data = { - dataset => "$scfg->{pool}/$vname", - name => $snap, + dataset => "$scfg->{pool}/$vname", + name => $snap, }; - my $response = freenas_request($scfg, 'POST', "storage/snapshot/", encode_json($data)); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - - if ($snap) { - eval { - freenas_create_lun($scfg, $vmid, "$vname\@$snap"); - $class->activate_volume($storeid, $scfg, "$vname\@$snap"); - }; - if ($@) { - die "$@ - unable to activate snapshot from remote FreeNAS storage"; - } - } + my $response = freenas_request($scfg, 'POST', "storage/snapshot/", encode_json($data)); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub volume_snapshot_delete { @@ -1141,54 +1128,51 @@ sub volume_snapshot_delete { my (undef, $vname, $vmid) = $class->parse_volname($volname); - if ($snap) { - eval { - my $target = freenas_get_target($scfg, $vmid); - die "volume_snapshot_delete-> missing target" unless $target; - my $extent = freenas_get_extent($scfg, "$vname\@$snap"); - die "volume_snapshot_delete-> missing extent" unless $extent; - my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); - die "volume_snapshot_delete-> missing target to extent" unless $tg2exent; - my $lun = freenas_get_lun_number($scfg, "$vname\@$snap"); - die "volume_snapshot_delete-> missing LUN" unless defined $lun; - freenas_delete_target_to_exent($scfg, $tg2exent); - freenas_delete_extent($scfg, $extent); - }; - warn "$@ - unable to deactivate snapshot from remote FreeNAS storage" if $@; - } + if ($snap eq 'vzdump') { + eval { + my $target = freenas_get_target($scfg, $vmid); + die "volume_snapshot_delete-> missing target" unless $target; + my $extent = freenas_get_extent($scfg, "$vname\@$snap"); + die "volume_snapshot_delete-> missing extent" unless $extent; + my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); + die "volume_snapshot_delete-> missing target to extent" unless $tg2exent; + my $lun = freenas_get_lun_number($scfg, "$vname\@$snap"); + die "volume_snapshot_delete-> missing LUN" unless defined $lun; + freenas_delete_target_to_exent($scfg, $tg2exent); + freenas_delete_extent($scfg, $extent); + $class->deactivate_volume($storeid, $scfg, "$vname\@$snap"); + }; + warn "$@ - unable to deactivate snapshot from remote FreeNAS storage" if $@; + } my $response = freenas_request($scfg, 'DELETE', "storage/snapshot/$scfg->{pool}/$vname\@$snap"); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - - if ($snap) { - $class->deactivate_volume($storeid, $scfg, "$vname\@$snap"); - } + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; } sub volume_snapshot_rollback { my ($class, $scfg, $storeid, $volname, $snap) = @_; - my ($vtype, $name, $vmid) = $class->parse_volname($volname); - my $target = freenas_get_target($scfg, $vmid); - die "volume_resize-> Missing target" unless $target; - my $extent = freenas_get_extent($scfg, $name); - die "volume_resize-> Missing extent" unless $extent; - my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); - die "volume_resize-> Missing target to extent" unless $tg2exent; - my $lunid = freenas_get_lun_number($scfg, $name); - die "volume_resize-> Missing LUN" unless defined $lunid; - freenas_delete_target_to_exent($scfg, $tg2exent); - freenas_delete_extent($scfg, $extent); - - my $data = { - force => bless( do{\(my $o = 0)}, 'JSON::XS::Boolean' ), - }; + my ($vtype, $name, $vmid) = $class->parse_volname($volname); + my $target = freenas_get_target($scfg, $vmid); + die "volume_resize-> Missing target" unless $target; + my $extent = freenas_get_extent($scfg, $name); + die "volume_resize-> Missing extent" unless $extent; + my $tg2exent = freenas_get_target_to_exent($scfg, $extent, $target); + die "volume_resize-> Missing target to extent" unless $tg2exent; + my $lunid = freenas_get_lun_number($scfg, $name); + die "volume_resize-> Missing LUN" unless defined $lunid; + freenas_delete_target_to_exent($scfg, $tg2exent); + freenas_delete_extent($scfg, $extent); + + my $data = { + force => bless( do{\(my $o = 0)}, 'JSON::XS::Boolean' ), + }; my $response = freenas_request($scfg, 'POST', "storage/snapshot/$scfg->{pool}/$name\@$snap/rollback/", encode_json($data)); - die HTTP::Status::status_message($response) if $response =~ /^\d+$/; - - $extent = freenas_create_extent($scfg, $name); - freenas_create_target_to_exent($scfg, $target, $extent, $lunid); - rescan_session($class, $storeid, $scfg, $name, $lunid); + die HTTP::Status::status_message($response) if $response =~ /^\d+$/; + + $extent = freenas_create_extent($scfg, $name); + freenas_create_target_to_exent($scfg, $target, $extent, $lunid); + rescan_session($class, $storeid, $scfg, $name, $lunid); } sub volume_rollback_is_possible { @@ -1198,7 +1182,7 @@ sub volume_rollback_is_possible { my $recentsnap = $class->freenas_get_latest_snapshot($scfg, $name); if ($snap ne $recentsnap) { - die "can't rollback, more recent snapshots exist"; + die "can't rollback, more recent snapshots exist"; } return 1; @@ -1214,21 +1198,21 @@ sub volume_has_feature { my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_; my $features = { - snapshot => { current => 1, snap => 1}, - clone => { base => 1}, - template => { current => 1}, - copy => { base => 1, current => 1}, + snapshot => { current => 1, snap => 1}, + clone => { base => 1}, + template => { current => 1}, + copy => { base => 1, current => 1}, }; my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) = - $class->parse_volname($volname); + $class->parse_volname($volname); my $key = undef; if ($snapname) { - $key = 'snap'; + $key = 'snap'; } else { - $key = $isBase ? 'base' : 'current'; + $key = $isBase ? 'base' : 'current'; } return 1 if $features->{$feature}->{$key}; @@ -1251,8 +1235,8 @@ sub deactivate_storage { # Procedure for activating a LUN: # # if session does not exist -# login to target -# deactivate all luns in session +# login to target +# deactivate all luns in session # get list of active luns # get lun number to activate # make list of our luns (active + new lun) @@ -1260,25 +1244,36 @@ sub deactivate_storage { # deactivate all luns except our luns sub activate_volume { my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_; + my $lun; + + my (undef, $name, $vmid) = $class->parse_volname($volname); + + my $active_luns = get_active_luns($class, $storeid, $scfg, $name); - return if $snapname; # Activated when creating snapshot - - my $name = ($class->parse_volname($volname))[1]; + if ($snapname) { + eval { + freenas_create_lun($scfg, $vmid, "$name\@$snapname"); + $lun = freenas_get_lun_number($scfg, "$name\@$snapname"); + $active_luns->{$lun} = "0:0:0:$lun"; + }; + if ($@) { + die "$@ - unable to activate snapshot from remote FreeNAS storage"; + } + } + + $lun = freenas_get_lun_number($scfg, $name); + $active_luns->{$lun} = "0:0:0:$lun"; - my $active_luns = get_active_luns($class, $storeid, $scfg, $name); - my $lun = freenas_get_lun_number($scfg, $name); - $active_luns->{$lun} = "0:0:0:$lun"; - - eval { - my $sid = get_sid($scfg, $name); - die "activate_volume-> Missing session" if $sid < 0; - # Add new LUN's to session - os_request("iscsiadm -m session -r $sid -R", 0, 60); - sleep 1; - # Remove all LUN's from session which is not currently active - deactivate_luns($scfg, $name, $active_luns); - }; - die "$@" if $@; + eval { + my $sid = get_sid($scfg, $name); + die "activate_volume-> Missing session" if $sid < 0; + # Add new LUN's to session + os_request("iscsiadm -m session -r $sid -R", 0, 60); + sleep 1; + # Remove all LUN's from session which is not currently active + deactivate_luns($scfg, $name, $active_luns); + }; + die "$@" if $@; return 1; } @@ -1286,26 +1281,25 @@ sub activate_volume { # Procedure for deactivating a LUN: # # if session exists -# get lun number to deactivate -# deactivate lun +# get lun number to deactivate +# deactivate lun sub deactivate_volume { my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_; - return if $snapname; # Deactivated when deleting snapshot - my $name = ($class->parse_volname($volname))[1]; - my $active_luns = get_active_luns($class, $storeid, $scfg, $name); - my $lun = freenas_get_lun_number($scfg, $name); - delete $active_luns->{$lun}; - - eval { - my $sid = get_sid($scfg, $name); - die "deactivate_volume-> Missing session" if $sid < 0; - deactivate_luns($scfg, $name, $active_luns); - delete_session($scfg, $sid) if !%$active_luns; - }; - die $@ if $@; + my $active_luns = get_active_luns($class, $storeid, $scfg, $name); + + my $lun = freenas_get_lun_number($scfg, $name); + delete $active_luns->{$lun}; + + eval { + my $sid = get_sid($scfg, $name); + die "deactivate_volume-> Missing session" if $sid < 0; + deactivate_luns($scfg, $name, $active_luns); + delete_session($scfg, $sid) if !%$active_luns; + }; + die $@ if $@; return 1; } -- 2.11.0 ---- This mail was virus scanned and spam checked before delivery. This mail is also DKIM signed. See header dkim-signature. _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel