← Back to team overview

nagios-charmers team mailing list archive

[Merge] ~aluria/hw-health-charm/+git/hw-health-charm:unittests-engrot6 into hw-health-charm:master

 

Alvaro Uría has proposed merging ~aluria/hw-health-charm/+git/hw-health-charm:unittests-engrot6 into hw-health-charm:master.

Requested reviews:
  Nagios Charm developers (nagios-charmers)

For more details, see:
https://code.launchpad.net/~aluria/hw-health-charm/+git/hw-health-charm/+merge/361504
-- 
Your team Nagios Charm developers is requested to review the proposed merge of ~aluria/hw-health-charm/+git/hw-health-charm:unittests-engrot6 into hw-health-charm:master.
diff --git a/src/files/check_mdadm.py b/src/files/check_mdadm.py
index 741865e..b2c8b4a 100644
--- a/src/files/check_mdadm.py
+++ b/src/files/check_mdadm.py
@@ -4,6 +4,7 @@
 import os
 import re
 import subprocess
+import sys
 
 
 def get_devices():
@@ -15,7 +16,8 @@ def get_devices():
                                            stderr=subprocess.PIPE)
             devices_re = re.compile('^ARRAY\s+([^ ]+) ')
             devices = set()
-            for line in devices_raw.stdout.readline().decode():
+            for line in devices_raw.stdout.readlines():
+                line = line.decode().strip()
                 device_re = devices_re.match(line)
                 if device_re is not None:
                     devices.add(device_re.group(1))
@@ -23,14 +25,15 @@ def get_devices():
         except Exception as e:
             # log error
             pass
-    return
+    return set()
 
 
-def main():
+def parse_output():
     devices = get_devices()
     if len(devices) == 0:
-        print('WARNING: unexpectedly checked no devices')
-        return 1
+        msg = 'WARNING: unexpectedly checked no devices'
+        rc = 1
+        return (msg, rc)
 
     mdadm_detail = ['/sbin/mdadm', '--detail']
     mdadm_detail.extend(sorted(devices))
@@ -39,12 +42,13 @@ def main():
                                                stdout=subprocess.PIPE,
                                                stderr=subprocess.PIPE)
     except Exception as error:
-        print('WARNING: error executing mdadm: {}'.format(error))
-        return 1
+        msg = 'WARNING: error executing mdadm: {}'.format(error)
+        rc = 1
+        return (msg, rc)
 
-    devices_re = '^(/[^ ]+):$'
-    state_re = '^\s*State :\s+(\S+)\s*'
-    status_re = '^\s*(Active|Working|Failed|Spare) Devices :\s+(\d+)'
+    devices_re = '^(/\S+):$'
+    state_re = '^\s*State\s+:\s+(\S+)$'
+    status_re = '^\s*(Active|Working|Failed|Spare) Devices\s+:\s+(\d+)$'
 
     devices_cre = re.compile(devices_re)
     state_cre = re.compile(state_re)
@@ -52,7 +56,8 @@ def main():
 
     device = None
     devices_stats = {}
-    for line in devices_details_raw.stdout.readline().decode():
+    for line in devices_details_raw.stdout.readlines():
+        line = line.decode().rstrip()
         m = devices_cre.match(line)
         if m:
             device = m.group(1)
@@ -75,8 +80,8 @@ def main():
 
     rc = devices_details_raw.wait()
     if rc:
-        print('WARNING: mdadm returned exit status {}'.format(int(rc)))
-        return 1
+        msg = 'WARNING: mdadm returned exit status {}'.format(int(rc))
+        return (msg, 1)
 
     parts = []
     msg = []
@@ -88,22 +93,30 @@ def main():
         else:
             parts.append('{} ok'.format(device))
 
-        for status in sorted(devices_stats[device]['stats']):
-            parts.append('{}[{}]'
-                         .format(status,
-                                 devices_stats[device]['stats'][status]))
-            if status == 'Failed' \
-                    and devices_stats[device]['stats'][status] > 0:
-                critical = True
+        if devices_stats[device]['stats'].get('Failed', 0) > 0:
+            critical = True
+            for status in sorted(devices_stats[device]['stats']):
+                parts.append('{}[{}]'
+                             .format(status,
+                                     devices_stats[device]['stats'][status]))
         msg.append(', '.join(parts))
+        parts = []
 
     msg = '; '.join(msg)
     if critical:
-        print('CRITICAL: {}'.format(msg))
-        return 2
+        msg_tmpl = 'CRITICAL: {}'
+        rc = 2
     else:
-        print('OK: {}'.format(msg))
-        return 0
+        msg_tmpl = 'OK: {}'
+        rc = 0
+    return (msg_tmpl.format(msg),
+            rc)
+
+
+def main():
+    msg, rc = parse_output()
+    print(msg)
+    sys.exit(rc)
 
 
 if __name__ == '__main__':
diff --git a/src/lib/utils/tooling.py b/src/lib/utils/tooling.py
index 387324f..3fe4fc6 100644
--- a/src/lib/utils/tooling.py
+++ b/src/lib/utils/tooling.py
@@ -8,7 +8,8 @@ from charmhelpers.core.hookenv import (
     INFO
 )
 # from charms.layer import snap
-from shutil import copy2
+# from shutil import copy2
+import shutil
 import subprocess
 
 TOOLING = {
@@ -41,6 +42,9 @@ def install_tools(tools, method=None):
                 except Exception as error:
                     log('Snap install error: {}'.format(error),
                         ERROR)
+        elif tool == 'mdadm':
+            # XXX(aluria): mdadm is assumed to be installed by default
+            count += 1
 
     return len(tools) >= count > 0
 
@@ -62,7 +66,7 @@ def configure_tools(tools):
                 cronjob_scriptname = os.path.basename(TOOLING[tool]['cronjob'])
                 if filepath:
                     subprocess.call(filepath)
-                    copy2(filepath, PLUGINS_DIR)
+                    shutil.copy2(filepath, PLUGINS_DIR)
                     log('Cronjob script [{}]'
                         ' copied to {}'.format(filepath, PLUGINS_DIR),
                         DEBUG)
@@ -85,7 +89,7 @@ def configure_tools(tools):
             if 'filename' in TOOLING[tool]:
                 filepath = _get_filepath(TOOLING[tool]['filename'])
                 if filepath:
-                    copy2(filepath, PLUGINS_DIR)
+                    shutil.copy2(filepath, PLUGINS_DIR)
                     count += 1
                     log('NRPE script for tool [{}] configured'.format(tool),
                         INFO)
diff --git a/src/reactive/hw_health.py b/src/reactive/hw_health.py
index 730d405..b136b1e 100644
--- a/src/reactive/hw_health.py
+++ b/src/reactive/hw_health.py
@@ -100,7 +100,8 @@ def configure_nrpe():
     # then, parse checks output for Nagios to alert on
     # configure_cronjobs(tools)
 
-    if configure_nrpe_checks(tools):
+    configured = configure_nrpe_checks(tools)
+    if configured:
         status.active('ready')
         set_flag('hw-health.nrpe')
     else:
diff --git a/src/tests/hw-health-samples/mdadm.output b/src/tests/hw-health-samples/mdadm.output
new file mode 100644
index 0000000..9356757
--- /dev/null
+++ b/src/tests/hw-health-samples/mdadm.output
@@ -0,0 +1,100 @@
+/dev/md0:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:05 2018
+     Raid Level : raid1
+     Array Size : 523712 (511.52 MiB 536.28 MB)
+  Used Dev Size : 523712 (511.52 MiB 536.28 MB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+    Update Time : Sun Nov  4 00:57:07 2018
+          State : clean
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:0  (local to host fnos-nvme05)
+           UUID : dfaf7413:7551b000:56dd7442:5b020adb
+         Events : 51
+
+    Number   Major   Minor   RaidDevice State
+       0       8        1        0      active sync   /dev/sda1
+       2       8       17        1      active sync   /dev/sdb1
+/dev/md1:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:09 2018
+     Raid Level : raid1
+     Array Size : 1948672 (1903.32 MiB 1995.44 MB)
+  Used Dev Size : 1948672 (1903.32 MiB 1995.44 MB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+    Update Time : Tue Nov 20 15:40:27 2018
+          State : clean
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:1  (local to host fnos-nvme05)
+           UUID : 8946258c:a6246ca7:8d3dc20a:33bcabcb
+         Events : 51
+
+    Number   Major   Minor   RaidDevice State
+       0       8        2        0      active sync   /dev/sda2
+       2       8       18        1      active sync   /dev/sdb2
+/dev/md3:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:16 2018
+     Raid Level : raid1
+     Array Size : 488148992 (465.54 GiB 499.86 GB)
+  Used Dev Size : 488148992 (465.54 GiB 499.86 GB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+  Intent Bitmap : Internal
+
+    Update Time : Tue Nov 27 16:47:44 2018
+          State : active
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:3  (local to host fnos-nvme05)
+           UUID : ae461b25:13219daa:75846e9e:2e5a52f1
+         Events : 1418
+
+    Number   Major   Minor   RaidDevice State
+       0       8        4        0      active sync   /dev/sda4
+       2       8       20        1      active sync   /dev/sdb4
+/dev/md2:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:13 2018
+     Raid Level : raid1
+     Array Size : 439320576 (418.97 GiB 449.86 GB)
+  Used Dev Size : 439320576 (418.97 GiB 449.86 GB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+  Intent Bitmap : Internal
+
+    Update Time : Tue Nov 27 16:47:41 2018
+          State : clean
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:2  (local to host fnos-nvme05)
+           UUID : a83fda21:56cf85d4:99c802ed:c07c4f76
+         Events : 5715
+
+    Number   Major   Minor   RaidDevice State
+       0       8        3        0      active sync   /dev/sda3
+       2       8       19        1      active sync   /dev/sdb3
diff --git a/src/tests/hw-health-samples/mdadm.output.critical b/src/tests/hw-health-samples/mdadm.output.critical
new file mode 100644
index 0000000..4bdab35
--- /dev/null
+++ b/src/tests/hw-health-samples/mdadm.output.critical
@@ -0,0 +1,100 @@
+/dev/md0:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:05 2018
+     Raid Level : raid1
+     Array Size : 523712 (511.52 MiB 536.28 MB)
+  Used Dev Size : 523712 (511.52 MiB 536.28 MB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+    Update Time : Sun Nov  4 00:57:07 2018
+          State : clean
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:0  (local to host fnos-nvme05)
+           UUID : dfaf7413:7551b000:56dd7442:5b020adb
+         Events : 51
+
+    Number   Major   Minor   RaidDevice State
+       0       8        1        0      active sync   /dev/sda1
+       2       8       17        1      active sync   /dev/sdb1
+/dev/md1:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:09 2018
+     Raid Level : raid1
+     Array Size : 1948672 (1903.32 MiB 1995.44 MB)
+  Used Dev Size : 1948672 (1903.32 MiB 1995.44 MB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+    Update Time : Tue Nov 20 15:40:27 2018
+          State : degraded
+ Active Devices : 1
+Working Devices : 1
+ Failed Devices : 1
+  Spare Devices : 0
+
+           Name : fnos-nvme05:1  (local to host fnos-nvme05)
+           UUID : 8946258c:a6246ca7:8d3dc20a:33bcabcb
+         Events : 51
+
+    Number   Major   Minor   RaidDevice State
+       0       8        2        0      active sync   /dev/sda2
+       2       8       18        1      active sync   /dev/sdb2
+/dev/md3:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:16 2018
+     Raid Level : raid1
+     Array Size : 488148992 (465.54 GiB 499.86 GB)
+  Used Dev Size : 488148992 (465.54 GiB 499.86 GB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+  Intent Bitmap : Internal
+
+    Update Time : Tue Nov 27 16:47:44 2018
+          State : active
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:3  (local to host fnos-nvme05)
+           UUID : ae461b25:13219daa:75846e9e:2e5a52f1
+         Events : 1418
+
+    Number   Major   Minor   RaidDevice State
+       0       8        4        0      active sync   /dev/sda4
+       2       8       20        1      active sync   /dev/sdb4
+/dev/md2:
+        Version : 1.2
+  Creation Time : Thu Aug  2 22:50:13 2018
+     Raid Level : raid1
+     Array Size : 439320576 (418.97 GiB 449.86 GB)
+  Used Dev Size : 439320576 (418.97 GiB 449.86 GB)
+   Raid Devices : 2
+  Total Devices : 2
+    Persistence : Superblock is persistent
+
+  Intent Bitmap : Internal
+
+    Update Time : Tue Nov 27 16:47:41 2018
+          State : clean
+ Active Devices : 2
+Working Devices : 2
+ Failed Devices : 0
+  Spare Devices : 0
+
+           Name : fnos-nvme05:2  (local to host fnos-nvme05)
+           UUID : a83fda21:56cf85d4:99c802ed:c07c4f76
+         Events : 5715
+
+    Number   Major   Minor   RaidDevice State
+       0       8        3        0      active sync   /dev/sda3
+       2       8       19        1      active sync   /dev/sdb3
diff --git a/src/tests/hw-health-samples/megacli.output.1 b/src/tests/hw-health-samples/megacli.output.1
new file mode 100644
index 0000000..2fb339f
--- /dev/null
+++ b/src/tests/hw-health-samples/megacli.output.1
@@ -0,0 +1,31 @@
+
+
+Adapter 0 -- Virtual Drive Information:
+Virtual Drive: 0 (Target Id: 0)
+Name                :vg01
+RAID Level          : Primary-1, Secondary-0, RAID Level Qualifier-0
+Size                : 7.276 TB
+Sector Size         : 512
+Is VD emulated      : No
+Mirror Data         : 7.276 TB
+State               : Optimal
+Strip Size          : 64 KB
+Number Of Drives    : 4
+Span Depth          : 1
+Default Cache Policy: WriteBack, ReadAhead, Direct, No Write Cache if Bad BBU
+Current Cache Policy: WriteBack, ReadAhead, Direct, No Write Cache if Bad BBU
+Default Access Policy: Read/Write
+Current Access Policy: Read/Write
+Disk Cache Policy   : Disk's Default
+Encryption Type     : None
+Default Power Savings Policy: Controller Defined
+Current Power Savings Policy: None
+Can spin up in 1 minute: Yes
+LD has drives that support T10 power conditions: Yes
+LD's IO profile supports MAX power savings with cached writes: No
+Bad Blocks Exist: No
+Is VD Cached: No
+
+
+
+Exit Code: 0x00
diff --git a/src/tests/hw-health-samples/sas2ircu.huawei.output.1 b/src/tests/hw-health-samples/sas2ircu.huawei.output.1
new file mode 100644
index 0000000..02ac325
--- /dev/null
+++ b/src/tests/hw-health-samples/sas2ircu.huawei.output.1
@@ -0,0 +1,150 @@
+LSI Corporation SAS2 IR Configuration Utility.
+Version 20.00.00.00 (2014.09.18)
+Copyright (c) 2008-2014 LSI Corporation. All rights reserved.
+
+Read configuration has been initiated for controller 0
+------------------------------------------------------------------------
+Controller information
+------------------------------------------------------------------------
+  Controller type                         : SAS2308_2
+  BIOS version                            : 7.39.00.00
+  Firmware version                        : 18.00.04.00
+  Channel description                     : 1 Serial Attached SCSI
+  Initiator ID                            : 0
+  Maximum physical devices                : 255
+  Concurrent commands supported           : 3072
+  Slot                                    : Unknown
+  Segment                                 : 0
+  Bus                                     : 1
+  Device                                  : 0
+  Function                                : 0
+  RAID Support                            : Yes
+------------------------------------------------------------------------
+IR Volume information
+------------------------------------------------------------------------
+------------------------------------------------------------------------
+Physical device information
+------------------------------------------------------------------------
+Initiator at ID #0
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 0
+  SAS Address                             : 4433221-1-0000-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1334PEJ8VRBS
+  GUID                                    : 5000cca250e03649
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 1
+  SAS Address                             : 4433221-1-0100-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1334PEHMEH5S
+  GUID                                    : 5000cca250d6ed31
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 2
+  SAS Address                             : 4433221-1-0200-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1338P4HVWD8B
+  GUID                                    : 5000cca249da4ffe
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 3
+  SAS Address                             : 4433221-1-0300-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1338P4HVVHZB
+  GUID                                    : 5000cca249da4cb0
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 4
+  SAS Address                             : 4433221-1-0400-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1338P4HVPDGB
+  GUID                                    : 5000cca249da397e
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 5
+  SAS Address                             : 4433221-1-0500-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1338P4HVWWKB
+  GUID                                    : 5000cca249da51d8
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 6
+  SAS Address                             : 4433221-1-0600-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1334PEHPU1XX
+  GUID                                    : 5000cca250d80160
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 7
+  SAS Address                             : 4433221-1-0700-0000
+  State                                   : Ready (RDY)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : HGST HUS724040AL
+  Firmware Revision                       : A8B0
+  Serial No                               : PN1338P4HVWV7B
+  GUID                                    : 5000cca249da51af
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+------------------------------------------------------------------------
+Enclosure information
+------------------------------------------------------------------------
+  Enclosure#                              : 1
+  Logical ID                              : 5384c4f8:aa1c9000
+  Numslots                                : 8
+  StartSlot                               : 0
+------------------------------------------------------------------------
+SAS2IRCU: Command DISPLAY Completed Successfully.
+SAS2IRCU: Utility Completed Successfully.
diff --git a/src/tests/hw-health-samples/sas3ircu.supermicro.output.1 b/src/tests/hw-health-samples/sas3ircu.supermicro.output.1
new file mode 100644
index 0000000..03ca92e
--- /dev/null
+++ b/src/tests/hw-health-samples/sas3ircu.supermicro.output.1
@@ -0,0 +1,82 @@
+Avago Technologies SAS3 IR Configuration Utility.
+Version 17.00.00.00 (2018.04.02)
+Copyright (c) 2009-2018 Avago Technologies. All rights reserved.
+
+Read configuration has been initiated for controller 0
+------------------------------------------------------------------------
+Controller information
+------------------------------------------------------------------------
+  Controller type                         : SAS3008
+  PI Supported                            : Yes
+  PI Mixing                               : Disabled
+  BIOS version                            : 8.35.00.00
+  Firmware version                        : 15.00.00.00
+  Channel description                     : 1 Serial Attached SCSI
+  Initiator ID                            : 0
+  Maximum physical devices                : 255
+  Concurrent commands supported           : 3072
+  Slot                                    : 2
+  Segment                                 : 0
+  Bus                                     : 2
+  Device                                  : 0
+  Function                                : 0
+  RAID Support                            : Yes
+------------------------------------------------------------------------
+IR Volume information
+------------------------------------------------------------------------
+IR volume 1
+  Volume ID                               : 323
+  PI Supported                            : No
+  Status of volume                        : Okay (OKY)
+  Volume wwid                             : 017e97cb06987855
+  RAID level                              : RAID1
+  Size (in MB)                            : 3814697
+  Physical hard disks                     :
+  PHY[0] Enclosure#/Slot#                 : 1:0
+  PHY[1] Enclosure#/Slot#                 : 1:1
+------------------------------------------------------------------------
+Physical device information
+------------------------------------------------------------------------
+Initiator at ID #0
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 0
+  PI Supported                            : No
+  SAS Address                             : 4433221-1-0000-0000
+  State                                   : Optimal (OPT)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : ST4000NM115-1YZ1
+  Firmware Revision                       : SN02
+  Serial No                               : ZC11155N
+  Unit Serial No(VPD)                     : ZC11155N
+  GUID                                    : 5000c500a1d45b35
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+
+Device is a Hard disk
+  Enclosure #                             : 1
+  Slot #                                  : 1
+  PI Supported                            : No
+  SAS Address                             : 4433221-1-0100-0000
+  State                                   : Optimal (OPT)
+  Size (in MB)/(in sectors)               : 3815447/7814037167
+  Manufacturer                            : ATA
+  Model Number                            : ST4000NM115-1YZ1
+  Firmware Revision                       : SN02
+  Serial No                               : ZC111FVA
+  Unit Serial No(VPD)                     : ZC111FVA
+  GUID                                    : 5000c500a1d51834
+  Protocol                                : SATA
+  Drive Type                              : SATA_HDD
+------------------------------------------------------------------------
+Enclosure information
+------------------------------------------------------------------------
+  Enclosure#                              : 1
+  Logical ID                              : 50030480:22df6300
+  Numslots                                : 8
+  StartSlot                               : 0
+------------------------------------------------------------------------
+SAS3IRCU: Command DISPLAY Completed Successfully.
+SAS3IRCU: Utility Completed Successfully.
diff --git a/src/tests/unit/test_check_mdadm.py b/src/tests/unit/test_check_mdadm.py
new file mode 100644
index 0000000..e9d36bf
--- /dev/null
+++ b/src/tests/unit/test_check_mdadm.py
@@ -0,0 +1,119 @@
+import mock
+import unittest
+import io
+import os
+import sys
+
+sys.path.append('files')
+import check_mdadm
+
+
+class TestCheckMegaCLI(unittest.TestCase):
+    @mock.patch('os.path.exists')
+    @mock.patch('subprocess.Popen')
+    def test_get_devices_mdadm_exists(self, mdadm_details, path_exists):
+        class Test_Popen(object):
+            def __init__(cls):
+                data = b'ARRAY /dev/md0 metadata=1.2 name=node00:0'
+                data += b' UUID=dfaf7413:7551b000:56dd7442:5b020adb\n'
+                data += b'ARRAY /dev/md1 metadata=1.2 name=node01:0'
+                data += b' UUID=dfaf7413:7551b000:56dd7442:5b020adc\n'
+                cls.stdout = io.BufferedReader(io.BytesIO(data))
+
+        mdadm_details.return_value = Test_Popen()
+        path_exists.return_value = True
+
+        real = check_mdadm.get_devices()
+        expected = set(['/dev/md0', '/dev/md1'])
+        self.assertEqual(real, expected)
+
+    @mock.patch('os.path.exists')
+    def test_get_devices_mdadm_notfound(self, path_exists):
+        path_exists.return_value = False
+        real = check_mdadm.get_devices()
+        expected = set()
+        self.assertEqual(real, expected)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('subprocess.Popen')
+    def test_get_devices_mdadm_exception(self, mdadm_details, path_exists):
+        path_exists.return_value = True
+        mdadm_details.side_effect = Exception
+        real = check_mdadm.get_devices()
+        expected = set()
+        self.assertEqual(real, expected)
+
+    @mock.patch('check_mdadm.get_devices')
+    @mock.patch('subprocess.Popen')
+    def test_parse_output_ok(self, mdadm_details, devices):
+        class Test_Popen(object):
+            def __init__(cls):
+                test_output = os.path.join(os.getcwd(),
+                                           'tests',
+                                           'hw-health-samples',
+                                           'mdadm.output')
+                cls.stdout = io.FileIO(test_output)
+                cls.wait = lambda: 0
+
+        devices.return_value = set(['/dev/md0', '/dev/md1', '/dev/md2'])
+        mdadm_details.return_value = Test_Popen()
+        real = check_mdadm.parse_output()
+        expected = ('OK: /dev/md0 ok; /dev/md1 ok; /dev/md3 ok; /dev/md2 ok',
+                    0)
+        self.assertEqual(real, expected)
+
+    @mock.patch('check_mdadm.get_devices')
+    @mock.patch('subprocess.Popen')
+    def test_parse_output_wait_warning(self, mdadm_details, devices):
+        class Test_Popen(object):
+            def __init__(cls):
+                test_output = os.path.join(os.getcwd(),
+                                           'tests',
+                                           'hw-health-samples',
+                                           'mdadm.output')
+                cls.stdout = io.FileIO(test_output)
+                cls.wait = lambda: 2
+
+        devices.return_value = set(['/dev/md0', '/dev/md1', '/dev/md2'])
+        mdadm_details.return_value = Test_Popen()
+        real = check_mdadm.parse_output()
+        expected = ('WARNING: mdadm returned exit status 2',
+                    1)
+        self.assertEqual(real, expected)
+
+    @mock.patch('check_mdadm.get_devices')
+    def test_parse_output_nodevices(self, devices):
+        devices.return_value = set()
+        real = check_mdadm.parse_output()
+        expected = ('WARNING: unexpectedly checked no devices', 1)
+        self.assertEqual(real, expected)
+
+    @mock.patch('check_mdadm.get_devices')
+    @mock.patch('subprocess.Popen')
+    def test_parse_output_mdadm_warning(self, mdadm_details, devices):
+        devices.return_value = set(['/dev/md0', '/dev/md1', '/dev/md2'])
+        mdadm_details.side_effect = Exception('ugly error')
+        real = check_mdadm.parse_output()
+        expected = ('WARNING: error executing mdadm: ugly error', 1)
+        self.assertEqual(real, expected)
+
+    @mock.patch('check_mdadm.get_devices')
+    @mock.patch('subprocess.Popen')
+    def test_parse_output_degraded(self, mdadm_details, devices):
+        class Test_Popen(object):
+            def __init__(cls):
+                test_output = os.path.join(os.getcwd(),
+                                           'tests',
+                                           'hw-health-samples',
+                                           'mdadm.output.critical')
+                cls.stdout = io.FileIO(test_output)
+                cls.wait = lambda: 0
+
+        devices.return_value = set(['/dev/md0', '/dev/md1', '/dev/md2'])
+        mdadm_details.return_value = Test_Popen()
+        real = check_mdadm.parse_output()
+        expected = ('CRITICAL: /dev/md0 ok; /dev/md1 degraded, Active[1],'
+                    ' Failed[1], Spare[0], Working[1]; /dev/md3 ok;'
+                    ' /dev/md2 ok',
+                    2)
+        self.assertEqual(real, expected)
diff --git a/src/tests/unit/test_check_megacli.py b/src/tests/unit/test_check_megacli.py
new file mode 100644
index 0000000..54ed500
--- /dev/null
+++ b/src/tests/unit/test_check_megacli.py
@@ -0,0 +1,19 @@
+# import mock
+import unittest
+import sys
+import os
+
+sys.path.append('files/megaraid')
+import check_megacli
+
+
+class TestCheckMegaCLI(unittest.TestCase):
+    def test_parse_output(self):
+        check_megacli.INPUT_FILE = os.path.join(os.getcwd(),
+                                                'tests',
+                                                'hw-health-samples',
+                                                'megacli.output.1')
+        real = check_megacli.parse_output()
+        expected = {'critical': False,
+                    'OK': 'OK: Optimal, ldrives[1], pdrives[4]'}
+        self.assertEqual(real, expected)
diff --git a/src/tests/unit/test_check_sas2ircu.py b/src/tests/unit/test_check_sas2ircu.py
new file mode 100644
index 0000000..af4a674
--- /dev/null
+++ b/src/tests/unit/test_check_sas2ircu.py
@@ -0,0 +1,18 @@
+# import mock
+import unittest
+import sys
+import os
+
+sys.path.append('files/mpt')
+import check_sas2ircu
+
+
+class TestCheckMegaCLI(unittest.TestCase):
+    def test_parse_output(self):
+        check_sas2ircu.INPUT_FILE = os.path.join(os.getcwd(),
+                                                 'tests',
+                                                 'hw-health-samples',
+                                                 'sas2ircu.huawei.output.1')
+        real = check_sas2ircu.parse_output()
+        expected = ('Ready[1:0,1:1,1:2,1:3,1:4,1:5,1:6,1:7]', False)
+        self.assertEqual(real, expected)
diff --git a/src/tests/unit/test_check_sas3ircu.py b/src/tests/unit/test_check_sas3ircu.py
new file mode 100644
index 0000000..cadaffb
--- /dev/null
+++ b/src/tests/unit/test_check_sas3ircu.py
@@ -0,0 +1,19 @@
+# import mock
+import unittest
+import sys
+import os
+
+sys.path.append('files/mpt')
+import check_sas3ircu
+
+
+class TestCheckMegaCLI(unittest.TestCase):
+    def test_parse_output(self):
+        _filepath = os.path.join(os.getcwd(),
+                                 'tests',
+                                 'hw-health-samples',
+                                 'sas3ircu.supermicro.output.1')
+        check_sas3ircu.INPUT_FILE = _filepath
+        real = check_sas3ircu.parse_output()
+        expected = ('Okay[323:(1:0,1:1)]; Optimal[1:0,1:1]', False)
+        self.assertEqual(real, expected)
diff --git a/src/tests/unit/test_tooling.py b/src/tests/unit/test_tooling.py
index 78a6790..d2fe1e1 100644
--- a/src/tests/unit/test_tooling.py
+++ b/src/tests/unit/test_tooling.py
@@ -1,17 +1,64 @@
-# import mock
+import mock
 import unittest
+import shutil
 import sys
-# import os
+import os
 # import io
 # import subprocess
 # import charmhelpers.core.host
+# from shutil import copy2
 
 sys.path.append('lib/utils')
 import tooling
 
 
 class TestTooling(unittest.TestCase):
-    def test_install_tools(self):
+    def test_install_tools_notools(self):
         real = tooling.install_tools([])
         expected = False
         self.assertEqual(real, expected)
+
+    def test_install_tools_unsupported_tools(self):
+        real = tooling.install_tools(['unittest1', 'unittest2'])
+        expected = False
+        self.assertEqual(real, expected)
+
+    def test_install_tools_supported_tools(self):
+        real = tooling.install_tools(['megacli', 'sas2ircu'])
+        expected = True
+        self.assertEqual(real, expected)
+
+    def test_install_tools_mdadm(self):
+        real = tooling.install_tools(['mdadm'])
+        expected = True
+        self.assertEqual(real, expected)
+
+    @mock.patch('os.environ')
+    @mock.patch('os.path.join')
+    @mock.patch('os.path.exists')
+    def test_get_filepath(self, path_exists, path_join, environ):
+        environ.return_value = {'CHARM_DIR': 'xxx'}
+        path_join.return_value = 'unittest1'
+        path_exists.return_value = True
+        real = tooling._get_filepath('asdfasdf')
+        expected = 'unittest1'
+        self.assertEqual(real, expected)
+
+    def test_configure_tools_notools(self):
+        real = tooling.configure_tools([])
+        expected = False
+        self.assertEqual(real, expected)
+
+    def test_configure_tools_unsupported_tools(self):
+        real = tooling.configure_tools(['unittest1', 'unittest2'])
+        expected = False
+        self.assertEqual(real, expected)
+
+    @mock.patch('shutil.copy2')
+    @mock.patch('tooling._get_filepath')
+    def test_configure_tools_mdadm(self, get_filepath, copy_file):
+        get_filepath.return_value = 'unittest'
+        copy_file.return_value = None
+        real = tooling.configure_tools(['mdadm'])
+        expected = True
+        self.assertEqual(real, expected)

Follow ups