\ No newline at end of file
+
diff --git a/maintenance_server_monitoring/.gitignore b/maintenance_server_monitoring/.gitignore
deleted file mode 100644
index 6da5887..0000000
--- a/maintenance_server_monitoring/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.*~
-*pyc
diff --git a/maintenance_server_monitoring/README.rst b/maintenance_server_monitoring/README.rst
deleted file mode 100644
index 4e6fa50..0000000
--- a/maintenance_server_monitoring/README.rst
+++ /dev/null
@@ -1,44 +0,0 @@
-======================================
-maintenance_server_monitoring
-======================================
-
-Monitor some data on remote hosts
-
-Installation
-============
-
-Use Odoo normal module installation procedure to install
-``maintenance_server_monitoring``.
-
-Known issues / Roadmap
-======================
-
-None yet.
-
-Bug Tracker
-===========
-
-Bugs are tracked on `our issues website `_. In case of
-trouble, please check there if your issue has already been
-reported. If you spotted it first, help us smashing it by providing a
-detailed and welcomed feedback.
-
-Credits
-=======
-
-Contributors
-------------
-
-* Clément Thomas
-
-Funders
--------
-
-The development of this module has been financially supported by:
-* Elabore (https://elabore.coop)
-
-
-Maintainer
-----------
-
-This module is maintained by Elabore.
diff --git a/maintenance_server_monitoring/__init__.py b/maintenance_server_monitoring/__init__.py
deleted file mode 100644
index cde864b..0000000
--- a/maintenance_server_monitoring/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import models
diff --git a/maintenance_server_monitoring/__manifest__.py b/maintenance_server_monitoring/__manifest__.py
deleted file mode 100644
index af59433..0000000
--- a/maintenance_server_monitoring/__manifest__.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2023 Stéphan Sainléger (Elabore)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-{
- "name": "maintenance_server_monitoring",
- "version": "16.0.1.0.0",
- "author": "Elabore",
- "website": "https://elabore.coop",
- "maintainer": "Clément Thomas",
- "license": "AGPL-3",
- "category": "Tools",
- "summary": "Monitor some data on remote hosts",
- # any module necessary for this one to work correctly
- "depends": [
- "base",
- "maintenance",
- "maintenance_server_ssh"
- ],
- "qweb": [
- # "static/src/xml/*.xml",
- ],
- "external_dependencies": {
- "python": [],
- },
- # always loaded
- "data": [
- "views/maintenance_equipment_views.xml",
- "data/cron.xml",
- ],
- # only loaded in demonstration mode
- "demo": [],
- "js": [],
- "css": [],
- "installable": True,
- # Install this module automatically if all dependency have been previously
- # and independently installed. Used for synergetic or glue modules.
- "auto_install": False,
- "application": False,
-}
\ No newline at end of file
diff --git a/maintenance_server_monitoring/data/cron.xml b/maintenance_server_monitoring/data/cron.xml
deleted file mode 100644
index f8dd274..0000000
--- a/maintenance_server_monitoring/data/cron.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- Server Monitoring : check all equipments
-
- code
- model.cron_monitoring_test()
- 1
- minutes
- -1
-
-
-
diff --git a/maintenance_server_monitoring/models/__init__.py b/maintenance_server_monitoring/models/__init__.py
deleted file mode 100644
index b31f6b6..0000000
--- a/maintenance_server_monitoring/models/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import maintenance_equipment
\ No newline at end of file
diff --git a/maintenance_server_monitoring/models/maintenance_equipment.py b/maintenance_server_monitoring/models/maintenance_equipment.py
deleted file mode 100644
index db91f89..0000000
--- a/maintenance_server_monitoring/models/maintenance_equipment.py
+++ /dev/null
@@ -1,372 +0,0 @@
-from odoo import fields, models, api
-import subprocess
-import sys
-import psutil
-from io import StringIO
-
-LOG_LIMIT = 100000
-
-AVAILABLE_MEMORY_PERCENT_COMMAND = "free | grep Mem | awk '{print $3/$2 * 100.0}'"
-MIN_AVAILABLE_MEMORY_PERCENT_WARNING = 20
-MIN_AVAILABLE_MEMORY_PERCENT_ERROR = 5
-
-USED_DISK_SPACE_COMMAND = "df /srv -h | tail -n +2 | sed -r 's/ +/ /g' | cut -f 5 -d ' ' | cut -f 1 -d %"
-MAX_USED_DISK_SPACE_WARNING = 70
-MAX_USED_DISK_SPACE_ERROR = 90
-
-MAX_PING_MS_WARNING = 1000
-MAX_PING_MS_ERROR = 5000
-
-
-"""
-if you want to add a new test :
- * add new field to MaintenanceEquipment (named {fieldname} below)
- * add a new function named test_{fieldname} which return a filled MonitoringTest class with :
- -> log = logs you want to appear in logs
- -> result = value which will be set to {fieldname}
- -> error = MonitoringTest.ERROR or MonitoringTest.WARNING to generate maintenance request
- ** Note you can use test_ok, test_warning, and test_error functions to simplify code **
- * add requirements if necessary in install_dependencies function
- * call your function in monitoring_test() with a simple launch_test({fieldname}, *args)
- if needed, *args can be passed by parameters to your test function
-
-
-"""
-
-
-class MaintenanceEquipment(models.Model):
- _inherit = 'maintenance.equipment'
-
- last_monitoring_test_date = fields.Datetime('Date of last monitoring test', readonly=True)
-
- #tests
- ping_ok = fields.Boolean("Ping ok", readonly=True)
- available_memory_percent = fields.Float('Percent of available memory', readonly=True)
- used_disk_space = fields.Float('Percent of used disk space', readonly=True)
- ssh_ok = fields.Boolean("SSH ok", readonly=True)
-
- #log
- log = fields.Html("Log", readonly=True)
-
- #maintenance requests
- error_maintenance_request = fields.Many2one('maintenance.request', "Error maintenance request")
- warning_maintenance_request = fields.Many2one('maintenance.request', "Warning maintenance request")
-
-
- class MonitoringTest:
- """Class to make the tests
- """
- WARNING = "warning"
- ERROR = "error"
-
- def __init__(self, name):
- self.name = name # name of the test
- self.result = 0 # result of the test
- self.log = "" # logs of the test
- self.date = fields.Datetime.now() # date of the test
- self.error = "" # errors of the test
-
- def add_to_log(self, text):
- """
- add a new line to logs composed with DATE > TEST NAME > WHAT TO LOG
- """
- self.log += f"{self.date} > {self.name} > {text}\n"
-
- def test_ok(self, result, log):
- """to call when the test is ok.
- It just fill the test with result and embellished log
-
- Args:
- result: result of test
- log (string): what to log
-
- Returns:
- MonitoringTest: filled test
- """
- self.add_to_log(log)
- self.result = result
- return self
-
- def test_error(self, result, log):
- """to call when test error.
- It just fill the test with result, embellished log and set error value to ERROR
-
- Args:
- result: result of test
- log (string): what to log
-
- Returns:
- MonitoringTest: filled test
- """
- self.add_to_log(f"🚨 ERROR : {log}")
- self.result = result
- self.error = self.ERROR
- return self
-
- def test_warning(self, result, log):
- """to call when test warning.
- It just fill the test with result, embellished log and set error value to WARNING
-
- Args:
- result: result of test
- log (string): what to log
-
- Returns:
- MonitoringTest: filled test
- """
- self.add_to_log(f"🔥 WARNING : {log}")
- self.result = result
- self.error = self.WARNING
- return self
-
- @api.model
- def cron_monitoring_test(self):
- """cron launch test on all equipments
- """
- self.search([]).monitoring_test()
-
- def monitoring_test(self):
-
- def launch_test(attribute, *test_function_args):
- """run test function with name = test_[attribute]
- associate result of test to equipment
- write logs of test
-
-
- Args:
- attribute (string): attribute of MaintenanceEquipment we want to test
-
- Returns:
- MonitoringTest: returned by test function
- """
- test_function = getattr(equipment,"test_"+attribute)
- test = test_function(*test_function_args)
- setattr(equipment, attribute, test.result)
- log.write(test.log)
- tests.append(test)
- return test
-
-
- for equipment in self:
-
- # we use StingIO instead of string to use mutable object
- log = StringIO()
-
- # array of all tests
- tests = []
-
- # install dependencies and log it
- log.write(equipment.install_dependencies().log) # launch_test is not used, only logs are necessary
-
- # run ping test
- launch_test("ping_ok")
-
- # SSH dependant test
- ssh = launch_test("ssh_ok").result
-
-
- if ssh:
- # test available memory
- launch_test("available_memory_percent", ssh)
-
- # test disk usage
- launch_test("used_disk_space", ssh)
- else:
- equipment.available_memory_percent = -1 #set -1 by convention if error
- equipment.used_disk_space = -1 #set -1 by convention if error
-
- # set test date
- equipment.last_monitoring_test_date = fields.Datetime.now()
-
- # write logs
- log.seek(0) #log is a StringIO so seek to beginning before read
- new_log = f'📣 {fields.Datetime.now()}\n{log.read()}\n'
- new_log = new_log.replace("\n"," ") # log field is HTML, so format lines
- equipment.log = f'{new_log} {equipment.log}'[:LOG_LIMIT] #limit logs
-
- # if error create maintenance request
- error = warning =False
- if any(test.error == test.ERROR for test in tests):
- error = True # if any arror in tests
- elif any(test.error == test.WARNING for test in tests):
- warning = True # if any warning in tests
-
- if error or warning:
- # check if error or warning request (not done) already exists before creating a new one
- # if only a warning request exists, error request will be created anyway
- existing_not_done_error_request = None
- existing_not_done_warning_request = None
- if equipment.error_maintenance_request and not equipment.error_maintenance_request.stage_id.done:
- existing_not_done_error_request = equipment.error_maintenance_request
- if equipment.warning_maintenance_request and not equipment.warning_maintenance_request.stage_id.done:
- existing_not_done_warning_request = equipment.warning_maintenance_request
- if (error and not existing_not_done_error_request) \
- or (warning and not existing_not_done_warning_request and not existing_not_done_error_request):
- maintenance_request = self.env['maintenance.request'].create({
- "name":f'[{"ERROR" if error else "WARNING"}] {equipment.name}',
- "equipment_id":equipment.id,
- "employee_id":equipment.employee_id,
- "user_id":equipment.technician_user_id,
- "maintenance_team_id":equipment.maintenance_team_id.id or self.env["maintenance.team"].search([], limit=1),
- "priority":'2' if error else '3',
- "maintenance_type":"corrective" if error else "preventive",
- "description":new_log
- })
- if error:
- equipment.error_maintenance_request = maintenance_request
- else:
- equipment.warning_maintenance_request = maintenance_request
-
-
-
- def install_dependencies(self):
- """
- install dependencies needed to do all tests, as python or shell programs
-
- Returns:
- MonitoringTest: representing current test with result=0 if not error
- """
- monitoring_test = self.MonitoringTest("install dependencies")
- if "ping3" in sys.modules:
- return monitoring_test.test_ok(0, "ping3 already installed")
- else:
- try:
- command = ['pip','install',"ping3"]
- response = subprocess.call(command) # run "pip install ping3" command
- if response == 0:
- return monitoring_test.test_ok(0, "ping3 installation successful")
- else:
- monitoring_test.test_error(f"ping3 : unable to install : response = {response}")
- except Exception as e:
- return monitoring_test.test_error(f"ping3 : unable to install : {e}")
-
- def test_ssh_ok(self):
- """
- test ssh with maintenance_server_ssh module
-
- Returns:
- MonitoringTest: representing current test with :
- * result = False if error
- * result = ssh connection if no error
- * error = MonitoringTest.ERROR if connection failed
- * log file
- """
- test = self.MonitoringTest("SSH OK")
- try:
- # SSH connection ok : set ssh connection in result, converted in boolean (True) when set in ssh_ok field
- return test.test_ok(self.get_ssh_connection(), "SSH Connection OK") #ssh connection given by maintenance_server_ssh module
- except Exception as e:
- # SSH connection failed
- return test.test_error(False, f"{fields.Datetime.now()} > SSH > connection failed {e}\n")
-
-
- def test_available_memory_percent(self, ssh):
- """
- test available memory with a bash command called by ssh
-
- Args:
- ssh (paramiko.SSHClient): ssh client
-
- Returns:
- MonitoringTest: representing current test with :
- * result = -2 if error
- * result = percent of available memory if no error
- * error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on result comparaison
- with MIN_AVAILABLE_MEMORY_PERCENT_WARNING and MIN_AVAILABLE_MEMORY_PERCENT_ERROR
- * log file
- """
- try:
- test = self.MonitoringTest("Available memory percent")
- _stdin, stdout, _stderr = ssh.exec_command(AVAILABLE_MEMORY_PERCENT_COMMAND)
- available_memory_percent = float(stdout.read().decode())
- if available_memory_percent > MIN_AVAILABLE_MEMORY_PERCENT_WARNING:
- return test.test_ok(available_memory_percent, f"{available_memory_percent}% available")
- elif available_memory_percent > MIN_AVAILABLE_MEMORY_PERCENT_ERROR:
- # memory between warning and error step
- return test.test_warning(available_memory_percent, f"{available_memory_percent}% available (<{MIN_AVAILABLE_MEMORY_PERCENT_WARNING})")
- else:
- # memory available lower than error step
- return test.test_error(available_memory_percent, f"{available_memory_percent}% available (<{MIN_AVAILABLE_MEMORY_PERCENT_ERROR})")
- except Exception as e:
- return test.test_error(-2, f"{e}")
-
-
-
- def test_used_disk_space(self, ssh):
- """
- test Used disk space with a bash command called by ssh
-
- Args:
- ssh (paramiko.SSHClient): ssh client
-
- Returns:
- MonitoringTest: representing current test with :
- * result = -2 if error
- * result = percent of Used disk space if no error
- * error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on result comparaison
- with MAX_USED_DISK_SPACE_WARNING and MAX_USED_DISK_SPACE_ERROR
- * log file
- """
- try:
- test = self.MonitoringTest("Used disk space")
- _stdin, stdout, _stderr = ssh.exec_command(USED_DISK_SPACE_COMMAND)
- used_disk_space = float(stdout.read().decode())
- if used_disk_space < MAX_USED_DISK_SPACE_WARNING:
- return test.test_ok(used_disk_space, f"{used_disk_space}% used")
- elif used_disk_space < MAX_USED_DISK_SPACE_ERROR:
- # disk usage between WARNING and ERROR steps
- return test.test_warning(used_disk_space, f"{used_disk_space}% used (>{MAX_USED_DISK_SPACE_WARNING})")
- else:
- # disk usage higher than ERROR steps
- return test.test_error(used_disk_space, f"{used_disk_space}% used (>{MAX_USED_DISK_SPACE_ERROR})")
-
- except Exception as e:
- return test.test_error(-2, f"{e}")
-
-
- def test_ping_ok(self):
- """
- test PING with ping3 library
-
- Returns:
- MonitoringTest: representing current test with :
- * result = False if error
- * result = True if no error
- * error defined with MonitoringTest.ERROR or MonitoringTest.WARNING depending on ping time comparaison
- with MAX_PING_MS_WARNING and MAX_PING_MS_ERROR
- * log file
- """
- test = self.MonitoringTest("Ping")
- try:
- from ping3 import ping
- except Exception as e:
- # unable to import ping3
- return test.test_error(False, f"ping3 dependencie not satisfied : {e}")
-
- hostname = self.server_domain
- if not hostname:
- # equipment host name not filled
- return test.test_error(False, f"host name seems empty !")
-
- try:
- r = ping(hostname)
- except Exception as e:
- # Any problem when call ping
- return test.test_error(False, f"unable to call ping ! > {e}")
-
- if r:
- test.result = True
- ping_ms = int(r*1000)
- if ping_ms < MAX_PING_MS_WARNING:
- # ping OK
- return test.test_ok(True, f"PING OK in {ping_ms} ms")
- elif ping_ms < MAX_PING_MS_ERROR:
- # ping result between WARNING and ERROR => WARNING
- return test.test_warning(True, f"PING OK in {ping_ms}ms (> {MAX_PING_MS_WARNING})")
- else:
- # ping result higher than ERROR => ERROR
- return test.test_error(False, f"PING OK in {ping_ms}ms (> {MAX_PING_MS_ERROR})")
- else:
- return test.test_error(False, "PING FAILED")
-
-
\ No newline at end of file
diff --git a/maintenance_server_monitoring/views/maintenance_equipment_views.xml b/maintenance_server_monitoring/views/maintenance_equipment_views.xml
deleted file mode 100644
index 0872c5e..0000000
--- a/maintenance_server_monitoring/views/maintenance_equipment_views.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
- equipment.form.server.inherit
- maintenance.equipment
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- equipment.tree.server.inherit
- maintenance.equipment
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/maintenance_server_ssh/.gitignore b/maintenance_server_ssh/.gitignore
deleted file mode 100644
index 6da5887..0000000
--- a/maintenance_server_ssh/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*.*~
-*pyc
diff --git a/maintenance_server_ssh/README.rst b/maintenance_server_ssh/README.rst
deleted file mode 100644
index 4b923f4..0000000
--- a/maintenance_server_ssh/README.rst
+++ /dev/null
@@ -1,44 +0,0 @@
-======================================
-maintenance_server_ssh
-======================================
-
-Create an SSH remote connection for maintenance equipment, usable for other modules
-
-Installation
-============
-
-Use Odoo normal module installation procedure to install
-``maintenance_server_ssh``.
-
-Known issues / Roadmap
-======================
-
-None yet.
-
-Bug Tracker
-===========
-
-Bugs are tracked on `our issues website `_. In case of
-trouble, please check there if your issue has already been
-reported. If you spotted it first, help us smashing it by providing a
-detailed and welcomed feedback.
-
-Credits
-=======
-
-Contributors
-------------
-
-* Clément Thomas
-
-Funders
--------
-
-The development of this module has been financially supported by:
-* Elabore (https://elabore.coop)
-
-
-Maintainer
-----------
-
-This module is maintained by Elabore.
diff --git a/maintenance_server_ssh/__init__.py b/maintenance_server_ssh/__init__.py
deleted file mode 100644
index cde864b..0000000
--- a/maintenance_server_ssh/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from . import models
diff --git a/maintenance_server_ssh/__manifest__.py b/maintenance_server_ssh/__manifest__.py
deleted file mode 100644
index bfd9770..0000000
--- a/maintenance_server_ssh/__manifest__.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright 2023 Stéphan Sainléger (Elabore)
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
-{
- "name": "maintenance_server_ssh",
- "version": "16.0.1.0.0",
- "author": "Elabore",
- "website": "https://elabore.coop",
- "maintainer": "Clément Thomas",
- "license": "AGPL-3",
- "category": "Tools",
- "summary": "Monitor some data on remote hosts",
- # any module necessary for this one to work correctly
- "depends": [
- "base",
- "maintenance",
- ],
- "qweb": [
- # "static/src/xml/*.xml",
- ],
- "external_dependencies": {
- "python": [],
- },
- # always loaded
- "data": [
- "views/maintenance_equipment_views.xml",
- ],
- # only loaded in demonstration mode
- "demo": [],
- "js": [],
- "css": [],
- "installable": True,
- # Install this module automatically if all dependency have been previously
- # and independently installed. Used for synergetic or glue modules.
- "auto_install": False,
- "application": False,
-}
\ No newline at end of file
diff --git a/maintenance_server_ssh/models/__init__.py b/maintenance_server_ssh/models/__init__.py
deleted file mode 100644
index b31f6b6..0000000
--- a/maintenance_server_ssh/models/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from . import maintenance_equipment
\ No newline at end of file
diff --git a/maintenance_server_ssh/models/maintenance_equipment.py b/maintenance_server_ssh/models/maintenance_equipment.py
deleted file mode 100644
index b661642..0000000
--- a/maintenance_server_ssh/models/maintenance_equipment.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from odoo import fields, models
-import subprocess
-import sys
-import psutil
-
-
-class MaintenanceEquipment(models.Model):
- _inherit = 'maintenance.equipment'
-
- server_domain = fields.Char('Server Domain')
- ssh_private_key_path = fields.Char("SSH private key path", default="/opt/odoo/auto/dev/ssh_keys/id_rsa")
-
- def get_ssh_connection(self):
- import paramiko
- ssh = paramiko.SSHClient()
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- ssh.connect(self.server_domain, username="root", key_filename=self.ssh_private_key_path)
- return ssh
-
-
\ No newline at end of file
diff --git a/maintenance_server_ssh/views/maintenance_equipment_views.xml b/maintenance_server_ssh/views/maintenance_equipment_views.xml
deleted file mode 100644
index a5f1018..0000000
--- a/maintenance_server_ssh/views/maintenance_equipment_views.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
- equipment.form.server.inherit
- maintenance.equipment
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- equipment.tree.server.inherit
- maintenance.equipment
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/maintenance_service_http_monitoring/DEV/.sisyphus/drafts/service-http-monitoring.md b/maintenance_service_http_monitoring/DEV/.sisyphus/drafts/service-http-monitoring.md
index 63d8f44..1b58943 100644
--- a/maintenance_service_http_monitoring/DEV/.sisyphus/drafts/service-http-monitoring.md
+++ b/maintenance_service_http_monitoring/DEV/.sisyphus/drafts/service-http-monitoring.md
@@ -3,6 +3,7 @@
## Contexte codebase
### Architecture des modules
+
```
maintenance_server_data (base, maintenance)
├── Définit: service, service.version, service.instance, backup.server
@@ -22,51 +23,64 @@ maintenance_server_monitoring (base, maintenance, maintenance_server_ssh)
```
### Modèle service.instance actuel (dans maintenance_server_data)
+
- equipment_id (Many2one → maintenance.equipment)
- service_id (Many2one → service, required)
- version_id (Many2one → service.version)
- service_url (Char) ← URL déjà existante, parfait pour les checks HTTP
### Pattern de création maintenance.request existant
+
- Vérifie si une request non-done existe déjà avant d'en créer une nouvelle
-- Stocke la référence sur equipment: error_maintenance_request / warning_maintenance_request
+- Stocke la référence sur equipment: error_maintenance_request /
+ warning_maintenance_request
- Assignation auto: employee_id, technician_user_id, maintenance_team_id
## Requirements confirmés (depuis les specs)
- [x] Modifier le module `maintenance_server_monitoring` (pas de nouveau module)
-- [x] Ajouter `maintenance_server_data` aux dépendances (nécessaire pour accéder à service.instance)
-- [x] Étendre `service.instance` avec: last_status_code (Integer), last_check_date (Datetime), status OK/KO (Boolean)
-- [x] Nouvelle vue liste standalone pour service.instance (nom, version, URL, date check, status code, statut)
+- [x] Ajouter `maintenance_server_data` aux dépendances (nécessaire pour accéder à
+ service.instance)
+- [x] Étendre `service.instance` avec: last_status_code (Integer), last_check_date
+ (Datetime), status OK/KO (Boolean)
+- [x] Nouvelle vue liste standalone pour service.instance (nom, version, URL, date
+ check, status code, statut)
- [x] Paramètres module: fréquence vérification + durée mode maintenance
- [x] Mode maintenance sur equipment: bool tracked + bandeau
- [x] Si service KO → création maintenance.request (pas de doublon si toujours KO)
## Décision architecturale : NOUVEAU MODULE
-**Nom**: `maintenance_service_http_monitoring`
-**Raison**: Séparation des préoccupations — monitoring infra (SSH/ping) vs monitoring applicatif (HTTP)
-**Dépendances**: `base`, `maintenance`, `maintenance_server_data`
-**PAS de dépendance** vers `maintenance_server_ssh` ni `maintenance_server_monitoring`
+**Nom**: `maintenance_service_http_monitoring` **Raison**: Séparation des préoccupations
+— monitoring infra (SSH/ping) vs monitoring applicatif (HTTP) **Dépendances**: `base`,
+`maintenance`, `maintenance_server_data` **PAS de dépendance** vers
+`maintenance_server_ssh` ni `maintenance_server_monitoring`
## Décisions techniques
- Paramètres via res.config.settings + ir.config_parameter (pattern standard Odoo)
-- _inherit = 'service.instance' dans le nouveau module pour étendre le modèle
+- \_inherit = 'service.instance' dans le nouveau module pour étendre le modèle
- Cron dédié pour les checks HTTP (fréquence configurable)
-- _inherit = 'maintenance.equipment' pour ajouter maintenance_mode + http_maintenance_request
+- \_inherit = 'maintenance.equipment' pour ajouter maintenance_mode +
+ http_maintenance_request
- mail.thread déjà hérité par maintenance.equipment dans Odoo base → tracking fonctionne
## Décisions utilisateur (interview)
-1. **Mode maintenance**: HTTP uniquement — le monitoring existant (ping, SSH, mémoire, disque) continue normalement
-2. **Lien maintenance.request**: Sur maintenance.equipment — nouveau champ `http_maintenance_request` (Many2one). Si plusieurs services KO sur un même equipment, UNE seule request qui liste les services KO.
-3. **Menu service list**: Sous Maintenance > principal, même niveau que Équipements et Demandes
-4. **Tests**: OUI — setup pytest-odoo + tests unitaires pour la logique HTTP check et création maintenance requests
+1. **Mode maintenance**: HTTP uniquement — le monitoring existant (ping, SSH, mémoire,
+ disque) continue normalement
+2. **Lien maintenance.request**: Sur maintenance.equipment — nouveau champ
+ `http_maintenance_request` (Many2one). Si plusieurs services KO sur un même
+ equipment, UNE seule request qui liste les services KO.
+3. **Menu service list**: Sous Maintenance > principal, même niveau que Équipements et
+ Demandes
+4. **Tests**: OUI — setup pytest-odoo + tests unitaires pour la logique HTTP check et
+ création maintenance requests
## Scope boundaries
### IN
+
- NOUVEAU module `maintenance_service_http_monitoring`
- Étendre service.instance avec champs monitoring HTTP
- Cron dédié pour checks HTTP (fréquence configurable)
@@ -77,6 +91,7 @@ maintenance_server_monitoring (base, maintenance, maintenance_server_ssh)
- Setup pytest-odoo + tests unitaires
### OUT
+
- Aucune modification de `maintenance_server_monitoring` ni de `maintenance_server_ssh`
- Pas de notification email (juste la maintenance.request)
- Pas de dashboard / reporting
@@ -84,9 +99,13 @@ maintenance_server_monitoring (base, maintenance, maintenance_server_ssh)
## Décisions Metis (post-gap-analysis)
-- Services orphelins (sans equipment_id): N'existent pas en pratique, filtrés par sécurité
-- Récupération service KO→OK: Rien d'automatique, close manuelle de la maintenance.request
-- Définition KO: Tout échec = KO (timeout, DNS, SSL, connexion refusée, code != 200). Log le détail.
-- Timeout HTTP: Hardcodé (constante, comme les seuils existants dans maintenance_server_monitoring)
+- Services orphelins (sans equipment_id): N'existent pas en pratique, filtrés par
+ sécurité
+- Récupération service KO→OK: Rien d'automatique, close manuelle de la
+ maintenance.request
+- Définition KO: Tout échec = KO (timeout, DNS, SSL, connexion refusée, code != 200).
+ Log le détail.
+- Timeout HTTP: Hardcodé (constante, comme les seuils existants dans
+ maintenance_server_monitoring)
- `requests` library: Déclarer dans external_dependencies.python
- Chaque requests.get() DOIT avoir timeout= (pylintrc enforce external-request-timeout)
diff --git a/maintenance_service_http_monitoring/DEV/.sisyphus/plans/service-http-monitoring.md b/maintenance_service_http_monitoring/DEV/.sisyphus/plans/service-http-monitoring.md
index 8ac4753..12c5e74 100644
--- a/maintenance_service_http_monitoring/DEV/.sisyphus/plans/service-http-monitoring.md
+++ b/maintenance_service_http_monitoring/DEV/.sisyphus/plans/service-http-monitoring.md
@@ -2,65 +2,93 @@
## TL;DR
-> **Quick Summary**: Créer un nouveau module Odoo 16 `maintenance_service_http_monitoring` qui vérifie périodiquement la disponibilité HTTP des services (`service.instance`) et crée automatiquement des demandes de maintenance si un service ne répond plus en 200.
+> **Quick Summary**: Créer un nouveau module Odoo 16
+> `maintenance_service_http_monitoring` qui vérifie périodiquement la disponibilité HTTP
+> des services (`service.instance`) et crée automatiquement des demandes de maintenance
+> si un service ne répond plus en 200.
>
> **Deliverables**:
+>
> - Nouveau module complet avec modèles, vues, cron, settings et tests
-> - Extension de `service.instance` avec champs de monitoring HTTP (status code, date check, état OK/KO)
-> - Extension de `maintenance.equipment` avec mode maintenance (désactive les checks HTTP) et lien vers la maintenance.request HTTP
+> - Extension de `service.instance` avec champs de monitoring HTTP (status code, date
+> check, état OK/KO)
+> - Extension de `maintenance.equipment` avec mode maintenance (désactive les checks
+> HTTP) et lien vers la maintenance.request HTTP
> - Vue liste standalone des services avec colonnes monitoring
> - Page de paramètres (fréquence de vérification, durée mode maintenance)
> - Cron configurable pour les vérifications HTTP
> - Tests unitaires avec pytest-odoo (HTTP mocké)
>
-> **Estimated Effort**: Medium
-> **Parallel Execution**: YES — 4 waves
-> **Critical Path**: T1 → T3 → T6 → T9
+> **Estimated Effort**: Medium **Parallel Execution**: YES — 4 waves **Critical Path**:
+> T1 → T3 → T6 → T9
---
## Context
### Original Request
-Être alerté si un service sur nos serveurs n'est plus disponible (requête HTTP retourne une réponse autre que 200). Un cron envoie des requêtes HTTPS sur tous les services actifs. Si réponse != 200, le service est marqué KO et une demande de maintenance est ouverte.
+
+Être alerté si un service sur nos serveurs n'est plus disponible (requête HTTP retourne
+une réponse autre que 200). Un cron envoie des requêtes HTTPS sur tous les services
+actifs. Si réponse != 200, le service est marqué KO et une demande de maintenance est
+ouverte.
### Interview Summary
+
**Key Discussions**:
-- **Architecture module**: Nouveau module indépendant plutôt que modifier `maintenance_server_monitoring` — séparation des préoccupations (monitoring infra SSH/ping vs monitoring applicatif HTTP)
-- **Mode maintenance scope**: Désactive les checks HTTP uniquement (pas ping/SSH/mémoire/disque)
-- **Lien maintenance.request**: Stocké sur `maintenance.equipment` (champ `http_maintenance_request`), une request par équipement listant tous les services KO
+
+- **Architecture module**: Nouveau module indépendant plutôt que modifier
+ `maintenance_server_monitoring` — séparation des préoccupations (monitoring infra
+ SSH/ping vs monitoring applicatif HTTP)
+- **Mode maintenance scope**: Désactive les checks HTTP uniquement (pas
+ ping/SSH/mémoire/disque)
+- **Lien maintenance.request**: Stocké sur `maintenance.equipment` (champ
+ `http_maintenance_request`), une request par équipement listant tous les services KO
- **Récupération KO→OK**: Close manuelle, pas d'auto-close
- **Définition KO**: Tout échec = KO (timeout, DNS, SSL, connexion refusée, code != 200)
- **Menu**: Sous Maintenance > principal, même niveau que Équipements
- **Tests**: Oui, setup pytest-odoo + tests unitaires
**Research Findings**:
+
- `service.instance` a déjà un champ `service_url` (Char) — parfait pour les checks HTTP
-- `maintenance.equipment` hérite `mail.thread` dans Odoo 16 base → `tracking=True` fonctionne sur equipment
+- `maintenance.equipment` hérite `mail.thread` dans Odoo 16 base → `tracking=True`
+ fonctionne sur equipment
- `service.instance` n'hérite PAS `mail.thread` → pas de tracking sur ses champs
-- Le pattern de création de `maintenance.request` est documenté dans `maintenance_server_monitoring/models/maintenance_equipment.py:194-218`
-- Le repo a des hooks pre-commit lourds : prettier, ruff, pylint_odoo (mandatory), docformatter
-- pylintrc enforce `external-request-timeout` → tout `requests.get()` DOIT avoir `timeout=`
-- `requests` est disponible dans l'environnement Odoo mais doit être déclaré dans `external_dependencies`
+- Le pattern de création de `maintenance.request` est documenté dans
+ `maintenance_server_monitoring/models/maintenance_equipment.py:194-218`
+- Le repo a des hooks pre-commit lourds : prettier, ruff, pylint_odoo (mandatory),
+ docformatter
+- pylintrc enforce `external-request-timeout` → tout `requests.get()` DOIT avoir
+ `timeout=`
+- `requests` est disponible dans l'environnement Odoo mais doit être déclaré dans
+ `external_dependencies`
### Metis Review
+
**Identified Gaps** (addressed):
-- Services orphelins (sans `equipment_id`): N'existent pas en pratique, filtrés par sécurité dans le cron
+
+- Services orphelins (sans `equipment_id`): N'existent pas en pratique, filtrés par
+ sécurité dans le cron
- Récupération KO→OK: Décision prise — close manuelle
- Définition de KO: Clarifié — tout échec réseau ou HTTP != 200 = KO
- Timeout HTTP: Constante hardcodée (pattern existant des seuils de monitoring)
- `requests` dans `external_dependencies`: Ajouté au plan
- `security/ir.model.access.csv`: Nécessaire pour le nouveau module
-- Cron overlap potentiel: Documenté — l'intervalle doit excéder le temps total de vérification
+- Cron overlap potentiel: Documenté — l'intervalle doit excéder le temps total de
+ vérification
---
## Work Objectives
### Core Objective
-Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et crée des demandes de maintenance automatiquement en cas d'indisponibilité.
+
+Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et crée des
+demandes de maintenance automatiquement en cas d'indisponibilité.
### Concrete Deliverables
+
- Module `maintenance_service_http_monitoring/` complet et installable
- Extension du modèle `service.instance` avec champs de monitoring
- Extension du modèle `maintenance.equipment` avec mode maintenance et lien HTTP request
@@ -70,7 +98,9 @@ Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et cr
- Tests unitaires
### Definition of Done
-- [ ] Module installable sans erreur sur une instance Odoo 16 avec `maintenance_server_data` installé
+
+- [ ] Module installable sans erreur sur une instance Odoo 16 avec
+ `maintenance_server_data` installé
- [ ] Cron de vérification HTTP fonctionne et met à jour les statuts des services
- [ ] Demande de maintenance créée automatiquement quand un service est KO
- [ ] Pas de doublon de maintenance.request si le service reste KO
@@ -81,9 +111,12 @@ Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et cr
- [ ] `pre-commit run --all-files` passe sans erreur
### Must Have
+
- Vérification HTTP avec `requests.get(url, timeout=X)`
-- Champs sur `service.instance`: `last_http_status_code`, `last_http_check_date`, `http_status_ok`
-- Champ sur `maintenance.equipment`: `http_maintenance_request` (Many2one), `maintenance_mode` (Boolean tracked), `maintenance_mode_start`, `maintenance_mode_end`
+- Champs sur `service.instance`: `last_http_status_code`, `last_http_check_date`,
+ `http_status_ok`
+- Champ sur `maintenance.equipment`: `http_maintenance_request` (Many2one),
+ `maintenance_mode` (Boolean tracked), `maintenance_mode_start`, `maintenance_mode_end`
- Cron séparé pour les checks HTTP
- Paramètres: fréquence de vérification, durée du mode maintenance
- Création de `maintenance.request` si KO, vérification doublon avant création
@@ -91,6 +124,7 @@ Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et cr
- Tests unitaires avec HTTP mocké
### Must NOT Have (Guardrails)
+
- Ne PAS modifier le module `maintenance_server_monitoring` ni `maintenance_server_ssh`
- Ne PAS modifier le module `maintenance_server_data`
- Ne PAS ajouter de notifications email
@@ -99,7 +133,8 @@ Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et cr
- Ne PAS auto-fermer les maintenance.request quand un service repasse OK
- Ne PAS parser le body des réponses HTTP
- Ne PAS ajouter d'authentification ou de headers custom aux requêtes HTTP
-- Ne PAS utiliser `requests.get()` sans `timeout=` (pylintrc enforce `external-request-timeout`)
+- Ne PAS utiliser `requests.get()` sans `timeout=` (pylintrc enforce
+ `external-request-timeout`)
- Ne PAS utiliser `except Exception` sans logger le détail de l'erreur
- Ne PAS ajouter de commentaires superflus ou de docstrings génériques (AI slop)
@@ -110,22 +145,24 @@ Créer un module Odoo 16 qui surveille la disponibilité HTTP des services et cr
> **ZERO HUMAN INTERVENTION** — ALL verification is agent-executed. No exceptions.
### Test Decision
+
- **Infrastructure exists**: NO (greenfield)
- **Automated tests**: YES (Tests-after)
- **Framework**: Odoo `TransactionCase` avec `unittest.mock` pour mocker HTTP
### QA Policy
-Every task MUST include agent-executed QA scenarios.
-Evidence saved to `.sisyphus/evidence/task-{N}-{scenario-slug}.{ext}`.
-| Deliverable Type | Verification Tool | Method |
-|------------------|-------------------|--------|
-| Python models | Bash (python/odoo shell) | Import, instancier, vérifier champs |
-| XML views | Bash (odoo --init) | Installation module, vérification logs |
-| Cron | Bash (odoo shell) | `env.ref()` du cron, vérification config |
-| Settings | Bash (odoo shell) | `ir.config_parameter` get/set |
-| Tests | Bash (pytest/odoo test) | Exécution test suite |
-| Linting | Bash (pre-commit) | `pre-commit run --all-files` |
+Every task MUST include agent-executed QA scenarios. Evidence saved to
+`.sisyphus/evidence/task-{N}-{scenario-slug}.{ext}`.
+
+| Deliverable Type | Verification Tool | Method |
+| ---------------- | ------------------------ | ---------------------------------------- |
+| Python models | Bash (python/odoo shell) | Import, instancier, vérifier champs |
+| XML views | Bash (odoo --init) | Installation module, vérification logs |
+| Cron | Bash (odoo shell) | `env.ref()` du cron, vérification config |
+| Settings | Bash (odoo shell) | `ir.config_parameter` get/set |
+| Tests | Bash (pytest/odoo test) | Exécution test suite |
+| Linting | Bash (pre-commit) | `pre-commit run --all-files` |
---
@@ -163,27 +200,27 @@ Max Concurrent: 3 (Wave 2)
### Dependency Matrix
-| Task | Depends On | Blocks | Wave |
-|------|------------|--------|------|
-| T1 | — | T2, T3, T4, T5 | 1 |
-| T2 | T1 | T3, T5 | 1 |
-| T3 | T1, T2 | T5, T6, T8 | 2 |
-| T4 | T1 | T7, T8 | 2 |
-| T5 | T1, T2, T3 | T8 | 2 |
-| T6 | T3 | T8 | 3 |
-| T7 | T4 | T8 | 3 |
-| T8 | T3, T4, T5, T6, T7 | F1-F4 | 4 |
-| F1-F4 | T8 | — | FINAL |
+| Task | Depends On | Blocks | Wave |
+| ----- | ------------------ | -------------- | ----- |
+| T1 | — | T2, T3, T4, T5 | 1 |
+| T2 | T1 | T3, T5 | 1 |
+| T3 | T1, T2 | T5, T6, T8 | 2 |
+| T4 | T1 | T7, T8 | 2 |
+| T5 | T1, T2, T3 | T8 | 2 |
+| T6 | T3 | T8 | 3 |
+| T7 | T4 | T8 | 3 |
+| T8 | T3, T4, T5, T6, T7 | F1-F4 | 4 |
+| F1-F4 | T8 | — | FINAL |
### Agent Dispatch Summary
-| Wave | # Parallel | Tasks → Agent Category |
-|------|------------|----------------------|
-| 1 | **2** | T1 → `quick`, T2 → `quick` |
-| 2 | **3** | T3 → `deep`, T4 → `unspecified-high`, T5 → `quick` |
-| 3 | **2** | T6 → `quick`, T7 → `quick` |
-| 4 | **1** | T8 → `unspecified-high` |
-| FINAL | **4** | F1 → `oracle`, F2 → `unspecified-high`, F3 → `unspecified-high`, F4 → `deep` |
+| Wave | # Parallel | Tasks → Agent Category |
+| ----- | ---------- | ---------------------------------------------------------------------------- |
+| 1 | **2** | T1 → `quick`, T2 → `quick` |
+| 2 | **3** | T3 → `deep`, T4 → `unspecified-high`, T5 → `quick` |
+| 3 | **2** | T6 → `quick`, T7 → `quick` |
+| 4 | **1** | T8 → `unspecified-high` |
+| FINAL | **4** | F1 → `oracle`, F2 → `unspecified-high`, F3 → `unspecified-high`, F4 → `deep` |
---
@@ -192,7 +229,9 @@ Max Concurrent: 3 (Wave 2)
- [ ] 1. Scaffolding du module `maintenance_service_http_monitoring`
**What to do**:
- - Créer le répertoire `maintenance_service_http_monitoring/` à la racine du repo (à côté de `maintenance_server_monitoring/`)
+
+ - Créer le répertoire `maintenance_service_http_monitoring/` à la racine du repo (à
+ côté de `maintenance_server_monitoring/`)
- Créer `__manifest__.py` avec :
- `name`: `maintenance_service_http_monitoring`
- `version`: `16.0.1.0.0`
@@ -202,18 +241,24 @@ Max Concurrent: 3 (Wave 2)
- `summary`: `Monitor HTTP availability of services`
- `depends`: `["base", "maintenance", "maintenance_server_data"]`
- `external_dependencies`: `{"python": ["requests"]}`
- - `data`: liste de tous les fichiers data/views/security (dans l'ordre : security d'abord, puis views, puis data)
+ - `data`: liste de tous les fichiers data/views/security (dans l'ordre : security
+ d'abord, puis views, puis data)
- `installable`: `True`
- Créer `__init__.py` qui importe `models`
- - Créer `models/__init__.py` qui importe les sous-modules (service_instance, maintenance_equipment, res_config_settings)
- - Créer `security/ir.model.access.csv` avec les règles d'accès pour `res.config.settings` (le modèle `service.instance` est déjà couvert par `maintenance_server_data`)
+ - Créer `models/__init__.py` qui importe les sous-modules (service_instance,
+ maintenance_equipment, res_config_settings)
+ - Créer `security/ir.model.access.csv` avec les règles d'accès pour
+ `res.config.settings` (le modèle `service.instance` est déjà couvert par
+ `maintenance_server_data`)
- Créer les répertoires vides : `views/`, `data/`, `tests/`
**Must NOT do**:
+
- Ne pas créer de fichiers modèle Python (juste les `__init__.py` avec les imports)
- Ne pas modifier de fichiers dans d'autres modules
**Recommended Agent Profile**:
+
- **Category**: `quick`
- Reason: Scaffolding simple, fichiers de configuration, pas de logique métier
- **Skills**: []
@@ -222,6 +267,7 @@ Max Concurrent: 3 (Wave 2)
- `git-master`: Pas de commit dans cette tâche
**Parallelization**:
+
- **Can Run In Parallel**: YES (avec T2)
- **Parallel Group**: Wave 1 (avec Task 2)
- **Blocks**: T2, T3, T4, T5
@@ -230,17 +276,28 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References** (existing code to follow):
- - `maintenance_server_monitoring/__manifest__.py` — Structure du manifest à suivre (même format, même auteur, même licence). Adapter les depends et external_dependencies
+
+ - `maintenance_server_monitoring/__manifest__.py` — Structure du manifest à suivre
+ (même format, même auteur, même licence). Adapter les depends et
+ external_dependencies
- `maintenance_server_monitoring/__init__.py` — Pattern d'import module
- - `maintenance_server_monitoring/models/__init__.py` — Pattern d'import des sous-modules modèle
+ - `maintenance_server_monitoring/models/__init__.py` — Pattern d'import des
+ sous-modules modèle
**API/Type References**:
- - `maintenance_server_data/security/ir.model.access.csv` — Format exact du CSV de sécurité à reproduire (colonnes, groupes utilisés : `base.group_user` et `maintenance.group_equipment_manager`)
+
+ - `maintenance_server_data/security/ir.model.access.csv` — Format exact du CSV de
+ sécurité à reproduire (colonnes, groupes utilisés : `base.group_user` et
+ `maintenance.group_equipment_manager`)
**WHY Each Reference Matters**:
- - Le manifest doit suivre exactement le même format que les autres modules du repo pour la cohérence
- - Les imports `__init__.py` doivent lister tous les fichiers Python qui seront créés dans les tâches suivantes
- - Le security CSV doit suivre le format exact d'Odoo (headers, colonnes) sinon le module ne s'installe pas
+
+ - Le manifest doit suivre exactement le même format que les autres modules du repo
+ pour la cohérence
+ - Les imports `__init__.py` doivent lister tous les fichiers Python qui seront créés
+ dans les tâches suivantes
+ - Le security CSV doit suivre le format exact d'Odoo (headers, colonnes) sinon le
+ module ne s'installe pas
**Acceptance Criteria**:
@@ -276,8 +333,13 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
- - Message: `feat(http_monitoring): scaffold new module maintenance_service_http_monitoring`
- - Files: `maintenance_service_http_monitoring/__manifest__.py`, `maintenance_service_http_monitoring/__init__.py`, `maintenance_service_http_monitoring/models/__init__.py`, `maintenance_service_http_monitoring/security/ir.model.access.csv`
+
+ - Message:
+ `feat(http_monitoring): scaffold new module maintenance_service_http_monitoring`
+ - Files: `maintenance_service_http_monitoring/__manifest__.py`,
+ `maintenance_service_http_monitoring/__init__.py`,
+ `maintenance_service_http_monitoring/models/__init__.py`,
+ `maintenance_service_http_monitoring/security/ir.model.access.csv`
- Pre-commit: N/A (pas de code Python significatif)
---
@@ -285,22 +347,29 @@ Max Concurrent: 3 (Wave 2)
- [ ] 2. Paramètres du module (res.config.settings)
**What to do**:
+
- Créer `maintenance_service_http_monitoring/models/res_config_settings.py` :
- `_inherit = 'res.config.settings'`
- - Champ `http_check_frequency` (Integer, default=1) — fréquence de vérification en heures
- - Champ `http_maintenance_mode_duration` (Integer, default=4) — durée du mode maintenance en heures
+ - Champ `http_check_frequency` (Integer, default=1) — fréquence de vérification en
+ heures
+ - Champ `http_maintenance_mode_duration` (Integer, default=4) — durée du mode
+ maintenance en heures
- Les deux champs liés à `ir.config_parameter` via `config_parameter=` attribute
- - Clés de paramètres : `maintenance_service_http_monitoring.http_check_frequency` et `maintenance_service_http_monitoring.maintenance_mode_duration`
+ - Clés de paramètres : `maintenance_service_http_monitoring.http_check_frequency` et
+ `maintenance_service_http_monitoring.maintenance_mode_duration`
- Créer `maintenance_service_http_monitoring/views/res_config_settings_views.xml` :
- - Hériter de la vue settings de Maintenance (`maintenance.res_config_settings_view_form`)
+ - Hériter de la vue settings de Maintenance
+ (`maintenance.res_config_settings_view_form`)
- Ajouter un groupe "HTTP Service Monitoring" avec les deux champs
- S'assurer que le fichier est listé dans `__manifest__.py` data
**Must NOT do**:
+
- Ne pas ajouter plus de 2 paramètres (fréquence + durée mode maintenance)
- Ne pas ajouter de paramètres par service (timeout custom, headers custom, etc.)
**Recommended Agent Profile**:
+
- **Category**: `quick`
- Reason: Petit modèle + petite vue, pattern Odoo standard
- **Skills**: []
@@ -308,15 +377,18 @@ Max Concurrent: 3 (Wave 2)
- `frontend-ui-ux`: Pas de design custom, juste un formulaire Odoo standard
**Parallelization**:
- - **Can Run In Parallel**: YES (avec T1 si les __init__.py sont prêts)
+
+ - **Can Run In Parallel**: YES (avec T1 si les **init**.py sont prêts)
- **Parallel Group**: Wave 1 (avec Task 1)
- **Blocks**: T3, T5
- - **Blocked By**: T1 (besoin du manifest et des __init__.py)
+ - **Blocked By**: T1 (besoin du manifest et des **init**.py)
**References** (CRITICAL):
**Pattern References**:
- - Aucun `res.config.settings` n'existe dans ce repo. Utiliser le pattern standard Odoo 16 :
+
+ - Aucun `res.config.settings` n'existe dans ce repo. Utiliser le pattern standard Odoo
+ 16 :
```python
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
@@ -328,11 +400,17 @@ Max Concurrent: 3 (Wave 2)
```
**External References**:
- - Pattern Odoo 16 `res.config.settings` : le champ doit avoir `config_parameter=` pour persister dans `ir.config_parameter`
- - Vue settings : hériter de `maintenance.res_config_settings_view_form` si elle existe, sinon créer une nouvelle section
+
+ - Pattern Odoo 16 `res.config.settings` : le champ doit avoir `config_parameter=` pour
+ persister dans `ir.config_parameter`
+ - Vue settings : hériter de `maintenance.res_config_settings_view_form` si elle
+ existe, sinon créer une nouvelle section
**WHY Each Reference Matters**:
- - Le pattern `config_parameter=` est la façon standard Odoo 16 de lier un champ de settings à `ir.config_parameter`. Sans ça, la valeur n'est pas persistée entre les sessions
+
+ - Le pattern `config_parameter=` est la façon standard Odoo 16 de lier un champ de
+ settings à `ir.config_parameter`. Sans ça, la valeur n'est pas persistée entre les
+ sessions
**Acceptance Criteria**:
@@ -363,8 +441,11 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES (group with T1)
- - Message: `feat(http_monitoring): add module settings for check frequency and maintenance mode duration`
- - Files: `maintenance_service_http_monitoring/models/res_config_settings.py`, `maintenance_service_http_monitoring/views/res_config_settings_views.xml`
+
+ - Message:
+ `feat(http_monitoring): add module settings for check frequency and maintenance mode duration`
+ - Files: `maintenance_service_http_monitoring/models/res_config_settings.py`,
+ `maintenance_service_http_monitoring/views/res_config_settings_views.xml`
- Pre-commit: N/A
---
@@ -372,42 +453,57 @@ Max Concurrent: 3 (Wave 2)
- [ ] 3. Extension du modèle `service.instance` — champs + méthode HTTP check
**What to do**:
+
- Créer `maintenance_service_http_monitoring/models/service_instance.py` :
- `_inherit = 'service.instance'`
- - Champ `last_http_status_code` (Integer, readonly=True, string="Last HTTP Status Code") — dernier code HTTP reçu, -1 si erreur réseau
- - Champ `last_http_check_date` (Datetime, readonly=True, string="Last HTTP Check Date")
- - Champ `http_status_ok` (Boolean, readonly=True, string="HTTP Status OK", default=True)
+ - Champ `last_http_status_code` (Integer, readonly=True, string="Last HTTP Status
+ Code") — dernier code HTTP reçu, -1 si erreur réseau
+ - Champ `last_http_check_date` (Datetime, readonly=True, string="Last HTTP Check
+ Date")
+ - Champ `http_status_ok` (Boolean, readonly=True, string="HTTP Status OK",
+ default=True)
- Méthode `check_http_status(self)` qui pour chaque record :
1. Vérifie que `service_url` est non vide et que `equipment_id` existe
2. Vérifie que l'équipement n'est PAS en mode maintenance
- 3. Envoie `requests.get(self.service_url, timeout=HTTP_CHECK_TIMEOUT)` dans un try/except
+ 3. Envoie `requests.get(self.service_url, timeout=HTTP_CHECK_TIMEOUT)` dans un
+ try/except
4. Si réponse 200 : `http_status_ok = True`, `last_http_status_code = 200`
- 5. Si réponse autre : `http_status_ok = False`, `last_http_status_code = response.status_code`
- 6. Si exception (timeout, DNS, SSL, connexion) : `http_status_ok = False`, `last_http_status_code = -1`
+ 5. Si réponse autre : `http_status_ok = False`,
+ `last_http_status_code = response.status_code`
+ 6. Si exception (timeout, DNS, SSL, connexion) : `http_status_ok = False`,
+ `last_http_status_code = -1`
7. Met à jour `last_http_check_date = fields.Datetime.now()`
- 8. Si `http_status_ok == False` : appelle la logique de création de maintenance.request (déléguée à T4 sur equipment)
+ 8. Si `http_status_ok == False` : appelle la logique de création de
+ maintenance.request (déléguée à T4 sur equipment)
- Constante `HTTP_CHECK_TIMEOUT = 10` (secondes) en haut du fichier
- Méthode `@api.model cron_check_http_services(self)` :
- 1. Recherche tous les `service.instance` avec `service_url != False` et `equipment_id != False`
+ 1. Recherche tous les `service.instance` avec `service_url != False` et
+ `equipment_id != False`
2. Filtre les équipements en mode maintenance
3. Appelle `check_http_status()` sur les records filtrés
**Must NOT do**:
+
- Ne PAS parser le body de la réponse HTTP
- - Ne PAS suivre les redirects manuellement (laisser le comportement par défaut de `requests`)
+ - Ne PAS suivre les redirects manuellement (laisser le comportement par défaut de
+ `requests`)
- Ne PAS ajouter `mail.thread` à `service.instance`
- Ne PAS utiliser `requests.get()` sans `timeout=`
- - Ne PAS attraper les exceptions silencieusement — logger le détail dans un champ ou dans les logs Odoo
+ - Ne PAS attraper les exceptions silencieusement — logger le détail dans un champ ou
+ dans les logs Odoo
- Ne PAS modifier `maintenance_server_data/models/service.py`
**Recommended Agent Profile**:
+
- **Category**: `deep`
- - Reason: Logique métier centrale du module, gestion d'erreurs réseau, interactions avec `requests`, pattern de vérification. Nécessite une implémentation soigneuse
+ - Reason: Logique métier centrale du module, gestion d'erreurs réseau, interactions
+ avec `requests`, pattern de vérification. Nécessite une implémentation soigneuse
- **Skills**: []
- **Skills Evaluated but Omitted**:
- `playwright`: Pas de browser automation, c'est du HTTP backend
**Parallelization**:
+
- **Can Run In Parallel**: YES (avec T4)
- **Parallel Group**: Wave 2 (avec Tasks 4, 5)
- **Blocks**: T5, T6, T8
@@ -416,20 +512,39 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References**:
- - `maintenance_server_data/models/service.py:15-22` — Définition originale de `service.instance` : les 4 champs existants (`equipment_id`, `service_id`, `version_id`, `service_url`). C'est le modèle qu'on étend via `_inherit`
- - `maintenance_server_monitoring/models/maintenance_equipment.py:128-218` — Pattern de la méthode `monitoring_test()` : comment itérer sur les records, exécuter des tests, écrire les logs, et déclencher la création de maintenance.request. S'en inspirer pour la structure générale mais NE PAS réutiliser la classe `MonitoringTest`
- - `maintenance_server_monitoring/models/maintenance_equipment.py:122-126` — Pattern de la méthode cron `cron_monitoring_test()` : comment écrire une méthode `@api.model` appelée par le cron qui fait un `self.search([])` puis appelle la méthode de test
+
+ - `maintenance_server_data/models/service.py:15-22` — Définition originale de
+ `service.instance` : les 4 champs existants (`equipment_id`, `service_id`,
+ `version_id`, `service_url`). C'est le modèle qu'on étend via `_inherit`
+ - `maintenance_server_monitoring/models/maintenance_equipment.py:128-218` — Pattern de
+ la méthode `monitoring_test()` : comment itérer sur les records, exécuter des tests,
+ écrire les logs, et déclencher la création de maintenance.request. S'en inspirer
+ pour la structure générale mais NE PAS réutiliser la classe `MonitoringTest`
+ - `maintenance_server_monitoring/models/maintenance_equipment.py:122-126` — Pattern de
+ la méthode cron `cron_monitoring_test()` : comment écrire une méthode `@api.model`
+ appelée par le cron qui fait un `self.search([])` puis appelle la méthode de test
**API/Type References**:
- - `maintenance_server_data/models/maintenance_equipment.py:9` — Le champ `service_ids = fields.One2many("service.instance", "equipment_id")` montre la relation inverse : depuis un `service.instance`, on accède à l'équipement via `self.equipment_id`
+
+ - `maintenance_server_data/models/maintenance_equipment.py:9` — Le champ
+ `service_ids = fields.One2many("service.instance", "equipment_id")` montre la
+ relation inverse : depuis un `service.instance`, on accède à l'équipement via
+ `self.equipment_id`
**External References**:
- - `requests` library : `requests.get(url, timeout=10)` — retourne un `Response` object avec `.status_code` (int). Exceptions possibles : `requests.ConnectionError`, `requests.Timeout`, `requests.RequestException` (base class)
+
+ - `requests` library : `requests.get(url, timeout=10)` — retourne un `Response` object
+ avec `.status_code` (int). Exceptions possibles : `requests.ConnectionError`,
+ `requests.Timeout`, `requests.RequestException` (base class)
**WHY Each Reference Matters**:
- - La définition originale de `service.instance` montre qu'il n'y a aucune logique métier sur ce modèle — on part d'une base simple
- - Le pattern `monitoring_test()` montre comment le repo gère déjà le monitoring : test → résultat → maintenance.request. On doit suivre la même philosophie
- - Le champ `service_url` est un simple `Char` — il peut contenir n'importe quoi, donc il faut valider/essayer avec un try/except
+
+ - La définition originale de `service.instance` montre qu'il n'y a aucune logique
+ métier sur ce modèle — on part d'une base simple
+ - Le pattern `monitoring_test()` montre comment le repo gère déjà le monitoring : test
+ → résultat → maintenance.request. On doit suivre la même philosophie
+ - Le champ `service_url` est un simple `Char` — il peut contenir n'importe quoi, donc
+ il faut valider/essayer avec un try/except
**Acceptance Criteria**:
@@ -463,23 +578,32 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
+
- Message: `feat(http_monitoring): add HTTP check logic on service.instance`
- Files: `maintenance_service_http_monitoring/models/service_instance.py`
- - Pre-commit: `python3 -c "import ast; ast.parse(open('maintenance_service_http_monitoring/models/service_instance.py').read())"`
+ - Pre-commit:
+ `python3 -c "import ast; ast.parse(open('maintenance_service_http_monitoring/models/service_instance.py').read())"`
---
-- [ ] 4. Extension de `maintenance.equipment` — mode maintenance + http_maintenance_request
+- [ ] 4. Extension de `maintenance.equipment` — mode maintenance +
+ http_maintenance_request
**What to do**:
+
- Créer `maintenance_service_http_monitoring/models/maintenance_equipment.py` :
- `_inherit = 'maintenance.equipment'`
- - Champ `maintenance_mode` (Boolean, string="Maintenance Mode", default=False, tracking=True) — tracking car `maintenance.equipment` hérite déjà `mail.thread`
- - Champ `maintenance_mode_start` (Datetime, string="Maintenance Mode Start", readonly=True)
- - Champ `maintenance_mode_end` (Datetime, string="Maintenance Mode End", readonly=True, help="Computed from start + configured duration")
- - Champ `http_maintenance_request` (Many2one to `maintenance.request`, string="HTTP Maintenance Request")
+ - Champ `maintenance_mode` (Boolean, string="Maintenance Mode", default=False,
+ tracking=True) — tracking car `maintenance.equipment` hérite déjà `mail.thread`
+ - Champ `maintenance_mode_start` (Datetime, string="Maintenance Mode Start",
+ readonly=True)
+ - Champ `maintenance_mode_end` (Datetime, string="Maintenance Mode End",
+ readonly=True, help="Computed from start + configured duration")
+ - Champ `http_maintenance_request` (Many2one to `maintenance.request`, string="HTTP
+ Maintenance Request")
- Méthode `action_activate_maintenance_mode(self)` :
- 1. Lit la durée depuis `ir.config_parameter` : `maintenance_service_http_monitoring.maintenance_mode_duration` (default 4h)
+ 1. Lit la durée depuis `ir.config_parameter` :
+ `maintenance_service_http_monitoring.maintenance_mode_duration` (default 4h)
2. Set `maintenance_mode = True`
3. Set `maintenance_mode_start = fields.Datetime.now()`
4. Set `maintenance_mode_end = now + timedelta(hours=duration)`
@@ -487,32 +611,42 @@ Max Concurrent: 3 (Wave 2)
1. Set `maintenance_mode = False`
2. Clear `maintenance_mode_start` et `maintenance_mode_end`
- Méthode `@api.model cron_deactivate_expired_maintenance_mode(self)` :
- 1. Recherche les équipements où `maintenance_mode = True` et `maintenance_mode_end <= now`
+ 1. Recherche les équipements où `maintenance_mode = True` et
+ `maintenance_mode_end <= now`
2. Appelle `action_deactivate_maintenance_mode()` sur ces records
- Méthode `create_http_maintenance_request(self, ko_services)` :
- 1. Vérifie si `http_maintenance_request` existe ET n'est pas done (`stage_id.done`)
+ 1. Vérifie si `http_maintenance_request` existe ET n'est pas done
+ (`stage_id.done`)
2. Si pas de request ouverte : crée une `maintenance.request` avec :
- `name`: `[HTTP KO] {equipment.name}`
- `equipment_id`: `self.id`
- `description`: Liste des services KO avec leur URL et code de statut
- `priority`: `'2'` (haute)
- `maintenance_type`: `'corrective'`
- - Assignation : `employee_id`, `technician_user_id`, `maintenance_team_id` (même pattern que `maintenance_server_monitoring`)
+ - Assignation : `employee_id`, `technician_user_id`, `maintenance_team_id`
+ (même pattern que `maintenance_server_monitoring`)
3. Stocke la référence dans `http_maintenance_request`
**Must NOT do**:
- - Ne PAS modifier le champ existant `error_maintenance_request` ni `warning_maintenance_request` (ceux-ci appartiennent au monitoring infra)
- - Ne PAS ajouter de logique HTTP dans ce fichier (la logique HTTP est dans `service_instance.py`)
+
+ - Ne PAS modifier le champ existant `error_maintenance_request` ni
+ `warning_maintenance_request` (ceux-ci appartiennent au monitoring infra)
+ - Ne PAS ajouter de logique HTTP dans ce fichier (la logique HTTP est dans
+ `service_instance.py`)
- Ne PAS auto-fermer la maintenance.request quand les services repassent OK
**Recommended Agent Profile**:
+
- **Category**: `unspecified-high`
- - Reason: Logique de mode maintenance avec auto-expiry, création de maintenance.request, interactions avec ir.config_parameter. Pas ultra-complexe mais nécessite rigueur
+ - Reason: Logique de mode maintenance avec auto-expiry, création de
+ maintenance.request, interactions avec ir.config_parameter. Pas ultra-complexe
+ mais nécessite rigueur
- **Skills**: []
- **Skills Evaluated but Omitted**:
- `git-master`: Pas de commit dans cette tâche seule
**Parallelization**:
+
- **Can Run In Parallel**: YES (avec T3 et T5)
- **Parallel Group**: Wave 2 (avec Tasks 3, 5)
- **Blocks**: T7, T8
@@ -521,16 +655,31 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References**:
- - `maintenance_server_monitoring/models/maintenance_equipment.py:194-218` — **PATTERN CLÉ** : Création de `maintenance.request` avec vérification de doublon. Reproduire EXACTEMENT ce pattern : vérifier `stage_id.done` avant de créer, assigner `employee_id`, `technician_user_id`, `maintenance_team_id`. Utiliser le même fallback `self.env["maintenance.team"].search([], limit=1)` si pas de team assignée
- - `maintenance_server_monitoring/models/maintenance_equipment.py:37-53` — Champs existants sur equipment pour comprendre la convention de nommage (readonly, strings)
- - `maintenance_server_monitoring/models/maintenance_equipment.py:52-53` — Champs `error_maintenance_request` et `warning_maintenance_request` : même type de champ Many2one vers `maintenance.request`. Le nouveau `http_maintenance_request` suit le même pattern
+
+ - `maintenance_server_monitoring/models/maintenance_equipment.py:194-218` — **PATTERN
+ CLÉ** : Création de `maintenance.request` avec vérification de doublon. Reproduire
+ EXACTEMENT ce pattern : vérifier `stage_id.done` avant de créer, assigner
+ `employee_id`, `technician_user_id`, `maintenance_team_id`. Utiliser le même
+ fallback `self.env["maintenance.team"].search([], limit=1)` si pas de team assignée
+ - `maintenance_server_monitoring/models/maintenance_equipment.py:37-53` — Champs
+ existants sur equipment pour comprendre la convention de nommage (readonly, strings)
+ - `maintenance_server_monitoring/models/maintenance_equipment.py:52-53` — Champs
+ `error_maintenance_request` et `warning_maintenance_request` : même type de champ
+ Many2one vers `maintenance.request`. Le nouveau `http_maintenance_request` suit le
+ même pattern
**API/Type References**:
- - `maintenance.request` fields utilisés dans la création : `name`, `equipment_id`, `employee_id`, `user_id`, `maintenance_team_id`, `priority`, `maintenance_type`, `description`
+
+ - `maintenance.request` fields utilisés dans la création : `name`, `equipment_id`,
+ `employee_id`, `user_id`, `maintenance_team_id`, `priority`, `maintenance_type`,
+ `description`
**WHY Each Reference Matters**:
- - Le pattern de création de maintenance.request (lignes 194-218) est LE pattern à reproduire — c'est la logique anti-doublon qui évite de spammer les requests
- - Les champs existants sur equipment montrent la convention du repo (readonly pour les champs calculés, tracking quand pertinent)
+
+ - Le pattern de création de maintenance.request (lignes 194-218) est LE pattern à
+ reproduire — c'est la logique anti-doublon qui évite de spammer les requests
+ - Les champs existants sur equipment montrent la convention du repo (readonly pour les
+ champs calculés, tracking quand pertinent)
**Acceptance Criteria**:
@@ -576,15 +725,19 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
- - Message: `feat(http_monitoring): add maintenance mode and HTTP request tracking on equipment`
+
+ - Message:
+ `feat(http_monitoring): add maintenance mode and HTTP request tracking on equipment`
- Files: `maintenance_service_http_monitoring/models/maintenance_equipment.py`
- - Pre-commit: `python3 -c "import ast; ast.parse(open('maintenance_service_http_monitoring/models/maintenance_equipment.py').read())"`
+ - Pre-commit:
+ `python3 -c "import ast; ast.parse(open('maintenance_service_http_monitoring/models/maintenance_equipment.py').read())"`
---
- [ ] 5. Cron HTTP monitoring (XML)
**What to do**:
+
- Créer `maintenance_service_http_monitoring/data/cron.xml` avec 2 crons :
1. **Cron HTTP check** :
- `id`: `ir_cron_http_service_monitoring`
@@ -604,18 +757,23 @@ Max Concurrent: 3 (Wave 2)
- `interval_type`: `minutes`
- `numbercall`: `-1`
- `doall`: `False`
- - S'assurer que `data/cron.xml` est listé dans `__manifest__.py` data (après les views)
+ - S'assurer que `data/cron.xml` est listé dans `__manifest__.py` data (après les
+ views)
**Must NOT do**:
- - Ne PAS modifier le cron existant `ir_cron_server_monitoring` dans `maintenance_server_monitoring`
+
+ - Ne PAS modifier le cron existant `ir_cron_server_monitoring` dans
+ `maintenance_server_monitoring`
- Ne PAS mettre une fréquence trop basse (< 15 minutes) pour le cron d'expiry
**Recommended Agent Profile**:
+
- **Category**: `quick`
- Reason: Fichier XML simple suivant un pattern existant
- **Skills**: []
**Parallelization**:
+
- **Can Run In Parallel**: YES (avec T3 et T4)
- **Parallel Group**: Wave 2 (avec Tasks 3, 4)
- **Blocks**: T8
@@ -624,10 +782,15 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References**:
- - `maintenance_server_monitoring/data/cron.xml:1-12` — **Pattern exact** à reproduire pour le format XML du cron. Même structure ``, mêmes champs. Adapter `model_id`, `code`, `interval_number`, `interval_type`
+
+ - `maintenance_server_monitoring/data/cron.xml:1-12` — **Pattern exact** à reproduire
+ pour le format XML du cron. Même structure ``, mêmes champs. Adapter
+ `model_id`, `code`, `interval_number`, `interval_type`
**WHY Each Reference Matters**:
- - Le format du cron XML doit être EXACTEMENT celui d'Odoo 16 sinon le module ne s'installe pas. Le fichier existant montre le format validé
+
+ - Le format du cron XML doit être EXACTEMENT celui d'Odoo 16 sinon le module ne
+ s'installe pas. Le fichier existant montre le format validé
**Acceptance Criteria**:
@@ -651,6 +814,7 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
+
- Message: `feat(http_monitoring): add HTTP check and maintenance mode expiry crons`
- Files: `maintenance_service_http_monitoring/data/cron.xml`
- Pre-commit: N/A
@@ -660,15 +824,19 @@ Max Concurrent: 3 (Wave 2)
- [ ] 6. Vue liste `service.instance` (tree + action + menu)
**What to do**:
+
- Créer `maintenance_service_http_monitoring/views/service_instance_views.xml` :
- **Tree view** `service_instance_http_monitoring_tree` :
- - Colonnes : `equipment_id`, `service_id` (nom du service), `version_id`, `service_url`, `last_http_check_date`, `last_http_status_code`, `http_status_ok`
- - Décoration conditionnelle : `decoration-danger="http_status_ok == False"` pour les lignes KO
+ - Colonnes : `equipment_id`, `service_id` (nom du service), `version_id`,
+ `service_url`, `last_http_check_date`, `last_http_status_code`, `http_status_ok`
+ - Décoration conditionnelle : `decoration-danger="http_status_ok == False"` pour
+ les lignes KO
- **Action** `service_instance_http_monitoring_action` :
- `res_model`: `service.instance`
- `view_mode`: `tree`
- `view_id`: ref vers la tree view ci-dessus
- - `domain`: `[('service_url', '!=', False)]` — ne montrer que les services avec une URL
+ - `domain`: `[('service_url', '!=', False)]` — ne montrer que les services avec
+ une URL
- Texte d'aide pour la vue vide
- **Menu item** :
- `parent`: `maintenance.menu_maintenance_title` (menu principal Maintenance)
@@ -678,11 +846,13 @@ Max Concurrent: 3 (Wave 2)
- S'assurer que le fichier est listé dans `__manifest__.py` data
**Must NOT do**:
+
- Ne PAS créer de form view, kanban, pivot ou graph
- Ne PAS ajouter de search panel complexe
- Ne PAS modifier les vues existantes dans `maintenance_server_data`
**Recommended Agent Profile**:
+
- **Category**: `quick`
- Reason: Vue XML standard, pas de logique complexe
- **Skills**: []
@@ -690,6 +860,7 @@ Max Concurrent: 3 (Wave 2)
- `frontend-ui-ux`: Pas de design custom, c'est une tree view Odoo standard
**Parallelization**:
+
- **Can Run In Parallel**: YES (avec T7)
- **Parallel Group**: Wave 3 (avec Task 7)
- **Blocks**: T8
@@ -698,14 +869,23 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References**:
- - `maintenance_server_data/views/service.xml:4-12` — Tree view existante de `service` : format exact d'une tree view pour un modèle service. Pattern à suivre pour la structure XML
- - `maintenance_server_data/views/service.xml:37-47` — Action existante pour `service` : format d'un `ir.actions.act_window`. Reproduire la structure
- - `maintenance_server_data/views/service.xml:74-93` — Menus existants : comment les menus sont enregistrés sous `maintenance.menu_maintenance_configuration`. Notre menu ira sous `maintenance.menu_maintenance_title` (un niveau plus haut)
- - `maintenance_server_monitoring/views/maintenance_equipment_views.xml:27-39` — Tree view avec `optional="hide"` : pattern pour les colonnes optionnelles
+
+ - `maintenance_server_data/views/service.xml:4-12` — Tree view existante de `service`
+ : format exact d'une tree view pour un modèle service. Pattern à suivre pour la
+ structure XML
+ - `maintenance_server_data/views/service.xml:37-47` — Action existante pour `service`
+ : format d'un `ir.actions.act_window`. Reproduire la structure
+ - `maintenance_server_data/views/service.xml:74-93` — Menus existants : comment les
+ menus sont enregistrés sous `maintenance.menu_maintenance_configuration`. Notre menu
+ ira sous `maintenance.menu_maintenance_title` (un niveau plus haut)
+ - `maintenance_server_monitoring/views/maintenance_equipment_views.xml:27-39` — Tree
+ view avec `optional="hide"` : pattern pour les colonnes optionnelles
**WHY Each Reference Matters**:
+
- Les vues service existantes montrent le format XML exact utilisé dans ce repo
- - Le menu existant montre la hiérarchie des menus Maintenance — notre menu doit se placer au bon niveau
+ - Le menu existant montre la hiérarchie des menus Maintenance — notre menu doit se
+ placer au bon niveau
- Le pattern `decoration-danger` est standard Odoo pour colorer les lignes en rouge
**Acceptance Criteria**:
@@ -730,7 +910,9 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
- - Message: `feat(http_monitoring): add service instance list view with HTTP monitoring columns`
+
+ - Message:
+ `feat(http_monitoring): add service instance list view with HTTP monitoring columns`
- Files: `maintenance_service_http_monitoring/views/service_instance_views.xml`
- Pre-commit: N/A
@@ -739,32 +921,44 @@ Max Concurrent: 3 (Wave 2)
- [ ] 7. Vue equipment — bandeau mode maintenance
**What to do**:
+
- Créer `maintenance_service_http_monitoring/views/maintenance_equipment_views.xml` :
- **Form view** (hérite de `maintenance.hr_equipment_view_form`) :
- - **Banner** : widget `web_ribbon` ou `div` conditionnel en haut du formulaire, visible quand `maintenance_mode == True`
+ - **Banner** : widget `web_ribbon` ou `div` conditionnel en haut du formulaire,
+ visible quand `maintenance_mode == True`
- Texte : "Mode maintenance actif — Vérifications HTTP désactivées"
- Afficher la date de fin prévue (`maintenance_mode_end`)
- **Boutons** dans le header ou dans un groupe :
- - Bouton "Activer le mode maintenance" (`action_activate_maintenance_mode`) visible si `maintenance_mode == False`
- - Bouton "Désactiver le mode maintenance" (`action_deactivate_maintenance_mode`) visible si `maintenance_mode == True`
+ - Bouton "Activer le mode maintenance" (`action_activate_maintenance_mode`)
+ visible si `maintenance_mode == False`
+ - Bouton "Désactiver le mode maintenance" (`action_deactivate_maintenance_mode`)
+ visible si `maintenance_mode == True`
- **Champs** dans un groupe ou dans l'onglet Monitoring existant :
- `maintenance_mode` (si pas déjà montré via le bandeau)
- `maintenance_mode_start` (readonly)
- `maintenance_mode_end` (readonly)
- - `http_maintenance_request` (readonly, lien cliquable vers la maintenance.request)
- - **Tree view** (optionnel) : ajouter `maintenance_mode` comme colonne optionnelle dans la liste des équipements
+ - `http_maintenance_request` (readonly, lien cliquable vers la
+ maintenance.request)
+ - **Tree view** (optionnel) : ajouter `maintenance_mode` comme colonne optionnelle
+ dans la liste des équipements
**Must NOT do**:
- - Ne PAS modifier la vue existante dans `maintenance_server_monitoring/views/maintenance_equipment_views.xml`
- - Ne PAS modifier la vue existante dans `maintenance_server_data/views/maintenance_equipment.xml`
- - Ne PAS ajouter de JS/OWL custom pour le bandeau — utiliser les widgets XML standard d'Odoo
+
+ - Ne PAS modifier la vue existante dans
+ `maintenance_server_monitoring/views/maintenance_equipment_views.xml`
+ - Ne PAS modifier la vue existante dans
+ `maintenance_server_data/views/maintenance_equipment.xml`
+ - Ne PAS ajouter de JS/OWL custom pour le bandeau — utiliser les widgets XML standard
+ d'Odoo
**Recommended Agent Profile**:
+
- **Category**: `quick`
- Reason: Vue XML standard avec héritage, pas de logique complexe
- **Skills**: []
**Parallelization**:
+
- **Can Run In Parallel**: YES (avec T6)
- **Parallel Group**: Wave 3 (avec Task 6)
- **Blocks**: T8
@@ -773,16 +967,28 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References**:
- - `maintenance_server_monitoring/views/maintenance_equipment_views.xml:3-24` — **Pattern exact** d'héritage de la form view equipment. Utilise `xpath expr="//notebook" position="inside"` pour ajouter un onglet. Pour le bandeau, utiliser `xpath expr="//sheet" position="before"` ou similaire
- - `maintenance_server_data/views/maintenance_equipment.xml:3-35` — Autre héritage de la même form view, montre comment utiliser différents `xpath` positions
- - `maintenance_server_monitoring/views/maintenance_equipment_views.xml:27-39` — Pattern tree view héritage avec `optional="hide"`
+
+ - `maintenance_server_monitoring/views/maintenance_equipment_views.xml:3-24` —
+ **Pattern exact** d'héritage de la form view equipment. Utilise
+ `xpath expr="//notebook" position="inside"` pour ajouter un onglet. Pour le bandeau,
+ utiliser `xpath expr="//sheet" position="before"` ou similaire
+ - `maintenance_server_data/views/maintenance_equipment.xml:3-35` — Autre héritage de
+ la même form view, montre comment utiliser différents `xpath` positions
+ - `maintenance_server_monitoring/views/maintenance_equipment_views.xml:27-39` —
+ Pattern tree view héritage avec `optional="hide"`
**External References**:
- - Odoo 16 banner/ribbon patterns : `
` — pattern standard pour les bandeaux conditionnels dans les forms Odoo
+
+ - Odoo 16 banner/ribbon patterns :
+ `
`
+ — pattern standard pour les bandeaux conditionnels dans les forms Odoo
**WHY Each Reference Matters**:
- - Les vues existantes montrent les xpath utilisés pour insérer du contenu dans la form equipment — on doit utiliser des xpath compatibles (pas en conflit)
- - Le pattern de bandeau conditionnel est standard Odoo mais il faut le bon `attrs` invisible
+
+ - Les vues existantes montrent les xpath utilisés pour insérer du contenu dans la form
+ equipment — on doit utiliser des xpath compatibles (pas en conflit)
+ - Le pattern de bandeau conditionnel est standard Odoo mais il faut le bon `attrs`
+ invisible
**Acceptance Criteria**:
@@ -806,7 +1012,9 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
- - Message: `feat(http_monitoring): add maintenance mode banner and controls on equipment form`
+
+ - Message:
+ `feat(http_monitoring): add maintenance mode banner and controls on equipment form`
- Files: `maintenance_service_http_monitoring/views/maintenance_equipment_views.xml`
- Pre-commit: N/A
@@ -815,6 +1023,7 @@ Max Concurrent: 3 (Wave 2)
- [ ] 8. Tests unitaires + pre-commit
**What to do**:
+
- Créer `maintenance_service_http_monitoring/tests/__init__.py` :
- Import du module de test
- Créer `maintenance_service_http_monitoring/tests/test_http_monitoring.py` :
@@ -825,7 +1034,8 @@ Max Concurrent: 3 (Wave 2)
- **Test 1 — HTTP 200 → service OK** :
- Mock `requests.get` pour retourner status_code=200
- Appeler `check_http_status()` sur le service instance
- - Asserter : `http_status_ok == True`, `last_http_status_code == 200`, `last_http_check_date` rempli
+ - Asserter : `http_status_ok == True`, `last_http_status_code == 200`,
+ `last_http_check_date` rempli
- **Test 2 — HTTP 500 → service KO + maintenance.request créée** :
- Mock `requests.get` pour retourner status_code=500
- Appeler `check_http_status()`
@@ -853,19 +1063,24 @@ Max Concurrent: 3 (Wave 2)
- Créer un service.instance sans `service_url`
- Appeler `cron_check_http_services()`
- Vérifier que le service n'a PAS été vérifié
- - Exécuter `pre-commit run --all-files` et corriger tous les problèmes de lint/format sur TOUS les fichiers du module
+ - Exécuter `pre-commit run --all-files` et corriger tous les problèmes de lint/format
+ sur TOUS les fichiers du module
**Must NOT do**:
+
- Ne PAS faire de vrais appels HTTP dans les tests
- Ne PAS tester les vues (pas de `HttpCase` pour ce module)
- Ne PAS ajouter de tests de performance
**Recommended Agent Profile**:
+
- **Category**: `unspecified-high`
- - Reason: Tests complets avec mocking, setUp complexe (création d'environnement Odoo de test), et passage du pre-commit sur tout le module
+ - Reason: Tests complets avec mocking, setUp complexe (création d'environnement Odoo
+ de test), et passage du pre-commit sur tout le module
- **Skills**: []
**Parallelization**:
+
- **Can Run In Parallel**: NO
- **Parallel Group**: Wave 4 (seul)
- **Blocks**: F1-F4
@@ -874,7 +1089,9 @@ Max Concurrent: 3 (Wave 2)
**References** (CRITICAL):
**Pattern References**:
+
- Aucun test n'existe dans ce repo. Utiliser le pattern standard Odoo 16 :
+
```python
from odoo.tests.common import TransactionCase
from unittest.mock import patch, MagicMock
@@ -886,11 +1103,18 @@ Max Concurrent: 3 (Wave 2)
```
**External References**:
- - Odoo 16 testing : `TransactionCase` est le test case standard pour les tests de modèle (pas de HTTP). Chaque test tourne dans une transaction qui est rollback à la fin
- - `unittest.mock.patch` : utiliser `@patch('odoo.addons.maintenance_service_http_monitoring.models.service_instance.requests')` pour mocker la librairie requests au bon niveau
+
+ - Odoo 16 testing : `TransactionCase` est le test case standard pour les tests de
+ modèle (pas de HTTP). Chaque test tourne dans une transaction qui est rollback à la
+ fin
+ - `unittest.mock.patch` : utiliser
+ `@patch('odoo.addons.maintenance_service_http_monitoring.models.service_instance.requests')`
+ pour mocker la librairie requests au bon niveau
**WHY Each Reference Matters**:
- - Le path de mock est CRITIQUE : il faut mocker `requests` là où il est importé (dans le module service_instance), pas globalement. Sinon le mock ne prend pas effet
+
+ - Le path de mock est CRITIQUE : il faut mocker `requests` là où il est importé (dans
+ le module service_instance), pas globalement. Sinon le mock ne prend pas effet
- `TransactionCase` rollback automatiquement — pas besoin de cleanup
**Acceptance Criteria**:
@@ -921,8 +1145,12 @@ Max Concurrent: 3 (Wave 2)
```
**Commit**: YES
- - Message: `feat(http_monitoring): add unit tests for HTTP monitoring and pre-commit fixes`
- - Files: `maintenance_service_http_monitoring/tests/__init__.py`, `maintenance_service_http_monitoring/tests/test_http_monitoring.py`, et tout fichier modifié par pre-commit
+
+ - Message:
+ `feat(http_monitoring): add unit tests for HTTP monitoring and pre-commit fixes`
+ - Files: `maintenance_service_http_monitoring/tests/__init__.py`,
+ `maintenance_service_http_monitoring/tests/test_http_monitoring.py`, et tout fichier
+ modifié par pre-commit
- Pre-commit: `pre-commit run --all-files`
---
@@ -931,42 +1159,59 @@ Max Concurrent: 3 (Wave 2)
> 4 review agents run in PARALLEL. ALL must APPROVE. Rejection → fix → re-run.
-- [ ] F1. **Plan Compliance Audit** — `oracle`
- Lire le plan end-to-end. Pour chaque "Must Have": vérifier que l'implémentation existe (lire le fichier, vérifier le code). Pour chaque "Must NOT Have": chercher les patterns interdits dans le codebase — rejeter avec file:line si trouvé. Vérifier que les fichiers evidence existent dans `.sisyphus/evidence/`. Comparer les deliverables avec le plan.
- Output: `Must Have [N/N] | Must NOT Have [N/N] | Tasks [N/N] | VERDICT: APPROVE/REJECT`
+- [ ] F1. **Plan Compliance Audit** — `oracle` Lire le plan end-to-end. Pour chaque
+ "Must Have": vérifier que l'implémentation existe (lire le fichier, vérifier le
+ code). Pour chaque "Must NOT Have": chercher les patterns interdits dans le
+ codebase — rejeter avec file:line si trouvé. Vérifier que les fichiers evidence
+ existent dans `.sisyphus/evidence/`. Comparer les deliverables avec le plan.
+ Output:
+ `Must Have [N/N] | Must NOT Have [N/N] | Tasks [N/N] | VERDICT: APPROVE/REJECT`
-- [ ] F2. **Code Quality Review** — `unspecified-high`
- Exécuter le linter + `pre-commit run --all-files`. Reviewer tous les fichiers modifiés/créés pour : `as any`/`@ts-ignore` (N/A ici), empty catches, `print()` en prod, code commenté, imports inutilisés. Vérifier AI slop : commentaires excessifs, sur-abstraction, noms génériques (data/result/item/temp). Vérifier que chaque `requests.get()` a un `timeout=`.
- Output: `Lint [PASS/FAIL] | Pre-commit [PASS/FAIL] | Files [N clean/N issues] | VERDICT`
+- [ ] F2. **Code Quality Review** — `unspecified-high` Exécuter le linter +
+ `pre-commit run --all-files`. Reviewer tous les fichiers modifiés/créés pour :
+ `as any`/`@ts-ignore` (N/A ici), empty catches, `print()` en prod, code commenté,
+ imports inutilisés. Vérifier AI slop : commentaires excessifs, sur-abstraction,
+ noms génériques (data/result/item/temp). Vérifier que chaque `requests.get()` a un
+ `timeout=`. Output:
+ `Lint [PASS/FAIL] | Pre-commit [PASS/FAIL] | Files [N clean/N issues] | VERDICT`
-- [ ] F3. **Real Manual QA** — `unspecified-high`
- Exécuter CHAQUE QA scenario de CHAQUE task — suivre les étapes exactes, capturer les preuves. Vérifier la cohérence inter-tâches : les méthodes appelées par les crons existent-elles ? Les champs référencés dans les vues existent-ils dans les modèles ? Les imports dans `__init__.py` correspondent-ils aux fichiers créés ? Sauvegarder dans `.sisyphus/evidence/final-qa/`.
- Output: `Scenarios [N/N pass] | Integration [N/N] | Edge Cases [N tested] | VERDICT`
+- [ ] F3. **Real Manual QA** — `unspecified-high` Exécuter CHAQUE QA scenario de CHAQUE
+ task — suivre les étapes exactes, capturer les preuves. Vérifier la cohérence
+ inter-tâches : les méthodes appelées par les crons existent-elles ? Les champs
+ référencés dans les vues existent-ils dans les modèles ? Les imports dans
+ `__init__.py` correspondent-ils aux fichiers créés ? Sauvegarder dans
+ `.sisyphus/evidence/final-qa/`. Output:
+ `Scenarios [N/N pass] | Integration [N/N] | Edge Cases [N tested] | VERDICT`
-- [ ] F4. **Scope Fidelity Check** — `deep`
- Pour chaque task : lire "What to do", lire le code réel. Vérifier 1:1 — tout ce qui est spécifié a été implémenté (rien manquant), rien au-delà n'a été ajouté (pas de scope creep). Vérifier la conformité "Must NOT do". Vérifier qu'AUCUN fichier des modules existants (`maintenance_server_monitoring`, `maintenance_server_ssh`, `maintenance_server_data`) n'a été modifié.
- Output: `Tasks [N/N compliant] | Scope Creep [CLEAN/N issues] | Existing Modules [UNTOUCHED/N issues] | VERDICT`
+- [ ] F4. **Scope Fidelity Check** — `deep` Pour chaque task : lire "What to do", lire
+ le code réel. Vérifier 1:1 — tout ce qui est spécifié a été implémenté (rien
+ manquant), rien au-delà n'a été ajouté (pas de scope creep). Vérifier la
+ conformité "Must NOT do". Vérifier qu'AUCUN fichier des modules existants
+ (`maintenance_server_monitoring`, `maintenance_server_ssh`,
+ `maintenance_server_data`) n'a été modifié. Output:
+ `Tasks [N/N compliant] | Scope Creep [CLEAN/N issues] | Existing Modules [UNTOUCHED/N issues] | VERDICT`
---
## Commit Strategy
-| After Task | Message | Files | Verification |
-|------------|---------|-------|--------------|
-| T1 | `feat(http_monitoring): scaffold new module maintenance_service_http_monitoring` | `__manifest__.py`, `__init__.py`, `models/__init__.py`, `security/ir.model.access.csv` | structure check |
-| T2 | `feat(http_monitoring): add module settings for check frequency and maintenance mode duration` | `models/res_config_settings.py`, `views/res_config_settings_views.xml` | syntax check |
-| T3 | `feat(http_monitoring): add HTTP check logic on service.instance` | `models/service_instance.py` | syntax + timeout check |
-| T4 | `feat(http_monitoring): add maintenance mode and HTTP request tracking on equipment` | `models/maintenance_equipment.py` | syntax + pattern check |
-| T5 | `feat(http_monitoring): add HTTP check and maintenance mode expiry crons` | `data/cron.xml` | XML validation |
-| T6 | `feat(http_monitoring): add service instance list view with HTTP monitoring columns` | `views/service_instance_views.xml` | XML validation |
-| T7 | `feat(http_monitoring): add maintenance mode banner and controls on equipment form` | `views/maintenance_equipment_views.xml` | XML validation |
-| T8 | `feat(http_monitoring): add unit tests and pre-commit compliance` | `tests/__init__.py`, `tests/test_http_monitoring.py` | pre-commit pass |
+| After Task | Message | Files | Verification |
+| ---------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- |
+| T1 | `feat(http_monitoring): scaffold new module maintenance_service_http_monitoring` | `__manifest__.py`, `__init__.py`, `models/__init__.py`, `security/ir.model.access.csv` | structure check |
+| T2 | `feat(http_monitoring): add module settings for check frequency and maintenance mode duration` | `models/res_config_settings.py`, `views/res_config_settings_views.xml` | syntax check |
+| T3 | `feat(http_monitoring): add HTTP check logic on service.instance` | `models/service_instance.py` | syntax + timeout check |
+| T4 | `feat(http_monitoring): add maintenance mode and HTTP request tracking on equipment` | `models/maintenance_equipment.py` | syntax + pattern check |
+| T5 | `feat(http_monitoring): add HTTP check and maintenance mode expiry crons` | `data/cron.xml` | XML validation |
+| T6 | `feat(http_monitoring): add service instance list view with HTTP monitoring columns` | `views/service_instance_views.xml` | XML validation |
+| T7 | `feat(http_monitoring): add maintenance mode banner and controls on equipment form` | `views/maintenance_equipment_views.xml` | XML validation |
+| T8 | `feat(http_monitoring): add unit tests and pre-commit compliance` | `tests/__init__.py`, `tests/test_http_monitoring.py` | pre-commit pass |
---
## Success Criteria
### Verification Commands
+
```bash
# Structure du module
ls maintenance_service_http_monitoring/{__manifest__.py,__init__.py,models/__init__.py,security/ir.model.access.csv}
@@ -988,9 +1233,12 @@ pre-commit run --all-files # Expected: exit 0
```
### Final Checklist
+
- [ ] Nouveau module `maintenance_service_http_monitoring/` créé et complet
-- [ ] `service.instance` étendu avec `last_http_status_code`, `last_http_check_date`, `http_status_ok`
-- [ ] `maintenance.equipment` étendu avec `maintenance_mode`, `maintenance_mode_start`, `maintenance_mode_end`, `http_maintenance_request`
+- [ ] `service.instance` étendu avec `last_http_status_code`, `last_http_check_date`,
+ `http_status_ok`
+- [ ] `maintenance.equipment` étendu avec `maintenance_mode`, `maintenance_mode_start`,
+ `maintenance_mode_end`, `http_maintenance_request`
- [ ] Cron HTTP check configuré (toutes les heures par défaut)
- [ ] Cron maintenance mode expiry configuré (toutes les 15 minutes)
- [ ] Settings page avec fréquence et durée mode maintenance
diff --git a/maintenance_service_http_monitoring/__manifest__.py b/maintenance_service_http_monitoring/__manifest__.py
index 37a8ca3..33fe667 100644
--- a/maintenance_service_http_monitoring/__manifest__.py
+++ b/maintenance_service_http_monitoring/__manifest__.py
@@ -10,7 +10,7 @@
"data": [
"data/cron.xml",
"views/service_instance_views.xml",
- "views/maintenance_equipment_views.xml"
+ "views/maintenance_equipment_views.xml",
],
- "installable": True
+ "installable": True,
}
diff --git a/maintenance_service_http_monitoring/data/cron.xml b/maintenance_service_http_monitoring/data/cron.xml
index a2f023e..3942a7b 100644
--- a/maintenance_service_http_monitoring/data/cron.xml
+++ b/maintenance_service_http_monitoring/data/cron.xml
@@ -1,8 +1,8 @@
-
+
HTTP Service Monitoring : check all services
-
+ codemodel.cron_check_http_services()15
@@ -11,8 +11,13 @@
False
- HTTP Service Monitoring : deactivate expired maintenance mode
-
+ HTTP Service Monitoring : deactivate expired maintenance mode
+ codemodel.cron_deactivate_expired_maintenance_mode()15
diff --git a/maintenance_service_http_monitoring/models/maintenance_equipment.py b/maintenance_service_http_monitoring/models/maintenance_equipment.py
index 3a86cb3..c864f5c 100644
--- a/maintenance_service_http_monitoring/models/maintenance_equipment.py
+++ b/maintenance_service_http_monitoring/models/maintenance_equipment.py
@@ -1,8 +1,10 @@
from datetime import timedelta
-from odoo import models, fields, api
+
+from odoo import api, fields, models
+
class MaintenanceEquipment(models.Model):
- _inherit = 'maintenance.equipment'
+ _inherit = "maintenance.equipment"
maintenance_mode = fields.Boolean(
string="Maintenance Mode",
@@ -19,37 +21,48 @@ class MaintenanceEquipment(models.Model):
help="Computed from start + configured duration",
)
http_maintenance_request = fields.Many2one(
- 'maintenance.request',
+ "maintenance.request",
string="HTTP Maintenance Request",
readonly=True,
)
def action_activate_maintenance_mode(self):
for rec in self:
- duration = int(self.env['ir.config_parameter'].sudo().get_param(
- 'maintenance_service_http_monitoring.maintenance_mode_duration', 4))
+ duration = int(
+ self.env["ir.config_parameter"]
+ .sudo()
+ .get_param(
+ "maintenance_service_http_monitoring.maintenance_mode_duration", 4
+ )
+ )
now = fields.Datetime.now()
- rec.write({
- 'maintenance_mode': True,
- 'maintenance_mode_start': now,
- 'maintenance_mode_end': now + timedelta(hours=duration),
- })
+ rec.write(
+ {
+ "maintenance_mode": True,
+ "maintenance_mode_start": now,
+ "maintenance_mode_end": now + timedelta(hours=duration),
+ }
+ )
def action_deactivate_maintenance_mode(self):
for rec in self:
- rec.write({
- 'maintenance_mode': False,
- 'maintenance_mode_start': False,
- 'maintenance_mode_end': False,
- })
+ rec.write(
+ {
+ "maintenance_mode": False,
+ "maintenance_mode_start": False,
+ "maintenance_mode_end": False,
+ }
+ )
@api.model
def cron_deactivate_expired_maintenance_mode(self):
now = fields.Datetime.now()
- expired = self.search([
- ('maintenance_mode', '=', True),
- ('maintenance_mode_end', '<=', now),
- ])
+ expired = self.search(
+ [
+ ("maintenance_mode", "=", True),
+ ("maintenance_mode_end", "<=", now),
+ ]
+ )
expired.action_deactivate_maintenance_mode()
def create_http_maintenance_request(self, ko_services):
@@ -57,13 +70,13 @@ class MaintenanceEquipment(models.Model):
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"),
+ ("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)
+ 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
@@ -72,26 +85,26 @@ class MaintenanceEquipment(models.Model):
if request and not request.stage_id.done:
return request
vals = {
- 'name': name,
- 'equipment_id': self.id,
- 'priority': '2',
- 'maintenance_type': 'corrective',
- 'description': self._build_ko_services_description(ko_services),
+ "name": name,
+ "equipment_id": self.id,
+ "priority": "2",
+ "maintenance_type": "corrective",
+ "description": self._build_ko_services_description(ko_services),
}
if self.employee_id:
- vals['employee_id'] = self.employee_id.id
+ vals["employee_id"] = self.employee_id.id
if self.technician_user_id:
- vals['user_id'] = self.technician_user_id.id
+ vals["user_id"] = self.technician_user_id.id
if self.maintenance_team_id:
- vals['maintenance_team_id'] = self.maintenance_team_id.id
+ vals["maintenance_team_id"] = self.maintenance_team_id.id
else:
- team = self.env['maintenance.team'].search([], limit=1)
+ team = self.env["maintenance.team"].search([], limit=1)
if team:
- vals['maintenance_team_id'] = team.id
- request = self.env['maintenance.request'].create(vals)
+ vals["maintenance_team_id"] = team.id
+ request = self.env["maintenance.request"].create(vals)
self.http_maintenance_request = request.id
return request
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)
+ 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 0841ea3..f629e1b 100644
--- a/maintenance_service_http_monitoring/models/service_instance.py
+++ b/maintenance_service_http_monitoring/models/service_instance.py
@@ -1,6 +1,6 @@
import logging
-from datetime import datetime
-from odoo import models, fields, api
+
+from odoo import api, fields, models
try:
import requests
@@ -11,8 +11,9 @@ _logger = logging.getLogger(__name__)
HTTP_CHECK_TIMEOUT = 10 # seconds
+
class ServiceInstance(models.Model):
- _inherit = 'service.instance'
+ _inherit = "service.instance"
last_http_status_code = fields.Integer(
string="Last HTTP Status Code",
@@ -34,7 +35,7 @@ class ServiceInstance(models.Model):
if not rec.service_url or not rec.equipment_id:
continue
equipment = rec.equipment_id
- if getattr(equipment, 'maintenance_mode', False):
+ if getattr(equipment, "maintenance_mode", False):
continue
status_ok = False
status_code = -1
@@ -45,25 +46,31 @@ class ServiceInstance(models.Model):
try:
response = requests.get(url, timeout=HTTP_CHECK_TIMEOUT)
status_code = response.status_code
- status_ok = (status_code == 200)
+ status_ok = status_code == 200
except requests.exceptions.RequestException as e:
- _logger.warning(f"HTTP check failed for %s: %s", rec.service_url, e)
- rec.write({
- 'last_http_status_code': status_code,
- 'last_http_check_date': now,
- 'http_status_ok': status_ok,
- })
+ _logger.warning("HTTP check failed for %s: %s", rec.service_url, e)
+ rec.write(
+ {
+ "last_http_status_code": status_code,
+ "last_http_check_date": now,
+ "http_status_ok": status_ok,
+ }
+ )
if not status_ok:
# Delegate maintenance.request creation to equipment
- if hasattr(equipment, 'create_http_maintenance_request'):
+ if hasattr(equipment, "create_http_maintenance_request"):
equipment.create_http_maintenance_request([rec])
@api.model
def cron_check_http_services(self):
- domain = [('active', '=', True), ('service_url', '!=', False), ('equipment_id', '!=', False)]
+ domain = [
+ ("active", "=", True),
+ ("service_url", "!=", False),
+ ("equipment_id", "!=", False),
+ ]
services = self.search(domain)
for service in services:
equipment = service.equipment_id
- if getattr(equipment, 'maintenance_mode', False):
+ if getattr(equipment, "maintenance_mode", False):
continue
service.check_http_status()
diff --git a/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml b/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml
index a1f4e79..07ed61d 100644
--- a/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml
+++ b/maintenance_service_http_monitoring/views/maintenance_equipment_views.xml
@@ -1,29 +1,42 @@
-
+
maintenance.equipment.form.http.monitoringmaintenance.equipment
-
+
-