diff --git a/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000..dc90e5a
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,17 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 60
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 7
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - pinned
+ - security
+# Label to use when marking an issue as stale
+staleLabel: wontfix
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/README.md b/README.md
index 5b0d190..f940668 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,17 @@
# FreeNAS ZFS over iSCSI interface [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=TCLNEMBUYQUXN&source=url)
-## Thank you for all that have donated to the project
+## Currently trying JFrog for the repo. Just changing the Automation and making sure all works.
I am currently testing an should have at least the repos up for production and testing. You will need to update your repos with the new location and gpg. I will have instructions on this page.
+
+## Also currently developing for TrueNAS-Core 13 to provide fixes to support all the [Free|True]NAS family.
+
+## Thank you for all that have recently donated to the project.
+ Daniel Most
+ Maksym Vasylenko
+ Alexander Finkhäuser - Reoccuring
+ Bjarte Kvamme - Reoccuring
+ Jonathan Schober - Reoccuring
+
+## And thanks to all that have donated to the project in the past.
Clevvi Technology
Mark Elkins - Reoccuring
Marc Hodler
@@ -29,6 +40,11 @@ On Proxmox 6
apt reinstall pve-manager pve-docs libpve-storage-perl
```
+On Proxmox 7
+```bash
+apt reinstall pve-manager pve-docs libpve-storage-perl
+```
+## Changing in the near future.
Issue the following to install the repo and get your Proxmox VE updating the FreeNAS patches automatically:
```bash
wget http://repo.ksatechnologies.com/debian/pve/ksatechnologies-release.gpg -O /etc/apt/trusted.gpg.d/ksatechnologies-repo.gpg
@@ -71,7 +87,7 @@ This will place you back to a normal and unpatched Proxmox VE install.
Please be aware that this plugin uses the FreeNAS APIs and NOT the ssh/scp interface like the other plugins use, but...
-You will still need to configure the SSH connector for listing the ZFS Pools because this is currently being done in a Proxmox module (ZFSPoolPlugin.pm). To configure this please follow the steps at https://pve.proxmox.com/wiki/Storage:_ZFS_over_iSCSI that have to do with SSH between Proxmox VE and FreeNAS. The code segment should start out �mkdir /etc/pve/priv/zfs�.
+You will still need to configure the SSH connector for listing the ZFS Pools because this is currently being done in a Proxmox module (ZFSPoolPlugin.pm). To configure this please follow the steps at https://pve.proxmox.com/wiki/Storage:_ZFS_over_iSCSI that have to do with SSH between Proxmox VE and FreeNAS. The code segment should start out `mkdir /etc/pve/priv/zfs`.
1. Remember to follow the instructions mentioned above for the SSH keys.
diff --git a/perl5/PVE/Storage/LunCmd/FreeNAS.pm b/perl5/PVE/Storage/LunCmd/FreeNAS.pm
index 1bc0198..1bb75c6 100644
--- a/perl5/PVE/Storage/LunCmd/FreeNAS.pm
+++ b/perl5/PVE/Storage/LunCmd/FreeNAS.pm
@@ -27,6 +27,8 @@ my $runawayprevent = 0; # Recursion prevention variable
my $freenas_api_version = "v1.0"; # Default to v1.0 of the API's
my $freenas_api_methods = undef; # API Methods Nested HASH Ref
my $freenas_api_variables = undef; # API Variable Nested HASH Ref
+my $truenas_version = undef;
+my $truenas_release_type = "Production";
# FreeNAS/TrueNAS (CORE) API Versioning HashRef Matrix
my $freenas_api_version_matrix = {
@@ -197,52 +199,45 @@ sub run_list_view {
#
#
-#
+# Optimized
sub run_list_lu {
my ($scfg, $timeout, $method, $result_value_type, @params) = @_;
my $object = $params[0];
- syslog("info", (caller(0))[3] . " : called with (method=$method; result_value_type=$result_value_type; object=$object)");
-
my $result = undef;
my $luns = freenas_list_lu($scfg);
- foreach my $lun (@$luns) {
- syslog("info", (caller(0))[3] . " : Verifing '$lun->{$freenas_api_variables->{'extentpath'}}' and '$object'");
- if ($dev_prefix . $lun->{$freenas_api_variables->{'extentpath'}} eq $object) {
- $result = $result_value_type eq "lun-id" ? $lun->{$freenas_api_variables->{'lunid'}} : $dev_prefix . $lun->{$freenas_api_variables->{'extentpath'}};
- syslog("info",(caller(0))[3] . "($object) '$result_value_type' found $result");
- last;
- }
- }
- if(!defined($result)) {
- syslog("info", (caller(0))[3] . "($object) : $result_value_type : lun not found");
- }
+ syslog("info", (caller(0))[3] . " : called with (method: '$method'; result_value_type: '$result_value_type'; param[0]: '$object')");
+ $object =~ s/^\Q$dev_prefix//;
+ syslog("info", (caller(0))[3] . " : TrueNAS object to find: '$object'");
+ if (defined($luns->{$object})) {
+ my $lu_object = $luns->{$object};
+ $result = $result_value_type eq "lun-id" ? $lu_object->{$freenas_api_variables->{'lunid'}} : $dev_prefix . $lu_object->{$freenas_api_variables->{'extentpath'}};
+ syslog("info",(caller(0))[3] . " '$object' with key '$result_value_type' found with value: '$result'");
+ } else {
+ syslog("info", (caller(0))[3] . " '$object' with key '$result_value_type' was not found");
+ }
return $result;
}
#
#
-#
+# Optimzed
sub run_list_extent {
my ($scfg, $timeout, $method, @params) = @_;
my $object = $params[0];
-
- syslog("info", (caller(0))[3] . " : called with (method=$method; object=$object)");
-
+ syslog("info", (caller(0))[3] . " : called with (method: '$method'; params[0]: '$object')");
my $result = undef;
my $luns = freenas_list_lu($scfg);
- foreach my $lun (@$luns) {
- syslog("info", (caller(0))[3] . " : Verifing '$lun->{$freenas_api_variables->{'extentpath'}}' and '$object'");
- if ($dev_prefix . $lun->{$freenas_api_variables->{'extentpath'}} eq $object) {
- $result = $lun->{$freenas_api_variables->{'extentnaa'}};
- syslog("info","FreeNAS::list_extent($object): naa found $result");
- last;
- }
- }
- if (!defined($result)) {
- syslog("info","FreeNAS::list_extent($object): naa not found");
- }
+ $object =~ s/^\Q$dev_prefix//;
+ syslog("info", (caller(0))[3] . " TrueNAS object to find: '$object'");
+ if (defined($luns->{$object})) {
+ my $lu_object = $luns->{$object};
+ $result = $lu_object->{$freenas_api_variables->{'extentnaa'}};
+ syslog("info",(caller(0))[3] . " '$object' wtih key '$freenas_api_variables->{'extentnaa'}' found with value: '$result'");
+ } else {
+ syslog("info",(caller(0))[3] . " '$object' with key '$freenas_api_variables->{'extentnaa'}' was not found");
+ }
return $result;
}
@@ -280,24 +275,24 @@ sub run_create_lu {
#
#
-#
+# Optimzied
sub run_delete_lu {
my ($scfg, $timeout, $method, @params) = @_;
my $lun_path = $params[0];
- syslog("info", (caller(0))[3] . " : called with (method=$method; param[0]=$lun_path)");
+ syslog("info", (caller(0))[3] . " : called with (method: '$method'; param[0]: '$lun_path')");
my $luns = freenas_list_lu($scfg);
my $lun = undef;
my $link = undef;
- foreach my $item (@$luns) {
- if($dev_prefix . $item->{ $freenas_api_variables->{'extentpath'}} eq $lun_path) {
- $lun = $item;
- last;
- }
- }
+ $lun_path =~ s/^\Q$dev_prefix//;
- die "Unable to find the lun $lun_path for $scfg->{target}" if !defined($lun);
+ if (defined($luns->{$lun_path})) {
+ $lun = $luns->{$lun_path};
+ syslog("info",(caller(0))[3] . " lun: '$lun_path' found");
+ } else {
+ die "Unable to find the lun $lun_path for $scfg->{target}";
+ }
my $target_id = freenas_get_targetid($scfg);
die "Unable to find the target id for $scfg->{target}" if !defined($target_id);
@@ -353,24 +348,34 @@ sub freenas_api_connect {
$freenas_server_list->{$apihost}->getUseragent()->ssl_opts(SSL_verify_mode => SSL_VERIFY_NONE);
}
# Check if the APIs are accessable via the selected host and scheme
- my $code = $freenas_server_list->{$apihost}->request('GET', $apiping)->responseCode();
- if ($code == 200) { # Successful connection
- syslog("info", (caller(0))[3] . " : REST connection successful to '" . $apihost . "' using the '" . $scheme . "' protocol");
- $runawayprevent = 0;
- } elsif ($runawayprevent > 1) { # Make sure we are not recursion calling.
+ my $api_response = $freenas_server_list->{$apihost}->request('GET', $apiping);
+ my $code = $api_response->responseCode();
+ my $type = $api_response->responseHeader('Content-Type');
+ syslog("info", (caller(0))[3] . " : REST connection header Content-Type:'" . $type . "'");
+
+ # Make sure we are not recursion calling.
+ if ($runawayprevent > 1) {
freenas_api_log_error($freenas_server_list->{$apihost});
die "Loop recursion prevention";
- } elsif ($code == 302) { # A 302 from FreeNAS means it doesn't like v1.0 APIs.
+ # Successful connection
+ } elsif ($code == 200 && ($type =~ /^text\/plain/ || $type =~ /^application\/json/)) {
+ syslog("info", (caller(0))[3] . " : REST connection successful to '" . $apihost . "' using the '" . $scheme . "' protocol");
+ $runawayprevent = 0;
+ # A 302 or 200 with Content-Type not 'text/plain' from {True|Free}NAS means it doesn't like v1.0 APIs.
+ # So change to v2.0 APIs.
+ } elsif ($code == 302 || ($code == 200 && $type !~ /^text\/plain/)) {
syslog("info", (caller(0))[3] . " : Changing to v2.0 API's");
$runawayprevent++;
$apiping =~ s/v1\.0/v2\.0/;
freenas_api_connect($scfg);
- } elsif ($code == 307) { # A 307 from FreeNAS means rediect http to https.
+ # A 307 from FreeNAS means rediect http to https.
+ } elsif ($code == 307) {
syslog("info", (caller(0))[3] . " : Redirecting to HTTPS protocol");
$runawayprevent++;
$scfg->{freenas_use_ssl} = 1;
freenas_api_connect($scfg);
- } else { # For now, any other code we fail.
+ # For now, any other code we fail.
+ } else {
freenas_api_log_error($freenas_server_list->{$apihost});
die "Unable to connect to the FreeNAS API service at '" . $apihost . "' using the '" . $scheme . "' protocol";
}
@@ -395,18 +400,36 @@ sub freenas_api_check {
$result = decode_json($freenas_rest_connection->responseContent());
};
if ($@) {
- $result->{'fullversion'} = $freenas_rest_connection->responseContent();
- $result->{'fullversion'} =~ s/^"//g;
+ $result = $freenas_rest_connection->responseContent();
+ } else {
+ $result = $freenas_rest_connection->responseContent();
}
- syslog("info", (caller(0))[3] . " : successful : Server version: " . $result->{'fullversion'});
- $result->{'fullversion'} =~ s/^((?!\-\d).*)\-(\d+)\.(\d+)\-([A-Za-z]*)(?(?=\-)\-(\d*)\-(\d*)|(\d?)\.?(\d?))//;
- $product_name = $1;
- my $freenas_version = sprintf("%02d%02d%02d%02d", $2, $3 || 0, $7 || 0, $8 || 0);
- syslog("info", (caller(0))[3] . " : ". $product_name . " Unformatted Version: " . $freenas_version);
- if ($freenas_version >= 11030100) {
+ $result =~ s/"//g;
+ syslog("info", (caller(0))[3] . " : successful : Server version: " . $result);
+ if ($result =~ /^(TrueNAS|FreeNAS)-(\d+)\.(\d+)\-U(\d+)(?(?=\.)\.(\d+))$/) {
+ $product_name = $1;
+ $truenas_version = sprintf("%02d%02d%02d%02d", $2, $3 || 0, $4 || 0, $5 || 0);
+ } elsif ($result =~ /^(TrueNAS)-(\d+)\.(\d+)(?(?=\-U\d+)-U(\d+)|-\w+)(?(?=\.).(\d+))$/) {
+ $product_name = $1;
+ $truenas_version = sprintf("%02d%02d%02d%02d", $2, $3 || 0, $4 || 0, $6 || 0);
+ $truenas_release_type = $5 || "Production";
+ } elsif ($result =~ /^(TrueNAS-SCALE)-(\d+)\.(\d+)(?(?=\-)-(\w+))\.(\d+)(?(?=\.)\.(\d+))(?(?=\-)-(\d+))$/) {
+ $product_name = $1;
+ $truenas_version = sprintf("%02d%02d%02d%02d", $2, $3 || 0, $5 || 0, $7 || 0);
+ $truenas_release_type = $4 || "Production";
+ } else {
+ $product_name = "Unknown";
+ $truenas_release_type = "Unknown";
+ syslog("error", (caller(0))[3] . " : Could not parse the version of TrueNAS.");
+ }
+ syslog("info", (caller(0))[3] . " : ". $product_name . " Unformatted Version: " . $truenas_version);
+ if ($truenas_version >= 11030100) {
$freenas_api_version = "v2.0";
$dev_prefix = "/dev/";
}
+ if ($truenas_release_type ne "Production") {
+ syslog("warn", (caller(0))[3] . " : The '" . $product_name . "' release type of '" . $truenas_release_type . "' may not worked due to unsupported changes.");
+ }
} else {
syslog("info", (caller(0))[3] . " : REST Client already initialized");
}
@@ -510,7 +533,11 @@ sub freenas_iscsi_create_extent {
my $name = $lun_path;
$name =~ s/^.*\///; # all from last /
- $name = $scfg->{'pool'} . ($product_name eq "TrueNAS-SCALE" ? '-' : '/') . $name;
+ my $pool = $scfg->{'pool'};
+ if ($product_name eq "TrueNAS-SCALE") {
+ $pool =~ s/\//-/g;
+ }
+ $name = $pool . ($product_name eq "TrueNAS-SCALE" ? '-' : '/') . $name;
my $device = $lun_path;
$device =~ s/^\/dev\///; # strip /dev/
@@ -677,7 +704,7 @@ sub freenas_list_lu {
my $targets = freenas_iscsi_get_target($scfg);
my $target_id = freenas_get_targetid($scfg);
- my @luns = ();
+ my %lun_hash;
my $iscsi_lunid = undef;
if(defined($target_id)) {
@@ -689,21 +716,21 @@ sub freenas_list_lu {
foreach my $node (@$extents) {
if($node->{'id'} == $item->{$freenas_api_variables->{'extentid'}}) {
if ($item->{$freenas_api_variables->{'lunid'}} =~ /(\d+)/) {
- $iscsi_lunid = "$1";
+ if (defined($node)) {
+ $node->{$freenas_api_variables->{'lunid'}} .= "$1";
+ $lun_hash{$node->{$freenas_api_variables->{'extentpath'}}} = $node;
+ }
+ last;
} else {
- syslog("info", (caller(0))[3] . " : iscsi_lunid did not pass tainted testing");
- next;
+ syslog("warn", (caller(0))[3] . " : iscsi_lunid did not pass tainted testing");
}
- $node->{$freenas_api_variables->{'lunid'}} .= $iscsi_lunid;
- push(@luns , $node);
- last;
}
}
}
}
}
syslog("info", (caller(0))[3] . " : successful");
- return \@luns;
+ return \%lun_hash;
}
#
diff --git a/perl5/PVE/Storage/ZFSPlugin-7.1-1_1.pm.patch b/perl5/PVE/Storage/ZFSPlugin-7.1-1_1.pm.patch
new file mode 100644
index 0000000..d791749
--- /dev/null
+++ b/perl5/PVE/Storage/ZFSPlugin-7.1-1_1.pm.patch
@@ -0,0 +1,147 @@
+--- ZFSPlugin.pm.orig 2022-02-04 12:08:01.000000000 -0500
++++ ZFSPlugin.pm 2022-03-26 13:51:40.660068908 -0400
+@@ -10,6 +10,7 @@
+
+ use base qw(PVE::Storage::ZFSPoolPlugin);
+ use PVE::Storage::LunCmd::Comstar;
++use PVE::Storage::LunCmd::FreeNAS;
+ use PVE::Storage::LunCmd::Istgt;
+ use PVE::Storage::LunCmd::Iet;
+ use PVE::Storage::LunCmd::LIO;
+@@ -26,13 +27,14 @@
+ modify_lu => 1,
+ add_view => 1,
+ list_view => 1,
++ list_extent => 1,
+ list_lu => 1,
+ };
+
+ my $zfs_unknown_scsi_provider = sub {
+ my ($provider) = @_;
+
+- die "$provider: unknown iscsi provider. Available [comstar, istgt, iet, LIO]";
++ die "$provider: unknown iscsi provider. Available [comstar, freenas, istgt, iet, LIO]";
+ };
+
+ my $zfs_get_base = sub {
+@@ -40,6 +42,8 @@
+
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ return PVE::Storage::LunCmd::Comstar::get_base;
++ } elsif ($scfg->{iscsiprovider} eq 'freenas') {
++ return PVE::Storage::LunCmd::FreeNAS::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ return PVE::Storage::LunCmd::Istgt::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+@@ -62,6 +66,8 @@
+ if ($lun_cmds->{$method}) {
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
++ } elsif ($scfg->{iscsiprovider} eq 'freenas') {
++ $msg = PVE::Storage::LunCmd::FreeNAS::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+@@ -166,6 +172,15 @@
+ die "lun_number for guid $guid is not a number";
+ }
+
++# Part of the multipath enhancement
++sub zfs_get_wwid_number {
++ my ($class, $scfg, $guid) = @_;
++
++ die "could not find lun_number for guid $guid" if !$guid;
++
++ return $class->zfs_request($scfg, undef, 'list_extent', $guid);
++}
++
+ # Configuration
+
+ sub type {
+@@ -184,6 +199,24 @@
+ description => "iscsi provider",
+ type => 'string',
+ },
++ # This is for FreeNAS iscsi and API intergration
++ # And some enhancements asked by the community
++ freenas_user => {
++ description => "FreeNAS API Username",
++ type => 'string',
++ },
++ freenas_password => {
++ description => "FreeNAS API Password",
++ type => 'string',
++ },
++ freenas_use_ssl => {
++ description => "FreeNAS API access via SSL",
++ type => 'boolean',
++ },
++ freenas_apiv4_host => {
++ description => "FreeNAS API Host",
++ type => 'string',
++ },
+ # this will disable write caching on comstar and istgt.
+ # it is not implemented for iet. iet blockio always operates with
+ # writethrough caching when not in readonly mode
+@@ -211,14 +244,18 @@
+ nodes => { optional => 1 },
+ disable => { optional => 1 },
+ portal => { fixed => 1 },
+- target => { fixed => 1 },
+- pool => { fixed => 1 },
++ target => { fixed => 0 },
++ pool => { fixed => 0 },
+ blocksize => { fixed => 1 },
+ iscsiprovider => { fixed => 1 },
+ nowritecache => { optional => 1 },
+ sparse => { optional => 1 },
+ comstar_hg => { optional => 1 },
+ comstar_tg => { optional => 1 },
++ freenas_user => { optional => 1 },
++ freenas_password => { optional => 1 },
++ freenas_use_ssl => { optional => 1 },
++ freenas_apiv4_host => { optional => 1 },
+ lio_tpg => { optional => 1 },
+ content => { optional => 1 },
+ bwlimit => { optional => 1 },
+@@ -243,6 +280,40 @@
+
+ my $path = "iscsi://$portal/$target/$lun";
+
++ # Multipath enhancement
++ eval {
++ my $wwid = $class->zfs_get_wwid_number($scfg, $guid);
++# syslog(info,"JD: path get_lun_number guid $guid");
++
++ if ($wwid =~ /^([-\@\w.]+)$/) {
++ $wwid = $1; # $data now untainted
++ } else {
++ die "Bad data in '$wwid'"; # log this somewhere
++ }
++ my $wwid_end = substr $wwid, 16;
++
++ my $mapper = '';
++ sleep 3;
++ run_command("iscsiadm -m session --rescan");
++ sleep 3;
++ my $line = `/usr/sbin/multipath -ll | grep \"$wwid_end\"`;
++ my ($mapper_device) = split(' ', $line);
++ $mapper_device = "" unless $mapper_device;
++ $mapper .= $mapper_device;
++
++ if ($mapper =~ /^([-\@\w.]+)$/) {
++ $mapper = $1; # $data now untainted
++ } else {
++ $mapper = '';
++ }
++
++# syslog(info,"Multipath mapper found: $mapper\n");
++ if ($mapper ne "") {
++ $path = "/dev/mapper/$mapper";
++ sleep 5;
++ }
++ };
++
+ return ($path, $vmid, $vtype);
+ }
+
diff --git a/perl5/PVE/Storage/ZFSPlugin.pm.patch b/perl5/PVE/Storage/ZFSPlugin.pm.patch
index 8fadf60..d791749 100644
--- a/perl5/PVE/Storage/ZFSPlugin.pm.patch
+++ b/perl5/PVE/Storage/ZFSPlugin.pm.patch
@@ -1,10 +1,5 @@
-<<<<<<< HEAD
---- ZFSPlugin.pm.orig 2019-09-23 12:17:37.000000000 -0400
-+++ ZFSPlugin.pm 2019-10-13 09:31:58.780554103 -0400
-=======
---- ZFSPlugin.pm.orig 2019-09-03 04:24:37.000000000 -0400
-+++ ZFSPlugin.pm 2019-09-22 13:54:51.570048336 -0400
->>>>>>> branch 'master' of https://github.com/TheGrandWazoo/freenas-proxmox.git
+--- ZFSPlugin.pm.orig 2022-02-04 12:08:01.000000000 -0500
++++ ZFSPlugin.pm 2022-03-26 13:51:40.660068908 -0400
@@ -10,6 +10,7 @@
use base qw(PVE::Storage::ZFSPoolPlugin);
@@ -47,8 +42,8 @@
} elsif ($scfg->{iscsiprovider} eq 'istgt') {
$msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
} elsif ($scfg->{iscsiprovider} eq 'iet') {
-@@ -162,6 +168,15 @@
- return $class->zfs_request($scfg, undef, 'list_view', $guid);
+@@ -166,6 +172,15 @@
+ die "lun_number for guid $guid is not a number";
}
+# Part of the multipath enhancement
@@ -63,7 +58,7 @@
# Configuration
sub type {
-@@ -180,6 +195,24 @@
+@@ -184,6 +199,24 @@
description => "iscsi provider",
type => 'string',
},
@@ -88,7 +83,7 @@
# this will disable write caching on comstar and istgt.
# it is not implemented for iet. iet blockio always operates with
# writethrough caching when not in readonly mode
-@@ -207,14 +240,18 @@
+@@ -211,14 +244,18 @@
nodes => { optional => 1 },
disable => { optional => 1 },
portal => { fixed => 1 },
@@ -109,7 +104,7 @@
lio_tpg => { optional => 1 },
content => { optional => 1 },
bwlimit => { optional => 1 },
-@@ -239,6 +276,40 @@
+@@ -243,6 +280,40 @@
my $path = "iscsi://$portal/$target/$lun";
diff --git a/pve-docs/api-viewer/apidoc-7.1-2_1.js.patch b/pve-docs/api-viewer/apidoc-7.1-2_1.js.patch
new file mode 100644
index 0000000..477f9f4
--- /dev/null
+++ b/pve-docs/api-viewer/apidoc-7.1-2_1.js.patch
@@ -0,0 +1,79 @@
+--- apidoc.js.orig 2021-11-15 10:07:34.000000000 -0500
++++ apidoc.js 2021-12-06 08:04:01.648822707 -0500
+@@ -44064,6 +44064,31 @@
+ "type" : "string",
+ "typetext" : ""
+ },
++ "freenas_user" : {
++ "description" : "FreeNAS user for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_password" : {
++ "description" : "FreeNAS password for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_use_ssl" : {
++ "description" : "FreeNAS API access via SSL",
++ "optional" : 1,
++ "type" : "boolean",
++ "typetext" : ""
++ },
++ "freenas_apiv4_host" : {
++ "description" : "FreeNAS API Host via IPv4",
++ "format" : "address",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
+ "fuse" : {
+ "description" : "Mount CephFS through FUSE.",
+ "optional" : 1,
+@@ -44275,6 +44300,12 @@
+ "type" : "boolean",
+ "typetext" : ""
+ },
++ "target" : {
++ "description" : "iSCSI target.",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
+ "transport" : {
+ "description" : "Gluster transport: tcp or rdma",
+ "enum" : [
+@@ -44547,6 +44578,31 @@
+ "optional" : 1,
+ "type" : "string",
+ "typetext" : ""
++ },
++ "freenas_user" : {
++ "description" : "FreeNAS user for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_password" : {
++ "description" : "FreeNAS password for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_use_ssl" : {
++ "description" : "FreeNAS API access via SSL",
++ "optional" : 1,
++ "type" : "boolean",
++ "typetext" : ""
++ },
++ "freenas_apiv4_host" : {
++ "description" : "FreeNAS API Host via IPv4",
++ "format" : "address",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
+ },
+ "fuse" : {
+ "description" : "Mount CephFS through FUSE.",
diff --git a/pve-docs/api-viewer/apidoc.js.patch b/pve-docs/api-viewer/apidoc.js.patch
index 3bc6dd7..477f9f4 100644
--- a/pve-docs/api-viewer/apidoc.js.patch
+++ b/pve-docs/api-viewer/apidoc.js.patch
@@ -1,6 +1,6 @@
---- apidoc.js.orig 2019-07-15 15:45:00.000000000 -0400
-+++ apidoc.js 2019-08-07 13:28:29.292382229 -0400
-@@ -35001,6 +35001,31 @@
+--- apidoc.js.orig 2021-11-15 10:07:34.000000000 -0500
++++ apidoc.js 2021-12-06 08:04:01.648822707 -0500
+@@ -44064,6 +44064,31 @@
"type" : "string",
"typetext" : ""
},
@@ -32,7 +32,7 @@
"fuse" : {
"description" : "Mount CephFS through FUSE.",
"optional" : 1,
-@@ -35157,6 +35182,12 @@
+@@ -44275,6 +44300,12 @@
"type" : "boolean",
"typetext" : ""
},
@@ -45,7 +45,7 @@
"transport" : {
"description" : "Gluster transport: tcp or rdma",
"enum" : [
-@@ -35362,6 +35393,31 @@
+@@ -44547,6 +44578,31 @@
"optional" : 1,
"type" : "string",
"typetext" : ""
diff --git a/pve-manager/js/pvemanagerlib-6.3-6_1.js.patch b/pve-manager/js/pvemanagerlib-6.3-6_1.js.patch
new file mode 100644
index 0000000..12dc4a5
--- /dev/null
+++ b/pve-manager/js/pvemanagerlib-6.3-6_1.js.patch
@@ -0,0 +1,189 @@
+--- pvemanagerlib.js.orig 2021-03-09 02:22:47.000000000 -0500
++++ pvemanagerlib.js 2021-03-20 11:52:39.694828636 -0400
+@@ -7849,6 +7849,7 @@
+ alias: ['widget.pveiScsiProviderSelector'],
+ comboItems: [
+ ['comstar', 'Comstar'],
++ ['freenas', 'FreeNAS-API'],
+ ['istgt', 'istgt'],
+ ['iet', 'IET'],
+ ['LIO', 'LIO'],
+@@ -46874,6 +46875,7 @@
+ data: {
+ isLIO: false,
+ isComstar: true,
++ isFreeNAS: false,
+ hasWriteCacheOption: true,
+ },
+ },
+@@ -46886,10 +46888,26 @@
+ },
+ },
+ changeISCSIProvider: function(f, newVal, oldVal) {
++ var me = this;
+ var vm = this.getViewModel();
+ vm.set('isLIO', newVal === 'LIO');
+ vm.set('isComstar', newVal === 'comstar');
+- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
++ vm.set('isFreeNAS', newVal === 'freenas');
++ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
++ if (newVal !== 'freenas') {
++ me.lookupReference('freenas_use_ssl_field').setValue(false);
++ me.lookupReference('freenas_apiv4_host_field').setValue('');
++ me.lookupReference('freenas_user_field').setValue('');
++ me.lookupReference('freenas_user_field').allowBlank = true;
++ me.lookupReference('freenas_password_field').setValue('');
++ me.lookupReference('freenas_password_field').allowBlank = true;
++ me.lookupReference('freenas_confirmpw_field').setValue('');
++ me.lookupReference('freenas_confirmpw_field').allowBlank = true;
++ } else {
++ me.lookupReference('freenas_user_field').allowBlank = false;
++ me.lookupReference('freenas_password_field').allowBlank = false;
++ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
++ }
+ },
+ },
+
+@@ -46907,6 +46925,7 @@
+ },
+
+ setValues: function diff(values) {
++ values.freenas_confirmpw = values.freenas_password;
+ values.writecache = values.nowritecache ? 0 : 1;
+ this.callParent([values]);
+ },
+@@ -46923,7 +46942,7 @@
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'pool',
+ value: '',
+ fieldLabel: gettext('Pool'),
+@@ -46933,11 +46952,11 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'blocksize',
+ value: '4k',
+- fieldLabel: gettext('Block Size'),
++ fieldLabel: gettext('ZFS Block Size'),
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'target',
+ value: '',
+ fieldLabel: gettext('Target'),
+@@ -46948,9 +46967,34 @@
+ name: 'comstar_tg',
+ value: '',
+ fieldLabel: gettext('Target group'),
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ allowBlank: true,
+ },
++ {
++ xtype: 'proxmoxcheckbox',
++ name: 'freenas_use_ssl',
++ reference: 'freenas_use_ssl_field',
++ inputId: 'freenas_use_ssl_field',
++ checked: false,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ uncheckedValue: 0,
++ fieldLabel: gettext('API use SSL'),
++ },
++ {
++ xtype: 'textfield',
++ name: 'freenas_user',
++ reference: 'freenas_user_field',
++ inputId: 'freenas_user_field',
++ value: '',
++ fieldLabel: gettext('API Username'),
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ },
+ ];
+
+ me.column2 = [
+@@ -46980,7 +47024,9 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'comstar_hg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ fieldLabel: gettext('Host group'),
+ allowBlank: true,
+ },
+@@ -46988,9 +47034,62 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'lio_tpg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
+- allowBlank: false,
++ bind: {
++ hidden: '{!isLIO}'
++ },
+ fieldLabel: gettext('Target portal group'),
++ allowBlank: true
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_apiv4_host',
++ reference: 'freenas_apiv4_host_field',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API IPv4 Host'),
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_password',
++ reference: 'freenas_password_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API Password'),
++ change: function(f, value) {
++ if (f.rendered) {
++ f.up().down('field[name=freenas_confirmpw]').validate();
++ }
++ },
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_confirmpw',
++ reference: 'freenas_confirmpw_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ submitValue: false,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('Confirm Password'),
++ validator: function(value) {
++ var pw = this.up().down('field[name=freenas_password]').getValue();
++ if (pw !== value) {
++ return "Passwords do not match!";
++ }
++ return true;
++ },
+ },
+ ];
+
diff --git a/pve-manager/js/pvemanagerlib-6.4-8_1.js.patch b/pve-manager/js/pvemanagerlib-6.4-8_1.js.patch
new file mode 100644
index 0000000..90e9098
--- /dev/null
+++ b/pve-manager/js/pvemanagerlib-6.4-8_1.js.patch
@@ -0,0 +1,189 @@
+--- pvemanagerlib.js.orig 2021-05-27 08:28:35.000000000 -0400
++++ pvemanagerlib.js 2021-06-10 11:03:15.380175988 -0400
+@@ -7900,6 +7900,7 @@
+ alias: ['widget.pveiScsiProviderSelector'],
+ comboItems: [
+ ['comstar', 'Comstar'],
++ ['freenas', 'FreeNAS-API'],
+ ['istgt', 'istgt'],
+ ['iet', 'IET'],
+ ['LIO', 'LIO'],
+@@ -47367,6 +47368,7 @@
+ data: {
+ isLIO: false,
+ isComstar: true,
++ isFreeNAS: false,
+ hasWriteCacheOption: true,
+ },
+ },
+@@ -47379,10 +47381,26 @@
+ },
+ },
+ changeISCSIProvider: function(f, newVal, oldVal) {
++ var me = this;
+ var vm = this.getViewModel();
+ vm.set('isLIO', newVal === 'LIO');
+ vm.set('isComstar', newVal === 'comstar');
+- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
++ vm.set('isFreeNAS', newVal === 'freenas');
++ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
++ if (newVal !== 'freenas') {
++ me.lookupReference('freenas_use_ssl_field').setValue(false);
++ me.lookupReference('freenas_apiv4_host_field').setValue('');
++ me.lookupReference('freenas_user_field').setValue('');
++ me.lookupReference('freenas_user_field').allowBlank = true;
++ me.lookupReference('freenas_password_field').setValue('');
++ me.lookupReference('freenas_password_field').allowBlank = true;
++ me.lookupReference('freenas_confirmpw_field').setValue('');
++ me.lookupReference('freenas_confirmpw_field').allowBlank = true;
++ } else {
++ me.lookupReference('freenas_user_field').allowBlank = false;
++ me.lookupReference('freenas_password_field').allowBlank = false;
++ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
++ }
+ },
+ },
+
+@@ -47400,6 +47418,7 @@
+ },
+
+ setValues: function(values) {
++ values.freenas_confirmpw = values.freenas_password;
+ values.writecache = values.nowritecache ? 0 : 1;
+ this.callParent([values]);
+ },
+@@ -47416,7 +47435,7 @@
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'pool',
+ value: '',
+ fieldLabel: gettext('Pool'),
+@@ -47426,11 +47445,11 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'blocksize',
+ value: '4k',
+- fieldLabel: gettext('Block Size'),
++ fieldLabel: gettext('ZFS Block Size'),
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'target',
+ value: '',
+ fieldLabel: gettext('Target'),
+@@ -47441,9 +47460,34 @@
+ name: 'comstar_tg',
+ value: '',
+ fieldLabel: gettext('Target group'),
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ allowBlank: true,
+ },
++ {
++ xtype: 'proxmoxcheckbox',
++ name: 'freenas_use_ssl',
++ reference: 'freenas_use_ssl_field',
++ inputId: 'freenas_use_ssl_field',
++ checked: false,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ uncheckedValue: 0,
++ fieldLabel: gettext('API use SSL'),
++ },
++ {
++ xtype: 'textfield',
++ name: 'freenas_user',
++ reference: 'freenas_user_field',
++ inputId: 'freenas_user_field',
++ value: '',
++ fieldLabel: gettext('API Username'),
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ },
+ ];
+
+ me.column2 = [
+@@ -47473,7 +47517,9 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'comstar_hg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ fieldLabel: gettext('Host group'),
+ allowBlank: true,
+ },
+@@ -47481,9 +47527,62 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'lio_tpg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
+- allowBlank: false,
++ bind: {
++ hidden: '{!isLIO}'
++ },
+ fieldLabel: gettext('Target portal group'),
++ allowBlank: true
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_apiv4_host',
++ reference: 'freenas_apiv4_host_field',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API IPv4 Host'),
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_password',
++ reference: 'freenas_password_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API Password'),
++ change: function(f, value) {
++ if (f.rendered) {
++ f.up().down('field[name=freenas_confirmpw]').validate();
++ }
++ },
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_confirmpw',
++ reference: 'freenas_confirmpw_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ submitValue: false,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('Confirm Password'),
++ validator: function(value) {
++ var pw = this.up().down('field[name=freenas_password]').getValue();
++ if (pw !== value) {
++ return "Passwords do not match!";
++ }
++ return true;
++ },
+ },
+ ];
+
diff --git a/pve-manager/js/pvemanagerlib-7.1-11_1.js.patch b/pve-manager/js/pvemanagerlib-7.1-11_1.js.patch
new file mode 100644
index 0000000..52019ab
--- /dev/null
+++ b/pve-manager/js/pvemanagerlib-7.1-11_1.js.patch
@@ -0,0 +1,189 @@
+--- pvemanagerlib.js.orig 2022-03-17 09:08:40.000000000 -0400
++++ pvemanagerlib.js 2022-04-03 08:54:10.229689187 -0400
+@@ -8068,6 +8068,7 @@
+ alias: ['widget.pveiScsiProviderSelector'],
+ comboItems: [
+ ['comstar', 'Comstar'],
++ ['freenas', 'FreeNAS-API'],
+ ['istgt', 'istgt'],
+ ['iet', 'IET'],
+ ['LIO', 'LIO'],
+@@ -49636,6 +49637,7 @@
+ data: {
+ isLIO: false,
+ isComstar: true,
++ isFreeNAS: false,
+ hasWriteCacheOption: true,
+ },
+ },
+@@ -49648,10 +49650,26 @@
+ },
+ },
+ changeISCSIProvider: function(f, newVal, oldVal) {
++ var me = this;
+ var vm = this.getViewModel();
+ vm.set('isLIO', newVal === 'LIO');
+ vm.set('isComstar', newVal === 'comstar');
+- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
++ vm.set('isFreeNAS', newVal === 'freenas');
++ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
++ if (newVal !== 'freenas') {
++ me.lookupReference('freenas_use_ssl_field').setValue(false);
++ me.lookupReference('freenas_apiv4_host_field').setValue('');
++ me.lookupReference('freenas_user_field').setValue('');
++ me.lookupReference('freenas_user_field').allowBlank = true;
++ me.lookupReference('freenas_password_field').setValue('');
++ me.lookupReference('freenas_password_field').allowBlank = true;
++ me.lookupReference('freenas_confirmpw_field').setValue('');
++ me.lookupReference('freenas_confirmpw_field').allowBlank = true;
++ } else {
++ me.lookupReference('freenas_user_field').allowBlank = false;
++ me.lookupReference('freenas_password_field').allowBlank = false;
++ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
++ }
+ },
+ },
+
+@@ -49669,6 +49687,7 @@
+ },
+
+ setValues: function(values) {
++ values.freenas_confirmpw = values.freenas_password;
+ values.writecache = values.nowritecache ? 0 : 1;
+ this.callParent([values]);
+ },
+@@ -49685,7 +49704,7 @@
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'pool',
+ value: '',
+ fieldLabel: gettext('Pool'),
+@@ -49695,11 +49714,11 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'blocksize',
+ value: '4k',
+- fieldLabel: gettext('Block Size'),
++ fieldLabel: gettext('ZFS Block Size'),
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'target',
+ value: '',
+ fieldLabel: gettext('Target'),
+@@ -49710,9 +49729,34 @@
+ name: 'comstar_tg',
+ value: '',
+ fieldLabel: gettext('Target group'),
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ allowBlank: true,
+ },
++ {
++ xtype: 'proxmoxcheckbox',
++ name: 'freenas_use_ssl',
++ reference: 'freenas_use_ssl_field',
++ inputId: 'freenas_use_ssl_field',
++ checked: false,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ uncheckedValue: 0,
++ fieldLabel: gettext('API use SSL'),
++ },
++ {
++ xtype: 'textfield',
++ name: 'freenas_user',
++ reference: 'freenas_user_field',
++ inputId: 'freenas_user_field',
++ value: '',
++ fieldLabel: gettext('API Username'),
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ },
+ ];
+
+ me.column2 = [
+@@ -49742,7 +49786,9 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'comstar_hg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ fieldLabel: gettext('Host group'),
+ allowBlank: true,
+ },
+@@ -49750,9 +49796,62 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'lio_tpg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
+- allowBlank: false,
++ bind: {
++ hidden: '{!isLIO}'
++ },
+ fieldLabel: gettext('Target portal group'),
++ allowBlank: true
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_apiv4_host',
++ reference: 'freenas_apiv4_host_field',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API IPv4 Host'),
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_password',
++ reference: 'freenas_password_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API Password'),
++ change: function(f, value) {
++ if (f.rendered) {
++ f.up().down('field[name=freenas_confirmpw]').validate();
++ }
++ },
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_confirmpw',
++ reference: 'freenas_confirmpw_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ submitValue: false,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('Confirm Password'),
++ validator: function(value) {
++ var pw = this.up().down('field[name=freenas_password]').getValue();
++ if (pw !== value) {
++ return "Passwords do not match!";
++ }
++ return true;
++ },
+ },
+ ];
+
diff --git a/pve-manager/js/pvemanagerlib.js.patch b/pve-manager/js/pvemanagerlib.js.patch
index b4da53d..52019ab 100644
--- a/pve-manager/js/pvemanagerlib.js.patch
+++ b/pve-manager/js/pvemanagerlib.js.patch
@@ -1,23 +1,23 @@
---- pvemanagerlib.js.orig 2019-09-03 04:31:21.000000000 -0400
-+++ pvemanagerlib.js 2019-09-22 13:54:51.602048049 -0400
-@@ -6183,6 +6183,7 @@
+--- pvemanagerlib.js.orig 2022-03-17 09:08:40.000000000 -0400
++++ pvemanagerlib.js 2022-04-03 08:54:10.229689187 -0400
+@@ -8068,6 +8068,7 @@
alias: ['widget.pveiScsiProviderSelector'],
comboItems: [
['comstar', 'Comstar'],
+ ['freenas', 'FreeNAS-API'],
- [ 'istgt', 'istgt'],
- [ 'iet', 'IET'],
- [ 'LIO', 'LIO']
-@@ -32992,6 +32993,7 @@
+ ['istgt', 'istgt'],
+ ['iet', 'IET'],
+ ['LIO', 'LIO'],
+@@ -49636,6 +49637,7 @@
data: {
isLIO: false,
isComstar: true,
-+ isFreeNAS: false,
- hasWriteCacheOption: true
- }
++ isFreeNAS: false,
+ hasWriteCacheOption: true,
+ },
},
-@@ -33004,10 +33006,26 @@
- }
+@@ -49648,10 +49650,26 @@
+ },
},
changeISCSIProvider: function(f, newVal, oldVal) {
+ var me = this;
@@ -41,19 +41,19 @@
+ me.lookupReference('freenas_password_field').allowBlank = false;
+ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
+ }
- }
+ },
},
-@@ -33025,6 +33043,7 @@
+@@ -49669,6 +49687,7 @@
},
- setValues: function diff(values) {
-+ values.freenas_confirmpw = values.freenas_password;
+ setValues: function(values) {
++ values.freenas_confirmpw = values.freenas_password;
values.writecache = values.nowritecache ? 0 : 1;
this.callParent([values]);
},
-@@ -33041,7 +33060,7 @@
- allowBlank: false
+@@ -49685,7 +49704,7 @@
+ allowBlank: false,
},
{
- xtype: me.isCreate ? 'textfield' : 'displayfield',
@@ -61,13 +61,13 @@
name: 'pool',
value: '',
fieldLabel: gettext('Pool'),
-@@ -33051,11 +33070,11 @@
+@@ -49695,11 +49714,11 @@
xtype: me.isCreate ? 'textfield' : 'displayfield',
name: 'blocksize',
value: '4k',
- fieldLabel: gettext('Block Size'),
+ fieldLabel: gettext('ZFS Block Size'),
- allowBlank: false
+ allowBlank: false,
},
{
- xtype: me.isCreate ? 'textfield' : 'displayfield',
@@ -75,7 +75,7 @@
name: 'target',
value: '',
fieldLabel: gettext('Target'),
-@@ -33066,8 +33085,33 @@
+@@ -49710,9 +49729,34 @@
name: 'comstar_tg',
value: '',
fieldLabel: gettext('Target group'),
@@ -83,8 +83,8 @@
+ bind: {
+ hidden: '{!isComstar}'
+ },
- allowBlank: true
-+ },
+ allowBlank: true,
+ },
+ {
+ xtype: 'proxmoxcheckbox',
+ name: 'freenas_use_ssl',
@@ -95,7 +95,7 @@
+ hidden: '{!isFreeNAS}'
+ },
+ uncheckedValue: 0,
-+ fieldLabel: gettext('API use SSL')
++ fieldLabel: gettext('API use SSL'),
+ },
+ {
+ xtype: 'textfield',
@@ -106,11 +106,12 @@
+ fieldLabel: gettext('API Username'),
+ bind: {
+ hidden: '{!isFreeNAS}'
-+ }
- }
++ },
++ },
];
-@@ -33098,7 +33142,9 @@
+ me.column2 = [
+@@ -49742,7 +49786,9 @@
xtype: me.isCreate ? 'textfield' : 'displayfield',
name: 'comstar_hg',
value: '',
@@ -119,19 +120,18 @@
+ hidden: '{!isComstar}'
+ },
fieldLabel: gettext('Host group'),
- allowBlank: true
+ allowBlank: true,
},
-@@ -33106,9 +33152,62 @@
+@@ -49750,9 +49796,62 @@
xtype: me.isCreate ? 'textfield' : 'displayfield',
name: 'lio_tpg',
value: '',
- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
- allowBlank: false,
-- fieldLabel: gettext('Target portal group')
+ bind: {
+ hidden: '{!isLIO}'
+ },
-+ fieldLabel: gettext('Target portal group'),
+ fieldLabel: gettext('Target portal group'),
+ allowBlank: true
+ },
+ {
@@ -162,7 +162,7 @@
+ if (f.rendered) {
+ f.up().down('field[name=freenas_confirmpw]').validate();
+ }
-+ }
++ },
+ },
+ {
+ xtype: 'proxmoxtextfield',
@@ -183,7 +183,7 @@
+ return "Passwords do not match!";
+ }
+ return true;
-+ }
- }
++ },
+ },
];
diff --git a/stable-7/perl5/PVE/Storage/ZFSPlugin-7.1-1_1.pm.patch b/stable-7/perl5/PVE/Storage/ZFSPlugin-7.1-1_1.pm.patch
new file mode 100644
index 0000000..d791749
--- /dev/null
+++ b/stable-7/perl5/PVE/Storage/ZFSPlugin-7.1-1_1.pm.patch
@@ -0,0 +1,147 @@
+--- ZFSPlugin.pm.orig 2022-02-04 12:08:01.000000000 -0500
++++ ZFSPlugin.pm 2022-03-26 13:51:40.660068908 -0400
+@@ -10,6 +10,7 @@
+
+ use base qw(PVE::Storage::ZFSPoolPlugin);
+ use PVE::Storage::LunCmd::Comstar;
++use PVE::Storage::LunCmd::FreeNAS;
+ use PVE::Storage::LunCmd::Istgt;
+ use PVE::Storage::LunCmd::Iet;
+ use PVE::Storage::LunCmd::LIO;
+@@ -26,13 +27,14 @@
+ modify_lu => 1,
+ add_view => 1,
+ list_view => 1,
++ list_extent => 1,
+ list_lu => 1,
+ };
+
+ my $zfs_unknown_scsi_provider = sub {
+ my ($provider) = @_;
+
+- die "$provider: unknown iscsi provider. Available [comstar, istgt, iet, LIO]";
++ die "$provider: unknown iscsi provider. Available [comstar, freenas, istgt, iet, LIO]";
+ };
+
+ my $zfs_get_base = sub {
+@@ -40,6 +42,8 @@
+
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ return PVE::Storage::LunCmd::Comstar::get_base;
++ } elsif ($scfg->{iscsiprovider} eq 'freenas') {
++ return PVE::Storage::LunCmd::FreeNAS::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ return PVE::Storage::LunCmd::Istgt::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+@@ -62,6 +66,8 @@
+ if ($lun_cmds->{$method}) {
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
++ } elsif ($scfg->{iscsiprovider} eq 'freenas') {
++ $msg = PVE::Storage::LunCmd::FreeNAS::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+@@ -166,6 +172,15 @@
+ die "lun_number for guid $guid is not a number";
+ }
+
++# Part of the multipath enhancement
++sub zfs_get_wwid_number {
++ my ($class, $scfg, $guid) = @_;
++
++ die "could not find lun_number for guid $guid" if !$guid;
++
++ return $class->zfs_request($scfg, undef, 'list_extent', $guid);
++}
++
+ # Configuration
+
+ sub type {
+@@ -184,6 +199,24 @@
+ description => "iscsi provider",
+ type => 'string',
+ },
++ # This is for FreeNAS iscsi and API intergration
++ # And some enhancements asked by the community
++ freenas_user => {
++ description => "FreeNAS API Username",
++ type => 'string',
++ },
++ freenas_password => {
++ description => "FreeNAS API Password",
++ type => 'string',
++ },
++ freenas_use_ssl => {
++ description => "FreeNAS API access via SSL",
++ type => 'boolean',
++ },
++ freenas_apiv4_host => {
++ description => "FreeNAS API Host",
++ type => 'string',
++ },
+ # this will disable write caching on comstar and istgt.
+ # it is not implemented for iet. iet blockio always operates with
+ # writethrough caching when not in readonly mode
+@@ -211,14 +244,18 @@
+ nodes => { optional => 1 },
+ disable => { optional => 1 },
+ portal => { fixed => 1 },
+- target => { fixed => 1 },
+- pool => { fixed => 1 },
++ target => { fixed => 0 },
++ pool => { fixed => 0 },
+ blocksize => { fixed => 1 },
+ iscsiprovider => { fixed => 1 },
+ nowritecache => { optional => 1 },
+ sparse => { optional => 1 },
+ comstar_hg => { optional => 1 },
+ comstar_tg => { optional => 1 },
++ freenas_user => { optional => 1 },
++ freenas_password => { optional => 1 },
++ freenas_use_ssl => { optional => 1 },
++ freenas_apiv4_host => { optional => 1 },
+ lio_tpg => { optional => 1 },
+ content => { optional => 1 },
+ bwlimit => { optional => 1 },
+@@ -243,6 +280,40 @@
+
+ my $path = "iscsi://$portal/$target/$lun";
+
++ # Multipath enhancement
++ eval {
++ my $wwid = $class->zfs_get_wwid_number($scfg, $guid);
++# syslog(info,"JD: path get_lun_number guid $guid");
++
++ if ($wwid =~ /^([-\@\w.]+)$/) {
++ $wwid = $1; # $data now untainted
++ } else {
++ die "Bad data in '$wwid'"; # log this somewhere
++ }
++ my $wwid_end = substr $wwid, 16;
++
++ my $mapper = '';
++ sleep 3;
++ run_command("iscsiadm -m session --rescan");
++ sleep 3;
++ my $line = `/usr/sbin/multipath -ll | grep \"$wwid_end\"`;
++ my ($mapper_device) = split(' ', $line);
++ $mapper_device = "" unless $mapper_device;
++ $mapper .= $mapper_device;
++
++ if ($mapper =~ /^([-\@\w.]+)$/) {
++ $mapper = $1; # $data now untainted
++ } else {
++ $mapper = '';
++ }
++
++# syslog(info,"Multipath mapper found: $mapper\n");
++ if ($mapper ne "") {
++ $path = "/dev/mapper/$mapper";
++ sleep 5;
++ }
++ };
++
+ return ($path, $vmid, $vtype);
+ }
+
diff --git a/stable-7/perl5/PVE/Storage/ZFSPlugin.pm.patch b/stable-7/perl5/PVE/Storage/ZFSPlugin.pm.patch
new file mode 100644
index 0000000..d791749
--- /dev/null
+++ b/stable-7/perl5/PVE/Storage/ZFSPlugin.pm.patch
@@ -0,0 +1,147 @@
+--- ZFSPlugin.pm.orig 2022-02-04 12:08:01.000000000 -0500
++++ ZFSPlugin.pm 2022-03-26 13:51:40.660068908 -0400
+@@ -10,6 +10,7 @@
+
+ use base qw(PVE::Storage::ZFSPoolPlugin);
+ use PVE::Storage::LunCmd::Comstar;
++use PVE::Storage::LunCmd::FreeNAS;
+ use PVE::Storage::LunCmd::Istgt;
+ use PVE::Storage::LunCmd::Iet;
+ use PVE::Storage::LunCmd::LIO;
+@@ -26,13 +27,14 @@
+ modify_lu => 1,
+ add_view => 1,
+ list_view => 1,
++ list_extent => 1,
+ list_lu => 1,
+ };
+
+ my $zfs_unknown_scsi_provider = sub {
+ my ($provider) = @_;
+
+- die "$provider: unknown iscsi provider. Available [comstar, istgt, iet, LIO]";
++ die "$provider: unknown iscsi provider. Available [comstar, freenas, istgt, iet, LIO]";
+ };
+
+ my $zfs_get_base = sub {
+@@ -40,6 +42,8 @@
+
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ return PVE::Storage::LunCmd::Comstar::get_base;
++ } elsif ($scfg->{iscsiprovider} eq 'freenas') {
++ return PVE::Storage::LunCmd::FreeNAS::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ return PVE::Storage::LunCmd::Istgt::get_base;
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+@@ -62,6 +66,8 @@
+ if ($lun_cmds->{$method}) {
+ if ($scfg->{iscsiprovider} eq 'comstar') {
+ $msg = PVE::Storage::LunCmd::Comstar::run_lun_command($scfg, $timeout, $method, @params);
++ } elsif ($scfg->{iscsiprovider} eq 'freenas') {
++ $msg = PVE::Storage::LunCmd::FreeNAS::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'istgt') {
+ $msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
+ } elsif ($scfg->{iscsiprovider} eq 'iet') {
+@@ -166,6 +172,15 @@
+ die "lun_number for guid $guid is not a number";
+ }
+
++# Part of the multipath enhancement
++sub zfs_get_wwid_number {
++ my ($class, $scfg, $guid) = @_;
++
++ die "could not find lun_number for guid $guid" if !$guid;
++
++ return $class->zfs_request($scfg, undef, 'list_extent', $guid);
++}
++
+ # Configuration
+
+ sub type {
+@@ -184,6 +199,24 @@
+ description => "iscsi provider",
+ type => 'string',
+ },
++ # This is for FreeNAS iscsi and API intergration
++ # And some enhancements asked by the community
++ freenas_user => {
++ description => "FreeNAS API Username",
++ type => 'string',
++ },
++ freenas_password => {
++ description => "FreeNAS API Password",
++ type => 'string',
++ },
++ freenas_use_ssl => {
++ description => "FreeNAS API access via SSL",
++ type => 'boolean',
++ },
++ freenas_apiv4_host => {
++ description => "FreeNAS API Host",
++ type => 'string',
++ },
+ # this will disable write caching on comstar and istgt.
+ # it is not implemented for iet. iet blockio always operates with
+ # writethrough caching when not in readonly mode
+@@ -211,14 +244,18 @@
+ nodes => { optional => 1 },
+ disable => { optional => 1 },
+ portal => { fixed => 1 },
+- target => { fixed => 1 },
+- pool => { fixed => 1 },
++ target => { fixed => 0 },
++ pool => { fixed => 0 },
+ blocksize => { fixed => 1 },
+ iscsiprovider => { fixed => 1 },
+ nowritecache => { optional => 1 },
+ sparse => { optional => 1 },
+ comstar_hg => { optional => 1 },
+ comstar_tg => { optional => 1 },
++ freenas_user => { optional => 1 },
++ freenas_password => { optional => 1 },
++ freenas_use_ssl => { optional => 1 },
++ freenas_apiv4_host => { optional => 1 },
+ lio_tpg => { optional => 1 },
+ content => { optional => 1 },
+ bwlimit => { optional => 1 },
+@@ -243,6 +280,40 @@
+
+ my $path = "iscsi://$portal/$target/$lun";
+
++ # Multipath enhancement
++ eval {
++ my $wwid = $class->zfs_get_wwid_number($scfg, $guid);
++# syslog(info,"JD: path get_lun_number guid $guid");
++
++ if ($wwid =~ /^([-\@\w.]+)$/) {
++ $wwid = $1; # $data now untainted
++ } else {
++ die "Bad data in '$wwid'"; # log this somewhere
++ }
++ my $wwid_end = substr $wwid, 16;
++
++ my $mapper = '';
++ sleep 3;
++ run_command("iscsiadm -m session --rescan");
++ sleep 3;
++ my $line = `/usr/sbin/multipath -ll | grep \"$wwid_end\"`;
++ my ($mapper_device) = split(' ', $line);
++ $mapper_device = "" unless $mapper_device;
++ $mapper .= $mapper_device;
++
++ if ($mapper =~ /^([-\@\w.]+)$/) {
++ $mapper = $1; # $data now untainted
++ } else {
++ $mapper = '';
++ }
++
++# syslog(info,"Multipath mapper found: $mapper\n");
++ if ($mapper ne "") {
++ $path = "/dev/mapper/$mapper";
++ sleep 5;
++ }
++ };
++
+ return ($path, $vmid, $vtype);
+ }
+
diff --git a/stable-7/pve-docs/api-viewer/apidoc-7.1-2_1.js.patch b/stable-7/pve-docs/api-viewer/apidoc-7.1-2_1.js.patch
new file mode 100644
index 0000000..477f9f4
--- /dev/null
+++ b/stable-7/pve-docs/api-viewer/apidoc-7.1-2_1.js.patch
@@ -0,0 +1,79 @@
+--- apidoc.js.orig 2021-11-15 10:07:34.000000000 -0500
++++ apidoc.js 2021-12-06 08:04:01.648822707 -0500
+@@ -44064,6 +44064,31 @@
+ "type" : "string",
+ "typetext" : ""
+ },
++ "freenas_user" : {
++ "description" : "FreeNAS user for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_password" : {
++ "description" : "FreeNAS password for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_use_ssl" : {
++ "description" : "FreeNAS API access via SSL",
++ "optional" : 1,
++ "type" : "boolean",
++ "typetext" : ""
++ },
++ "freenas_apiv4_host" : {
++ "description" : "FreeNAS API Host via IPv4",
++ "format" : "address",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
+ "fuse" : {
+ "description" : "Mount CephFS through FUSE.",
+ "optional" : 1,
+@@ -44275,6 +44300,12 @@
+ "type" : "boolean",
+ "typetext" : ""
+ },
++ "target" : {
++ "description" : "iSCSI target.",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
+ "transport" : {
+ "description" : "Gluster transport: tcp or rdma",
+ "enum" : [
+@@ -44547,6 +44578,31 @@
+ "optional" : 1,
+ "type" : "string",
+ "typetext" : ""
++ },
++ "freenas_user" : {
++ "description" : "FreeNAS user for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_password" : {
++ "description" : "FreeNAS password for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_use_ssl" : {
++ "description" : "FreeNAS API access via SSL",
++ "optional" : 1,
++ "type" : "boolean",
++ "typetext" : ""
++ },
++ "freenas_apiv4_host" : {
++ "description" : "FreeNAS API Host via IPv4",
++ "format" : "address",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
+ },
+ "fuse" : {
+ "description" : "Mount CephFS through FUSE.",
diff --git a/stable-7/pve-docs/api-viewer/apidoc.js.patch b/stable-7/pve-docs/api-viewer/apidoc.js.patch
new file mode 100644
index 0000000..477f9f4
--- /dev/null
+++ b/stable-7/pve-docs/api-viewer/apidoc.js.patch
@@ -0,0 +1,79 @@
+--- apidoc.js.orig 2021-11-15 10:07:34.000000000 -0500
++++ apidoc.js 2021-12-06 08:04:01.648822707 -0500
+@@ -44064,6 +44064,31 @@
+ "type" : "string",
+ "typetext" : ""
+ },
++ "freenas_user" : {
++ "description" : "FreeNAS user for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_password" : {
++ "description" : "FreeNAS password for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_use_ssl" : {
++ "description" : "FreeNAS API access via SSL",
++ "optional" : 1,
++ "type" : "boolean",
++ "typetext" : ""
++ },
++ "freenas_apiv4_host" : {
++ "description" : "FreeNAS API Host via IPv4",
++ "format" : "address",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
+ "fuse" : {
+ "description" : "Mount CephFS through FUSE.",
+ "optional" : 1,
+@@ -44275,6 +44300,12 @@
+ "type" : "boolean",
+ "typetext" : ""
+ },
++ "target" : {
++ "description" : "iSCSI target.",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
+ "transport" : {
+ "description" : "Gluster transport: tcp or rdma",
+ "enum" : [
+@@ -44547,6 +44578,31 @@
+ "optional" : 1,
+ "type" : "string",
+ "typetext" : ""
++ },
++ "freenas_user" : {
++ "description" : "FreeNAS user for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_password" : {
++ "description" : "FreeNAS password for API access",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
++ },
++ "freenas_use_ssl" : {
++ "description" : "FreeNAS API access via SSL",
++ "optional" : 1,
++ "type" : "boolean",
++ "typetext" : ""
++ },
++ "freenas_apiv4_host" : {
++ "description" : "FreeNAS API Host via IPv4",
++ "format" : "address",
++ "optional" : 1,
++ "type" : "string",
++ "typetext" : ""
+ },
+ "fuse" : {
+ "description" : "Mount CephFS through FUSE.",
diff --git a/stable-7/pve-manager/js/pvemanagerlib-7.1-11_1.js.patch b/stable-7/pve-manager/js/pvemanagerlib-7.1-11_1.js.patch
new file mode 100644
index 0000000..52019ab
--- /dev/null
+++ b/stable-7/pve-manager/js/pvemanagerlib-7.1-11_1.js.patch
@@ -0,0 +1,189 @@
+--- pvemanagerlib.js.orig 2022-03-17 09:08:40.000000000 -0400
++++ pvemanagerlib.js 2022-04-03 08:54:10.229689187 -0400
+@@ -8068,6 +8068,7 @@
+ alias: ['widget.pveiScsiProviderSelector'],
+ comboItems: [
+ ['comstar', 'Comstar'],
++ ['freenas', 'FreeNAS-API'],
+ ['istgt', 'istgt'],
+ ['iet', 'IET'],
+ ['LIO', 'LIO'],
+@@ -49636,6 +49637,7 @@
+ data: {
+ isLIO: false,
+ isComstar: true,
++ isFreeNAS: false,
+ hasWriteCacheOption: true,
+ },
+ },
+@@ -49648,10 +49650,26 @@
+ },
+ },
+ changeISCSIProvider: function(f, newVal, oldVal) {
++ var me = this;
+ var vm = this.getViewModel();
+ vm.set('isLIO', newVal === 'LIO');
+ vm.set('isComstar', newVal === 'comstar');
+- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
++ vm.set('isFreeNAS', newVal === 'freenas');
++ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
++ if (newVal !== 'freenas') {
++ me.lookupReference('freenas_use_ssl_field').setValue(false);
++ me.lookupReference('freenas_apiv4_host_field').setValue('');
++ me.lookupReference('freenas_user_field').setValue('');
++ me.lookupReference('freenas_user_field').allowBlank = true;
++ me.lookupReference('freenas_password_field').setValue('');
++ me.lookupReference('freenas_password_field').allowBlank = true;
++ me.lookupReference('freenas_confirmpw_field').setValue('');
++ me.lookupReference('freenas_confirmpw_field').allowBlank = true;
++ } else {
++ me.lookupReference('freenas_user_field').allowBlank = false;
++ me.lookupReference('freenas_password_field').allowBlank = false;
++ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
++ }
+ },
+ },
+
+@@ -49669,6 +49687,7 @@
+ },
+
+ setValues: function(values) {
++ values.freenas_confirmpw = values.freenas_password;
+ values.writecache = values.nowritecache ? 0 : 1;
+ this.callParent([values]);
+ },
+@@ -49685,7 +49704,7 @@
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'pool',
+ value: '',
+ fieldLabel: gettext('Pool'),
+@@ -49695,11 +49714,11 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'blocksize',
+ value: '4k',
+- fieldLabel: gettext('Block Size'),
++ fieldLabel: gettext('ZFS Block Size'),
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'target',
+ value: '',
+ fieldLabel: gettext('Target'),
+@@ -49710,9 +49729,34 @@
+ name: 'comstar_tg',
+ value: '',
+ fieldLabel: gettext('Target group'),
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ allowBlank: true,
+ },
++ {
++ xtype: 'proxmoxcheckbox',
++ name: 'freenas_use_ssl',
++ reference: 'freenas_use_ssl_field',
++ inputId: 'freenas_use_ssl_field',
++ checked: false,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ uncheckedValue: 0,
++ fieldLabel: gettext('API use SSL'),
++ },
++ {
++ xtype: 'textfield',
++ name: 'freenas_user',
++ reference: 'freenas_user_field',
++ inputId: 'freenas_user_field',
++ value: '',
++ fieldLabel: gettext('API Username'),
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ },
+ ];
+
+ me.column2 = [
+@@ -49742,7 +49786,9 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'comstar_hg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ fieldLabel: gettext('Host group'),
+ allowBlank: true,
+ },
+@@ -49750,9 +49796,62 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'lio_tpg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
+- allowBlank: false,
++ bind: {
++ hidden: '{!isLIO}'
++ },
+ fieldLabel: gettext('Target portal group'),
++ allowBlank: true
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_apiv4_host',
++ reference: 'freenas_apiv4_host_field',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API IPv4 Host'),
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_password',
++ reference: 'freenas_password_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API Password'),
++ change: function(f, value) {
++ if (f.rendered) {
++ f.up().down('field[name=freenas_confirmpw]').validate();
++ }
++ },
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_confirmpw',
++ reference: 'freenas_confirmpw_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ submitValue: false,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('Confirm Password'),
++ validator: function(value) {
++ var pw = this.up().down('field[name=freenas_password]').getValue();
++ if (pw !== value) {
++ return "Passwords do not match!";
++ }
++ return true;
++ },
+ },
+ ];
+
diff --git a/stable-7/pve-manager/js/pvemanagerlib.js.patch b/stable-7/pve-manager/js/pvemanagerlib.js.patch
new file mode 100644
index 0000000..52019ab
--- /dev/null
+++ b/stable-7/pve-manager/js/pvemanagerlib.js.patch
@@ -0,0 +1,189 @@
+--- pvemanagerlib.js.orig 2022-03-17 09:08:40.000000000 -0400
++++ pvemanagerlib.js 2022-04-03 08:54:10.229689187 -0400
+@@ -8068,6 +8068,7 @@
+ alias: ['widget.pveiScsiProviderSelector'],
+ comboItems: [
+ ['comstar', 'Comstar'],
++ ['freenas', 'FreeNAS-API'],
+ ['istgt', 'istgt'],
+ ['iet', 'IET'],
+ ['LIO', 'LIO'],
+@@ -49636,6 +49637,7 @@
+ data: {
+ isLIO: false,
+ isComstar: true,
++ isFreeNAS: false,
+ hasWriteCacheOption: true,
+ },
+ },
+@@ -49648,10 +49650,26 @@
+ },
+ },
+ changeISCSIProvider: function(f, newVal, oldVal) {
++ var me = this;
+ var vm = this.getViewModel();
+ vm.set('isLIO', newVal === 'LIO');
+ vm.set('isComstar', newVal === 'comstar');
+- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
++ vm.set('isFreeNAS', newVal === 'freenas');
++ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
++ if (newVal !== 'freenas') {
++ me.lookupReference('freenas_use_ssl_field').setValue(false);
++ me.lookupReference('freenas_apiv4_host_field').setValue('');
++ me.lookupReference('freenas_user_field').setValue('');
++ me.lookupReference('freenas_user_field').allowBlank = true;
++ me.lookupReference('freenas_password_field').setValue('');
++ me.lookupReference('freenas_password_field').allowBlank = true;
++ me.lookupReference('freenas_confirmpw_field').setValue('');
++ me.lookupReference('freenas_confirmpw_field').allowBlank = true;
++ } else {
++ me.lookupReference('freenas_user_field').allowBlank = false;
++ me.lookupReference('freenas_password_field').allowBlank = false;
++ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
++ }
+ },
+ },
+
+@@ -49669,6 +49687,7 @@
+ },
+
+ setValues: function(values) {
++ values.freenas_confirmpw = values.freenas_password;
+ values.writecache = values.nowritecache ? 0 : 1;
+ this.callParent([values]);
+ },
+@@ -49685,7 +49704,7 @@
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'pool',
+ value: '',
+ fieldLabel: gettext('Pool'),
+@@ -49695,11 +49714,11 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'blocksize',
+ value: '4k',
+- fieldLabel: gettext('Block Size'),
++ fieldLabel: gettext('ZFS Block Size'),
+ allowBlank: false,
+ },
+ {
+- xtype: me.isCreate ? 'textfield' : 'displayfield',
++ xtype: 'textfield',
+ name: 'target',
+ value: '',
+ fieldLabel: gettext('Target'),
+@@ -49710,9 +49729,34 @@
+ name: 'comstar_tg',
+ value: '',
+ fieldLabel: gettext('Target group'),
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ allowBlank: true,
+ },
++ {
++ xtype: 'proxmoxcheckbox',
++ name: 'freenas_use_ssl',
++ reference: 'freenas_use_ssl_field',
++ inputId: 'freenas_use_ssl_field',
++ checked: false,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ uncheckedValue: 0,
++ fieldLabel: gettext('API use SSL'),
++ },
++ {
++ xtype: 'textfield',
++ name: 'freenas_user',
++ reference: 'freenas_user_field',
++ inputId: 'freenas_user_field',
++ value: '',
++ fieldLabel: gettext('API Username'),
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ },
+ ];
+
+ me.column2 = [
+@@ -49742,7 +49786,9 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'comstar_hg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isComstar}' } : { hidden: '{!isComstar}' },
++ bind: {
++ hidden: '{!isComstar}'
++ },
+ fieldLabel: gettext('Host group'),
+ allowBlank: true,
+ },
+@@ -49750,9 +49796,62 @@
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
+ name: 'lio_tpg',
+ value: '',
+- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
+- allowBlank: false,
++ bind: {
++ hidden: '{!isLIO}'
++ },
+ fieldLabel: gettext('Target portal group'),
++ allowBlank: true
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_apiv4_host',
++ reference: 'freenas_apiv4_host_field',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API IPv4 Host'),
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_password',
++ reference: 'freenas_password_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('API Password'),
++ change: function(f, value) {
++ if (f.rendered) {
++ f.up().down('field[name=freenas_confirmpw]').validate();
++ }
++ },
++ },
++ {
++ xtype: 'proxmoxtextfield',
++ name: 'freenas_confirmpw',
++ reference: 'freenas_confirmpw_field',
++ inputType: me.isCreate ? '' : 'password',
++ value: '',
++ editable: true,
++ submitValue: false,
++ emptyText: Proxmox.Utils.noneText,
++ bind: {
++ hidden: '{!isFreeNAS}'
++ },
++ fieldLabel: gettext('Confirm Password'),
++ validator: function(value) {
++ var pw = this.up().down('field[name=freenas_password]').getValue();
++ if (pw !== value) {
++ return "Passwords do not match!";
++ }
++ return true;
++ },
+ },
+ ];
+