From 9a597cac6462b1e7c656e90c88b3cf7cb70fe619 Mon Sep 17 00:00:00 2001
From: "Samantha N. Bueno" <sbueno+anaconda@redhat.com>
Date: Fri, 1 May 2015 14:39:07 -0400
Subject: [PATCH] Add the s390 plugin

---
 Sconscript                          |   9 +-
 dist/libblockdev.spec               |  33 ++
 docs/libblockdev-docs.xml           |   1 +
 docs/libblockdev-sections.txt       |  14 +
 src/lib/blockdev.c                  |  16 +-
 src/lib/plugin_apis/s390.api        | 100 ++++++
 src/lib/plugins.h                   |   1 +
 src/plugins/s390.c                  | 613 ++++++++++++++++++++++++++++++++++++
 src/plugins/s390.h                  | 101 ++++++
 src/python/gi/overrides/BlockDev.py |  62 ++++
 tests/s390_test.py                  |  71 +++++
 11 files changed, 1017 insertions(+), 4 deletions(-)
 create mode 100644 src/lib/plugin_apis/s390.api
 create mode 100644 src/plugins/s390.c
 create mode 100644 src/plugins/s390.h
 create mode 100644 tests/s390_test.py

diff --git a/Sconscript b/Sconscript
index 717198a..ce3ba1d 100644
--- a/Sconscript
+++ b/Sconscript
@@ -7,7 +7,7 @@ MAIN_VERSION = "0.1.0"
 MAJOR_MINOR = MAIN_VERSION.rsplit(".", 1)[0]
 MAJOR_VERSION = MAIN_VERSION.split(".", 1)[0]
 LIB_FILES = ["src/lib/blockdev.c", "src/lib/blockdev.h", "src/lib/plugins.h"]
-PLUGIN_NAMES = ["crypto", "dm", "loop", "lvm", "mpath", "swap", "btrfs", "mdraid"]
+PLUGIN_NAMES = ["crypto", "dm", "loop", "lvm", "mpath", "swap", "btrfs", "mdraid", "s390"]
 PLUGIN_HEADER_FILES = ["build/plugin_apis/"+name+".h" for name in PLUGIN_NAMES]
 UTILS_FILES = ["src/utils/exec.c", "src/utils/sizes.c", "src/utils/exec.h", "src/utils/sizes.h"]
 
@@ -78,6 +78,12 @@ swap_env.Append(LIBPATH=".")
 swap_env.Append(LIBS="bd_utils")
 plugins.append(swap_env.SharedLibrary("bd_swap", ["src/plugins/swap.c"]))
 
+s390_env = glib_warnall_env.Clone()
+s390_env.Append(CPPPATH="src/utils")
+s390_env.Append(LIBPATH=".")
+s390_env.Append(LIBS="bd_utils")
+plugins.append(s390_env.SharedLibrary("bd_s390", ["src/plugins/s390.c"]))
+
 
 ## boilerplate code generation
 bpg_builder = Builder(action=generate_boilerplate_files)
@@ -93,6 +99,7 @@ boiler_code.append(bpg_env.BpG(["plugin_apis/loop.h", "plugin_apis/loop.c"], "sr
 boiler_code.append(bpg_env.BpG(["plugin_apis/mdraid.h", "plugin_apis/mdraid.c"], "src/lib/plugin_apis/mdraid.api"))
 boiler_code.append(bpg_env.BpG(["plugin_apis/mpath.h", "plugin_apis/mpath.c"], "src/lib/plugin_apis/mpath.api"))
 boiler_code.append(bpg_env.BpG(["plugin_apis/swap.h", "plugin_apis/swap.c"], "src/lib/plugin_apis/swap.api"))
+boiler_code.append(bpg_env.BpG(["plugin_apis/s390.h", "plugin_apis/s390.c"], "src/lib/plugin_apis/s390.api"))
 
 
 ## the library itself
diff --git a/dist/libblockdev.spec b/dist/libblockdev.spec
index 47f1c47..e63ecc5 100644
--- a/dist/libblockdev.spec
+++ b/dist/libblockdev.spec
@@ -217,6 +217,25 @@ Requires: glib2-devel
 This package contains header files and pkg-config files needed for development
 with the libblockdev-swap plugin/library.
 
+%package s390
+Summary:    The s390 plugin for the libblockdev library
+Requires: s390utils
+
+%description s390
+The libblockdev library plugin (and in the same time a standalone library)
+providing the functionality related to s390 devices.
+
+%package s390-devel
+Summary:     Development files for the libblockdev-s390 plugin/library
+Requires: %{name}-s390%{?_isa} = %{version}-%{release}
+Requires: %{name}-utils-devel%{?_isa}
+Requires: glib2-devel
+Requires: s390utils-devel
+
+%description s390-devel
+This package contains header files and pkg-config files needed for development
+with the libblockdev-s390 plugin/library.
+
 
 %package plugins-all
 Summary:     Meta-package that pulls all the libblockdev plugins as dependencies
@@ -229,6 +248,9 @@ Requires: %{name}-lvm%{?_isa} = %{version}-%{release}
 Requires: %{name}-mdraid%{?_isa} = %{version}-%{release}
 Requires: %{name}-mpath%{?_isa} = %{version}-%{release}
 Requires: %{name}-swap%{?_isa} = %{version}-%{release}
+%ifarch s390 s390x
+Requires: %{name}-s390%{?_isa} = %{version}-%{release}
+%endif
 
 %description plugins-all
 A meta-package that pulls all the libblockdev plugins as dependencies.
@@ -264,6 +286,8 @@ CFLAGS="%{optflags}" make PREFIX=%{buildroot} SITEDIRS=%{buildroot}%{python2_sit
 %postun mpath -p /sbin/ldconfig
 %post swap -p /sbin/ldconfig
 %postun swap -p /sbin/ldconfig
+%post s390 -p /sbin/ldconfig
+%postun s390 -p /sbin/ldconfig
 
 
 %files
@@ -368,6 +392,15 @@ CFLAGS="%{optflags}" make PREFIX=%{buildroot} SITEDIRS=%{buildroot}%{python2_sit
 %dir %{_includedir}/blockdev
 %{_includedir}/blockdev/swap.h
 
+
+%files s390
+%{_libdir}/libbd_s390.so.*
+
+%files s390-devel
+%{_libdir}/libbd_s390.so
+%dir %{_includedir}/blockdev
+%{_includedir}/blockdev/s390.h
+
 %files plugins-all
 
 %changelog
diff --git a/docs/libblockdev-docs.xml b/docs/libblockdev-docs.xml
index 32b712a..db3bacc 100644
--- a/docs/libblockdev-docs.xml
+++ b/docs/libblockdev-docs.xml
@@ -30,6 +30,7 @@
     <xi:include href="xml/mpath.xml"/>
     <xi:include href="xml/plugins.xml"/>
     <xi:include href="xml/swap.xml"/>
+    <xi:include href="xml/s390.xml"/>
     <xi:include href="xml/utils.xml"/>
   </chapter>
 
diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index 0e33ddb..26dca00 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -259,3 +259,17 @@ bd_swap_swapoff
 bd_swap_swapstatus
 </SECTION>
 
+<SECTION>
+<FILE>s390</FILE>
+bd_s390_error_quark
+BDS390Error
+bd_s390_dasd_format
+bd_s390_dasd_needs_format
+bd_s390_sanitize_dev_input
+bd_s390_dasd_online
+bd_s390_dasd_is_ldl
+bd_s390_zfcp_sanitize_wwpn_input
+bd_s390_zfcp_sanitize_lun_input
+bd_s390_zfcp_online
+bd_s390_zfcp_offline
+</SECTION>
diff --git a/src/lib/blockdev.c b/src/lib/blockdev.c
index 44f987a..5297ef9 100644
--- a/src/lib/blockdev.c
+++ b/src/lib/blockdev.c
@@ -19,6 +19,8 @@
 #include "plugin_apis/dm.h"
 #include "plugin_apis/mdraid.h"
 #include "plugin_apis/mdraid.c"
+#include "plugin_apis/s390.h"
+#include "plugin_apis/s390.c"
 
 /**
  * SECTION: blockdev
@@ -41,7 +43,8 @@ static gchar * default_plugin_so[BD_PLUGIN_UNDEF] = {
     "libbd_lvm.so."MAJOR_VER, "libbd_btrfs.so."MAJOR_VER,
     "libbd_swap.so."MAJOR_VER, "libbd_loop.so."MAJOR_VER,
     "libbd_crypto.so."MAJOR_VER, "libbd_mpath.so."MAJOR_VER,
-    "libbd_dm.so."MAJOR_VER, "libbd_mdraid.so."MAJOR_VER
+    "libbd_dm.so."MAJOR_VER, "libbd_mdraid.so."MAJOR_VER,
+    "libbd_s390.so."MAJOR_VER, "libbd_s390.so."MAJOR_VER
 };
 static BDPluginStatus plugins[BD_PLUGIN_UNDEF] = {
     {{BD_PLUGIN_LVM, NULL}, NULL},
@@ -51,10 +54,11 @@ static BDPluginStatus plugins[BD_PLUGIN_UNDEF] = {
     {{BD_PLUGIN_CRYPTO, NULL}, NULL},
     {{BD_PLUGIN_MPATH, NULL}, NULL},
     {{BD_PLUGIN_DM, NULL}, NULL},
-    {{BD_PLUGIN_MDRAID, NULL}, NULL}
+    {{BD_PLUGIN_MDRAID, NULL}, NULL},
+    {{BD_PLUGIN_S390, NULL}, NULL}
 };
 static gchar* plugin_names[BD_PLUGIN_UNDEF] = {
-    "lvm", "btrfs", "swap", "loop", "crypto", "mpath", "dm", "mdraid"
+    "lvm", "btrfs", "swap", "loop", "crypto", "mpath", "dm", "mdraid", "s390"
 };
 
 static void set_plugin_so_name (BDPlugin name, gchar *so_name) {
@@ -93,6 +97,10 @@ static void unload_plugins () {
     if (plugins[BD_PLUGIN_MDRAID].handle && !unload_mdraid (plugins[BD_PLUGIN_MDRAID].handle))
         g_warning ("Failed to close the mdraid plugin");
     plugins[BD_PLUGIN_MDRAID].handle = NULL;
+
+    if (plugins[BD_PLUGIN_S390].handle && !unload_s390 (plugins[BD_PLUGIN_S390].handle))
+        g_warning ("Failed to close the s390 plugin");
+    plugins[BD_PLUGIN_S390].handle = NULL;
 }
 
 static gboolean load_plugins (BDPluginSpec **require_plugins, gboolean reload) {
@@ -137,6 +145,8 @@ static gboolean load_plugins (BDPluginSpec **require_plugins, gboolean reload) {
         plugins[BD_PLUGIN_DM].handle = load_dm_from_plugin(plugins[BD_PLUGIN_DM].spec.so_name);
     if (!plugins[BD_PLUGIN_MDRAID].handle && plugins[BD_PLUGIN_MDRAID].spec.so_name)
         plugins[BD_PLUGIN_MDRAID].handle = load_mdraid_from_plugin(plugins[BD_PLUGIN_MDRAID].spec.so_name);
+    if (!plugins[BD_PLUGIN_S390].handle && plugins[BD_PLUGIN_S390].spec.so_name)
+        plugins[BD_PLUGIN_S390].handle = load_s390_from_plugin(plugins[BD_PLUGIN_S390].spec.so_name);
 
     for (i=0; (i < BD_PLUGIN_UNDEF) && requested_loaded; i++)
         if (plugins[i].spec.so_name)
diff --git a/src/lib/plugin_apis/s390.api b/src/lib/plugin_apis/s390.api
new file mode 100644
index 0000000..af35501
--- /dev/null
+++ b/src/lib/plugin_apis/s390.api
@@ -0,0 +1,100 @@
+#include <ctype.h>
+#include <asm/dasd.h>
+#include <glib.h>
+#include <linux/fs.h>
+#include <s390utils/vtoc.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+typedef enum {
+    BD_S390_ERROR_DEVICE,
+    BD_S390_ERROR_FORMAT_FAILED,
+    BD_S390_ERROR_PARSE,
+} BDS390Error;
+
+
+/**
+ * bd_s390_dasd_format:
+ * @dasd: dasd to format
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether dasdfmt was successful or not
+ */
+gboolean bd_s390_dasd_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_needs_format:
+ * @dasd: dasd to check, whether it needs dasdfmt run on it
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd needs dasdfmt run against it
+ */
+gboolean bd_s390_dasd_needs_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_sanitize_input:
+ * @dev a DASD or zFCP device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized dasd or zfcp device number
+ */
+gchar* bd_s390_sanitize_dev_input (gchar *dev, GError **error);
+
+/**
+ * bd_s390_dasd_online:
+ * @dasd: dasd to switch online
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd was successfully switched online
+ */
+gboolean bd_s390_dasd_online (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_is_cdl:
+ * @dasd: dasd to check, whether it is CDL formatted
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd is CDL formatted
+ */
+gboolean bd_s390_dasd_is_ldl (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_wwpn_input:
+ * @dev a zFCP WWPN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP WWPN
+ */
+gchar* bd_s390_zfcp_sanitize_wwpn_input (gchar *wwpn, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_lun_input:
+ * @dev a zFCP LUN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP LUN
+ */
+gchar* bd_s390_zfcp_sanitize_lun_input (gchar *lun, GError **error);
+
+/**
+ * bd_s390_zfcp_online:
+ * @devno a zFCP LUN device number
+ * @wwpn a zFCP WWPN
+ * @lun a zFCP LUN
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a zfcp device was successfully switched online
+ */
+gboolean* bd_s390_zfcp_online (gchar *devno, gchar *wwpn, gchar *lun, GError **error);
+
+/**
+ * bd_s390_zfcp_offline:
+ * @devno a zFCP LUN device number
+ * @wwpn a zFCP WWPN
+ * @lun a zFCP LUN
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a zfcp device was successfully switched offline
+ */
+gboolean* bd_s390_zfcp_offline (gchar *devno, gchar *wwpn, gchar *lun, GError **error);
diff --git a/src/lib/plugins.h b/src/lib/plugins.h
index 0320b9b..a6f60fc 100644
--- a/src/lib/plugins.h
+++ b/src/lib/plugins.h
@@ -13,6 +13,7 @@ typedef enum {
     BD_PLUGIN_MPATH,
     BD_PLUGIN_DM,
     BD_PLUGIN_MDRAID,
+    BD_PLUGIN_S390,
     BD_PLUGIN_UNDEF
 } BDPlugin;
 
diff --git a/src/plugins/s390.c b/src/plugins/s390.c
new file mode 100644
index 0000000..e007b5b
--- /dev/null
+++ b/src/plugins/s390.c
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2015  Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Samantha N. Bueno <sbueno@redhat.com>
+ */
+
+#include <ctype.h>
+#include <glib.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <utils.h>
+#include <asm/dasd.h>
+#include <s390utils/vtoc.h>
+
+#include "s390.h"
+
+
+/**
+ *  * SECTION: s390
+ *   * @short_description: plugin for operations with s390
+ *    * @title: s390
+ *     * @include: s390.h
+ *      *
+ *       * A plugin for operations with s390 devices.
+ *         */
+
+/**
+ *  * bd_lvm_error_quark: (skip)
+ *   */
+GQuark bd_s390_error_quark (void) {
+    return g_quark_from_static_string ("g-bd-s390-error-quark");
+}
+
+
+/**
+ * check: (skip)
+ */
+gboolean check() {
+    GError *error = NULL;
+    /* dasdfmt doesn't return version info */
+    gboolean ret = bd_utils_check_util_version ("dasdfmt", NULL, NULL, NULL, &error);
+    if (!ret && error) {
+        g_warning("Cannot load the s390 plugin: %s" , error->message);
+        g_clear_error (&error);
+    }
+    return ret;
+}
+
+/**
+ * bd_s390_dasd_format:
+ * @dasd: dasd to format
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether dasdfmt was successful or not
+ */
+gboolean bd_s390_dasd_format (gchar *dasd, GError **error) {
+    gint rc;
+    gchar cmd[46]; /* should accomodate dasds from dasda - dasdzzz */
+
+    g_snprintf(cmd, sizeof(cmd), "/sbin/dasdfmt -y -d cdl -b 4096 /dev/%s", dasd);
+    rc = system(cmd);
+
+    /* rc is 0 on successful dasdfmt */
+    if (rc) {
+        gchar errmsg[20];
+        g_snprintf (errmsg, sizeof(errmsg), "dasdfmt failed: %d", rc);
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_FORMAT_FAILED, errmsg);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/**
+ * bd_s390_dasd_needs_format:
+ * @dasd: dasd to check, given as device id
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd needs dasdfmt run against it
+ */
+gboolean bd_s390_dasd_needs_format (gchar *dasd, GError **error) {
+    gchar status[11], path[47];
+    gint fd, rc;
+
+    g_snprintf (path, sizeof(path), "/sys/bus/ccw/drivers/dasd-eckd/%s/status", dasd);
+    fd = open (path, O_RDONLY);
+
+    if (!fd)
+        return FALSE;
+
+    rc = read (fd, status, 11);
+    if (rc == -1) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Error checking status of device %s", dasd);
+        return FALSE;
+    }
+
+    if (strncmp (status, "unformatted", 11) == 0) {
+        g_warning ("%s (/dev/%s) status is %s, needs dasdfmt", dasd, dasd, status);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+/**
+ * bd_s390_dasd_online:
+ * @dasd: dasd to switch online, given as device id
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd was successfully switched online
+ */
+gboolean bd_s390_dasd_online (gchar *dasd, GError **error) {
+    gint rc, wrc, online;
+    gchar cmd[35], path[48];
+    FILE *fd;
+
+    g_snprintf(path, sizeof(path), "/sys/bus/ccw/drivers/dasd-eckd/%s/online", dasd);
+    fd = fopen(path, "r+");
+    if (!fd) {
+        /* DASD might be in device ignore list; try to rm it */
+        g_snprintf(cmd, sizeof(cmd), "/usr/sbin/dasd_cio_free -d %s", dasd);
+        rc = system(cmd);
+
+        /* rc is 0 on successful dasd_cio_free */
+        if (rc) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not free device %s.", dasd);
+            return FALSE;
+        }
+        /* fd is NULL at this point, so try to open it */
+        fd = fopen(path, "r+");
+        if (!fd) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not open device %s even after removing it from the device ignore list.", dasd);
+            return FALSE;
+        }
+    }
+
+    /* check whether our DASD is online; if not, set it */
+    online = fgetc(fd);
+    if (online == EOF) {
+        /* there was some error checking the 'online' status */
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Error checking if device %s is online", dasd);
+        return FALSE;
+    }
+    if(((char)online) == '1') {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "DASD device %s is already online.", dasd);
+        return FALSE;
+    }
+    else {
+        wrc = fputc('1', fd);
+    }
+
+    fclose(fd);
+
+    if (wrc == EOF) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not set DASD device %s online", dasd);
+        return FALSE;
+    }
+
+    /* verify whether we successfully switched the DASD online */
+    if (((char)wrc) == '1')
+        return TRUE;
+    else {
+        return FALSE;
+    }
+}
+
+/**
+ * bd_s390_dasd_read_vlabel:
+ * @dasd_info:
+ * @fd: a file descriptor for the open DASD device
+ * @blksize:
+ * @vlabel:
+ * @error: (out): place to store error (if any)
+ *
+ * Returns:
+ */
+static int bd_s390_dasd_read_vlabel(dasd_information2_t *dasd_info, int fd, int blksize, volume_label_t *vlabel) {
+    gint rc;
+    unsigned long vlabel_start;
+    vlabel_start = dasd_info->label_block * blksize;
+
+    memset(vlabel, 0, sizeof(volume_label_t));
+
+    if (lseek(fd, vlabel_start, SEEK_SET) < 0)
+        return 2;
+
+    rc = read(fd, vlabel, sizeof(volume_label_t));
+    if (rc != sizeof(volume_label_t))
+        return 1;
+
+    return 0;
+}
+
+/**
+ * bd_s390_dasd_is_cdl:
+ * @dasd: dasd to check, whether it is CDL formatted
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd is CDL formatted
+ */
+gboolean bd_s390_dasd_is_ldl (gchar *dasd) {
+    gchar devname[16];
+    gint f, ret, blksize;
+    dasd_information2_t dasd_info;
+    volume_label_t vlabel;
+
+    memset(&dasd_info, 0, sizeof(dasd_info));
+    strcpy(devname, "/dev/");
+    strcat(devname, dasd);
+
+    if ((f = open(devname, O_RDONLY)) == -1)
+        return FALSE;
+    if (ioctl(f, BLKSSZGET, &blksize) != 0) {
+        close(f);
+        return FALSE;
+    }
+
+    if (ioctl(f, BIODASDINFO2, &dasd_info) != 0) {
+        close(f);
+        return FALSE;
+    }
+
+    ret = bd_s390_dasd_read_vlabel(&dasd_info, f, blksize, &vlabel);
+    close(f);
+    if (ret == 2)
+        return FALSE;
+    if (dasd_info.format == DASD_FORMAT_CDL) /* VOL1, CDL */
+        return FALSE;
+    else {
+        return TRUE;
+    }
+}
+
+/**
+ * bd_s390_dasd_sanitize_input:
+ * @dev a DASD or zFCP device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized dasd or zfcp device number
+ */
+gchar* bd_s390_sanitize_dev_input (gchar *dev, GError **error) {
+
+    gchar *tok = NULL;
+    gchar *tmptok = NULL;
+    gchar *prepend = NULL;
+    gchar *ret = NULL;
+    gint i = 0;
+
+    /* first make sure we're not being played */
+    if ((dev == NULL) || (!*dev))
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a device number or the number is invalid");
+
+    /* convert everything to lowercase */
+    while (dev[i]) {
+        putchar(tolower(dev[i]));
+        i++;
+    }
+
+    /* we only care about the last piece of the device number, since
+     * that's the only part which will need formatting */
+    tok = g_strrstr(dev, ".");
+    tmptok = &tok[1]; /* want to ignore the delimiter char */
+
+    /* calculate if/how much to pad tok with */
+    prepend = g_strnfill((4 - strlen(tok)), '0');
+
+    /* combine it all together */
+    if (prepend == NULL) {
+        ret = g_strdup_printf("0.0.%s", tmptok);
+    }
+    else {
+        ret = g_strdup_printf("0.0.%s%s", prepend, tmptok);
+    }
+    return ret;
+}
+
+/**
+ * bd_s390_zfcp_sanitize_wwpn_input:
+ * @dev a zFCP WWPN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP WWPN
+ */
+gchar* bd_s390_zfcp_sanitize_wwpn_input (gchar *wwpn, GError **error) {
+    gint i = 0;
+    gchar *fullwwpn;
+
+    /* first make sure we're not being played */
+    if ((wwpn == NULL) || (!*wwpn)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a WWPN or the WWPN is invalid");
+    }
+
+    /* convert everything to lowercase */
+    while (wwpn[i]) {
+        putchar(tolower(wwpn[i]));
+        i++;
+    }
+
+    if (strncmp(wwpn, "0x", 2) == 0) {
+        /* user entered a properly formatted wwpn */
+        fullwwpn = g_strdup_printf("%s", wwpn);
+    }
+    else {
+        fullwwpn = g_strdup_printf("0x%s", wwpn);
+    }
+    return fullwwpn;
+}
+
+/**
+ * bd_s390_zfcp_sanitize_lun_input:
+ * @dev a zFCP LUN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP LUN
+ */
+gchar* bd_s390_zfcp_sanitize_lun_input (gchar *lun, GError **error) {
+    gint i = 0;
+    gchar *tmplun = NULL;
+    gchar *fulllun = NULL;
+    gchar *prepend = NULL;
+    gchar *append = NULL;
+
+    /* first make sure we're not being played */
+    if ((lun == NULL) || (!*lun)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a LUN or the LUN is invalid");
+    }
+
+    /* convert everything to lowercase */
+    while (lun[i]) {
+        putchar (tolower(lun[i]));
+        i++;
+    }
+
+    if ((g_str_has_prefix(lun, "0x")) && (strlen(lun) == 18)) {
+        /* user entered a properly formatted lun */
+        fulllun = g_strdup_printf ("%s", lun);
+    }
+    else {
+        /* we need to mangle the input to make it proper. ugh. */
+        if (g_str_has_prefix(lun, "0x")) {
+            /* this may seem odd, but it makes the math easier a ways down */
+            tmplun = &lun[2];
+        }
+        else {
+            tmplun = lun;
+        }
+
+        if (strlen(tmplun) < 4) {
+            /* check if/how many zeros we pad to the left */
+            prepend = g_strnfill((4 - strlen(tmplun)), '0');
+            /* check if/how many zeros we pad to the right */
+            append = g_strnfill((16 - (strlen(tmplun) + strlen(prepend))), '0');
+        }
+        else {
+            /* didn't need to pad anything on the left; so just check if/how
+             * many zeros we pad to the right */
+            append = g_strnfill((16 - (strlen(tmplun))), '0');
+        }
+
+        if (prepend == NULL) {
+            fulllun = g_strdup_printf ("0x%s%s", tmplun, append);
+        }
+        else {
+            fulllun = g_strdup_printf ("0x%s%s%s", prepend, tmplun, append);
+        }
+    }
+    return fulllun;
+}
+
+/**
+ * bd_s390_zfcp_online:
+ * @devno a zFCP LUN device number
+ * @wwpn a zFCP WWPN
+ * @lun a zFCP LUN
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: wether a zfcp device was successfully switched online
+ */
+gboolean bd_s390_zfcp_online (gchar *devno, gchar *wwpn, gchar *lun, GError **error) {
+    gchar *online = NULL;
+    gchar *portadd = NULL;
+    gchar *portdir = NULL;
+    gchar *unitadd = NULL;
+    gchar *unitdir = NULL;
+    gchar *failed = NULL;
+    gchar *zfcpsysfs = "/sys/bus/ccw/drivers/zfcp";
+    gchar **fail = NULL;
+    gchar **isonline = 0;
+    gint rc = NULL;
+    GIOChannel *devfd = NULL;
+    GIOChannel *portdirfd = NULL;
+    GIOChannel *portaddfd = NULL;
+    GIOChannel *unitaddfd = NULL;
+    GIOChannel *unitdirfd = NULL;
+    GIOChannel *failfd = NULL;
+    GIOStatus status = G_IO_STATUS_ERROR;
+
+    /* first some basic sanity checks on our input */
+    if ((devno == NULL) || (!*devno)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a zFCP device number or the device number is invalid");
+        return FALSE;
+    }
+    if ((wwpn == NULL) || (!*wwpn)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a zFCP WWPN or the WWPN is invalid");
+        return FALSE;
+    }
+    if ((lun == NULL) || (!*lun)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a zFCP LUN or the LUN is invalid");
+        return FALSE;
+    }
+
+    /* now properly set some of our vars we'll need */
+    online = g_strdup_printf ("%s/%s/online", zfcpsysfs, devno);
+    portadd = g_strdup_printf ("%s/%s/port_add", zfcpsysfs, devno);
+    portdir = g_strdup_printf ("%s/%s/%s", zfcpsysfs, devno, wwpn);
+    unitadd = g_strdup_printf ("%s/unit_add", portdir);
+    unitdir = g_strdup_printf ("%s/%s", portdir, lun);
+    failed = g_strdup_printf ("%s/failed", unitdir);
+
+    /* begin with setting of 'online' file */
+    devfd = g_io_channel_new_file (online, "r+", error);
+    if (!devfd) {
+        /* device might be in device ignore list; try to rm it */
+        gchar *args[4] = {"/usr/sbin/zfcp_cio_free", "-r", devno, NULL};
+        rc = bd_utils_exec_and_report_error(args, error);
+
+        /* rc is 0 on successful zfcp_cio_free */
+        if (rc) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not free device %s.", devno);
+            return FALSE;
+        }
+        /* devfd is NULL at this point, so try to open it */
+        devfd = g_io_channel_new_file (online, "r+", error);
+        if (!devfd) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not open device %s even after removing it from the device ignore list.", devno);
+            return FALSE;
+        }
+    }
+
+    /* check whether our device is online; if not, set it */
+    status = g_io_channel_read_line(devfd, isonline, NULL, NULL, error);
+    if (status != G_IO_STATUS_NORMAL) {
+        /* there was some error checking the 'online' status */
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Error checking if device %s is online", devno);
+        return FALSE;
+    }
+    if (*isonline[0] == '1') {
+        /* device is already online */
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "zFCP device %s is already online.", devno);
+        return FALSE;
+    }
+    else {
+        /* device is not online, so we must set it */
+        status = g_io_channel_write_chars (devfd, "1", 1, NULL, error);
+        if (status != G_IO_STATUS_NORMAL) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not set zFCP device %s online.", devno);
+            return FALSE;
+        }
+    }
+
+    status = g_io_channel_shutdown(devfd, TRUE, error);
+    /* end with setting 'online' file */
+
+    /* begin with setting 'portdir' and 'portadd' files */
+    portdirfd = g_io_channel_new_file (portdir, "r+", error);
+    if (!portdirfd) {
+        portaddfd = g_io_channel_new_file (portadd, "r+", error);
+        if (portaddfd) {
+            /* older zfcp sysfs interface */
+            status = g_io_channel_write_chars (portaddfd, wwpn, (strlen(wwpn)), NULL, error);
+            if (status != G_IO_STATUS_NORMAL) {
+                g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not add %s to zFCP device %s.", wwpn, devno);
+                return FALSE;
+            }
+        }
+        else {
+            /* newer zfcp sysfs interface with auto port scan */
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "WWPN %s not found at zFCP device %s", wwpn, devno);
+            return FALSE;
+        }
+    }
+    else {
+        /* older zfcp sysfs interface */
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "WWPN %s at zFCP device %s is already there", wwpn, devno);
+        return FALSE;
+    }
+
+    status = g_io_channel_shutdown(portdirfd, TRUE, error);
+    status = g_io_channel_shutdown(portaddfd, TRUE, error);
+    /* end with setting 'portdir' and 'portadd' files */
+
+    /* begin with setting 'unitdir' and 'unitadd' files */
+    unitdirfd = g_io_channel_new_file (unitdir, "r+", error);
+    if (!unitdirfd) {
+        unitaddfd = g_io_channel_new_file (unitadd, "r+", error);
+        if (!unitaddfd) {
+            g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not add LUN %s to WWPN %s on zFCP device %s.", lun, wwpn, devno);               
+            return FALSE;
+        }
+        else {
+            status = g_io_channel_write_chars (unitaddfd, lun, (strlen(lun)), NULL, error);            
+            if (status != G_IO_STATUS_NORMAL) {
+                g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not add LUN %s to WWPN %s on zFCP device %s.", lun, wwpn, devno);               
+                return FALSE;
+            }
+        }
+    }
+    else {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "LUN %s at WWPN %s on zFCP device %s is already configured", lun, wwpn, devno);
+        return FALSE;
+    }
+
+    status = g_io_channel_shutdown(unitdirfd, TRUE, error);
+    status = g_io_channel_shutdown(unitaddfd, TRUE, error);
+    /* end with setting 'unitdir' and 'unitadd' files */
+
+    /* begin assessing whether there were any failures in preceding actions */
+    failfd = g_io_channel_new_file (failed, "r", error);
+    status = g_io_channel_read_line(devfd, fail, NULL, NULL, error);
+    if (status != G_IO_STATUS_NORMAL) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not read failed attribute of LUN %s at WWPN %s on zFCP device %s", lun, wwpn, devno);
+        return FALSE;
+    }
+    if ((fail != NULL) && (*fail[0] != '0')) {
+        //rc = bd_s390_zfcp_offline(devno, wwpn, lun, error);
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Failed LUN %s at WWPN %s on zFCP device %s removed again.", lun, wwpn, devno);
+        return FALSE;
+    }    
+    status = g_io_channel_shutdown(failfd, TRUE, error);
+    /* end with failure assessment */
+
+    /* if you made it this far, you deserve a prize */
+    return TRUE;
+}
+
+
+/**
+ * bd_s390_zfcp_offline:
+ * @zfcp a zFCP LUN device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: wether a zfcp device was successfully switched offline
+ *
+ */
+gboolean bd_s390_zfcp_offline (gchar *devno, gchar *wwpn, gchar *lun, GError **error) {
+/*
+    gchar *offline = NULL;
+    gchar *portdir = NULL;
+    gchar *portadd = NULL;
+    gchar *portremove = NULL;
+    gchar *unitremove = NULL;
+    gchar *devdir = NULL;
+    gchar *zfcpsysfs = "/sys/bus/ccw/drivers/zfcp";
+    gint rc = NULL;
+    GIOChannel *devfd = NULL;
+    GIOChannel *portdirfd = NULL;
+    GIOChannel *portaddfd = NULL;
+    GIOChannel *portremovefd = NULL;
+    GIOChannel *unitremovefd = NULL;
+    GIOChannel *devdirfd = NULL;
+    GIOStatus status = G_IO_STATUS_ERROR;
+
+    /* first some basic sanity checks on our input *
+    if ((devno == NULL) || (!*devno)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a zFCP device number or the device number is invalid");
+        return FALSE;
+    }
+    if ((wwpn == NULL) || (!*wwpn)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a zFCP WWPN or the WWPN is invalid");
+        return FALSE;
+    }
+    if ((lun == NULL) || (!*lun)) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "You have not specified a zFCP LUN or the LUN is invalid");
+        return FALSE;
+    }
+
+    /* now properly set some of our vars we'll need *
+    online = g_strdup_printf ("%s/%s/online", zfcpsysfs, devno);
+    portdir = g_strdup_printf ("%s/%s/%s", zfcpsysfs, devno, wwpn);
+    portadd = g_strdup_printf ("%s/%s/port_add", zfcpsysfs, devno);
+    portremove = g_strdup_printf ("%s/%s/port_remove", zfcpsysfs, devno);
+    unitremove = g_strdup_printf ("%s/%s/%s/unit_add", zfcpsysfs, devno, wwpn);
+    devdir = g_strdup_printf ("%s/%s", zfcpsysfs, devno);
+
+    // bd_s390_zfcp_scsi_offline();
+
+    /* begin with setting of 'online' file *
+    unitremovefd = g_io_channel_new_file (unitremove, "r+", error);
+    if (!unitremovefd) {
+        g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_DEVICE, "Could not remove LUN %s at WWPN %s on zFCP device %s.", lun, wwpn, devno);
+        return FALSE;
+    }
+    else {
+                
+*/
+    return TRUE;
+}
+
diff --git a/src/plugins/s390.h b/src/plugins/s390.h
new file mode 100644
index 0000000..6373b39
--- /dev/null
+++ b/src/plugins/s390.h
@@ -0,0 +1,101 @@
+#include <ctype.h>
+#include <glib.h>
+#include <linux/fs.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <asm/dasd.h>
+#include <s390utils/vtoc.h>
+
+GQuark bd_s390_error_quark (void);
+#define BD_S390_ERROR bd_s390_error_quark ()
+typedef enum {
+    BD_S390_ERROR_DEVICE,
+    BD_S390_ERROR_FORMAT_FAILED,
+    BD_S390_ERROR_DASDFMT,
+} BDS390Error;
+
+/**
+ * bd_s390_dasd_format:
+ * @dasd: dasd to format
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether dasdfmt was successful or not
+ */
+gboolean bd_s390_dasd_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_needs_format:
+ * @dasd: dasd to check, whether it needs dasdfmt run on it
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd needs dasdfmt run against it
+ */
+gboolean bd_s390_dasd_needs_format (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_sanitize_input:
+ * @dev a DASD or zFCP device number
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized dasd or zfcp device number
+ */
+gchar* bd_s390_sanitize_dev_input (gchar *dev, GError **error);
+
+/**
+ * bd_s390_dasd_online:
+ * @dasd: dasd to switch online
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd was successfully switched online
+ */
+gboolean bd_s390_dasd_online (gchar *dasd, GError **error);
+
+/**
+ * bd_s390_dasd_is_cdl:
+ * @dasd: dasd to check, whether it is CDL formatted
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a dasd is CDL formatted
+ */
+gboolean bd_s390_dasd_is_ldl (gchar *dasd);
+
+/**
+ * bd_s390_zfcp_sanitize_wwpn_input:
+ * @dev a zFCP WWPN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP WWPN
+ */
+gchar* bd_s390_zfcp_sanitize_wwpn_input (gchar *wwpn, GError **error);
+
+/**
+ * bd_s390_zfcp_sanitize_lun_input:
+ * @dev a zFCP LUN identifier
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: a synthesized zFCP LUN
+ */
+gchar* bd_s390_zfcp_sanitize_lun_input (gchar *lun, GError **error);
+
+/**
+ * bd_s390_zfcp_online:
+ * @devno a zFCP LUN device number
+ * @wwpn a zFCP WWPN
+ * @lun a zFCP LUN
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a zfcp device was successfully switched online
+ */
+gboolean bd_s390_zfcp_online (gchar *devno, gchar *wwpn, gchar *lun, GError **error);
+
+/**
+ * bd_s390_zfcp_offline:
+ * @devno a zFCP LUN device number
+ * @wwpn a zFCP WWPN
+ * @lun a zFCP LUN
+ * @error: (out): place to store error (if any)
+ *
+ * Returns: whether a zfcp device was successfully switched offline
+ */
+gboolean bd_s390_zfcp_offline (gchar *devno, gchar *wwpn, gchar *lun, GError **error);
diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
index 5e47f03..b581355 100644
--- a/src/python/gi/overrides/BlockDev.py
+++ b/src/python/gi/overrides/BlockDev.py
@@ -45,6 +45,7 @@ bd_plugins = { "lvm": BlockDev.Plugin.LVM,
                "swap": BlockDev.Plugin.SWAP,
                "mdraid": BlockDev.Plugin.MDRAID,
                "mpath": BlockDev.Plugin.MPATH,
+               "s390": BlockDev.Plugin.S390,
 }
 
 _init = BlockDev.init
@@ -258,6 +259,60 @@ def swap_swapon(device, priority=-1):
 __all__.append("swap_swapon")
 
 
+_s390_dasd_format = BlockDev.s390_dasd_format
+@override(BlockDev.s390_dasd_format)
+def s390_dasd_format(dasd):
+    return _s390_dasd_format(dasd)
+__all__.append("s390_dasd_format")
+
+_s390_dasd_needs_format = BlockDev.s390_dasd_needs_format
+@override(BlockDev.s390_dasd_needs_format)
+def s390_dasd_needs_format(dasd):
+    return _s390_dasd_needs_format(dasd)
+__all__.append("s390_dasd_needs_format")
+
+_s390_dasd_online = BlockDev.s390_dasd_online
+@override(BlockDev.s390_dasd_online)
+def s390_dasd_online(dasd):
+    return _s390_dasd_online(dasd)
+__all__.append("s390_dasd_online")
+
+_s390_dasd_is_ldl = BlockDev.s390_dasd_is_ldl
+@override(BlockDev.s390_dasd_is_ldl)
+def s390_dasd_is_ldl(dasd):
+    return _s390_dasd_is_ldl(dasd)
+__all__.append("s390_dasd_is_ldl")
+
+_s390_sanitize_dev_input = BlockDev.s390_sanitize_dev_input
+@override(BlockDev.s390_sanitize_dev_input)
+def s390_sanitize_dev_input(dev):
+    return _s390_sanitize_dev_input(dev)
+__all__.append("s390_sanitize_dev_input")
+
+_s390_zfcp_sanitize_wwpn_input = BlockDev.s390_zfcp_sanitize_wwpn_input
+@override(BlockDev.s390_zfcp_sanitize_wwpn_input)
+def s390_zfcp_sanitize_wwpn_input(wwpn):
+    return _s390_zfcp_sanitize_wwpn_input(wwpn)
+__all__.append("s390_zfcp_sanitize_wwpn_input")
+
+_s390_zfcp_sanitize_lun_input = BlockDev.s390_zfcp_sanitize_lun_input
+@override(BlockDev.s390_zfcp_sanitize_lun_input)
+def s390_zfcp_sanitize_lun_input(lun):
+    return _s390_zfcp_sanitize_lun_input(lun)
+__all__.append("s390_zfcp_sanitize_lun_input")
+
+_s390_zfcp_online = BlockDev.s390_zfcp_online
+@override(BlockDev.s390_zfcp_online)
+def s390_zfcp_online(devno, wwpn, lun):
+    return _s390_zfcp_online(devno, wwpn, lun)
+__all__.append("s390_zfcp_online")
+
+_s390_zfcp_offline = BlockDev.s390_zfcp_offline
+@override(BlockDev.s390_zfcp_offline)
+def s390_zfcp_offline(devno, wwpn, lun):
+    return _s390_zfcp_offline(devno, wwpn, lun)
+__all__.append("s390_zfcp_offline")
+
 ## defined in this overrides only!
 def plugin_specs_from_names(plugin_names):
     ret = []
@@ -424,6 +479,10 @@ class SwapError(BlockDevError):
     pass
 __all__.append("SwapError")
 
+class S390Error(BlockDevError):
+    pass
+__all__.append("S390Error")
+
 class UtilsError(BlockDevError):
     pass
 __all__.append("UtilsError")
@@ -458,5 +517,8 @@ __all__.append("mpath")
 swap = ErrorProxy("swap", BlockDev, [(GLib.Error, SwapError)], [not_implemented_rule])
 __all__.append("swap")
 
+s390 = ErrorProxy("s390", BlockDev, [(GLib.Error, S390Error)], [not_implemented_rule])
+__all__.append("s390")
+
 utils = ErrorProxy("utils", BlockDev, [(GLib.Error, UtilsError)])
 __all__.append("utils")
diff --git a/tests/s390_test.py b/tests/s390_test.py
new file mode 100644
index 0000000..7aeba17
--- /dev/null
+++ b/tests/s390_test.py
@@ -0,0 +1,71 @@
+import unittest
+import os
+import overrides_hack
+
+from utils import create_sparse_tempfile, fake_utils, fake_path
+from gi.repository import BlockDev, GLib
+if not BlockDev.is_initialized():
+    BlockDev.init(None, None)
+
+class S390TestCase(unittest.TestCase):
+    def dasd_testSanitize(self):
+        with self.assertRaises(ValueError):
+            BlockDev.s390.dasd_sanitize_input("")
+
+        # without a ., the whole value is assumed to be the device number
+        # and a bus number is prepended
+        # this test is the same for validating zfcp and dasd device number
+        # input
+        dev = "1234abc"
+        self.assertEqual(BlockDev.s390.sanitize_input(dev), '0.0.' + dev)
+
+        # whatever is on the left side of the rightmost period is assumed to
+        # be the bus number
+        dev = "zed.1234abq"
+        self.assertEqual(BlockDev.s390.dasd_sanitize_input(dev), dev)
+
+        # the device number is padded on the left with 0s up to 4 digits
+        dev = "zed.abc"
+        self.assertEqual(BlockDev.s390.dasd_sanitize_input(dev), "zed.0abc")
+        dev = "abc"
+        self.assertEqual(BlockDev.s390.dasd_sanitize_input(dev), "0.0.0abc")
+        dev = ".abc"
+        self.assertEqual(BlockDev.s390.dasd_sanitize_input(dev), "0.0.0abc")
+
+        # a complete number is unchanged
+        dev = "0.0.abcd"
+        self.assertEqual(BlockDev.s390.dasd_sanitize_input(dev), dev)
+
+    def zfcp_testSanitize(self):
+        with self.assertRaises(ValueError):
+            BlockDev.s390.zfcp_sanitize_wwpn_input("")
+            BlockDev.s390.zfcp_sanitize_lun_input("")
+
+        # == wwpn tests
+        # missing "0x" from beginning of wwpn; this should be added by fx
+        wwpn = "01234567abcdefgh"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_wwpn_input(wwpn), "0x01234567abcdefgh")
+        # this should be fine as-is
+        wwpn = "0x01234567abcdefgh"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_wwpn_input(wwpn), wwpn)
+
+        # == lun tests
+        # user does not prepend lun with "0x"; this should get added
+        lun = "0x01234567abcdefgh"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_lun_input(lun), "0x01234567abcdefgh")
+        # a user enters a lun that is between 0 and 16 chars long (non-inclusive); 0 padding should be added to expand to 16
+        lun = "0x123"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_lun_input(lun), "0x01234567abcdefgh")
+        lun = "0x12345"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_lun_input(lun), "0x01234567abcdefgh")
+        lun = "0x123456"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_lun_input(lun), "0x01234567abcdefgh")
+        # this should be fine as-is
+        lun = "0x1234567800000000"
+        self.assertEqual(BlockDev.s390.zfcp_sanitize_lun_input(lun), lun)
+
+class S390UnloadTest(unittest.TestCase):
+    def tearDown(self):
+        # make sure the library is initialized with all plugins loaded for other
+        # tests
+        self.assertTrue(BlockDev.reinit(None, True, None))
-- 
2.4.0

