Skip to content

Commit 60d61ad

Browse files
authored
Merge pull request #2147 from rackerlabs/neutron-evpn-vni-policy
fix(neutron-understack): wire up better compat for evpn alias
2 parents 98e3936 + 49628a1 commit 60d61ad

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)