Merge "Raise on API errors getting aggregates/traits"

This commit is contained in:
Zuul 2018-01-23 16:00:26 +00:00 committed by Gerrit Code Review
commit 27eadbc499
3 changed files with 99 additions and 134 deletions

View File

@ -2129,6 +2129,16 @@ class ResourceProviderRetrievalFailed(NovaException):
msg_fmt = _("Failed to get resource provider with UUID %(uuid)s")
class ResourceProviderAggregateRetrievalFailed(NovaException):
msg_fmt = _("Failed to get aggregates for resource provider with UUID"
" %(uuid)s")
class ResourceProviderTraitRetrievalFailed(NovaException):
msg_fmt = _("Failed to get traits for resource provider with UUID"
" %(uuid)s")
class ResourceProviderCreationFailed(NovaException):
msg_fmt = _("Failed to create resource provider %(name)s")

View File

@ -365,10 +365,14 @@ class SchedulerReportClient(object):
@safe_connect
def _get_provider_aggregates(self, rp_uuid):
"""Queries the placement API for a resource provider's aggregates.
Returns a set() of aggregate UUIDs or None if no such resource provider
was found or there was an error communicating with the placement API.
:param rp_uuid: UUID of the resource provider to grab aggregates for.
:return: A set() of aggregate UUIDs, which may be empty if the
specified provider has no aggregate associations.
:raise: ResourceProviderAggregateRetrievalFailed on errors. In
particular, we raise this exception (as opposed to returning
None or the empty set()) if the specified resource provider
does not exist.
"""
resp = self.get("/resource_providers/%s/aggregates" % rp_uuid,
version='1.1')
@ -377,33 +381,28 @@ class SchedulerReportClient(object):
return set(data['aggregates'])
placement_req_id = get_placement_request_id(resp)
if resp.status_code == 404:
msg = ("[%(placement_req_id)s] Tried to get a provider's "
"aggregates; however the provider %(uuid)s does not exist.")
args = {
'uuid': rp_uuid,
'placement_req_id': placement_req_id,
}
LOG.warning(msg, args)
else:
msg = ("[%(placement_req_id)s] Failed to retrieve aggregates from "
"placement API for resource provider with UUID %(uuid)s. "
"Got %(status_code)d: %(err_text)s.")
args = {
'placement_req_id': placement_req_id,
'uuid': rp_uuid,
'status_code': resp.status_code,
'err_text': resp.text,
}
LOG.error(msg, args)
msg = ("[%(placement_req_id)s] Failed to retrieve aggregates from "
"placement API for resource provider with UUID %(uuid)s. "
"Got %(status_code)d: %(err_text)s.")
args = {
'placement_req_id': placement_req_id,
'uuid': rp_uuid,
'status_code': resp.status_code,
'err_text': resp.text,
}
LOG.error(msg, args)
raise exception.ResourceProviderAggregateRetrievalFailed(uuid=rp_uuid)
@safe_connect
def _get_provider_traits(self, rp_uuid):
"""Queries the placement API for a resource provider's traits. Returns
a set() of string trait names, or None if no such resource provider was
found or there was an error communicating with the placement API.
"""Queries the placement API for a resource provider's traits.
:param rp_uuid: UUID of the resource provider to grab traits for.
:return: A set() of string trait names, which may be empty if the
specified provider has no traits.
:raise: ResourceProviderTraitRetrievalFailed on errors. In particular,
we raise this exception (as opposed to returning None or the
empty set()) if the specified resource provider does not exist.
"""
resp = self.get("/resource_providers/%s/traits" % rp_uuid,
version='1.6')
@ -412,19 +411,13 @@ class SchedulerReportClient(object):
return set(resp.json()['traits'])
placement_req_id = get_placement_request_id(resp)
if resp.status_code == 404:
LOG.warning(
"[%(placement_req_id)s] Tried to get a provider's traits, but "
"the provider %(uuid)s does not exist.",
{'uuid': rp_uuid, 'placement_req_id': placement_req_id})
else:
LOG.error(
"[%(placement_req_id)s] Failed to retrieve traits from "
"placement API for resource provider with UUID %(uuid)s. Got "
"%(status_code)d: %(err_text)s.",
{'placement_req_id': placement_req_id, 'uuid': rp_uuid,
'status_code': resp.status_code, 'err_text': resp.text})
return None
LOG.error(
"[%(placement_req_id)s] Failed to retrieve traits from "
"placement API for resource provider with UUID %(uuid)s. Got "
"%(status_code)d: %(err_text)s.",
{'placement_req_id': placement_req_id, 'uuid': rp_uuid,
'status_code': resp.status_code, 'err_text': resp.text})
raise exception.ResourceProviderTraitRetrievalFailed(uuid=rp_uuid)
@safe_connect
def _get_resource_provider(self, uuid):
@ -688,30 +681,32 @@ class SchedulerReportClient(object):
by aggregate with the specified provider,
including their traits and aggregates (but not
*their* sharing providers).
:raise: On various placement API errors, one of:
- ResourceProviderAggregateRetrievalFailed
- ResourceProviderTraitRetrievalFailed
- ResourceProviderRetrievalFailed
"""
if force or self._associations_stale(rp_uuid):
# Refresh aggregates
aggs = self._get_provider_aggregates(rp_uuid)
if aggs is not None:
msg = ("Refreshing aggregate associations for resource "
"provider %s, aggregates: %s")
LOG.debug(msg, rp_uuid, ','.join(aggs or ['None']))
msg = ("Refreshing aggregate associations for resource provider "
"%s, aggregates: %s")
LOG.debug(msg, rp_uuid, ','.join(aggs or ['None']))
# NOTE(efried): This will blow up if called for a RP that
# doesn't exist in our _provider_tree.
self._provider_tree.update_aggregates(
rp_uuid, aggs, generation=generation)
# NOTE(efried): This will blow up if called for a RP that doesn't
# exist in our _provider_tree.
self._provider_tree.update_aggregates(
rp_uuid, aggs, generation=generation)
# Refresh traits
traits = self._get_provider_traits(rp_uuid)
if traits is not None:
msg = ("Refreshing trait associations for resource "
"provider %s, traits: %s")
LOG.debug(msg, rp_uuid, ','.join(traits or ['None']))
# NOTE(efried): This will blow up if called for a RP that
# doesn't exist in our _provider_tree.
self._provider_tree.update_traits(
rp_uuid, traits, generation=generation)
msg = ("Refreshing trait associations for resource provider %s, "
"traits: %s")
LOG.debug(msg, rp_uuid, ','.join(traits or ['None']))
# NOTE(efried): This will blow up if called for a RP that doesn't
# exist in our _provider_tree.
self._provider_tree.update_traits(
rp_uuid, traits, generation=generation)
if refresh_sharing:
# Refresh providers associated by aggregate

View File

@ -1806,69 +1806,44 @@ class TestAggregates(SchedulerReportClientTestCase):
def test_get_provider_aggregates_found(self):
uuid = uuids.compute_node
resp_mock = mock.Mock(status_code=200)
json_data = {
'aggregates': [
uuids.agg1,
uuids.agg2,
],
}
resp_mock.json.return_value = json_data
self.ks_adap_mock.get.return_value = resp_mock
result = self.client._get_provider_aggregates(uuid)
expected = set([
aggs = [
uuids.agg1,
uuids.agg2,
])
expected_url = '/resource_providers/' + uuid + '/aggregates'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, raise_exc=False, microversion='1.1')
self.assertEqual(expected, result)
@mock.patch.object(report.LOG, 'warning')
def test_get_provider_aggregates_not_found(self, log_mock):
"""Test that when the placement API returns a 404 when looking up a
provider's aggregates, that we simply return None and log a warning
(since _get_provider_aggregates() should be called after
_ensure_resource_provider()).
"""
uuid = uuids.compute_node
resp_mock = mock.Mock(status_code=404)
]
resp_mock.json.return_value = {'aggregates': aggs}
self.ks_adap_mock.get.return_value = resp_mock
self.ks_adap_mock.get.return_value.headers = {
'x-openstack-request-id': uuids.request_id}
result = self.client._get_provider_aggregates(uuid)
expected_url = '/resource_providers/' + uuid + '/aggregates'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, raise_exc=False, microversion='1.1')
self.assertTrue(log_mock.called)
self.assertEqual(uuids.request_id,
log_mock.call_args[0][1]['placement_req_id'])
self.assertIsNone(result)
self.assertEqual(set(aggs), result)
@mock.patch.object(report.LOG, 'error')
def test_get_provider_aggregates_bad_request(self, log_mock):
"""Test that when the placement API returns a 400 when looking up a
provider's aggregates, that we simply return None and log an error.
def test_get_provider_aggregates_error(self, log_mock):
"""Test that when the placement API returns any error when looking up a
provider's aggregates, we raise an exception.
"""
uuid = uuids.compute_node
resp_mock = mock.Mock(status_code=400)
resp_mock = mock.Mock(headers={
'x-openstack-request-id': uuids.request_id})
self.ks_adap_mock.get.return_value = resp_mock
self.ks_adap_mock.get.return_value.headers = {
'x-openstack-request-id': uuids.request_id}
result = self.client._get_provider_aggregates(uuid)
for status_code in (400, 404, 503):
resp_mock.status_code = status_code
self.assertRaises(
exception.ResourceProviderAggregateRetrievalFailed,
self.client._get_provider_aggregates, uuid)
expected_url = '/resource_providers/' + uuid + '/aggregates'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, raise_exc=False, microversion='1.1')
self.assertTrue(log_mock.called)
self.assertEqual(uuids.request_id,
log_mock.call_args[0][1]['placement_req_id'])
self.assertIsNone(result)
expected_url = '/resource_providers/' + uuid + '/aggregates'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, raise_exc=False, microversion='1.1')
self.assertTrue(log_mock.called)
self.assertEqual(uuids.request_id,
log_mock.call_args[0][1]['placement_req_id'])
self.ks_adap_mock.get.reset_mock()
log_mock.reset_mock()
class TestTraits(SchedulerReportClientTestCase):
@ -1891,45 +1866,30 @@ class TestTraits(SchedulerReportClientTestCase):
expected_url, **self.trait_api_kwargs)
self.assertEqual(set(traits), result)
@mock.patch.object(report.LOG, 'warning')
def test_get_provider_traits_not_found(self, log_mock):
"""Test that when the placement API returns a 404 when looking up a
provider's traits, we simply return None and log a warning.
"""
uuid = uuids.compute_node
self.ks_adap_mock.get.return_value = mock.Mock(
status_code=404, headers={
'x-openstack-request-id': uuids.request_id})
result = self.client._get_provider_traits(uuid)
expected_url = '/resource_providers/' + uuid + '/traits'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, raise_exc=False, microversion='1.6')
self.assertTrue(log_mock.called)
self.assertEqual(uuids.request_id,
log_mock.call_args[0][1]['placement_req_id'])
self.assertIsNone(result)
@mock.patch.object(report.LOG, 'error')
def test_get_provider_traits_bad_request(self, log_mock):
"""Test that when the placement API returns a 400 when looking up a
provider's traits, that we simply return None and log an error.
def test_get_provider_traits_error(self, log_mock):
"""Test that when the placement API returns any error when looking up a
provider's traits, we raise an exception.
"""
uuid = uuids.compute_node
self.ks_adap_mock.get.return_value = mock.Mock(
status_code=400, headers={
'x-openstack-request-id': uuids.request_id})
resp_mock = mock.Mock(headers={
'x-openstack-request-id': uuids.request_id})
self.ks_adap_mock.get.return_value = resp_mock
result = self.client._get_provider_traits(uuid)
for status_code in (400, 404, 503):
resp_mock.status_code = status_code
self.assertRaises(
exception.ResourceProviderTraitRetrievalFailed,
self.client._get_provider_traits, uuid)
expected_url = '/resource_providers/' + uuid + '/traits'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, **self.trait_api_kwargs)
self.assertTrue(log_mock.called)
self.assertEqual(uuids.request_id,
log_mock.call_args[0][1]['placement_req_id'])
self.assertIsNone(result)
expected_url = '/resource_providers/' + uuid + '/traits'
self.ks_adap_mock.get.assert_called_once_with(
expected_url, **self.trait_api_kwargs)
self.assertTrue(log_mock.called)
self.assertEqual(uuids.request_id,
log_mock.call_args[0][1]['placement_req_id'])
self.ks_adap_mock.get.reset_mock()
log_mock.reset_mock()
def test_ensure_traits(self):
"""Successful paths, various permutations of traits existing or needing