From e147a63cb396ebaadd7c841a305cf3a52be1d373 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Wed, 24 Feb 2016 17:14:59 +0800 Subject: [PATCH] [Ironic]Match vif-pif mac address before setting 'vif_port_id' When booting an ironic instance with multi networks, the ironic port 'vif_port_id' may mismatch with the corresponding neutron port. Closes-Bug: #1549068 Co-Authored-By: Sivaramakrishna Garimella (sivaramakrishna.garimella@hp.com) Change-Id: Id5f033136283987eef8de4ce2f6be256e48cdbf8 --- nova/tests/unit/virt/ironic/test_driver.py | 43 +++++++++++++++++++++- nova/tests/unit/virt/ironic/utils.py | 18 +++++++++ nova/virt/ironic/driver.py | 19 ++++++---- 3 files changed, 71 insertions(+), 9 deletions(-) diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py index 3e60632b1f75..e2a6902bb7d7 100644 --- a/nova/tests/unit/virt/ironic/test_driver.py +++ b/nova/tests/unit/virt/ironic/test_driver.py @@ -1321,7 +1321,8 @@ class IronicDriverTestCase(test.NoDBTestCase): def test_plug_vifs_with_port(self, mock_uvifs, mock_port_udt, mock_lp): node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node = ironic_utils.get_test_node(uuid=node_uuid) - port = ironic_utils.get_test_port() + # make the address be consistent with network_info's + port = ironic_utils.get_test_port(address='fake') mock_lp.return_value = [port] @@ -1356,6 +1357,46 @@ class IronicDriverTestCase(test.NoDBTestCase): fields=ironic_driver._NODE_FIELDS) mock__plug_vifs.assert_called_once_with(node, instance, network_info) + @mock.patch.object(FAKE_CLIENT.port, 'update') + @mock.patch.object(FAKE_CLIENT.node, 'list_ports') + @mock.patch.object(ironic_driver.IronicDriver, '_unplug_vifs') + def test_plug_vifs_multiple_ports(self, mock_uvifs, mock_lp, + mock_port_udt): + node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' + node = ironic_utils.get_test_node(uuid=node_uuid) + first_ironic_port_uuid = 'aaaaaaaa-bbbb-1111-dddd-eeeeeeeeeeee' + first_port = ironic_utils.get_test_port(uuid=first_ironic_port_uuid, + node_uuid=node_uuid, + address='11:FF:FF:FF:FF:FF') + second_ironic_port_uuid = 'aaaaaaaa-bbbb-2222-dddd-eeeeeeeeeeee' + second_port = ironic_utils.get_test_port(uuid=second_ironic_port_uuid, + node_uuid=node_uuid, + address='22:FF:FF:FF:FF:FF') + mock_lp.return_value = [second_port, first_port] + instance = fake_instance.fake_instance_obj(self.ctx, + node=node_uuid) + first_vif_id = 'aaaaaaaa-vv11-cccc-dddd-eeeeeeeeeeee' + second_vif_id = 'aaaaaaaa-vv22-cccc-dddd-eeeeeeeeeeee' + first_vif = ironic_utils.get_test_vif( + address='22:FF:FF:FF:FF:FF', + id=second_vif_id) + second_vif = ironic_utils.get_test_vif( + address='11:FF:FF:FF:FF:FF', + id=first_vif_id) + network_info = [first_vif, second_vif] + self.driver._plug_vifs(node, instance, network_info) + + # asserts + mock_uvifs.assert_called_once_with(node, instance, network_info) + mock_lp.assert_called_once_with(node_uuid) + calls = (mock.call(first_ironic_port_uuid, + [{'op': 'add', 'path': '/extra/vif_port_id', + 'value': first_vif_id}]), + mock.call(second_ironic_port_uuid, + [{'op': 'add', 'path': '/extra/vif_port_id', + 'value': second_vif_id}])) + mock_port_udt.assert_has_calls(calls, any_order=True) + @mock.patch.object(FAKE_CLIENT.port, 'update') @mock.patch.object(FAKE_CLIENT.node, 'list_ports') @mock.patch.object(ironic_driver.IronicDriver, '_unplug_vifs') diff --git a/nova/tests/unit/virt/ironic/utils.py b/nova/tests/unit/virt/ironic/utils.py index c55966f71365..d25930ed85ab 100644 --- a/nova/tests/unit/virt/ironic/utils.py +++ b/nova/tests/unit/virt/ironic/utils.py @@ -61,6 +61,24 @@ def get_test_port(**kw): 'updated_at': kw.get('updated_at')})() +def get_test_vif(**kw): + return { + 'profile': kw.get('profile', {}), + 'ovs_interfaceid': kw.get('ovs_interfaceid'), + 'preserve_on_delete': kw.get('preserve_on_delete', False), + 'network': kw.get('network', {}), + 'devname': kw.get('devname', 'tapaaaaaaaa-00'), + 'vnic_type': kw.get('vnic_type', 'baremetal'), + 'qbh_params': kw.get('qbh_params'), + 'meta': kw.get('meta', {}), + 'details': kw.get('details', {}), + 'address': kw.get('address', 'FF:FF:FF:FF:FF:FF'), + 'active': kw.get('active', True), + 'type': kw.get('type', 'ironic'), + 'id': kw.get('id', 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'), + 'qbg_params': kw.get('qbg_params')} + + def get_test_flavor(**kw): default_extra_specs = {'baremetal:deploy_kernel_id': 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py index bd58db77323b..9cbf4d706459 100644 --- a/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py @@ -1012,13 +1012,16 @@ class IronicDriver(virt_driver.ComputeDriver): if len(network_info) > 0: # not needed if no vif are defined - for vif, pif in zip(network_info, ports): - # attach what neutron needs directly to the port - port_id = six.text_type(vif['id']) - patch = [{'op': 'add', - 'path': '/extra/vif_port_id', - 'value': port_id}] - self.ironicclient.call("port.update", pif.uuid, patch) + for vif in network_info: + for pif in ports: + if vif['address'] == pif.address: + # attach what neutron needs directly to the port + port_id = six.text_type(vif['id']) + patch = [{'op': 'add', + 'path': '/extra/vif_port_id', + 'value': port_id}] + self.ironicclient.call("port.update", pif.uuid, patch) + break def _unplug_vifs(self, node, instance, network_info): # NOTE(PhilDay): Accessing network_info will block if the thread @@ -1033,7 +1036,7 @@ class IronicDriver(virt_driver.ComputeDriver): detail=True) # not needed if no vif are defined - for vif, pif in zip(network_info, ports): + for pif in ports: if 'vif_port_id' in pif.extra: # we can not attach a dict directly patch = [{'op': 'remove', 'path': '/extra/vif_port_id'}]