Skip to content

Commit 7032865

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] Fix updating network segmentation ID" into stable/yoga
2 parents 0836d57 + c11103a commit 7032865

File tree

6 files changed

+128
-19
lines changed

6 files changed

+128
-19
lines changed

neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,16 @@ def sb_ovn(self):
179179
def sb_ovn(self, val):
180180
self._sb_ovn = val
181181

182+
def get_supported_vif_types(self):
183+
vif_types = set()
184+
for ch in self.sb_ovn.chassis_list().execute(check_error=True):
185+
dp_type = ch.external_ids.get('datapath-type', '')
186+
if dp_type == ovn_const.CHASSIS_DATAPATH_NETDEV:
187+
vif_types.add(portbindings.VIF_TYPE_VHOST_USER)
188+
else:
189+
vif_types.add(portbindings.VIF_TYPE_OVS)
190+
return list(vif_types)
191+
182192
def check_vlan_transparency(self, context):
183193
"""OVN driver vlan transparency support."""
184194
vlan_transparency_network_types = [
@@ -213,6 +223,10 @@ def connectivity(self):
213223
def supported_extensions(self, extensions):
214224
return set(ovn_extensions.ML2_SUPPORTED_API_EXTENSIONS) & extensions
215225

226+
@staticmethod
227+
def provider_network_attribute_updates_supported():
228+
return [provider_net.SEGMENTATION_ID]
229+
216230
def subscribe(self):
217231
registry.subscribe(self.pre_fork_initialize,
218232
resources.PROCESS,

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,15 @@ def update_network(self, context, network, original_network=None):
19541954
if ovn_ls_azs != neutron_net_azs:
19551955
self.sync_ha_chassis_group(context, network['id'], txn)
19561956

1957+
# Update the segment tags, if any
1958+
segments = segments_db.get_network_segments(context, network['id'])
1959+
for segment in segments:
1960+
tag = segment.get(segment_def.SEGMENTATION_ID)
1961+
tag = [] if tag is None else tag
1962+
lport_name = utils.ovn_provnet_port_name(segment['id'])
1963+
txn.add(self._nb_idl.set_lswitch_port(lport_name=lport_name,
1964+
tag=tag, if_exists=True))
1965+
19571966
self._qos_driver.update_network(txn, network, original_network)
19581967

19591968
if check_rev_cmd.result == ovn_const.TXN_COMMITTED:

neutron/plugins/ml2/plugin.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -916,16 +916,26 @@ def _update_segmentation_id(self, context, network, net_data):
916916
vif_types = [portbindings.VIF_TYPE_UNBOUND,
917917
portbindings.VIF_TYPE_BINDING_FAILED]
918918
for mech_driver in self.mechanism_manager.ordered_mech_drivers:
919-
if (isinstance(mech_driver.obj,
920-
mech_agent.AgentMechanismDriverBase) and
921-
provider_net.SEGMENTATION_ID in mech_driver.obj.
919+
if (provider_net.SEGMENTATION_ID in mech_driver.obj.
922920
provider_network_attribute_updates_supported()):
923-
agent_type = mech_driver.obj.agent_type
924-
agents = self.get_agents(
925-
context, filters={'agent_type': [agent_type]})
926-
for agent in agents:
927-
vif_types.append(
928-
mech_driver.obj.get_supported_vif_type(agent))
921+
if isinstance(mech_driver.obj,
922+
mech_agent.AgentMechanismDriverBase):
923+
agent_type = mech_driver.obj.agent_type
924+
agents = self.get_agents(
925+
context, filters={'agent_type': [agent_type]})
926+
for agent in agents:
927+
vif_types.append(
928+
mech_driver.obj.get_supported_vif_type(agent))
929+
else:
930+
# For non-AgentMechanismDriverBase drivers such as OVN
931+
# which inherits from MechanismDriver
932+
# TODO(lucasagomes): Perhaps we should
933+
# include get_supported_vif_types() as part of the
934+
# MechanismDriver's api in neutron-lib and remove this
935+
# check in the future ?
936+
if getattr(mech_driver.obj, 'get_supported_vif_types'):
937+
vif_types.extend(
938+
mech_driver.obj.get_supported_vif_types())
929939

930940
if ports_obj.Port.check_network_ports_by_binding_types(
931941
context, network['id'], vif_types, negative_search=True):

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,6 +2547,40 @@ def test_sync_ha_chassis_group_no_az_hints(self):
25472547
self.nb_ovn.ha_chassis_group_add_chassis.assert_has_calls(
25482548
expected_calls, any_order=True)
25492549

2550+
def test_update_network_segmentation_id(self):
2551+
new_vlan_tag = 123
2552+
net_arg = {pnet.NETWORK_TYPE: 'vlan',
2553+
pnet.PHYSICAL_NETWORK: 'physnet1',
2554+
pnet.SEGMENTATION_ID: '1'}
2555+
net = self._make_network(self.fmt, 'net1', True,
2556+
arg_list=(pnet.NETWORK_TYPE,
2557+
pnet.PHYSICAL_NETWORK,
2558+
pnet.SEGMENTATION_ID,),
2559+
**net_arg)['network']
2560+
# Make sure the network was created with 1 VLAN segment
2561+
segments = segments_db.get_network_segments(self.context, net['id'])
2562+
segment = segments[0]
2563+
self.assertEqual(len(segments), 1)
2564+
self.assertEqual(segment['segmentation_id'], 1)
2565+
2566+
# Issue an update to the network changing the segmentation_id
2567+
data = {'network': {pnet.SEGMENTATION_ID: new_vlan_tag}}
2568+
req = self.new_update_request('networks', data, net['id'])
2569+
res = self.deserialize(self.fmt, req.get_response(self.api))
2570+
self.assertEqual(new_vlan_tag, res['network'][pnet.SEGMENTATION_ID])
2571+
2572+
# Assert the tag was changed in the Neutron database
2573+
segments = segments_db.get_network_segments(self.context, net['id'])
2574+
segment = segments[0]
2575+
self.assertEqual(len(segments), 1)
2576+
self.assertEqual(segment['segmentation_id'], new_vlan_tag)
2577+
2578+
# Assert the tag was changed in the OVN database
2579+
expected_call = mock.call(
2580+
lport_name=ovn_utils.ovn_provnet_port_name(segment['id']),
2581+
tag=new_vlan_tag, if_exists=True)
2582+
self.nb_ovn.set_lswitch_port.assert_has_calls([expected_call])
2583+
25502584

25512585
class OVNMechanismDriverTestCase(MechDriverSetupBase,
25522586
test_plugin.Ml2PluginV2TestCase):
@@ -2891,6 +2925,10 @@ def _test_segments_helper(self):
28912925
segment_id=self.seg_2['id']) as subnet:
28922926
self.sub_2 = subnet
28932927

2928+
# TODO(lucasagomes): This test should use <mock>.assert_has_calls()
2929+
# to validate if the method was called with the correct values instead
2930+
# of peeking at call_args_list otherwise every time there's a new
2931+
# call to the mocked method the indexes will change
28942932
def test_create_segments_subnet_metadata_ip_allocation(self):
28952933
self._test_segments_helper()
28962934
ovn_nb_api = self.mech_driver.nb_ovn
@@ -2899,34 +2937,34 @@ def test_create_segments_subnet_metadata_ip_allocation(self):
28992937
# created subnet.
29002938
self.assertIn(
29012939
'10.0.1.2',
2902-
ovn_nb_api.set_lswitch_port.call_args_list[0][1]['addresses'][0])
2940+
ovn_nb_api.set_lswitch_port.call_args_list[2][1]['addresses'][0])
29032941

29042942
# Assert that the second subnet address also has been allocated for
29052943
# metadata port.
29062944
self.assertIn(
29072945
'10.0.2.2',
2908-
ovn_nb_api.set_lswitch_port.call_args_list[1][1]['addresses'][0])
2946+
ovn_nb_api.set_lswitch_port.call_args_list[6][1]['addresses'][0])
29092947
# Assert also that the first subnet address is in this update
29102948
self.assertIn(
29112949
'10.0.1.2',
2912-
ovn_nb_api.set_lswitch_port.call_args_list[1][1]['addresses'][0])
2950+
ovn_nb_api.set_lswitch_port.call_args_list[6][1]['addresses'][0])
29132951
self.assertEqual(
2914-
ovn_nb_api.set_lswitch_port.call_count, 2)
2952+
ovn_nb_api.set_lswitch_port.call_count, 7)
29152953

29162954
# Make sure both updates where on same metadata port
29172955
args_list = ovn_nb_api.set_lswitch_port.call_args_list
29182956
self.assertEqual(
29192957
'ovnmeta-%s' % self.net['network']['id'],
2920-
args_list[1][1]['external_ids']['neutron:device_id'])
2958+
args_list[6][1]['external_ids']['neutron:device_id'])
29212959
self.assertEqual(
2922-
args_list[1][1]['external_ids']['neutron:device_id'],
2923-
args_list[0][1]['external_ids']['neutron:device_id'])
2960+
args_list[6][1]['external_ids']['neutron:device_id'],
2961+
args_list[2][1]['external_ids']['neutron:device_id'])
29242962
self.assertEqual(
2925-
args_list[1][1]['external_ids']['neutron:device_owner'],
2926-
args_list[0][1]['external_ids']['neutron:device_owner'])
2963+
args_list[6][1]['external_ids']['neutron:device_owner'],
2964+
args_list[2][1]['external_ids']['neutron:device_owner'])
29272965
self.assertEqual(
29282966
const.DEVICE_OWNER_DISTRIBUTED,
2929-
args_list[1][1]['external_ids']['neutron:device_owner'])
2967+
args_list[6][1]['external_ids']['neutron:device_owner'])
29302968

29312969
def test_create_segments_mixed_allocation_prohibited(self):
29322970
self._test_segments_helper()

neutron/tests/unit/plugins/ml2/test_plugin.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,37 @@ def test__update_segmentation_id_agentless_mech_drivers(self):
533533
portbindings.VIF_TYPE_BINDING_FAILED],
534534
negative_search=True)
535535

536+
@mock.patch.object(ml2_plugin, 'isinstance')
537+
@mock.patch.object(port_obj.Port, 'check_network_ports_by_binding_types')
538+
def test__update_segmentation_id_non_AgentMechanismDriverBase(
539+
self, mock_check_network, mock_isinstance):
540+
plugin = directory.get_plugin()
541+
542+
mock_check_network.return_value = False
543+
mock_isinstance.return_value = False
544+
mock_update_net_segment = mock.patch.object(
545+
plugin.type_manager, 'update_network_segment').start()
546+
547+
fake_vif_types = ['fake-vif-type0', 'fake-vif-type1']
548+
fake_driver = mock.Mock()
549+
fake_driver.obj.get_supported_vif_types.return_value = (
550+
fake_vif_types)
551+
fake_driver.obj.provider_network_attribute_updates_supported.\
552+
return_value = {pnet.SEGMENTATION_ID: 1000}
553+
plugin.mechanism_manager.ordered_mech_drivers = [fake_driver]
554+
555+
with self.network() as net:
556+
net_data = {pnet.SEGMENTATION_ID: 1000}
557+
plugin._update_segmentation_id(self.context, net['network'],
558+
net_data)
559+
560+
# Assert the new method get_supported_vif_types() has been called
561+
fake_driver.obj.get_supported_vif_types.assert_called_once_with()
562+
563+
# Assert the update method has been called
564+
mock_update_net_segment.assert_called_once_with(
565+
self.context, net['network'], net_data, mock.ANY)
566+
536567
def test_update_network_with_empty_body(self):
537568
with self.network() as network:
538569
network_id = network["network"]["id"]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
Fixes an issue in the ML2/OVN driver where the network
5+
segment tag was not being updated in the OVN Northbound
6+
database. For more information, see bug `1944708
7+
<https://bugs.launchpad.net/neutron/+bug/1944708>`_.

0 commit comments

Comments
 (0)