Skip to content

Commit e8468a6

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] Enable "ha" API flag for OVN routers"
2 parents 63d6079 + b8953b5 commit e8468a6

File tree

7 files changed

+162
-4
lines changed

7 files changed

+162
-4
lines changed

neutron/common/ovn/extensions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from neutron_lib.api.definitions import l3_enable_default_route_ecmp
4545
from neutron_lib.api.definitions import l3_ext_gw_mode
4646
from neutron_lib.api.definitions import l3_ext_gw_multihoming
47+
from neutron_lib.api.definitions import l3_ext_ha_mode
4748
from neutron_lib.api.definitions import l3_flavors
4849
from neutron_lib.api.definitions import logging
4950
from neutron_lib.api.definitions import multiprovidernet
@@ -125,6 +126,7 @@
125126
l3_ext_gw_multihoming.ALIAS,
126127
l3_enable_default_route_bfd.ALIAS,
127128
l3_enable_default_route_ecmp.ALIAS,
129+
l3_ext_ha_mode.ALIAS,
128130
]
129131
ML2_SUPPORTED_API_EXTENSIONS = [
130132
address_group.ALIAS,

neutron/db/ovn_l3_hamode_db.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright 2024 Red Hat, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from neutron_lib.callbacks import events
16+
from neutron_lib.callbacks import priority_group
17+
from neutron_lib.callbacks import registry
18+
from neutron_lib.callbacks import resources
19+
20+
from neutron.db import l3_attrs_db
21+
22+
23+
@registry.has_registry_receivers
24+
class OVN_L3_HA_db_mixin(l3_attrs_db.ExtraAttributesMixin):
25+
"""Mixin class to add high availability capability to OVN routers."""
26+
27+
@registry.receives(resources.ROUTER, [events.PRECOMMIT_CREATE],
28+
priority_group.PRIORITY_ROUTER_EXTENDED_ATTRIBUTE)
29+
def _precommit_router_create(self, resource, event, trigger, payload):
30+
"""Event handler to set ha flag creation."""
31+
# NOTE(ralonsoh): OVN L3 router HA flag is mandatory and True always,
32+
# enforced by ``OvnDriver.ha_support`` set to ``MANDATORY``. This flag
33+
# cannot be updated.
34+
router_db = payload.metadata['router_db']
35+
self.set_extra_attr_value(router_db, 'ha', True)

neutron/services/l3_router/service_providers/driver_controller.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def _update_router_provider(self, resource, event, trigger, payload=None):
139139
# attributes via the API.
140140
try:
141141
_ensure_driver_supports_request(drv, payload.request_body)
142-
except lib_exc.InvalidInput:
142+
except lib_exc.InvalidInput as exc:
143143
# the current driver does not support this request, we need to
144144
# migrate to a new provider. populate the distributed and ha
145145
# flags from the previous state if not in the update so we can
@@ -162,7 +162,7 @@ def _update_router_provider(self, resource, event, trigger, payload=None):
162162
{'ha_flag': payload.request_body['ha'],
163163
'distributed_flag':
164164
payload.request_body['distributed']})
165-
new_drv = self._attrs_to_driver(payload.request_body)
165+
new_drv = self._attrs_to_driver(payload.request_body, exc=exc)
166166
if new_drv:
167167
LOG.debug("Router %(id)s migrating from %(old)s provider to "
168168
"%(new)s provider.", {'id': payload.resource_id,
@@ -224,7 +224,7 @@ def _get_l3_driver_by_flavor(self, context, flavor_id):
224224
driver = self.drivers[provider['provider']]
225225
return driver
226226

227-
def _attrs_to_driver(self, router):
227+
def _attrs_to_driver(self, router, exc=None):
228228
"""Get a provider driver handle based on the ha/distributed flags."""
229229
distributed = _is_distributed(
230230
router.get('distributed', lib_const.ATTR_NOT_SPECIFIED))
@@ -236,6 +236,9 @@ def _attrs_to_driver(self, router):
236236
for driver in drivers:
237237
if _is_driver_compatible(distributed, ha, driver):
238238
return driver
239+
240+
if exc:
241+
raise exc
239242
raise NotImplementedError(
240243
_("Could not find a service provider that supports "
241244
"distributed=%(d)s and ha=%(h)s") % {'d': distributed, 'h': ha}

neutron/services/ovn_l3/service_providers/ovn.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from neutron.common.ovn import constants as ovn_const
2727
from neutron.common.ovn import utils
2828
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
29+
from neutron.db import ovn_l3_hamode_db as ovn_l3_ha
2930
from neutron.db import ovn_revision_numbers_db as db_rev
3031
from neutron.extensions import revisions
3132
from neutron.objects import router as l3_obj
@@ -37,7 +38,8 @@
3738

3839

3940
@registry.has_registry_receivers
40-
class OvnDriver(base.L3ServiceProvider):
41+
class OvnDriver(base.L3ServiceProvider,
42+
ovn_l3_ha.OVN_L3_HA_db_mixin):
4143
ha_support = base.MANDATORY
4244
distributed_support = base.MANDATORY
4345

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Copyright 2024 Red Hat, Inc.
2+
# All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
5+
# not use this file except in compliance with the License. You may obtain
6+
# a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
# License for the specific language governing permissions and limitations
14+
# under the License.
15+
16+
from neutron_lib import context
17+
from neutron_lib.db import api as db_api
18+
from neutron_lib.plugins import constants as plugin_constants
19+
from neutron_lib.plugins import directory
20+
21+
from neutron.db import ovn_l3_hamode_db
22+
from neutron.objects import router as router_obj
23+
from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
24+
from neutron.tests.unit.db import test_l3_dvr_db
25+
26+
27+
class FakeOVNL3Plugin(test_l3_dvr_db.FakeL3Plugin,
28+
ovn_l3_hamode_db.OVN_L3_HA_db_mixin):
29+
pass
30+
31+
32+
class OVN_L3_HA_db_mixinTestCase(test_plugin.NeutronDbPluginV2TestCase):
33+
34+
def setUp(self, **kwargs):
35+
super().setUp(plugin='ml2', **kwargs)
36+
self.core_plugin = directory.get_plugin()
37+
self.ctx = context.get_admin_context()
38+
self.mixin = FakeOVNL3Plugin()
39+
directory.add_plugin(plugin_constants.L3, self.mixin)
40+
41+
def _create_router(self, router):
42+
with db_api.CONTEXT_WRITER.using(self.ctx):
43+
return self.mixin._create_router_db(self.ctx, router, 'foo_tenant')
44+
45+
def test_create_router(self):
46+
router_dict = {'name': 'foo_router', 'admin_state_up': True,
47+
'distributed': False}
48+
router_db = self._create_router(router_dict)
49+
router = router_obj.Router.get_object(self.ctx, id=router_db.id)
50+
self.assertTrue(router.extra_attributes.ha)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright 2024 Red Hat, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
# not use this file except in compliance with the License. You may obtain
5+
# a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
# License for the specific language governing permissions and limitations
13+
# under the License.
14+
15+
from unittest import mock
16+
17+
from neutron_lib.callbacks import events
18+
from neutron_lib.callbacks import registry
19+
from neutron_lib import context
20+
from neutron_lib import exceptions as lib_exc
21+
22+
from neutron.services.l3_router.service_providers import driver_controller \
23+
as l3_driver_controller
24+
from neutron.services.ovn_l3.service_providers import driver_controller
25+
from neutron.tests.unit import testlib_api
26+
27+
28+
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
29+
30+
31+
class TestDriverController(testlib_api.SqlTestCase):
32+
33+
def setUp(self):
34+
super(TestDriverController, self).setUp()
35+
self.setup_coreplugin(DB_PLUGIN_KLASS)
36+
self.fake_l3 = mock.Mock()
37+
self.dc = driver_controller.DriverController(self.fake_l3)
38+
self.fake_l3.l3_driver_controller = self.dc
39+
self.ctx = context.get_admin_context()
40+
41+
def test__update_router_provider_ha_mandatory(self):
42+
test_dc = driver_controller.DriverController(self.fake_l3)
43+
with mock.patch.object(registry, "publish") as mock_cb:
44+
with mock.patch.object(test_dc, "get_provider_for_router"):
45+
with mock.patch.object(
46+
l3_driver_controller,
47+
"_ensure_driver_supports_request") as _ensure:
48+
_ensure.side_effect = lib_exc.InvalidInput(
49+
error_message='message')
50+
self.assertRaises(
51+
lib_exc.InvalidInput,
52+
test_dc._update_router_provider,
53+
None, None, None,
54+
payload=events.DBEventPayload(
55+
None, request_body={'ha': False,
56+
'distributed': True},
57+
states=({'flavor_id': None},))
58+
)
59+
mock_cb.assert_not_called()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Enabled the ``ha`` API extension for OVN routers. Now the high
5+
availability flag ``ha`` can be set and read for OVN routers. NOTE:
6+
currently OVN routers are always HA; the flag is mandatory
7+
and cannot be unset (but now can be read).

0 commit comments

Comments
 (0)