Enforce case-sensitive hostnames in aggregate host add

If we are using a case-insensitive backend database like mysql, a
user can request an aggregate host add with a non-matching hostname
and we will not signal failure. We will create a mapping which will
not actually include that host in the aggregate, which will be
confusing later. This change makes us fail if the host name does not
match exactly, which is the same behavior as if we are using a
case-sensitive backend (like postgres).

Change-Id: I597dee74d33de337913eddda74ab056fbf81a23c
Closes-Bug: #1709260
(cherry picked from commit c8e2de6684)
(cherry picked from commit c72bb2c747)
(cherry picked from commit 22197f3d01)
This commit is contained in:
Dan Smith 2018-09-24 13:36:39 -07:00 committed by Matt Riedemann
parent c5cae2f634
commit 0bd38dbadc
2 changed files with 34 additions and 3 deletions

View File

@ -4896,14 +4896,23 @@ class AggregateAPI(base.Base):
try:
mapping = objects.HostMapping.get_by_host(context, host_name)
nova_context.set_target_cell(context, mapping.cell_mapping)
objects.Service.get_by_compute_host(context, host_name)
service = objects.Service.get_by_compute_host(context, host_name)
except exception.HostMappingNotFound:
try:
# NOTE(danms): This targets our cell
_find_service_in_cell(context, service_host=host_name)
service = _find_service_in_cell(context,
service_host=host_name)
except exception.NotFound:
raise exception.ComputeHostNotFound(host=host_name)
if service.host != host_name:
# NOTE(danms): If we found a service but it is not an
# exact match, we may have a case-insensitive backend
# database (like mysql) which will end up with us
# adding the host-aggregate mapping with a
# non-matching hostname.
raise exception.ComputeHostNotFound(host=host_name)
aggregate = objects.Aggregate.get_by_id(context, aggregate_id)
self.is_safe_to_update_az(context, aggregate.metadata,
hosts=[host_name], aggregate=aggregate)

View File

@ -11507,6 +11507,26 @@ class ComputeAPIAggrTestCase(BaseTestCase):
self.assertEqual(fake_notifier.NOTIFICATIONS[1].publisher_id,
'compute.fake-mini')
@mock.patch('nova.objects.Service.get_by_compute_host')
def test_add_host_to_aggregate_raise_not_found_case(self,
mock_get_service):
# Ensure ComputeHostNotFound is raised when adding a host with a
# hostname that doesn't exactly map what we have stored.
def return_anyway(context, host_name):
return objects.Service(host=host_name.upper())
mock_get_service.side_effect = return_anyway
aggr = self.api.create_aggregate(self.context, 'fake_aggregate',
'fake_zone')
fake_notifier.NOTIFICATIONS = []
values = _create_service_entries(self.context)
fake_host = values[0][1][0]
self.assertRaises(exception.ComputeHostNotFound,
self.api.add_host_to_aggregate,
self.context, aggr.id, fake_host)
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.context.set_target_cell')
def test_add_host_to_aggregate_raise_cn_not_found(self, mock_st,
@ -11674,7 +11694,9 @@ class ComputeAPIAggrCallsSchedulerTestCase(test.NoDBTestCase):
agg = objects.Aggregate(name='fake', metadata={})
agg.add_host = mock.Mock()
with test.nested(
mock.patch.object(objects.Service, 'get_by_compute_host'),
mock.patch.object(objects.Service, 'get_by_compute_host',
return_value=objects.Service(
host='fakehost')),
mock.patch.object(objects.Aggregate, 'get_by_id',
return_value=agg)):
self.api.add_host_to_aggregate(self.context, 1, 'fakehost')