[pve-devel] [PATCH 1/2] Added a new 'generic' LUN management driver to ZFS Plugin. This patch introduces a 'generic' LUN management driver which just invokes an independent 'management helper' (which can be either an script or a native binary) which is invoked for lun creation/deletion/etc.

Pablo Ruiz García pablo.ruiz at gmail.com
Sun Feb 23 00:00:42 CET 2014


From: Pablo Ruiz Garcia <pablo.ruiz at gmail.com>

This way, the specifics of the lun management can be developed independently
of proxmox, and we can avoid interfering with proxmox's release schedule.

The 'protocol' which proxmox uses to communicate with the helper is simply
based on exposing a set of environment variables along with a few command
line arguments which are passed to helper script. This is based on the
same concept around CGI, AGI, and other similar decoupled invocation protocols.

Also, the helper can be invoked either locally or remotelly thru ssh (just
the same way it goes for invoking zfs itself), and some 'commands' expect
some kind of output as response (as it happends with Comstar's code). And
of course, helper's exit code is honored, so a non-zero exit code is
considered an error.

The actual commands supported by the 'helper protocol' are:

* create-lun
  - Arguments: full_zvol_device_path
  - Description: This command should handle creation of a LUN entry for the
                 given ZVOL device, and it is expected to return the LUN's
                 unique-id as output.

* resize-lun
  - Arguments: size uuid_of_lun
  - Description: This command should handle resizing of an specified LUN/ZVOL.

* delete-lun
  - Arguments: uuid_of_lun
  - Description: This command should delete the specified LUN entry.

* add-view
  - Arguments: full_zvol_device_path
  - Description: This command should (if not already done by 'create-lun')
                 expose/publish the LUN attached to the specified ZVOL.
                 Or do nothing if creation and sharing of LUN are both
                 tasks executed by 'create-lun'.

* list-view
  - Arguments: uuid_of_lun
  - Description: This command should return the LUN number of the given
                 LUN unique-id as output.

* list-lun
  - Arguments: full_zvol_device_path
  - Description: This command should return the given ZVOL/LUN's unique-id.

This actual commands are modelled after the ones invoked by Comstar's LunCmd
perl module, which even if it maybe a bit too coupled to Comstar's/Solaris'
way of doing things. It seems to be flexible enough as to be usefull for other
implementations (as there are already one for Iet and one for BSD's Istgt).

Signed-off-by: Pablo Ruiz García <pablo.ruiz at gmail.com>
---
 PVE/Storage/LunCmd/Generic.pm |   87 +++++++++++++++++++++++++++++++++++++++++
 PVE/Storage/ZFSPlugin.pm      |   24 +++++++++--
 2 files changed, 107 insertions(+), 4 deletions(-)
 create mode 100644 PVE/Storage/LunCmd/Generic.pm

diff --git a/PVE/Storage/LunCmd/Generic.pm b/PVE/Storage/LunCmd/Generic.pm
new file mode 100644
index 0000000..4e7ec2b
--- /dev/null
+++ b/PVE/Storage/LunCmd/Generic.pm
@@ -0,0 +1,87 @@
+package PVE::Storage::LunCmd::Generic;
+
+use strict;
+use warnings;
+use Digest::MD5 qw(md5_hex);
+use PVE::Tools qw(run_command file_read_firstline trim dir_glob_regex dir_glob_foreach);
+use Data::Dumper;
+
+my @ssh_opts = ('-o', 'BatchMode=yes');
+my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
+my $id_rsa_path = '/etc/pve/priv/zfs';
+
+sub get_base {
+    return '/dev';
+}
+
+sub encode_cfg_value {
+    my ($key, $value) = @_;
+
+    if ($key eq 'nodes' || $key eq 'content') {
+        return join(',', keys(%$value));
+    }
+
+    return $value;
+}
+
+sub run_lun_command {
+    my ($scfg, $timeout, $method, @params) = @_;
+
+    my $msg = '';
+    my %vars = ();
+    my ($guid, $env, $lundev, $size) = undef;
+
+    my $helper = $scfg->{iscsihelper};
+    die "No 'iscsihelper' defined" if !$helper;
+
+    $timeout = 10 if !$timeout;
+
+    if ($method eq 'create_lu') {
+        $lundev = $params[0];
+        $helper .= " create-lun $lundev";
+    } elsif ($method eq 'modify_lu') {
+        $size = $params[0];
+        $guid = $params[1];
+        $helper .= " resize-lun ";
+    } elsif ($method eq 'delete_lu') {
+        $guid = $params[0];
+        $helper .= " delete-lun $guid";
+    } elsif ($method eq 'add_view') {
+        $lundev = $params[0];
+        $helper .= " add-view $lundev";
+    } elsif ($method eq 'list_view') {
+        $guid = $params[0];
+        $helper .= " list-view $guid";
+    } elsif ($method eq 'list_lu') {
+        $guid = $params[0];
+        $helper .= " list-lun $guid";
+    } else {
+        die "$method not implemented yet!";
+    }
+
+    # Common environment variables
+    $vars{SSHKEY} = "$id_rsa_path/$scfg->{portal}_id_rsa";
+    $vars{LUNDEV} = $lundev if $lundev;
+    $vars{LUNUUID} = $guid if $guid;
+
+    foreach my $k (keys %$scfg) { 
+        $env .= "PMXCFG_$k=\"". encode_cfg_value($k, $scfg->{$k}) ."\" "; 
+    }
+    foreach my $k (keys %vars) { 
+        $env .= "PMXVAR_$k=\"$vars{$k}\" "; 
+    }
+
+    my $output = sub {
+        my $line = shift;
+        $msg .= "$line";
+    };
+
+    my $target = 'root@' . $scfg->{portal};
+    my $cmd = !$scfg->{remotehelper} ? "$env $helper"
+        : [@ssh_cmd, '-i'. "$id_rsa_path/$scfg->{portal}_id_rsa", $target, "$env $helper"];
+
+    run_command($cmd, timeout => $timeout, outfunc => $output);
+
+    return $msg;
+}
+
diff --git a/PVE/Storage/ZFSPlugin.pm b/PVE/Storage/ZFSPlugin.pm
index d077cfb..e89b131 100644
--- a/PVE/Storage/ZFSPlugin.pm
+++ b/PVE/Storage/ZFSPlugin.pm
@@ -11,6 +11,7 @@ use base qw(PVE::Storage::Plugin);
 use PVE::Storage::LunCmd::Comstar;
 use PVE::Storage::LunCmd::Istgt;
 use PVE::Storage::LunCmd::Iet;
+use PVE::Storage::LunCmd::Generic;
 
 my @ssh_opts = ('-o', 'BatchMode=yes');
 my @ssh_cmd = ('/usr/bin/ssh', @ssh_opts);
@@ -29,7 +30,7 @@ my $lun_cmds = {
 my $zfs_unknown_scsi_provider = sub {
     my ($provider) = @_;
 
-    die "$provider: unknown iscsi provider. Available [comstar, istgt, iet]";
+    die "$provider: unknown iscsi provider. Available [comstar, istgt, iet, generic]";
 };
 
 my $zfs_get_base = sub {
@@ -41,6 +42,8 @@ my $zfs_get_base = sub {
         return PVE::Storage::LunCmd::Istgt::get_base;
     } elsif ($scfg->{iscsiprovider} eq 'iet') {
         return PVE::Storage::LunCmd::Iet::get_base;
+    } elsif ($scfg->{iscsiprovider} eq 'generic') {
+        return PVE::Storage::LunCmd::Generic::get_base;
     } else {
         $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
     }
@@ -63,6 +66,8 @@ sub zfs_request {
             $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 'generic') {
+            $msg = PVE::Storage::LunCmd::Generic::run_lun_command($scfg, $timeout, $method, @params);
         } else {
             $zfs_unknown_scsi_provider->($scfg->{iscsiprovider});
         }
@@ -320,10 +325,20 @@ sub properties {
         description => "iscsi provider",
         type => 'string',
     },
+        iscsihelper => {
+            description => "iSCSI helper command (used by generic iscsi provider to handle lun creation/deletion/etc.",
+            type => 'string',
+            optional => 1,
+        },
+        remotehelper => {
+            description => "Wether helper command should be invoked locally (at pmx host) or remotelly (at iscsi server).",
+            type => 'boolean',
+            optiona => 1,
+        },
         blocksize => {
             description => "block size",
             type => 'string',
-        }
+        },
     };
 }
 
@@ -336,7 +351,9 @@ sub options {
     pool => { fixed => 1 },
     blocksize => { fixed => 1 },
     iscsiprovider => { fixed => 1 },
-    content => { optional => 1 },
+    iscsihelper => { optional => 1 },
+    remotehelper => { optional => 1 },
+    content => { optional => 1 }
     };
 }
 
@@ -489,7 +506,6 @@ sub list_images {
     if (my $dat = $cache->{zfs}->{$zfspool}) {
 
     foreach my $image (keys %$dat) {
-
         my $volname = $dat->{$image}->{name};
         my $parent = $dat->{$image}->{parent};
 
-- 
1.7.1




More information about the pve-devel mailing list