diff --git a/tests/inventory/test_compliance_checks_views.py b/tests/inventory/test_compliance_checks_views.py
index 69d3fe8896..e8d355a468 100644
--- a/tests/inventory/test_compliance_checks_views.py
+++ b/tests/inventory/test_compliance_checks_views.py
@@ -515,7 +515,7 @@ def test_machine_no_compliance_checks(self):
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance checks (0)")
+ self.assertContains(response, "
Compliance checks
")
def test_machine_no_tags_no_compliance_checks_in_scope(self):
self._login(
@@ -528,7 +528,7 @@ def test_machine_no_tags_no_compliance_checks_in_scope(self):
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance checks (0)")
+ self.assertContains(response, "Compliance checks
")
def test_machine_with_tag_no_compliance_checks_in_scope(self):
self._login(
@@ -543,7 +543,7 @@ def test_machine_with_tag_no_compliance_checks_in_scope(self):
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance checks (0)")
+ self.assertContains(response, "Compliance checks
")
def test_machine_source_mismatch_no_compliance_checks_in_scope(self):
self._login(
@@ -555,7 +555,7 @@ def test_machine_source_mismatch_no_compliance_checks_in_scope(self):
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance checks (0)")
+ self.assertContains(response, "Compliance checks
")
def test_machine_source_match_platform_missmatch_no_compliance_checks_in_scope(self):
self._login(
@@ -567,7 +567,7 @@ def test_machine_source_match_platform_missmatch_no_compliance_checks_in_scope(s
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance checks (0)")
+ self.assertContains(response, "Compliance checks
")
def test_machine_source_match_one_compliance_checks_in_scope(self):
self._login(
@@ -579,7 +579,8 @@ def test_machine_source_match_one_compliance_checks_in_scope(self):
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance check (1)")
+ self.assertContains(response, "Compliance check
")
+ self.assertContains(response, "0/1 OK, 1 Pending")
self.assertContains(response, cc.compliance_check.name)
cc_redirect_link = reverse("compliance_checks:redirect", args=(cc.compliance_check.pk,))
compliance_check_statuses = response.context["compliance_check_statuses"]
@@ -600,7 +601,8 @@ def test_machine_no_tags_compliance_checks_one_in_scope_one_out_of_scope_pending
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance check (1)")
+ self.assertContains(response, "Compliance check
")
+ self.assertContains(response, "0/1 OK, 1 Pending")
self.assertContains(response, cc_without_tag.compliance_check.name)
self.assertNotContains(response, cc_with_tags.compliance_check.name)
cc_redirect_link = reverse("compliance_checks:redirect", args=(cc_without_tag.compliance_check.pk,))
@@ -623,7 +625,8 @@ def test_machine_no_tags_compliance_checks_one_in_scope_one_out_of_scope_pending
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance check (1)")
+ self.assertContains(response, "Compliance check
")
+ self.assertContains(response, "0/1 OK, 1 Pending")
self.assertContains(response, cc_without_tag.compliance_check.name)
self.assertNotContains(response, cc_with_tags.compliance_check.name)
cc_redirect_link = reverse("compliance_checks:redirect", args=(cc_without_tag.compliance_check.pk,))
@@ -646,7 +649,7 @@ def test_machine_no_tags_compliance_checks_one_in_scope_one_out_of_scope_no_sect
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertNotContains(response, "Compliance check (1)")
+ self.assertNotContains(response, "Compliance check
")
self.assertNotContains(response, cc_without_tag.compliance_check.name)
cc_redirect_link = reverse("compliance_checks:redirect", args=(cc_without_tag.compliance_check.pk,))
self.assertNotContains(response, cc_redirect_link)
@@ -673,7 +676,8 @@ def test_machine_tags_once_compliance_check_in_scope_ok_with_link(self):
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance check (1)")
+ self.assertContains(response, "Compliance check
")
+ self.assertContains(response, "1/1 OK")
self.assertContains(response, cc.compliance_check.name)
cc_redirect_link = reverse("compliance_checks:redirect", args=(cc.compliance_check.pk,))
self.assertContains(response, cc_redirect_link)
@@ -714,7 +718,8 @@ def test_machine_tags_once_compliance_check_in_scope_two_different_statuses_ok_w
response = self.client.get(self.machine.get_absolute_url())
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "inventory/machine_detail.html")
- self.assertContains(response, "Compliance check (1)")
+ self.assertContains(response, "Compliance check
")
+ self.assertContains(response, "1/1 OK")
self.assertContains(response, cc.compliance_check.name)
cc_redirect_link = reverse("compliance_checks:redirect", args=(cc.compliance_check.pk,))
self.assertContains(response, cc_redirect_link)
diff --git a/zentral/contrib/inventory/templates/inventory/machine_detail.html b/zentral/contrib/inventory/templates/inventory/machine_detail.html
index 9716439f95..bdc95789e3 100644
--- a/zentral/contrib/inventory/templates/inventory/machine_detail.html
+++ b/zentral/contrib/inventory/templates/inventory/machine_detail.html
@@ -463,7 +463,12 @@ Extra facts
{% if perms.compliance_checks.view_machinestatus %}
-
Compliance check{{ compliance_check_statuses|length|pluralize }} ({{ compliance_check_statuses|length }})
+
Compliance check{{ compliance_check_total|pluralize }}
+ {% if compliance_check_total %}
+
+ {{ compliance_check_ok }}/{{ compliance_check_total }} OK{% if compliance_check_failed %}, {{ compliance_check_failed }} Failed{% endif %}{% if compliance_check_pending %}, {{ compliance_check_pending }} Pending{% endif %}{% if compliance_check_unknown %}, {{ compliance_check_unknown }} Unknown{% endif %}
+
+ {% endif %}
{% if compliance_check_statuses %}
diff --git a/zentral/contrib/inventory/views.py b/zentral/contrib/inventory/views.py
index 32a2f4d2ff..a6eb045617 100644
--- a/zentral/contrib/inventory/views.py
+++ b/zentral/contrib/inventory/views.py
@@ -16,6 +16,7 @@
from zentral.conf import settings
from zentral.core.compliance_checks import compliance_check_class_from_model
from zentral.core.compliance_checks.forms import ComplianceCheckForm
+from zentral.core.compliance_checks.models import Status
from zentral.core.incidents.models import MachineIncident
from zentral.core.stores.conf import frontend_store, stores
from zentral.core.stores.views import EventsView, FetchEventsView, EventsStoreRedirectView
@@ -535,6 +536,7 @@ def ms_sort_key(t):
# compliance checks
compliance_check_statuses = []
+ cc_total = cc_ok = cc_pending = cc_unknown = cc_failed = 0
if self.request.user.has_perm("compliance_checks.view_machinestatus"):
for cc_model, cc_pk, cc_name, status, status_time in machine.compliance_check_statuses():
cc_url = None
@@ -542,7 +544,21 @@ def ms_sort_key(t):
if self.request.user.has_perms(cc_cls.required_view_permissions):
cc_url = reverse("compliance_checks:redirect", args=(cc_pk,))
compliance_check_statuses.append((cc_url, cc_name, status, status_time))
+ cc_total += 1
+ if status == Status.OK:
+ cc_ok += 1
+ elif status == Status.PENDING:
+ cc_pending += 1
+ elif status == Status.UNKNOWN:
+ cc_unknown += 1
+ elif status == Status.FAILED:
+ cc_failed += 1
context["compliance_check_statuses"] = compliance_check_statuses
+ context["compliance_check_total"] = cc_total
+ context["compliance_check_ok"] = cc_ok
+ context["compliance_check_failed"] = cc_failed
+ context["compliance_check_pending"] = cc_pending
+ context["compliance_check_unknown"] = cc_unknown
# event links
context['show_events_link'] = frontend_store.machine_events