Avoid creating a root provider when parent is not found

Before this change, when agent called to conductor to report_data(),
if the parent provider was not found by hostname, we would log an error,
and then continue to create the "child" provider with no parent. We should
never do this if we are supposed to have a parent. Cleanup from this
situation is also messy.

This makes us raise PlacementResourceProviderNotFound() in that case,
which aborts the report and thus does not create the provider incorrectly.
It also makes the agent catch that exception and moves the log message
to the agent where the actual problem is (i.e. likely misconfiguration).

The exception used here is actually defined incorrectly, having a message
class variable instead of _msg_fmt, which caused it to not render properly.
This fixes that along the way and adds tests for the new conductor and
agent behaviors.

Closes task: 38813
Closes task: 38814

Change-Id: Ied8ee91592eb0b4675f9c155e30a6c3a7df9b597
This commit is contained in:
Dan Smith 2020-02-19 11:37:19 -08:00
parent e0ba01891f
commit d279c22d1e
5 changed files with 35 additions and 4 deletions

View File

@ -72,4 +72,7 @@ class ResourceTracker(object):
acc_list.extend(acc_driver.discover())
# Call conductor_api here to diff and report acc data. Now, we actually
# do not have the method report_data.
self.conductor_api.report_data(context, self.host, acc_list)
try:
self.conductor_api.report_data(context, self.host, acc_list)
except exception.PlacementResourceProviderNotFound as e:
LOG.error('Unable to report usage: %s', e)

View File

@ -243,7 +243,8 @@ class PlacementEndpointNotFound(NotFound):
class PlacementResourceProviderNotFound(NotFound):
message = _("Placement resource provider not found %(resource_provider)s.")
_msg_fmt = _("Placement resource provider not found: "
"%(resource_provider)s.")
class PlacementInventoryNotFound(NotFound):

View File

@ -364,8 +364,8 @@ class ConductorManager(object):
pr_uuid = provider["resource_providers"][0]["uuid"]
return pr_uuid
except IndexError:
LOG.error("Error, provider %(hostname)s can not be found",
{"hostname": hostname})
raise exception.PlacementResourceProviderNotFound(
resource_provider=hostname)
except Exception as e:
LOG.error("Error, could not access placement. Details: %(info)s",
{"info": e})

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
"""Cyborg agent resource_tracker test cases."""
from cyborg.agent.resource_tracker import ResourceTracker
@ -49,3 +50,13 @@ class TestResourceTracker(base.TestCase):
enabled_drivers = ['invalid_driver']
self.assertRaises(exception.InvalidDriver, self.rt._initialize_drivers,
enabled_drivers)
@mock.patch('cyborg.agent.resource_tracker.LOG')
def test_update_usage_failed_parent_provider(self, mock_log):
with mock.patch.object(self.rt.conductor_api, 'report_data') as m:
m.side_effect = exception.PlacementResourceProviderNotFound(
resource_provider='foo')
self.rt.update_usage(None)
m.assert_called_once_with(None, 'fake-mini', [])
mock_log.error.assert_called_once_with('Unable to report usage: %s',
m.side_effect)

View File

@ -12,6 +12,7 @@
import fixtures
import mock
from cyborg.common import exception
from cyborg.conductor import manager
from cyborg.tests import base
@ -70,3 +71,18 @@ class ConductorManagerTest(base.TestCase):
self.placement_mock.add_traits_to_rp.assert_called_once_with(
sub_pr_uuid, traits)
self.assertEqual(sub_pr_uuid, actual)
def test_get_root_provider(self):
self.placement_mock.get.return_value.json.return_value = {
'resource_providers': [{'uuid': mock.sentinel.uuid}],
}
uuid = self.cm._get_root_provider(mock.sentinel.context, 'foo')
self.assertEqual(mock.sentinel.uuid, uuid)
def test_get_root_provider_not_found(self):
self.placement_mock.get.return_value.json.return_value = {
'resource_providers': [],
}
self.assertRaises(exception.PlacementResourceProviderNotFound,
self.cm._get_root_provider,
mock.sentinel.context, 'foo')