Skip to content

Commit 5bb6d4c

Browse files
stephenfinBalazs Gibizer
authored andcommitted
functional: Add reproducer for #1907775
You can currently remove a host that has instances scheduled to it from an aggregate. If the aggregate is configured as part of an availability zone (AZ), this would in turn remove the host from the AZ, leaving instances originally scheduled to that AZ stranded on a host that is no longer a member of the AZ. This is clearly undesirable and should be blocked at the API level. You can also add a host to an aggregate where it wasn't in one before. Because nova provides a default AZ for hosts that don't belong to an aggregate, adding a host to an aggregate doesn't just assign it to an AZ, it removes it from the default 'nova' one (or whatever you've configured via '[DEFAULT] default_availability_zone'). As noted in the docs [1], people should not rely on scheduling to the default AZ, but if they had, we'd end up in the same situation as above. Add tests for both, with a fix coming after. [1] https://docs.openstack.org/nova/latest/admin/availability-zones.html Change-Id: I21f7f93ee0ec0cd3a290afba59342b31d074cf2f Signed-off-by: Stephen Finucane <[email protected]> Related-Bug: #1907775
1 parent 755aa11 commit 5bb6d4c

File tree

1 file changed

+115
-6
lines changed

1 file changed

+115
-6
lines changed

nova/tests/functional/test_aggregates.py

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,24 @@ def _add_host_to_aggregate(self, agg, host):
197197
agg = self.aggregates[agg]
198198
self.admin_api.add_host_to_aggregate(agg['id'], host)
199199

200-
def _boot_server(self, az=None, flavor_id=None, image_id=None,
201-
end_status='ACTIVE'):
200+
def _remove_host_from_aggregate(self, agg, host):
201+
"""Remove a compute host from both nova and placement aggregates.
202+
203+
:param agg: Name of the nova aggregate
204+
:param host: Name of the compute host
205+
"""
206+
agg = self.aggregates[agg]
207+
self.admin_api.remove_host_from_aggregate(agg['id'], host)
208+
209+
def _boot_server(
210+
self, az=None, host=None, flavor_id=None, image_id=None,
211+
end_status='ACTIVE',
212+
):
202213
flavor_id = flavor_id or self.flavors[0]['id']
203214
image_uuid = image_id or '155d900f-4e14-4e4c-a73d-069cbf4541e6'
204215
server_req = self._build_server(
205-
image_uuid=image_uuid,
206-
flavor_id=flavor_id,
207-
networks='none', az=az)
216+
image_uuid=image_uuid, flavor_id=flavor_id,
217+
networks='none', az=az, host=host)
208218

209219
created_server = self.api.post_server({'server': server_req})
210220
server = self._wait_for_state_change(created_server, end_status)
@@ -284,7 +294,7 @@ def _set_traits_on_aggregate(self, agg, traits):
284294

285295
class AggregatePostTest(AggregateRequestFiltersTest):
286296

287-
def test_set_az_for_aggreate_no_instances(self):
297+
def test_set_az_for_aggregate_no_instances(self):
288298
"""Should be possible to update AZ for an empty aggregate.
289299
290300
Check you can change the AZ name of an aggregate when it does
@@ -343,6 +353,105 @@ def test_cannot_delete_az(self):
343353
self.assertEqual(az, agg['availability_zone'])
344354

345355

356+
class AggregateHostMoveTestCase(AggregateRequestFiltersTest):
357+
358+
def setUp(self):
359+
super().setUp()
360+
# keep it separate from the parent class' setup
361+
self.host = 'host3'
362+
az = 'custom-az'
363+
self._start_compute(self.host)
364+
self._create_aggregate('ag1-no-az')
365+
self._create_aggregate('ag2-no-az')
366+
self._create_aggregate('ag3-custom-az')
367+
self._set_az_aggregate('ag3-custom-az', az)
368+
self._create_aggregate('ag4-custom-az')
369+
self._set_az_aggregate('ag4-custom-az', az)
370+
371+
def test_add_host_with_instances_default_az_doesnt_change(self):
372+
# server will be in default AZ
373+
self._boot_server(host=self.host)
374+
375+
# the AZ of the server does not change as the aggregate is also in
376+
# the default AZ.
377+
self._add_host_to_aggregate('ag1-no-az', self.host)
378+
379+
def test_add_host_with_instances_custom_az_doesnt_change(self):
380+
self._add_host_to_aggregate('ag3-custom-az', self.host)
381+
# server will be in custom AZ
382+
self._boot_server(host=self.host)
383+
384+
# as the new aggregate also in the custom-az this does not need the
385+
# server to move between AZs, so this is OK.
386+
self._add_host_to_aggregate('ag4-custom-az', self.host)
387+
388+
def test_cannot_add_host_with_instances_default_az_then_custom_az(self):
389+
# server will be in default AZ
390+
self._boot_server(host=self.host)
391+
392+
# FIXME(stephenfin): This is bug #1907775, where we should reject the
393+
# request to add a host to an aggregate when and instance would need
394+
# to move between AZs
395+
396+
# The server would need to move from default AZ to custom AZ, that
397+
# is not OK
398+
self._add_host_to_aggregate('ag3-custom-az', self.host)
399+
400+
def test_add_host_with_instances_custom_az_then_default(self):
401+
self._add_host_to_aggregate('ag3-custom-az', self.host)
402+
# server will be in custom AZ
403+
self._boot_server(host=self.host)
404+
405+
# The server is still in the custom AZ and that is OK as the host is
406+
# also in that AZ even after added to an aggregate without AZ.
407+
self._add_host_to_aggregate('ag1-no-az', self.host)
408+
409+
def test_remove_host_with_instances_default_az_doesnt_change(self):
410+
self._add_host_to_aggregate('ag1-no-az', self.host)
411+
self._add_host_to_aggregate('ag2-no-az', self.host)
412+
# server will be in default AZ
413+
self._boot_server(host=self.host)
414+
415+
# The server still remains in default AZ so no AZ change, this is OK
416+
self._remove_host_from_aggregate('ag1-no-az', self.host)
417+
# still OK as the host not in any aggregate means instance is in
418+
# default AZ.
419+
self._remove_host_from_aggregate('ag2-no-az', self.host)
420+
421+
def test_remove_host_with_instances_custom_az_doesnt_change(self):
422+
self._add_host_to_aggregate('ag3-custom-az', self.host)
423+
self._add_host_to_aggregate('ag4-custom-az', self.host)
424+
# server will be in custom AZ
425+
self._boot_server(host=self.host)
426+
427+
# The server still remains in custom AZ so no AZ change, it is OK.
428+
self._remove_host_from_aggregate('ag3-custom-az', self.host)
429+
430+
def test_cannot_remove_host_with_instances_custom_az_to_default(self):
431+
self._add_host_to_aggregate('ag3-custom-az', self.host)
432+
# server will be in custom AZ
433+
self._boot_server(host=self.host)
434+
435+
# FIXME(stephenfin): This is bug #1907775, where we should reject the
436+
# request to remove a host from the aggregate when there are instances
437+
# on said host
438+
439+
# The server would need to move to the default AZ, that is not OK.
440+
self._remove_host_from_aggregate('ag3-custom-az', self.host)
441+
442+
def test_moving_host_around_az_without_instances(self):
443+
# host moving from default to custom AZ
444+
self._add_host_to_aggregate('ag3-custom-az', self.host)
445+
# host still in custom AZ
446+
self._add_host_to_aggregate('ag1-no-az', self.host)
447+
# host moves from custom to default AZ
448+
self._remove_host_from_aggregate('ag3-custom-az', self.host)
449+
# host still in default AZ
450+
self._remove_host_from_aggregate('ag1-no-az', self.host)
451+
# host still in default AZ
452+
self._add_host_to_aggregate('ag1-no-az', self.host)
453+
454+
346455
# NOTE: this test case has the same test methods as AggregatePostTest
347456
# but for the AZ update it uses PUT /os-aggregates/{aggregate_id} method
348457
class AggregatePutTest(AggregatePostTest):

0 commit comments

Comments
 (0)