nagios-charmers team mailing list archive
-
nagios-charmers team
-
Mailing list archive
-
Message #00611
[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