OpenStack con kernel 6.x: el metadata agent de OVN se reinicia en bucle (pyroute2 y `NLM_F_BULK`)


#1

Introducción

Tras actualizar el kernel de un nodo de computación de OpenStack (Neutron 16.4.x con OVN) a un kernel 6.x (por ejemplo 6.12.X), el servicio neutron-ovn-metadata-agent dejó de funcionar. El checker openstack_astack_checker de Core-Admin empezó a mostrar este error:

Fallo en el software de OpenStack
 Software 'Nova-Comp' has errors
    * ERROR * - NOVACOMP_OVNHA_RUN: OVN Metadata HAProxy is not running

Este artículo explica el origen del problema, a qué afecta y cómo corregirlo antes de actualizar el kernel en el resto de nodos de computación.

Resumen: la librería python3-pyroute2 0.5.x, la que trae OpenStack, hace todas las operaciones de borrado de red con unos flags que, desde el kernel 5.19, significan “borrado masivo”. El kernel 6.x las rechaza con Operation not supported. La solución es corregir esos flags en pyroute2, un cambio de pocas líneas.

Síntomas

  • El checker marca NOVACOMP_OVNHA_RUN: OVN Metadata HAProxy is not running.
  • systemctl status neutron-ovn-metadata-agent muestra el servicio activo, pero siempre con pocos segundos de antigüedad: systemd lo reinicia en bucle.
  • En /var/log/neutron/neutron-ovn-metadata-agent.log se repite en cada arranque:
CRITICAL neutron [-] Unhandled error: neutron.privileged.agent.linux.ip_lib.InterfaceOperationNotSupported:
  Operation not supported on interface tapXXXXXXXX-11, namespace ovnmeta-XXXXXXXX-....
...
  File ".../neutron/agent/ovn/metadata/agent.py", line 481, in provision_datapath
    ip2.addr.delete(ipaddr)
...
INFO oslo_service.service [-] Parent process has died unexpectedly, exiting
  • Las instancias de ese nodo no reciben metadatos (http://169.254.169.254). Esto afecta a cloud-init, a la inyección de claves SSH, etc.

Para confirmar el bucle de reinicios:

systemctl show neutron-ovn-metadata-agent -p NRestarts -p ActiveEnterTimestamp
grep -c InterfaceOperationNotSupported /var/log/neutron/neutron-ovn-metadata-agent.log

Causa

Qué hace el agente

Al arrancar, el metadata agent revisa el namespace ovnmeta-<red> de cada red y compara las direcciones de la interfaz tap<red>-11 con las que debe tener el puerto de metadata. Borra las que sobran.

Con IPv6 activo en el host, que es lo que viene por defecto, esa interfaz siempre tiene una dirección link-local fe80::…. Esa dirección no pertenece al puerto de metadata, así que el agente la borra en cada arranque. Con el kernel 5.4 el borrado funciona sin problemas. Con el kernel 6.x falla, y como el agente no captura ese error, se detiene del todo antes de lanzar los procesos haproxy.

Por qué falla el borrado con kernel 6.x

pyroute2 0.5.x manda todos los borrados netlink (RTM_DELADDR, RTM_DELLINK, RTM_DELROUTE, RTM_DELNEIGH, RTM_DELRULE, RTM_DELQDISC…) con los mismos flags que las altas:

NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL

En un borrado, NLM_F_CREATE y NLM_F_EXCL no tienen sentido, y los kernels antiguos simplemente los ignoraban. Pero desde el kernel 5.19 existe el borrado masivo (NLM_F_BULK), que usa el mismo bit que NLM_F_EXCL (0x200). Un kernel 6.x interpreta entonces que pyroute2 pide borrar de forma masiva y lo rechaza:

Bulk delete is not supported  ->  EOPNOTSUPP (95, 'Operation not supported')

La herramienta ip (iproute2) manda los borrados solo con NLM_F_REQUEST | NLM_F_ACK. Por eso ip addr del … funciona en el mismo nodo donde el agente falla, y eso despista bastante durante el diagnóstico.

A qué afecta

No es un problema de IPv6 ni solo del metadata agent. En un nodo con kernel ≥ 5.19 y pyroute2 0.5.x falla cualquier borrado de red hecho con pyroute2:

Operación Quién la usa en un nodo de computación Efecto
Borrar dirección IPv6/IPv4 metadata agent al montar el namespace el agente se reinicia en bucle y no hay metadatos
Borrar dirección IPv4 metadata agent al cambiar subredes o IPs del puerto de metadata el agente vuelve a caer
Borrar interfaz (veth/tap) metadata agent al desmontar una red; os-vif/nova al borrar o migrar instancias pueden quedar interfaces huérfanas o fallar operaciones
Borrar rutas, reglas, vecinos, qdisc privsep de neutron/nova según la operación error en la operación concreta

Mientras no se aplique la corrección, cualquier nodo de computación con OpenStack que arranque con un kernel 6.x tendrá este problema.

Diagnóstico: ¿está afectado mi nodo?

Esta prueba se hace en un namespace desechable. No toca nada de producción ni afecta a OVS ni a las instancias.

Crear el entorno de prueba:

ip netns add crad-test ; ip link add crt-a type veth peer name crt-b netns crad-test
ip -n crad-test link set crt-b up ; ip link set crt-a up ; sleep 3 ; ip -n crad-test addr add 10.99.99.1/24 dev crt-b

Intentar borrar una IPv4 igual que lo hace Neutron:

python3 -c "import socket; from pyroute2 import NetNS; ns = NetNS('crad-test'); i = ns.link_lookup(ifname='crt-b')[0]; ns.addr('delete', i, address='10.99.99.1', mask=24, family=socket.AF_INET); print('delete OK')"

Limpiar:

ip netns del crad-test ; ip link del crt-a 2>/dev/null

Cómo interpretar el resultado:

  • delete OK: el nodo no está afectado, ya sea porque el kernel es antiguo o porque pyroute2 ya está corregido.
  • NetlinkError: (95, 'Operation not supported'): el nodo está afectado. Aplica la corrección.

Corrección

El arreglo consiste en que pyroute2 mande los borrados solo con NLM_F_REQUEST | NLM_F_ACK, igual que iproute2 y las versiones modernas de pyroute2. El cambio:

  • funciona igual con kernel 5.4 y con 6.x, así que se puede aplicar antes de actualizar el kernel;
  • corrige todos los borrados, no solo el caso del metadata agent;
  • no modifica Neutron ni el kernel.

Subir pyroute2 a una versión nueva no se recomienda: Neutron depende de la API 0.5.x y el cambio podría romper otras partes del agente.

Todos los pasos se hacen como root en el nodo de computación.

Cuidado al copiar y pegar líneas muy largas: se han visto casos en los que se perdía un espacio en el punto donde el navegador corta la línea. Eso rompe el patrón del sed sin dar ningún error. Por eso se comprueba siempre después con grep y diff.

1. Localizar el fichero y hacer una copia de seguridad

F=$(python3 -c "import pyroute2.iproute.linux as m; print(m.__file__)") ; echo $F
python3 -c "import pyroute2; print(pyroute2.__version__)"

La versión debe ser 0.5.x. Si es otra, revisa el fichero a mano antes de seguir: las líneas pueden ser diferentes.

cp -a $F $F.orig-pre-6.12

Si el fichero .orig-pre-6.12 ya existe de un intento anterior, no lo sobrescribas: podrías guardar como “original” un fichero modificado a medias.

2. Corregir los flags de los borrados

sed -i -E 's/\((RTM_DEL[A-Z]+), flags_make\)/(\1, flags_base)/' $F
sed -i 's/(RTM_DELLINK, flags_create)/(RTM_DELLINK, flags_req)/' $F
sed -i 's/(RTM_DELADDR, flags_create)/(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK)/' $F

3. Comprobar el cambio

El grep no debe devolver nada, el diff solo debe mostrar líneas RTM_DEL… y debe salir COMPILE-OK:

grep -nE "RTM_DEL[A-Z]+, flags_(make|create)" $F ; diff $F.orig-pre-6.12 $F ; python3 -m py_compile $F && echo COMPILE-OK

Si el grep devuelve alguna línea, uno de los sed no se ha aplicado. Lo más habitual es un espacio perdido al copiar: repite ese sed. Si el diff muestra cambios en líneas que no son RTM_DEL…, restaura la copia con cp -a $F.orig-pre-6.12 $F y revisa.

4. Probar

Repite la prueba de la sección Diagnóstico. Ahora debe responder delete OK, incluso con kernel 6.x.

5. Fijar el paquete

apt-mark hold python3-pyroute2

Así una actualización de python3-pyroute2 no deshace la corrección.

6. Reiniciar los servicios

Los demonios privsep conservan el código antiguo en memoria hasta que se reinicia el servicio que los lanza. Reiniciar nova-compute no afecta a las instancias en ejecución.

systemctl restart neutron-ovn-metadata-agent nova-compute ; sleep 10 ; systemctl is-active neutron-ovn-metadata-agent nova-compute

Verificación

Tras aplicar la corrección en un nodo que ya tiene el kernel 6.x, lo más fiable es volver a crear el namespace de metadata. Así se reproduce exactamente lo que pasa tras un reinicio del nodo. Los metadatos de ese nodo se cortan unos segundos. Sustituye <red> por el identificador de tu red; ip netns list | grep ovnmeta muestra los que hay.

systemctl stop neutron-ovn-metadata-agent ; ip netns del ovnmeta-<red> ; ip link del tap<red8>-10 2>/dev/null
systemctl start neutron-ovn-metadata-agent ; sleep 20 ; ip netns exec ovnmeta-<red> ip -br addr ; pgrep -af "haproxy -f"

(<red8> son los 8 primeros caracteres del identificador de la red, igual que aparece en el nombre de la interfaz tap.)

Resultado correcto:

  • la interfaz tap<red8>-11 tiene sus direcciones IPv4 y 169.254.169.254/16, sin fe80::, porque el agente ha podido borrarla;
  • hay un proceso haproxy -f /var/lib/neutron/ovn-metadata-proxy/<red>.conf por red;
  • el contador de InterfaceOperationNotSupported en el log no sube;
  • el checker openstack_astack_checker vuelve a OK en su siguiente ejecución.

Procedimiento recomendado para actualizar el kernel de los nodos

  1. Aplicar la corrección de pyroute2 en el nodo (sección Corrección), todavía con el kernel antiguo. Es compatible.
  2. Comprobar que el grep del paso 3 no devuelve nada, que la prueba de diagnóstico da delete OK y que apt-mark showhold muestra python3-pyroute2.
  3. Vaciar el nodo de instancias con drain, instalar el kernel nuevo y reiniciar.
  4. Repetir la prueba de diagnóstico, ya con el kernel 6.x, y revisar el checker openstack_astack_checker.

Nunca arranques un nodo de computación OpenStack con kernel ≥ 5.19 sin haber corregido antes pyroute2.

Solución de emergencia sin corregir pyroute2

Si un nodo ya está en el bucle y necesitas recuperar los metadatos de inmediato, puedes borrar a mano la link-local que bloquea al agente:

ip netns exec ovnmeta-<red> ip -6 addr show dev tap<red8>-11
ip netns exec ovnmeta-<red> ip addr del <fe80::…/64> dev tap<red8>-11

En el siguiente reinicio automático el agente montará el namespace completo. Esto no corrige el problema: en cuanto se vuelva a crear el namespace, por ejemplo tras un reinicio del nodo, o el agente tenga que borrar cualquier otra dirección, volverá a fallar. Aplica la corrección en cuanto puedas.

Preguntas frecuentes

¿Por qué no desactivar IPv6 para que no aparezca la link-local?
No resuelve la causa. Fallaría igualmente cualquier otro borrado, como una IPv4 al cambiar subredes o una interfaz al borrar instancias. Además, el sysctl net.ipv6.conf.default.disable_ipv6 del host no se aplica a las interfaces que se crean directamente dentro de un namespace nuevo, porque los namespaces nuevos parten de los valores IPv6 por defecto.

¿Afecta a los nodos con kernel 5.4?
No. En esos kernels el bit 0x200 no tiene significado en un borrado y los flags de más se ignoran. Aun así, conviene aplicar la corrección en todos los nodos antes de actualizar el kernel.

¿Qué pasa si se actualiza python3-pyroute2?
La corrección se perdería. Por eso se fija el paquete con apt-mark hold python3-pyroute2. Si quitas el hold y actualizas el paquete, vuelve a aplicar los pasos de la sección Corrección. Es normal que debsums o dpkg --verify marquen el fichero como modificado.

¿Hay que reiniciar las instancias?
No. Solo hay que reiniciar neutron-ovn-metadata-agent y nova-compute, y las instancias en ejecución no se ven afectadas.

¿Cómo vuelvo atrás?
Restaura la copia de seguridad, quita el hold y reinicia los servicios:

F=$(python3 -c "import pyroute2.iproute.linux as m; print(m.__file__)") ; cp -a $F.orig-pre-6.12 $F
apt-mark unhold python3-pyroute2 ; systemctl restart neutron-ovn-metadata-agent nova-compute