Merge "Raise on API errors getting aggregates/traits"
This commit is contained in:
commit
27eadbc499
@ -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")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user