From 2f22e6062682b12a26634c85927ff4efeff7abc4 Mon Sep 17 00:00:00 2001 From: KevinAdams Date: Mon, 4 Feb 2019 10:48:05 -0500 Subject: [PATCH 1/3] Remove ZFSPoolPlugin.pm from repo. - Mistakenly added the ZFSPoolPlugin.pm to the repo. --- perl5/PVE/Storage/.gitignore | 1 + perl5/PVE/Storage/ZFSPoolPlugin.pm | 734 ----------------------------- 2 files changed, 1 insertion(+), 734 deletions(-) delete mode 100644 perl5/PVE/Storage/ZFSPoolPlugin.pm diff --git a/perl5/PVE/Storage/.gitignore b/perl5/PVE/Storage/.gitignore index d7b8bf3..bafa238 100644 --- a/perl5/PVE/Storage/.gitignore +++ b/perl5/PVE/Storage/.gitignore @@ -1 +1,2 @@ /ZFSPlugin.pm +/ZFSPoolPlugin.pm diff --git a/perl5/PVE/Storage/ZFSPoolPlugin.pm b/perl5/PVE/Storage/ZFSPoolPlugin.pm deleted file mode 100644 index 6e08457..0000000 --- a/perl5/PVE/Storage/ZFSPoolPlugin.pm +++ /dev/null @@ -1,734 +0,0 @@ -package PVE::Storage::ZFSPoolPlugin; - -use strict; -use warnings; -use IO::File; -use POSIX; -use PVE::Tools qw(run_command); -use PVE::Storage::Plugin; -use PVE::RPCEnvironment; -use Net::IP; - -use base qw(PVE::Storage::Plugin); - -sub type { - return 'zfspool'; -} - -sub plugindata { - return { - content => [ {images => 1, rootdir => 1}, {images => 1 , rootdir => 1}], - format => [ { raw => 1, subvol => 1 } , 'raw' ], - }; -} - -sub properties { - return { - blocksize => { - description => "block size", - type => 'string', - }, - sparse => { - description => "use sparse volumes", - type => 'boolean', - }, - }; -} - -sub options { - return { - pool => { fixed => 1 }, - blocksize => { optional => 1 }, - sparse => { optional => 1 }, - nodes => { optional => 1 }, - disable => { optional => 1 }, - content => { optional => 1 }, - bwlimit => { optional => 1 }, - }; -} - -# static zfs helper methods - -sub zfs_parse_size { - my ($text) = @_; - - return 0 if !$text; - - if ($text =~ m/^(\d+(\.\d+)?)([TGMK])?$/) { - - my ($size, $reminder, $unit) = ($1, $2, $3); - - if ($unit) { - if ($unit eq 'K') { - $size *= 1024; - } elsif ($unit eq 'M') { - $size *= 1024*1024; - } elsif ($unit eq 'G') { - $size *= 1024*1024*1024; - } elsif ($unit eq 'T') { - $size *= 1024*1024*1024*1024; - } else { - die "got unknown zfs size unit '$unit'\n"; - } - } - - if ($reminder) { - $size = ceil($size); - } - - return $size; - - } - - warn "unable to parse zfs size '$text'\n"; - - return 0; -} - -sub zfs_parse_zvol_list { - my ($text) = @_; - - my $list = (); - - return $list if !$text; - - my @lines = split /\n/, $text; - foreach my $line (@lines) { - my ($dataset, $size, $origin, $type, $refquota) = split(/\s+/, $line); - next if !($type eq 'volume' || $type eq 'filesystem'); - - my $zvol = {}; - my @parts = split /\//, $dataset; - next if scalar(@parts) < 2; # we need pool/name - my $name = pop @parts; - my $pool = join('/', @parts); - - next unless $name =~ m!^(vm|base|subvol|basevol)-(\d+)-(\S+)$!; - $zvol->{owner} = $2; - - $zvol->{pool} = $pool; - $zvol->{name} = $name; - if ($type eq 'filesystem') { - if ($refquota eq 'none') { - $zvol->{size} = 0; - } else { - $zvol->{size} = zfs_parse_size($refquota); - } - $zvol->{format} = 'subvol'; - } else { - $zvol->{size} = zfs_parse_size($size); - $zvol->{format} = 'raw'; - } - if ($origin !~ /^-$/) { - $zvol->{origin} = $origin; - } - push @$list, $zvol; - } - - return $list; -} - -sub parse_volname { - my ($class, $volname) = @_; - - if ($volname =~ m/^(((base|basevol)-(\d+)-\S+)\/)?((base|basevol|vm|subvol)-(\d+)-\S+)$/) { - my $format = ($6 eq 'subvol' || $6 eq 'basevol') ? 'subvol' : 'raw'; - my $isBase = ($6 eq 'base' || $6 eq 'basevol'); - return ('images', $5, $7, $2, $4, $isBase, $format); - } - - die "unable to parse zfs volume name '$volname'\n"; -} - -# virtual zfs methods (subclass can overwrite them) - -sub path { - my ($class, $scfg, $volname, $storeid, $snapname) = @_; - - my ($vtype, $name, $vmid) = $class->parse_volname($volname); - - my $path = ''; - - if ($vtype eq "images") { - if ($name =~ m/^subvol-/ || $name =~ m/^basevol-/) { - # fixme: we currently assume standard mount point?! - $path = "/$scfg->{pool}/$name"; - } else { - $path = "/dev/zvol/$scfg->{pool}/$name"; - } - $path .= "\@$snapname" if defined($snapname); - } else { - die "$vtype is not allowed in ZFSPool!"; - } - - return ($path, $vmid, $vtype); -} - -sub zfs_request { - my ($class, $scfg, $timeout, $method, @params) = @_; - - my $default_timeout = PVE::RPCEnvironment->is_worker() ? 60*60 : 5; - - my $cmd = []; - - if ($method eq 'zpool_list') { - push @$cmd, 'zpool', 'list'; - } elsif ($method eq 'zpool_import') { - push @$cmd, 'zpool', 'import'; - $default_timeout = 15 if $default_timeout < 15; - } else { - push @$cmd, 'zfs', $method; - } - - push @$cmd, @params; - - my $msg = ''; - - my $output = sub { - my $line = shift; - $msg .= "$line\n"; - }; - - $timeout = $default_timeout if !$timeout; - - run_command($cmd, errmsg => "zfs error", outfunc => $output, timeout => $timeout); - - return $msg; -} - -sub alloc_image { - my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_; - - my $volname = $name; - - if ($fmt eq 'raw') { - - die "illegal name '$volname' - should be 'vm-$vmid-*'\n" - if $volname && $volname !~ m/^vm-$vmid-/; - $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid, $fmt) - if !$volname; - - $class->zfs_create_zvol($scfg, $volname, $size); - my $devname = "/dev/zvol/$scfg->{pool}/$volname"; - - my $timeout = PVE::RPCEnvironment->is_worker() ? 60*5 : 10; - for (my $i = 1; $i <= $timeout; $i++) { - last if -b $devname; - die "Timeout: no zvol after $timeout sec found.\n" - if $i == $timeout; - - sleep(1); - } - } elsif ( $fmt eq 'subvol') { - - die "illegal name '$volname' - should be 'subvol-$vmid-*'\n" - if $volname && $volname !~ m/^subvol-$vmid-/; - $volname = $class->zfs_find_free_diskname($storeid, $scfg, $vmid, $fmt) - if !$volname; - - die "illegal name '$volname' - should be 'subvol-$vmid-*'\n" - if $volname !~ m/^subvol-$vmid-/; - - $class->zfs_create_subvol($scfg, $volname, $size); - - } else { - die "unsupported format '$fmt'"; - } - - return $volname; -} - -sub free_image { - my ($class, $storeid, $scfg, $volname, $isBase) = @_; - - my (undef, $name, undef) = $class->parse_volname($volname); - - $class->zfs_delete_zvol($scfg, $name); - - return undef; -} - -sub list_images { - my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_; - - $cache->{zfs} = $class->zfs_list_zvol($scfg) if !$cache->{zfs}; - my $zfspool = $scfg->{pool}; - my $res = []; - - if (my $dat = $cache->{zfs}->{$zfspool}) { - - foreach my $image (keys %$dat) { - - my $info = $dat->{$image}; - - my $volname = $info->{name}; - my $parent = $info->{parent}; - my $owner = $info->{vmid}; - - if ($parent && $parent =~ m/^(\S+)\@__base__$/) { - my ($basename) = ($1); - $info->{volid} = "$storeid:$basename/$volname"; - } else { - $info->{volid} = "$storeid:$volname"; - } - - if ($vollist) { - my $found = grep { $_ eq $info->{volid} } @$vollist; - next if !$found; - } else { - next if defined ($vmid) && ($owner ne $vmid); - } - - push @$res, $info; - } - } - return $res; -} - -sub zfs_get_pool_stats { - my ($class, $scfg) = @_; - - my $available = 0; - my $used = 0; - - my $text = $class->zfs_request($scfg, undef, 'get', '-o', 'value', '-Hp', - 'available,used', $scfg->{pool}); - - my @lines = split /\n/, $text; - - if($lines[0] =~ /^(\d+)$/) { - $available = $1; - } - - if($lines[1] =~ /^(\d+)$/) { - $used = $1; - } - - return ($available, $used); -} - -sub zfs_create_zvol { - my ($class, $scfg, $zvol, $size) = @_; - - my $cmd = ['create']; - - push @$cmd, '-s' if $scfg->{sparse}; - - push @$cmd, '-b', $scfg->{blocksize} if $scfg->{blocksize}; - - push @$cmd, '-V', "${size}k", "$scfg->{pool}/$zvol"; - - $class->zfs_request($scfg, undef, @$cmd); -} - -sub zfs_create_subvol { - my ($class, $scfg, $volname, $size) = @_; - - my $dataset = "$scfg->{pool}/$volname"; - - my $cmd = ['create', '-o', 'acltype=posixacl', '-o', 'xattr=sa', - '-o', "refquota=${size}k", $dataset]; - - $class->zfs_request($scfg, undef, @$cmd); -} - -sub zfs_delete_zvol { - my ($class, $scfg, $zvol) = @_; - - my $err; - - for (my $i = 0; $i < 6; $i++) { - - eval { $class->zfs_request($scfg, undef, 'destroy', '-r', "$scfg->{pool}/$zvol"); }; - if ($err = $@) { - if ($err =~ m/^zfs error:(.*): dataset is busy.*/) { - sleep(1); - } elsif ($err =~ m/^zfs error:.*: dataset does not exist.*$/) { - $err = undef; - last; - } else { - die $err; - } - } else { - last; - } - } - - die $err if $err; -} - -sub zfs_list_zvol { - my ($class, $scfg) = @_; - - my $text = $class->zfs_request($scfg, 10, 'list', '-o', 'name,volsize,origin,type,refquota', '-t', 'volume,filesystem', '-Hr'); - my $zvols = zfs_parse_zvol_list($text); - return undef if !$zvols; - - my $list = (); - foreach my $zvol (@$zvols) { - my $pool = $zvol->{pool}; - my $name = $zvol->{name}; - my $parent = $zvol->{origin}; - if($zvol->{origin} && $zvol->{origin} =~ m/^$scfg->{pool}\/(\S+)$/){ - $parent = $1; - } - - $list->{$pool}->{$name} = { - name => $name, - size => $zvol->{size}, - parent => $parent, - format => $zvol->{format}, - vmid => $zvol->{owner}, - }; - } - - return $list; -} - -sub zfs_find_free_diskname { - my ($class, $storeid, $scfg, $vmid, $format) = @_; - - my $volumes = $class->zfs_list_zvol($scfg); - my $dat = $volumes->{$scfg->{pool}}; - - my $disk_list = [ keys %$dat ]; - return PVE::Storage::Plugin::get_next_vm_diskname($disk_list, $storeid, $vmid, $format, $scfg); -} - -sub zfs_get_latest_snapshot { - my ($class, $scfg, $volname) = @_; - - my $vname = ($class->parse_volname($volname))[1]; - - # abort rollback if snapshot is not the latest - my @params = ('-t', 'snapshot', '-o', 'name', '-s', 'creation'); - my $text = $class->zfs_request($scfg, undef, 'list', @params); - my @snapshots = split(/\n/, $text); - - my $recentsnap; - foreach (@snapshots) { - if (/$scfg->{pool}\/$vname/) { - s/^.*@//; - $recentsnap = $_; - } - } - - return $recentsnap; -} - -sub status { - my ($class, $storeid, $scfg, $cache) = @_; - - my $total = 0; - my $free = 0; - my $used = 0; - my $active = 0; - - eval { - ($free, $used) = $class->zfs_get_pool_stats($scfg); - $active = 1; - $total = $free + $used; - }; - warn $@ if $@; - - return ($total, $free, $used, $active); -} - -sub volume_size_info { - my ($class, $scfg, $storeid, $volname, $timeout) = @_; - - my (undef, $vname, undef, undef, undef, undef, $format) = - $class->parse_volname($volname); - - my $attr = $format eq 'subvol' ? 'refquota' : 'volsize'; - my $text = $class->zfs_request($scfg, undef, 'get', '-Hp', $attr, "$scfg->{pool}/$vname"); - if ($text =~ /\s$attr\s(\d+)\s/) { - return $1; - } - - die "Could not get zfs volume size\n"; -} - -sub volume_snapshot { - my ($class, $scfg, $storeid, $volname, $snap) = @_; - - my $vname = ($class->parse_volname($volname))[1]; - - $class->zfs_request($scfg, undef, 'snapshot', "$scfg->{pool}/$vname\@$snap"); -} - -sub volume_snapshot_delete { - my ($class, $scfg, $storeid, $volname, $snap, $running) = @_; - - my $vname = ($class->parse_volname($volname))[1]; - - $class->deactivate_volume($storeid, $scfg, $vname, $snap, {}); - $class->zfs_request($scfg, undef, 'destroy', "$scfg->{pool}/$vname\@$snap"); -} - -sub volume_snapshot_rollback { - my ($class, $scfg, $storeid, $volname, $snap) = @_; - - my $vname = ($class->parse_volname($volname))[1]; - - $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$vname\@$snap"); -} - -sub volume_rollback_is_possible { - my ($class, $scfg, $storeid, $volname, $snap) = @_; - - my $recentsnap = $class->zfs_get_latest_snapshot($scfg, $volname); - if ($snap ne $recentsnap) { - die "can't rollback, more recent snapshots exist\n"; - } - - return 1; -} - -sub volume_snapshot_list { - my ($class, $scfg, $storeid, $volname) = @_; - - my ($vtype, $name, $vmid) = $class->parse_volname($volname); - - my $zpath = "$scfg->{pool}/$name"; - - my $snaps = []; - - my $cmd = ['zfs', 'list', '-r', '-H', '-S', 'name', '-t', 'snap', '-o', - 'name', $zpath]; - - my $outfunc = sub { - my $line = shift; - - if ($line =~ m/^\Q$zpath\E@(.*)$/) { - push @$snaps, $1; - } - }; - - eval { run_command( [$cmd], outfunc => $outfunc , errfunc => sub{}); }; - - # return an empty array if dataset does not exist. - return $snaps; -} - -sub activate_storage { - my ($class, $storeid, $scfg, $cache) = @_; - - # Note: $scfg->{pool} can include dataset / - my $pool = $scfg->{pool}; - $pool =~ s!/.*$!!; - - my @param = ('-o', 'name', '-H', "$pool"); - my $res; - eval { - $res = $class->zfs_request($scfg, undef, 'zpool_list', @param); - }; - - if ($@ || !defined($res) || $res !~ $pool) { - eval { - @param = ('-d', '/dev/disk/by-id/', "$pool"); - $class->zfs_request($scfg, undef, 'zpool_import', @param); - }; - die "could not activate storage '$storeid', $@\n" if $@; - } - return 1; -} - -sub deactivate_storage { - my ($class, $storeid, $scfg, $cache) = @_; - return 1; -} - -sub activate_volume { - my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_; - return 1; -} - -sub deactivate_volume { - my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_; - return 1; -} - -sub clone_image { - my ($class, $scfg, $storeid, $volname, $vmid, $snap) = @_; - - $snap ||= '__base__'; - - my ($vtype, $basename, $basevmid, undef, undef, $isBase, $format) = - $class->parse_volname($volname); - - die "clone_image only works on base images\n" if !$isBase; - - my $name = $class->zfs_find_free_diskname($storeid, $scfg, $vmid, $format); - - if ($format eq 'subvol') { - my $size = $class->zfs_request($scfg, undef, 'list', '-H', '-o', 'refquota', "$scfg->{pool}/$basename"); - chomp($size); - $class->zfs_request($scfg, undef, 'clone', "$scfg->{pool}/$basename\@$snap", "$scfg->{pool}/$name", '-o', "refquota=$size"); - } else { - $class->zfs_request($scfg, undef, 'clone', "$scfg->{pool}/$basename\@$snap", "$scfg->{pool}/$name"); - } - - return "$basename/$name"; -} - -sub create_base { - my ($class, $storeid, $scfg, $volname) = @_; - - my $snap = '__base__'; - - my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) = - $class->parse_volname($volname); - - die "create_base not possible with base image\n" if $isBase; - - my $newname = $name; - if ( $format eq 'subvol' ) { - $newname =~ s/^subvol-/basevol-/; - } else { - $newname =~ s/^vm-/base-/; - } - my $newvolname = $basename ? "$basename/$newname" : "$newname"; - - $class->zfs_request($scfg, undef, 'rename', "$scfg->{pool}/$name", "$scfg->{pool}/$newname"); - - my $running = undef; #fixme : is create_base always offline ? - - $class->volume_snapshot($scfg, $storeid, $newname, $snap, $running); - - return $newvolname; -} - -sub volume_resize { - my ($class, $scfg, $storeid, $volname, $size, $running) = @_; - - my $new_size = int($size/1024); - - my (undef, $vname, undef, undef, undef, undef, $format) = - $class->parse_volname($volname); - - my $attr = $format eq 'subvol' ? 'refquota' : 'volsize'; - - $class->zfs_request($scfg, undef, 'set', "$attr=${new_size}k", "$scfg->{pool}/$vname"); - - return $new_size; -} - -sub storage_can_replicate { - my ($class, $scfg, $storeid, $format) = @_; - - return 1 if $format eq 'raw' || $format eq 'subvol'; - - 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}, - sparseinit => { base => 1, current => 1}, - replicate => { 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 volume_export { - my ($class, $scfg, $storeid, $fh, $volname, $format, $snapshot, $base_snapshot, $with_snapshots) = @_; - - die "unsupported export stream format for $class: $format\n" - if $format ne 'zfs'; - - die "$class storage can only export snapshots\n" - if !defined($snapshot); - - my $dataset = ($class->parse_volname($volname))[1]; - - my $fd = fileno($fh); - die "internal error: invalid file handle for volume_export\n" - if !defined($fd); - $fd = ">&$fd"; - - # For zfs we always create a replication stream (-R) which means the remote - # side will always delete non-existing source snapshots. This should work - # for all our use cases. - my $cmd = ['zfs', 'send', '-Rpv']; - if (defined($base_snapshot)) { - my $arg = $with_snapshots ? '-I' : '-i'; - push @$cmd, $arg, $base_snapshot; - } - push @$cmd, '--', "$scfg->{pool}/$dataset\@$snapshot"; - - run_command($cmd, output => $fd); - - return; -} - -sub volume_export_formats { - my ($class, $scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots) = @_; - - my @formats = ('zfs'); - # TODOs: - # push @formats, 'fies' if $volname !~ /^(?:basevol|subvol)-/; - # push @formats, 'raw' if !$base_snapshot && !$with_snapshots; - return @formats; -} - -sub volume_import { - my ($class, $scfg, $storeid, $fh, $volname, $format, $base_snapshot, $with_snapshots) = @_; - - die "unsupported import stream format for $class: $format\n" - if $format ne 'zfs'; - - my $fd = fileno($fh); - die "internal error: invalid file handle for volume_import\n" - if !defined($fd); - - my $dataset = ($class->parse_volname($volname))[1]; - my $zfspath = "$scfg->{pool}/$dataset"; - my $suffix = defined($base_snapshot) ? "\@$base_snapshot" : ''; - my $exists = 0 == run_command(['zfs', 'get', '-H', 'name', $zfspath.$suffix], - noerr => 1, errfunc => sub {}); - if (defined($base_snapshot)) { - die "base snapshot '$zfspath\@$base_snapshot' doesn't exist\n" if !$exists; - } else { - die "volume '$zfspath' already exists\n" if $exists; - } - - eval { run_command(['zfs', 'recv', '-F', '--', $zfspath], input => "<&$fd") }; - if (my $err = $@) { - if (defined($base_snapshot)) { - eval { run_command(['zfs', 'rollback', '-r', '--', "$zfspath\@$base_snapshot"]) }; - } else { - eval { run_command(['zfs', 'destroy', '-r', '--', $zfspath]) }; - } - die $err; - } - - return; -} - -sub volume_import_formats { - my ($class, $scfg, $storeid, $volname, $base_snapshot, $with_snapshots) = @_; - - return $class->volume_export_formats($scfg, $storeid, $volname, undef, $base_snapshot, $with_snapshots); -} - -1; From 920fbed34276c1fa7a0ff6a6773429e7e04eda86 Mon Sep 17 00:00:00 2001 From: Kevin Scott Adams Date: Tue, 5 Feb 2019 07:39:03 -0500 Subject: [PATCH 2/3] Fixed issue on non-FreeNAS providers that would give a 'property' error. - Issue was found that when saving a non-FreeNAS provider config that a 'delete: property error' was generated via the javascript. This issue came from using the 'deleteEmpty' property used in the password fields of the FreeNAS.pm provider pluging. Fixes issue #36, I hope. --- pve-manager/js/pvemanagerlib-5.3-8_1.js.patch | 189 ++++++++++++++++++ pve-manager/js/pvemanagerlib.js.patch | 8 +- 2 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 pve-manager/js/pvemanagerlib-5.3-8_1.js.patch diff --git a/pve-manager/js/pvemanagerlib-5.3-8_1.js.patch b/pve-manager/js/pvemanagerlib-5.3-8_1.js.patch new file mode 100644 index 0000000..9c25bc6 --- /dev/null +++ b/pve-manager/js/pvemanagerlib-5.3-8_1.js.patch @@ -0,0 +1,189 @@ +--- pvemanagerlib.js.orig 2019-01-11 04:23:42.000000000 -0500 ++++ pvemanagerlib.js 2019-02-05 07:30:18.168655803 -0500 +@@ -5894,6 +5894,7 @@ + alias: ['widget.pveiScsiProviderSelector'], + comboItems: [ + ['comstar', 'Comstar'], ++ ['freenas', 'FreeNAS-API'], + [ 'istgt', 'istgt'], + [ 'iet', 'IET'], + [ 'LIO', 'LIO'] +@@ -30085,6 +30086,7 @@ + data: { + isLIO: false, + isComstar: true, ++ isFreeNAS: false, + hasWriteCacheOption: true + } + }, +@@ -30097,10 +30099,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; ++ } + } + }, + +@@ -30118,6 +30136,7 @@ + }, + + setValues: function diff(values) { ++ values.freenas_confirmpw = values.freenas_password; + values.writecache = values.nowritecache ? 0 : 1; + this.callParent([values]); + }, +@@ -30134,7 +30153,7 @@ + allowBlank: false + }, + { +- xtype: me.isCreate ? 'textfield' : 'displayfield', ++ xtype: 'textfield', + name: 'pool', + value: '', + fieldLabel: gettext('Pool'), +@@ -30144,11 +30163,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'), +@@ -30159,8 +30178,33 @@ + 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}' ++ } + } + ]; + +@@ -30191,7 +30235,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 + }, +@@ -30199,9 +30245,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'), ++ 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 d0e07e7..9c25bc6 100644 --- a/pve-manager/js/pvemanagerlib.js.patch +++ b/pve-manager/js/pvemanagerlib.js.patch @@ -1,5 +1,5 @@ ---- pvemanagerlib-5.3-6.js 2018-12-21 12:54:58.650421078 -0500 -+++ pvemanagerlib.js 2019-02-01 13:52:17.906074280 -0500 +--- pvemanagerlib.js.orig 2019-01-11 04:23:42.000000000 -0500 ++++ pvemanagerlib.js 2019-02-05 07:30:18.168655803 -0500 @@ -5894,6 +5894,7 @@ alias: ['widget.pveiScsiProviderSelector'], comboItems: [ @@ -121,7 +121,7 @@ fieldLabel: gettext('Host group'), allowBlank: true }, -@@ -30199,9 +30245,64 @@ +@@ -30199,9 +30245,62 @@ xtype: me.isCreate ? 'textfield' : 'displayfield', name: 'lio_tpg', value: '', @@ -153,7 +153,6 @@ + inputType: me.isCreate ? '' : 'password', + value: '', + editable: true, -+ deleteEmpty: true, + emptyText: Proxmox.Utils.noneText, + bind: { + hidden: '{!isFreeNAS}' @@ -173,7 +172,6 @@ + value: '', + editable: true, + submitValue: false, -+ deleteEmpty: true, + emptyText: Proxmox.Utils.noneText, + bind: { + hidden: '{!isFreeNAS}' From ea73c4f7c9961d80e9d4e375821b8f404c6834c8 Mon Sep 17 00:00:00 2001 From: Kevin Scott Adams Date: Tue, 26 Feb 2019 21:28:15 -0500 Subject: [PATCH 3/3] Update to latest Proxmox VE update - Update ZFSPlugin.pm to reflect 'libpve-storage-perl: 5.0-38' - Update pvemanager.js to reflect 'pve-manager: 5.3-11' - Update pve-docs.js to reflect 'pve-docs: 5.3-3' --- perl5/PVE/Storage/ZFSPlugin-5.0-38_1.pm.patch | 147 ++++++++++++++ perl5/PVE/Storage/ZFSPlugin.pm.patch | 4 +- pve-docs/api-viewer/apidoc-5.3-3_1.js.patch | 79 ++++++++ pve-docs/api-viewer/apidoc.js.patch | 10 +- .../js/pvemanagerlib-5.3-11_1.js.patch | 189 ++++++++++++++++++ pve-manager/js/pvemanagerlib.js.patch | 22 +- 6 files changed, 433 insertions(+), 18 deletions(-) create mode 100644 perl5/PVE/Storage/ZFSPlugin-5.0-38_1.pm.patch create mode 100644 pve-docs/api-viewer/apidoc-5.3-3_1.js.patch create mode 100644 pve-manager/js/pvemanagerlib-5.3-11_1.js.patch diff --git a/perl5/PVE/Storage/ZFSPlugin-5.0-38_1.pm.patch b/perl5/PVE/Storage/ZFSPlugin-5.0-38_1.pm.patch new file mode 100644 index 0000000..fa40368 --- /dev/null +++ b/perl5/PVE/Storage/ZFSPlugin-5.0-38_1.pm.patch @@ -0,0 +1,147 @@ +--- ZFSPlugin.pm.orig 2019-02-07 09:14:12.000000000 -0500 ++++ ZFSPlugin.pm 2019-02-26 20:53:29.096598801 -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') { +@@ -160,6 +166,15 @@ + return $class->zfs_request($scfg, undef, 'list_view', $guid); + } + ++# 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 { +@@ -178,6 +193,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 +@@ -205,14 +238,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 }, +@@ -237,6 +274,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 = `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 b16823d..fa40368 100644 --- a/perl5/PVE/Storage/ZFSPlugin.pm.patch +++ b/perl5/PVE/Storage/ZFSPlugin.pm.patch @@ -1,5 +1,5 @@ ---- ZFSPlugin-5.0-34.pm 2018-12-21 12:52:21.764194866 -0500 -+++ ZFSPlugin.pm 2018-12-21 12:53:08.663665081 -0500 +--- ZFSPlugin.pm.orig 2019-02-07 09:14:12.000000000 -0500 ++++ ZFSPlugin.pm 2019-02-26 20:53:29.096598801 -0500 @@ -10,6 +10,7 @@ use base qw(PVE::Storage::ZFSPoolPlugin); diff --git a/pve-docs/api-viewer/apidoc-5.3-3_1.js.patch b/pve-docs/api-viewer/apidoc-5.3-3_1.js.patch new file mode 100644 index 0000000..ff25121 --- /dev/null +++ b/pve-docs/api-viewer/apidoc-5.3-3_1.js.patch @@ -0,0 +1,79 @@ +--- apidoc.js.orig 2019-02-20 06:20:05.000000000 -0500 ++++ apidoc.js 2019-02-26 20:51:26.857204965 -0500 +@@ -33709,6 +33709,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, +@@ -33865,6 +33890,12 @@ + "type" : "boolean", + "typetext" : "" + }, ++ "target" : { ++ "description" : "iSCSI target.", ++ "optional" : 1, ++ "type" : "string", ++ "typetext" : "" ++ }, + "transport" : { + "description" : "Gluster transport: tcp or rdma", + "enum" : [ +@@ -34071,6 +34102,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 47bdbd2..ff25121 100644 --- a/pve-docs/api-viewer/apidoc.js.patch +++ b/pve-docs/api-viewer/apidoc.js.patch @@ -1,6 +1,6 @@ ---- apidoc-5.3-1.js 2018-12-21 12:46:55.459865478 -0500 -+++ apidoc.js 2018-12-06 15:50:21.487987807 -0500 -@@ -33477,6 +33477,31 @@ +--- apidoc.js.orig 2019-02-20 06:20:05.000000000 -0500 ++++ apidoc.js 2019-02-26 20:51:26.857204965 -0500 +@@ -33709,6 +33709,31 @@ "type" : "string", "typetext" : "" }, @@ -32,7 +32,7 @@ "fuse" : { "description" : "Mount CephFS through FUSE.", "optional" : 1, -@@ -33633,6 +33658,12 @@ +@@ -33865,6 +33890,12 @@ "type" : "boolean", "typetext" : "" }, @@ -45,7 +45,7 @@ "transport" : { "description" : "Gluster transport: tcp or rdma", "enum" : [ -@@ -33840,6 +33871,31 @@ +@@ -34071,6 +34102,31 @@ "optional" : 1, "type" : "string", "typetext" : "" diff --git a/pve-manager/js/pvemanagerlib-5.3-11_1.js.patch b/pve-manager/js/pvemanagerlib-5.3-11_1.js.patch new file mode 100644 index 0000000..cfb2f99 --- /dev/null +++ b/pve-manager/js/pvemanagerlib-5.3-11_1.js.patch @@ -0,0 +1,189 @@ +--- pvemanagerlib.js.orig 2019-02-20 13:40:43.000000000 -0500 ++++ pvemanagerlib.js 2019-02-26 20:49:41.032327478 -0500 +@@ -5920,6 +5920,7 @@ + alias: ['widget.pveiScsiProviderSelector'], + comboItems: [ + ['comstar', 'Comstar'], ++ ['freenas', 'FreeNAS-API'], + [ 'istgt', 'istgt'], + [ 'iet', 'IET'], + [ 'LIO', 'LIO'] +@@ -30182,6 +30183,7 @@ + data: { + isLIO: false, + isComstar: true, ++ isFreeNAS: false, + hasWriteCacheOption: true + } + }, +@@ -30194,10 +30196,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; ++ } + } + }, + +@@ -30215,6 +30233,7 @@ + }, + + setValues: function diff(values) { ++ values.freenas_confirmpw = values.freenas_password; + values.writecache = values.nowritecache ? 0 : 1; + this.callParent([values]); + }, +@@ -30231,7 +30250,7 @@ + allowBlank: false + }, + { +- xtype: me.isCreate ? 'textfield' : 'displayfield', ++ xtype: 'textfield', + name: 'pool', + value: '', + fieldLabel: gettext('Pool'), +@@ -30241,11 +30260,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'), +@@ -30256,8 +30275,33 @@ + 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}' ++ } + } + ]; + +@@ -30288,7 +30332,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 + }, +@@ -30296,9 +30342,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'), ++ 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 9c25bc6..cfb2f99 100644 --- a/pve-manager/js/pvemanagerlib.js.patch +++ b/pve-manager/js/pvemanagerlib.js.patch @@ -1,6 +1,6 @@ ---- pvemanagerlib.js.orig 2019-01-11 04:23:42.000000000 -0500 -+++ pvemanagerlib.js 2019-02-05 07:30:18.168655803 -0500 -@@ -5894,6 +5894,7 @@ +--- pvemanagerlib.js.orig 2019-02-20 13:40:43.000000000 -0500 ++++ pvemanagerlib.js 2019-02-26 20:49:41.032327478 -0500 +@@ -5920,6 +5920,7 @@ alias: ['widget.pveiScsiProviderSelector'], comboItems: [ ['comstar', 'Comstar'], @@ -8,7 +8,7 @@ [ 'istgt', 'istgt'], [ 'iet', 'IET'], [ 'LIO', 'LIO'] -@@ -30085,6 +30086,7 @@ +@@ -30182,6 +30183,7 @@ data: { isLIO: false, isComstar: true, @@ -16,7 +16,7 @@ hasWriteCacheOption: true } }, -@@ -30097,10 +30099,26 @@ +@@ -30194,10 +30196,26 @@ } }, changeISCSIProvider: function(f, newVal, oldVal) { @@ -44,7 +44,7 @@ } }, -@@ -30118,6 +30136,7 @@ +@@ -30215,6 +30233,7 @@ }, setValues: function diff(values) { @@ -52,7 +52,7 @@ values.writecache = values.nowritecache ? 0 : 1; this.callParent([values]); }, -@@ -30134,7 +30153,7 @@ +@@ -30231,7 +30250,7 @@ allowBlank: false }, { @@ -61,7 +61,7 @@ name: 'pool', value: '', fieldLabel: gettext('Pool'), -@@ -30144,11 +30163,11 @@ +@@ -30241,11 +30260,11 @@ xtype: me.isCreate ? 'textfield' : 'displayfield', name: 'blocksize', value: '4k', @@ -75,7 +75,7 @@ name: 'target', value: '', fieldLabel: gettext('Target'), -@@ -30159,8 +30178,33 @@ +@@ -30256,8 +30275,33 @@ name: 'comstar_tg', value: '', fieldLabel: gettext('Target group'), @@ -110,7 +110,7 @@ } ]; -@@ -30191,7 +30235,9 @@ +@@ -30288,7 +30332,9 @@ xtype: me.isCreate ? 'textfield' : 'displayfield', name: 'comstar_hg', value: '', @@ -121,7 +121,7 @@ fieldLabel: gettext('Host group'), allowBlank: true }, -@@ -30199,9 +30245,62 @@ +@@ -30296,9 +30342,62 @@ xtype: me.isCreate ? 'textfield' : 'displayfield', name: 'lio_tpg', value: '',