diff --git a/cmk/gui/crash_reporting/__init__.py b/cmk/gui/crash_reporting/__init__.py index d6a1480c4f6..b3d33425583 100644 --- a/cmk/gui/crash_reporting/__init__.py +++ b/cmk/gui/crash_reporting/__init__.py @@ -15,12 +15,20 @@ from .views import ( CommandDeleteCrashReports, DataSourceCrashReports, + PainterCrashCheckName, PainterCrashException, + PainterCrashHost, PainterCrashIdent, + PainterCrashItem, + PainterCrashServiceDescription, PainterCrashSource, PainterCrashTime, PainterCrashType, PainterCrashVersion, + SorterCrashCheckName, + SorterCrashHost, + SorterCrashItem, + SorterCrashServiceDescription, SorterCrashTime, ) @@ -36,13 +44,21 @@ def register( ) -> None: crash_reporting_pages.register(page_registry) data_source_registry.register(DataSourceCrashReports) + sorter_registry.register(SorterCrashCheckName) + sorter_registry.register(SorterCrashHost) + sorter_registry.register(SorterCrashItem) + sorter_registry.register(SorterCrashServiceDescription) sorter_registry.register(SorterCrashTime) command_registry.register(CommandDeleteCrashReports) + painter_registry.register(PainterCrashCheckName) painter_registry.register(PainterCrashException) + painter_registry.register(PainterCrashHost) painter_registry.register(PainterCrashIdent) + painter_registry.register(PainterCrashItem) + painter_registry.register(PainterCrashServiceDescription) + painter_registry.register(PainterCrashSource) painter_registry.register(PainterCrashTime) painter_registry.register(PainterCrashType) - painter_registry.register(PainterCrashSource) painter_registry.register(PainterCrashVersion) config_variable_registry.register(ConfigVariableCrashReportTarget) config_variable_registry.register(ConfigVariableCrashReportURL) diff --git a/cmk/gui/crash_reporting/views.py b/cmk/gui/crash_reporting/views.py index da4cc5c3333..b6e3908af2a 100644 --- a/cmk/gui/crash_reporting/views.py +++ b/cmk/gui/crash_reporting/views.py @@ -37,7 +37,7 @@ CommandGroupVarious, PERMISSION_SECTION_ACTION, ) -from cmk.gui.views.sorter import cmp_simple_number, Sorter +from cmk.gui.views.sorter import cmp_simple_number, cmp_simple_string, Sorter from cmk.gui.visuals.filter import Filter from .helpers import local_files_involved_in_crash @@ -107,6 +107,30 @@ def parse_rows(self, rows: Iterable[Row]) -> Iterable[Row]: "crash_exc_type": crash_info_raw["exc_type"], "crash_exc_value": crash_info_raw["exc_value"], "crash_exc_traceback": crash_info_raw["exc_traceback"], + **( + {"crash_host": crash_info_raw["details"]["host"]} + if isinstance(crash_info_raw.get("details"), dict) + and crash_info_raw["details"].get("host") + else {} + ), + **( + {"crash_item": crash_info_raw["details"]["item"]} + if isinstance(crash_info_raw.get("details"), dict) + and crash_info_raw["details"].get("item") + else {} + ), + **( + {"crash_check_type": crash_info_raw["details"]["check_type"]} + if isinstance(crash_info_raw.get("details"), dict) + and crash_info_raw["details"].get("check_type") + else {} + ), + **( + {"crash_service_description": crash_info_raw["details"]["description"]} + if isinstance(crash_info_raw.get("details"), dict) + and crash_info_raw["details"].get("description") + else {} + ), } def get_crash_report_rows( @@ -384,3 +408,180 @@ def command_delete_crash_report_action( action=command_delete_crash_report_action, affected_output_cb=command_delete_crash_report_affected, ) + + +class PainterCrashHost(Painter): + @property + def ident(self) -> str: + return "crash_host" + + def title(self, cell): + return _("Crash Host") + + def short_title(self, cell): + return _("Host") + + @property + def columns(self) -> Sequence[ColumnName]: + return ["crash_host"] + + def render(self, row: Row, cell: Cell, user: LoggedInUser) -> CellSpec: + if not row.get("crash_host"): + return None, "" + + url = makeuri_contextless( + self.request, + [ + ("host", row["crash_host"]), + ("site", row["site"]), + ("view_name", "host"), + ], + filename="view.py", + ) + return None, HTMLWriter.render_a(row["crash_host"], href=url) + + +class PainterCrashItem(Painter): + @property + def ident(self) -> str: + return "crash_item" + + def title(self, cell): + return _("Crash Service Item") + + def short_title(self, cell): + return _("Item") + + @property + def columns(self) -> Sequence[ColumnName]: + return ["crash_item"] + + def render(self, row: Row, cell: Cell, user: LoggedInUser) -> CellSpec: + return None, row.get("crash_item", "") + + +class PainterCrashCheckName(Painter): + @property + def ident(self) -> str: + return "crash_check_type" + + def title(self, cell): + return _("Crash Check Name") + + def short_title(self, cell): + return _("Check") + + @property + def columns(self) -> Sequence[ColumnName]: + return ["crash_check_type"] + + def render(self, row: Row, cell: Cell, user: LoggedInUser) -> CellSpec: + return None, row.get("crash_check_type", "") + + +class PainterCrashServiceDescription(Painter): + @property + def ident(self) -> str: + return "crash_service_description" + + def title(self, cell): + return _("Crash Service Description") + + def short_title(self, cell): + return _("Service") + + @property + def columns(self) -> Sequence[ColumnName]: + return ["crash_service_description"] + + def render(self, row: Row, cell: Cell, user: LoggedInUser) -> CellSpec: + if not row.get("crash_service_description"): + return None, "" + + url = makeuri_contextless( + self.request, + [ + ("host", row["crash_host"]), + ("site", row["site"]), + ("view_name", "service"), + ("service", row["crash_service_description"]), + ], + filename="view.py", + ) + return None, HTMLWriter.render_a(row["crash_service_description"], href=url) + + +def _sort_crash_host( + r1: Row, + r2: Row, + *, + parameters: Mapping[str, Any] | None, + config: Config, + request: Request, +) -> int: + return cmp_simple_string("crash_host", r1, r2) + + +SorterCrashHost = Sorter( + ident="crash_host", + title=_l("Crash Host"), + columns=["crash_host"], + sort_function=_sort_crash_host, +) + + +def _sort_crash_item( + r1: Row, + r2: Row, + *, + parameters: Mapping[str, Any] | None, + config: Config, + request: Request, +) -> int: + return cmp_simple_string("crash_item", r1, r2) + + +SorterCrashItem = Sorter( + ident="crash_item", + title=_l("Crash Item"), + columns=["crash_item"], + sort_function=_sort_crash_item, +) + + +def _sort_crash_check_type( + r1: Row, + r2: Row, + *, + parameters: Mapping[str, Any] | None, + config: Config, + request: Request, +) -> int: + return cmp_simple_string("crash_check_type", r1, r2) + + +SorterCrashCheckName = Sorter( + ident="crash_check_type", + title=_l("Crash Check Name"), + columns=["crash_check_type"], + sort_function=_sort_crash_check_type, +) + + +def _sort_crash_service_description( + r1: Row, + r2: Row, + *, + parameters: Mapping[str, Any] | None, + config: Config, + request: Request, +) -> int: + return cmp_simple_string("crash_service_description", r1, r2) + + +SorterCrashServiceDescription = Sorter( + ident="crash_service_description", + title=_l("Crash Service Description"), + columns=["crash_service_description"], + sort_function=_sort_crash_service_description, +) diff --git a/cmk/gui/views/builtin_views.py b/cmk/gui/views/builtin_views.py index 3a32a5d1121..3e5e460554c 100644 --- a/cmk/gui/views/builtin_views.py +++ b/cmk/gui/views/builtin_views.py @@ -5196,6 +5196,8 @@ def _simple_host_view(custom_attributes, add_context=None): "num_columns": 1, "painters": [ ColumnSpec(name="crash_ident"), + ColumnSpec(name="crash_host"), + ColumnSpec(name="crash_service_description"), ColumnSpec(name="crash_type"), ColumnSpec(name="crash_source"), ColumnSpec(name="crash_version"), diff --git a/tests/unit/cmk/gui/plugins/views/test_painters.py b/tests/unit/cmk/gui/plugins/views/test_painters.py index 8c59faf8b1e..ea9ec5e79f6 100644 --- a/tests/unit/cmk/gui/plugins/views/test_painters.py +++ b/tests/unit/cmk/gui/plugins/views/test_painters.py @@ -100,6 +100,10 @@ def test_registered_painters() -> None: "comment_id", "comment_time", "comment_what", + "crash_host", + "crash_item", + "crash_service_description", + "crash_check_type", "crash_exception", "crash_ident", "crash_time",