scstZFSPlugin.pm 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package PVE::Storage::Custom::scstZFSPlugin;
  2. eval {
  3. require 'PVE/Storage/Custom/Shared/ZFSPluginPlus.pm';
  4. };
  5. use PVE::Storage::Custom::LunCmd::SCST;
  6. use Data::Dumper;
  7. use PVE::Tools qw(run_command);
  8. # inherit on the ZFSPlugin
  9. #use base qw(PVE::Storage::ZFSPlugin);
  10. @ISA = qw(PVE::Storage::Custom::Shared::ZFSPluginPlus);
  11. my @ssh_opts = ('-o', 'BatchMode=yes');
  12. my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
  13. my $id_rsa_path = '/etc/pve/priv/zfs';
  14. # plugin configuration
  15. sub api {
  16. return 1;
  17. }
  18. sub type {
  19. return 'scstzfs';
  20. }
  21. sub plugindata {
  22. return {
  23. content => [ { images => 1 }, {images => 1} ]
  24. };
  25. }
  26. sub properties {
  27. return {
  28. nowritecache_scst => {
  29. description => "disable write cache on the target",
  30. type => 'boolean'
  31. },
  32. chap_user => {
  33. type => 'string'
  34. },
  35. chap_pass => {
  36. type => 'string'
  37. },
  38. volume_prefix => {
  39. type => 'string'
  40. },
  41. port => {
  42. type => 'int'
  43. },
  44. ini_group => {
  45. type => 'string'
  46. },
  47. esos => {
  48. type => 'int'
  49. }
  50. }
  51. }
  52. sub options {
  53. return {
  54. nodes => { optional => 1 },
  55. disable => { optional => 1 },
  56. portal => { fixed => 1 },
  57. target => { fixed => 1 },
  58. iscsiprovider => {fixed => 1},
  59. pool => { fixed => 1 },
  60. blocksize => { fixed => 1 },
  61. nowritecache => { optional => 1 },
  62. sparse => { optional => 1 },
  63. comstar_hg => { optional => 1 },
  64. comstar_tg => { optional => 1 },
  65. content => { optional => 1 },
  66. shared => { fixed => 1 },
  67. transport => { optional => 1, default => 'iscsi' }, #iscsi or rdma
  68. chap_user => { optional => 1 },
  69. chap_pass => { optional => 1 },
  70. port => { optional => 1 },
  71. volume_prefix => { optional => 1, default => '' }, #prefixes created volume-names - allows to share pool for multiple clusters
  72. ini_group => { optional => 1, default => ''},
  73. esos => { optional=>1, default => 0 }
  74. };
  75. }
  76. my $lun_cmds = {
  77. create_lu => 1,
  78. delete_lu => 1,
  79. import_lu => 1,
  80. modify_lu => 1,
  81. add_view => 1,
  82. list_view => 1,
  83. list_lu => 1,
  84. };
  85. # SCST-specifics
  86. my $zfs_get_base = sub {
  87. my ($scfg) = @_;
  88. return PVE::Storage::Custom::LunCmd::SCST::get_base;
  89. };
  90. sub zfs_get_base {
  91. my ($scfg) = @_;
  92. return $zfs_get_base->($scfg);
  93. }
  94. sub zfs_request {
  95. my ($class, $scfg, $timeout, $method, @params) = @_;
  96. $timeout = PVE::RPCEnvironment::is_worker() ? 60*60 : 10
  97. if !$timeout;
  98. my $msg = '';
  99. if ($lun_cmds->{$method}) {
  100. $msg = PVE::Storage::Custom::LunCmd::SCST::run_lun_command($scfg, $timeout, $method, @params);
  101. } else {
  102. my $target = 'root@' . $scfg->{portal};
  103. my $cmd = [@ssh_cmd, '-i', "$id_rsa_path/$scfg->{portal}_id_rsa", $target];
  104. if ($method eq 'zpool_list') {
  105. push @$cmd, 'zpool', 'list';
  106. } else {
  107. push @$cmd, 'zfs', $method;
  108. }
  109. push @$cmd, @params;
  110. my $output = sub {
  111. my $line = shift;
  112. $msg .= "$line\n";
  113. };
  114. # print Dumper($cmd);
  115. run_command($cmd, outfunc => $output, timeout => $timeout);
  116. }
  117. return $msg;
  118. }
  119. # we want to allow copy of snap, currently dies at line 377 in ZFSPlugin.pm
  120. sub volume_has_feature {
  121. my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
  122. my $features = {
  123. snapshot => { current => 1, snap => 1},
  124. clone => { base => 1},
  125. template => { current => 1},
  126. copy => { base => 1, current => 1, snap=>1}, #added snap
  127. };
  128. my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
  129. $class->parse_volname($volname);
  130. my $key = undef;
  131. if ($snapname) {
  132. $key = 'snap';
  133. } else {
  134. $key = $isBase ? 'base' : 'current';
  135. }
  136. return 1 if $features->{$feature}->{$key};
  137. return undef;
  138. }
  139. #
  140. # + added port, chap-auth and transport (iser)
  141. # + allow access to materialized snapshots
  142. sub path {
  143. my ($class, $scfg, $volname, $storeid, $snapname) = @_;
  144. # die "direct access to snapshots not implemented"
  145. # if defined($snapname);
  146. my ($vtype, $name, $vmid) = $class->parse_volname($volname);
  147. if(defined($snapname)) {
  148. $name = $class->materialize_snapshot($scfg,$volname,$snapname);
  149. }
  150. my $target = $scfg->{target};
  151. my $portal = $scfg->{portal};
  152. my $guid = $class->zfs_get_lu_name($scfg, $name);
  153. my $lun = $class->zfs_get_lun_number($scfg, $guid);
  154. my $transport = $scfg->{transport};
  155. if($transport eq 'rdma') {
  156. $transport = 'iser';
  157. } else {
  158. $transport = 'iscsi';
  159. }
  160. my $authString = $scfg->{chap_user} && $scfg->{chap_pass} ? "$scfg->{chap_user}%$scfg->{chap_pass}@" : '';
  161. my $port = $scfg->{port} ? ":$scfg->{port}" : ':3260'; #qemu-image insists on port !
  162. my $path = "$transport://$authString$portal$port/$target/$lun";
  163. return ($path, $vmid, $vtype);
  164. }
  165. # Other parts of Storage implementation is identical to ZFSPlugin and therefore not changed
  166. 1;