diff --git a/maintenance_service_http_monitoring/README.md b/maintenance_service_http_monitoring/README.md
index 603b247..eeee667 100644
--- a/maintenance_service_http_monitoring/README.md
+++ b/maintenance_service_http_monitoring/README.md
@@ -98,10 +98,11 @@ On service instances, you can see:
## Automatic Maintenance Requests
When a service fails HTTP checks:
-- A corrective maintenance request is created with prefix "[HTTP KO]"
-- The request is linked to the equipment
-- Only one request per equipment per day is created
-- The request description lists all failing services
+- A corrective maintenance request is created per failing service, named
+ ``[HTTP KO] {service_url}``
+- The request description includes the error detail: the HTTP status code,
+ or a network error label (timeout / DNS / SSL) when no HTTP response was received
+- No duplicate is created as long as an open request already exists for that service
- A **double-check** is performed before creating the request: the service is retested
after 2 seconds. A maintenance request is only created if the service fails **both**
checks, reducing noise from transient HTTP errors
diff --git a/maintenance_service_http_monitoring/models/maintenance_equipment.py b/maintenance_service_http_monitoring/models/maintenance_equipment.py
index 21b107f..2a409fa 100644
--- a/maintenance_service_http_monitoring/models/maintenance_equipment.py
+++ b/maintenance_service_http_monitoring/models/maintenance_equipment.py
@@ -27,11 +27,6 @@ class MaintenanceEquipment(models.Model):
readonly=True,
help="Computed from start + configured duration",
)
- http_maintenance_request = fields.Many2one(
- "maintenance.request",
- string="HTTP Maintenance Request",
- readonly=True,
- )
def action_activate_maintenance_mode(self):
for rec in self:
@@ -72,31 +67,30 @@ class MaintenanceEquipment(models.Model):
)
expired.action_deactivate_maintenance_mode()
- def create_http_maintenance_request(self, ko_services):
+ def create_http_maintenance_request(self, ko_service):
+ """
+ Create or return the open maintenance.request for a single KO service.
+
+ Deduplication: if ko_service already has an open (non-done) request,
+ return it without creating a new one.
+ """
self.ensure_one()
- today = fields.Date.context_today(self)
- name = f"[HTTP KO] {self.name}"
- domain = [
- ("name", "=", name),
- ("equipment_id", "=", self.id),
- ("maintenance_type", "=", "corrective"),
- ("create_date", ">=", f"{today} 00:00:00"),
- ("create_date", "<=", f"{today} 23:59:59"),
- ]
- existing = self.env["maintenance.request"].search(domain, limit=1)
- # Check if a task with same name already exist for the day, if it’s the case : skip
- if existing:
- self.http_maintenance_request = existing.id
+ existing = ko_service.http_maintenance_request
+ if existing and not existing.stage_id.done:
return existing
- request = self.http_maintenance_request
- if request and not request.stage_id.done:
- return request
+ status_code = ko_service.last_http_status_code
+ if status_code == -1:
+ error_detail = "Erreur réseau (timeout / DNS / SSL)"
+ else:
+ error_detail = f"HTTP {status_code}"
+ name = f"[HTTP KO] {ko_service.service_url}"
+ description = f"Service KO: {ko_service.service_url}\n{error_detail}"
vals = {
"name": name,
"equipment_id": self.id,
"priority": "2",
"maintenance_type": "corrective",
- "description": self._build_ko_services_description(ko_services),
+ "description": description,
}
if self.employee_id:
vals["employee_id"] = self.employee_id.id
@@ -109,8 +103,8 @@ class MaintenanceEquipment(models.Model):
if team:
vals["maintenance_team_id"] = team.id
request = self.env["maintenance.request"].create(vals)
- self.http_maintenance_request = request.id
- self._notify_webhook(request, ko_services)
+ ko_service.http_maintenance_request = request.id
+ self._notify_webhook(request, ko_service)
return request
def _notify_webhook(self, request, ko_service):
@@ -156,7 +150,3 @@ class MaintenanceEquipment(models.Model):
request.id,
e,
)
-
- def _build_ko_services_description(self, ko_services):
- lines = [f"Service KO: {s.service_url or s.name}" for s in ko_services]
- return "\n".join(lines)
diff --git a/maintenance_service_http_monitoring/models/service_instance.py b/maintenance_service_http_monitoring/models/service_instance.py
index 72ecd5c..2360a1f 100644
--- a/maintenance_service_http_monitoring/models/service_instance.py
+++ b/maintenance_service_http_monitoring/models/service_instance.py
@@ -31,6 +31,11 @@ class ServiceInstance(models.Model):
readonly=True,
default=True,
)
+ http_maintenance_request = fields.Many2one(
+ "maintenance.request",
+ string="HTTP Maintenance Request",
+ readonly=True,
+ )
def check_http_status(self):
"""
@@ -98,4 +103,4 @@ class ServiceInstance(models.Model):
ko_confirmed = ko_after_pass1.check_http_status()
for service in ko_confirmed:
- service.equipment_id.create_http_maintenance_request([service])
+ service.equipment_id.create_http_maintenance_request(service)
diff --git a/maintenance_service_http_monitoring/tests/test_http_monitoring.py b/maintenance_service_http_monitoring/tests/test_http_monitoring.py
index 2040f56..e74500c 100644
--- a/maintenance_service_http_monitoring/tests/test_http_monitoring.py
+++ b/maintenance_service_http_monitoring/tests/test_http_monitoring.py
@@ -55,7 +55,7 @@ class TestHttpMonitoring(TransactionCase):
self.assertIsNotNone(self.service_instance.last_http_check_date)
# ------------------------------------------------------------------
- # Test 2 -- Two KO passes -> maintenance.request created
+ # Test 2 -- Two KO passes -> maintenance.request created on the service
# ------------------------------------------------------------------
def test_http_500_creates_maintenance_request(self):
with (
@@ -69,11 +69,13 @@ class TestHttpMonitoring(TransactionCase):
self.assertFalse(self.service_instance.http_status_ok)
self.assertEqual(self.service_instance.last_http_status_code, 500)
- request = self.equipment.http_maintenance_request
+ request = self.service_instance.http_maintenance_request
self.assertTrue(request)
- self.assertEqual(request.name, f"[HTTP KO] {self.equipment.name}")
+ self.assertEqual(request.name, f"[HTTP KO] {self.service_instance.service_url}")
self.assertEqual(request.priority, "2")
self.assertEqual(request.maintenance_type, "corrective")
+ self.assertIn("HTTP 500", request.description)
+ self.assertIn(self.service_instance.service_url, request.description)
# ------------------------------------------------------------------
# Test 3 -- Network error -> KO with code -1
@@ -99,7 +101,7 @@ class TestHttpMonitoring(TransactionCase):
mock_requests.exceptions.RequestException = Exception
self.env["service.instance"].cron_check_http_services()
- request_1 = self.equipment.http_maintenance_request
+ request_1 = self.service_instance.http_maintenance_request
self.assertTrue(request_1)
with (
@@ -110,7 +112,7 @@ class TestHttpMonitoring(TransactionCase):
mock_requests.exceptions.RequestException = Exception
self.env["service.instance"].cron_check_http_services()
- self.assertEqual(self.equipment.http_maintenance_request, request_1)
+ self.assertEqual(self.service_instance.http_maintenance_request, request_1)
self.assertEqual(
self.env["maintenance.request"].search_count(
[("equipment_id", "=", self.equipment.id)]
@@ -247,11 +249,9 @@ class TestHttpMonitoring(TransactionCase):
mock_requests.exceptions.RequestException = Exception
self.env["service.instance"].cron_check_http_services()
- # Final status reflects pass 2 result
self.assertTrue(self.service_instance.http_status_ok)
self.assertEqual(self.service_instance.last_http_status_code, 200)
- # No maintenance.request must have been created
- self.assertFalse(self.equipment.http_maintenance_request)
+ self.assertFalse(self.service_instance.http_maintenance_request)
self.assertEqual(
self.env["maintenance.request"].search_count(
[("equipment_id", "=", self.equipment.id)]
@@ -271,12 +271,44 @@ class TestHttpMonitoring(TransactionCase):
mock_requests.exceptions.RequestException = Exception
self.env["service.instance"].cron_check_http_services()
- # sleep must have been called between the two passes
mock_sleep.assert_called_once_with(2)
- # requests.get must have been called twice (pass 1 + pass 2)
self.assertEqual(mock_requests.get.call_count, 2)
- # Final status is KO
self.assertFalse(self.service_instance.http_status_ok)
self.assertEqual(self.service_instance.last_http_status_code, 503)
- # maintenance.request created
- self.assertTrue(self.equipment.http_maintenance_request)
+ self.assertTrue(self.service_instance.http_maintenance_request)
+
+ # ------------------------------------------------------------------
+ # Test 14 -- 2 KO services on same equipment -> 2 distinct requests
+ # ------------------------------------------------------------------
+ def test_two_ko_services_same_equipment_create_two_requests(self):
+ service2 = self.env["service"].create({"name": "Test Service 2"})
+ service_instance2 = self.env["service.instance"].create(
+ {
+ "equipment_id": self.equipment.id,
+ "service_id": service2.id,
+ "service_url": "https://other.example.com",
+ }
+ )
+
+ with (
+ patch(SERVICE_INSTANCE_REQUESTS) as mock_requests,
+ patch(SERVICE_INSTANCE_SLEEP),
+ ):
+ mock_requests.get.return_value = _mock_response(500)
+ mock_requests.exceptions.RequestException = Exception
+ self.env["service.instance"].cron_check_http_services()
+
+ req1 = self.service_instance.http_maintenance_request
+ req2 = service_instance2.http_maintenance_request
+
+ self.assertTrue(req1)
+ self.assertTrue(req2)
+ self.assertNotEqual(req1, req2)
+ self.assertEqual(req1.name, f"[HTTP KO] {self.service_instance.service_url}")
+ self.assertEqual(req2.name, f"[HTTP KO] {service_instance2.service_url}")
+ self.assertEqual(
+ self.env["maintenance.request"].search_count(
+ [("equipment_id", "=", self.equipment.id)]
+ ),
+ 2,
+ )
diff --git a/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml b/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml
index 9031d62..16a49eb 100644
--- a/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml
+++ b/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml
@@ -36,7 +36,6 @@
-
diff --git a/maintenance_service_http_monitoring/views/service_instance_views.xml b/maintenance_service_http_monitoring/views/service_instance_views.xml
index 9b89a90..4536ee0 100644
--- a/maintenance_service_http_monitoring/views/service_instance_views.xml
+++ b/maintenance_service_http_monitoring/views/service_instance_views.xml
@@ -17,6 +17,7 @@
+