diff --git a/cmk/gui/painter/v0/painters.py b/cmk/gui/painter/v0/painters.py index 2c198d3bd4c..3636b18cdd1 100644 --- a/cmk/gui/painter/v0/painters.py +++ b/cmk/gui/painter/v0/painters.py @@ -5835,6 +5835,19 @@ def render(self, row: Row, cell: Cell, user: LoggedInUser) -> CellSpec: return ("", row["log_options"]) +def _log_comment_min_fields(log_type: str) -> int: + """Minimum number of ";"-separated fields in a notification log line + before a trailing comment field is present. + + Host notification log lines have one field fewer than service + notification log lines (they lack the ";" segment), so the + threshold below which no comment field can be present differs between + the two. See cmk.events.log_to_history._format_notification_message, + which is the counterpart producing these log lines. + """ + return 6 if "SERVICE" in log_type else 5 + + class PainterLogComment(Painter): @property @override @@ -5852,14 +5865,14 @@ def short_title(self, cell: Cell) -> str: @property @override def columns(self) -> Sequence[ColumnName]: - return ["log_options"] + return ["log_options", "log_type"] @override def render(self, row: Row, cell: Cell, user: LoggedInUser) -> CellSpec: msg = row["log_options"] if ";" in msg: parts = msg.split(";") - if len(parts) > 6: + if len(parts) > _log_comment_min_fields(row.get("log_type", "")): return ("", parts[-1]) return ("", "") diff --git a/tests/unit/cmk/gui/plugins/views/test_painters.py b/tests/unit/cmk/gui/plugins/views/test_painters.py index 31aa76dbe4b..96d9a766844 100644 --- a/tests/unit/cmk/gui/plugins/views/test_painters.py +++ b/tests/unit/cmk/gui/plugins/views/test_painters.py @@ -23,7 +23,7 @@ from cmk.gui.http import request from cmk.gui.logged_in import user from cmk.gui.painter.v0 import all_painters -from cmk.gui.painter.v0.painters import _paint_custom_notes +from cmk.gui.painter.v0.painters import _log_comment_min_fields, _paint_custom_notes from cmk.gui.type_defs import ColumnSpec, DynamicIconName, Row from cmk.gui.utils.html import HTML from cmk.gui.utils.roles import UserPermissions @@ -1655,3 +1655,47 @@ def test_paint_custom_notes_file_inclusion_and_html_tags( expected_string = str(HTML.without_escaping("
".join(expected_notes))) assert expected_string == notes_as_string + + +@pytest.mark.parametrize( + "log_type, log_options, expected_comment", + [ + # Host notification log lines have no ";" segment, so with + # a comment present they only ever reach 6 fields, not 7. + pytest.param( + "HOST NOTIFICATION RESULT", + "cmkadmin;myhost;CRITICAL;my_plugin;some output;the comment", + "the comment", + id="host-with-comment", + ), + pytest.param( + "HOST NOTIFICATION RESULT", + "cmkadmin;myhost;CRITICAL;my_plugin;some output", + "", + id="host-without-comment", + ), + pytest.param( + "SERVICE NOTIFICATION RESULT", + "cmkadmin;myhost;My Service;CRITICAL;my_plugin;some output;the comment", + "the comment", + id="service-with-comment", + ), + pytest.param( + "SERVICE NOTIFICATION RESULT", + "cmkadmin;myhost;My Service;CRITICAL;my_plugin;some output", + "", + id="service-without-comment", + ), + ], +) +def test_log_comment_field_count( + log_type: str, log_options: str, expected_comment: str +) -> None: + # Regression test for the comment field being silently dropped for host + # notifications: PainterLogComment.render used a hardcoded threshold of + # 6 fields that only matches the (one field longer) service notification + # log line layout. + parts = log_options.split(";") + min_fields = _log_comment_min_fields(log_type) + actual_comment = parts[-1] if len(parts) > min_fields else "" + assert actual_comment == expected_comment