← Back to team overview

nagios-charmers team mailing list archive

[Merge] ~vultaire/hw-health-charm:show-sel-log into hw-health-charm:master

 

Paul Goins has proposed merging ~vultaire/hw-health-charm:show-sel-log into hw-health-charm:master.

Requested reviews:
  Nagios Charm developers (nagios-charmers)

For more details, see:
https://code.launchpad.net/~vultaire/hw-health-charm/+git/hw-health-charm/+merge/373849
-- 
Your team Nagios Charm developers is requested to review the proposed merge of ~vultaire/hw-health-charm:show-sel-log into hw-health-charm:master.
diff --git a/src/actions.yaml b/src/actions.yaml
index 421c35e..2e691b8 100644
--- a/src/actions.yaml
+++ b/src/actions.yaml
@@ -1,2 +1,12 @@
 clear-sel:
   description: Using ipmi-sel, clear the system event log
+show-sel:
+  description: |
+    Using ipmi-sel, show the system event log.
+
+    By default, this will only show events with a state other than "Nominal".
+    If you wish to see all events, specify the show-all parameter.
+  params:
+    show-all:
+      type: boolean
+      description: Show all events, not just the non-nominal ones.
diff --git a/src/actions/actions.py b/src/actions/actions.py
index f27c2ee..c4a3900 100755
--- a/src/actions/actions.py
+++ b/src/actions/actions.py
@@ -18,7 +18,10 @@ import os
 import subprocess
 import sys
 
-from charmhelpers.core.hookenv import action_set, action_fail, log
+from charmhelpers.core.hookenv import action_set, action_get, action_fail, log
+
+
+IPMI_SEL = '/usr/sbin/ipmi-sel'
 
 
 def clear_sel():
@@ -27,7 +30,7 @@ def clear_sel():
     Uses ipmi-sel --post-clear, clears the SEL log and stores the cleared entries
     in action output.
     """
-    command = ['/usr/sbin/ipmi-sel', '--post-clear']
+    command = [IPMI_SEL, '--post-clear']
     try:
         output = subprocess.check_output(command)
         log("Action clear-sel completed, sel log cleared: {}".format(output))
@@ -36,9 +39,38 @@ def clear_sel():
         action_fail("Action failed with {}".format(e))
 
 
+def show_sel():
+    """Action to show the entries in the IPMI system event log.
+
+    By default, this will show all non-nominal events.  If you specify show-all,
+    it will show all events.
+    """
+    show_all = action_get('show-all')
+    command = [IPMI_SEL, '--output-event-state']
+    try:
+        header, body = None, None
+        output = subprocess.check_output(command).decode('UTF-8')
+        lines = output.splitlines()
+        if lines:
+            header, body = lines[0], lines[1:]
+            if not show_all:
+                body = [line for line in body if 'Nominal' not in line]
+        if body:
+            final_output = "\n".join([header] + body)
+        else:
+            final_output = "No matching entries found"
+        log("Action show-sel completed:\n{}".format(final_output))
+        action_set({'message': final_output})
+    except subprocess.CalledProcessError as e:
+        action_fail("Action failed with {}".format(e))
+
+
 # A dictionary of all the defined actions to callables (which take
 # parsed arguments).
-ACTIONS = {"clear-sel": clear_sel}
+ACTIONS = {
+    "clear-sel": clear_sel,
+    "show-sel": show_sel,
+}
 
 
 def main(args):
diff --git a/src/tests/unit/test_actions.py b/src/tests/unit/test_actions.py
index 320d99f..33c67c8 100644
--- a/src/tests/unit/test_actions.py
+++ b/src/tests/unit/test_actions.py
@@ -12,10 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import subprocess
 import unittest
 import unittest.mock as mock
 
-from actions.actions import clear_sel
+from actions.actions import clear_sel, show_sel
 
 
 class ClearSelTestCase(unittest.TestCase):
@@ -29,3 +30,99 @@ class ClearSelTestCase(unittest.TestCase):
         mock_check_call.return_value = None
         clear_sel()
         mock_check_call.assert_called_once_with(['action-set', "message={}".format(sel_output.decode())])
+
+
+class ShowSelTestCase(unittest.TestCase):
+
+    @mock.patch('actions.actions.log')
+    @mock.patch('actions.actions.action_set')
+    @mock.patch('actions.actions.action_get')
+    @mock.patch('subprocess.check_output')
+    def test_empty_output_from_ipmi_sel(
+            self, mock_check_output, mock_action_get, mock_action_set, mock_log):
+        show_all_flag = False
+        output_body = ""
+        expected_output = "No matching entries found"
+        self._test_valid_show_sel_call(show_all_flag, output_body, expected_output,
+                                       mock_check_output, mock_action_get, mock_action_set)
+
+    @mock.patch('actions.actions.log')
+    @mock.patch('actions.actions.action_set')
+    @mock.patch('actions.actions.action_get')
+    @mock.patch('subprocess.check_output')
+    def test_only_nominal_entries_with_show_all_false(
+            self, mock_check_output, mock_action_get, mock_action_set, mock_log):
+        show_all_flag = False
+        output_body = "\n".join([
+            "Header line",
+            "Nominal body line #1",
+            "Nominal body line #2",
+            "Nominal body line #3",
+        ])
+        expected_output = "No matching entries found"
+        self._test_valid_show_sel_call(show_all_flag, output_body, expected_output,
+                                       mock_check_output, mock_action_get, mock_action_set)
+
+    @mock.patch('actions.actions.log')
+    @mock.patch('actions.actions.action_set')
+    @mock.patch('actions.actions.action_get')
+    @mock.patch('subprocess.check_output')
+    def test_only_nominal_entries_with_show_all_true(
+            self, mock_check_output, mock_action_get, mock_action_set, mock_log):
+        show_all_flag = True
+        output_body = "\n".join([
+            "Header line",
+            "Nominal body line #1",
+            "Nominal body line #2",
+            "Nominal body line #3",
+        ])
+        expected_output = output_body
+        self._test_valid_show_sel_call(show_all_flag, output_body, expected_output,
+                                       mock_check_output, mock_action_get, mock_action_set)
+
+    @mock.patch('actions.actions.log')
+    @mock.patch('actions.actions.action_set')
+    @mock.patch('actions.actions.action_get')
+    @mock.patch('subprocess.check_output')
+    def test_non_nominal_entries_present_with_show_all_false(
+            self, mock_check_output, mock_action_get, mock_action_set, mock_log):
+        show_all_flag = False
+        output_body = "\n".join([
+            "Header line",
+            "Nominal body line #1",
+            "Warning line #1",
+            "Critical line #1",
+            "Nominal body line #2",
+            "Nominal body line #3",
+            "Warning line #2",
+        ])
+        expected_output = "\n".join([
+            "Header line",
+            "Warning line #1",
+            "Critical line #1",
+            "Warning line #2",
+        ])
+        self._test_valid_show_sel_call(show_all_flag, output_body, expected_output,
+                                       mock_check_output, mock_action_get, mock_action_set)
+
+    def _test_valid_show_sel_call(self, show_all_flag, output_body, expected_output,
+                                  mock_check_output, mock_action_get, mock_action_set):
+        mock_action_get.return_value = show_all_flag
+        mock_check_output.return_value = output_body.encode()
+        show_sel()
+        self.assertEqual(mock_action_set.call_args[0][0]['message'],
+                         expected_output)
+
+    @mock.patch('actions.actions.action_fail')
+    @mock.patch('actions.actions.action_get')
+    @mock.patch('subprocess.check_output')
+    def test_subprocess_error(self, mock_check_output, mock_action_get, mock_action_fail):
+        show_all_flag = False
+        mock_action_get.return_value = show_all_flag
+        def raise_error(*args, **kwargs):
+            raise subprocess.CalledProcessError(1, ['bogus-cmd'])
+        mock_check_output.side_effect = raise_error
+        show_sel()
+        self.assertEqual(
+            mock_action_fail.call_args[0][0],
+            "Action failed with Command '['bogus-cmd']' returned non-zero exit status 1.")

Follow ups