* Do not commit. This is just a demo.

 * With support of:
    * systems()
    * pools()
    * volumes()
    * disks() with Disk.STATUS_FREE
    * volume_raid_info()

 * A lot of TODOs in code. I am seeking a hpsa server with a lot of disks.[1]

[1] If anyone have a server with faulty disk or spare disk,
    please email me the output of 'hpssacli show config detail'.

Patches is based on:
    [PATCH V2 0/7] Add new disk status: STATUS_FREE

Signed-off-by: Gris Ge <f...@redhat.com>
---
 config/pluginconf.d/hpsa.conf |   1 +
 configure.ac                  |  11 +
 plugin/hpsa/Makefile.am       |   8 +
 plugin/hpsa/__init__.py       |   1 +
 plugin/hpsa/hpsa.py           | 523 ++++++++++++++++++++++++++++++++++++++++++
 plugin/hpsa/hpsa_lsmplugin    |  37 +++
 plugin/hpsa/utils.py          |  55 +++++
 7 files changed, 636 insertions(+)
 create mode 100644 config/pluginconf.d/hpsa.conf
 create mode 100644 plugin/hpsa/Makefile.am
 create mode 100644 plugin/hpsa/__init__.py
 create mode 100644 plugin/hpsa/hpsa.py
 create mode 100755 plugin/hpsa/hpsa_lsmplugin
 create mode 100644 plugin/hpsa/utils.py

diff --git a/config/pluginconf.d/hpsa.conf b/config/pluginconf.d/hpsa.conf
new file mode 100644
index 0000000..35b52d4
--- /dev/null
+++ b/config/pluginconf.d/hpsa.conf
@@ -0,0 +1 @@
+require-root-privilege = true;
diff --git a/configure.ac b/configure.ac
index 74e6811..56a4fba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -190,6 +190,16 @@ AC_ARG_WITH([megaraid],
     [],
     [with_megaraid=yes])
 
+dnl ==========================================================================
+dnl Add option '--without-hpsa' to exclude hpsa plugin.
+dnl ==========================================================================
+
+AC_ARG_WITH([hpsa],
+    [AS_HELP_STRING([--without-hpsa],
+        [Do not build the HP SmartArray plugin])],
+    [],
+    [with_hpsa=yes])
+
 AM_CONDITIONAL([WITH_MEGARAID], [test "x$with_megaraid" = "xyes"])
 
 dnl ==========================================================================
@@ -221,6 +231,7 @@ AC_OUTPUT(libstoragemgmt.pc \
           plugin/Makefile \
           plugin/simc/Makefile \
           plugin/megaraid/Makefile \
+          plugin/hpsa/Makefile \
           daemon/Makefile \
           config/Makefile \
           doc/Makefile \
diff --git a/plugin/hpsa/Makefile.am b/plugin/hpsa/Makefile.am
new file mode 100644
index 0000000..3076cbc
--- /dev/null
+++ b/plugin/hpsa/Makefile.am
@@ -0,0 +1,8 @@
+if WITH_HPSA
+plugindir = $(pythondir)/lsm/plugin
+hpsadir = $(plugindir)/hpsa
+
+hpsa_PYTHON = __init__.py hpsa.py utils.py
+
+dist_bin_SCRIPTS= hpsa_lsmplugin
+endif
diff --git a/plugin/hpsa/__init__.py b/plugin/hpsa/__init__.py
new file mode 100644
index 0000000..61332b4
--- /dev/null
+++ b/plugin/hpsa/__init__.py
@@ -0,0 +1 @@
+from lsm.plugin.hpsa.hpsa import SmartArray
diff --git a/plugin/hpsa/hpsa.py b/plugin/hpsa/hpsa.py
new file mode 100644
index 0000000..cfb176c
--- /dev/null
+++ b/plugin/hpsa/hpsa.py
@@ -0,0 +1,523 @@
+# Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <f...@redhat.com>
+
+import os
+from string import strip
+import re
+import errno
+
+from lsm import (uri_parse, search_property, size_human_2_size_bytes,
+                 Capabilities, LsmError, ErrorNumber, System, Client,
+                 Disk, VERSION, search_property, IPlugin, Pool, Volume)
+
+from lsm.plugin.hpsa.utils import cmd_exec, ExecError, file_read
+
+# Naming scheme
+
+
+def _handle_errors(method):
+    def _wrapper(*args, **kwargs):
+        try:
+            return method(*args, **kwargs)
+        except LsmError:
+            raise
+        except KeyError as key_error:
+            raise LsmError(
+                ErrorNumber.PLUGIN_BUG,
+                "Expected key missing from SmartArray hpssacli output:%s" %
+                key_error)
+        except ExecError as exec_error:
+            raise LsmError(ErrorNumber.PLUGIN_BUG, str(exec_error))
+        except Exception as common_error:
+            raise LsmError(
+                ErrorNumber.PLUGIN_BUG,
+                "Got unexpected error %s" % common_error)
+
+    return _wrapper
+
+
+def _disk_type_of(hp_disk):
+    disk_interface = hp_disk['Interface Type']
+    if disk_interface == 'SATA':
+        return Disk.TYPE_SATA
+    elif disk_interface == 'Solid State SATA':
+        return Disk.TYPE_SSD
+    elif disk_interface == 'SAS':
+        return Disk.TYPE_SAS
+
+    return Disk.TYPE_UNKNOWN
+
+
+def _disk_status_of(hp_disk, flag_free):
+    # TODO(Gris Ge): Need more document or test for non-OK disks.
+    if hp_disk['Status'] == 'OK':
+        disk_status = Disk.STATUS_OK
+    else:
+        disk_status = Disk.STATUS_OTHER
+
+    if flag_free:
+        disk_status |= Disk.STATUS_FREE
+
+    return disk_status
+
+
+def _hp_size_to_lsm(hp_size):
+    """
+    HP Using 'TB, GB, MB, KB' and etc, for LSM, they are 'TiB' and etc.
+    Return int of block bytes
+    """
+    re_regex = re.compile("^([0-9\.]+) +([EPTGMK])B$")
+    re_match = re_regex.match(hp_size)
+    if re_match:
+        return size_human_2_size_bytes(
+            "%s%siB" % (re_match.group(1), re_match.group(2)))
+
+    raise LsmError(
+        ErrorNumber.PLUGIN_BUG,
+        "_hp_size_to_lsm(): Got unexpected HP size string %s" %
+        hp_size)
+
+
+def _pool_status_of(hp_array):
+    """
+    Return (status, status_info)
+    """
+    if hp_array['Status'] == 'OK':
+        return Pool.STATUS_OK, ''
+    else:
+        # TODO(Gris Ge): Try degrade a RAID or fail a RAID.
+        return Pool.STATUS_OTHER, hp_array['Status']
+
+
+def _pool_id_of(sys_id, array_name):
+    return "%s:%s" % (sys_id, array_name.replace(' ', ''))
+
+
+def _hp_raid_type_to_lsm(hp_ld):
+    """
+    Based on this property:
+        Fault Tolerance: 0/5/6/1+0
+    """
+    hp_raid_level = hp_ld['Fault Tolerance']
+    if hp_raid_level == '0':
+        return Volume.RAID_TYPE_RAID0
+    elif hp_raid_level == '5':
+        return Volume.RAID_TYPE_RAID5
+    elif hp_raid_level == '6':
+        return Volume.RAID_TYPE_RAID6
+    elif hp_raid_level == '1+0':
+        return Volume.RAID_TYPE_RAID10
+
+    return Volume.RAID_TYPE_UNKNOWN
+
+
+def _parse_hpssacli_output(output):
+    """
+    Got a output string of hpssacli to dictionary(nested).
+    """
+    output_lines = [ l for l in output.split("\n") if l ]
+
+    data = {}
+
+    # Detemine indention level
+    top_indention_level= sorted(
+        set(
+            len(line) - len(line.lstrip())
+            for line in output_lines))[0]
+
+    indent_2_data = {
+        top_indention_level: data
+    }
+
+    for line_num in range(len(output_lines)):
+        cur_line = output_lines[line_num]
+        if cur_line.strip() == 'None attached':
+            continue
+        if line_num + 1 == len(output_lines):
+            nxt_line = ''
+        else:
+            nxt_line = output_lines[line_num + 1]
+
+        cur_indent_count = len(cur_line) - len(cur_line.lstrip())
+        nxt_indent_count = len(nxt_line) - len(nxt_line.lstrip())
+
+        cur_line_splitted = cur_line.split(": ")
+        cur_data_pointer = indent_2_data[cur_indent_count]
+
+        if nxt_indent_count > cur_indent_count:
+            nxt_line_splitted = nxt_line.split(": ")
+            if len(nxt_line_splitted) == 1:
+                new_data = []
+            else:
+                new_data = {}
+
+            if cur_line.lstrip() not in cur_data_pointer:
+                cur_data_pointer[cur_line.lstrip()] = new_data
+                indent_2_data[nxt_indent_count] = new_data
+            elif type(cur_data_pointer[cur_line.lstrip()]) != type(new_data):
+                raise LsmError(
+                    ErrorNumber.PLUGIN_BUG,
+                    "_parse_hpssacli_output(): Unexpected line '%s%s\n'" %
+                    cur_line, nxt_line)
+        else:
+            if len(cur_line_splitted) == 1:
+                if type(indent_2_data[cur_indent_count]) != list:
+                    raise Exception("not a list: '%s'" % cur_line)
+                cur_data_pointer.append(cur_line.lstrip())
+            else:
+                cur_data_pointer[cur_line_splitted[0].lstrip()] = \
+                    ": ".join(cur_line_splitted[1:]).strip()
+    return data
+
+
+class SmartArray(IPlugin):
+    _DEFAULT_BIN_PATHS = [
+        "/usr/sbin/hpssacli", "/opt/hp/hpssacli/bld/hpssacli"]
+
+    def __init__(self):
+        self._sacli_bin = None
+
+    def _find_sacli(self):
+        """
+        Try _DEFAULT_MDADM_BIN_PATHS
+        """
+        for cur_path in SmartArray._DEFAULT_BIN_PATHS:
+            if os.path.lexists(cur_path):
+                self._sacli_bin = cur_path
+
+        if not self._sacli_bin:
+            raise LsmError(
+                ErrorNumber.INVALID_ARGUMENT,
+                "SmartArray sacli is not installed correctly")
+
+    @_handle_errors
+    def plugin_register(self, uri, password, timeout, flags=Client.FLAG_RSVD):
+        if os.geteuid() != 0:
+            raise LsmError(
+                ErrorNumber.INVALID_ARGUMENT,
+                "This plugin requires root privilege both daemon and client")
+        uri_parsed = uri_parse(uri)
+        self._sacli_bin = uri_parsed.get('parameters', {}).get('sacli')
+        if not self._sacli_bin:
+            self._find_sacli()
+
+        self._sacli_exec(['version'], flag_convert=False)
+
+    @_handle_errors
+    def plugin_unregister(self, flags=Client.FLAG_RSVD):
+        pass
+
+    @_handle_errors
+    def job_status(self, job_id, flags=Client.FLAG_RSVD):
+        raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
+
+    @_handle_errors
+    def job_free(self, job_id, flags=Client.FLAG_RSVD):
+        pass
+
+    @_handle_errors
+    def plugin_info(self, flags=Client.FLAG_RSVD):
+        return "LSI SmartArray Plugin", VERSION
+
+    @_handle_errors
+    def time_out_set(self, ms, flags=Client.FLAG_RSVD):
+        raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
+
+    @_handle_errors
+    def time_out_get(self, flags=Client.FLAG_RSVD):
+        raise LsmError(ErrorNumber.NO_SUPPORT, "Not supported yet")
+
+    @_handle_errors
+    def capabilities(self, system, flags=Client.FLAG_RSVD):
+        cur_lsm_syss = self.systems()
+        if system.id not in list(s.id for s in cur_lsm_syss):
+            raise LsmError(
+                ErrorNumber.NOT_FOUND_SYSTEM,
+                "System not found")
+        cap = Capabilities()
+        cap.set(Capabilities.DISKS)
+        cap.set(Capabilities.VOLUMES)
+        return cap
+
+    def _sacli_exec(self, sacli_cmds, flag_convert=True):
+        """
+        If flag_convert is True, convert data into dict.
+        """
+        sacli_cmds.insert(0, self._sacli_bin)
+        try:
+            output = cmd_exec(sacli_cmds)
+        except OSError as os_error:
+            if os_error.errno == errno.ENOENT:
+                raise LsmError(
+                    ErrorNumber.INVALID_ARGUMENT,
+                    "hpssacli binary '%s' is not exist or executable." %
+                    self._sacli_bin)
+            else:
+                raise
+
+        if flag_convert:
+            return _parse_hpssacli_output(output)
+        else:
+            return output
+
+    @staticmethod
+    def _lsm_status_of_ctrl(ctrl_status):
+        status_info = ''
+        status = System.STATUS_UNKNOWN
+        check_list = [
+            'Controller Status', 'Cache Status', 'Battery/Capacitor Status']
+        for key_name in check_list:
+            if ctrl_status[key_name] != 'OK':
+                # TODO(Gris Ge): Beg HP for possible values
+                status = System.STATUS_OTHER
+                status_info += ctrl_status[key_name]
+        if status != System.STATUS_OTHER:
+            status = System.STATUS_OK
+
+        return status, status_info
+
+    def _sys_id_of_ctrl_num(self, ctrl_num, ctrl_show_all_output=None):
+        if ctrl_show_all_output is None:
+            return self._sacli_exec(
+                ["/c%d" % ctrl_num, "show"])['Serial Number']
+        else:
+            return ctrl_show_all_output['Basics']['Serial Number']
+
+    @_handle_errors
+    def systems(self, flags=0):
+        """
+        Depend on command:
+            hpssacli ctrl all show detail
+            hpssacli ctrl all show status
+        """
+        rc_lsm_syss = []
+        ctrl_all_show = self._sacli_exec(
+            ["ctrl", "all", "show", "detail"])
+        ctrl_all_status = self._sacli_exec(
+            ["ctrl", "all", "show", "status"])
+        print ctrl_all_show.items()
+        print ctrl_all_status.items()
+
+        for ctrl_name in ctrl_all_show.keys():
+            ctrl_data = ctrl_all_show[ctrl_name]
+            sys_id = ctrl_data['Serial Number']
+            (status, status_info) = SmartArray._lsm_status_of_ctrl(
+                ctrl_all_status[ctrl_name])
+
+            plugin_data = "%s" % ctrl_data['Slot']
+
+            rc_lsm_syss.append(
+                System(sys_id, ctrl_name, status, status_info, plugin_data))
+
+        return rc_lsm_syss
+
+    @staticmethod
+    def _hp_disk_to_lsm_disk(hp_disk, sys_id, ctrl_num, key_name,
+                             flag_free=False):
+        disk_id = hp_disk['Serial Number']
+        disk_num = key_name[len("physicaldrive "):]
+        disk_name = "%s %s" % (hp_disk['Model'], disk_num)
+        disk_type = _disk_type_of(hp_disk)
+        blk_size = int(hp_disk['Native Block Size'])
+        blk_count = int(_hp_size_to_lsm(hp_disk['Size']) / blk_size)
+        status = _disk_status_of(hp_disk, flag_free)
+        plugin_data = "%s:%s" % (ctrl_num, disk_num)
+
+        return Disk(
+            disk_id, disk_name, disk_type, blk_size, blk_count,
+            status, sys_id, plugin_data)
+
+    @_handle_errors
+    def disks(self, search_key=None, search_value=None,
+              flags=Client.FLAG_RSVD):
+        """
+        Depend on command:
+            hpssacli ctrl all show config detail
+        """
+        # TODO(Gris Ge): Need real test on spare disk and free disk.
+        #                The free disks is purely base on HP document.
+        rc_lsm_disks = []
+        ctrl_all_conf = self._sacli_exec(
+            ["ctrl", "all", "show", "config", "detail"])
+        for ctrl_data in ctrl_all_conf.values():
+            sys_id = ctrl_data['Serial Number']
+            ctrl_num = ctrl_data['Slot']
+            for key_name in ctrl_data.keys():
+                if key_name.startswith("Array:"):
+                    for array_key_name in ctrl_data[key_name].keys():
+                        if array_key_name.startswith("physicaldrive"):
+                            rc_lsm_disks.append(
+                                SmartArray._hp_disk_to_lsm_disk(
+                                    ctrl_data[key_name][array_key_name],
+                                    sys_id, ctrl_num, array_key_name,
+                                    flag_free=False))
+
+                if key_name == 'unassigned':
+                    for array_key_name in ctrl_data[key_name].keys():
+                        if array_key_name.startswith("physicaldrive"):
+                            rc_lsm_disks.append(
+                                SmartArray._hp_disk_to_lsm_disk(
+                                    ctrl_data[key_name][array_key_name],
+                                    sys_id, ctrl_num, array_key_name,
+                                    flag_free=False))
+
+        return search_property(rc_lsm_disks, search_key, search_value)
+
+    def _hp_array_to_lsm_pool(self, hp_array, array_name, sys_id, ctrl_num):
+        print hp_array.items()
+        pool_id = _pool_id_of(sys_id, array_name)
+        name = array_name
+        elem_type = Pool.ELEMENT_TYPE_VOLUME | Pool.ELEMENT_TYPE_VOLUME_FULL
+        unsupported_actions = 0
+        # TODO(Gris Ge): HP does not provide a precise number of bytes.
+        free_space = _hp_size_to_lsm(hp_array['Unused Space'])
+        total_space = free_space
+        for key_name in hp_array.keys():
+            if key_name.startswith('Logical Drive'):
+                total_space += _hp_size_to_lsm(hp_array[key_name]['Size'])
+
+        (status, status_info) = _pool_status_of(hp_array)
+
+        plugin_data = "%s:%s" % (
+            ctrl_num, array_name[len("Array: "):])
+
+        return Pool(
+            pool_id, name, elem_type, unsupported_actions,
+            total_space, free_space, status, status_info,
+            sys_id, plugin_data)
+
+    @_handle_errors
+    def pools(self, search_key=None, search_value=None,
+              flags=Client.FLAG_RSVD):
+        """
+        Depend on command:
+            hpssacli ctrl all show config detail
+        """
+        lsm_pools = []
+        ctrl_all_conf = self._sacli_exec(
+            ["ctrl", "all", "show", "config", "detail"])
+        for ctrl_data in ctrl_all_conf.values():
+            sys_id = ctrl_data['Serial Number']
+            ctrl_num = ctrl_data['Slot']
+            for key_name in ctrl_data.keys():
+                if key_name.startswith("Array:"):
+                    lsm_pools.append(
+                        self._hp_array_to_lsm_pool(
+                            ctrl_data[key_name], key_name, sys_id, ctrl_num))
+
+        return search_property(lsm_pools, search_key, search_value)
+
+    @staticmethod
+    def _hp_ld_to_lsm_vol(hp_ld, pool_id, sys_id, ctrl_num, array_num,
+                          hp_ld_name):
+        ld_num = hp_ld_name[len("Logical Drive: "):]
+        name = hp_ld_name
+        vpd83 = hp_ld['Unique Identifier'].lower()
+        vol_id = vpd83
+        # No document or command output indicate block size
+        # of volume. So we try to read from linux kernel, if failed
+        # try 512 and roughly calculate the sector count.
+        sd_name_regex = re.compile("/dev/(sd[a-z]+)")
+        regex_match = sd_name_regex.search(hp_ld['Disk Name'])
+        if regex_match:
+            sd_name = regex_match.group(1)
+            block_size = int(file_read(
+                "/sys/block/%s/queue/logical_block_size" % sd_name))
+            num_of_blocks = int(file_read("/sys/block/%s/size" % sd_name))
+        else:
+            block_size = 512
+            num_of_blocks = int(_hp_size_to_lsm(hp_ld['Size']) / block_size)
+
+        # HP SmartArray does not allow disabling volume.
+        admin_state = Volume.ADMIN_STATE_ENABLED
+
+        plugin_data = "%s:%s:%s" % (ctrl_num, array_num, ld_num)
+
+        return Volume(
+            vol_id, name, vpd83, block_size, num_of_blocks, admin_state,
+            sys_id, pool_id, plugin_data)
+
+    @_handle_errors
+    def volumes(self, search_key=None, search_value=None, flags=0):
+        """
+        Depend on command:
+            hpssacli ctrl all show config detail
+        """
+        lsm_vols = []
+        ctrl_all_conf = self._sacli_exec(
+            ["ctrl", "all", "show", "config", "detail"])
+        for ctrl_data in ctrl_all_conf.values():
+            ctrl_num = ctrl_data['Slot']
+            sys_id = ctrl_data['Serial Number']
+            for key_name in ctrl_data.keys():
+                if not key_name.startswith("Array:"):
+                    continue
+                pool_id = _pool_id_of(sys_id, key_name)
+                array_num = key_name[len('Array: '):]
+                for array_key_name in ctrl_data[key_name].keys():
+                    if not array_key_name.startswith("Logical Drive"):
+                        continue
+                    lsm_vols.append(
+                        SmartArray._hp_ld_to_lsm_vol(
+                            ctrl_data[key_name][array_key_name],
+                            pool_id, sys_id, ctrl_num, array_num,
+                            array_key_name))
+
+        return search_property(lsm_vols, search_key, search_value)
+
+    @_handle_errors
+    def volume_raid_info(self, volume, flags=Client.FLAG_RSVD):
+        """
+        Depend on command:
+            hpssacli ctrl slot=0 show config detail
+        """
+        if not volume.plugin_data:
+            raise LsmError(
+                ErrorNumber.INVALID_ARGUMENT,
+                "Ilegal input volume argument: missing plugin_data property")
+
+        (ctrl_num, array_num, ld_num) = volume.plugin_data.split(":")
+
+        ctrl_conf = self._sacli_exec(
+            ["ctrl", "slot=%s" % ctrl_num, "show", "config", "detail"])
+        ctrl_data = ctrl_conf.values()[0]
+        disk_count = 0
+        flag_found = False
+        for key_name in ctrl_data.keys():
+            if key_name != "Array: %s" % array_num:
+                continue
+            for array_key_name in ctrl_data[key_name].keys():
+                if array_key_name == "Logical Drive: %s" % ld_num:
+                    hp_ld = ctrl_data[key_name][array_key_name]
+                    flag_found = True
+                    raid_type = _hp_raid_type_to_lsm(
+                        hp_ld)
+                    strip_size = _hp_size_to_lsm(
+                        hp_ld['Strip Size'])
+                    stripe_size = _hp_size_to_lsm(
+                        hp_ld['Full Stripe Size'])
+                elif array_key_name.startswith("physicaldrive"):
+                    hp_disk = ctrl_data[key_name][array_key_name]
+                    if hp_disk['Drive Type'] == 'Data Drive':
+                        disk_count += 1
+
+        if flag_found is False:
+            raise LsmError(
+                ErrorNumber.NOT_FOUND_VOLUME,
+                "Volume not found")
+
+        return [raid_type, strip_size, disk_count, strip_size, stripe_size]
diff --git a/plugin/hpsa/hpsa_lsmplugin b/plugin/hpsa/hpsa_lsmplugin
new file mode 100755
index 0000000..07230f5
--- /dev/null
+++ b/plugin/hpsa/hpsa_lsmplugin
@@ -0,0 +1,37 @@
+#!/usr/bin/env python2
+# Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+#
+# Author: tasleson
+#         Gris Ge <f...@redhat.com>
+
+import sys
+import syslog
+import traceback
+
+try:
+    from lsm import PluginRunner
+    from lsm.plugin.hpsa import SmartArray
+
+    if __name__ == '__main__':
+        PluginRunner(SmartArray, sys.argv).run()
+except Exception:
+    #This should be quite rare, but when it does happen this is pretty
+    #key in understanding what happened, especially when it happens when
+    #running from the daemon.
+    msg = str(traceback.format_exc())
+    syslog.syslog(syslog.LOG_ERR, msg)
+    sys.stderr.write(msg)
+    sys.exit(1)
diff --git a/plugin/hpsa/utils.py b/plugin/hpsa/utils.py
new file mode 100644
index 0000000..23ae6cd
--- /dev/null
+++ b/plugin/hpsa/utils.py
@@ -0,0 +1,55 @@
+## Copyright (C) 2015 Red Hat, Inc.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Author: Gris Ge <f...@redhat.com>
+
+import subprocess
+import os
+
+
+def cmd_exec(cmds):
+    """
+    Execute provided command and return the STDOUT as string.
+    Raise ExecError if command return code is not zero
+    """
+    cmd_popen = subprocess.Popen(
+        cmds, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+        env={"PATH": os.getenv("PATH")})
+    str_stdout = "".join(list(cmd_popen.stdout)).strip()
+    str_stderr = "".join(list(cmd_popen.stderr)).strip()
+    errno = cmd_popen.wait()
+    if errno != 0:
+        raise ExecError(" ".join(cmds), errno, str_stdout, str_stderr)
+    return str_stdout
+
+
+def file_read(file_path):
+    """
+    Read file and return string of file content.
+    Use 'cat' command to better utilize the ExecError.
+    """
+    return cmd_exec(['cat', file_path])
+
+class ExecError(Exception):
+    def __init__(self, cmd, errno, stdout, stderr, *args, **kwargs):
+        Exception.__init__(self, *args, **kwargs)
+        self.cmd = cmd
+        self.errno = errno
+        self.stdout = stdout
+        self.stderr = stderr
+
+    def __str__(self):
+        return "cmd: '%s', errno: %d, stdout: '%s', stderr: '%s'" % \
+            (self.cmd, self.errno, self.stdout, self.stderr)
-- 
1.8.3.1


------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the 
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
Libstoragemgmt-devel mailing list
Libstoragemgmt-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libstoragemgmt-devel

Reply via email to