Skip to content

Commit c6d6907

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Delete resource provider in tree by top-down traversable order"
2 parents f644da1 + a45daaf commit c6d6907

File tree

3 files changed

+124
-16
lines changed

3 files changed

+124
-16
lines changed

nova/scheduler/client/report.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,13 +2153,21 @@ def delete_resource_provider(self, context, compute_node, cascade=False):
21532153
context, host, nodename)
21542154
for instance_uuid in instance_uuids:
21552155
self.delete_allocation_for_instance(context, instance_uuid)
2156-
try:
2157-
self._delete_provider(rp_uuid, global_request_id=context.global_id)
2158-
except (exception.ResourceProviderInUse,
2159-
exception.ResourceProviderDeletionFailed):
2160-
# TODO(efried): Raise these. Right now this is being left a no-op
2161-
# for backward compatibility.
2162-
pass
2156+
# Ensure to delete resource provider in tree by top-down
2157+
# traversable order.
2158+
rps_to_refresh = self.get_providers_in_tree(context, rp_uuid)
2159+
self._provider_tree.populate_from_iterable(rps_to_refresh)
2160+
provider_uuids = self._provider_tree.get_provider_uuids_in_tree(
2161+
rp_uuid)
2162+
for provider_uuid in provider_uuids[::-1]:
2163+
try:
2164+
self._delete_provider(provider_uuid,
2165+
global_request_id=context.global_id)
2166+
except (exception.ResourceProviderInUse,
2167+
exception.ResourceProviderDeletionFailed):
2168+
# TODO(efried): Raise these. Right now this is being
2169+
# left a no-op for backward compatibility.
2170+
pass
21632171

21642172
def get_provider_by_name(self, context, name):
21652173
"""Queries the placement API for resource provider information matching

nova/tests/functional/api_sample_tests/test_services.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,17 @@ def fake_hm_get_by_host(context, host):
143143
def fake_hm_destroy(context, host):
144144
return 1
145145

146+
def fake_get_providers_in_tree(self, context, compute_node_id):
147+
return [{
148+
'uuid': compute_node_id,
149+
'name': 'test',
150+
'generation': 1,
151+
'parent_provider_uuid': None
152+
}]
153+
154+
def fake_get_provider_uuids_in_tree(context, compute_node_id):
155+
return [compute_node_id]
156+
146157
self.stub_out('nova.db.api.service_get_by_uuid',
147158
db_service_get_by_uuid)
148159
self.stub_out('nova.db.api.compute_node_get_all_by_host',
@@ -152,6 +163,14 @@ def fake_hm_destroy(context, host):
152163
fake_hm_get_by_host)
153164
self.stub_out('nova.objects.host_mapping.HostMapping._destroy_in_db',
154165
fake_hm_destroy)
166+
self.stub_out(
167+
'nova.scheduler.client.report.SchedulerReportClient.'
168+
'get_providers_in_tree',
169+
fake_get_providers_in_tree)
170+
self.stub_out(
171+
'nova.compute.provider_tree.ProviderTree.'
172+
'get_provider_uuids_in_tree',
173+
fake_get_provider_uuids_in_tree)
155174

156175
def test_service_enable(self):
157176
"""Enable an existing service."""

nova/tests/unit/scheduler/client/test_report.py

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3146,17 +3146,24 @@ def test_refresh_associations_disabled(self):
31463146

31473147
class TestAllocations(SchedulerReportClientTestCase):
31483148

3149+
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3150+
'get_providers_in_tree')
31493151
@mock.patch("nova.scheduler.client.report.SchedulerReportClient."
31503152
"delete")
31513153
@mock.patch("nova.scheduler.client.report.SchedulerReportClient."
31523154
"delete_allocation_for_instance")
31533155
@mock.patch("nova.objects.InstanceList.get_uuids_by_host_and_node")
31543156
def test_delete_resource_provider_cascade(self, mock_by_host,
3155-
mock_del_alloc, mock_delete):
3156-
self.client._provider_tree.new_root(uuids.cn, uuids.cn, generation=1)
3157+
mock_del_alloc, mock_delete, mock_get_rpt):
31573158
cn = objects.ComputeNode(uuid=uuids.cn, host="fake_host",
31583159
hypervisor_hostname="fake_hostname", )
31593160
mock_by_host.return_value = [uuids.inst1, uuids.inst2]
3161+
mock_get_rpt.return_value = [{
3162+
'uuid': cn.uuid,
3163+
'name': mock.sentinel.name,
3164+
'generation': 1,
3165+
'parent_provider_uuid': None
3166+
}]
31603167
resp_mock = mock.Mock(status_code=204)
31613168
mock_delete.return_value = resp_mock
31623169
self.client.delete_resource_provider(self.context, cn, cascade=True)
@@ -3168,17 +3175,24 @@ def test_delete_resource_provider_cascade(self, mock_by_host,
31683175
exp_url, global_request_id=self.context.global_id)
31693176
self.assertFalse(self.client._provider_tree.exists(uuids.cn))
31703177

3178+
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3179+
'get_providers_in_tree')
31713180
@mock.patch("nova.scheduler.client.report.SchedulerReportClient."
31723181
"delete")
31733182
@mock.patch("nova.scheduler.client.report.SchedulerReportClient."
31743183
"delete_allocation_for_instance")
31753184
@mock.patch("nova.objects.InstanceList.get_uuids_by_host_and_node")
31763185
def test_delete_resource_provider_no_cascade(self, mock_by_host,
3177-
mock_del_alloc, mock_delete):
3178-
self.client._provider_tree.new_root(uuids.cn, uuids.cn, generation=1)
3186+
mock_del_alloc, mock_delete, mock_get_rpt):
31793187
self.client._association_refresh_time[uuids.cn] = mock.Mock()
31803188
cn = objects.ComputeNode(uuid=uuids.cn, host="fake_host",
31813189
hypervisor_hostname="fake_hostname", )
3190+
mock_get_rpt.return_value = [{
3191+
'uuid': cn.uuid,
3192+
'name': mock.sentinel.name,
3193+
'generation': 1,
3194+
'parent_provider_uuid': None
3195+
}]
31823196
mock_by_host.return_value = [uuids.inst1, uuids.inst2]
31833197
resp_mock = mock.Mock(status_code=204)
31843198
mock_delete.return_value = resp_mock
@@ -3189,14 +3203,21 @@ def test_delete_resource_provider_no_cascade(self, mock_by_host,
31893203
exp_url, global_request_id=self.context.global_id)
31903204
self.assertNotIn(uuids.cn, self.client._association_refresh_time)
31913205

3206+
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3207+
'get_providers_in_tree')
31923208
@mock.patch("nova.scheduler.client.report.SchedulerReportClient."
31933209
"delete")
31943210
@mock.patch('nova.scheduler.client.report.LOG')
3195-
def test_delete_resource_provider_log_calls(self, mock_log, mock_delete):
3196-
# First, check a successful call
3197-
self.client._provider_tree.new_root(uuids.cn, uuids.cn, generation=1)
3211+
def test_delete_resource_provider_log_calls(self, mock_log, mock_delete,
3212+
get_rpt_mock):
31983213
cn = objects.ComputeNode(uuid=uuids.cn, host="fake_host",
31993214
hypervisor_hostname="fake_hostname", )
3215+
get_rpt_mock.return_value = [{
3216+
'uuid': cn.uuid,
3217+
'name': mock.sentinel.name,
3218+
'generation': 1,
3219+
'parent_provider_uuid': None
3220+
}]
32003221
resp_mock = fake_requests.FakeResponse(204)
32013222
mock_delete.return_value = resp_mock
32023223
self.client.delete_resource_provider(self.context, cn)
@@ -3220,15 +3241,75 @@ def test_delete_resource_provider_log_calls(self, mock_log, mock_delete):
32203241
self.assertEqual(0, mock_log.info.call_count)
32213242
self.assertEqual(1, mock_log.error.call_count)
32223243

3244+
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3245+
'get_providers_in_tree')
3246+
@mock.patch("nova.scheduler.client.report.SchedulerReportClient."
3247+
"delete")
3248+
@mock.patch('nova.scheduler.client.report.LOG')
3249+
def test_delete_resource_providers_by_order(self, mock_log, mock_delete,
3250+
mock_get_rpt):
3251+
"""Ensure that more than on RP is in the tree and that all of them
3252+
is gets deleted in the proper order.
3253+
"""
3254+
cn = objects.ComputeNode(uuid=uuids.cn, host="fake_host",
3255+
hypervisor_hostname="fake_hostname", )
3256+
mock_get_rpt.return_value = [
3257+
{
3258+
'uuid': uuids.child1,
3259+
'name': mock.sentinel.name,
3260+
'generation': 1,
3261+
'parent_provider_uuid': cn.uuid
3262+
},
3263+
{
3264+
'uuid': uuids.gc1_1,
3265+
'name': mock.sentinel.name,
3266+
'generation': 1,
3267+
'parent_provider_uuid': uuids.child1
3268+
},
3269+
{
3270+
'uuid': cn.uuid,
3271+
'name': mock.sentinel.name,
3272+
'generation': 1,
3273+
'parent_provider_uuid': None
3274+
}
3275+
]
3276+
mock_delete.return_value = True
3277+
self.client.delete_resource_provider(self.context, cn)
3278+
self.assertEqual(3, mock_delete.call_count)
3279+
# Delete RP in correct order
3280+
mock_delete.assert_has_calls([
3281+
mock.call('/resource_providers/%s' % uuids.gc1_1,
3282+
global_request_id=mock.ANY),
3283+
mock.call('/resource_providers/%s' % uuids.child1,
3284+
global_request_id=mock.ANY),
3285+
mock.call('/resource_providers/%s' % cn.uuid,
3286+
global_request_id=mock.ANY),
3287+
])
3288+
exp_url = "Deleted resource provider %s"
3289+
# Logging info in correct order: uuids.gc1_1, uuids.child1, cn.uuid
3290+
mock_log.assert_has_calls([
3291+
mock.call.info(exp_url, uuids.gc1_1),
3292+
mock.call.info(exp_url, uuids.child1),
3293+
mock.call.info(exp_url, cn.uuid),
3294+
])
3295+
3296+
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
3297+
'get_providers_in_tree')
32233298
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.delete',
32243299
new=mock.Mock(side_effect=ks_exc.EndpointNotFound()))
3225-
def test_delete_resource_provider_placement_exception(self):
3300+
def test_delete_resource_provider_placement_exception(self,
3301+
mock_get_rpt):
32263302
"""Ensure that a ksa exception in delete_resource_provider raises
32273303
through.
32283304
"""
3229-
self.client._provider_tree.new_root(uuids.cn, uuids.cn, generation=1)
32303305
cn = objects.ComputeNode(uuid=uuids.cn, host="fake_host",
32313306
hypervisor_hostname="fake_hostname", )
3307+
mock_get_rpt.return_value = [{
3308+
'uuid': cn.uuid,
3309+
'name': mock.sentinel.name,
3310+
'generation': 1,
3311+
'parent_provider_uuid': None
3312+
}]
32323313
self.assertRaises(
32333314
ks_exc.ClientException,
32343315
self.client.delete_resource_provider, self.context, cn)

0 commit comments

Comments
 (0)