From: Ryota MIBU Date: Thu, 28 Mar 2013 06:38:13 +0000 (+0900) Subject: Prevents a portinfo deletion in vm migration with NEC plugin. X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=a4e2442f25b63c976490882b21979a1a44963618;p=openstack-build%2Fneutron-build.git Prevents a portinfo deletion in vm migration with NEC plugin. Fixes: Bug 1160193 This patch prevents a portinfo deletion at the end of the migration sequence by ignoring port_removed message from the source host. Change-Id: I96932587b9d660aa01f8fcae0b0426c849d6c483 --- diff --git a/quantum/plugins/nec/nec_plugin.py b/quantum/plugins/nec/nec_plugin.py index 5e230371f..f1c14df1c 100644 --- a/quantum/plugins/nec/nec_plugin.py +++ b/quantum/plugins/nec/nec_plugin.py @@ -683,7 +683,21 @@ class NECPluginV2RPCCallbacks(object): mac=p.get('mac', '')) self.plugin.activate_port_if_ready(rpc_context, port) for id in kwargs.get('port_removed', []): + portinfo = ndb.get_portinfo(session, id) + if not portinfo: + LOG.debug(_("update_ports(): ignore port_removed message " + "due to portinfo for port_id=%s was not " + "registered"), id) + continue + if portinfo.datapath_id is not datapath_id: + LOG.debug(_("update_ports(): ignore port_removed message " + "received from different host " + "(registered_datapath_id=%(registered)s, " + "received_datapath_id=%(received)s)."), + {'registered': portinfo.datapath_id, + 'received': datapath_id}) + continue port = self.plugin.get_port(rpc_context, id) - if port and ndb.get_portinfo(session, id): + if port: ndb.del_portinfo(session, id) self.plugin.deactivate_port(rpc_context, port) diff --git a/quantum/tests/unit/nec/test_nec_plugin.py b/quantum/tests/unit/nec/test_nec_plugin.py index b510c0b0f..cc5d00453 100644 --- a/quantum/tests/unit/nec/test_nec_plugin.py +++ b/quantum/tests/unit/nec/test_nec_plugin.py @@ -13,12 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +import mock + +from quantum import context as q_context +from quantum import manager +from quantum.common import topics from quantum.extensions import portbindings +from quantum.plugins.nec import nec_plugin from quantum.tests.unit import _test_extension_portbindings as test_bindings from quantum.tests.unit import test_db_plugin as test_plugin from quantum.tests.unit import test_security_groups_rpc as test_sg_rpc +OFC_MANAGER = 'quantum.plugins.nec.nec_plugin.ofc_manager.OFCManager' PLUGIN_NAME = 'quantum.plugins.nec.nec_plugin.NECPluginV2' @@ -63,3 +70,113 @@ class TestNecPortBinding(test_bindings.PortBindingsTestCase, class TestNecPortBindingNoSG(TestNecPortBinding): HAS_PORT_FILTER = False FIREWALL_DRIVER = test_sg_rpc.FIREWALL_NOOP_DRIVER + + +class TestNecPortsV2Callback(NecPluginV2TestCase): + + def setUp(self): + self.addCleanup(mock.patch.stopall) + ofc_manager_p = mock.patch(OFC_MANAGER) + ofc_manager_cls = ofc_manager_p.start() + self.ofc = mock.Mock() + ofc_manager_cls.return_value = self.ofc + self.ofc_port_exists = False + self._setup_side_effects() + + super(TestNecPortsV2Callback, self).setUp() + self.context = q_context.get_admin_context() + self.plugin = manager.QuantumManager.get_plugin() + self.callbacks = nec_plugin.NECPluginV2RPCCallbacks(self.plugin) + + def _setup_side_effects(self): + def _create_ofc_port_called(*args, **kwargs): + self.ofc_port_exists = True + + def _delete_ofc_port_called(*args, **kwargs): + self.ofc_port_exists = False + + def _exists_ofc_port_called(*args, **kwargs): + return self.ofc_port_exists + + self.ofc.create_ofc_port.side_effect = _create_ofc_port_called + self.ofc.delete_ofc_port.side_effect = _delete_ofc_port_called + self.ofc.exists_ofc_port.side_effect = _exists_ofc_port_called + + def _rpcapi_update_ports(self, agent_id='nec-q-agent.fake', + datapath_id="0xabc", added=[], removed=[]): + kwargs = {'topic': topics.AGENT, + 'agent_id': agent_id, + 'datapath_id': datapath_id, + 'port_added': added, 'port_removed': removed} + self.callbacks.update_ports(self.context, **kwargs) + + def test_port_create(self): + with self.port() as port: + port_id = port['port']['id'] + sport = self.plugin.get_port(self.context, port_id) + self.assertEqual(sport['status'], 'DOWN') + + portinfo = {'id': port_id, 'port_no': 123} + self._rpcapi_update_ports(added=[portinfo]) + + sport = self.plugin.get_port(self.context, port_id) + self.assertEqual(sport['status'], 'ACTIVE') + + expected = [ + mock.call.exists_ofc_port(mock.ANY, port_id), + mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY), + ] + self.ofc.assert_has_calls(expected) + + def test_port_delete(self): + with self.port() as port: + port_id = port['port']['id'] + portinfo = {'id': port_id, 'port_no': 456} + self._rpcapi_update_ports(added=[portinfo]) + + expected = [ + mock.call.exists_ofc_port(mock.ANY, port_id), + mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY), + mock.call.exists_ofc_port(mock.ANY, port_id), + mock.call.delete_ofc_port(mock.ANY, port_id, mock.ANY), + ] + self.ofc.assert_has_calls(expected) + + def test_port_migration(self): + agent_id_a, datapath_id_a, port_no_a = 'nec-q-agent.aa', '0xaaa', 10 + agent_id_b, datapath_id_b, port_no_b = 'nec-q-agent.bb', '0xbbb', 11 + + with self.port() as port: + port_id = port['port']['id'] + sport = self.plugin.get_port(self.context, port_id) + self.assertEqual(sport['status'], 'DOWN') + + portinfo_a = {'id': port_id, 'port_no': port_no_a} + self._rpcapi_update_ports(agent_id=agent_id_a, + datapath_id=datapath_id_a, + added=[portinfo_a]) + + portinfo_b = {'id': port_id, 'port_no': port_no_b} + self._rpcapi_update_ports(agent_id=agent_id_b, + datapath_id=datapath_id_b, + added=[portinfo_b]) + + self._rpcapi_update_ports(agent_id=agent_id_a, + datapath_id=datapath_id_a, + removed=[port_id]) + + sport = self.plugin.get_port(self.context, port_id) + self.assertEqual(sport['status'], 'ACTIVE') + self.assertTrue(self.ofc_port_exists) + + expected = [ + mock.call.exists_ofc_port(mock.ANY, port_id), + mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY), + mock.call.exists_ofc_port(mock.ANY, port_id), + mock.call.delete_ofc_port(mock.ANY, port_id, mock.ANY), + mock.call.exists_ofc_port(mock.ANY, port_id), + mock.call.create_ofc_port(mock.ANY, port_id, mock.ANY), + ] + self.ofc.assert_has_calls(expected) + self.assertEqual(2, self.ofc.create_ofc_port.call_count) + self.assertEqual(1, self.ofc.delete_ofc_port.call_count)