Search in nova services instead of hypervisors
Nova services and hypervisor naming can differ, as they retireve node names in different way. In the meanwhile we operate with nova.services while enabling/disabling nodes duringh the incident. So we're supposed to have in database record matching to what we have in service list, but not in hypervisor list. Closes-Bug: #1839715 Change-Id: I9c591d33f17a8d5950bdb1fc2d686e2301fc6d95
This commit is contained in:

committed by
Radosław Piliszek

parent
ee48dc2842
commit
4322968b89
@@ -88,7 +88,9 @@ Creates a host under given segment.
|
||||
Creates a Host under given segment with name, type, control_attributes.
|
||||
User can set sepcific hosts as reserved by setting reserved attribute to True.
|
||||
By default `on_maintenance` mode which indicates whether host is under
|
||||
maintenance or not is False when host is created.
|
||||
maintenance or not is False when host is created. Host name should be equal
|
||||
to nova-compute host name from nova service list and host name from the
|
||||
corosync cluster.
|
||||
|
||||
**Preconditions**
|
||||
|
||||
|
@@ -110,7 +110,7 @@ class HostsController(wsgi.Controller):
|
||||
host_data = body.get('host')
|
||||
try:
|
||||
host = self.api.create_host(context, segment_id, host_data)
|
||||
except exception.HypervisorNotFoundByName as e:
|
||||
except exception.ComputeNotFoundByName as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.message)
|
||||
except exception.FailoverSegmentNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
@@ -145,7 +145,7 @@ class HostsController(wsgi.Controller):
|
||||
host_data = body.get('host')
|
||||
try:
|
||||
host = self.api.update_host(context, segment_id, id, host_data)
|
||||
except exception.HypervisorNotFoundByName as e:
|
||||
except exception.ComputeNotFoundByName as e:
|
||||
raise exc.HTTPBadRequest(explanation=e.message)
|
||||
except exception.HostNotFound as e:
|
||||
raise exc.HTTPNotFound(explanation=e.format_message())
|
||||
|
@@ -247,18 +247,15 @@ class API(object):
|
||||
return nova.servers.unlock(uuid)
|
||||
|
||||
@translate_nova_exception
|
||||
def hypervisor_search(self, context, hypervisor_name):
|
||||
"""Search hypervisor with case sensitive hostname."""
|
||||
def find_compute_service(self, context, compute_name):
|
||||
"""Find compute service with case sensitive hostname."""
|
||||
nova = novaclient(context)
|
||||
msg = ("Call hypervisor search command to get list of matching "
|
||||
"hypervisor name '%(hypervisor_name)s'")
|
||||
LOG.info(msg, {'hypervisor_name': hypervisor_name})
|
||||
try:
|
||||
hypervisors_list = nova.hypervisors.search(hypervisor_name)
|
||||
if hypervisor_name not in [host.hypervisor_hostname for host in
|
||||
hypervisors_list]:
|
||||
raise exception.HypervisorNotFoundByName(
|
||||
hypervisor_name=hypervisor_name)
|
||||
except nova_exception.NotFound:
|
||||
raise exception.HypervisorNotFoundByName(
|
||||
hypervisor_name=hypervisor_name)
|
||||
msg = ("Call compute service find command to get list of matching "
|
||||
"hypervisor name '%(compute_name)s'")
|
||||
LOG.info(msg, {'compute_name': compute_name})
|
||||
|
||||
computes = \
|
||||
nova.services.list(binary='nova-compute', host=compute_name)
|
||||
if len(computes) == 0:
|
||||
raise exception.ComputeNotFoundByName(
|
||||
compute_name=compute_name)
|
||||
|
@@ -276,8 +276,9 @@ class HostNotFoundByName(HostNotFound):
|
||||
msg_fmt = _("Host with name %(host_name)s could not be found.")
|
||||
|
||||
|
||||
class HypervisorNotFoundByName(NotFound):
|
||||
msg_fmt = _("Hypervisor with name %(hypervisor_name)s could not be found.")
|
||||
class ComputeNotFoundByName(NotFound):
|
||||
msg_fmt = _("Compute service with name %(compute_name)s could not "
|
||||
"be found.")
|
||||
|
||||
|
||||
class FailoverSegmentExists(MasakariException):
|
||||
|
@@ -151,7 +151,7 @@ class HostAPI(object):
|
||||
|
||||
def _is_valid_host_name(self, context, name):
|
||||
novaclient = nova.API()
|
||||
novaclient.hypervisor_search(context, name)
|
||||
novaclient.find_compute_service(context, name)
|
||||
|
||||
def get_host(self, context, segment_uuid, host_uuid):
|
||||
"""Get a host by id"""
|
||||
|
@@ -304,26 +304,29 @@ class NovaApiTestCase(test.TestCase):
|
||||
mock_servers.unlock.assert_called_once_with(uuidsentinel.fake_server)
|
||||
|
||||
@mock.patch('masakari.compute.nova.novaclient')
|
||||
def test_hypervisor_search_non_existing_host_name(self, mock_novaclient):
|
||||
mock_novaclient.return_value.hypervisors.search.side_effect = (
|
||||
nova_exception.NotFound(http.NOT_FOUND))
|
||||
def test_find_compute_service_non_existing_host_name(
|
||||
self, mock_novaclient):
|
||||
host = 'fake'
|
||||
|
||||
self.assertRaises(exception.HypervisorNotFoundByName,
|
||||
self.api.hypervisor_search, context, 'abc')
|
||||
mock_services = mock.MagicMock()
|
||||
mock_novaclient.return_value = mock.MagicMock(services=mock_services)
|
||||
mock_services.list.return_value = []
|
||||
|
||||
self.assertRaises(exception.ComputeNotFoundByName,
|
||||
self.api.find_compute_service, self.ctx, host)
|
||||
mock_novaclient.assert_called_once_with(self.ctx)
|
||||
mock_services.list.assert_called_once_with(host=host,
|
||||
binary='nova-compute')
|
||||
|
||||
@mock.patch('masakari.compute.nova.novaclient')
|
||||
def test_hypervisor_search_case_sensitive(self, mock_novaclient):
|
||||
test_hypers = [
|
||||
dict(id=1,
|
||||
uuid=uuidsentinel.hyper1,
|
||||
hypervisor_hostname="xyz.one"),
|
||||
dict(id=2,
|
||||
uuid=uuidsentinel.hyper2,
|
||||
hypervisor_hostname="xyz.two", ),
|
||||
dict(id=3,
|
||||
uuid=uuidsentinel.hyper3,
|
||||
hypervisor_hostname="XYZ", )
|
||||
]
|
||||
mock_novaclient.hypervisors.search.return_value = test_hypers
|
||||
self.assertRaises(exception.HypervisorNotFoundByName,
|
||||
self.api.hypervisor_search, context, 'xyz')
|
||||
def test_find_compute_service_existing_host_name(self, mock_novaclient):
|
||||
host = 'fake'
|
||||
|
||||
mock_services = mock.MagicMock()
|
||||
mock_novaclient.return_value = mock.MagicMock(services=mock_services)
|
||||
mock_services.list.return_value = [mock.MagicMock(id='fake_id')]
|
||||
|
||||
self.assertIsNone(self.api.find_compute_service(self.ctx, host))
|
||||
mock_novaclient.assert_called_once_with(self.ctx)
|
||||
mock_services.list.assert_called_once_with(binary='nova-compute',
|
||||
host=host)
|
||||
|
@@ -362,9 +362,9 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
|
||||
@mock.patch.object(host_obj, 'Host')
|
||||
@mock.patch.object(host_obj.Host, 'create')
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
|
||||
def test_create(self, mock_get, mock_hypervisor_search,
|
||||
def test_create(self, mock_get, mock_find_compute_service,
|
||||
mock_host_create, mock_host_obj):
|
||||
mock_get.return_value = self.failover_segment
|
||||
|
||||
@@ -381,13 +381,13 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
host_data)
|
||||
self._assert_host_data(self.host, _make_host_obj(result))
|
||||
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
|
||||
def test_create_non_existing_host(self, mock_segment_get,
|
||||
mock_hypervisor_search):
|
||||
mock_find_compute_service):
|
||||
mock_segment_get.return_value = self.failover_segment
|
||||
mock_hypervisor_search.side_effect = exception\
|
||||
.HypervisorNotFoundByName(hypervisor_name='host-2')
|
||||
mock_find_compute_service.side_effect = exception\
|
||||
.ComputeNotFoundByName(compute_name='host-2')
|
||||
|
||||
host_data = {
|
||||
"name": 'host-2',
|
||||
@@ -397,18 +397,18 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
"control_attributes": "fake-control_attributes"
|
||||
}
|
||||
|
||||
self.assertRaises(exception.HypervisorNotFoundByName,
|
||||
self.assertRaises(exception.ComputeNotFoundByName,
|
||||
self.host_api.create_host,
|
||||
self.context, uuidsentinel.fake_segment1, host_data)
|
||||
|
||||
@mock.patch.object(host_obj, 'Host')
|
||||
@mock.patch.object(host_obj.Host, 'create')
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(api_utils, 'notify_about_host_api')
|
||||
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
|
||||
def test_host_create_exception(self, mock_get,
|
||||
mock_notify_about_host_api,
|
||||
mock_hypervisor_search, mock_host_obj,
|
||||
mock_find_compute_service, mock_host_obj,
|
||||
mock_host_create):
|
||||
mock_get.return_value = self.failover_segment
|
||||
host_data = {
|
||||
@@ -433,16 +433,16 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
exception=e,
|
||||
tb=mock.ANY)]
|
||||
mock_notify_about_host_api.assert_has_calls(notify_calls)
|
||||
mock_hypervisor_search.assert_called_once()
|
||||
mock_find_compute_service.assert_called_once()
|
||||
|
||||
@mock.patch.object(api_utils, 'notify_about_host_api')
|
||||
@mock.patch('oslo_utils.uuidutils.generate_uuid')
|
||||
@mock.patch('masakari.db.host_create')
|
||||
@mock.patch.object(host_obj.Host, '_from_db_object')
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
|
||||
def test_create_convert_boolean_attributes(self, mock_get_segment,
|
||||
mock_hypervisor_search,
|
||||
mock_find_compute_service,
|
||||
mock__from_db_object,
|
||||
mock_host_create,
|
||||
mock_generate_uuid,
|
||||
@@ -500,11 +500,11 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(segment_obj.FailoverSegment,
|
||||
'is_under_recovery')
|
||||
@mock.patch.object(host_obj, 'Host')
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(host_obj.Host, 'save')
|
||||
@mock.patch.object(host_obj.Host, 'get_by_uuid')
|
||||
def test_update(
|
||||
self, mock_get, mock_update, mock_hypervisor_search,
|
||||
self, mock_get, mock_update, mock_find_compute_service,
|
||||
mock_host_obj, mock_is_under_recovery):
|
||||
host_data = {"name": "host_1"}
|
||||
mock_get.return_value = self.host
|
||||
@@ -519,16 +519,17 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
|
||||
@mock.patch.object(segment_obj.FailoverSegment,
|
||||
'is_under_recovery')
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(host_obj.Host, 'get_by_uuid')
|
||||
def test_update_with_non_existing_host(
|
||||
self, mock_get, mock_hypervisor_search, mock_is_under_recovery):
|
||||
self, mock_get, mock_find_compute_service,
|
||||
mock_is_under_recovery):
|
||||
host_data = {"name": "host-2"}
|
||||
mock_get.return_value = self.host
|
||||
mock_hypervisor_search.side_effect = (
|
||||
exception.HypervisorNotFoundByName(hypervisor_name='host-2'))
|
||||
mock_find_compute_service.side_effect = (
|
||||
exception.ComputeNotFoundByName(compute_name='host-2'))
|
||||
mock_is_under_recovery.return_value = False
|
||||
self.assertRaises(exception.HypervisorNotFoundByName,
|
||||
self.assertRaises(exception.ComputeNotFoundByName,
|
||||
self.host_api.update_host, self.context,
|
||||
uuidsentinel.fake_segment,
|
||||
uuidsentinel.fake_host_1,
|
||||
@@ -536,13 +537,13 @@ class HostAPITestCase(test.NoDBTestCase):
|
||||
|
||||
@mock.patch.object(segment_obj.FailoverSegment,
|
||||
'is_under_recovery')
|
||||
@mock.patch.object(nova_obj.API, 'hypervisor_search')
|
||||
@mock.patch.object(nova_obj.API, 'find_compute_service')
|
||||
@mock.patch.object(host_obj.Host, 'save')
|
||||
@mock.patch.object(api_utils, 'notify_about_host_api')
|
||||
@mock.patch.object(host_obj.Host, 'get_by_uuid')
|
||||
def test_host_update_exception(
|
||||
self, mock_get, mock_notify_about_host_api, mock_host_obj,
|
||||
mock_hypervisor_search, mock_is_under_recovery):
|
||||
mock_find_compute_service, mock_is_under_recovery):
|
||||
host_data = {"name": "host_1"}
|
||||
mock_get.return_value = self.host
|
||||
e = exception.InvalidInput(reason="TEST")
|
||||
|
7
releasenotes/notes/compute_search-3da97e69e661a73f.yaml
Normal file
7
releasenotes/notes/compute_search-3da97e69e661a73f.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes validation of compute host existence from checking hypervisor list
|
||||
to compute service list. Since masakari needs to match nova compute service
|
||||
hostname with the one in pacemaker cluster and added to API for correctly
|
||||
processing hostmonitors failover notifications.
|
Reference in New Issue
Block a user