Files
nova-powervm/nova_powervm/tests/virt/powervm/test_live_migration.py
esberglu c1a17ca319 Replace TestCase with NoDBTestCase
It was suggested that we replace TestCase with NoDBTestCase [1]. This
does two things. First it prevents the test case setup from running the
database schema migrations unnecessarily which should improve
performance. Second it will cause the test to fail if the code does
touch the DB when the developer is claiming that it shouldn't.

[1] https://review.openstack.org/#/c/409404/46/nova/tests/unit/virt/powervm/test_media.py

Change-Id: I8bcc9cbdcadd9fe89a4450ca405387899baa9fc3
2017-11-13 10:08:31 -06:00

315 lines
14 KiB
Python

# Copyright 2015, 2017 IBM Corp.
#
# 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 __future__ import absolute_import
import fixtures
import mock
from nova import exception
from nova import objects
from nova.objects import migrate_data as mig_obj
from nova import test
from nova.tests.unit import fake_network
from nova_powervm.tests.virt import powervm
from nova_powervm.tests.virt.powervm import fixtures as fx
from nova_powervm.virt.powervm import live_migration as lpm
class TestLPM(test.NoDBTestCase):
def setUp(self):
super(TestLPM, self).setUp()
self.flags(disk_driver='localdisk', group='powervm')
self.drv_fix = self.useFixture(fx.PowerVMComputeDriver())
self.drv = self.drv_fix.drv
self.apt = self.drv.adapter
self.inst = objects.Instance(**powervm.TEST_INSTANCE)
self.network_infos = fake_network.fake_get_instance_nw_info(self, 1)
self.inst.info_cache = objects.InstanceInfoCache(
network_info=self.network_infos)
self.mig_data = mig_obj.PowerVMLiveMigrateData()
self.mig_data.host_mig_data = {}
self.mig_data.dest_ip = '1'
self.mig_data.dest_user_id = 'neo'
self.mig_data.dest_sys_name = 'a'
self.mig_data.public_key = 'PublicKey'
self.mig_data.dest_proc_compat = 'a,b,c'
self.mig_data.vol_data = {}
self.mig_data.vea_vlan_mappings = {}
self.lpmsrc = lpm.LiveMigrationSrc(self.drv, self.inst, self.mig_data)
self.lpmdst = lpm.LiveMigrationDest(self.drv, self.inst)
self.add_key = self.useFixture(fixtures.MockPatch(
'pypowervm.tasks.management_console.add_authorized_key')).mock
self.get_key = self.useFixture(fixtures.MockPatch(
'pypowervm.tasks.management_console.get_public_key')).mock
self.get_key.return_value = 'PublicKey'
# Short path to the host's migration_data
self.host_mig_data = self.drv.host_wrapper.migration_data
@mock.patch('pypowervm.tasks.storage.ScrubOrphanStorageForLpar')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM')
@mock.patch('nova_powervm.virt.powervm.vm.get_instance_wrapper')
@mock.patch('pypowervm.tasks.vterm.close_vterm')
def test_lpm_source(self, mock_vterm_close, mock_get_wrap,
mock_cd, mock_scrub):
self.host_mig_data['active_migrations_supported'] = 4
self.host_mig_data['active_migrations_in_progress'] = 2
with mock.patch.object(
self.lpmsrc, '_check_migration_ready', return_value=None):
# Test the bad path first, then patch in values to make succeed
mock_wrap = mock.Mock(id=123)
mock_get_wrap.return_value = mock_wrap
self.assertRaises(exception.MigrationPreCheckError,
self.lpmsrc.check_source, 'context',
'block_device_info', [])
# Patch the proc compat fields, to get further
pm = mock.PropertyMock(return_value='b')
type(mock_wrap).proc_compat_mode = pm
self.assertRaises(exception.MigrationPreCheckError,
self.lpmsrc.check_source, 'context',
'block_device_info', [])
pm = mock.PropertyMock(return_value='Not_Migrating')
type(mock_wrap).migration_state = pm
# Get a volume driver.
mock_vol_drv = mock.MagicMock()
# Finally, good path.
self.lpmsrc.check_source('context', 'block_device_info',
[mock_vol_drv])
# Ensure we built a scrubber.
mock_scrub.assert_called_with(mock.ANY, 123)
# Ensure we added the subtasks to remove the vopts.
mock_cd.return_value.dlt_vopt.assert_called_once_with(
mock.ANY, stg_ftsk=mock_scrub.return_value,
remove_mappings=False)
# And ensure the scrubber was executed
mock_scrub.return_value.execute.assert_called_once_with()
mock_vol_drv.pre_live_migration_on_source.assert_called_once_with(
{})
# Ensure migration counts are validated
self.host_mig_data['active_migrations_in_progress'] = 4
self.assertRaises(exception.MigrationPreCheckError,
self.lpmsrc.check_source, 'context',
'block_device_info', [])
# Ensure the vterm was closed
mock_vterm_close.assert_called_once_with(
self.apt, mock_wrap.uuid)
def test_lpm_dest(self):
src_compute_info = {'stats': {'memory_region_size': 1}}
dst_compute_info = {'stats': {'memory_region_size': 1}}
self.host_mig_data['active_migrations_supported'] = 4
self.host_mig_data['active_migrations_in_progress'] = 2
with mock.patch.object(self.drv.host_wrapper, 'refresh') as mock_rfh:
self.lpmdst.check_destination(
'context', src_compute_info, dst_compute_info)
mock_rfh.assert_called_once_with()
# Ensure migration counts are validated
self.host_mig_data['active_migrations_in_progress'] = 4
self.assertRaises(exception.MigrationPreCheckError,
self.lpmdst.check_destination, 'context',
src_compute_info, dst_compute_info)
# Repair the stat
self.host_mig_data['active_migrations_in_progress'] = 2
# Ensure diff memory sizes raises an exception
dst_compute_info['stats']['memory_region_size'] = 2
self.assertRaises(exception.MigrationPreCheckError,
self.lpmdst.check_destination, 'context',
src_compute_info, dst_compute_info)
@mock.patch('pypowervm.tasks.storage.ComprehensiveScrub')
@mock.patch('nova_powervm.virt.powervm.vif.'
'pre_live_migrate_at_destination')
def test_pre_live_mig(self, mock_vif_pre, mock_scrub):
vol_drv = mock.MagicMock()
network_infos = [{'type': 'pvm_sea'}]
def update_vea_mapping(adapter, host_uuid, instance, network_info,
vea_vlan_mappings):
# Make sure what comes in is None, but that we change it.
self.assertEqual(vea_vlan_mappings, {})
vea_vlan_mappings['test'] = 'resp'
mock_vif_pre.side_effect = update_vea_mapping
resp = self.lpmdst.pre_live_migration(
'context', 'block_device_info', network_infos, 'disk_info',
self.mig_data, [vol_drv])
# Make sure the pre_live_migrate_at_destination was invoked for the vif
mock_vif_pre.assert_called_once_with(
self.drv.adapter, self.drv.host_uuid, self.inst, network_infos[0],
mock.ANY)
self.assertEqual({'test': 'resp'}, self.mig_data.vea_vlan_mappings)
# Make sure we get something back, and that the volume driver was
# invoked.
self.assertIsNotNone(resp)
vol_drv.pre_live_migration_on_destination.assert_called_once_with(
self.mig_data.vol_data)
self.assertEqual(1, mock_scrub.call_count)
self.add_key.assert_called_once_with(self.apt, 'PublicKey')
vol_drv.reset_mock()
raising_vol_drv = mock.Mock()
raising_vol_drv.pre_live_migration_on_destination.side_effect = (
Exception('foo'))
self.assertRaises(
exception.MigrationPreCheckError, self.lpmdst.pre_live_migration,
'context', 'block_device_info', network_infos, 'disk_info',
self.mig_data, [vol_drv, raising_vol_drv])
vol_drv.pre_live_migration_on_destination.assert_called_once_with({})
(raising_vol_drv.pre_live_migration_on_destination.
assert_called_once_with({}))
def test_src_cleanup(self):
vol_drv = mock.Mock()
self.lpmdst.cleanup_volume(vol_drv)
# Ensure the volume driver is not called
self.assertEqual(0, vol_drv.cleanup_volume_at_destination.call_count)
def test_src_cleanup_valid(self):
vol_drv = mock.Mock()
self.lpmdst.pre_live_vol_data = {'vscsi-vol-id': 'fake_udid'}
self.lpmdst.cleanup_volume(vol_drv)
# Ensure the volume driver was called to clean up the volume.
vol_drv.cleanup_volume_at_destination.assert_called_once()
@mock.patch('pypowervm.tasks.migration.migrate_lpar')
@mock.patch('nova_powervm.virt.powervm.live_migration.LiveMigrationSrc.'
'_convert_nl_io_mappings')
@mock.patch('nova_powervm.virt.powervm.vif.pre_live_migrate_at_source')
def test_live_migration(self, mock_vif_pre_lpm, mock_convert_mappings,
mock_migr):
mock_trunk = mock.MagicMock()
mock_vif_pre_lpm.return_value = [mock_trunk]
mock_convert_mappings.return_value = ['AABBCCDDEEFF/5']
self.lpmsrc.lpar_w = mock.Mock()
self.lpmsrc.live_migration('context', self.mig_data)
mock_migr.assert_called_once_with(
self.lpmsrc.lpar_w, 'a', sdn_override=True, tgt_mgmt_svr='1',
tgt_mgmt_usr='neo', validate_only=False,
virtual_fc_mappings=None, virtual_scsi_mappings=None,
vlan_check_override=True, vlan_mappings=['AABBCCDDEEFF/5'])
# Network assertions
mock_vif_pre_lpm.assert_called_once_with(
self.drv.adapter, self.drv.host_uuid, self.inst, mock.ANY)
mock_trunk.delete.assert_called_once()
# Test that we raise errors received during migration
mock_migr.side_effect = ValueError()
self.assertRaises(ValueError, self.lpmsrc.live_migration, 'context',
self.mig_data)
mock_migr.assert_called_with(
self.lpmsrc.lpar_w, 'a', sdn_override=True, tgt_mgmt_svr='1',
tgt_mgmt_usr='neo', validate_only=False,
virtual_fc_mappings=None, virtual_scsi_mappings=None,
vlan_mappings=['AABBCCDDEEFF/5'], vlan_check_override=True)
def test_convert_nl_io_mappings(self):
# Test simple None case
self.assertIsNone(self.lpmsrc._convert_nl_io_mappings(None))
# Do some mappings
test_mappings = {'aa:bb:cc:dd:ee:ff': 5, 'aa:bb:cc:dd:ee:ee': 126}
expected = ['AABBCCDDEEFF/5', 'AABBCCDDEEEE/126']
self.assertEqual(
set(expected),
set(self.lpmsrc._convert_nl_io_mappings(test_mappings)))
@mock.patch('pypowervm.tasks.migration.migrate_recover')
def test_rollback(self, mock_migr):
self.lpmsrc.lpar_w = mock.Mock()
# Test no need to rollback
self.lpmsrc.lpar_w.migration_state = 'Not_Migrating'
self.lpmsrc.rollback_live_migration('context')
self.assertTrue(self.lpmsrc.lpar_w.refresh.called)
self.assertFalse(mock_migr.called)
# Test calling the rollback
self.lpmsrc.lpar_w.reset_mock()
self.lpmsrc.lpar_w.migration_state = 'Pretend its Migrating'
self.lpmsrc.rollback_live_migration('context')
self.assertTrue(self.lpmsrc.lpar_w.refresh.called)
mock_migr.assert_called_once_with(self.lpmsrc.lpar_w, force=True)
# Test exception from rollback
mock_migr.reset_mock()
self.lpmsrc.lpar_w.reset_mock()
mock_migr.side_effect = ValueError()
self.lpmsrc.rollback_live_migration('context')
self.assertTrue(self.lpmsrc.lpar_w.refresh.called)
mock_migr.assert_called_once_with(self.lpmsrc.lpar_w, force=True)
def test_check_migration_ready(self):
lpar_w, host_w = mock.Mock(), mock.Mock()
lpar_w.can_lpm.return_value = (True, None)
self.lpmsrc._check_migration_ready(lpar_w, host_w)
lpar_w.can_lpm.assert_called_once_with(host_w, migr_data={})
lpar_w.can_lpm.return_value = (False, 'This is the reason message.')
self.assertRaises(exception.MigrationPreCheckError,
self.lpmsrc._check_migration_ready, lpar_w, host_w)
@mock.patch('pypowervm.tasks.migration.migrate_abort')
def test_migration_abort(self, mock_mig_abort):
self.lpmsrc.lpar_w = mock.Mock()
self.lpmsrc.migration_abort()
mock_mig_abort.called_once_with(self.lpmsrc.lpar_w)
@mock.patch('pypowervm.tasks.migration.migrate_recover')
def test_migration_recover(self, mock_mig_recover):
self.lpmsrc.lpar_w = mock.Mock()
self.lpmsrc.migration_recover()
mock_mig_recover.called_once_with(self.lpmsrc.lpar_w, force=True)
@mock.patch('nova_powervm.virt.powervm.vif.post_live_migrate_at_source')
def test_post_live_migration_at_source(self, mock_vif_post_lpm_at_source):
network_infos = [{'devname': 'tap-dev1', 'address': 'mac-addr1',
'network': {'bridge': 'br-int'}, 'id': 'vif_id_1'},
{'devname': 'tap-dev2', 'address': 'mac-addr2',
'network': {'bridge': 'br-int'}, 'id': 'vif_id_2'}]
self.lpmsrc.post_live_migration_at_source(network_infos)
# Assertions
for network_info in network_infos:
mock_vif_post_lpm_at_source.assert_any_call(mock.ANY, mock.ANY,
mock.ANY, network_info)