Skip to content

Commit 49628a1

Browse files
committed
fix(neutron-understack): wire up better compat for evpn alias
If the evpn alias doesn't exist we need to wire up our compat layer but if it does then we need to use the one that exists. This provides an easier to use compat layer which avoids the DuplicatePolicyError that is seen when starting up the services.
1 parent 98e3936 commit 49628a1

4 files changed

Lines changed: 72 additions & 25 deletions

File tree

python/neutron-understack/neutron_understack/conf/policies/evpn.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from neutron.conf.policies import base
22
from oslo_policy import policy
33

4+
from neutron_understack import evpn_compat
5+
46
COLLECTION_PATH = "/routers"
57
RESOURCE_PATH = "/routers/{id}"
68

@@ -31,4 +33,9 @@
3133

3234

3335
def list_rules():
36+
# When core owns EVPN it registers these same policies; registering them
37+
# again makes neutron.policy.register_rules() raise DuplicatePolicyError
38+
# and abort policy initialization for the whole neutron-server.
39+
if evpn_compat.core_provides_evpn():
40+
return []
3441
return rules
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Compatibility helpers for the EVPN router extension.
2+
3+
The EVPN router feature (the ``evpn_vni`` attribute, its API extension and its
4+
policies) was upstreamed into neutron. On a neutron that carries it (e.g. the
5+
understack/2026.1 branch) core provides all of:
6+
7+
* ``neutron_lib.api.definitions.evpn`` -- the router ``evpn_vni`` attribute
8+
* ``neutron.extensions.evpn`` -- the API extension
9+
* ``neutron.conf.policies.evpn`` -- the create/get ``router:evpn_vni`` policies
10+
11+
If ``neutron_understack`` also registers any of these, neutron-server fails at
12+
startup -- most visibly ``DuplicatePolicyError`` raised from
13+
``neutron.policy.register_rules()``, which aborts policy initialization for the
14+
whole service. When core owns EVPN, Understack must register none of them and
15+
contribute only its runtime VNI allocation (the service-plugin callbacks).
16+
17+
Every surface that would otherwise register an EVPN artifact keys off
18+
``core_provides_evpn()`` so the decision is made in exactly one place.
19+
"""
20+
21+
from neutron.conf import policies as core_policies
22+
23+
from neutron_understack.api.definitions import understack_vni
24+
25+
try:
26+
from neutron_lib.api.definitions import evpn as core_evpn_apidef
27+
except ImportError:
28+
core_evpn_apidef = None
29+
30+
# The router EVPN policy name core registers when it owns the feature. This is
31+
# also the exact rule neutron_understack would otherwise re-register, so its
32+
# presence in core's policy list is what triggers the DuplicatePolicyError.
33+
_CORE_EVPN_POLICY = "create_router:evpn_vni"
34+
35+
36+
def core_provides_evpn():
37+
"""Return True when neutron core registers the EVPN router feature itself.
38+
39+
Checks the actual policy list neutron.policy.register_rules() consumes
40+
(``neutron.conf.policies.list_rules()``), rather than merely whether the
41+
extension module is importable -- core registers these policies
42+
unconditionally at startup, independent of which extensions are loaded.
43+
"""
44+
return any(rule.name == _CORE_EVPN_POLICY for rule in core_policies.list_rules())
45+
46+
47+
def api_definition():
48+
"""The api-definition to use for the router VNI extension.
49+
50+
Prefer core's ``evpn`` api-definition when core owns the feature so the
51+
``evpn_vni`` attribute is defined exactly once; otherwise fall back to
52+
Understack's own definition.
53+
"""
54+
if core_provides_evpn() and core_evpn_apidef is not None:
55+
return core_evpn_apidef
56+
return understack_vni
Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,11 @@
1-
from importlib import util as importlib_util
2-
31
from neutron_lib.api import extensions
42

5-
try:
6-
from neutron_lib.api.definitions import evpn as evpn_apidef
7-
except ImportError:
8-
evpn_apidef = None
9-
103
from neutron_understack.api.definitions import understack_vni as apidef
114

125

13-
def _api_definition():
14-
if (
15-
evpn_apidef is not None
16-
and importlib_util.find_spec("neutron.extensions.evpn") is None
17-
):
18-
return evpn_apidef
19-
return apidef
20-
21-
6+
# This descriptor carries Understack's own ``understack_vni`` alias. It is only
7+
# applied to routers when the UnderstackVniPlugin advertises that alias (see
8+
# vrf._supported_extension_aliases); when core owns EVPN the plugin advertises
9+
# core's ``evpn`` alias instead, so this descriptor is loaded but never applied.
2210
class Understack_vni(extensions.APIExtensionDescriptor):
23-
api_definition = _api_definition()
11+
api_definition = apidef

python/neutron-understack/neutron_understack/l3_router/vrf.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@
66
from neutron_lib.callbacks import registry
77
from neutron_lib.callbacks import resources
88
from neutron_lib.db import resource_extend
9-
10-
try:
11-
from neutron_lib.api.definitions import evpn as evpn_apidef
12-
except ImportError:
13-
evpn_apidef = None
149
from neutron_lib.plugins import constants as plugin_constants
1510
from neutron_lib.plugins import directory
1611
from neutron_lib.services import base as service_base
1712
from oslo_config import cfg
1813
from oslo_log import log as logging
1914

2015
from neutron_understack import config
16+
from neutron_understack import evpn_compat
2117
from neutron_understack.api.definitions import understack_vni as apidef
2218
from neutron_understack.l3_router import understack_vni_db
2319

@@ -44,9 +40,9 @@ def _is_vrf_router(context, router):
4440

4541

4642
def _supported_extension_aliases():
47-
if evpn_apidef is not None:
48-
return [evpn_apidef.ALIAS]
49-
return [apidef.ALIAS]
43+
# Advertise whichever extension provides the router evpn_vni attribute:
44+
# core's ``evpn`` when core owns it, otherwise Understack's own.
45+
return [evpn_compat.api_definition().ALIAS]
5046

5147

5248
@resource_extend.has_resource_extenders

0 commit comments

Comments
 (0)