Fixed bug that old VLAN ID is used in multi-POD environment
When a network spreads multiple PODs and port creation is required just after port deletion for a same POD, allocation of VLAN ID for a network segment (POD) can be racy. As a result, a corresponding port creation on NWA fails because VLAN segment is released during the operation. To fix the bug, this commit makes the following changes: * NWA plugin now deletes VLAN ID information from neutron DB before calling delete request to NWA. * When the delete request completes and if VLAN ID of the requested segment is zero (which means another allocation request is ongogin), VLAN ID information is not touched. Note that this bug does not occurs in a single POD environemnt. Change-Id: I2fe86c54063310497ca0091200433e54bb16b198 Closes-Bug: #1569323
This commit is contained in:
parent
1e8bf7394e
commit
9cbfd415f0
|
@ -433,6 +433,7 @@ class AgentProxyL2(object):
|
|||
if 1 < gd_count:
|
||||
nwa_data = self._delete_general_dev_data(
|
||||
nwa_data=nwa_data, **kwargs)
|
||||
self._delete_general_dev_segment(context, nwa_data, nwa_info)
|
||||
raise nwa_exc.AgentProxyException(value=nwa_data)
|
||||
|
||||
# delete general dev
|
||||
|
@ -514,6 +515,15 @@ class AgentProxyL2(object):
|
|||
|
||||
return nwa_data
|
||||
|
||||
def _delete_general_dev_segment(self, context, nwa_data, nwa_info):
|
||||
network_id = nwa_info['network']['id']
|
||||
physical_network = nwa_info['physical_network']
|
||||
resource_group_name = nwa_info['resource_group_name']
|
||||
if not check_segment_gd(network_id, resource_group_name, nwa_data):
|
||||
self.nwa_l2_rpc.release_dynamic_segment_from_agent(
|
||||
context, physical_network, network_id
|
||||
)
|
||||
|
||||
@utils.log_method_return_value
|
||||
@helpers.log_method_call
|
||||
def _delete_general_dev(self, context, **kwargs):
|
||||
|
@ -540,6 +550,7 @@ class AgentProxyL2(object):
|
|||
if body['status'] == 'SUCCEED':
|
||||
LOG.debug("DeleteGeneralDev SUCCEED")
|
||||
nwa_data = self._delete_general_dev_data(**kwargs)
|
||||
self._delete_general_dev_segment(context, nwa_data, nwa_info)
|
||||
else:
|
||||
LOG.debug("DeleteGeneralDev %s", body['status'])
|
||||
raise nwa_exc.AgentProxyException(value=nwa_data)
|
||||
|
|
|
@ -181,6 +181,7 @@ class NECNWAMechanismDriver(ovs.OpenvswitchMechanismDriver):
|
|||
try:
|
||||
kwargs = self._make_l2api_kwargs(
|
||||
context, use_original_port=use_original_port)
|
||||
self._l2_delete_segment(context, kwargs['nwa_info'])
|
||||
proxy = self._get_l2api_proxy(context, kwargs['tenant_id'])
|
||||
kwargs['nwa_info'] = self._revert_dhcp_agent_device_id(
|
||||
context, kwargs['nwa_info'])
|
||||
|
@ -210,6 +211,16 @@ class NECNWAMechanismDriver(ovs.OpenvswitchMechanismDriver):
|
|||
)
|
||||
return nwa_info
|
||||
|
||||
def _l2_delete_segment(self, context, nwa_info):
|
||||
session = context.network._plugin_context.session
|
||||
del_segment = db.get_dynamic_segment(
|
||||
session,
|
||||
context.network.current['id'],
|
||||
physical_network=nwa_info['physical_network'])
|
||||
if del_segment:
|
||||
LOG.debug('delete_network_segment %s', del_segment)
|
||||
db.delete_network_segment(session, del_segment['id'])
|
||||
|
||||
def _l3_create_tenant_fw(self, context):
|
||||
device_owner = context._port['device_owner']
|
||||
grplst = [res['device_owner'] for res in self.resource_groups]
|
||||
|
|
|
@ -133,6 +133,6 @@ class NwaL2ServerRpcCallback(object):
|
|||
session, network_id, physical_network=physical_network,
|
||||
)
|
||||
if del_segment:
|
||||
LOG.debug("release_dynamic_segment segment_id=%s",
|
||||
del_segment['id'])
|
||||
db_ml2.delete_network_segment(session, del_segment['id'])
|
||||
LOG.debug("release_dynamic_segment segment=%s", del_segment)
|
||||
if del_segment['segmentation_id'] != 0:
|
||||
db_ml2.delete_network_segment(session, del_segment['id'])
|
||||
|
|
|
@ -497,3 +497,15 @@ class TestNECNWAMechanismDriver(TestMechNwa):
|
|||
self.context._port['device_owner'] = constants.DEVICE_OWNER_ROUTER_INTF
|
||||
gntb.side_effect = Exception
|
||||
self.driver._bind_port_nwa(self.context)
|
||||
|
||||
@patch('neutron.plugins.ml2.db.get_dynamic_segment')
|
||||
@patch('neutron.plugins.ml2.db.delete_network_segment')
|
||||
def test__l2_delete_segment(self, dns, gds):
|
||||
gds.return_value = None
|
||||
self.driver._l2_delete_segment(self.context, MagicMock())
|
||||
self.assertEqual(0, dns.call_count)
|
||||
|
||||
dns.mock_reset()
|
||||
gds.return_value = {'id': 'ID-100'}
|
||||
self.driver._l2_delete_segment(self.context, MagicMock())
|
||||
self.assertEqual(1, dns.call_count)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright 2016 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from neutron.tests import base
|
||||
|
||||
from networking_nec.nwa.l2.rpc import nwa_l2_server_api
|
||||
|
||||
|
||||
class TestNwaL2ServerApi(base.BaseTestCase):
|
||||
|
||||
@mock.patch('neutron.common.rpc.get_client')
|
||||
def setUp(self, f1):
|
||||
super(TestNwaL2ServerApi, self).setUp()
|
||||
self.proxy = nwa_l2_server_api.NwaL2ServerRpcApi("dummy-topic")
|
||||
self.context = mock.MagicMock()
|
||||
|
||||
def test_release_dynamic_segment_from_agent(self):
|
||||
cctxt = mock.MagicMock()
|
||||
self.proxy.client.prepare.return_value = cctxt
|
||||
self.proxy.release_dynamic_segment_from_agent(
|
||||
self.context, 'physical_network', 'network_id')
|
||||
self.assertEqual(1, cctxt.call.call_count)
|
|
@ -0,0 +1,47 @@
|
|||
# Copyright 2016 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from neutron.tests import base
|
||||
|
||||
from networking_nec.nwa.l2.rpc import nwa_l2_server_callback
|
||||
|
||||
|
||||
class TestNwaL2ServerRpcCallback(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNwaL2ServerRpcCallback, self).setUp()
|
||||
self.callback = nwa_l2_server_callback.NwaL2ServerRpcCallback()
|
||||
self.context = mock.MagicMock()
|
||||
|
||||
@mock.patch('neutron.db.api.get_session')
|
||||
@mock.patch('neutron.plugins.ml2.db.get_dynamic_segment')
|
||||
@mock.patch('neutron.plugins.ml2.db.delete_network_segment')
|
||||
def test_release_dynamic_segment_from_agent(self, dns, gds, gs):
|
||||
del_segment = {'segmentation_id': 0, 'id': 'ID-0'}
|
||||
gds.return_value = del_segment
|
||||
self.callback.release_dynamic_segment_from_agent(
|
||||
self.context,
|
||||
network_id='network-id',
|
||||
physical_network='physical-network')
|
||||
self.assertEqual(0, dns.call_count)
|
||||
|
||||
dns.reset_mock()
|
||||
del_segment = {'segmentation_id': 4000, 'id': 'ID-4000'}
|
||||
gds.return_value = del_segment
|
||||
self.callback.release_dynamic_segment_from_agent(
|
||||
self.context,
|
||||
network_id='network-id',
|
||||
physical_network='physical-network')
|
||||
self.assertEqual(1, dns.call_count)
|
Loading…
Reference in New Issue