Validate requested host/node during servers create

This change adds a method to validate a requested host and/or
hypervisor_hostname(node) during servers create. We are simply
checking for existence of the resource, so the host is validated
for the HostMapping and the node is validated by checking for
the related resource provider in the placement service.

Part of Blueprint: add-host-and-hypervisor-hostname-flag-to-create-server

Change-Id: I009761162817c6ed1035d2208605268dc82d430f
This commit is contained in:
zhu.boxiang 2019-05-24 16:35:00 +08:00
parent a628d2f09a
commit cf670873ad
2 changed files with 166 additions and 0 deletions

View File

@ -952,6 +952,48 @@ class API(base.Base):
br.create()
im.create()
def _validate_host_or_node(self, context, host, hypervisor_hostname):
"""Check whether compute nodes exist by validating the host
and/or the hypervisor_hostname. There are three cases:
1. If only host is supplied, we can lookup the HostMapping in
the API DB.
2. If only node is supplied, we can query a resource provider
with that name in placement.
3. If both host and node are supplied, we can get the cell from
HostMapping and from that lookup the ComputeNode with the
given cell.
:param context: The API request context.
:param host: Target host.
:param hypervisor_hostname: Target node.
:raises: ComputeHostNotFound if we find no compute nodes with host
and/or hypervisor_hostname.
"""
if host:
# When host is specified.
try:
host_mapping = objects.HostMapping.get_by_host(context, host)
except exception.HostMappingNotFound:
LOG.warning('No host-to-cell mapping found for host '
'%(host)s.', {'host': host})
raise exception.ComputeHostNotFound(host=host)
# When both host and node are specified.
if hypervisor_hostname:
cell = host_mapping.cell_mapping
with nova_context.target_cell(context, cell) as cctxt:
# Here we only do an existence check, so we don't
# need to store the return value into a variable.
objects.ComputeNode.get_by_host_and_nodename(
cctxt, host, hypervisor_hostname)
elif hypervisor_hostname:
# When only node is specified.
try:
self.placementclient.get_provider_by_name(
context, hypervisor_hostname)
except exception.ResourceProviderNotFound:
raise exception.ComputeHostNotFound(host=hypervisor_hostname)
def _provision_instances(self, context, instance_type, min_count,
max_count, base_options, boot_meta, security_groups,
block_device_mapping, shutdown_terminate,

View File

@ -6308,6 +6308,130 @@ class _ComputeAPIUnitTestMixIn(object):
self.context, ids, rebuild=True)
get_min_version.assert_called_once_with(self.context, 'nova-compute')
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_host(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm):
host = 'fake-host'
node = None
self.compute_api._validate_host_or_node(self.context, host, node)
mock_get_hm.assert_called_once_with(self.context, 'fake-host')
mock_get_host_node.assert_not_called()
mock_get_provider_by_name.assert_not_called()
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_invalid_host(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm):
host = 'fake-host'
node = None
mock_get_hm.side_effect = exception.HostMappingNotFound(name=host)
self.assertRaises(exception.ComputeHostNotFound,
self.compute_api._validate_host_or_node,
self.context, host, node)
mock_get_hm.assert_called_once_with(self.context, 'fake-host')
mock_get_host_node.assert_not_called()
mock_get_provider_by_name.assert_not_called()
@mock.patch('nova.context.target_cell')
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_host_and_node(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm,
mock_target_cell):
host = 'fake-host'
node = 'fake-host'
self.compute_api._validate_host_or_node(self.context, host, node)
mock_get_host_node.assert_called_once_with(
mock_target_cell.return_value.__enter__.return_value,
'fake-host', 'fake-host')
mock_get_hm.assert_called_once_with(self.context, 'fake-host')
mock_get_provider_by_name.assert_not_called()
@mock.patch('nova.context.target_cell')
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_invalid_host_and_node(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm,
mock_target_cell):
host = 'fake-host'
node = 'fake-host'
mock_get_host_node.side_effect = (
exception.ComputeHostNotFound(host=host))
self.assertRaises(exception.ComputeHostNotFound,
self.compute_api._validate_host_or_node,
self.context, host, node)
mock_get_host_node.assert_called_once_with(
mock_target_cell.return_value.__enter__.return_value,
'fake-host', 'fake-host')
mock_get_hm.assert_called_once_with(self.context, 'fake-host')
mock_get_provider_by_name.assert_not_called()
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_node(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm):
host = None
node = 'fake-host'
self.compute_api._validate_host_or_node(self.context, host, node)
mock_get_provider_by_name.assert_called_once_with(
self.context, 'fake-host')
mock_get_host_node.assert_not_called()
mock_get_hm.assert_not_called()
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_invalid_node(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm):
host = None
node = 'fake-host'
mock_get_provider_by_name.side_effect = (
exception.ResourceProviderNotFound(name_or_uuid=node))
self.assertRaises(exception.ComputeHostNotFound,
self.compute_api._validate_host_or_node,
self.context, host, node)
mock_get_provider_by_name.assert_called_once_with(
self.context, 'fake-host')
mock_get_host_node.assert_not_called()
mock_get_hm.assert_not_called()
@mock.patch('nova.objects.HostMapping.get_by_host')
@mock.patch('nova.objects.ComputeNode.get_by_host_and_nodename')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'get_provider_by_name')
def test__validate_host_or_node_with_rp_500_exception(
self, mock_get_provider_by_name, mock_get_host_node, mock_get_hm):
host = None
node = 'fake-host'
mock_get_provider_by_name.side_effect = (
exception.PlacementAPIConnectFailure())
self.assertRaises(exception.PlacementAPIConnectFailure,
self.compute_api._validate_host_or_node,
self.context, host, node)
mock_get_provider_by_name.assert_called_once_with(
self.context, 'fake-host')
mock_get_host_node.assert_not_called()
mock_get_hm.assert_not_called()
class ComputeAPIUnitTestCase(_ComputeAPIUnitTestMixIn, test.NoDBTestCase):
def setUp(self):