Update for Proxmox 8 and Bearer Token
- Patch updates for Proxmox VE 8 - Update to select Basic or Bearer authentication.
This commit is contained in:
parent
466d819a89
commit
b9dd1d6f89
|
|
@ -128,10 +128,13 @@ sub run_lun_command {
|
||||||
|
|
||||||
syslog("info",(caller(0))[3] . " : $method(@params)");
|
syslog("info",(caller(0))[3] . " : $method(@params)");
|
||||||
|
|
||||||
if(!defined($scfg->{'freenas_user'}) || !defined($scfg->{'freenas_password'})) {
|
if (defined($scfg->{'truenas_token_auth'}) && $scfg->{'truenas_token_auth'}) {
|
||||||
die "Undefined freenas_user and/or freenas_password.";
|
if (!defined($scfg->{'truenas_secret'})) {
|
||||||
|
die "Undefined `truenas_secret` variable.";
|
||||||
|
}
|
||||||
|
} elsif (!defined($scfg->{'freenas_user'}) || !defined($scfg->{'freenas_password'})) {
|
||||||
|
die "Undefined `freenas_user` and/or `freenas_password` variables.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defined $freenas_server_list->{defined($scfg->{freenas_apiv4_host}) ? $scfg->{freenas_apiv4_host} : $scfg->{portal}}) {
|
if (!defined $freenas_server_list->{defined($scfg->{freenas_apiv4_host}) ? $scfg->{freenas_apiv4_host} : $scfg->{portal}}) {
|
||||||
freenas_api_check($scfg);
|
freenas_api_check($scfg);
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +344,13 @@ sub freenas_api_connect {
|
||||||
}
|
}
|
||||||
$freenas_server_list->{$apihost}->setHost($scheme . '://' . $apihost);
|
$freenas_server_list->{$apihost}->setHost($scheme . '://' . $apihost);
|
||||||
$freenas_server_list->{$apihost}->addHeader('Content-Type', 'application/json');
|
$freenas_server_list->{$apihost}->addHeader('Content-Type', 'application/json');
|
||||||
$freenas_server_list->{$apihost}->addHeader('Authorization', 'Basic ' . encode_base64($scfg->{freenas_user} . ':' . $scfg->{freenas_password}));
|
if (defined($scfg->{'truenas_token_auth'})) {
|
||||||
|
syslog("info", (caller(0))[3] . " : Authentication using Bearer Token Auth");
|
||||||
|
$freenas_server_list->{$apihost}->addHeader('Authorization', 'Bearer ' . $scfg->{truenas_secret});
|
||||||
|
} else {
|
||||||
|
syslog("info", (caller(0))[3] . " : Authentication using Basic Auth");
|
||||||
|
$freenas_server_list->{$apihost}->addHeader('Authorization', 'Basic ' . encode_base64($scfg->{freenas_user} . ':' . $scfg->{freenas_password}));
|
||||||
|
}
|
||||||
# If using SSL, don't verify SSL certs
|
# If using SSL, don't verify SSL certs
|
||||||
if ($scfg->{freenas_use_ssl}) {
|
if ($scfg->{freenas_use_ssl}) {
|
||||||
$freenas_server_list->{$apihost}->getUseragent()->ssl_opts(verify_hostname => 0);
|
$freenas_server_list->{$apihost}->getUseragent()->ssl_opts(verify_hostname => 0);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
--- ZFSPlugin.pm.orig 2023-12-31 09:56:18.895228853 -0500
|
||||||
|
+++ ZFSPlugin.pm 2023-12-31 09:57:08.830488875 -0500
|
||||||
|
@@ -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,32 @@
|
||||||
|
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 (Deprecated)",
|
||||||
|
+ type => 'string',
|
||||||
|
+ },
|
||||||
|
+ truenas_secret => {
|
||||||
|
+ description => "TrueNAS API Secret",
|
||||||
|
+ type => 'string',
|
||||||
|
+ },
|
||||||
|
+ truenas_token_auth => {
|
||||||
|
+ description => "TrueNAS API Authentication with Token",
|
||||||
|
+ type => 'boolean',
|
||||||
|
+ },
|
||||||
|
+ 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 +252,20 @@
|
||||||
|
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 },
|
||||||
|
+ truenas_secret => { optional => 1 },
|
||||||
|
+ truenas_token_auth => { optional => 1 },
|
||||||
|
+ freenas_use_ssl => { optional => 1 },
|
||||||
|
+ freenas_apiv4_host => { optional => 1 },
|
||||||
|
lio_tpg => { optional => 1 },
|
||||||
|
content => { optional => 1 },
|
||||||
|
bwlimit => { optional => 1 },
|
||||||
|
@@ -243,6 +290,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
--- ZFSPlugin.pm.orig 2022-02-04 12:08:01.000000000 -0500
|
--- ZFSPlugin.pm.orig 2023-12-31 09:56:18.895228853 -0500
|
||||||
+++ ZFSPlugin.pm 2022-03-26 13:51:40.660068908 -0400
|
+++ ZFSPlugin.pm 2023-12-31 09:57:08.830488875 -0500
|
||||||
@@ -10,6 +10,7 @@
|
@@ -10,6 +10,7 @@
|
||||||
|
|
||||||
use base qw(PVE::Storage::ZFSPoolPlugin);
|
use base qw(PVE::Storage::ZFSPoolPlugin);
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
sub type {
|
sub type {
|
||||||
@@ -184,6 +199,24 @@
|
@@ -184,6 +199,32 @@
|
||||||
description => "iscsi provider",
|
description => "iscsi provider",
|
||||||
type => 'string',
|
type => 'string',
|
||||||
},
|
},
|
||||||
|
|
@ -69,9 +69,17 @@
|
||||||
+ type => 'string',
|
+ type => 'string',
|
||||||
+ },
|
+ },
|
||||||
+ freenas_password => {
|
+ freenas_password => {
|
||||||
+ description => "FreeNAS API Password",
|
+ description => "FreeNAS API Password (Deprecated)",
|
||||||
+ type => 'string',
|
+ type => 'string',
|
||||||
+ },
|
+ },
|
||||||
|
+ truenas_secret => {
|
||||||
|
+ description => "TrueNAS API Secret",
|
||||||
|
+ type => 'string',
|
||||||
|
+ },
|
||||||
|
+ truenas_token_auth => {
|
||||||
|
+ description => "TrueNAS API Authentication with Token",
|
||||||
|
+ type => 'boolean',
|
||||||
|
+ },
|
||||||
+ freenas_use_ssl => {
|
+ freenas_use_ssl => {
|
||||||
+ description => "FreeNAS API access via SSL",
|
+ description => "FreeNAS API access via SSL",
|
||||||
+ type => 'boolean',
|
+ type => 'boolean',
|
||||||
|
|
@ -83,7 +91,7 @@
|
||||||
# this will disable write caching on comstar and istgt.
|
# this will disable write caching on comstar and istgt.
|
||||||
# it is not implemented for iet. iet blockio always operates with
|
# it is not implemented for iet. iet blockio always operates with
|
||||||
# writethrough caching when not in readonly mode
|
# writethrough caching when not in readonly mode
|
||||||
@@ -211,14 +244,18 @@
|
@@ -211,14 +252,20 @@
|
||||||
nodes => { optional => 1 },
|
nodes => { optional => 1 },
|
||||||
disable => { optional => 1 },
|
disable => { optional => 1 },
|
||||||
portal => { fixed => 1 },
|
portal => { fixed => 1 },
|
||||||
|
|
@ -99,12 +107,14 @@
|
||||||
comstar_tg => { optional => 1 },
|
comstar_tg => { optional => 1 },
|
||||||
+ freenas_user => { optional => 1 },
|
+ freenas_user => { optional => 1 },
|
||||||
+ freenas_password => { optional => 1 },
|
+ freenas_password => { optional => 1 },
|
||||||
|
+ truenas_secret => { optional => 1 },
|
||||||
|
+ truenas_token_auth => { optional => 1 },
|
||||||
+ freenas_use_ssl => { optional => 1 },
|
+ freenas_use_ssl => { optional => 1 },
|
||||||
+ freenas_apiv4_host => { optional => 1 },
|
+ freenas_apiv4_host => { optional => 1 },
|
||||||
lio_tpg => { optional => 1 },
|
lio_tpg => { optional => 1 },
|
||||||
content => { optional => 1 },
|
content => { optional => 1 },
|
||||||
bwlimit => { optional => 1 },
|
bwlimit => { optional => 1 },
|
||||||
@@ -243,6 +280,40 @@
|
@@ -243,6 +290,40 @@
|
||||||
|
|
||||||
my $path = "iscsi://$portal/$target/$lun";
|
my $path = "iscsi://$portal/$target/$lun";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
--- apidoc.js.orig 2024-01-06 13:02:06.730512378 -0500
|
||||||
|
+++ apidoc.js 2024-01-06 13:02:55.349787105 -0500
|
||||||
|
@@ -50336,6 +50336,37 @@
|
||||||
|
"type" : "string",
|
||||||
|
"typetext" : "<string>"
|
||||||
|
},
|
||||||
|
+ "freenas_user" : {
|
||||||
|
+ "description" : "FreeNAS user for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_password" : {
|
||||||
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS Secret for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_use_ssl" : {
|
||||||
|
+ "description" : "FreeNAS API access via SSL",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "boolean",
|
||||||
|
+ "typetext" : "<boolean>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_apiv4_host" : {
|
||||||
|
+ "description" : "FreeNAS API Host via IPv4",
|
||||||
|
+ "format" : "address",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
"fuse" : {
|
||||||
|
"description" : "Mount CephFS through FUSE.",
|
||||||
|
"optional" : 1,
|
||||||
|
@@ -50555,6 +50586,12 @@
|
||||||
|
"type" : "boolean",
|
||||||
|
"typetext" : "<boolean>"
|
||||||
|
},
|
||||||
|
+ "target" : {
|
||||||
|
+ "description" : "iSCSI target.",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
"transport" : {
|
||||||
|
"description" : "Gluster transport: tcp or rdma",
|
||||||
|
"enum" : [
|
||||||
|
@@ -50854,6 +50891,37 @@
|
||||||
|
"optional" : 1,
|
||||||
|
"type" : "string",
|
||||||
|
"typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_user" : {
|
||||||
|
+ "description" : "FreeNAS user for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_password" : {
|
||||||
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS secret for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_use_ssl" : {
|
||||||
|
+ "description" : "FreeNAS API access via SSL",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "boolean",
|
||||||
|
+ "typetext" : "<boolean>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_apiv4_host" : {
|
||||||
|
+ "description" : "FreeNAS API Host via IPv4",
|
||||||
|
+ "format" : "address",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
},
|
||||||
|
"fuse" : {
|
||||||
|
"description" : "Mount CephFS through FUSE.",
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
--- apidoc.js.orig 2021-11-15 10:07:34.000000000 -0500
|
--- apidoc.js.orig 2023-08-16 06:03:55.000000000 -0400
|
||||||
+++ apidoc.js 2021-12-06 08:04:01.648822707 -0500
|
+++ apidoc.js 2023-12-26 14:45:47.202566775 -0500
|
||||||
@@ -44064,6 +44064,31 @@
|
@@ -47579,6 +47579,37 @@
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"typetext" : "<string>"
|
"typetext" : "<string>"
|
||||||
},
|
},
|
||||||
|
|
@ -11,7 +11,13 @@
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
+ },
|
+ },
|
||||||
+ "freenas_password" : {
|
+ "freenas_password" : {
|
||||||
+ "description" : "FreeNAS password for API access",
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS Secret for API access",
|
||||||
+ "optional" : 1,
|
+ "optional" : 1,
|
||||||
+ "type" : "string",
|
+ "type" : "string",
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
|
|
@ -32,7 +38,7 @@
|
||||||
"fuse" : {
|
"fuse" : {
|
||||||
"description" : "Mount CephFS through FUSE.",
|
"description" : "Mount CephFS through FUSE.",
|
||||||
"optional" : 1,
|
"optional" : 1,
|
||||||
@@ -44275,6 +44300,12 @@
|
@@ -47798,6 +47829,12 @@
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"typetext" : "<boolean>"
|
"typetext" : "<boolean>"
|
||||||
},
|
},
|
||||||
|
|
@ -45,7 +51,7 @@
|
||||||
"transport" : {
|
"transport" : {
|
||||||
"description" : "Gluster transport: tcp or rdma",
|
"description" : "Gluster transport: tcp or rdma",
|
||||||
"enum" : [
|
"enum" : [
|
||||||
@@ -44547,6 +44578,31 @@
|
@@ -48097,6 +48134,37 @@
|
||||||
"optional" : 1,
|
"optional" : 1,
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"typetext" : "<string>"
|
"typetext" : "<string>"
|
||||||
|
|
@ -57,7 +63,13 @@
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
+ },
|
+ },
|
||||||
+ "freenas_password" : {
|
+ "freenas_password" : {
|
||||||
+ "description" : "FreeNAS password for API access",
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS secret for API access",
|
||||||
+ "optional" : 1,
|
+ "optional" : 1,
|
||||||
+ "type" : "string",
|
+ "type" : "string",
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,162 @@
|
||||||
--- pvemanagerlib.js.orig 2022-03-17 09:08:40.000000000 -0400
|
--- pvemanagerlib.js.orig 2023-12-30 15:36:27.913505863 -0500
|
||||||
+++ pvemanagerlib.js 2022-04-03 08:54:10.229689187 -0400
|
+++ pvemanagerlib.js 2024-01-02 09:30:56.000000000 -0500
|
||||||
@@ -8068,6 +8068,7 @@
|
@@ -9228,6 +9228,7 @@
|
||||||
alias: ['widget.pveiScsiProviderSelector'],
|
alias: ['widget.pveiScsiProviderSelector'],
|
||||||
comboItems: [
|
comboItems: [
|
||||||
['comstar', 'Comstar'],
|
['comstar', 'Comstar'],
|
||||||
+ ['freenas', 'FreeNAS-API'],
|
+ ['freenas', 'FreeNAS/TrueNAS API'],
|
||||||
['istgt', 'istgt'],
|
['istgt', 'istgt'],
|
||||||
['iet', 'IET'],
|
['iet', 'IET'],
|
||||||
['LIO', 'LIO'],
|
['LIO', 'LIO'],
|
||||||
@@ -49636,6 +49637,7 @@
|
@@ -58017,16 +58018,24 @@
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
+
|
||||||
|
Ext.define('PVE.storage.ZFSInputPanel', {
|
||||||
|
extend: 'PVE.panel.StorageBase',
|
||||||
|
|
||||||
|
viewModel: {
|
||||||
|
parent: null,
|
||||||
data: {
|
data: {
|
||||||
|
+isComstar: true,
|
||||||
|
+ isFreeNAS: false,
|
||||||
isLIO: false,
|
isLIO: false,
|
||||||
isComstar: true,
|
- isComstar: true,
|
||||||
+ isFreeNAS: false,
|
+ isToken: false,
|
||||||
hasWriteCacheOption: true,
|
hasWriteCacheOption: true,
|
||||||
},
|
},
|
||||||
|
+formulas: {
|
||||||
|
+ hideUsername: function(get) {
|
||||||
|
+ return (!get('isFreeNAS') || !(get('isFreeNAS') && !get('isToken')));
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
},
|
},
|
||||||
@@ -49648,10 +49650,26 @@
|
|
||||||
|
controller: {
|
||||||
|
@@ -58034,13 +58043,42 @@
|
||||||
|
control: {
|
||||||
|
'field[name=iscsiprovider]': {
|
||||||
|
change: 'changeISCSIProvider',
|
||||||
|
+},
|
||||||
|
+ 'field[name=truenas_token_auth]': {
|
||||||
|
+ change: 'changeUsername',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
changeISCSIProvider: function(f, newVal, oldVal) {
|
changeISCSIProvider: function(f, newVal, oldVal) {
|
||||||
+ var me = this;
|
+var me = this;
|
||||||
var vm = this.getViewModel();
|
var vm = this.getViewModel();
|
||||||
vm.set('isLIO', newVal === 'LIO');
|
vm.set('isLIO', newVal === 'LIO');
|
||||||
vm.set('isComstar', newVal === 'comstar');
|
vm.set('isComstar', newVal === 'comstar');
|
||||||
- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
|
- vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'istgt');
|
||||||
+ vm.set('isFreeNAS', newVal === 'freenas');
|
+ vm.set('isFreeNAS', newVal === 'freenas');
|
||||||
+ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
|
+ vm.set('hasWriteCacheOption', newVal === 'comstar' || newVal === 'freenas' || newVal === 'istgt');
|
||||||
+ if (newVal !== 'freenas') {
|
+ if (newVal !== 'freenas') {
|
||||||
+ me.lookupReference('freenas_use_ssl_field').setValue(false);
|
+ me.lookupReference('freenas_use_ssl_field').setValue(false);
|
||||||
+ me.lookupReference('freenas_apiv4_host_field').setValue('');
|
+ me.lookupReference('truenas_token_auth_field').setValue(false);
|
||||||
+ me.lookupReference('freenas_user_field').setValue('');
|
+ me.lookupReference('freenas_apiv4_host_field').setValue('');
|
||||||
+ me.lookupReference('freenas_user_field').allowBlank = true;
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
+ me.lookupReference('freenas_password_field').setValue('');
|
+ me.lookupReference('freenas_user_field').allowBlank = true;
|
||||||
+ me.lookupReference('freenas_password_field').allowBlank = true;
|
+ me.lookupReference('truenas_secret_field').setValue('');
|
||||||
+ me.lookupReference('freenas_confirmpw_field').setValue('');
|
+ me.lookupReference('truenas_secret_field').allowBlank = true;
|
||||||
+ me.lookupReference('freenas_confirmpw_field').allowBlank = true;
|
+ me.lookupReference('truenas_confirm_secret_field').setValue('');
|
||||||
+ } else {
|
+ me.lookupReference('truenas_confirm_secret_field').allowBlank = true;
|
||||||
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
+ } else {
|
||||||
+ me.lookupReference('freenas_password_field').allowBlank = false;
|
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
||||||
+ me.lookupReference('freenas_confirmpw_field').allowBlank = false;
|
+ me.lookupReference('truenas_secret_field').allowBlank = false;
|
||||||
+ }
|
+ me.lookupReference('truenas_confirm_secret_field').allowBlank = false;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ changeUsername: function(f, newVal, oldVal) {
|
||||||
|
+ var me = this;
|
||||||
|
+ var vm = me.getViewModel();
|
||||||
|
+ vm.set('isToken', newVal);
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = newVal;
|
||||||
|
+ if (newVal) {
|
||||||
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
|
+ }
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -49669,6 +49687,7 @@
|
@@ -58053,28 +58091,78 @@
|
||||||
|
|
||||||
|
values.nowritecache = values.writecache ? 0 : 1;
|
||||||
|
delete values.writecache;
|
||||||
|
+ console.warn(values.freenas_password);
|
||||||
|
+ if (values.freenas_password) {
|
||||||
|
+ values.truenas_secret = values.freenas_password;
|
||||||
|
+ }
|
||||||
|
+ console.warn(values.truenas_secret);
|
||||||
|
|
||||||
|
return me.callParent([values]);
|
||||||
},
|
},
|
||||||
|
|
||||||
setValues: function(values) {
|
setValues: function(values) {
|
||||||
+ values.freenas_confirmpw = values.freenas_password;
|
- values.writecache = values.nowritecache ? 0 : 1;
|
||||||
values.writecache = values.nowritecache ? 0 : 1;
|
- this.callParent([values]);
|
||||||
this.callParent([values]);
|
+ if (values.freenas_password) {
|
||||||
|
+ values.truenas_secret = values.freenas_password;
|
||||||
|
+ }
|
||||||
|
+ values.truenas_confirm_secret = values.truenas_secret;
|
||||||
|
+ values.writecache = values.nowritecache ? 0 : 1;
|
||||||
|
+ this.callParent([values]);
|
||||||
},
|
},
|
||||||
@@ -49685,7 +49704,7 @@
|
|
||||||
|
initComponent: function() {
|
||||||
|
- var me = this;
|
||||||
|
+ var me = this;
|
||||||
|
+
|
||||||
|
+ var tnsecret = Ext.create('Ext.form.TextField', {
|
||||||
|
+ xtype: 'proxmoxtextfield',
|
||||||
|
+ name: 'truenas_secret',
|
||||||
|
+ reference: 'truenas_secret_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=truenas_confirm_secret]').validate();
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- me.column1 = [
|
||||||
|
- {
|
||||||
|
- xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
- name: 'portal',
|
||||||
|
+ var tnconfirmsecret = Ext.create('Ext.form.TextField', {
|
||||||
|
+ xtype: 'proxmoxtextfield',
|
||||||
|
+ name: 'truenas_confirm_secret',
|
||||||
|
+ reference: 'truenas_confirm_secret_field',
|
||||||
|
+ inputType: me.isCreate ? '' : 'password',
|
||||||
|
+ value: '',
|
||||||
|
+ editable: true,
|
||||||
|
+ submitValue: false,
|
||||||
|
+ emptyText: Proxmox.Utils.noneText,
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{!isFreeNAS}'
|
||||||
|
+ },
|
||||||
|
+ fieldLabel: gettext('Confirm API Password'),
|
||||||
|
+ validator: function(value) {
|
||||||
|
+ var pw = me.up().down('field[name=truenas_secret]').getValue();
|
||||||
|
+ if (pw !== value) {
|
||||||
|
+ return "Secrets do not match!";
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ me.column1 = [
|
||||||
|
+ {
|
||||||
|
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
+ name: 'portal',
|
||||||
|
value: '',
|
||||||
|
fieldLabel: gettext('Portal'),
|
||||||
allowBlank: false,
|
allowBlank: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -61,7 +165,7 @@
|
||||||
name: 'pool',
|
name: 'pool',
|
||||||
value: '',
|
value: '',
|
||||||
fieldLabel: gettext('Pool'),
|
fieldLabel: gettext('Pool'),
|
||||||
@@ -49695,11 +49714,11 @@
|
@@ -58084,11 +58172,11 @@
|
||||||
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
name: 'blocksize',
|
name: 'blocksize',
|
||||||
value: '4k',
|
value: '4k',
|
||||||
|
|
@ -75,7 +179,7 @@
|
||||||
name: 'target',
|
name: 'target',
|
||||||
value: '',
|
value: '',
|
||||||
fieldLabel: gettext('Target'),
|
fieldLabel: gettext('Target'),
|
||||||
@@ -49710,9 +49729,34 @@
|
@@ -58099,8 +58187,59 @@
|
||||||
name: 'comstar_tg',
|
name: 'comstar_tg',
|
||||||
value: '',
|
value: '',
|
||||||
fieldLabel: gettext('Target group'),
|
fieldLabel: gettext('Target group'),
|
||||||
|
|
@ -84,7 +188,7 @@
|
||||||
+ hidden: '{!isComstar}'
|
+ hidden: '{!isComstar}'
|
||||||
+ },
|
+ },
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
},
|
+},
|
||||||
+ {
|
+ {
|
||||||
+ xtype: 'proxmoxcheckbox',
|
+ xtype: 'proxmoxcheckbox',
|
||||||
+ name: 'freenas_use_ssl',
|
+ name: 'freenas_use_ssl',
|
||||||
|
|
@ -98,6 +202,32 @@
|
||||||
+ fieldLabel: gettext('API use SSL'),
|
+ fieldLabel: gettext('API use SSL'),
|
||||||
+ },
|
+ },
|
||||||
+ {
|
+ {
|
||||||
|
+ xtype: 'proxmoxcheckbox',
|
||||||
|
+ name: 'truenas_token_auth',
|
||||||
|
+ reference: 'truenas_token_auth_field',
|
||||||
|
+ inputId: 'truenas_use_token_auth_field',
|
||||||
|
+ checked: false,
|
||||||
|
+ listeners: {
|
||||||
|
+ change: function(field, newValue) {
|
||||||
|
+ if (newValue === true) {
|
||||||
|
+ tnsecret.labelEl.update('API Token');
|
||||||
|
+ tnconfirmsecret.labelEl.update('Confirm API Token');
|
||||||
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = true;
|
||||||
|
+ } else {
|
||||||
|
+ tnsecret.labelEl.update('API Password');
|
||||||
|
+ tnconfirmsecret.labelEl.update('Confirm API Password');
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{!isFreeNAS}'
|
||||||
|
+ },
|
||||||
|
+ uncheckedValue: 0,
|
||||||
|
+ fieldLabel: gettext('API Token Auth'),
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
+ xtype: 'textfield',
|
+ xtype: 'textfield',
|
||||||
+ name: 'freenas_user',
|
+ name: 'freenas_user',
|
||||||
+ reference: 'freenas_user_field',
|
+ reference: 'freenas_user_field',
|
||||||
|
|
@ -105,13 +235,12 @@
|
||||||
+ value: '',
|
+ value: '',
|
||||||
+ fieldLabel: gettext('API Username'),
|
+ fieldLabel: gettext('API Username'),
|
||||||
+ bind: {
|
+ bind: {
|
||||||
+ hidden: '{!isFreeNAS}'
|
+ hidden: '{hideUsername}'
|
||||||
+ },
|
+ },
|
||||||
+ },
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
me.column2 = [
|
@@ -58131,7 +58270,9 @@
|
||||||
@@ -49742,7 +49786,9 @@
|
|
||||||
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
name: 'comstar_hg',
|
name: 'comstar_hg',
|
||||||
value: '',
|
value: '',
|
||||||
|
|
@ -122,18 +251,19 @@
|
||||||
fieldLabel: gettext('Host group'),
|
fieldLabel: gettext('Host group'),
|
||||||
allowBlank: true,
|
allowBlank: true,
|
||||||
},
|
},
|
||||||
@@ -49750,9 +49796,62 @@
|
@@ -58139,15 +58280,32 @@
|
||||||
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
name: 'lio_tpg',
|
name: 'lio_tpg',
|
||||||
value: '',
|
value: '',
|
||||||
- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
|
- bind: me.isCreate ? { disabled: '{!isLIO}' } : { hidden: '{!isLIO}' },
|
||||||
- allowBlank: false,
|
- allowBlank: false,
|
||||||
|
- fieldLabel: gettext('Target portal group'),
|
||||||
+ bind: {
|
+ bind: {
|
||||||
+ hidden: '{!isLIO}'
|
+ hidden: '{!isLIO}'
|
||||||
+ },
|
+ },
|
||||||
fieldLabel: gettext('Target portal group'),
|
+ fieldLabel: gettext('Target portal group'),
|
||||||
+ allowBlank: true
|
+ allowBlank: true
|
||||||
+ },
|
},
|
||||||
+ {
|
+ {
|
||||||
+ xtype: 'proxmoxtextfield',
|
+ xtype: 'proxmoxtextfield',
|
||||||
+ name: 'freenas_apiv4_host',
|
+ name: 'freenas_apiv4_host',
|
||||||
|
|
@ -146,44 +276,14 @@
|
||||||
+ },
|
+ },
|
||||||
+ fieldLabel: gettext('API IPv4 Host'),
|
+ fieldLabel: gettext('API IPv4 Host'),
|
||||||
+ },
|
+ },
|
||||||
+ {
|
+ tnsecret,
|
||||||
+ xtype: 'proxmoxtextfield',
|
+ tnconfirmsecret,
|
||||||
+ 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;
|
|
||||||
+ },
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
+
|
||||||
|
Ext.define('PVE.storage.ZFSPoolSelector', {
|
||||||
|
extend: 'PVE.form.ComboBoxSetStoreNode',
|
||||||
|
alias: 'widget.pveZFSPoolSelector',
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,189 +0,0 @@
|
||||||
--- 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;
|
|
||||||
+ },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
--- ZFSPlugin.pm.orig 2023-12-31 09:56:18.895228853 -0500
|
||||||
|
+++ ZFSPlugin.pm 2023-12-31 09:57:08.830488875 -0500
|
||||||
|
@@ -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,32 @@
|
||||||
|
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 (Deprecated)",
|
||||||
|
+ type => 'string',
|
||||||
|
+ },
|
||||||
|
+ truenas_secret => {
|
||||||
|
+ description => "TrueNAS API Secret",
|
||||||
|
+ type => 'string',
|
||||||
|
+ },
|
||||||
|
+ truenas_token_auth => {
|
||||||
|
+ description => "TrueNAS API Authentication with Token",
|
||||||
|
+ type => 'boolean',
|
||||||
|
+ },
|
||||||
|
+ 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 +252,20 @@
|
||||||
|
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 },
|
||||||
|
+ truenas_secret => { optional => 1 },
|
||||||
|
+ truenas_token_auth => { optional => 1 },
|
||||||
|
+ freenas_use_ssl => { optional => 1 },
|
||||||
|
+ freenas_apiv4_host => { optional => 1 },
|
||||||
|
lio_tpg => { optional => 1 },
|
||||||
|
content => { optional => 1 },
|
||||||
|
bwlimit => { optional => 1 },
|
||||||
|
@@ -243,6 +290,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,422 @@
|
||||||
|
package PVE::Storage::ZFSPlugin;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use IO::File;
|
||||||
|
use POSIX;
|
||||||
|
use PVE::Tools qw(run_command);
|
||||||
|
use PVE::Storage::ZFSPoolPlugin;
|
||||||
|
use PVE::RPCEnvironment;
|
||||||
|
|
||||||
|
use base qw(PVE::Storage::ZFSPoolPlugin);
|
||||||
|
use PVE::Storage::LunCmd::Comstar;
|
||||||
|
use PVE::Storage::LunCmd::Istgt;
|
||||||
|
use PVE::Storage::LunCmd::Iet;
|
||||||
|
use PVE::Storage::LunCmd::LIO;
|
||||||
|
|
||||||
|
|
||||||
|
my @ssh_opts = ('-o', 'BatchMode=yes');
|
||||||
|
my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
|
||||||
|
my $id_rsa_path = '/etc/pve/priv/zfs';
|
||||||
|
|
||||||
|
my $lun_cmds = {
|
||||||
|
create_lu => 1,
|
||||||
|
delete_lu => 1,
|
||||||
|
import_lu => 1,
|
||||||
|
modify_lu => 1,
|
||||||
|
add_view => 1,
|
||||||
|
list_view => 1,
|
||||||
|
list_lu => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
my $zfs_unknown_scsi_provider = sub {
|
||||||
|
my ($provider) = @_;
|
||||||
|
|
||||||
|
die "$provider: unknown iscsi provider. Available [comstar, istgt, iet, LIO]";
|
||||||
|
};
|
||||||
|
|
||||||
|
my $zfs_get_base = sub {
|
||||||
|
my ($scfg) = @_;
|
||||||
|
|
||||||
|
if ($scfg->{iscsiprovider} eq 'comstar') {
|
||||||
|
return PVE::Storage::LunCmd::Comstar::get_base;
|
||||||
|
} elsif ($scfg->{iscsiprovider} eq 'istgt') {
|
||||||
|
return PVE::Storage::LunCmd::Istgt::get_base;
|
||||||
|
} elsif ($scfg->{iscsiprovider} eq 'iet') {
|
||||||
|
return PVE::Storage::LunCmd::Iet::get_base;
|
||||||
|
} elsif ($scfg->{iscsiprovider} eq 'LIO') {
|
||||||
|
return PVE::Storage::LunCmd::LIO::get_base;
|
||||||
|
} else {
|
||||||
|
$zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sub zfs_request {
|
||||||
|
my ($class, $scfg, $timeout, $method, @params) = @_;
|
||||||
|
|
||||||
|
$timeout = PVE::RPCEnvironment->is_worker() ? 60*60 : 10
|
||||||
|
if !$timeout;
|
||||||
|
|
||||||
|
my $msg = '';
|
||||||
|
|
||||||
|
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 'istgt') {
|
||||||
|
$msg = PVE::Storage::LunCmd::Istgt::run_lun_command($scfg, $timeout, $method, @params);
|
||||||
|
} elsif ($scfg->{iscsiprovider} eq 'iet') {
|
||||||
|
$msg = PVE::Storage::LunCmd::Iet::run_lun_command($scfg, $timeout, $method, @params);
|
||||||
|
} elsif ($scfg->{iscsiprovider} eq 'LIO') {
|
||||||
|
$msg = PVE::Storage::LunCmd::LIO::run_lun_command($scfg, $timeout, $method, @params);
|
||||||
|
} else {
|
||||||
|
$zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
my $target = 'root@' . $scfg->{portal};
|
||||||
|
|
||||||
|
my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target];
|
||||||
|
|
||||||
|
if ($method eq 'zpool_list') {
|
||||||
|
push @$cmd, 'zpool', 'list';
|
||||||
|
} else {
|
||||||
|
push @$cmd, 'zfs', $method;
|
||||||
|
}
|
||||||
|
|
||||||
|
push @$cmd, @params;
|
||||||
|
|
||||||
|
my $output = sub {
|
||||||
|
my $line = shift;
|
||||||
|
$msg .= "$line\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
run_command($cmd, outfunc => $output, timeout => $timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_get_lu_name {
|
||||||
|
my ($class, $scfg, $zvol) = @_;
|
||||||
|
|
||||||
|
my $base = $zfs_get_base->($scfg);
|
||||||
|
|
||||||
|
$zvol = ($class->parse_volname($zvol))[1];
|
||||||
|
|
||||||
|
my $object = ($zvol =~ /^.+\/.+/) ? "$base/$zvol" : "$base/$scfg->{pool}/$zvol";
|
||||||
|
|
||||||
|
my $lu_name = $class->zfs_request($scfg, undef, 'list_lu', $object);
|
||||||
|
|
||||||
|
return $lu_name if $lu_name;
|
||||||
|
|
||||||
|
die "Could not find lu_name for zvol $zvol";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_add_lun_mapping_entry {
|
||||||
|
my ($class, $scfg, $zvol, $guid) = @_;
|
||||||
|
|
||||||
|
if (!defined($guid)) {
|
||||||
|
$guid = $class->zfs_get_lu_name($scfg, $zvol);
|
||||||
|
}
|
||||||
|
|
||||||
|
$class->zfs_request($scfg, undef, 'add_view', $guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_delete_lu {
|
||||||
|
my ($class, $scfg, $zvol) = @_;
|
||||||
|
|
||||||
|
my $guid = $class->zfs_get_lu_name($scfg, $zvol);
|
||||||
|
|
||||||
|
$class->zfs_request($scfg, undef, 'delete_lu', $guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_create_lu {
|
||||||
|
my ($class, $scfg, $zvol) = @_;
|
||||||
|
|
||||||
|
my $base = $zfs_get_base->($scfg);
|
||||||
|
my $guid = $class->zfs_request($scfg, undef, 'create_lu', "$base/$scfg->{pool}/$zvol");
|
||||||
|
|
||||||
|
return $guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_import_lu {
|
||||||
|
my ($class, $scfg, $zvol) = @_;
|
||||||
|
|
||||||
|
my $base = $zfs_get_base->($scfg);
|
||||||
|
$class->zfs_request($scfg, undef, 'import_lu', "$base/$scfg->{pool}/$zvol");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_resize_lu {
|
||||||
|
my ($class, $scfg, $zvol, $size) = @_;
|
||||||
|
|
||||||
|
my $guid = $class->zfs_get_lu_name($scfg, $zvol);
|
||||||
|
|
||||||
|
$class->zfs_request($scfg, undef, 'modify_lu', "${size}K", $guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub zfs_get_lun_number {
|
||||||
|
my ($class, $scfg, $guid) = @_;
|
||||||
|
|
||||||
|
die "could not find lun_number for guid $guid" if !$guid;
|
||||||
|
|
||||||
|
if ($class->zfs_request($scfg, undef, 'list_view', $guid) =~ /^(\d+)$/) {
|
||||||
|
return $1;
|
||||||
|
}
|
||||||
|
|
||||||
|
die "lun_number for guid $guid is not a number";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
sub type {
|
||||||
|
return 'zfs';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub plugindata {
|
||||||
|
return {
|
||||||
|
content => [ {images => 1}, { images => 1 }],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub properties {
|
||||||
|
return {
|
||||||
|
iscsiprovider => {
|
||||||
|
description => "iscsi provider",
|
||||||
|
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
|
||||||
|
nowritecache => {
|
||||||
|
description => "disable write caching on the target",
|
||||||
|
type => 'boolean',
|
||||||
|
},
|
||||||
|
comstar_tg => {
|
||||||
|
description => "target group for comstar views",
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
comstar_hg => {
|
||||||
|
description => "host group for comstar views",
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
lio_tpg => {
|
||||||
|
description => "target portal group for Linux LIO targets",
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub options {
|
||||||
|
return {
|
||||||
|
nodes => { optional => 1 },
|
||||||
|
disable => { optional => 1 },
|
||||||
|
portal => { fixed => 1 },
|
||||||
|
target => { fixed => 1 },
|
||||||
|
pool => { fixed => 1 },
|
||||||
|
blocksize => { fixed => 1 },
|
||||||
|
iscsiprovider => { fixed => 1 },
|
||||||
|
nowritecache => { optional => 1 },
|
||||||
|
sparse => { optional => 1 },
|
||||||
|
comstar_hg => { optional => 1 },
|
||||||
|
comstar_tg => { optional => 1 },
|
||||||
|
lio_tpg => { optional => 1 },
|
||||||
|
content => { optional => 1 },
|
||||||
|
bwlimit => { optional => 1 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Storage implementation
|
||||||
|
|
||||||
|
sub path {
|
||||||
|
my ($class, $scfg, $volname, $storeid, $snapname) = @_;
|
||||||
|
|
||||||
|
die "direct access to snapshots not implemented"
|
||||||
|
if defined($snapname);
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
|
||||||
|
my $target = $scfg->{target};
|
||||||
|
my $portal = $scfg->{portal};
|
||||||
|
|
||||||
|
my $guid = $class->zfs_get_lu_name($scfg, $name);
|
||||||
|
my $lun = $class->zfs_get_lun_number($scfg, $guid);
|
||||||
|
|
||||||
|
my $path = "iscsi://$portal/$target/$lun";
|
||||||
|
|
||||||
|
return ($path, $vmid, $vtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub create_base {
|
||||||
|
my ($class, $storeid, $scfg, $volname) = @_;
|
||||||
|
|
||||||
|
my $snap = '__base__';
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
|
||||||
|
$class->parse_volname($volname);
|
||||||
|
|
||||||
|
die "create_base not possible with base image\n" if $isBase;
|
||||||
|
|
||||||
|
my $newname = $name;
|
||||||
|
$newname =~ s/^vm-/base-/;
|
||||||
|
|
||||||
|
my $newvolname = $basename ? "$basename/$newname" : "$newname";
|
||||||
|
|
||||||
|
$class->zfs_delete_lu($scfg, $name);
|
||||||
|
$class->zfs_request($scfg, undef, 'rename', "$scfg->{pool}/$name", "$scfg->{pool}/$newname");
|
||||||
|
|
||||||
|
my $guid = $class->zfs_create_lu($scfg, $newname);
|
||||||
|
$class->zfs_add_lun_mapping_entry($scfg, $newname, $guid);
|
||||||
|
|
||||||
|
my $running = undef; #fixme : is create_base always offline ?
|
||||||
|
|
||||||
|
$class->volume_snapshot($scfg, $storeid, $newname, $snap, $running);
|
||||||
|
|
||||||
|
return $newvolname;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clone_image {
|
||||||
|
my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_;
|
||||||
|
|
||||||
|
my $name = $class->SUPER::clone_image($scfg, $storeid, $volname, $vmid, $snap);
|
||||||
|
|
||||||
|
# get ZFS dataset name from PVE volname
|
||||||
|
my (undef, $clonedname) = $class->parse_volname($name);
|
||||||
|
|
||||||
|
my $guid = $class->zfs_create_lu($scfg, $clonedname);
|
||||||
|
$class->zfs_add_lun_mapping_entry($scfg, $clonedname, $guid);
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub alloc_image {
|
||||||
|
my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
|
||||||
|
|
||||||
|
die "unsupported format '$fmt'" if $fmt ne 'raw';
|
||||||
|
|
||||||
|
die "illegal name '$name' - should be 'vm-$vmid-*'\n"
|
||||||
|
if $name && $name !~ m/^vm-$vmid-/;
|
||||||
|
|
||||||
|
my $volname = $name;
|
||||||
|
|
||||||
|
$volname = $class->find_free_diskname($storeid, $scfg, $vmid, $fmt) if !$volname;
|
||||||
|
|
||||||
|
$class->zfs_create_zvol($scfg, $volname, $size);
|
||||||
|
|
||||||
|
my $guid = $class->zfs_create_lu($scfg, $volname);
|
||||||
|
$class->zfs_add_lun_mapping_entry($scfg, $volname, $guid);
|
||||||
|
|
||||||
|
return $volname;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub free_image {
|
||||||
|
my ($class, $storeid, $scfg, $volname, $isBase) = @_;
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
|
||||||
|
$class->zfs_delete_lu($scfg, $name);
|
||||||
|
|
||||||
|
eval { $class->zfs_delete_zvol($scfg, $name); };
|
||||||
|
if (my $err = $@) {
|
||||||
|
my $guid = $class->zfs_create_lu($scfg, $name);
|
||||||
|
$class->zfs_add_lun_mapping_entry($scfg, $name, $guid);
|
||||||
|
die $err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_resize {
|
||||||
|
my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
|
||||||
|
|
||||||
|
$volname = ($class->parse_volname($volname))[1];
|
||||||
|
|
||||||
|
my $new_size = $class->SUPER::volume_resize($scfg, $storeid, $volname, $size, $running);
|
||||||
|
|
||||||
|
$class->zfs_resize_lu($scfg, $volname, $new_size);
|
||||||
|
|
||||||
|
return $new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_snapshot_delete {
|
||||||
|
my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
|
||||||
|
|
||||||
|
$volname = ($class->parse_volname($volname))[1];
|
||||||
|
|
||||||
|
$class->zfs_request($scfg, undef, 'destroy', "$scfg->{pool}/$volname\@$snap");
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_snapshot_rollback {
|
||||||
|
my ($class, $scfg, $storeid, $volname, $snap) = @_;
|
||||||
|
|
||||||
|
$volname = ($class->parse_volname($volname))[1];
|
||||||
|
|
||||||
|
$class->zfs_delete_lu($scfg, $volname);
|
||||||
|
|
||||||
|
$class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$volname\@$snap");
|
||||||
|
|
||||||
|
$class->zfs_import_lu($scfg, $volname);
|
||||||
|
|
||||||
|
$class->zfs_add_lun_mapping_entry($scfg, $volname);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub storage_can_replicate {
|
||||||
|
my ($class, $scfg, $storeid, $format) = @_;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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},
|
||||||
|
};
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
|
||||||
|
$class->parse_volname($volname);
|
||||||
|
|
||||||
|
my $key = undef;
|
||||||
|
|
||||||
|
if ($snapname) {
|
||||||
|
$key = 'snap';
|
||||||
|
} else {
|
||||||
|
$key = $isBase ? 'base' : 'current';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1 if $features->{$feature}->{$key};
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub activate_storage {
|
||||||
|
my ($class, $storeid, $scfg, $cache) = @_;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deactivate_storage {
|
||||||
|
my ($class, $storeid, $scfg, $cache) = @_;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub activate_volume {
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
|
||||||
|
|
||||||
|
die "unable to activate snapshot from remote zfs storage" if $snapname;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deactivate_volume {
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
|
||||||
|
|
||||||
|
die "unable to deactivate snapshot from remote zfs storage" if $snapname;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
--- ZFSPlugin.pm.orig 2022-02-04 12:08:01.000000000 -0500
|
--- ZFSPlugin.pm.orig 2023-12-31 09:56:18.895228853 -0500
|
||||||
+++ ZFSPlugin.pm 2022-03-26 13:51:40.660068908 -0400
|
+++ ZFSPlugin.pm 2023-12-31 09:57:08.830488875 -0500
|
||||||
@@ -10,6 +10,7 @@
|
@@ -10,6 +10,7 @@
|
||||||
|
|
||||||
use base qw(PVE::Storage::ZFSPoolPlugin);
|
use base qw(PVE::Storage::ZFSPoolPlugin);
|
||||||
|
|
@ -58,7 +58,7 @@
|
||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
sub type {
|
sub type {
|
||||||
@@ -184,6 +199,24 @@
|
@@ -184,6 +199,32 @@
|
||||||
description => "iscsi provider",
|
description => "iscsi provider",
|
||||||
type => 'string',
|
type => 'string',
|
||||||
},
|
},
|
||||||
|
|
@ -69,9 +69,17 @@
|
||||||
+ type => 'string',
|
+ type => 'string',
|
||||||
+ },
|
+ },
|
||||||
+ freenas_password => {
|
+ freenas_password => {
|
||||||
+ description => "FreeNAS API Password",
|
+ description => "FreeNAS API Password (Deprecated)",
|
||||||
+ type => 'string',
|
+ type => 'string',
|
||||||
+ },
|
+ },
|
||||||
|
+ truenas_secret => {
|
||||||
|
+ description => "TrueNAS API Secret",
|
||||||
|
+ type => 'string',
|
||||||
|
+ },
|
||||||
|
+ truenas_token_auth => {
|
||||||
|
+ description => "TrueNAS API Authentication with Token",
|
||||||
|
+ type => 'boolean',
|
||||||
|
+ },
|
||||||
+ freenas_use_ssl => {
|
+ freenas_use_ssl => {
|
||||||
+ description => "FreeNAS API access via SSL",
|
+ description => "FreeNAS API access via SSL",
|
||||||
+ type => 'boolean',
|
+ type => 'boolean',
|
||||||
|
|
@ -83,7 +91,7 @@
|
||||||
# this will disable write caching on comstar and istgt.
|
# this will disable write caching on comstar and istgt.
|
||||||
# it is not implemented for iet. iet blockio always operates with
|
# it is not implemented for iet. iet blockio always operates with
|
||||||
# writethrough caching when not in readonly mode
|
# writethrough caching when not in readonly mode
|
||||||
@@ -211,14 +244,18 @@
|
@@ -211,14 +252,20 @@
|
||||||
nodes => { optional => 1 },
|
nodes => { optional => 1 },
|
||||||
disable => { optional => 1 },
|
disable => { optional => 1 },
|
||||||
portal => { fixed => 1 },
|
portal => { fixed => 1 },
|
||||||
|
|
@ -99,12 +107,14 @@
|
||||||
comstar_tg => { optional => 1 },
|
comstar_tg => { optional => 1 },
|
||||||
+ freenas_user => { optional => 1 },
|
+ freenas_user => { optional => 1 },
|
||||||
+ freenas_password => { optional => 1 },
|
+ freenas_password => { optional => 1 },
|
||||||
|
+ truenas_secret => { optional => 1 },
|
||||||
|
+ truenas_token_auth => { optional => 1 },
|
||||||
+ freenas_use_ssl => { optional => 1 },
|
+ freenas_use_ssl => { optional => 1 },
|
||||||
+ freenas_apiv4_host => { optional => 1 },
|
+ freenas_apiv4_host => { optional => 1 },
|
||||||
lio_tpg => { optional => 1 },
|
lio_tpg => { optional => 1 },
|
||||||
content => { optional => 1 },
|
content => { optional => 1 },
|
||||||
bwlimit => { optional => 1 },
|
bwlimit => { optional => 1 },
|
||||||
@@ -243,6 +280,40 @@
|
@@ -243,6 +290,40 @@
|
||||||
|
|
||||||
my $path = "iscsi://$portal/$target/$lun";
|
my $path = "iscsi://$portal/$target/$lun";
|
||||||
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
--- apidoc.js.orig 2024-01-06 13:02:06.730512378 -0500
|
||||||
|
+++ apidoc.js 2024-01-06 13:02:55.349787105 -0500
|
||||||
|
@@ -50336,6 +50336,37 @@
|
||||||
|
"type" : "string",
|
||||||
|
"typetext" : "<string>"
|
||||||
|
},
|
||||||
|
+ "freenas_user" : {
|
||||||
|
+ "description" : "FreeNAS user for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_password" : {
|
||||||
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS Secret for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_use_ssl" : {
|
||||||
|
+ "description" : "FreeNAS API access via SSL",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "boolean",
|
||||||
|
+ "typetext" : "<boolean>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_apiv4_host" : {
|
||||||
|
+ "description" : "FreeNAS API Host via IPv4",
|
||||||
|
+ "format" : "address",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
"fuse" : {
|
||||||
|
"description" : "Mount CephFS through FUSE.",
|
||||||
|
"optional" : 1,
|
||||||
|
@@ -50555,6 +50586,12 @@
|
||||||
|
"type" : "boolean",
|
||||||
|
"typetext" : "<boolean>"
|
||||||
|
},
|
||||||
|
+ "target" : {
|
||||||
|
+ "description" : "iSCSI target.",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
"transport" : {
|
||||||
|
"description" : "Gluster transport: tcp or rdma",
|
||||||
|
"enum" : [
|
||||||
|
@@ -50854,6 +50891,37 @@
|
||||||
|
"optional" : 1,
|
||||||
|
"type" : "string",
|
||||||
|
"typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_user" : {
|
||||||
|
+ "description" : "FreeNAS user for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_password" : {
|
||||||
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS secret for API access",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_use_ssl" : {
|
||||||
|
+ "description" : "FreeNAS API access via SSL",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "boolean",
|
||||||
|
+ "typetext" : "<boolean>"
|
||||||
|
+ },
|
||||||
|
+ "freenas_apiv4_host" : {
|
||||||
|
+ "description" : "FreeNAS API Host via IPv4",
|
||||||
|
+ "format" : "address",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
},
|
||||||
|
"fuse" : {
|
||||||
|
"description" : "Mount CephFS through FUSE.",
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
||||||
--- apidoc.js.orig 2021-11-15 10:07:34.000000000 -0500
|
--- apidoc.js.orig 2024-01-06 13:02:06.730512378 -0500
|
||||||
+++ apidoc.js 2021-12-06 08:04:01.648822707 -0500
|
+++ apidoc.js 2024-01-06 13:02:55.349787105 -0500
|
||||||
@@ -44064,6 +44064,31 @@
|
@@ -50336,6 +50336,37 @@
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"typetext" : "<string>"
|
"typetext" : "<string>"
|
||||||
},
|
},
|
||||||
|
|
@ -11,7 +11,13 @@
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
+ },
|
+ },
|
||||||
+ "freenas_password" : {
|
+ "freenas_password" : {
|
||||||
+ "description" : "FreeNAS password for API access",
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS Secret for API access",
|
||||||
+ "optional" : 1,
|
+ "optional" : 1,
|
||||||
+ "type" : "string",
|
+ "type" : "string",
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
|
|
@ -32,7 +38,7 @@
|
||||||
"fuse" : {
|
"fuse" : {
|
||||||
"description" : "Mount CephFS through FUSE.",
|
"description" : "Mount CephFS through FUSE.",
|
||||||
"optional" : 1,
|
"optional" : 1,
|
||||||
@@ -44275,6 +44300,12 @@
|
@@ -50555,6 +50586,12 @@
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"typetext" : "<boolean>"
|
"typetext" : "<boolean>"
|
||||||
},
|
},
|
||||||
|
|
@ -45,7 +51,7 @@
|
||||||
"transport" : {
|
"transport" : {
|
||||||
"description" : "Gluster transport: tcp or rdma",
|
"description" : "Gluster transport: tcp or rdma",
|
||||||
"enum" : [
|
"enum" : [
|
||||||
@@ -44547,6 +44578,31 @@
|
@@ -50854,6 +50891,37 @@
|
||||||
"optional" : 1,
|
"optional" : 1,
|
||||||
"type" : "string",
|
"type" : "string",
|
||||||
"typetext" : "<string>"
|
"typetext" : "<string>"
|
||||||
|
|
@ -57,7 +63,13 @@
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
+ },
|
+ },
|
||||||
+ "freenas_password" : {
|
+ "freenas_password" : {
|
||||||
+ "description" : "FreeNAS password for API access",
|
+ "description" : "FreeNAS password for API access (Deprecated)",
|
||||||
|
+ "optional" : 1,
|
||||||
|
+ "type" : "string",
|
||||||
|
+ "typetext" : "<string>"
|
||||||
|
+ },
|
||||||
|
+ "truenas_secret" : {
|
||||||
|
+ "description" : "TrueNAS secret for API access",
|
||||||
+ "optional" : 1,
|
+ "optional" : 1,
|
||||||
+ "type" : "string",
|
+ "type" : "string",
|
||||||
+ "typetext" : "<string>"
|
+ "typetext" : "<string>"
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
--- pvemanagerlib.js.orig 2023-12-30 15:36:27.913505863 -0500
|
||||||
|
+++ pvemanagerlib.js 2024-01-02 09:30:56.000000000 -0500
|
||||||
|
@@ -9228,6 +9228,7 @@
|
||||||
|
alias: ['widget.pveiScsiProviderSelector'],
|
||||||
|
comboItems: [
|
||||||
|
['comstar', 'Comstar'],
|
||||||
|
+ ['freenas', 'FreeNAS/TrueNAS API'],
|
||||||
|
['istgt', 'istgt'],
|
||||||
|
['iet', 'IET'],
|
||||||
|
['LIO', 'LIO'],
|
||||||
|
@@ -58017,16 +58018,24 @@
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
+
|
||||||
|
Ext.define('PVE.storage.ZFSInputPanel', {
|
||||||
|
extend: 'PVE.panel.StorageBase',
|
||||||
|
|
||||||
|
viewModel: {
|
||||||
|
parent: null,
|
||||||
|
data: {
|
||||||
|
+isComstar: true,
|
||||||
|
+ isFreeNAS: false,
|
||||||
|
isLIO: false,
|
||||||
|
- isComstar: true,
|
||||||
|
+ isToken: false,
|
||||||
|
hasWriteCacheOption: true,
|
||||||
|
},
|
||||||
|
+formulas: {
|
||||||
|
+ hideUsername: function(get) {
|
||||||
|
+ return (!get('isFreeNAS') || !(get('isFreeNAS') && !get('isToken')));
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
@@ -58034,13 +58043,42 @@
|
||||||
|
control: {
|
||||||
|
'field[name=iscsiprovider]': {
|
||||||
|
change: 'changeISCSIProvider',
|
||||||
|
+},
|
||||||
|
+ 'field[name=truenas_token_auth]': {
|
||||||
|
+ change: 'changeUsername',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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('truenas_token_auth_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('truenas_secret_field').setValue('');
|
||||||
|
+ me.lookupReference('truenas_secret_field').allowBlank = true;
|
||||||
|
+ me.lookupReference('truenas_confirm_secret_field').setValue('');
|
||||||
|
+ me.lookupReference('truenas_confirm_secret_field').allowBlank = true;
|
||||||
|
+ } else {
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
||||||
|
+ me.lookupReference('truenas_secret_field').allowBlank = false;
|
||||||
|
+ me.lookupReference('truenas_confirm_secret_field').allowBlank = false;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ changeUsername: function(f, newVal, oldVal) {
|
||||||
|
+ var me = this;
|
||||||
|
+ var vm = me.getViewModel();
|
||||||
|
+ vm.set('isToken', newVal);
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = newVal;
|
||||||
|
+ if (newVal) {
|
||||||
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
|
+ }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
@@ -58053,28 +58091,78 @@
|
||||||
|
|
||||||
|
values.nowritecache = values.writecache ? 0 : 1;
|
||||||
|
delete values.writecache;
|
||||||
|
+ console.warn(values.freenas_password);
|
||||||
|
+ if (values.freenas_password) {
|
||||||
|
+ values.truenas_secret = values.freenas_password;
|
||||||
|
+ }
|
||||||
|
+ console.warn(values.truenas_secret);
|
||||||
|
|
||||||
|
return me.callParent([values]);
|
||||||
|
},
|
||||||
|
|
||||||
|
setValues: function(values) {
|
||||||
|
- values.writecache = values.nowritecache ? 0 : 1;
|
||||||
|
- this.callParent([values]);
|
||||||
|
+ if (values.freenas_password) {
|
||||||
|
+ values.truenas_secret = values.freenas_password;
|
||||||
|
+ }
|
||||||
|
+ values.truenas_confirm_secret = values.truenas_secret;
|
||||||
|
+ values.writecache = values.nowritecache ? 0 : 1;
|
||||||
|
+ this.callParent([values]);
|
||||||
|
},
|
||||||
|
|
||||||
|
initComponent: function() {
|
||||||
|
- var me = this;
|
||||||
|
+ var me = this;
|
||||||
|
+
|
||||||
|
+ var tnsecret = Ext.create('Ext.form.TextField', {
|
||||||
|
+ xtype: 'proxmoxtextfield',
|
||||||
|
+ name: 'truenas_secret',
|
||||||
|
+ reference: 'truenas_secret_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=truenas_confirm_secret]').validate();
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- me.column1 = [
|
||||||
|
- {
|
||||||
|
- xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
- name: 'portal',
|
||||||
|
+ var tnconfirmsecret = Ext.create('Ext.form.TextField', {
|
||||||
|
+ xtype: 'proxmoxtextfield',
|
||||||
|
+ name: 'truenas_confirm_secret',
|
||||||
|
+ reference: 'truenas_confirm_secret_field',
|
||||||
|
+ inputType: me.isCreate ? '' : 'password',
|
||||||
|
+ value: '',
|
||||||
|
+ editable: true,
|
||||||
|
+ submitValue: false,
|
||||||
|
+ emptyText: Proxmox.Utils.noneText,
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{!isFreeNAS}'
|
||||||
|
+ },
|
||||||
|
+ fieldLabel: gettext('Confirm API Password'),
|
||||||
|
+ validator: function(value) {
|
||||||
|
+ var pw = me.up().down('field[name=truenas_secret]').getValue();
|
||||||
|
+ if (pw !== value) {
|
||||||
|
+ return "Secrets do not match!";
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ me.column1 = [
|
||||||
|
+ {
|
||||||
|
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
+ name: 'portal',
|
||||||
|
value: '',
|
||||||
|
fieldLabel: gettext('Portal'),
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
- xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
+ xtype: 'textfield',
|
||||||
|
name: 'pool',
|
||||||
|
value: '',
|
||||||
|
fieldLabel: gettext('Pool'),
|
||||||
|
@@ -58084,11 +58172,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'),
|
||||||
|
@@ -58099,8 +58187,59 @@
|
||||||
|
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: 'proxmoxcheckbox',
|
||||||
|
+ name: 'truenas_token_auth',
|
||||||
|
+ reference: 'truenas_token_auth_field',
|
||||||
|
+ inputId: 'truenas_use_token_auth_field',
|
||||||
|
+ checked: false,
|
||||||
|
+ listeners: {
|
||||||
|
+ change: function(field, newValue) {
|
||||||
|
+ if (newValue === true) {
|
||||||
|
+ tnsecret.labelEl.update('API Token');
|
||||||
|
+ tnconfirmsecret.labelEl.update('Confirm API Token');
|
||||||
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = true;
|
||||||
|
+ } else {
|
||||||
|
+ tnsecret.labelEl.update('API Password');
|
||||||
|
+ tnconfirmsecret.labelEl.update('Confirm API Password');
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{!isFreeNAS}'
|
||||||
|
+ },
|
||||||
|
+ uncheckedValue: 0,
|
||||||
|
+ fieldLabel: gettext('API Token Auth'),
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ xtype: 'textfield',
|
||||||
|
+ name: 'freenas_user',
|
||||||
|
+ reference: 'freenas_user_field',
|
||||||
|
+ inputId: 'freenas_user_field',
|
||||||
|
+ value: '',
|
||||||
|
+ fieldLabel: gettext('API Username'),
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{hideUsername}'
|
||||||
|
+ },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@@ -58131,7 +58270,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,
|
||||||
|
},
|
||||||
|
@@ -58139,15 +58280,32 @@
|
||||||
|
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'),
|
||||||
|
+ 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'),
|
||||||
|
+ },
|
||||||
|
+ tnsecret,
|
||||||
|
+ tnconfirmsecret,
|
||||||
|
];
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
+
|
||||||
|
Ext.define('PVE.storage.ZFSPoolSelector', {
|
||||||
|
extend: 'PVE.form.ComboBoxSetStoreNode',
|
||||||
|
alias: 'widget.pveZFSPoolSelector',
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
--- pvemanagerlib.js.orig 2023-12-30 15:36:27.913505863 -0500
|
||||||
|
+++ pvemanagerlib.js 2024-01-02 09:30:56.000000000 -0500
|
||||||
|
@@ -9228,6 +9228,7 @@
|
||||||
|
alias: ['widget.pveiScsiProviderSelector'],
|
||||||
|
comboItems: [
|
||||||
|
['comstar', 'Comstar'],
|
||||||
|
+ ['freenas', 'FreeNAS/TrueNAS API'],
|
||||||
|
['istgt', 'istgt'],
|
||||||
|
['iet', 'IET'],
|
||||||
|
['LIO', 'LIO'],
|
||||||
|
@@ -58017,16 +58018,24 @@
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
+
|
||||||
|
Ext.define('PVE.storage.ZFSInputPanel', {
|
||||||
|
extend: 'PVE.panel.StorageBase',
|
||||||
|
|
||||||
|
viewModel: {
|
||||||
|
parent: null,
|
||||||
|
data: {
|
||||||
|
+isComstar: true,
|
||||||
|
+ isFreeNAS: false,
|
||||||
|
isLIO: false,
|
||||||
|
- isComstar: true,
|
||||||
|
+ isToken: false,
|
||||||
|
hasWriteCacheOption: true,
|
||||||
|
},
|
||||||
|
+formulas: {
|
||||||
|
+ hideUsername: function(get) {
|
||||||
|
+ return (!get('isFreeNAS') || !(get('isFreeNAS') && !get('isToken')));
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
@@ -58034,13 +58043,42 @@
|
||||||
|
control: {
|
||||||
|
'field[name=iscsiprovider]': {
|
||||||
|
change: 'changeISCSIProvider',
|
||||||
|
+},
|
||||||
|
+ 'field[name=truenas_token_auth]': {
|
||||||
|
+ change: 'changeUsername',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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('truenas_token_auth_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('truenas_secret_field').setValue('');
|
||||||
|
+ me.lookupReference('truenas_secret_field').allowBlank = true;
|
||||||
|
+ me.lookupReference('truenas_confirm_secret_field').setValue('');
|
||||||
|
+ me.lookupReference('truenas_confirm_secret_field').allowBlank = true;
|
||||||
|
+ } else {
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
||||||
|
+ me.lookupReference('truenas_secret_field').allowBlank = false;
|
||||||
|
+ me.lookupReference('truenas_confirm_secret_field').allowBlank = false;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ changeUsername: function(f, newVal, oldVal) {
|
||||||
|
+ var me = this;
|
||||||
|
+ var vm = me.getViewModel();
|
||||||
|
+ vm.set('isToken', newVal);
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = newVal;
|
||||||
|
+ if (newVal) {
|
||||||
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
|
+ }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
@@ -58053,28 +58091,78 @@
|
||||||
|
|
||||||
|
values.nowritecache = values.writecache ? 0 : 1;
|
||||||
|
delete values.writecache;
|
||||||
|
+ console.warn(values.freenas_password);
|
||||||
|
+ if (values.freenas_password) {
|
||||||
|
+ values.truenas_secret = values.freenas_password;
|
||||||
|
+ }
|
||||||
|
+ console.warn(values.truenas_secret);
|
||||||
|
|
||||||
|
return me.callParent([values]);
|
||||||
|
},
|
||||||
|
|
||||||
|
setValues: function(values) {
|
||||||
|
- values.writecache = values.nowritecache ? 0 : 1;
|
||||||
|
- this.callParent([values]);
|
||||||
|
+ if (values.freenas_password) {
|
||||||
|
+ values.truenas_secret = values.freenas_password;
|
||||||
|
+ }
|
||||||
|
+ values.truenas_confirm_secret = values.truenas_secret;
|
||||||
|
+ values.writecache = values.nowritecache ? 0 : 1;
|
||||||
|
+ this.callParent([values]);
|
||||||
|
},
|
||||||
|
|
||||||
|
initComponent: function() {
|
||||||
|
- var me = this;
|
||||||
|
+ var me = this;
|
||||||
|
+
|
||||||
|
+ var tnsecret = Ext.create('Ext.form.TextField', {
|
||||||
|
+ xtype: 'proxmoxtextfield',
|
||||||
|
+ name: 'truenas_secret',
|
||||||
|
+ reference: 'truenas_secret_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=truenas_confirm_secret]').validate();
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
|
||||||
|
- me.column1 = [
|
||||||
|
- {
|
||||||
|
- xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
- name: 'portal',
|
||||||
|
+ var tnconfirmsecret = Ext.create('Ext.form.TextField', {
|
||||||
|
+ xtype: 'proxmoxtextfield',
|
||||||
|
+ name: 'truenas_confirm_secret',
|
||||||
|
+ reference: 'truenas_confirm_secret_field',
|
||||||
|
+ inputType: me.isCreate ? '' : 'password',
|
||||||
|
+ value: '',
|
||||||
|
+ editable: true,
|
||||||
|
+ submitValue: false,
|
||||||
|
+ emptyText: Proxmox.Utils.noneText,
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{!isFreeNAS}'
|
||||||
|
+ },
|
||||||
|
+ fieldLabel: gettext('Confirm API Password'),
|
||||||
|
+ validator: function(value) {
|
||||||
|
+ var pw = me.up().down('field[name=truenas_secret]').getValue();
|
||||||
|
+ if (pw !== value) {
|
||||||
|
+ return "Secrets do not match!";
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+ },
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ me.column1 = [
|
||||||
|
+ {
|
||||||
|
+ xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
+ name: 'portal',
|
||||||
|
value: '',
|
||||||
|
fieldLabel: gettext('Portal'),
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
- xtype: me.isCreate ? 'textfield' : 'displayfield',
|
||||||
|
+ xtype: 'textfield',
|
||||||
|
name: 'pool',
|
||||||
|
value: '',
|
||||||
|
fieldLabel: gettext('Pool'),
|
||||||
|
@@ -58084,11 +58172,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'),
|
||||||
|
@@ -58099,8 +58187,59 @@
|
||||||
|
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: 'proxmoxcheckbox',
|
||||||
|
+ name: 'truenas_token_auth',
|
||||||
|
+ reference: 'truenas_token_auth_field',
|
||||||
|
+ inputId: 'truenas_use_token_auth_field',
|
||||||
|
+ checked: false,
|
||||||
|
+ listeners: {
|
||||||
|
+ change: function(field, newValue) {
|
||||||
|
+ if (newValue === true) {
|
||||||
|
+ tnsecret.labelEl.update('API Token');
|
||||||
|
+ tnconfirmsecret.labelEl.update('Confirm API Token');
|
||||||
|
+ me.lookupReference('freenas_user_field').setValue('');
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = true;
|
||||||
|
+ } else {
|
||||||
|
+ tnsecret.labelEl.update('API Password');
|
||||||
|
+ tnconfirmsecret.labelEl.update('Confirm API Password');
|
||||||
|
+ me.lookupReference('freenas_user_field').allowBlank = false;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ },
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{!isFreeNAS}'
|
||||||
|
+ },
|
||||||
|
+ uncheckedValue: 0,
|
||||||
|
+ fieldLabel: gettext('API Token Auth'),
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ xtype: 'textfield',
|
||||||
|
+ name: 'freenas_user',
|
||||||
|
+ reference: 'freenas_user_field',
|
||||||
|
+ inputId: 'freenas_user_field',
|
||||||
|
+ value: '',
|
||||||
|
+ fieldLabel: gettext('API Username'),
|
||||||
|
+ bind: {
|
||||||
|
+ hidden: '{hideUsername}'
|
||||||
|
+ },
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@@ -58131,7 +58270,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,
|
||||||
|
},
|
||||||
|
@@ -58139,15 +58280,32 @@
|
||||||
|
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'),
|
||||||
|
+ 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'),
|
||||||
|
+ },
|
||||||
|
+ tnsecret,
|
||||||
|
+ tnconfirmsecret,
|
||||||
|
];
|
||||||
|
|
||||||
|
me.callParent();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
+
|
||||||
|
Ext.define('PVE.storage.ZFSPoolSelector', {
|
||||||
|
extend: 'PVE.form.ComboBoxSetStoreNode',
|
||||||
|
alias: 'widget.pveZFSPoolSelector',
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue