Skip to content

Commit 7366e3c

Browse files
author
Balazs Gibizer
committed
Reproduce bug 1907522 in functional test
Cross cell resize does not support neutron ports with resource request as nova fails to send a proper port binding to neutron. This causes the migration to fail. However we should simply not allow the migration to go cross cell if the server has such ports. This patch adds a functional test to reproduce the problem On the stable branches Ie15ec8299ae52ae8f5334d591ed3944e9585cf71 was never done and therefore bug 1907511 visible first during the reproduction (this bug does not exists on master due to the above linked change). But if 1907511 has fixed then 1907522 would be visible. Our incoming bugfix solves fixes both bug at the same time. This means that the backport is not clean, the reproduction test changed to assert bug 1907511 instead of bug 1907522 Change-Id: Id91d2e817ef6bd21124bb840bdb098054e9753b8 Related-Bug: #1907522 Related-Bug: #1907511 (cherry picked from commit f96ade2)
1 parent c70d974 commit 7366e3c

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

nova/compute/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3879,6 +3879,8 @@ def _allow_cross_cell_resize(context, instance):
38793879
:param instance: Instance object being resized
38803880
:returns: True if cross-cell resize is allowed, False otherwise
38813881
"""
3882+
# TODO(gibi): do not allow cross cell migration if the instance has
3883+
# neutron ports with resource request. See bug 1907522.
38823884
# First check to see if the requesting project/user is allowed by
38833885
# policy to perform cross-cell resize.
38843886
allowed = context.can(

nova/tests/functional/test_servers.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
from nova.network import neutron as neutronapi
4141
from nova import objects
4242
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
4345
from nova.scheduler import utils
4446
from nova import test
4547
from nova.tests import fixtures as nova_fixtures
@@ -8256,3 +8258,110 @@ def test_rebuild_old_compute(self, old_compute_version):
82568258
'OS-DCF:diskConfig': 'AUTO'}})
82578259
self.assertEqual(403, ex.response.status_code)
82588260
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

Comments
 (0)