La versión corta
- Restaurar un volcado no neutraliza nada. La neutralización es un paso separado, y está desactivada por defecto en cada punto de entrada que Odoo envía
- La neutralización es una opción por módulo. Para cada módulo instalado, Odoo busca <module>/data/neutralize.sql y lo ejecuta como SQL en bruto. Un módulo sin ese archivo no contribuye en nada, silenciosamente.
- La cobertura del núcleo es buena: servidores de correo, crons, webhooks, proveedores de pago, sincronización bancaria, transportistas de entrega, tokens de IAP y más:
- Sus módulos personalizados no están cubiertos a menos que usted haya escrito el archivo. Tampoco la mayoría de los módulos de terceros y OCA.
- Las acciones automatizadas de tipo código permanecen activadas, y los disparadores de creación/escritura no necesitan un cron para activarse.
- Ejecute esto antes de confiar en nada: odoo neutralize -d staging_db
Lo que realmente es la neutralización
Todo el mecanismo es un módulo corto. Esto es, en sustancia:
def get_neutralization_queries(modules):
for module in modules:
filename = f'{module}/data/neutralize.sql'
with suppress(FileNotFoundError):
with file_open(filename) as file:
yield file.read().strip()
Un módulo que no tiene nada que decir y un módulo que nunca fue preguntado producen una salida idéntica: nada. Sin advertencias, sin línea de registro, sin lista de módulos que no contribuyeron con consultas. Cuando la ejecución termina, registra "Neutralización finalizada" independientemente de si neutralizó cuarenta módulos o cuatro.
Eso tiene tres consecuencias, todas ellas lecturas directas del código anterior.
Es optativo, por módulo. La cobertura es una lista mantenida por quien escribió cada módulo. Odoo mantiene una buena para el suyo. Nadie mantiene una para el tuyo.
Solo ve módulos instalados. El estado debe estar instalado, para actualizar o eliminar. Razonable, y vale la pena saberlo.
Es SQL en bruto en un cursor, no el ORM. Sin Python, sin ganchos de creación/escritura, sin campos calculados recalculados. Cualquier cosa que necesite desactivarse debe poder expresarse como SQL contra tablas en esa base de datos.
Un detalle más que no es obvio del código pero se deriva de él: get_installed_modules ejecuta un SELECT sin ORDER BY, por lo que el orden en que se ejecutan los archivos de módulo no está especificado. Nunca escribas un neutralize.sql que dependa de que otro módulo se haya ejecutado primero.
Lo que el núcleo realmente apaga
El archivo de Base es lo suficientemente corto como para leerlo completo, y es la mejor evidencia en esta publicación:
-- desactivar servidores de correo
UPDATE ir_mail_server
SET active = false;
-- insertar servidor de correo ficticio para evitar usar servidores de respaldo especificados mediante línea de comandos
INSERT INTO ir_mail_server(name, smtp_port, smtp_host, smtp_encryption, active, smtp_authentication)
VALUES ('neutralización - desactivar correos', 1025, 'inválido', 'ninguno', true, 'login');
-- desactivar crons
UPDATE ir_cron SET active = false
WHERE id NOT IN (
SELECT res_id FROM ir_model_data
WHERE model = 'ir.cron' AND name = 'autovacuum_job' AND module = 'base');
-- bandera de neutralización para la base de datos
INSERT INTO ir_config_parameter (key, value)
VALUES ('database.is_neutralized', true)
ON CONFLICT (key) DO UPDATE SET value = true;
-- desactivar webhooks
UPDATE ir_act_server
SET webhook_url = 'neutralización - desactivar webhook'
WHERE state = 'webhook';
El servidor de correo ficticio merece atención, porque es un trabajo cuidadoso. Se inserta activo, apuntando a un host inválido en el puerto 1025. Está ahí para que un servidor SMTP pasado en la línea de comandos no pueda ser recogido como una alternativa una vez que los servidores reales estén desactivados. Los envíos fallan ruidosamente en lugar de escapar silenciosamente. Ese es el intercambio correcto, y es la razón por la que una base de datos neutralizada es genuinamente segura o, el camino del correo.
Nota también la cláusula cron: solo base.autovacuum_job sobrevive. Todo lo demás programado está desactivado.
Hay 76 archivos neutralize.sql en Community, además de más en Enterprise. Los de Enterprise son donde viven las integraciones costosas. Un ejemplo, nuevamente leído de la fuente: account_online_synchronization establece account_online_link.client_id = 'duplicate', que es la sincronización bancaria; whatsapp reemplaza el token de cuenta con 'dummy_token'; social_facebook anula el token de acceso de Facebook; delivery_ups_rest establece las credenciales de UPS a 'dummy'; voip pone al proveedor en modo demo; sale_amazon reemplaza la clave del vendedor y el token de actualización; l10n_be_hr_payroll reemplaza res_company.onss_expeditor_number, que es bajo lo que se presentan las declaraciones Dimona belgas.
Esta es una mejor cobertura de la que la mayoría de la gente asume, y vale la pena decirlo tan claramente. El problema no es que Odoo haga esto mal. El problema es que la cobertura es una lista, y tu lista es más larga que la de ellos.
Los valores predeterminados son el borde afilado
La neutralización es un paso distinto de la restauración, y está desactivada a menos que la actives.
Así que la restauración del administrador de la base de datos, la restauración de la CLI y la ruta duplicada producen una base de datos de producción completamente armada a menos que alguien pida activamente lo contrario. Y un pg_restore simple o createdb más restauración no neutraliza nada en absoluto, porque nunca toca la ruta de código de Odoo en primer lugar. No hay ninguna versión de “restaurar un volcado” que sea segura por sí misma.
Dos cosas siguen. Primero, si restauras a mano, la neutralización es tu paso a recordar, cada vez, incluyendo la restauración que hiciste a las 22:40 porque surgió algo urgente. Segundo, si falla, lo dice:
"Ocurrió un error durante la neutralización. ¡LA BASE DE DATOS NO ESTÁ NEUTRALIZADA!"
La CLI registra eso y sale 1. Lo que también significa que la falla de cualquier archivo de módulo individual aborta toda la ejecución, incluyendo la tuya, si envías una con un error tipográfico.
Lo que no cubre
Las acciones automatizadas permanecen activas, y este es el elemento más agudo de la lista. addons/base_automation/ no envía datos/neutralize.sql, su directorio data/ contiene solo base_automation_data.xml y digest_data.xml. Base desactiva las acciones del servidor donde el estado = 'webhook', que cubre exactamente el tipo de webhook y nada más. Una acción del servidor de tipo código, el tipo que llama a la API de un socio, publica en un canal de Slack, notifica a un proveedor de cumplimiento, empuja un registro a un sistema de almacén, queda intacta.
Las automatizaciones basadas en el tiempo están, de hecho, detenidas, porque su activador se ejecuta a partir de base_automation.ir_cron_data_base_automation_check, y la eliminación de cron la desactiva. Pero los activadores on-create y on-write no necesitan un cron. Se activan en el momento en que alguien edita un registro. Esa es la razón principal por la que construiste una base de datos de staging.
Los módulos personalizados se neutralizan solo si tú escribiste el archivo. Nada genérico los cubre. Si tu módulo tiene una clave API, abre una conexión saliente, o publica en un webhook, get_neutralization_queries busca your_module/data/neutralize.sql, no lo encuentra, y sigue adelante sin decir una palabra.
Módulos de terceros y OCA: lo mismo. Algunos envían uno. La mayoría no. Revisa los que se comunican con el mundo exterior, en lugar de asumir.
Los datos personales quedan intactos. Nada en ningún neutralize.sql anonimiza un nombre de cliente, una dirección de correo electrónico, un número de teléfono, un salario o una cuenta bancaria. Más sobre eso a continuación.
El filestore no es parte de esto. La neutralización es SQL contra la base de datos. Los archivos adjuntos llegan por una ruta separada y son inafectados por todo esto.
La solución de dos líneas
La buena noticia es genuinamente buena: la solución para tus propios módulos es un archivo, y una vez que está en el repositorio se aplica en cada clon futuro, para siempre, sin que nadie recuerde nada.
Crea your_module/data/neutralize.sql. No va en __manifest__.py, get_neutralization_queries construye la ruta a partir del nombre del módulo y la abre directamente, por lo que el archivo existente en esa ruta es todo el paso de registro.
Lo que va en él es SQL en bruto, ejecutado en un cursor. Eso significa:
- Múltiples declaraciones en un archivo están bien. Base tiene cinco.
- UPDATE, INSERT, DELETE y TRUNCATE están todos en uso en el núcleo. El folklore a nivel de plan que neutralize.sql es solo de UPDATE es incorrecto.
- Sin Python, sin ORM. Sin sobrescrituras de crear/escribir, sin métodos de cálculo, sin @api.constrains, sin entorno.
- Nada fuera de la base de datos. No puede rotar una clave en tu proveedor, eliminar un archivo del almacén de archivos, o desregistrar un webhook en el extremo lejano. Solo puede hacer que esta base de datos deje de poder usarlos.
- No debe generar excepciones. Un fallo aborta la ejecución para cada módulo, no solo para el tuyo.
Un archivo realista para un módulo con una integración saliente:
-- your_module/data/neutralize.sql
-- 1. apunta el conector a algo que no puede resolverse, y desactívalo.
UPDATE your_module_connector
SET endpoint_url = 'https://neutralized.invalid',
api_key = 'neutralized',
active = false;
-- 2. limpia la cola de cualquier cosa que esté esperando ser enviada.
DELETE FROM your_module_outbound_queue;
-- 3. desactivar las acciones del servidor de código que este módulo envía
ACTUALIZAR ir_act_server
ESTABLECER código = 'pass # neutralizado'
DONDE estado = 'código'
Y id EN (SELECCIONAR res_id
DE ir_model_data
DONDE modelo = 'ir.actions.server'
Y módulo = 'tu_módulo');
Adapta los nombres de las tablas y columnas a tu esquema, y verifícalos con la versión que estás ejecutando antes de confiar en la tercera declaración. El patrón es lo que se transfiere: desactivar el interruptor, invalidar la credencial y drenar cualquier cosa que ya esté en cola. Invalidar la credencial es tan importante como desactivar el interruptor, porque el interruptor es exactamente lo que un desarrollador vuelve a activar para probar algo.
Prueba ejecutando odoo neutralize -d <copia> --stdout y confirmando que tu archivo aparece en la salida. Luego ejecútalo de verdad contra una copia desechable y verifica las filas.
Pre-vuelo: antes de que alguien abra la base de datos de staging
Ejecuta esto en orden. Toma diez minutos y son los mismos diez minutos cada vez.
- Confirma que la neutralización realmente se ejecutó. Verifica database.is_neutralized en ir_config_parameter. Si está ausente, nada se ejecutó, lo que sea que alguien recuerde haber hecho.
- Busca el banner. web activa web.neutralize_banner; el sitio web activa website.neutralize_ribbon. Una base de datos neutralizada debería estar visiblemente marcada en la interfaz. Si no lo está, considera eso como la respuesta.
- Lee el SQL que se ejecutó. odoo neutralizar -d <db> --stdout. Compara los módulos representados en la salida con tu lista de módulos instalados. La diferencia entre esas dos listas es tu exposición, y es el único lugar en este procedimiento donde aprendes algo que no sospechabas previamente.
- Correo. Todas las filas en ir_mail_server inactivas excepto la ficticia en el host inválido. mail_template.mail_server_id nulo. fetchmail_server inactivo, así que nada está recibiendo correo tampoco.
- Crons. Solo base.autovacuum_job activo en ir_cron.
- Acciones del servidor. Cada fila en ir_act_server con estado = 'webhook' debe llevar el marcador de neutralización en webhook_url. Luego lista los que tienen estado = 'code' y léelos, porque base no tocó esos. Presta atención a los que están vinculados a los disparadores on-create y on-write.
- Proveedores y transportistas. estados de payment_provider, filas de delivery_carrier, tokens de iap_account, y, si ejecutas Enterprise, sincronización bancaria, WhatsApp, VoIP y los conectores sociales. Todos estos tienen cobertura central cuando el módulo está instalado. Confirma en lugar de asumir, porque solo están cubiertos si el módulo que los posee fue instalado en ese momento.
- Tu propio patrimonio. Revisa la lista de módulos instalados para cualquier cosa personalizada, OCA o de pago que se comunique con el mundo exterior, y verifica cada uno por un data/neutralize.sql. Esta es la lista que crece cada trimestre y nunca disminuye.
- El almacén de archivos. Vino con archivos adjuntos reales: contratos firmados, documentos de identificación, PDFs de nómina. Nada en la neutralización se aplica a esto.
- Acceso. Decide quién puede iniciar sesión en esta base de datos antes de decirle a alguien que existe.
La parte de neutralización no puede ayudar con
La neutralización detiene la base de datos alcanzando personas. No la detiene conteniendo ellas.
Cada nombre de cliente, dirección de correo electrónico, número de teléfono, dirección de entrega, línea de factura, registro de empleado, salario y detalle bancario en producción ahora también está en staging, sin cambios. Esa copia es un segundo conjunto completo de datos personales, se encuentra dentro de su registro de procesamiento, y está bajo la misma obligación de asegurarla que la producción.
Generalmente se reduce. La producción tiene acceso restringido, un rastro de auditoría y un proceso de cambio. Staging tiene un inicio de sesión compartido, un amplio grupo de desarrolladores y una URL que alguien pegó en un chat hace dos meses, porque es solo staging. Los datos no saben eso.
Nada aquí es una razón para no restaurar los datos de producción, los datos realistas son la razón por la que el entorno es útil, y esto no es asesoría legal. Es una razón para dar a staging el mismo control de acceso y la misma disciplina de retención que a la producción, y para eliminar las copias que dejó de usar.
Lo que esto realmente es
La neutralización es un buen mecanismo que hace exactamente lo que dice. La brecha está entre “la neutralización se ejecutó” y “todo lo peligroso está apagado”, y permanece abierta porque ambos estados se ven idénticos desde el exterior: misma línea de registro, mismo banner, misma ausencia de errores. Nada en su instancia le dirá que el conector que uno de sus desarrolladores escribió en 2023 nunca estuvo en la lista.
Esa es la diferencia entre Odoo funcionando y Odoo funcionando correctamente, de nuevo. Rara vez es dramático. Es un archivo que nunca se creó, y permanece así hasta que alguien lo busque.
Staging que llega neutralizado, cada vez
La versión confiable de esto no es una mejor lista de verificación. No es tener que recordar.
En el alojamiento de Skysize Odoo, odoo neutralize se ejecuta en la rama de staging automáticamente en cada ruta donde llegan nuevos datos de producción: una restauración en un contenedor existente, una restauración en uno nuevo, una restauración azul-verde y un clon de staging. Se ejecuta dentro del contenedor, contra la base de datos que acaba de llegar, antes de que tú llegues a ella.
Deliberadamente no lo hace de nuevo en un push de código ordinario. Ese es el detalle que vale más que cualquier adjetivo: si reactivaste un proveedor de pago de prueba o apuntaste un conector a un sandbox después del clon, el siguiente commit no deshace tu trabajo. La neutralización ocurre cuando llegan los datos de producción, y solo entonces. También se pueden tomar copias de seguridad pre-neutralizadas para descargar, neutralizadas a través de una copia desechable para que la fuente nunca se modifique.
Lo que no hace, y preferiríamos decirlo: no escribe neutralize.sql para tus módulos personalizados. La cobertura del núcleo es la cobertura del núcleo. Ese archivo es tuyo para enviar, toma una tarde, y es la única cosa de mayor valor que puedes hacer con lo que acabas de leer.
Construido por exingenieros de Odoo. Prueba gratis hoy.