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:
Dmitriy Rabotyagov
2020-05-16 12:22:13 +03:00
committed by Radosław Piliszek
parent ee48dc2842
commit 4322968b89
8 changed files with 72 additions and 61 deletions

View File

@@ -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**

View File

@@ -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())

View File

@@ -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)

View File

@@ -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):

View File

@@ -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"""

View File

@@ -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)

View File

@@ -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")

View 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.