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:
parent
a628d2f09a
commit
cf670873ad
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue