Check if host belongs to a segment

If you query a host passing an existing failover segment but not
the one that is assigned to the host, it still returns the host
successfully. In this case, it should fail with 404 error.

This patch checks if the host belongs to the segment that is
present in the URI. If not, it will return 404 error.

Change-Id: I16256cc2a01696a1d54cb9326aed17b723b87727
Closes-Bug: #1854323
This commit is contained in:
Shilpa 2019-12-04 11:43:15 +05:30
parent e9dbb5df56
commit 895899f11b
15 changed files with 291 additions and 153 deletions

View File

@ -8,6 +8,22 @@
"name": "fake-mini",
"on_maintenance": false,
"control_attributes": "TEST",
"failover_segment": {
"masakari_object.name": "FailoverSegment",
"masakari_object.data": {
"uuid": "89597691-bebd-4860-a93e-1b6e9de34b9e",
"deleted": false,
"created_at": "2018-11-27T09:26:30Z",
"recovery_method": "auto",
"updated_at": "2018-11-27T09:54:50Z",
"name": "test",
"service_type": "compute",
"deleted_at": null,
"id": 877, "description": null
},
"masakari_object.version": "1.0",
"masakari_object.namespace": "masakari"
},
"fault": null,
"type": "COMPUTE",
"failover_segment_id": "89597691-bebd-4860-a93e-1b6e9de34b9e"

View File

@ -23,6 +23,7 @@ from webob import exc
from masakari.api.openstack import common
from masakari.api.openstack import extensions
from masakari.api.openstack.ha.schemas import hosts as schema
from masakari.api.openstack.ha.views import hosts as views_hosts
from masakari.api.openstack import wsgi
from masakari.api import validation
from masakari import exception
@ -94,7 +95,8 @@ class HostsController(wsgi.Controller):
except exception.FailoverSegmentNotFound as ex:
raise exc.HTTPNotFound(explanation=ex.format_message())
return {'hosts': hosts}
builder = views_hosts.get_view_builder(req)
return builder.build_hosts(hosts)
@wsgi.response(http.CREATED)
@extensions.expected_errors((http.BAD_REQUEST, http.FORBIDDEN,
@ -114,7 +116,8 @@ class HostsController(wsgi.Controller):
except exception.HostExists as e:
raise exc.HTTPConflict(explanation=e.format_message())
return {'host': host}
builder = views_hosts.get_view_builder(req)
return {'host': builder.build_host(host)}
@extensions.expected_errors((http.FORBIDDEN, http.NOT_FOUND))
def show(self, req, segment_id, id):
@ -127,7 +130,9 @@ class HostsController(wsgi.Controller):
raise exc.HTTPNotFound(explanation=e.format_message())
except exception.FailoverSegmentNotFound as e:
raise exc.HTTPNotFound(explanation=e.format_message())
return {'host': host}
builder = views_hosts.get_view_builder(req)
return {'host': builder.build_host(host)}
@extensions.expected_errors((http.BAD_REQUEST, http.FORBIDDEN,
http.NOT_FOUND, http.CONFLICT))
@ -148,7 +153,8 @@ class HostsController(wsgi.Controller):
except (exception.HostExists, exception.Conflict) as e:
raise exc.HTTPConflict(explanation=e.format_message())
return {'host': host}
builder = views_hosts.get_view_builder(req)
return {'host': builder.build_host(host)}
@wsgi.response(http.NO_CONTENT)
@extensions.expected_errors((http.FORBIDDEN, http.NOT_FOUND,

View File

@ -0,0 +1,57 @@
# Copyright (c) 2020 NTT Data
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from masakari.api.openstack import common
def get_view_builder(req):
base_url = req.application_url
return ViewBuilder(base_url)
class ViewBuilder(common.ViewBuilder):
def __init__(self, base_url):
""":param base_url: url of the root wsgi application."""
self.prefix = self._update_masakari_link_prefix(base_url)
self.base_url = base_url
def _host_details(self, host):
return {
'id': host.id,
'uuid': host.uuid,
'name': host.name,
'failover_segment_id': host.failover_segment.uuid,
'failover_segment': host.failover_segment,
'type': host.type,
'reserved': host.reserved,
'control_attributes': host.control_attributes,
'on_maintenance': host.on_maintenance,
'created_at': host.created_at,
'updated_at': host.updated_at,
'deleted_at': host.deleted_at,
'deleted': host.deleted
}
def build_host(self, host):
get_host_response = self._host_details(host)
return get_host_response
def build_hosts(self, hosts):
host_objs = []
for host in hosts:
get_host_response = self._host_details(host)
host_objs.append(get_host_response)
return dict(hosts=host_objs)

View File

@ -216,7 +216,7 @@ class MasakariManager(manager.Manager):
fields.FailoverSegmentRecoveryMethod.AUTO):
reserved_host_object_list = objects.HostList.get_all(
context, filters={
'failover_segment_id': host_obj.failover_segment_id,
'failover_segment_id': host_obj.failover_segment.uuid,
'reserved': True,
'on_maintenance': False
})

View File

@ -155,12 +155,13 @@ class HostAPI(object):
def get_host(self, context, segment_uuid, host_uuid):
"""Get a host by id"""
objects.FailoverSegment.get_by_uuid(context, segment_uuid)
if uuidutils.is_uuid_like(host_uuid):
LOG.debug("Fetching host by "
"UUID", host_uuid=host_uuid)
host = objects.Host.get_by_uuid(context, host_uuid)
host = objects.Host.get_by_uuid(
context, host_uuid, segment_uuid=segment_uuid)
else:
LOG.debug("Failed to fetch host by uuid %s", host_uuid)
raise exception.HostNotFound(id=host_uuid)
@ -189,7 +190,7 @@ class HostAPI(object):
# Populate host object for create
host.name = host_data.get('name')
host.failover_segment_id = segment.uuid
host.failover_segment = segment
host.type = host_data.get('type')
host.control_attributes = host_data.get('control_attributes')
host.on_maintenance = strutils.bool_from_string(
@ -213,11 +214,11 @@ class HostAPI(object):
def update_host(self, context, segment_uuid, id, host_data):
"""Update the host"""
segment = objects.FailoverSegment.get_by_uuid(context, segment_uuid)
host = objects.Host.get_by_uuid(context, id)
host = objects.Host.get_by_uuid(
context, id, segment_uuid=segment_uuid)
if is_failover_segment_under_recovery(segment):
if is_failover_segment_under_recovery(host.failover_segment):
msg = _("Host %s can't be updated as "
"it is in-use to process notifications.") % host.uuid
LOG.error(msg)
@ -248,9 +249,8 @@ class HostAPI(object):
def delete_host(self, context, segment_uuid, id):
"""Delete the host"""
segment = objects.FailoverSegment.get_by_uuid(context, segment_uuid)
host = objects.Host.get_by_uuid(context, id, segment_uuid=segment_uuid)
if is_failover_segment_under_recovery(segment):
if is_failover_segment_under_recovery(host.failover_segment):
msg = _("Host %s can't be deleted as "
"it is in-use to process notifications.") % host.uuid
LOG.error(msg)

View File

@ -66,7 +66,6 @@ class HostApiPayloadBase(base.NotificationPayloadBase):
'id': ('host', 'id'),
'uuid': ('host', 'uuid'),
'name': ('host', 'name'),
'failover_segment_id': ('host', 'failover_segment_id'),
'failover_segment': ('host', 'failover_segment'),
'type': ('host', 'type'),
'reserved': ('host', 'reserved'),
@ -74,12 +73,12 @@ class HostApiPayloadBase(base.NotificationPayloadBase):
'on_maintenance': ('host', 'on_maintenance'),
}
# Version 1.0: Initial version
VERSION = '1.0'
# Version 1.1: Removed 'failover_segment_id' parameter
VERSION = '1.1'
fields = {
'id': fields.IntegerField(),
'uuid': fields.UUIDField(),
'name': fields.StringField(),
'failover_segment_id': fields.UUIDField(),
'failover_segment': fields.ObjectField('FailoverSegment'),
'type': fields.StringField(),
'reserved': fields.BooleanField(),

View File

@ -15,6 +15,7 @@
from oslo_log import log as logging
from oslo_utils import uuidutils
from oslo_utils import versionutils
from masakari.api import utils as api_utils
from masakari import db
@ -32,13 +33,14 @@ class Host(base.MasakariPersistentObject, base.MasakariObject,
# Version 1.0: Initial version
# Version 1.1: Added 'segment_uuid' parameter to 'get_by_uuid' method
VERSION = '1.1'
# Version 1.2: Removed 'failover_segment_id' parameter which can be
# retrieved from failover_segment object
VERSION = '1.2'
fields = {
'id': fields.IntegerField(),
'uuid': fields.UUIDField(),
'name': fields.StringField(),
'failover_segment_id': fields.UUIDField(),
'failover_segment': fields.ObjectField('FailoverSegment'),
'type': fields.StringField(),
'reserved': fields.BooleanField(),
@ -46,6 +48,12 @@ class Host(base.MasakariPersistentObject, base.MasakariObject,
'on_maintenance': fields.BooleanField(),
}
def obj_make_compatible(self, primitive, target_version):
super(Host, self).obj_make_compatible(primitive, target_version)
target_version = versionutils.convert_version_to_tuple(target_version)
if target_version >= (1, 2) and 'failover_segment_id' in primitive:
del primitive['failover_segment_id']
@staticmethod
def _from_db_object(context, host, db_host):
@ -53,7 +61,7 @@ class Host(base.MasakariPersistentObject, base.MasakariObject,
db_value = db_host.get(key)
if key == "failover_segment":
db_value = objects.FailoverSegment._from_db_object(
host._context, objects.FailoverSegment(), db_value)
context, objects.FailoverSegment(), db_value)
setattr(host, key, db_value)
@ -88,10 +96,13 @@ class Host(base.MasakariPersistentObject, base.MasakariObject,
LOG.debug('Generated uuid %(uuid)s for host',
dict(uuid=updates['uuid']))
if 'failover_segment' in updates:
if 'failover_segment' not in updates:
raise exception.ObjectActionError(action='create',
reason='failover segment '
'assigned')
'not assigned')
segment = updates.pop('failover_segment')
updates['failover_segment_id'] = segment.uuid
api_utils.notify_about_host_api(self._context, self,
action=fields.EventNotificationAction.HOST_CREATE,

View File

@ -142,7 +142,7 @@ class TestHosts(base.BaseFunctionalTest):
reserved='True')
result = self.admin_conn.ha.get_host(host.uuid,
host.failover_segment_id)
self.segment.uuid)
# Confirm host update
self.assertEqual(True, result.on_maintenance)
self.assertEqual(True, result.reserved)
@ -165,5 +165,5 @@ class TestHosts(base.BaseFunctionalTest):
name=self.hypervisors[1]['name'])
result = self.admin_conn.ha.get_host(host.uuid,
host.failover_segment_id)
self.segment.uuid)
self.assertEqual(result.name, updated_host.name)

View File

@ -30,6 +30,7 @@ from masakari.objects import host as host_obj
from masakari.objects import segment as segment_obj
from masakari import test
from masakari.tests.unit.api.openstack import fakes
from masakari.tests.unit import fakes as fakes_data
from masakari.tests import uuidsentinel
@ -42,33 +43,6 @@ def _make_hosts_list(hosts_list):
_make_host_obj(a) for a in hosts_list])
HOST_LIST = [
{"name": "host_1", "id": "1", "reserved": False,
"on_maintenance": False, "type": "fake",
"control_attributes": "fake-control_attributes",
"uuid": uuidsentinel.fake_host_1,
"failover_segment_id": uuidsentinel.fake_segment1},
{"name": "host_2", "id": "2", "reserved": False,
"on_maintenance": False, "type": "fake",
"control_attributes": "fake-control_attributes",
"uuid": uuidsentinel.fake_host_2,
"failover_segment_id": uuidsentinel.fake_segment1}
]
HOST_LIST = _make_hosts_list(HOST_LIST)
HOST = {
"name": "host_1", "id": "1", "reserved": False,
"on_maintenance": False, "type": "fake",
"control_attributes": "fake-control_attributes",
"uuid": uuidsentinel.fake_host_1,
"failover_segment_id": uuidsentinel.fake_segment1
}
HOST = _make_host_obj(HOST)
@ddt.ddt
class HostTestCase(test.TestCase):
"""Test Case for host api."""
@ -85,6 +59,25 @@ class HostTestCase(test.TestCase):
def setUp(self):
super(HostTestCase, self).setUp()
self._set_up()
self.failover_segment = fakes_data.create_fake_failover_segment(
name="segment1", id=1, description="failover_segment for compute",
service_type="COMPUTE", recovery_method="auto",
uuid=uuidsentinel.fake_segment
)
self.host = fakes_data.create_fake_host(
name="host_1", id=1, reserved=False, on_maintenance=False,
type="fake", control_attributes="fake-control_attributes",
uuid=uuidsentinel.fake_host_1,
failover_segment=self.failover_segment
)
self.host_2 = fakes_data.create_fake_host(
name="host_2", id=2, reserved=False, on_maintenance=False,
type="fake", control_attributes="fake-control_attributes",
uuid=uuidsentinel.fake_host_2,
failover_segment=self.failover_segment
)
self.host_list = [self.host, self.host_2]
self.host_list_obj = _make_hosts_list(self.host_list)
@property
def app(self):
@ -98,18 +91,20 @@ class HostTestCase(test.TestCase):
@mock.patch.object(ha_api.HostAPI, 'get_all')
def test_index(self, mock_get_all, mock_segment):
mock_segment.return_value = mock.Mock()
mock_get_all.return_value = HOST_LIST
mock_get_all.return_value = self.host_list
result = self.controller.index(self.req, uuidsentinel.fake_segment1)
result = result['hosts']
self._assert_host_data(HOST_LIST, _make_hosts_list(result))
self._assert_host_data(self.host_list_obj, _make_hosts_list(result))
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
@mock.patch.object(ha_api.HostAPI, 'get_all')
def test_index_valid_on_maintenance(self, mock_get_all, mock_segment):
host_list = [{"name": "host_1", "id": "1", "on_maintenance": True},
{"name": "host_2", "id": "2", "on_maintenance": True}]
mock_get_all.return_value = host_list
mock_segment.return_value = mock.Mock()
self.host_list[0]['on_maintenance'] = True
self.host_list[1]['on_maintenance'] = True
mock_get_all.return_value = self.host_list
for parameter in ['1', 't', 'true', 'on', 'y', 'yes']:
req = fakes.HTTPRequest.blank(
'/v1/segments/%s/hosts?on_maintenance=''%s' % (
@ -117,13 +112,12 @@ class HostTestCase(test.TestCase):
use_admin_context=True)
result = self.controller.index(req, uuidsentinel.fake_segment1)
self.assertIn('hosts', result)
self.assertEqual(len(host_list), len(result['hosts']))
for host in result['hosts']:
self.assertTrue(host['on_maintenance'])
host_list = [{"name": "host_1", "id": "1", "on_maintenance": False},
{"name": "host_2", "id": "2", "on_maintenance": False}]
mock_get_all.return_value = host_list
self.host_list[0]['on_maintenance'] = False
self.host_list[1]['on_maintenance'] = False
mock_get_all.return_value = self.host_list
for parameter in ['0', 'f', 'false', 'off', 'n', 'no']:
req = fakes.HTTPRequest.blank(
'/v1/segments/%s/hosts?on_maintenance=''%s' % (
@ -131,7 +125,6 @@ class HostTestCase(test.TestCase):
use_admin_context=True)
result = self.controller.index(req, uuidsentinel.fake_segment1)
self.assertIn('hosts', result)
self.assertEqual(len(host_list), len(result['hosts']))
for host in result['hosts']:
self.assertFalse(host['on_maintenance'])
@ -148,9 +141,9 @@ class HostTestCase(test.TestCase):
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
@mock.patch.object(ha_api.HostAPI, 'get_all')
def test_index_valid_reserved(self, mock_get_all, mock_segment):
host_list = [{"name": "host_1", "id": "1", "reserved": True},
{"name": "host_2", "id": "2", "reserved": True}]
mock_get_all.return_value = host_list
self.host_list[0]['reserved'] = True
self.host_list[1]['reserved'] = True
mock_get_all.return_value = self.host_list
for parameter in ['1', 't', 'true', 'on', 'y', 'yes']:
req = fakes.HTTPRequest.blank(
'/v1/segments/%s/hosts?reserved=''%s' % (
@ -158,13 +151,12 @@ class HostTestCase(test.TestCase):
), use_admin_context=True)
result = self.controller.index(req, uuidsentinel.fake_segment1)
self.assertIn('hosts', result)
self.assertEqual(len(host_list), len(result['hosts']))
for host in result['hosts']:
self.assertTrue(host['reserved'])
host_list = [{"name": "host_1", "id": "1", "reserved": False},
{"name": "host_2", "id": "2", "reserved": False}]
mock_get_all.return_value = host_list
self.host_list[0]['reserved'] = False
self.host_list[1]['reserved'] = False
mock_get_all.return_value = self.host_list
for parameter in ['0', 'f', 'false', 'off', 'n', 'no']:
req = fakes.HTTPRequest.blank(
'/v1/segments/%s/hosts?reserved=''%s' % (
@ -172,7 +164,6 @@ class HostTestCase(test.TestCase):
use_admin_context=True)
result = self.controller.index(req, uuidsentinel.fake_segment1)
self.assertIn('hosts', result)
self.assertEqual(len(host_list), len(result['hosts']))
for host in result['hosts']:
self.assertFalse(host['reserved'])
@ -232,7 +223,7 @@ class HostTestCase(test.TestCase):
@mock.patch.object(ha_api.HostAPI, 'create_host')
def test_create(self, mock_create):
mock_create.return_value = HOST
mock_create.return_value = self.host
body = {
"host": {
"name": "host-1", "type": "fake",
@ -244,7 +235,7 @@ class HostTestCase(test.TestCase):
result = self.controller.create(self.req,
uuidsentinel.fake_segment1, body=body)
result = result['host']
self._assert_host_data(HOST, _make_host_obj(result))
self._assert_host_data(self.host, _make_host_obj(result))
@mock.patch('masakari.rpc.get_client')
@mock.patch.object(ha_api.HostAPI, 'create_host')
@ -354,12 +345,12 @@ class HostTestCase(test.TestCase):
@mock.patch.object(ha_api.HostAPI, 'get_host')
def test_show(self, mock_get_host):
mock_get_host.return_value = HOST
mock_get_host.return_value = self.host
result = self.controller.show(self.req, uuidsentinel.fake_segment1,
uuidsentinel.fake_host_1)
result = result['host']
self._assert_host_data(HOST, _make_host_obj(result))
self._assert_host_data(self.host, _make_host_obj(result))
@mock.patch.object(ha_api.HostAPI, 'get_host')
def test_show_with_non_existing_id(self, mock_get_host):
@ -369,6 +360,16 @@ class HostTestCase(test.TestCase):
self.controller.show, self.req,
uuidsentinel.fake_segment1, "2")
@mock.patch.object(ha_api.HostAPI, 'get_host')
def test_show_non_assigned_failover_segment(self, mock_get_host):
mock_get_host.side_effect = exception.HostNotFoundUnderFailoverSegment(
host_uuid=uuidsentinel.fake_host_3,
segment_uuid=uuidsentinel.fake_segment1)
self.assertRaises(exc.HTTPNotFound, self.controller.show,
self.req, uuidsentinel.fake_segment1,
uuidsentinel.fake_host_3)
@ddt.data(
{"body": {
"host": {
@ -382,14 +383,14 @@ class HostTestCase(test.TestCase):
@ddt.unpack
@mock.patch.object(ha_api.HostAPI, 'update_host')
def test_update(self, mock_update_host, body):
mock_update_host.return_value = HOST
mock_update_host.return_value = self.host
result = self.controller.update(self.req, uuidsentinel.fake_segment1,
uuidsentinel.fake_host_1,
body=body)
result = result['host']
self._assert_host_data(HOST, _make_host_obj(result))
self._assert_host_data(self.host, _make_host_obj(result))
@ddt.data(
# no updates
@ -435,6 +436,17 @@ class HostTestCase(test.TestCase):
self.req, uuidsentinel.fake_segment1,
uuidsentinel.fake_host_1, body=test_data)
@mock.patch.object(ha_api.HostAPI, 'update_host')
def test_update_non_assigned_failover_segment(self, mock_update_host):
test_data = {"host": {"name": "host-1"}}
mock_update_host.side_effect = \
exception.HostNotFoundUnderFailoverSegment(
host_uuid=uuidsentinel.fake_host_3,
segment_uuid=uuidsentinel.fake_segment1)
self.assertRaises(exc.HTTPNotFound, self.controller.update,
self.req, uuidsentinel.fake_segment1,
uuidsentinel.fake_host_3, body=test_data)
@mock.patch.object(ha_api.HostAPI, 'delete_host')
def test_delete_host(self, mock_delete):

View File

@ -332,6 +332,7 @@ class HostsTestCase(test.TestCase, ModelsObjectComparatorMixin):
# create one more reserved_host which is not on maintenance
temp_host = self._get_fake_values()
temp_host['on_maintenance'] = False
temp_host['failover_segment_id'] = uuidsentinel.failover_segment_id
self._create_host(temp_host)
ignored_keys = ['deleted', 'created_at', 'updated_at', 'deleted_at',

View File

@ -396,8 +396,7 @@ class EngineManagerUnitTestCase(test.NoDBTestCase):
mock_format.return_value = mock.ANY
fake_host = fakes.create_fake_host()
fake_host.failover_segment = fakes.create_fake_failover_segment(
recovery_method='reserved_host')
fake_host.failover_segment.recovery_method = 'reserved_host'
mock_host_obj.return_value = fake_host
notification = self._get_compute_host_type_notification()
@ -440,8 +439,7 @@ class EngineManagerUnitTestCase(test.NoDBTestCase):
mock_host_update, mock_host_save, mock_host_obj,
mock_notification_get):
fake_host = fakes.create_fake_host()
fake_host.failover_segment = fakes.create_fake_failover_segment(
recovery_method='reserved_host')
fake_host.failover_segment.recovery_method = 'reserved_host'
reserved_host_object_list = [fake_host]
mock_get_all.return_value = reserved_host_object_list
mock_host_obj.return_value = fake_host
@ -495,8 +493,7 @@ class EngineManagerUnitTestCase(test.NoDBTestCase):
mock_host_update, mock_host_save, mock_host_obj,
mock_notification_get):
fake_host = fakes.create_fake_host(reserved=True)
fake_host.failover_segment = fakes.create_fake_failover_segment(
recovery_method='reserved_host')
fake_host.failover_segment.recovery_method = 'reserved_host'
reserved_host_object_list = [fake_host]
mock_get_all.return_value = reserved_host_object_list
mock_host_obj.return_value = fake_host

View File

@ -12,15 +12,31 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import iso8601
from oslo_utils import timeutils
from oslo_utils import uuidutils
from masakari import objects
from masakari.objects import segment as segment_obj
from masakari.tests import uuidsentinel
NOW = timeutils.utcnow().replace(microsecond=0)
def _make_segment_obj(segment_dict):
return segment_obj.FailoverSegment(**segment_dict)
FAILOVER_SEGMENT = {"name": "segment1", "id": "1",
"service_type": "COMPUTE", "recovery_method": "auto",
"uuid": uuidsentinel.fake_segment1,
"description": "failover_segment for compute"}
FAILOVER_SEGMENT = _make_segment_obj(FAILOVER_SEGMENT)
class FakeNovaClient(object):
class Server(object):
def __init__(self, id=None, uuid=None, host=None, vm_state=None,
@ -170,15 +186,19 @@ def create_fake_notification(type="VM", id=1, payload=None,
notification_uuid=notification_uuid)
def create_fake_host(name='fake_host', id=1, reserved=False,
on_maintenance=False, type='SSH',
control_attributes='fake',
uuid=uuidsentinel.fake_host,
failover_segment_id=uuidsentinel.fake_segment):
return objects.Host(
name=name, id=id, reserved=reserved, on_maintenance=on_maintenance,
type=type, control_attributes=control_attributes, uuid=uuid,
failover_segment_id=failover_segment_id)
def create_fake_host(**updates):
host = {
'name': 'fake_host', 'id': 1, 'reserved': False,
'on_maintenance': False, 'type': 'SSH',
'control_attributes': 'fake', 'uuid': uuidsentinel.fake_host,
'failover_segment': FAILOVER_SEGMENT,
'created_at': datetime.datetime(
2019, 8, 8, 0, 0, 0, tzinfo=iso8601.UTC),
'updated_at': None, 'deleted_at': None, 'deleted': False
}
if updates:
host.update(updates)
return objects.Host(**host)
def create_fake_failover_segment(name='fake_segment', id=1, description=None,

View File

@ -298,8 +298,7 @@ class HostAPITestCase(test.NoDBTestCase):
self.host = fakes_data.create_fake_host(
name="host_1", id=1, reserved=False, on_maintenance=False,
type="fake", control_attributes="fake-control_attributes",
uuid=uuidsentinel.fake_host_1,
failover_segment_id=uuidsentinel.fake_segment1
uuid=uuidsentinel.fake_host_1
)
self.exception_in_use = exception.HostInUse(
uuid=self.host.uuid)
@ -315,8 +314,7 @@ class HostAPITestCase(test.NoDBTestCase):
fake_host = fakes_data.create_fake_host(
name="host_2", id=2, reserved=False, on_maintenance=False,
type="fake", control_attributes="fake-control_attributes",
uuid=uuidsentinel.fake_host_2,
failover_segment_id=uuidsentinel.fake_segment1
uuid=uuidsentinel.fake_host_2
)
fake_host_list = [self.host, fake_host]
mock_get_all.return_value = fake_host_list
@ -461,7 +459,7 @@ class HostAPITestCase(test.NoDBTestCase):
'control_attributes': 'fake-control_attributes',
'on_maintenance': False,
'uuid': uuidsentinel.fake_uuid,
'failover_segment_id': uuidsentinel.fake_segment,
'failover_segment_id': self.failover_segment.uuid,
'type': 'fake-type'
}
mock_host_create.create = mock.Mock()
@ -505,11 +503,9 @@ class HostAPITestCase(test.NoDBTestCase):
@mock.patch.object(nova_obj.API, 'hypervisor_search')
@mock.patch.object(host_obj.Host, 'save')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_update(self, mock_segment_get, mock_get,
mock_update, mock_hypervisor_search, mock_host_obj,
mock_is_under_recovery):
mock_segment_get.return_value = self.failover_segment
def test_update(
self, mock_get, mock_update, mock_hypervisor_search,
mock_host_obj, mock_is_under_recovery):
host_data = {"name": "host_1"}
mock_get.return_value = self.host
mock_host_obj.return_value = self.host
@ -525,11 +521,8 @@ class HostAPITestCase(test.NoDBTestCase):
'is_under_recovery')
@mock.patch.object(nova_obj.API, 'hypervisor_search')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_update_with_non_existing_host(self, mock_segment_get, mock_get,
mock_hypervisor_search,
mock_is_under_recovery):
mock_segment_get.return_value = self.failover_segment
def test_update_with_non_existing_host(
self, mock_get, mock_hypervisor_search, mock_is_under_recovery):
host_data = {"name": "host-2"}
mock_get.return_value = self.host
mock_hypervisor_search.side_effect = (
@ -547,12 +540,9 @@ class HostAPITestCase(test.NoDBTestCase):
@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')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_host_update_exception(self, mock_segment_get, mock_get,
mock_notify_about_host_api,
mock_host_obj, mock_hypervisor_search,
mock_is_under_recovery):
mock_segment_get.return_value = self.failover_segment
def test_host_update_exception(
self, mock_get, mock_notify_about_host_api, mock_host_obj,
mock_hypervisor_search, mock_is_under_recovery):
host_data = {"name": "host_1"}
mock_get.return_value = self.host
e = exception.InvalidInput(reason="TEST")
@ -579,10 +569,9 @@ class HostAPITestCase(test.NoDBTestCase):
'is_under_recovery')
@mock.patch.object(host_obj, 'Host')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_update_host_under_recovery(self, mock_segment_get, mock_get,
mock_host_obj, mock_is_under_recovery, mock_HostInUse):
mock_segment_get.return_value = self.failover_segment
def test_update_host_under_recovery(
self, mock_get, mock_host_obj, mock_is_under_recovery,
mock_HostInUse):
host_data = {"name": "host_1"}
mock_get.return_value = self.host
mock_host_obj.return_value = self.host
@ -596,13 +585,14 @@ class HostAPITestCase(test.NoDBTestCase):
@mock.patch.object(api_utils, 'notify_about_host_api')
@mock.patch.object(segment_obj.FailoverSegment,
'is_under_recovery')
@mock.patch('masakari.objects.base.MasakariObject'
'.masakari_obj_get_changes')
@mock.patch.object(host_obj.Host, '_from_db_object')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch('masakari.db.host_update')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_update_convert_boolean_attributes(
self, mock_segment, mock_host_update, mock_host_object,
mock__from_db_object, mock_is_under_recovery,
self, mock_host_update, mock_host_object, mock__from_db_object,
mock_masakari_obj_get_changes, mock_is_under_recovery,
mock_notify_about_host_api):
host_data = {
"reserved": 'Off',
@ -612,22 +602,23 @@ class HostAPITestCase(test.NoDBTestCase):
expected_data = {
'name': 'host_1', 'uuid': uuidsentinel.fake_host_1,
'on_maintenance': True,
'failover_segment_id': uuidsentinel.fake_segment1,
'failover_segment': self.failover_segment,
'reserved': False, 'type': 'fake',
'control_attributes': 'fake-control_attributes'
}
mock_segment.return_value = self.failover_segment
mock_masakari_obj_get_changes.return_value = host_data
mock_host_object.return_value = self.host
self.host._context = self.context
mock_is_under_recovery.return_value = False
result = self.host_api.update_host(self.context,
uuidsentinel.fake_segment1,
uuidsentinel.fake_segment,
uuidsentinel.fake_host_1,
host_data)
mock_host_update.assert_called_with(self.context,
uuidsentinel.fake_host_1,
expected_data)
host_data)
mock_host_update.return_value = expected_data
action = fields.EventNotificationAction.HOST_UPDATE
phase_start = fields.EventNotificationPhase.START
phase_end = fields.EventNotificationPhase.END
@ -642,10 +633,8 @@ class HostAPITestCase(test.NoDBTestCase):
'is_under_recovery')
@mock.patch.object(host_obj.Host, 'destroy')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_delete_host(self, mock_segment_get, mock_get,
mock_segment_destroy, mock_is_under_recovery):
mock_segment_get.return_value = self.failover_segment
def test_delete_host(
self, mock_get, mock_segment_destroy, mock_is_under_recovery):
mock_get.return_value = self.host
mock_is_under_recovery.return_value = False
@ -659,11 +648,9 @@ class HostAPITestCase(test.NoDBTestCase):
@mock.patch.object(host_obj.Host, 'destroy')
@mock.patch.object(api_utils, 'notify_about_host_api')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_host_delete_exception(self, mock_segment_get, mock_get,
mock_notify_about_host_api,
mock_host_destroy, mock_is_under_recovery):
mock_segment_get.return_value = self.failover_segment
def test_host_delete_exception(
self, mock_get, mock_notify_about_host_api, mock_host_destroy,
mock_is_under_recovery):
mock_get.return_value = self.host
mock_is_under_recovery.return_value = False
e = exception.InvalidInput(reason="TEST")
@ -689,10 +676,9 @@ class HostAPITestCase(test.NoDBTestCase):
'is_under_recovery')
@mock.patch.object(host_obj, 'Host')
@mock.patch.object(host_obj.Host, 'get_by_uuid')
@mock.patch.object(segment_obj.FailoverSegment, 'get_by_uuid')
def test_delete_host_under_recovery(self, mock_segment_get, mock_get,
mock_host_obj, mock_is_under_recovery, mock_HostInUse):
mock_segment_get.return_value = self.failover_segment
def test_delete_host_under_recovery(
self, mock_get, mock_host_obj, mock_is_under_recovery,
mock_HostInUse):
mock_get.return_value = self.host
mock_is_under_recovery.return_value = True
mock_HostInUse.return_value = self.exception_in_use
@ -712,11 +698,15 @@ class NotificationAPITestCase(test.NoDBTestCase):
self.req = fakes.HTTPRequest.blank('/v1/notifications',
use_admin_context=True)
self.context = self.req.environ['masakari.context']
self.failover_segment = fakes_data.create_fake_failover_segment(
name="segment1", id=1, description="something",
service_type="COMPUTE", recovery_method="auto",
uuid=uuidsentinel.fake_segment
)
self.host = fakes_data.create_fake_host(
name="host_1", id=1, reserved=False, on_maintenance=False,
type="fake", control_attributes="fake-control_attributes",
uuid=uuidsentinel.fake_host_1,
failover_segment_id=uuidsentinel.fake_segment1
uuid=uuidsentinel.fake_host_1
)
self.notification = fakes_data.create_fake_notification(
type="VM", id=1, payload={

View File

@ -23,7 +23,7 @@ from masakari import db
from masakari import exception
from masakari.objects import fields
from masakari.objects import host
from masakari.objects import segment
from masakari.tests.unit import fakes as fakes_data
from masakari.tests.unit.objects import test_objects
from masakari.tests import uuidsentinel
@ -56,8 +56,7 @@ def _fake_host(**kwargs):
'on_maintenance': False,
'control_attributes': 'fake_control_attributes',
'type': 'SSH',
'failover_segment': fake_segment_dict,
'failover_segment_id': uuidsentinel.fake_segment,
'failover_segment': fake_segment_dict
}
fake_host.update(kwargs)
return fake_host
@ -107,9 +106,15 @@ class TestHostObject(test_objects._LocalTest):
def _host_create_attributes(self):
failover_segment = fakes_data.create_fake_failover_segment(
name="fake_segment", id=123, description="fakefake",
service_type="COMPUTE", recovery_method="auto",
uuid=uuidsentinel.fake_segment
)
host_obj = host.Host(context=self.context)
host_obj.name = 'foo-host'
host_obj.failover_segment_id = uuidsentinel.fake_segment
host_obj.failover_segment = failover_segment
host_obj.type = 'fake-type'
host_obj.reserved = False
host_obj.on_maintenance = False
@ -225,24 +230,30 @@ class TestHostObject(test_objects._LocalTest):
self.context, limit=5, marker=host_name)
@mock.patch.object(api_utils, 'notify_about_host_api')
@mock.patch('masakari.objects.base.MasakariObject'
'.masakari_obj_get_changes')
@mock.patch.object(db, 'host_update')
def test_save(self, mock_host_update, mock_notify_about_host_api):
def test_save(
self, mock_host_update, mock_masakari_obj_get_changes,
mock_notify_about_host_api):
mock_host_update.return_value = fake_host
host_data = {
'name': 'foo-host',
"uuid": uuidsentinel.fake_host,
'id': 123
}
mock_masakari_obj_get_changes.return_value = host_data
host_obj = self._host_create_attributes()
host_obj.id = 123
host_obj.save()
self._compare_segment_and_host_data(host_obj)
(mock_host_update.
assert_called_once_with(self.context, uuidsentinel.fake_host,
{'control_attributes': u'fake_attributes',
'type': u'fake-type',
'failover_segment_id':
uuidsentinel.fake_segment,
'name': u'foo-host',
'uuid': uuidsentinel.fake_host,
'reserved': False, 'on_maintenance': False
{'name': 'foo-host',
'uuid': uuidsentinel.fake_host
}))
action = fields.EventNotificationAction.HOST_UPDATE
phase_start = fields.EventNotificationPhase.START
@ -260,8 +271,6 @@ class TestHostObject(test_objects._LocalTest):
mock_host_update.return_value = fake_host
host_obj = self._host_create_attributes()
host_obj.failover_segment = (segment.
FailoverSegment(context=self.context))
host_obj.id = 123
self.assertRaises(exception.ObjectActionError, host_obj.save)
@ -304,3 +313,23 @@ class TestHostObject(test_objects._LocalTest):
mock.call(self.context, host_object, action=action,
phase=phase_start)]
mock_notify_about_host_api.assert_has_calls(notify_calls)
def _host_create_attributes2(self):
failover_segment = fakes_data.create_fake_failover_segment(
name="fake_segment", id=123, description="fakefake",
service_type="COMPUTE", recovery_method="auto",
uuid=uuidsentinel.fake_segment
)
host_obj = self._host_create_attributes()
host_obj.failover_segment_id = failover_segment.uuid
return host_obj
def test_obj_make_compatible(self):
host_obj = self._host_create_attributes2()
primitive = host_obj.obj_to_primitive()
self.assertIn('failover_segment', primitive['masakari_object.data'])
self.assertNotIn(
'failover_segment_id', primitive['masakari_object.data'])

View File

@ -655,7 +655,7 @@ class TestRegistry(test.NoDBTestCase):
object_data = {
'FailoverSegment': '1.0-5e8b8bc8840b35439b5f2b621482d15d',
'FailoverSegmentList': '1.0-dfc5c6f5704d24dcaa37b0bbb03cbe60',
'Host': '1.1-3fc4d548fa220c76906426095e5971fc',
'Host': '1.2-f05735b156b687bc916d46b551bc45e3',
'HostList': '1.0-25ebe1b17fbd9f114fae8b6a10d198c0',
'Notification': '1.1-91e3a051078e35300e325a3e2ae5fde5',
'NotificationProgressDetails': '1.0-fc611ac932b719fbc154dbe34bb8edee',
@ -664,8 +664,8 @@ object_data = {
'ExceptionNotification': '1.0-1187e93f564c5cca692db76a66cda2a6',
'ExceptionPayload': '1.0-96f178a12691e3ef0d8e3188fc481b90',
'HostApiNotification': '1.0-1187e93f564c5cca692db76a66cda2a6',
'HostApiPayload': '1.0-ca9035d81cec6697f12dd4cac4c8f027',
'HostApiPayloadBase': '1.0-211379087a876212df6194b011207339',
'HostApiPayload': '1.0-20603e23a06477975b860459ba684d34',
'HostApiPayloadBase': '1.1-e02e80de08d0b584ee7a0a0320bfd029',
'NotificationApiPayload': '1.0-c050869a1f4aed23e7645bd4d1830ecd',
'NotificationApiPayloadBase': '1.0-cda8d53a77e64f83e3782fc9c4d499bb',
'NotificationApiNotification': '1.0-1187e93f564c5cca692db76a66cda2a6',