Skip to content

Commit ca7ec4a

Browse files
authored
Merge pull request #2134 from rackerlabs/svi_external_gateway
feat(neutron): Prevent SVI routers from using external gateways PUC-1754
2 parents 04c4c9f + 12c343a commit ca7ec4a

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,39 @@ def _is_svi_flavor(self, context, router):
335335
# flavor -> provider -> driver lookup.
336336
return _is_svi_router(context, router)
337337

338+
@registry.receives(resources.ROUTER_GATEWAY, [events.BEFORE_CREATE])
339+
def _reject_svi_external_gateway(self, _resource, _event, _trigger, payload):
340+
context = payload.context
341+
router_id = payload.resource_id
342+
router = self.l3plugin.get_router(context, router_id)
343+
344+
if not self._is_svi_flavor(context, router):
345+
LOG.debug(
346+
"SVI gateway check skipped: router %(router)s is not SVI "
347+
"(name=%(name)s flavor=%(flavor)s)",
348+
{
349+
"router": router_id,
350+
"name": router.get("name"),
351+
"flavor": router.get("flavor_id"),
352+
},
353+
)
354+
return
355+
356+
network_id = (payload.metadata or {}).get("network_id")
357+
LOG.warning(
358+
"SVI gateway check FAILED: router %(router)s (%(name)s) cannot "
359+
"have an external gateway network %(network)s",
360+
{
361+
"router": router_id,
362+
"name": router.get("name"),
363+
"network": network_id,
364+
},
365+
)
366+
raise n_exc.BadRequest(
367+
resource="router",
368+
msg="SVI routers cannot have an external gateway.",
369+
)
370+
338371
@registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_CREATE])
339372
def _validate_svi_router_interface(self, _resource, _event, _trigger, payload):
340373
router = payload.states[0]

python/neutron-understack/neutron_understack/tests/test_svi.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,67 @@ def test_rejects_svi_router_interface_without_scope(self, caplog, mocker):
283283

284284
assert "must belong to an address scope" in str(exc_info.value)
285285
assert "attach_mode=port" in caplog.text
286+
287+
288+
class TestRejectSviExternalGatewayCallback:
289+
def test_rejects_svi_router_external_gateway(self, caplog, mocker):
290+
router = {"id": "router-a", "name": "svi-router", "flavor_id": "flavor-a"}
291+
l3_plugin = FakeL3Plugin(router)
292+
flavor_plugin = FakeFlavorPlugin(_svi_driver())
293+
_patch_plugins(
294+
mocker,
295+
core_plugin=FakeCorePlugin({}),
296+
l3_plugin=l3_plugin,
297+
flavor_plugin=flavor_plugin,
298+
)
299+
provider = svi.Svi(l3_plugin)
300+
payload = SimpleNamespace(
301+
context="context",
302+
resource_id="router-a",
303+
metadata={"network_id": "external-net"},
304+
)
305+
306+
caplog.set_level(logging.WARNING, logger=svi.LOG.name)
307+
with pytest.raises(n_exc.BadRequest) as exc_info:
308+
provider._reject_svi_external_gateway(None, None, None, payload)
309+
310+
assert "cannot have an external gateway" in str(exc_info.value)
311+
assert "SVI gateway check FAILED" in caplog.text
312+
assert "external-net" in caplog.text
313+
314+
def test_allows_non_svi_router_external_gateway(self, mocker):
315+
router = {"id": "router-a", "name": "vrf-router", "flavor_id": "flavor-a"}
316+
l3_plugin = FakeL3Plugin(router)
317+
flavor_plugin = FakeFlavorPlugin("neutron_understack.l3_router.vrf.Vrf")
318+
_patch_plugins(
319+
mocker,
320+
core_plugin=FakeCorePlugin({}),
321+
l3_plugin=l3_plugin,
322+
flavor_plugin=flavor_plugin,
323+
)
324+
provider = svi.Svi(l3_plugin)
325+
payload = SimpleNamespace(
326+
context="context",
327+
resource_id="router-a",
328+
metadata={"network_id": "external-net"},
329+
)
330+
331+
provider._reject_svi_external_gateway(None, None, None, payload)
332+
333+
def test_allows_router_without_flavor(self, mocker):
334+
router = {"id": "router-a", "name": "plain-router", "flavor_id": None}
335+
l3_plugin = FakeL3Plugin(router)
336+
_patch_plugins(
337+
mocker,
338+
core_plugin=FakeCorePlugin({}),
339+
l3_plugin=l3_plugin,
340+
flavor_plugin=FakeFlavorPlugin(_svi_driver()),
341+
)
342+
provider = svi.Svi(l3_plugin)
343+
payload = SimpleNamespace(
344+
context="context",
345+
resource_id="router-a",
346+
metadata={"network_id": "external-net"},
347+
)
348+
349+
provider._reject_svi_external_gateway(None, None, None, payload)

0 commit comments

Comments
 (0)