|
40 | 40 | from nova.network import neutron as neutronapi |
41 | 41 | from nova import objects |
42 | 42 | from nova.objects import block_device as block_device_obj |
| 43 | +from nova.policies import base as base_policies |
| 44 | +from nova.policies import servers as servers_policies |
43 | 45 | from nova.scheduler import utils |
44 | 46 | from nova import test |
45 | 47 | from nova.tests import fixtures as nova_fixtures |
@@ -8256,3 +8258,110 @@ def test_rebuild_old_compute(self, old_compute_version): |
8256 | 8258 | 'OS-DCF:diskConfig': 'AUTO'}}) |
8257 | 8259 | self.assertEqual(403, ex.response.status_code) |
8258 | 8260 | self._check_allocations_usage(self.server) |
| 8261 | + |
| 8262 | + |
| 8263 | +class CrossCellResizeWithQoSPort(PortResourceRequestBasedSchedulingTestBase): |
| 8264 | + NUMBER_OF_CELLS = 2 |
| 8265 | + |
| 8266 | + def setUp(self): |
| 8267 | + # Use our custom weigher defined above to make sure that we have |
| 8268 | + # a predictable host order in the alternate list returned by the |
| 8269 | + # scheduler for migration. |
| 8270 | + self.useFixture(nova_fixtures.HostNameWeigherFixture()) |
| 8271 | + super(CrossCellResizeWithQoSPort, self).setUp() |
| 8272 | + # start compute2 in cell2, compute1 is started in cell1 by default |
| 8273 | + self.compute2 = self._start_compute('host2', cell_name='cell2') |
| 8274 | + self.compute2_rp_uuid = self._get_provider_uuid_by_host('host2') |
| 8275 | + self._create_networking_rp_tree('host2', self.compute2_rp_uuid) |
| 8276 | + self.compute2_service_id = self.admin_api.get_services( |
| 8277 | + host='host2', binary='nova-compute')[0]['id'] |
| 8278 | + |
| 8279 | + # Enable cross-cell resize policy since it defaults to not allow |
| 8280 | + # anyone to perform that type of operation. For these tests we'll |
| 8281 | + # just allow admins to perform cross-cell resize. |
| 8282 | + self.policy.set_rules({ |
| 8283 | + servers_policies.CROSS_CELL_RESIZE: |
| 8284 | + base_policies.RULE_ADMIN_API}, |
| 8285 | + overwrite=False) |
| 8286 | + |
| 8287 | + def test_cross_cell_migrate_server_with_qos_ports(self): |
| 8288 | + """Test that cross cell migration is not supported with qos ports and |
| 8289 | + nova therefore falls back to do a same cell migration instead. |
| 8290 | + To test this properly we first make sure that there is no valid host |
| 8291 | + in the same cell but there is valid host in another cell and observe |
| 8292 | + that the migration fails with NoValidHost. Then we start a new compute |
| 8293 | + in the same cell the instance is in and retry the migration that is now |
| 8294 | + expected to pass. |
| 8295 | + """ |
| 8296 | + |
| 8297 | + non_qos_normal_port = self.neutron.port_1 |
| 8298 | + qos_normal_port = self.neutron.port_with_resource_request |
| 8299 | + qos_sriov_port = self.neutron.port_with_sriov_resource_request |
| 8300 | + |
| 8301 | + server = self._create_server_with_ports_and_check_allocation( |
| 8302 | + non_qos_normal_port, qos_normal_port, qos_sriov_port) |
| 8303 | + |
| 8304 | + orig_create_binding = neutronapi.API._create_port_binding |
| 8305 | + |
| 8306 | + hosts = { |
| 8307 | + 'host1': self.compute1_rp_uuid, 'host2': self.compute2_rp_uuid} |
| 8308 | + |
| 8309 | + # Add an extra check to our neutron fixture. This check makes sure that |
| 8310 | + # the RP sent in the binding corresponds to host of the binding. In a |
| 8311 | + # real deployment this is checked by the Neutron server. As bug |
| 8312 | + # 1907522 showed we fail this check for cross cell migration with qos |
| 8313 | + # ports in a real deployment. So to reproduce that bug we need to have |
| 8314 | + # the same check in our test env too. |
| 8315 | + def spy_on_create_binding(context, client, port_id, data): |
| 8316 | + host_rp_uuid = hosts[data['binding']['host']] |
| 8317 | + device_rp_uuid = data['binding']['profile'].get('allocation') |
| 8318 | + if port_id == qos_normal_port['id']: |
| 8319 | + if device_rp_uuid != self.ovs_bridge_rp_per_host[host_rp_uuid]: |
| 8320 | + raise exception.PortBindingFailed(port_id=port_id) |
| 8321 | + elif port_id == qos_sriov_port['id']: |
| 8322 | + if (device_rp_uuid not in |
| 8323 | + self.sriov_dev_rp_per_host[host_rp_uuid].values()): |
| 8324 | + raise exception.PortBindingFailed(port_id=port_id) |
| 8325 | + |
| 8326 | + return orig_create_binding(context, client, port_id, data) |
| 8327 | + |
| 8328 | + with mock.patch( |
| 8329 | + 'nova.network.neutron.API._create_port_binding', |
| 8330 | + side_effect=spy_on_create_binding, autospec=True |
| 8331 | + ): |
| 8332 | + # We expect the migration to fail as the only available target |
| 8333 | + # host is in a different cell and while cross cell migration is |
| 8334 | + # enabled it is not supported for neutron ports with resource |
| 8335 | + # request. |
| 8336 | + # FIXME(gibi): We expect this to fail with NoValidHost. |
| 8337 | + # Unfortunately it fails by not finding the target compute service |
| 8338 | + # in the same cell the source service. This is bug 1907511. If |
| 8339 | + # there would be a standalone fix for 1907511 then the next failure |
| 8340 | + # would be 1907522. Our coming fix will fix both bug with a same |
| 8341 | + # fix. |
| 8342 | + self.api.post_server_action(server['id'], {'migrate': None}) |
| 8343 | + self._wait_for_migration_status(server, ['error']) |
| 8344 | + self._wait_for_action_fail_completion( |
| 8345 | + server, 'migrate', 'conductor_migrate_server') |
| 8346 | + # This is the root case |
| 8347 | + self.assertIn( |
| 8348 | + "AttributeError: 'NoneType' object has no attribute 'version'", |
| 8349 | + self.stdlog.logger.output) |
| 8350 | + |
| 8351 | + # Now start a new compute in the same cell as the instance and retry |
| 8352 | + # the migration. |
| 8353 | + # |
| 8354 | + # This should work after the fallback to same cell resize is |
| 8355 | + # implemented |
| 8356 | + # |
| 8357 | + # self._start_compute('host3', cell_name='cell1') |
| 8358 | + # |
| 8359 | + # with mock.patch( |
| 8360 | + # 'nova.network.neutron.API._create_port_binding', |
| 8361 | + # side_effect=spy_on_create_binding, autospec=True |
| 8362 | + # ): |
| 8363 | + # server = self._migrate_server(server) |
| 8364 | + # self.assertEqual('host3', server['OS-EXT-SRV-ATTR:host']) |
| 8365 | + |
| 8366 | + self._delete_server_and_check_allocations( |
| 8367 | + server, qos_normal_port, qos_sriov_port) |
0 commit comments