Skip to content

Commit b6e3e68

Browse files
kyu0Alex-Welsh
authored andcommitted
Fix VLAN network detection to use VlanTypeDriver ranges
The _is_vlan_project_network method in DNSExtensionDriverML2 was only checking the static network_vlan_ranges configuration from ml2_conf.ini, ignoring the dynamic ranges provided by the network-segment-range service plugin. This caused a KeyError when creating subnets or VMs on VLAN networks with segment ranges managed via the API. This fix updates the method to use VlanTypeDriver.get_network_segment_ranges(), which properly handles both static configuration and DB-based dynamic ranges when the network-segment-range plugin is enabled. Closes-Bug: #2140291 Signed-off-by: Kyuyeong Lee <kyu0.lee@samsung.com> Change-Id: I8f3a2b1c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a (cherry picked from commit cd8f88e)
1 parent 8721905 commit b6e3e68

2 files changed

Lines changed: 125 additions & 7 deletions

File tree

neutron/plugins/ml2/extensions/dns_integration.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from neutron_lib.exceptions import dns as dns_exc
2424
from neutron_lib.plugins import directory
2525
from neutron_lib.plugins.ml2 import api
26-
from neutron_lib.plugins import utils as plugin_utils
2726
from oslo_config import cfg
2827
from oslo_log import log as logging
2928

@@ -349,9 +348,23 @@ def _get_details(self, context, network_id):
349348

350349
class DNSExtensionDriverML2(DNSExtensionDriver):
351350

351+
def __init__(self):
352+
super().__init__()
353+
self._vlan_driver = None
354+
self._plugin = None
355+
352356
def initialize(self):
353357
LOG.info("DNSExtensionDriverML2 initialization complete")
354358

359+
@property
360+
def vlan_driver(self):
361+
if not self._vlan_driver:
362+
if not self._plugin:
363+
self._plugin = directory.get_plugin()
364+
self._vlan_driver = self._plugin.type_manager.drivers.get(
365+
lib_const.TYPE_VLAN)
366+
return self._vlan_driver
367+
355368
def _is_tunnel_project_network(self, provider_net):
356369
if provider_net['network_type'] == lib_const.TYPE_GENEVE:
357370
tunnel_ranges = cfg.CONF.ml2_type_geneve.vni_ranges
@@ -369,15 +382,15 @@ def _is_tunnel_project_network(self, provider_net):
369382
return int(tun_min) <= segmentation_id <= int(tun_max)
370383

371384
def _is_vlan_project_network(self, provider_net):
372-
network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
373-
cfg.CONF.ml2_type_vlan.network_vlan_ranges)
374-
vlan_ranges = network_vlan_ranges[provider_net['physical_network']]
385+
if not self.vlan_driver:
386+
return False
387+
network_vlan_ranges = self.vlan_driver.obj.get_network_segment_ranges()
388+
vlan_ranges = network_vlan_ranges.get(provider_net['physical_network'])
375389
if not vlan_ranges:
376390
return False
377391
segmentation_id = int(provider_net['segmentation_id'])
378-
for vlan_range in vlan_ranges:
379-
if vlan_range[0] <= segmentation_id <= vlan_range[1]:
380-
return True
392+
return any(vlan_range[0] <= segmentation_id <= vlan_range[1]
393+
for vlan_range in vlan_ranges)
381394

382395
def external_dns_not_needed(self, context, network, subnets):
383396
dns_driver = _get_dns_driver()

neutron/tests/unit/plugins/ml2/extensions/test_dns_integration.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,111 @@ def test_update_fixed_ips_no_effect_after_clearing_dns_domain(self,
760760
dns_data_db, dns_data_db_1, dns_data_db_2)
761761

762762

763+
class DNSExtensionDriverML2TestCase(testtools.TestCase):
764+
765+
def setUp(self):
766+
super().setUp()
767+
self.driver = dns_integration.DNSExtensionDriverML2()
768+
self.mock_vlan_driver = mock.Mock()
769+
self.mock_vlan_driver_property = mock.patch.object(
770+
type(self.driver), 'vlan_driver',
771+
new_callable=mock.PropertyMock,
772+
return_value=self.mock_vlan_driver).start()
773+
774+
def test__is_vlan_project_network_with_multiple_ranges(self):
775+
self.mock_vlan_driver.obj.get_network_segment_ranges.return_value = {
776+
'physnet1': [(100, 200), (300, 400)],
777+
'physnet2': [(500, 600)]
778+
}
779+
780+
provider_net_1 = {
781+
'physical_network': 'physnet1',
782+
'segmentation_id': 150
783+
}
784+
self.assertTrue(self.driver._is_vlan_project_network(provider_net_1))
785+
786+
provider_net_2 = {
787+
'physical_network': 'physnet1',
788+
'segmentation_id': 250
789+
}
790+
self.assertFalse(self.driver._is_vlan_project_network(provider_net_2))
791+
792+
provider_net_3 = {
793+
'physical_network': 'physnet1',
794+
'segmentation_id': 350
795+
}
796+
self.assertTrue(self.driver._is_vlan_project_network(provider_net_3))
797+
798+
provider_net_4 = {
799+
'physical_network': 'physnet2',
800+
'segmentation_id': 550
801+
}
802+
self.assertTrue(self.driver._is_vlan_project_network(provider_net_4))
803+
804+
provider_net_5 = {
805+
'physical_network': 'physnet2',
806+
'segmentation_id': 650
807+
}
808+
self.assertFalse(self.driver._is_vlan_project_network(provider_net_5))
809+
810+
def test__is_vlan_project_network_boundary_values(self):
811+
self.mock_vlan_driver.obj.get_network_segment_ranges.return_value = {
812+
'physnet1': [(100, 200)]
813+
}
814+
815+
provider_net_1 = {
816+
'physical_network': 'physnet1',
817+
'segmentation_id': 100
818+
}
819+
self.assertTrue(self.driver._is_vlan_project_network(provider_net_1))
820+
821+
provider_net_2 = {
822+
'physical_network': 'physnet1',
823+
'segmentation_id': 200
824+
}
825+
self.assertTrue(self.driver._is_vlan_project_network(provider_net_2))
826+
827+
provider_net_3 = {
828+
'physical_network': 'physnet1',
829+
'segmentation_id': 99
830+
}
831+
self.assertFalse(self.driver._is_vlan_project_network(provider_net_3))
832+
833+
provider_net_4 = {
834+
'physical_network': 'physnet1',
835+
'segmentation_id': 201
836+
}
837+
self.assertFalse(self.driver._is_vlan_project_network(provider_net_4))
838+
839+
def test__is_vlan_project_network_physnet_not_found(self):
840+
self.mock_vlan_driver.obj.get_network_segment_ranges.return_value = {
841+
'physnet1': [(100, 200)]
842+
}
843+
844+
provider_net = {
845+
'physical_network': 'physnet2',
846+
'segmentation_id': 150
847+
}
848+
self.assertFalse(self.driver._is_vlan_project_network(provider_net))
849+
850+
def test__is_vlan_project_network_empty_ranges(self):
851+
self.mock_vlan_driver.obj.get_network_segment_ranges.return_value = {}
852+
853+
provider_net = {
854+
'physical_network': 'physnet1',
855+
'segmentation_id': 100
856+
}
857+
self.assertFalse(self.driver._is_vlan_project_network(provider_net))
858+
859+
def test__is_vlan_project_network_no_vlan_driver(self):
860+
self.mock_vlan_driver_property.return_value = None
861+
provider_net = {
862+
'physical_network': 'physnet1',
863+
'segmentation_id': 100
864+
}
865+
self.assertFalse(self.driver._is_vlan_project_network(provider_net))
866+
867+
763868
class TestDesignateClientKeystoneV3(testtools.TestCase):
764869
"""Test case for designate clients """
765870

0 commit comments

Comments
 (0)