|
28 | 28 |
|
29 | 29 | import nova
|
30 | 30 | from nova import context
|
| 31 | +from nova import exception |
31 | 32 | from nova.network import constants
|
32 | 33 | from nova import objects
|
33 | 34 | from nova.objects import fields
|
@@ -951,6 +952,88 @@ def test_create_server_after_change_in_nonsriov_pf_to_sriov_pf(self):
|
951 | 952 | ],
|
952 | 953 | )
|
953 | 954 |
|
| 955 | + def test_change_bound_port_vnic_type_kills_compute_at_restart(self): |
| 956 | + """Create a server with a direct port and change the vnic_type of the |
| 957 | + bound port to macvtap. Then restart the compute service. |
| 958 | +
|
| 959 | + As the vnic_type is changed on the port but the vif_type is hwveb |
| 960 | + instead of macvtap the vif plug logic will try to look up the netdev |
| 961 | + of the parent VF. Howvere that VF consumed by the instance so the |
| 962 | + netdev does not exists. This causes that the compute service will fail |
| 963 | + with an exception during startup |
| 964 | + """ |
| 965 | + pci_info = fakelibvirt.HostPCIDevicesInfo(num_pfs=1, num_vfs=2) |
| 966 | + self.start_compute(pci_info=pci_info) |
| 967 | + |
| 968 | + # create a direct port |
| 969 | + port = self.neutron.network_4_port_1 |
| 970 | + self.neutron.create_port({'port': port}) |
| 971 | + |
| 972 | + # create a server using the VF via neutron |
| 973 | + server = self._create_server(networks=[{'port': port['id']}]) |
| 974 | + |
| 975 | + # update the vnic_type of the port in neutron |
| 976 | + port = copy.deepcopy(port) |
| 977 | + port['binding:vnic_type'] = 'macvtap' |
| 978 | + self.neutron.update_port(port['id'], {"port": port}) |
| 979 | + |
| 980 | + compute = self.computes['compute1'] |
| 981 | + |
| 982 | + # Force an update on the instance info cache to ensure nova gets the |
| 983 | + # information about the updated port |
| 984 | + with context.target_cell( |
| 985 | + context.get_admin_context(), |
| 986 | + self.host_mappings['compute1'].cell_mapping |
| 987 | + ) as cctxt: |
| 988 | + compute.manager._heal_instance_info_cache(cctxt) |
| 989 | + self.assertIn( |
| 990 | + 'The vnic_type of the bound port %s has been changed in ' |
| 991 | + 'neutron from "direct" to "macvtap". Changing vnic_type of a ' |
| 992 | + 'bound port is not supported by Nova. To avoid breaking the ' |
| 993 | + 'connectivity of the instance please change the port ' |
| 994 | + 'vnic_type back to "direct".' % port['id'], |
| 995 | + self.stdlog.logger.output, |
| 996 | + ) |
| 997 | + |
| 998 | + def fake_get_ifname_by_pci_address(pci_addr: str, pf_interface=False): |
| 999 | + # we want to fail the netdev lookup only if the pci_address is |
| 1000 | + # already consumed by our instance. So we look into the instance |
| 1001 | + # definition to see if the device is attached to the instance as VF |
| 1002 | + conn = compute.manager.driver._host.get_connection() |
| 1003 | + dom = conn.lookupByUUIDString(server['id']) |
| 1004 | + dev = dom._def['devices']['nics'][0] |
| 1005 | + lookup_addr = pci_addr.replace(':', '_').replace('.', '_') |
| 1006 | + if ( |
| 1007 | + dev['type'] == 'hostdev' and |
| 1008 | + dev['source'] == 'pci_' + lookup_addr |
| 1009 | + ): |
| 1010 | + # nova tried to look up the netdev of an already consumed VF. |
| 1011 | + # So we have to fail |
| 1012 | + raise exception.PciDeviceNotFoundById(id=pci_addr) |
| 1013 | + |
| 1014 | + # We need to simulate the actual failure manually as in our functional |
| 1015 | + # environment all the PCI lookup is mocked. In reality nova tries to |
| 1016 | + # look up the netdev of the pci device on the host used by the port as |
| 1017 | + # the parent of the macvtap. However, as the originally direct port is |
| 1018 | + # bound to the instance, the VF pci device is already consumed by the |
| 1019 | + # instance and therefore there is no netdev for the VF. |
| 1020 | + with mock.patch( |
| 1021 | + 'nova.pci.utils.get_ifname_by_pci_address', |
| 1022 | + side_effect=fake_get_ifname_by_pci_address, |
| 1023 | + ): |
| 1024 | + # Nova cannot prevent the vnic_type change on a bound port. Neutron |
| 1025 | + # should prevent that instead. But the nova-compute should still |
| 1026 | + # be able to start up and only log an ERROR for this instance in |
| 1027 | + # inconsistent state. |
| 1028 | + self.restart_compute_service('compute1') |
| 1029 | + |
| 1030 | + self.assertIn( |
| 1031 | + 'Virtual interface plugging failed for instance. Probably the ' |
| 1032 | + 'vnic_type of the bound port has been changed. Nova does not ' |
| 1033 | + 'support such change.', |
| 1034 | + self.stdlog.logger.output, |
| 1035 | + ) |
| 1036 | + |
954 | 1037 |
|
955 | 1038 | class SRIOVAttachDetachTest(_PCIServersTestBase):
|
956 | 1039 | # no need for aliases as these test will request SRIOV via neutron
|
|
0 commit comments