nova/nova/tests/unit/api/openstack/compute/test_server_migrations.py

421 lines
18 KiB
Python

# Copyright 2016 OpenStack Foundation
# 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.
import copy
import datetime
import mock
from oslo_utils.fixture import uuidsentinel as uuids
import six
import webob
from nova.api.openstack.compute import server_migrations
from nova import exception
from nova import objects
from nova.objects import base
from nova import test
from nova.tests.unit.api.openstack import fakes
SERVER_UUID = uuids.server_uuid
fake_migrations = [
{
'id': 1234,
'source_node': 'node1',
'dest_node': 'node2',
'source_compute': 'compute1',
'dest_compute': 'compute2',
'dest_host': '1.2.3.4',
'status': 'running',
'instance_uuid': SERVER_UUID,
'old_instance_type_id': 1,
'new_instance_type_id': 2,
'migration_type': 'live-migration',
'hidden': False,
'memory_total': 123456,
'memory_processed': 12345,
'memory_remaining': 111111,
'disk_total': 234567,
'disk_processed': 23456,
'disk_remaining': 211111,
'created_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
'updated_at': datetime.datetime(2012, 10, 29, 13, 42, 2),
'deleted_at': None,
'deleted': False,
'uuid': uuids.migration1,
},
{
'id': 5678,
'source_node': 'node10',
'dest_node': 'node20',
'source_compute': 'compute10',
'dest_compute': 'compute20',
'dest_host': '5.6.7.8',
'status': 'running',
'instance_uuid': SERVER_UUID,
'old_instance_type_id': 5,
'new_instance_type_id': 6,
'migration_type': 'live-migration',
'hidden': False,
'memory_total': 456789,
'memory_processed': 56789,
'memory_remaining': 400000,
'disk_total': 96789,
'disk_processed': 6789,
'disk_remaining': 90000,
'created_at': datetime.datetime(2013, 10, 22, 13, 42, 2),
'updated_at': datetime.datetime(2013, 10, 22, 13, 42, 2),
'deleted_at': None,
'deleted': False,
'uuid': uuids.migration2,
}
]
migrations_obj = base.obj_make_list(
'fake-context',
objects.MigrationList(),
objects.Migration,
fake_migrations)
class ServerMigrationsTestsV21(test.NoDBTestCase):
wsgi_api_version = '2.22'
def setUp(self):
super(ServerMigrationsTestsV21, self).setUp()
self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version)
self.context = self.req.environ['nova.context']
self.controller = server_migrations.ServerMigrationsController()
self.compute_api = self.controller.compute_api
def test_force_complete_succeeded(self):
@mock.patch.object(self.compute_api, 'live_migrate_force_complete')
@mock.patch.object(self.compute_api, 'get')
def _do_test(compute_api_get, live_migrate_force_complete):
self.controller._force_complete(self.req, '1', '1',
body={'force_complete': None})
live_migrate_force_complete.assert_called_once_with(
self.context, compute_api_get(), '1')
_do_test()
def _test_force_complete_failed_with_exception(self, fake_exc,
expected_exc):
@mock.patch.object(self.compute_api, 'live_migrate_force_complete',
side_effect=fake_exc)
@mock.patch.object(self.compute_api, 'get')
def _do_test(compute_api_get, live_migrate_force_complete):
self.assertRaises(expected_exc,
self.controller._force_complete,
self.req, '1', '1',
body={'force_complete': None})
_do_test()
def test_force_complete_instance_not_migrating(self):
self._test_force_complete_failed_with_exception(
exception.InstanceInvalidState(instance_uuid='', state='',
attr='', method=''),
webob.exc.HTTPConflict)
def test_force_complete_migration_not_found(self):
self._test_force_complete_failed_with_exception(
exception.MigrationNotFoundByStatus(instance_id='', status=''),
webob.exc.HTTPBadRequest)
def test_force_complete_instance_is_locked(self):
self._test_force_complete_failed_with_exception(
exception.InstanceIsLocked(instance_uuid=''),
webob.exc.HTTPConflict)
def test_force_complete_invalid_migration_state(self):
self._test_force_complete_failed_with_exception(
exception.InvalidMigrationState(migration_id='', instance_uuid='',
state='', method=''),
webob.exc.HTTPBadRequest)
def test_force_complete_instance_not_found(self):
self._test_force_complete_failed_with_exception(
exception.InstanceNotFound(instance_id=''),
webob.exc.HTTPNotFound)
def test_force_complete_unexpected_error(self):
self._test_force_complete_failed_with_exception(
exception.NovaException(),
webob.exc.HTTPInternalServerError)
class ServerMigrationsTestsV223(ServerMigrationsTestsV21):
wsgi_api_version = '2.23'
def setUp(self):
super(ServerMigrationsTestsV223, self).setUp()
self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version,
use_admin_context=True)
self.context = self.req.environ['nova.context']
@mock.patch('nova.compute.api.API.get_migrations_in_progress_by_instance')
@mock.patch('nova.compute.api.API.get')
def test_index(self, m_get_instance, m_get_mig):
migrations = [server_migrations.output(mig) for mig in migrations_obj]
migrations_in_progress = {'migrations': migrations}
for mig in migrations_in_progress['migrations']:
self.assertIn('id', mig)
self.assertNotIn('deleted', mig)
self.assertNotIn('deleted_at', mig)
m_get_mig.return_value = migrations_obj
response = self.controller.index(self.req, SERVER_UUID)
self.assertEqual(migrations_in_progress, response)
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
@mock.patch('nova.compute.api.API.get')
def test_index_invalid_instance(self, m_get_instance):
m_get_instance.side_effect = exception.InstanceNotFound(instance_id=1)
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.index,
self.req, SERVER_UUID)
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
@mock.patch('nova.compute.api.API.get')
def test_show(self, m_get_instance, m_get_mig):
migrations = [server_migrations.output(mig) for mig in migrations_obj]
m_get_mig.return_value = migrations_obj[0]
response = self.controller.show(self.req, SERVER_UUID,
migrations_obj[0].id)
self.assertEqual(migrations[0], response['migration'])
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
@mock.patch('nova.compute.api.API.get')
def test_show_migration_non_progress(self, m_get_instance, m_get_mig):
non_progress_mig = copy.deepcopy(migrations_obj[0])
non_progress_mig.status = "reverted"
m_get_mig.return_value = non_progress_mig
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show,
self.req, SERVER_UUID,
non_progress_mig.id)
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
@mock.patch('nova.compute.api.API.get')
def test_show_migration_not_live_migration(self, m_get_instance,
m_get_mig):
non_progress_mig = copy.deepcopy(migrations_obj[0])
non_progress_mig.migration_type = "resize"
m_get_mig.return_value = non_progress_mig
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show,
self.req, SERVER_UUID,
non_progress_mig.id)
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
@mock.patch('nova.compute.api.API.get_migration_by_id_and_instance')
@mock.patch('nova.compute.api.API.get')
def test_show_migration_not_exist(self, m_get_instance, m_get_mig):
m_get_mig.side_effect = exception.MigrationNotFoundForInstance(
migration_id=migrations_obj[0].id,
instance_id=SERVER_UUID)
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show,
self.req, SERVER_UUID,
migrations_obj[0].id)
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
@mock.patch('nova.compute.api.API.get')
def test_show_migration_invalid_instance(self, m_get_instance):
m_get_instance.side_effect = exception.InstanceNotFound(instance_id=1)
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.show,
self.req, SERVER_UUID,
migrations_obj[0].id)
m_get_instance.assert_called_once_with(self.context, SERVER_UUID,
expected_attrs=None,
cell_down_support=False)
class ServerMigrationsTestsV224(ServerMigrationsTestsV21):
wsgi_api_version = '2.24'
def setUp(self):
super(ServerMigrationsTestsV224, self).setUp()
self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version,
use_admin_context=True)
self.context = self.req.environ['nova.context']
def test_cancel_live_migration_succeeded(self):
@mock.patch.object(self.compute_api, 'live_migrate_abort')
@mock.patch.object(self.compute_api, 'get')
def _do_test(mock_get, mock_abort):
self.controller.delete(self.req, 'server-id', 'migration-id')
mock_abort.assert_called_once_with(self.context,
mock_get(),
'migration-id',
support_abort_in_queue=False)
_do_test()
def _test_cancel_live_migration_failed(self, fake_exc, expected_exc):
@mock.patch.object(self.compute_api, 'live_migrate_abort',
side_effect=fake_exc)
@mock.patch.object(self.compute_api, 'get')
def _do_test(mock_get, mock_abort):
self.assertRaises(expected_exc,
self.controller.delete,
self.req,
'server-id',
'migration-id')
_do_test()
def test_cancel_live_migration_invalid_state(self):
self._test_cancel_live_migration_failed(
exception.InstanceInvalidState(instance_uuid='',
state='',
attr='',
method=''),
webob.exc.HTTPConflict)
def test_cancel_live_migration_migration_not_found(self):
self._test_cancel_live_migration_failed(
exception.MigrationNotFoundForInstance(migration_id='',
instance_id=''),
webob.exc.HTTPNotFound)
def test_cancel_live_migration_invalid_migration_state(self):
self._test_cancel_live_migration_failed(
exception.InvalidMigrationState(migration_id='',
instance_uuid='',
state='',
method=''),
webob.exc.HTTPBadRequest)
def test_cancel_live_migration_instance_not_found(self):
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.delete,
self.req,
'server-id',
'migration-id')
class ServerMigrationsTestsV265(ServerMigrationsTestsV224):
wsgi_api_version = '2.65'
def test_cancel_live_migration_succeeded(self):
@mock.patch.object(self.compute_api, 'live_migrate_abort')
@mock.patch.object(self.compute_api, 'get')
def _do_test(mock_get, mock_abort):
self.controller.delete(self.req, 'server-id', 1)
mock_abort.assert_called_once_with(self.context,
mock_get.return_value, 1,
support_abort_in_queue=True)
_do_test()
def test_cancel_live_migration_in_queue_not_yet_available(self):
exc = exception.AbortQueuedLiveMigrationNotYetSupported(
migration_id=1, status='queued')
@mock.patch.object(self.compute_api, 'live_migrate_abort',
side_effect=exc)
@mock.patch.object(self.compute_api, 'get')
def _do_test(mock_get, mock_abort):
error = self.assertRaises(webob.exc.HTTPConflict,
self.controller.delete,
self.req, 'server-id', 1)
self.assertIn("Aborting live migration 1 with status queued is "
"not yet supported for this instance.",
six.text_type(error))
mock_abort.assert_called_once_with(self.context,
mock_get.return_value, 1,
support_abort_in_queue=True)
_do_test()
class ServerMigrationsPolicyEnforcementV21(test.NoDBTestCase):
wsgi_api_version = '2.22'
def setUp(self):
super(ServerMigrationsPolicyEnforcementV21, self).setUp()
self.controller = server_migrations.ServerMigrationsController()
self.req = fakes.HTTPRequest.blank('', version=self.wsgi_api_version)
def test_force_complete_policy_failed(self):
rule_name = "os_compute_api:servers:migrations:force_complete"
self.policy.set_rules({rule_name: "project:non_fake"})
body_args = {'force_complete': None}
exc = self.assertRaises(exception.PolicyNotAuthorized,
self.controller._force_complete, self.req,
fakes.FAKE_UUID, fakes.FAKE_UUID,
body=body_args)
self.assertEqual(
"Policy doesn't allow %s to be performed." % rule_name,
exc.format_message())
class ServerMigrationsPolicyEnforcementV223(
ServerMigrationsPolicyEnforcementV21):
wsgi_api_version = '2.23'
def test_migration_index_failed(self):
rule_name = "os_compute_api:servers:migrations:index"
self.policy.set_rules({rule_name: "project:non_fake"})
exc = self.assertRaises(exception.PolicyNotAuthorized,
self.controller.index, self.req,
fakes.FAKE_UUID)
self.assertEqual("Policy doesn't allow %s to be performed." %
rule_name, exc.format_message())
def test_migration_show_failed(self):
rule_name = "os_compute_api:servers:migrations:show"
self.policy.set_rules({rule_name: "project:non_fake"})
exc = self.assertRaises(exception.PolicyNotAuthorized,
self.controller.show, self.req,
fakes.FAKE_UUID, 1)
self.assertEqual("Policy doesn't allow %s to be performed." %
rule_name, exc.format_message())
class ServerMigrationsPolicyEnforcementV224(
ServerMigrationsPolicyEnforcementV223):
wsgi_api_version = '2.24'
def test_migrate_delete_failed(self):
rule_name = "os_compute_api:servers:migrations:delete"
self.policy.set_rules({rule_name: "project:non_fake"})
self.assertRaises(exception.PolicyNotAuthorized,
self.controller.delete, self.req,
fakes.FAKE_UUID, '10')