Notify mech drivers with original and modified network.

Include original and modified network when notifying mechanism drivers in PRECOMMIT event. PRECOMMIT_CREATE modified network includes new segment, while original does not. Exact opposite for PRECOMMIT_DELETE

Closes-Bug: #1967742
Change-Id: I364fc7981458374ed25eb8837d1ed3afff046b95
This commit is contained in:
Miro Tomaska 2022-05-06 13:42:11 -05:00
parent 42ce0ea42c
commit d1fe14d366
3 changed files with 152 additions and 9 deletions
neutron
plugins/ml2
tests/unit/plugins/ml2

@ -162,9 +162,9 @@ class TypeManager(stevedore.named.NamedExtensionManager):
net_segments = segments_db.get_networks_segments(context, ids)
for network in networks:
segments = net_segments[network['id']]
self._extend_network_dict_provider(network, segments)
self.extend_network_with_provider_segments(network, segments)
def _extend_network_dict_provider(self, network, segments):
def extend_network_with_provider_segments(self, network, segments):
if not segments:
LOG.debug("Network %s has no segments", network['id'])
for attr in provider.ATTRIBUTES:
@ -183,6 +183,22 @@ class TypeManager(stevedore.named.NamedExtensionManager):
network[provider.SEGMENTATION_ID] = segment[
api.SEGMENTATION_ID]
@staticmethod
def pop_segments_from_network(network):
multiple_segments = network.pop(mpnet_apidef.SEGMENTS, [])
if multiple_segments:
network_segments = multiple_segments
else:
network_segments = [
{provider_key: network.pop(provider_key)
for provider_key in provider.ATTRIBUTES}]
return (
[{api.NETWORK_TYPE: network_segment[provider.NETWORK_TYPE],
api.PHYSICAL_NETWORK: network_segment[provider.PHYSICAL_NETWORK],
api.SEGMENTATION_ID: network_segment[provider.SEGMENTATION_ID]}
for network_segment in network_segments])
def initialize(self):
for network_type, driver in self.drivers.items():
LOG.info("Initializing driver for type '%s'", network_type)

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
from eventlet import greenthread
import netaddr
from netaddr.strategy import eui48
@ -2511,7 +2513,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
try:
self._notify_mechanism_driver_for_segment_change(
event, context, network_id)
event, context, segment)
except ml2_exc.MechanismDriverError:
with excutils.save_and_reraise_exception():
LOG.error("mechanism_manager error occurred when "
@ -2519,15 +2521,53 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
"'%(segment)s'",
{'event': event, 'segment': segment['id']})
def _build_original_network(self, event, network, changed_segment):
"""Constructs a copy of the given network where the given
segment will be either removed(precommit_create) or added
(precommit_delete) to the copied network
"""
network_copy = copy.deepcopy(network)
network_segments = managers.TypeManager()\
.pop_segments_from_network(network_copy)
if event == events.PRECOMMIT_CREATE:
network_segments = [network_segment
for network_segment in network_segments
# A segment popped from a network could have its
# segmentation_id set to None if the segment
# beeing created is partial.
if not ((network_segment[api.SEGMENTATION_ID] ==
changed_segment[api.SEGMENTATION_ID] or
network_segment[api.SEGMENTATION_ID] is None) and
network_segment[api.NETWORK_TYPE] ==
changed_segment[api.NETWORK_TYPE] and
network_segment[api.PHYSICAL_NETWORK] ==
changed_segment[api.PHYSICAL_NETWORK])]
elif event == events.PRECOMMIT_DELETE:
network_segments.append(changed_segment)
self.type_manager.extend_network_with_provider_segments(
network_copy, network_segments)
return network_copy
def _notify_mechanism_driver_for_segment_change(self, event,
context, network_id):
network_with_segments = self.get_network(context, network_id)
mech_context = driver_context.NetworkContext(
self, context, network_with_segments,
original_network=network_with_segments)
context, segment):
network = self.get_network(context, segment['network_id'])
if event in [events.PRECOMMIT_CREATE, events.PRECOMMIT_DELETE]:
original_network = self._build_original_network(
event, network, segment)
mech_context = driver_context.NetworkContext(
self, context, network,
original_network=original_network)
self.mechanism_manager.update_network_precommit(mech_context)
elif event in [events.AFTER_CREATE, events.AFTER_DELETE]:
if event in [events.AFTER_CREATE, events.AFTER_DELETE]:
mech_context = driver_context.NetworkContext(
self, context, network,
original_network=network)
self.mechanism_manager.update_network_postcommit(mech_context)
@staticmethod

@ -3647,3 +3647,90 @@ class TestML2Segments(Ml2PluginV2TestCase):
resource_id=segment['id']))
exist_port = self._show('ports', port['port']['id'])
self.assertEqual(port['port']['id'], exist_port['port']['id'])
def test_precommit_create_builds_single_segment_original_network(self):
event = events.PRECOMMIT_CREATE
network_type = 'vlan'
physical_network = self.physnet
segmentation_id = 2
network_segments = [{pnet.NETWORK_TYPE: 'vlan',
pnet.PHYSICAL_NETWORK: self.physnet2,
pnet.SEGMENTATION_ID: 1},
{pnet.NETWORK_TYPE: network_type,
pnet.PHYSICAL_NETWORK: physical_network,
pnet.SEGMENTATION_ID: segmentation_id}]
new_segment = {driver_api.NETWORK_TYPE: network_type,
driver_api.PHYSICAL_NETWORK: physical_network,
driver_api.SEGMENTATION_ID: segmentation_id}
with self.network(**{'arg_list': (mpnet_apidef.SEGMENTS, ),
mpnet_apidef.SEGMENTS: network_segments})\
as test_network:
multisegment_network = test_network['network']
observed_network = self.driver._build_original_network(
event, multisegment_network, new_segment)
# Should become a single segment network
self.assertNotIn(mpnet_apidef.SEGMENTS, observed_network)
# Where new segment in not part of the network
self.assertNotEqual(segmentation_id,
observed_network[pnet.SEGMENTATION_ID])
def test_precommit_create_builds_multisegment_original_network(self):
event = events.PRECOMMIT_CREATE
network_type = 'vlan'
physical_network = self.physnet
segmentation_id = 3
network_segments = [{pnet.NETWORK_TYPE: 'vlan',
pnet.PHYSICAL_NETWORK: self.physnet2,
pnet.SEGMENTATION_ID: 1},
{pnet.NETWORK_TYPE: 'vlan',
pnet.PHYSICAL_NETWORK: self.physnet3,
pnet.SEGMENTATION_ID: 2},
{pnet.NETWORK_TYPE: network_type,
pnet.PHYSICAL_NETWORK: physical_network,
pnet.SEGMENTATION_ID: segmentation_id}]
new_segment = {driver_api.NETWORK_TYPE: network_type,
driver_api.PHYSICAL_NETWORK: physical_network,
driver_api.SEGMENTATION_ID: segmentation_id}
with self.network(**{'arg_list': (mpnet_apidef.SEGMENTS, ),
mpnet_apidef.SEGMENTS: network_segments})\
as test_network:
multisegment_network = test_network['network']
observed_network = self.driver._build_original_network(
event, multisegment_network, new_segment)
# Should remain a multisegment network
self.assertIn(mpnet_apidef.SEGMENTS, observed_network)
# Where new segment in not part of the multisegment
self.assertNotIn(network_segments[2],
observed_network[mpnet_apidef.SEGMENTS])
def test_precommit_delete_builds_multisegment_original_network(self):
event = events.PRECOMMIT_DELETE
network_type = 'vlan'
physical_network = self.physnet
segmentation_id = 2
deleted_segment = {driver_api.NETWORK_TYPE: network_type,
driver_api.PHYSICAL_NETWORK: physical_network,
driver_api.SEGMENTATION_ID: segmentation_id}
expected_segment = {pnet.NETWORK_TYPE: network_type,
pnet.PHYSICAL_NETWORK: physical_network,
pnet.SEGMENTATION_ID: segmentation_id}
with self.network() as test_network:
# network() implicitaly creates a single segment
single_segment_network = test_network['network']
observed_network = self.driver._build_original_network(
event, single_segment_network, deleted_segment)
# Should become a multisegment network
self.assertIn(mpnet_apidef.SEGMENTS, observed_network)
self.assertEqual(2, len(observed_network[mpnet_apidef.SEGMENTS]))
# Where new segment is part of segments
self.assertIn(expected_segment,
observed_network[mpnet_apidef.SEGMENTS])