bemade-addons/unifi_integration/models/unifi_routing.py
2025-03-26 08:46:03 -04:00

195 lines
6.5 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
import logging
_logger = logging.getLogger(__name__)
class UnifiRouting(models.Model):
"""Route pour le système UniFi
Ce modèle stocke les routes individuelles pour les contrôleurs UniFi.
Il gère les routes statiques et dynamiques.
Les routes sont liées à un site spécifique et sont automatiquement supprimées
lorsque le site est supprimé (cascade).
"""
_name = 'unifi.routing'
_description = 'UniFi Route'
_rec_name = 'destination'
_order = 'destination'
site_id = fields.Many2one(
comodel_name='unifi.site',
string='Site',
required=True,
ondelete='cascade',
help='Site this route belongs to'
)
destination = fields.Char(
string='Destination',
required=True,
help='Destination network in CIDR format (e.g., 192.168.1.0/24)'
)
gateway = fields.Char(
string='Gateway',
required=True,
help='Next hop IP address'
)
interface = fields.Char(
string='Interface',
help='Network interface for this route'
)
metric = fields.Integer(
string='Metric',
default=0,
help='Route metric (priority)'
)
route_type = fields.Selection(
selection=[
('static', 'Static'),
('dynamic', 'Dynamic'),
],
string='Route Type',
default='static',
help='Type of route'
)
protocol = fields.Selection(
selection=[
('static', 'Static'),
('ospf', 'OSPF'),
('bgp', 'BGP'),
('rip', 'RIP'),
],
string='Protocol',
default='static',
help='Routing protocol'
)
enabled = fields.Boolean(
string='Enabled',
default=True,
help='Whether this route is active'
)
description = fields.Text(
string='Description',
help='Optional description for this route'
)
unifi_id = fields.Char(
string='UniFi ID',
help='ID of this route in the UniFi system'
)
last_sync = fields.Datetime(
string='Last Synchronization',
help='Last time this route was synchronized with the UniFi system'
)
raw_data = fields.Text(
string='Raw Data',
help='Raw route data in JSON format'
)
@api.constrains('destination')
def _check_destination_format(self):
"""Vérifie que la destination est au format CIDR valide"""
for record in self:
# Validation simple pour le format CIDR
if '/' not in record.destination:
raise ValidationError(_("La destination doit être au format CIDR (réseau/préfixe)."))
network, prefix = record.destination.split('/')
# Validation simple pour IPv4
ip_parts = network.split('.')
if len(ip_parts) != 4:
raise ValidationError(_("L'adresse réseau doit être au format IPv4 (x.x.x.x)."))
for part in ip_parts:
try:
num = int(part)
if num < 0 or num > 255:
raise ValidationError(_("Chaque partie de l'adresse IP doit être comprise entre 0 et 255."))
except ValueError as exc:
raise ValidationError(_("L'adresse IP doit contenir uniquement des nombres.")) from exc
# Validation du préfixe
try:
prefix_num = int(prefix)
if prefix_num < 0 or prefix_num > 32:
raise ValidationError(_("Le préfixe doit être compris entre 0 et 32."))
except ValueError as exc:
raise ValidationError(_("Le préfixe doit être un nombre.")) from exc
@api.constrains('gateway')
def _check_gateway_format(self):
"""Vérifie que la passerelle est au format IP valide"""
for record in self:
# Validation simple pour IPv4
ip_parts = record.gateway.split('.')
if len(ip_parts) != 4:
raise ValidationError(_("La passerelle doit être au format IPv4 (x.x.x.x)."))
for part in ip_parts:
try:
num = int(part)
if num < 0 or num > 255:
raise ValidationError(_("Chaque partie de l'adresse IP doit être comprise entre 0 et 255."))
except ValueError as exc:
raise ValidationError(_("L'adresse IP doit contenir uniquement des nombres.")) from exc
def name_get(self):
"""Personnalise l'affichage du nom des enregistrements"""
result = []
for record in self:
name = f"{record.destination} via {record.gateway}"
result.append((record.id, name))
return result
def sync_from_unifi(self):
"""Synchronise les données depuis le système UniFi"""
for record in self:
site = record.site_id
if not site:
continue
_logger.info("Synchronizing route %s from UniFi site %s", record.destination, site.name)
# La synchronisation dépend du type d'API du site
if site.api_type == 'controller':
# TODO(dev): Implémenter la synchronisation depuis l'API Controller
pass
elif site.api_type == 'site_manager':
# TODO(dev): Implémenter la synchronisation depuis l'API Site Manager
pass
record.last_sync = fields.Datetime.now()
def push_to_unifi(self):
"""Pousse les modifications vers le système UniFi"""
for record in self:
site = record.site_id
if not site:
continue
_logger.info("Pushing route %s to UniFi site %s", record.destination, site.name)
# La synchronisation dépend du type d'API du site
if site.api_type == 'controller':
# TODO(dev): Implémenter la synchronisation vers l'API Controller
pass
elif site.api_type == 'site_manager':
# TODO(dev): Implémenter la synchronisation vers l'API Site Manager
pass
record.last_sync = fields.Datetime.now()