diff --git a/plinth/config.py b/plinth/config.py index c01b302f8..d00b906fb 100644 --- a/plinth/config.py +++ b/plinth/config.py @@ -119,8 +119,10 @@ class DropinConfigs(app_module.FollowerComponent): result_string = Result.PASSED if result else Result.FAILED template = _('Static configuration {etc_path} is setup properly') description = format_lazy(template, etc_path=str(etc_path)) + parameters = {'etc_path': str(etc_path)} results.append( - DiagnosticCheck(check_id, description, result_string)) + DiagnosticCheck(check_id, description, result_string, + parameters)) return results diff --git a/plinth/daemon.py b/plinth/daemon.py index 6f2f4e9e6..16022170d 100644 --- a/plinth/daemon.py +++ b/plinth/daemon.py @@ -106,8 +106,9 @@ class Daemon(app.LeaderComponent): template = gettext_lazy('Service {service_name} is running') description = format_lazy(template, service_name=self.unit) + parameters = {'service_name': self.unit} - return DiagnosticCheck(check_id, description, result) + return DiagnosticCheck(check_id, description, result, parameters) class RelatedDaemon(app.FollowerComponent): @@ -158,7 +159,9 @@ def diagnose_port_listening(port, kind='tcp', listen_address=None): result = _check_port(port, kind, listen_address) + parameters = {'kind': kind, 'port': port} if listen_address: + parameters['listen_address'] = listen_address check_id = f'daemon-listening-address-{kind}-{port}-{listen_address}' template = gettext_lazy( 'Listening on {kind} port {listen_address}:{port}') @@ -170,7 +173,8 @@ def diagnose_port_listening(port, kind='tcp', listen_address=None): description = format_lazy(template, kind=kind, port=port) return DiagnosticCheck(check_id, description, - Result.PASSED if result else Result.FAILED) + Result.PASSED if result else Result.FAILED, + parameters) def _check_port(port, kind='tcp', listen_address=None): @@ -236,9 +240,10 @@ def diagnose_netcat(host, port, input='', negate=False): check_id = f'daemon-netcat-{host}-{port}' description = _('Connect to {host}:{port}') + parameters = {'host': host, 'port': port, 'negate': negate} if negate: check_id = f'daemon-netcat-negate-{host}-{port}' description = _('Cannot connect to {host}:{port}') return DiagnosticCheck(check_id, description.format(host=host, port=port), - result) + result, parameters) diff --git a/plinth/modules/apache/components.py b/plinth/modules/apache/components.py index b14c63fdc..405ff368b 100644 --- a/plinth/modules/apache/components.py +++ b/plinth/modules/apache/components.py @@ -149,7 +149,9 @@ def diagnose_url(url, kind=None, env=None, check_certificate=True, except FileNotFoundError: result = Result.ERROR + parameters = {'url': url} if kind: + parameters['kind'] = kind check_id = f'apache-url-kind-{url}-{kind}' template = gettext_lazy('Access URL {url} on tcp{kind}') description = format_lazy(template, url=url, kind=kind) @@ -158,7 +160,7 @@ def diagnose_url(url, kind=None, env=None, check_certificate=True, template = gettext_lazy('Access URL {url}') description = format_lazy(template, url=url) - return DiagnosticCheck(check_id, description, result) + return DiagnosticCheck(check_id, description, result, parameters) def diagnose_url_on_all(url, expect_redirects=False, **kwargs): diff --git a/plinth/modules/apache/tests/test_components.py b/plinth/modules/apache/tests/test_components.py index 4c575ad2b..ec2ddcf60 100644 --- a/plinth/modules/apache/tests/test_components.py +++ b/plinth/modules/apache/tests/test_components.py @@ -246,17 +246,18 @@ def test_diagnose_url(get_addresses, check): 'wrapper': 'test-wrapper', 'expected_output': 'test-expected' } + parameters = {key: args[key] for key in ['url', 'kind']} check.return_value = True result = diagnose_url(**args) assert result == DiagnosticCheck( 'apache-url-kind-https://localhost/test-4', - 'Access URL https://localhost/test on tcp4', Result.PASSED) + 'Access URL https://localhost/test on tcp4', Result.PASSED, parameters) check.return_value = False result = diagnose_url(**args) assert result == DiagnosticCheck( 'apache-url-kind-https://localhost/test-4', - 'Access URL https://localhost/test on tcp4', Result.FAILED) + 'Access URL https://localhost/test on tcp4', Result.FAILED, parameters) del args['kind'] args['url'] = 'https://{host}/test' @@ -272,14 +273,24 @@ def test_diagnose_url(get_addresses, check): 'numeric': False, 'url_address': 'test-host-2' }] + parameters = [ + { + 'url': 'https://test-host-1/test', + 'kind': '4' + }, + { + 'url': 'https://test-host-2/test', + 'kind': '6' + }, + ] results = diagnose_url_on_all(**args) assert results == [ DiagnosticCheck('apache-url-kind-https://test-host-1/test-4', 'Access URL https://test-host-1/test on tcp4', - Result.PASSED), + Result.PASSED, parameters[0]), DiagnosticCheck('apache-url-kind-https://test-host-2/test-6', 'Access URL https://test-host-2/test on tcp6', - Result.PASSED), + Result.PASSED, parameters[1]), ] diff --git a/plinth/modules/diagnostics/check.py b/plinth/modules/diagnostics/check.py index 02a747b78..aa3babc50 100644 --- a/plinth/modules/diagnostics/check.py +++ b/plinth/modules/diagnostics/check.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later """Diagnostic check data type.""" -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import StrEnum @@ -19,7 +19,8 @@ class Result(StrEnum): @dataclass class DiagnosticCheck: - """A diagnostic check and optional result.""" + """A diagnostic check and optional result and parameters.""" check_id: str description: str result: Result = Result.NOT_DONE + parameters: dict = field(default_factory=dict) diff --git a/plinth/modules/diagnostics/tests/test_check.py b/plinth/modules/diagnostics/tests/test_check.py index 3284988a6..cf1521295 100644 --- a/plinth/modules/diagnostics/tests/test_check.py +++ b/plinth/modules/diagnostics/tests/test_check.py @@ -26,5 +26,12 @@ def test_diagnostic_check(): assert check.check_id == 'some-check-id' assert check.description == 'sample check' assert check.result == Result.NOT_DONE + assert not check.parameters + check = DiagnosticCheck('some-check-id', 'sample check', Result.PASSED) assert check.result == Result.PASSED + assert not check.parameters + + check = DiagnosticCheck('some-check-id', 'sample check', Result.PASSED, + {'key': 'value'}) + assert check.parameters['key'] == 'value' diff --git a/plinth/modules/firewall/components.py b/plinth/modules/firewall/components.py index fe8744ed4..56656d1dd 100644 --- a/plinth/modules/firewall/components.py +++ b/plinth/modules/firewall/components.py @@ -136,7 +136,9 @@ class Firewall(app.FollowerComponent): template = _( 'Port {name} ({details}) available for internal networks') description = format_lazy(template, name=port, details=details) - results.append(DiagnosticCheck(check_id, description, result)) + parameters = {'name': port, 'details': details} + results.append( + DiagnosticCheck(check_id, description, result, parameters)) # External zone if self.is_external: @@ -155,7 +157,9 @@ class Firewall(app.FollowerComponent): ) description = format_lazy(template, name=port, details=details) - results.append(DiagnosticCheck(check_id, description, result)) + parameters = {'name': port, 'details': details} + results.append( + DiagnosticCheck(check_id, description, result, parameters)) return results diff --git a/plinth/modules/firewall/tests/test_components.py b/plinth/modules/firewall/tests/test_components.py index 61c0d3df9..041088a2b 100644 --- a/plinth/modules/firewall/tests/test_components.py +++ b/plinth/modules/firewall/tests/test_components.py @@ -158,19 +158,31 @@ def test_diagnose(get_enabled_services, get_port_details): DiagnosticCheck( 'firewall-port-internal-test-port1', 'Port test-port1 (1234/tcp, 1234/udp) available for internal ' - 'networks', Result.PASSED), + 'networks', Result.PASSED, { + 'name': 'test-port1', + 'details': '1234/tcp, 1234/udp' + }), DiagnosticCheck( 'firewall-port-external-unavailable-test-port1', 'Port test-port1 (1234/tcp, 1234/udp) unavailable for external ' - 'networks', Result.PASSED), + 'networks', Result.PASSED, { + 'name': 'test-port1', + 'details': '1234/tcp, 1234/udp' + }), DiagnosticCheck( 'firewall-port-internal-test-port2', 'Port test-port2 (2345/udp) available for internal networks', - Result.FAILED), + Result.FAILED, { + 'name': 'test-port2', + 'details': '2345/udp' + }), DiagnosticCheck( 'firewall-port-external-unavailable-test-port2', 'Port test-port2 (2345/udp) unavailable for external networks', - Result.FAILED), + Result.FAILED, { + 'name': 'test-port2', + 'details': '2345/udp' + }), ] firewall = Firewall('test-firewall-1', ports=['test-port3', 'test-port4'], @@ -180,19 +192,31 @@ def test_diagnose(get_enabled_services, get_port_details): DiagnosticCheck( 'firewall-port-internal-test-port3', 'Port test-port3 (3456/tcp) available for internal networks', - Result.PASSED), + Result.PASSED, { + 'name': 'test-port3', + 'details': '3456/tcp' + }), DiagnosticCheck( 'firewall-port-external-available-test-port3', 'Port test-port3 (3456/tcp) available for external networks', - Result.PASSED), + Result.PASSED, { + 'name': 'test-port3', + 'details': '3456/tcp' + }), DiagnosticCheck( 'firewall-port-internal-test-port4', 'Port test-port4 (4567/udp) available for internal networks', - Result.FAILED), + Result.FAILED, { + 'name': 'test-port4', + 'details': '4567/udp' + }), DiagnosticCheck( 'firewall-port-external-available-test-port4', 'Port test-port4 (4567/udp) available for external networks', - Result.FAILED), + Result.FAILED, { + 'name': 'test-port4', + 'details': '4567/udp' + }), ] diff --git a/plinth/modules/users/__init__.py b/plinth/modules/users/__init__.py index 3eb21e0b2..2e99cd04a 100644 --- a/plinth/modules/users/__init__.py +++ b/plinth/modules/users/__init__.py @@ -125,8 +125,9 @@ def _diagnose_ldap_entry(search_item): template = _('Check LDAP entry "{search_item}"') description = format_lazy(template, search_item=search_item) + parameters = {'search_item': search_item} - return DiagnosticCheck(check_id, description, result) + return DiagnosticCheck(check_id, description, result, parameters) def _diagnose_nslcd_config(config, key, value): @@ -139,8 +140,9 @@ def _diagnose_nslcd_config(config, key, value): template = _('Check nslcd config "{key} {value}"') description = format_lazy(template, key=key, value=value) + parameters = {'key': key, 'value': value} - return DiagnosticCheck(check_id, description, result) + return DiagnosticCheck(check_id, description, result, parameters) def _diagnose_nsswitch_config(): @@ -169,8 +171,10 @@ def _diagnose_nsswitch_config(): template = _('Check nsswitch config "{database}"') description = format_lazy(template, database=database) + parameters = {'database': database} - results.append(DiagnosticCheck(check_id, description, result)) + results.append( + DiagnosticCheck(check_id, description, result, parameters)) return results diff --git a/plinth/package.py b/plinth/package.py index 2919328f3..ea88bbbf6 100644 --- a/plinth/package.py +++ b/plinth/package.py @@ -214,8 +214,10 @@ class Packages(app_module.FollowerComponent): description = _( 'Package {expression} is not available for ' 'install').format(expression=package_expression) + parameters = {'package_expression': str(package_expression)} results.append( - DiagnosticCheck(check_id, description, Result.FAILED)) + DiagnosticCheck(check_id, description, Result.FAILED, + parameters)) continue result = Result.WARNING @@ -231,7 +233,12 @@ class Packages(app_module.FollowerComponent): '({latest_version})').format( package_name=package_name, latest_version=latest_version) - results.append(DiagnosticCheck(check_id, description, result)) + parameters = { + 'package_name': package_name, + 'latest_version': latest_version, + } + results.append( + DiagnosticCheck(check_id, description, result, parameters)) return results diff --git a/plinth/tests/test_config.py b/plinth/tests/test_config.py index c48a0eeb6..58c19ce02 100644 --- a/plinth/tests/test_config.py +++ b/plinth/tests/test_config.py @@ -165,11 +165,12 @@ def test_dropin_config_diagnose_symlinks(dropin_configs, tmp_path): DiagnosticCheck( f'dropin-config-{tmp_path}/etc/test/path1', f'Static configuration {tmp_path}/etc/test/path1 is setup ' - 'properly', Result.FAILED), + 'properly', Result.FAILED, + {'etc_path': f'{tmp_path}/etc/test/path1'}), DiagnosticCheck( f'dropin-config-{tmp_path}/etc/path2', f'Static configuration {tmp_path}/etc/path2 is setup properly', - Result.FAILED), + Result.FAILED, {'etc_path': f'{tmp_path}/etc/path2'}), ] # Proper symlinks exist diff --git a/plinth/tests/test_daemon.py b/plinth/tests/test_daemon.py index 7f84ee3d0..6f3dd06b8 100644 --- a/plinth/tests/test_daemon.py +++ b/plinth/tests/test_daemon.py @@ -151,7 +151,8 @@ def test_diagnose(port_listening, service_is_running, daemon): results = daemon.diagnose() assert results == [ DiagnosticCheck('daemon-running-test-unit', - 'Service test-unit is running', Result.PASSED), + 'Service test-unit is running', Result.PASSED, + {'service_name': 'test-unit'}), DiagnosticCheck('test-result-8273-tcp4', 'test-result-8273-tcp4', Result.PASSED), DiagnosticCheck('test-result-345-udp', 'test-result-345-udp', @@ -216,21 +217,35 @@ def test_diagnose_port_listening(connections): results = diagnose_port_listening(1234) assert results == DiagnosticCheck('daemon-listening-tcp-1234', 'Listening on tcp port 1234', - Result.PASSED) + Result.PASSED, { + 'kind': 'tcp', + 'port': 1234 + }) results = diagnose_port_listening(1234, 'tcp', '0.0.0.0') assert results == DiagnosticCheck( 'daemon-listening-address-tcp-1234-0.0.0.0', - 'Listening on tcp port 0.0.0.0:1234', Result.PASSED) + 'Listening on tcp port 0.0.0.0:1234', Result.PASSED, { + 'kind': 'tcp', + 'port': 1234, + 'listen_address': '0.0.0.0' + }) # Failed results results = diagnose_port_listening(4321) assert results == DiagnosticCheck('daemon-listening-tcp-4321', 'Listening on tcp port 4321', - Result.FAILED) + Result.FAILED, { + 'kind': 'tcp', + 'port': 4321 + }) results = diagnose_port_listening(4321, 'tcp', '0.0.0.0') assert results == DiagnosticCheck( 'daemon-listening-address-tcp-4321-0.0.0.0', - 'Listening on tcp port 0.0.0.0:4321', Result.FAILED) + 'Listening on tcp port 0.0.0.0:4321', Result.FAILED, { + 'kind': 'tcp', + 'port': 4321, + 'listen_address': '0.0.0.0' + }) # Check if psutil call is being made with right argument results = diagnose_port_listening(1234, 'tcp') @@ -278,29 +293,32 @@ def test_diagnose_netcat(popen): """Test running diagnostic test using netcat.""" popen().returncode = 0 result = diagnose_netcat('test-host', 3300, input='test-input') + parameters = {'host': 'test-host', 'port': 3300, 'negate': False} assert result == DiagnosticCheck('daemon-netcat-test-host-3300', 'Connect to test-host:3300', - Result.PASSED) + Result.PASSED, parameters) assert popen.mock_calls[1][1] == (['nc', 'test-host', '3300'], ) assert popen.mock_calls[2] == call().communicate(input=b'test-input') result = diagnose_netcat('test-host', 3300, input='test-input', negate=True) + parameters2 = parameters.copy() + parameters2['negate'] = True assert result == DiagnosticCheck('daemon-netcat-negate-test-host-3300', 'Cannot connect to test-host:3300', - Result.FAILED) + Result.FAILED, parameters2) popen().returncode = 1 result = diagnose_netcat('test-host', 3300, input='test-input') assert result == DiagnosticCheck('daemon-netcat-test-host-3300', 'Connect to test-host:3300', - Result.FAILED) + Result.FAILED, parameters) result = diagnose_netcat('test-host', 3300, input='test-input', negate=True) assert result == DiagnosticCheck('daemon-netcat-negate-test-host-3300', 'Cannot connect to test-host:3300', - Result.PASSED) + Result.PASSED, parameters2) def test_related_daemon_initialization(): diff --git a/plinth/tests/test_package.py b/plinth/tests/test_package.py index 633fbb450..19dfefc59 100644 --- a/plinth/tests/test_package.py +++ b/plinth/tests/test_package.py @@ -242,20 +242,23 @@ def test_diagnose(cache): assert results == [ DiagnosticCheck('package-available-package1', 'Package package1 is not available for install', - Result.FAILED), + Result.FAILED, {'package_expression': 'package1'}), DiagnosticCheck('package-latest-package2', 'Package package2 is the latest version (2.0)', - Result.PASSED), + Result.PASSED, + {'package_name': 'package2', 'latest_version': '2.0'}), DiagnosticCheck('package-latest-package3', 'Package package3 is the latest version (3.0)', - Result.WARNING), + Result.WARNING, + {'package_name': 'package3', 'latest_version': '3.0'}), DiagnosticCheck( 'package-available-package4 | package5', 'Package package4 | package5 is not available for install', - Result.FAILED), + Result.FAILED, {'package_expression': 'package4 | package5'}), DiagnosticCheck('package-latest-package7', 'Package package7 is the latest version (4.0)', - Result.PASSED), + Result.PASSED, + {'package_name': 'package7', 'latest_version': '4.0'}), ]