14 KiB
Modèles nécessaires pour l'intégration des API UniFi
Ce document détaille les modèles et champs nécessaires pour interagir avec les deux API UniFi (Site Manager et Controller), en maximisant la réutilisation des modèles entre les deux types d'API.
Principes de conception
Pour permettre une intégration efficace avec les deux API tout en minimisant la duplication de code, nous adoptons les principes suivants :
- Modèle de base commun : Chaque entité principale aura un modèle de base commun avec les champs partagés entre les deux API.
- Champs spécifiques à l'API : Les champs spécifiques à chaque API seront clairement identifiés.
- Abstraction des méthodes d'accès : Les méthodes d'accès aux API seront abstraites pour permettre une implémentation spécifique à chaque type d'API.
- Héritage et polymorphisme : Utilisation de l'héritage Odoo pour spécialiser les modèles selon le type d'API.
1. Modèle principal : udm.site
Le modèle udm.site est le point central de l'intégration. Il doit être adapté pour supporter les deux types d'API.
Champs communs
# Champs communs à tous les sites UniFi
name = fields.Char(string='Nom', required=True)
description = fields.Text(string='Description')
api_type = fields.Selection([
('site_manager', 'Site Manager API (distant)'),
('controller', 'Controller API (locale)')
], string='Type d\'API', required=True)
site_id = fields.Char(string='ID du site', help="Identifiant du site dans UniFi")
last_sync = fields.Datetime(string='Dernière synchronisation')
sync_interval = fields.Integer(string='Intervalle de synchronisation (min)', default=60)
active = fields.Boolean(string='Actif', default=True)
Champs spécifiques à l'API Site Manager
# Champs spécifiques à l'API Site Manager
api_key = fields.Char(string='Clé API', help="Clé API pour l'accès au Site Manager")
api_key_encrypted = fields.Char(string='Clé API (chiffrée)', help="Version chiffrée de la clé API")
mfa_enabled = fields.Boolean(string='Authentification à deux facteurs', default=False)
mfa_token = fields.Char(string='Token MFA', help="Token d'authentification à deux facteurs")
cloud_site_id = fields.Char(string='ID du site cloud', help="Identifiant du site dans le cloud UniFi")
Champs spécifiques à l'API Controller
# Champs spécifiques à l'API Controller
host = fields.Char(string='Hôte/IP', help="Adresse IP ou nom d'hôte du contrôleur")
port = fields.Integer(string='Port', default=443)
username = fields.Char(string='Nom d'utilisateur')
password = fields.Char(string='Mot de passe')
password_encrypted = fields.Char(string='Mot de passe (chiffré)')
verify_ssl = fields.Boolean(string='Vérifier SSL', default=False)
controller_type = fields.Selection([
('udm', 'UDM Pro/UCG Max'),
('controller', 'Contrôleur UniFi standard')
], string='Type de contrôleur', default='udm')
Méthodes abstraites
def authenticate(self):
"""Authentifie auprès de l'API appropriée selon le type"""
if self.api_type == 'site_manager':
return self._authenticate_site_manager()
else:
return self._authenticate_controller()
def _authenticate_site_manager(self):
"""Implémentation spécifique pour l'API Site Manager"""
pass
def _authenticate_controller(self):
"""Implémentation spécifique pour l'API Controller"""
pass
def get_sites(self):
"""Récupère les sites disponibles selon le type d'API"""
if self.api_type == 'site_manager':
return self._get_sites_site_manager()
else:
return self._get_sites_controller()
2. Modèle d'authentification : udm.auth.session
Ce modèle gère les sessions d'authentification pour les deux types d'API.
class UdmAuthSession(models.Model):
_name = 'udm.auth.session'
_description = 'Session d\'authentification UniFi'
site_id = fields.Many2one('udm.site', string='Site', required=True, ondelete='cascade')
auth_type = fields.Selection([
('api_key', 'Clé API'),
('cookie', 'Cookie de session'),
('bearer', 'Token Bearer')
], string='Type d\'authentification', required=True)
token = fields.Char(string='Token/Cookie', help="Token d'authentification ou cookie de session")
token_encrypted = fields.Char(string='Token/Cookie (chiffré)')
expiry = fields.Datetime(string='Expiration')
is_valid = fields.Boolean(string='Valide', default=True)
# Méthodes pour gérer les sessions d'authentification
def validate(self):
"""Vérifie si la session est toujours valide"""
pass
def refresh(self):
"""Rafraîchit la session si nécessaire"""
pass
3. Modèle pour l'authentification à deux facteurs : udm.mfa
class UdmMfa(models.TransientModel):
_name = 'udm.mfa'
_description = 'Authentification à deux facteurs UniFi'
site_id = fields.Many2one('udm.site', string='Site', required=True)
mfa_type = fields.Selection([
('totp', 'TOTP (Google Authenticator)'),
('sms', 'SMS'),
('email', 'Email')
], string='Type de MFA', default='totp')
mfa_code = fields.Char(string='Code MFA', required=True)
def validate_mfa(self):
"""Valide le code MFA et complète l'authentification"""
pass
4. Modèle de configuration : udm.api.config
Ce modèle stocke les configurations spécifiques à chaque type d'API.
class UdmApiConfig(models.Model):
_name = 'udm.api.config'
_description = 'Configuration API UniFi'
site_id = fields.Many2one('udm.site', string='Site', required=True, ondelete='cascade')
api_type = fields.Selection(related='site_id.api_type', string='Type d\'API')
base_url = fields.Char(string='URL de base', compute='_compute_base_url')
endpoints = fields.Text(string='Endpoints', help="Configuration JSON des endpoints API")
rate_limit = fields.Integer(string='Limite de taux', default=60)
timeout = fields.Integer(string='Timeout (secondes)', default=30)
def _compute_base_url(self):
"""Calcule l'URL de base en fonction du type d'API"""
for config in self:
if config.api_type == 'site_manager':
config.base_url = 'https://unifi.ui.com/api'
else:
site = config.site_id
prefix = '/proxy/network' if site.controller_type == 'udm' else ''
config.base_url = f'https://{site.host}:{site.port}{prefix}'
5. Adaptations des modèles existants
Les modèles existants doivent être adaptés pour fonctionner avec les deux types d'API. Voici les principales adaptations nécessaires :
5.1 udm.device
# Champs communs
site_id = fields.Many2one('udm.site', string='Site', required=True, ondelete='cascade')
name = fields.Char(string='Nom')
mac_address = fields.Char(string='Adresse MAC', required=True)
ip_address = fields.Char(string='Adresse IP')
model = fields.Char(string='Modèle')
device_type = fields.Selection([...], string='Type d\'appareil')
# Champs spécifiques à l'API Site Manager
cloud_device_id = fields.Char(string='ID de l\'appareil cloud')
# Champs spécifiques à l'API Controller
device_id = fields.Char(string='ID de l\'appareil local')
adopted = fields.Boolean(string='Adopté')
5.2 udm.network
# Champs communs
site_id = fields.Many2one('udm.site', string='Site', required=True, ondelete='cascade')
name = fields.Char(string='Nom', required=True)
subnet = fields.Char(string='Sous-réseau')
purpose = fields.Selection([...], string='Objectif')
# Champs spécifiques à l'API Site Manager
cloud_network_id = fields.Char(string='ID du réseau cloud')
# Champs spécifiques à l'API Controller
network_id = fields.Char(string='ID du réseau local')
5.3 udm.firewall.rule
# Champs communs
site_id = fields.Many2one('udm.site', string='Site', required=True, ondelete='cascade')
name = fields.Char(string='Nom', required=True)
action = fields.Selection([...], string='Action')
enabled = fields.Boolean(string='Activé', default=True)
# Champs spécifiques à l'API Site Manager
cloud_rule_id = fields.Char(string='ID de la règle cloud')
# Champs spécifiques à l'API Controller
rule_id = fields.Char(string='ID de la règle locale')
6. Nouveaux modèles pour la gestion des sites
6.1 udm.site.discovery
Ce modèle transitoire est utilisé pour découvrir les sites disponibles lors de l'ajout d'un nouveau site.
class UdmSiteDiscovery(models.TransientModel):
_name = 'udm.site.discovery'
_description = 'Découverte de sites UniFi'
wizard_id = fields.Many2one('udm.site.import.wizard', string='Assistant')
api_type = fields.Selection(related='wizard_id.api_type')
site_name = fields.Char(string='Nom du site')
site_id = fields.Char(string='ID du site')
description = fields.Text(string='Description')
selected = fields.Boolean(string='Sélectionné', default=False)
def import_site(self):
"""Importe le site sélectionné"""
pass
6.2 udm.site.import.wizard (extension)
Extension du wizard existant pour supporter les deux types d'API.
class UdmSiteImportWizard(models.TransientModel):
_name = 'udm.site.import.wizard'
_description = 'Assistant d\'importation de site UniFi'
# Étape actuelle
state = fields.Selection([
('api_type', 'Choix du type d\'API'),
('controller_config', 'Configuration du contrôleur'),
('site_manager_config', 'Configuration du Site Manager'),
('mfa', 'Authentification à deux facteurs'),
('site_selection', 'Sélection du site'),
('summary', 'Résumé')
], string='Étape', default='api_type')
# Choix du type d'API
api_type = fields.Selection([
('site_manager', 'Site Manager API (distant)'),
('controller', 'Controller API (locale)')
], string='Type d\'API')
# Configuration du contrôleur
name = fields.Char(string='Nom du site')
host = fields.Char(string='Hôte/IP')
port = fields.Integer(string='Port', default=443)
username = fields.Char(string='Nom d\'utilisateur')
password = fields.Char(string='Mot de passe')
verify_ssl = fields.Boolean(string='Vérifier SSL', default=False)
controller_type = fields.Selection([
('udm', 'UDM Pro/UCG Max'),
('controller', 'Contrôleur UniFi standard')
], string='Type de contrôleur', default='udm')
# Configuration du Site Manager
api_key = fields.Char(string='Clé API')
mfa_enabled = fields.Boolean(string='Authentification à deux facteurs', default=False)
# Sites découverts
discovered_site_ids = fields.One2many('udm.site.discovery', 'wizard_id', string='Sites découverts')
# Méthodes de navigation
def action_next(self):
"""Passe à l'étape suivante"""
pass
def action_previous(self):
"""Revient à l'étape précédente"""
pass
# Méthodes spécifiques à chaque étape
def action_discover_sites(self):
"""Découvre les sites disponibles"""
pass
def action_import_sites(self):
"""Importe les sites sélectionnés"""
pass
7. Modèle pour la gestion des erreurs et journalisation
class UdmApiLog(models.Model):
_name = 'udm.api.log'
_description = 'Journal API UniFi'
_order = 'timestamp desc'
site_id = fields.Many2one('udm.site', string='Site', ondelete='cascade')
api_type = fields.Selection(related='site_id.api_type')
timestamp = fields.Datetime(string='Horodatage', default=fields.Datetime.now)
endpoint = fields.Char(string='Endpoint')
method = fields.Selection([
('GET', 'GET'),
('POST', 'POST'),
('PUT', 'PUT'),
('DELETE', 'DELETE')
], string='Méthode')
request_data = fields.Text(string='Données de requête')
response_data = fields.Text(string='Données de réponse')
status_code = fields.Integer(string='Code de statut')
success = fields.Boolean(string='Succès', compute='_compute_success')
error_message = fields.Text(string='Message d\'erreur')
def _compute_success(self):
"""Détermine si la requête a réussi en fonction du code de statut"""
for log in self:
log.success = 200 <= log.status_code < 300
8. Modèle pour la synchronisation
class UdmSyncJob(models.Model):
_name = 'udm.sync.job'
_description = 'Tâche de synchronisation UniFi'
_order = 'start_time desc'
site_id = fields.Many2one('udm.site', string='Site', required=True, ondelete='cascade')
api_type = fields.Selection(related='site_id.api_type')
start_time = fields.Datetime(string='Heure de début', default=fields.Datetime.now)
end_time = fields.Datetime(string='Heure de fin')
state = fields.Selection([
('draft', 'Brouillon'),
('running', 'En cours'),
('done', 'Terminé'),
('failed', 'Échoué')
], string='État', default='draft')
sync_type = fields.Selection([
('full', 'Complète'),
('incremental', 'Incrémentielle')
], string='Type de synchronisation', default='incremental')
log_ids = fields.One2many('udm.api.log', 'sync_job_id', string='Journaux')
result_summary = fields.Text(string='Résumé des résultats')
def action_run(self):
"""Exécute la tâche de synchronisation"""
pass
def action_cancel(self):
"""Annule la tâche de synchronisation"""
pass
Conclusion
Cette structure de modèles permet une intégration efficace avec les deux types d'API UniFi tout en maximisant la réutilisation du code. Les points clés sont :
- Séparation claire des champs communs et spécifiques à chaque API
- Abstraction des méthodes d'accès pour permettre des implémentations spécifiques
- Modèles de support pour l'authentification, la journalisation et la synchronisation
- Assistant flexible pour guider l'utilisateur à travers le processus d'ajout de site
Cette conception facilite la maintenance et l'évolution du module, tout en offrant une expérience utilisateur cohérente quel que soit le type d'API utilisé.