nova-powervm/nova_powervm/tests/virt/powervm/test_driver.py

1630 lines
71 KiB
Python

# Copyright 2014, 2016 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.
#
import logging
import mock
from oslo_serialization import jsonutils
from nova import block_device as nova_block_device
from nova.compute import task_states
from nova import exception as exc
from nova import objects
from nova.objects import base as obj_base
from nova.objects import block_device as bdmobj
from nova import test
from nova.tests.unit import fake_instance
from nova.virt import block_device as nova_virt_bdm
from nova.virt import driver as virt_driver
from nova.virt import fake
import pypowervm.adapter as pvm_adp
import pypowervm.exceptions as pvm_exc
import pypowervm.tests.test_fixtures as pvm_fx
from pypowervm.tests.test_utils import pvmhttp
import pypowervm.utils.transaction as pvm_tx
import pypowervm.wrappers.base_partition as pvm_bp
import pypowervm.wrappers.logical_partition as pvm_lpar
import pypowervm.wrappers.managed_system as pvm_ms
import pypowervm.wrappers.virtual_io_server as pvm_vios
from nova_powervm.tests.virt import powervm
from nova_powervm.tests.virt.powervm import fixtures as fx
from nova_powervm.virt.powervm import driver
from nova_powervm.virt.powervm import exception as p_exc
from nova_powervm.virt.powervm import live_migration as lpm
from nova_powervm.virt.powervm import vm
MS_HTTPRESP_FILE = "managedsystem.txt"
MS_NAME = 'HV4'
LPAR_HTTPRESP_FILE = "lpar.txt"
VIOS_HTTPRESP_FILE = "fake_vios_ssp_npiv.txt"
LOG = logging.getLogger(__name__)
logging.basicConfig()
class TestPowerVMDriver(test.TestCase):
def setUp(self):
super(TestPowerVMDriver, self).setUp()
ms_http = pvmhttp.load_pvm_resp(MS_HTTPRESP_FILE)
self.assertIsNotNone(ms_http, "Could not load %s " % MS_HTTPRESP_FILE)
entries = ms_http.response.feed.findentries(pvm_ms._SYSTEM_NAME,
MS_NAME)
self.assertNotEqual(entries, None,
"Could not find %s in %s" %
(MS_NAME, MS_HTTPRESP_FILE))
self.wrapper = pvm_ms.System.wrap(entries[0])
self.flags(disk_driver='localdisk', group='powervm')
self.flags(host='host1', my_ip='127.0.0.1')
self.drv_fix = self.useFixture(fx.PowerVMComputeDriver())
self.drv = self.drv_fix.drv
self.apt = self.drv.adapter
self._setup_lpm()
self.disk_dvr = self.drv.disk_dvr
self.vol_fix = self.useFixture(fx.VolumeAdapter())
self.vol_drv = self.vol_fix.drv
self.crt_lpar_p = mock.patch('nova_powervm.virt.powervm.vm.crt_lpar')
self.crt_lpar = self.crt_lpar_p.start()
self.addCleanup(self.crt_lpar_p.stop)
self.get_inst_wrap_p = mock.patch('nova_powervm.virt.powervm.vm.'
'get_instance_wrapper')
self.get_inst_wrap = self.get_inst_wrap_p.start()
self.addCleanup(self.get_inst_wrap_p.stop)
wrap = pvm_lpar.LPAR.wrap(pvmhttp.load_pvm_resp(
LPAR_HTTPRESP_FILE).response)[0]
self.crt_lpar.return_value = wrap
self.get_inst_wrap.return_value = wrap
self.build_tx_feed_p = mock.patch('nova_powervm.virt.powervm.vios.'
'build_tx_feed_task')
self.build_tx_feed = self.build_tx_feed_p.start()
self.addCleanup(self.build_tx_feed_p.stop)
self.useFixture(pvm_fx.FeedTaskFx([pvm_vios.VIOS.wrap(
pvmhttp.load_pvm_resp(VIOS_HTTPRESP_FILE).response)]))
self.stg_ftsk = pvm_tx.FeedTask('fake', pvm_vios.VIOS.getter(self.apt))
self.build_tx_feed.return_value = self.stg_ftsk
scrub_stg_p = mock.patch('pypowervm.tasks.storage.'
'add_lpar_storage_scrub_tasks')
self.scrub_stg = scrub_stg_p.start()
self.addCleanup(scrub_stg_p.stop)
# Create an instance to test with
self.inst = objects.Instance(**powervm.TEST_INST_SPAWNING)
self.inst_ibmi = objects.Instance(**powervm.TEST_INST_SPAWNING)
self.inst_ibmi.system_metadata = {'image_os_distro': 'ibmi'}
def _setup_lpm(self):
"""Setup the lpm environment.
This may have to be called directly by tests since the lpm code
cleans up the dict entry on the last expected lpm method.
"""
self.lpm = mock.Mock()
self.lpm_inst = mock.Mock()
self.lpm_inst.uuid = 'inst1'
self.drv.live_migrations = {'inst1': self.lpm}
def test_driver_create(self):
"""Validates that a driver of the PowerVM type can be initialized."""
test_drv = driver.PowerVMDriver(fake.FakeVirtAPI())
self.assertIsNotNone(test_drv)
def test_cleanup_host(self):
self.drv.cleanup_host('fake_host')
self.assertTrue(
self.drv.session.get_event_listener.return_value.shutdown.called)
def test_get_volume_connector(self):
"""Tests that a volume connector can be built."""
vol_connector = self.drv.get_volume_connector(mock.Mock())
self.assertIsNotNone(vol_connector['wwpns'])
self.assertIsNotNone(vol_connector['host'])
def test_get_disk_adapter(self):
# Ensure we can handle upper case option and we instantiate the class
self.flags(disk_driver='LoCaLDisK', group='powervm')
self.drv.disk_dvr = None
self.drv._get_disk_adapter()
# The local disk driver has been mocked, so we just compare the name
self.assertIn('LocalStorage()', str(self.drv.disk_dvr))
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova.context.get_admin_context')
def test_driver_ops(self, mock_get_ctx, mock_getuuid):
"""Validates the PowerVM driver operations."""
# get_info()
inst = fake_instance.fake_instance_obj(mock.sentinel.ctx)
mock_getuuid.return_value = '1234'
info = self.drv.get_info(inst)
self.assertEqual(info.id, '1234')
# list_instances()
tgt_mock = 'nova_powervm.virt.powervm.vm.get_lpar_names'
with mock.patch(tgt_mock) as mock_get_list:
fake_lpar_list = ['1', '2']
mock_get_list.return_value = fake_lpar_list
inst_list = self.drv.list_instances()
self.assertEqual(fake_lpar_list, inst_list)
# instance_exists()
tgt_mock = 'nova_powervm.virt.powervm.vm.instance_exists'
with mock.patch(tgt_mock) as mock_inst_exists:
mock_inst_exists.side_effect = [True, False]
self.assertTrue(self.drv.instance_exists(mock.Mock()))
self.assertFalse(self.drv.instance_exists(mock.Mock()))
def test_instance_on_disk(self):
"""Validates the instance_on_disk method."""
@mock.patch.object(self.drv, '_is_booted_from_volume')
@mock.patch.object(self.drv, '_get_block_device_info')
@mock.patch.object(self.disk_dvr, 'capabilities')
@mock.patch.object(self.disk_dvr, 'get_disk_ref')
def inst_on_disk(mock_disk_ref, mock_capb, mock_block, mock_boot):
# Test boot from volume.
mock_boot.return_value = True
self.assertTrue(self.drv.instance_on_disk(self.inst))
mock_boot.return_value = False
# Disk driver is shared storage and can find the disk
mock_capb['shared_storage'] = True
mock_disk_ref.return_value = 'disk_reference'
self.assertTrue(self.drv.instance_on_disk(self.inst))
# Disk driver can't find it
mock_disk_ref.return_value = None
self.assertFalse(self.drv.instance_on_disk(self.inst))
# Disk driver exception
mock_disk_ref.side_effect = ValueError('Bad disk')
self.assertFalse(self.drv.instance_on_disk(self.inst))
mock_disk_ref.side_effect = None
# Not on shared storage
mock_capb['shared_storage'] = False
self.assertFalse(self.drv.instance_on_disk(self.inst))
inst_on_disk()
@mock.patch('nova_powervm.virt.powervm.tasks.storage.'
'CreateAndConnectCfgDrive.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.ConnectVolume'
'.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_ops(
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_disk_img,
mock_conn_vol, mock_crt_cfg_drv):
"""Validates the 'typical' spawn flow of the spawn of an instance.
Uses a basic disk image, attaching networks and powering on.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = False
# Invoke the method.
self.drv.spawn('context', self.inst, powervm.IMAGE1,
'injected_files', 'admin_password')
# Assert the correct tasks were called
self.assertTrue(mock_plug_vifs.called)
self.assertTrue(mock_plug_mgmt_vif.called)
self.assertTrue(mock_crt_disk_img.called)
self.crt_lpar.assert_called_with(
self.apt, self.drv.host_wrapper, self.inst, self.inst.get_flavor())
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Assert that tasks that are not supposed to be called are not called
self.assertFalse(mock_conn_vol.called)
self.assertFalse(mock_crt_cfg_drv.called)
self.scrub_stg.assert_called_with([9], self.stg_ftsk, lpars_exist=True)
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'create_cfg_drv_vopt')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'_validate_vopt_vg')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_cfg(
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_val_vopt,
mock_cfg_vopt, mock_plug_vifs, mock_plug_mgmt_vif):
"""Validates the PowerVM spawn w/ config drive operations."""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = True
# Invoke the method.
self.drv.spawn('context', self.inst, powervm.IMAGE1,
'injected_files', 'admin_password')
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
# Config drive was called
self.assertTrue(mock_val_vopt.called)
self.assertTrue(mock_cfg_vopt.called)
# Power on was called
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
self.scrub_stg.assert_called_with([9], self.stg_ftsk, lpars_exist=True)
@mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.save')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_bdms(
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save):
"""Validates the PowerVM spawn.
Specific Test: spawn of an image that has a disk image and block device
mappings are passed into spawn which originated from either the image
metadata itself or the create server request. In particular, test when
the BDMs passed in do not have the root device for the instance.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = False
# Create some fake BDMs
block_device_info = self._fake_bdms()
# Invoke the method.
self.drv.spawn('context', self.inst, powervm.IMAGE1,
'injected_files', 'admin_password',
block_device_info=block_device_info)
self.assertTrue(mock_boot_from_vol.called)
# Since the root device is not in the BDMs we expect the image disk to
# be created.
self.assertTrue(mock_crt_img.called)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
# Power on was called
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Check that the connect volume was called
self.assertEqual(2, self.vol_drv.connect_volume.call_count)
# Make sure the save was invoked
self.assertEqual(2, mock_save.call_count)
self.scrub_stg.assert_called_with([9], self.stg_ftsk, lpars_exist=True)
@mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.save')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_image_meta_root_bdm(
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save):
"""Validates the PowerVM spawn.
Specific Test: spawn of an image that does not have a disk image but
rather the block device mappings are passed into spawn. These
originated from either the image metadata itself or the create server
request. In particular, test when the BDMs passed in have the root
device for the instance and image metadata from an image is also
passed.
Note this tests the ability to spawn an image that does not
contain a disk image but rather contains block device mappings
containing the root BDM. The
nova.compute.api.API.snapshot_volume_backed flow produces such images.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = True
# Create some fake BDMs
block_device_info = self._fake_bdms()
# Invoke the method.
self.drv.spawn('context', self.inst, powervm.IMAGE1,
'injected_files', 'admin_password',
block_device_info=block_device_info)
self.assertTrue(mock_boot_from_vol.called)
# Since the root device is in the BDMs we do not expect the image disk
# to be created.
self.assertFalse(mock_crt_img.called)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
# Power on was called
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Check that the connect volume was called
self.assertEqual(2, self.vol_drv.connect_volume.call_count)
self.scrub_stg.assert_called_with([9], self.stg_ftsk, lpars_exist=True)
@mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.save')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_with_root_bdm(
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_img, mock_save):
"""Validates the PowerVM spawn.
Specific test: when no image is given and only block device mappings
are given on the create server request.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = True
# Create some fake BDMs
block_device_info = self._fake_bdms()
# Invoke the method.
self.drv.spawn('context', self.inst, powervm.IMAGE1,
'injected_files', 'admin_password',
block_device_info=block_device_info)
self.assertTrue(mock_boot_from_vol.called)
# Since the root device is in the BDMs we do not expect the image disk
# to be created.
self.assertFalse(mock_crt_img.called)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
# Power on was called
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Check that the connect volume was called
self.assertEqual(2, self.vol_drv.connect_volume.call_count)
# Make sure the BDM save was invoked twice.
self.assertEqual(2, mock_save.call_count)
self.scrub_stg.assert_called_with([9], self.stg_ftsk, lpars_exist=True)
@mock.patch('nova_powervm.virt.powervm.tasks.storage.'
'CreateAndConnectCfgDrive.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.ConnectVolume'
'.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.FindDisk'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_recreate(
self, mock_pwron, mock_get_flv, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_find_disk,
mock_conn_vol, mock_crt_cfg_drv):
"""Validates the 'recreate' spawn flow.
Uses a basic disk image, attaching networks and powering on.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = False
self.inst.task_state = task_states.REBUILD_SPAWNING
# Invoke the method.
self.drv.spawn('context', self.inst, powervm.EMPTY_IMAGE,
'injected_files', 'admin_password')
# Assert the correct tasks were called
self.assertTrue(mock_plug_vifs.called)
self.assertTrue(mock_plug_mgmt_vif.called)
self.assertTrue(mock_find_disk.called)
self.crt_lpar.assert_called_with(
self.apt, self.drv.host_wrapper, self.inst, self.inst.get_flavor())
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Assert that tasks that are not supposed to be called are not called
self.assertFalse(mock_conn_vol.called)
self.assertFalse(mock_crt_cfg_drv.called)
self.scrub_stg.assert_called_with([9], self.stg_ftsk, lpars_exist=True)
@mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.save')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova_powervm.virt.powervm.vm.dlt_lpar')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
@mock.patch('pypowervm.tasks.power.power_off')
def test_spawn_ops_rollback(
self, mock_pwroff, mock_pwron, mock_get_flv, mock_cfg_drv, mock_dlt,
mock_plug_vifs, mock_plug_mgmt_vifs, mock_save):
"""Validates the PowerVM driver operations. Will do a rollback."""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
block_device_info = self._fake_bdms()
# Make sure power on fails.
mock_pwron.side_effect = exc.Forbidden()
# Invoke the method.
self.assertRaises(exc.Forbidden, self.drv.spawn, 'context', self.inst,
powervm.IMAGE1, 'injected_files', 'admin_password',
block_device_info=block_device_info)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
self.assertEqual(2, self.vol_drv.connect_volume.call_count)
# Power on was called
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Validate the rollbacks were called
self.assertEqual(2, self.vol_drv.disconnect_volume.call_count)
@mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.save')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('nova_powervm.virt.powervm.tasks.vm.UpdateIBMiSettings'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_get_boot_connectivity_type')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_ibmi(
self, mock_pwron, mock_boot_conn_type,
mock_update_lod_src, mock_get_flv, mock_cfg_drv,
mock_plug_vifs, mock_plug_mgmt_vif, mock_boot_from_vol,
mock_crt_img, mock_save):
"""Validates the PowerVM spawn to create an IBMi server.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst_ibmi.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = True
mock_boot_conn_type.return_value = 'vscsi'
# Create some fake BDMs
block_device_info = self._fake_bdms()
# Invoke the method.
self.drv.spawn('context', self.inst_ibmi, powervm.IMAGE1,
'injected_files', 'admin_password',
block_device_info=block_device_info)
self.assertTrue(mock_boot_from_vol.called)
# Since the root device is in the BDMs we do not expect the image disk
# to be created.
self.assertFalse(mock_crt_img.called)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst_ibmi,
self.inst_ibmi.get_flavor())
self.assertTrue(mock_boot_conn_type.called)
self.assertTrue(mock_update_lod_src.called)
# Power on was called
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Check that the connect volume was called
self.assertEqual(2, self.vol_drv.connect_volume.call_count)
# Make sure the BDM save was invoked twice.
self.assertEqual(2, mock_save.call_count)
@mock.patch('nova_powervm.virt.powervm.tasks.storage.'
'CreateAndConnectCfgDrive.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.ConnectVolume'
'.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('nova_powervm.virt.powervm.tasks.vm.UpdateIBMiSettings'
'.execute')
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_get_boot_connectivity_type')
@mock.patch('pypowervm.tasks.power.power_on')
def test_spawn_ibmi_without_bdms(
self, mock_pwron, mock_boot_conn_type, mock_update_lod_src,
mock_get_flv, mock_cfg_drv, mock_plug_vifs,
mock_plug_mgmt_vif, mock_boot_from_vol, mock_crt_disk_img,
mock_conn_vol, mock_crt_cfg_drv):
"""Validates the 'typical' spawn flow for IBMi
Perform an UT using an image with local disk, attaching networks
and powering on.
"""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst_ibmi.get_flavor()
mock_cfg_drv.return_value = False
mock_boot_from_vol.return_value = False
mock_boot_conn_type.return_value = 'vscsi'
# Invoke the method.
self.drv.spawn('context', self.inst_ibmi, powervm.IMAGE1,
'injected_files', 'admin_password')
# Assert the correct tasks were called
self.assertTrue(mock_plug_vifs.called)
self.assertTrue(mock_plug_mgmt_vif.called)
self.assertTrue(mock_crt_disk_img.called)
self.crt_lpar.assert_called_with(
self.apt, self.drv.host_wrapper, self.inst_ibmi,
self.inst_ibmi.get_flavor())
self.assertTrue(mock_update_lod_src.called)
self.assertTrue(mock_pwron.called)
self.assertFalse(mock_pwron.call_args[1]['synchronous'])
# Assert that tasks that are not supposed to be called are not called
self.assertFalse(mock_conn_vol.called)
self.assertFalse(mock_crt_cfg_drv.called)
@mock.patch('nova_powervm.virt.powervm.disk.localdisk.LocalStorage.'
'delete_disks')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.CreateDiskForImg.'
'execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova_powervm.virt.powervm.vm.dlt_lpar')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
def test_spawn_ops_rollback_disk(
self, mock_get_flv, mock_cfg_drv, mock_dlt, mock_plug_vifs,
mock_plug_mgmt_vifs, mock_crt_disk, mock_delete_disks):
"""Validates the rollback if failure occurs on disk create."""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
# Make sure power on fails.
mock_crt_disk.side_effect = exc.Forbidden()
# Invoke the method.
self.assertRaises(exc.Forbidden, self.drv.spawn, 'context', self.inst,
powervm.IMAGE1, 'injected_files', 'admin_password',
block_device_info=None)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
# Since the create disks method failed, the delete disks should not
# have been called
self.assertFalse(mock_delete_disks.called)
@mock.patch('nova.virt.block_device.DriverVolumeBlockDevice.save')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugMgmtVif.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.network.PlugVifs.execute')
@mock.patch('nova_powervm.virt.powervm.vm.dlt_lpar')
@mock.patch('nova.virt.configdrive.required_by')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
@mock.patch('pypowervm.tasks.power.power_on')
@mock.patch('pypowervm.tasks.power.power_off')
def test_spawn_ops_rollback_on_vol_connect(
self, mock_pwroff, mock_pwron, mock_get_flv, mock_cfg_drv, mock_dlt,
mock_plug_vifs, mock_plug_mgmt_vifs, mock_save):
"""Validates the rollbacks on a volume connect failure."""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
mock_cfg_drv.return_value = False
block_device_info = self._fake_bdms()
# Have the connect fail. Also fail the disconnect on revert. Should
# not block the rollback.
self.vol_drv.connect_volume.side_effect = exc.Forbidden()
self.vol_drv.disconnect_volume.side_effect = p_exc.VolumeDetachFailed(
volume_id='1', instance_name=self.inst.name, reason='Test Case')
# Invoke the method.
self.assertRaises(exc.Forbidden, self.drv.spawn, 'context', self.inst,
powervm.IMAGE1, 'injected_files', 'admin_password',
block_device_info=block_device_info)
# Create LPAR was called
self.crt_lpar.assert_called_with(self.apt, self.drv.host_wrapper,
self.inst, self.inst.get_flavor())
self.assertEqual(1, self.vol_drv.connect_volume.call_count)
# Power on should not be called. Shouldn't get that far in flow.
self.assertFalse(mock_pwron.called)
# Disconnect should, as it may need to remove from one of the VIOSes
# (but maybe failed on another).
self.assertTrue(self.vol_drv.disconnect_volume.called)
@mock.patch('nova.block_device.get_root_bdm')
@mock.patch('nova.virt.driver.block_device_info_get_mapping')
def test_is_booted_from_volume(self, mock_get_mapping, mock_get_root_bdm):
block_device_info = self._fake_bdms()
ret = self.drv._is_booted_from_volume(block_device_info)
mock_get_root_bdm.\
assert_called_once_with(mock_get_mapping.return_value)
self.assertTrue(ret)
self.assertEqual(1, mock_get_mapping.call_count)
mock_get_mapping.reset_mock()
mock_get_root_bdm.return_value = None
ret = self.drv._is_booted_from_volume(block_device_info)
self.assertFalse(ret)
self.assertEqual(1, mock_get_mapping.call_count)
# Test if block_device_info is None
ret = self.drv._is_booted_from_volume(None)
self.assertFalse(ret)
def test_get_inst_xag(self):
# No volumes - should be just the SCSI mapping
xag = self.drv._get_inst_xag(mock.Mock(), None)
self.assertEqual([pvm_vios.VIOS.xags.SCSI_MAPPING], xag)
# The vSCSI Volume attach - only needs the SCSI mapping.
self.flags(fc_attach_strategy='vscsi', group='powervm')
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
self.assertEqual([pvm_vios.VIOS.xags.SCSI_MAPPING], xag)
# The NPIV volume attach - requires SCSI, Storage and FC Mapping
self.flags(fc_attach_strategy='npiv', group='powervm')
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
self.assertEqual(set([pvm_vios.VIOS.xags.STORAGE,
pvm_vios.VIOS.xags.SCSI_MAPPING,
pvm_vios.VIOS.xags.FC_MAPPING]), set(xag))
# The vSCSI Volume attach - Ensure case insensitive.
self.flags(fc_attach_strategy='VSCSI', group='powervm')
xag = self.drv._get_inst_xag(mock.Mock(), [mock.Mock()])
self.assertEqual([pvm_vios.VIOS.xags.SCSI_MAPPING], xag)
def test_add_vol_conn_task(self):
bdm, vol_drv = mock.MagicMock(), mock.MagicMock()
flow = mock.Mock()
vals = [(bdm, vol_drv), (bdm, vol_drv)]
with mock.patch.object(self.drv, '_vol_drv_iter', return_value=vals):
self.drv._add_volume_connection_tasks(
'context', 'instance', 'bdms', flow, 'stg_ftsk')
self.assertEqual(4, flow.add.call_count)
def test_add_vol_disconn_task(self):
bdm, vol_drv = mock.MagicMock(), mock.MagicMock()
flow = mock.Mock()
vals = [(bdm, vol_drv), (bdm, vol_drv)]
with mock.patch.object(self.drv, '_vol_drv_iter', return_value=vals):
self.drv._add_volume_disconnection_tasks(
'context', 'instance', 'bdms', flow, 'stg_ftsk')
self.assertEqual(2, flow.add.call_count)
@mock.patch('nova_powervm.virt.powervm.driver.PowerVMDriver.'
'_is_booted_from_volume')
@mock.patch('nova_powervm.virt.powervm.vm.dlt_lpar')
@mock.patch('nova_powervm.virt.powervm.vm.power_off')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'dlt_vopt')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'_validate_vopt_vg')
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
def test_destroy_internal(
self, mock_get_flv, mock_pvmuuid, mock_val_vopt, mock_dlt_vopt,
mock_pwroff, mock_dlt, mock_boot_from_vol):
"""Validates the basic PowerVM destroy."""
# BDMs
mock_bdms = self._fake_bdms()
mock_boot_from_vol.return_value = False
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
# Power off was called
mock_pwroff.assert_called_with(self.drv.adapter, self.inst,
self.drv.host_uuid,
force_immediate=True)
# Validate that the vopt delete was called
self.assertTrue(mock_dlt_vopt.called)
# Validate that the volume detach was called
self.assertEqual(2, self.vol_drv.disconnect_volume.call_count)
# Delete LPAR was called
mock_dlt.assert_called_with(self.apt, mock.ANY)
# Validate root device in bdm was checked.
mock_boot_from_vol.assert_called_with(mock_bdms)
# Validate disk driver detach and delete disk methods were called.
self.assertTrue(self.drv.disk_dvr.delete_disks.called)
self.assertTrue(self.drv.disk_dvr.disconnect_image_disk.called)
def reset_mocks():
# Reset the mocks
for mk in [mock_pwroff, mock_dlt, mock_dlt_vopt,
self.vol_drv, mock_dlt,
mock_boot_from_vol]:
mk.reset_mock()
def assert_not_called():
# Power off was not called
self.assertFalse(mock_pwroff.called)
# Validate that the vopt delete was not called
self.assertFalse(mock_dlt_vopt.called)
# Validate that the volume detach was not called
self.assertFalse(self.vol_drv.disconnect_volume.called)
# Delete LPAR was not called
self.assertFalse(mock_dlt.called)
# Test when the VM's root device is a BDM.
reset_mocks()
mock_boot_from_vol.return_value = True
self.drv.disk_dvr.delete_disks.reset_mock()
self.drv.disk_dvr.disconnect_image_disk.reset_mock()
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
# Validate root device in bdm was checked.
mock_boot_from_vol.assert_called_with(mock_bdms)
# Validate disk driver detach and delete disk methods were called.
self.assertFalse(self.drv.disk_dvr.delete_disks.called)
self.assertFalse(self.drv.disk_dvr.disconnect_image_disk.called)
# Test when destroy_disks set to False.
reset_mocks()
mock_boot_from_vol.return_value = True
self.drv.disk_dvr.delete_disks.reset_mock()
self.drv.disk_dvr.disconnect_image_disk.reset_mock()
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms, destroy_disks=False)
mock_pwroff.assert_called_with(self.drv.adapter, self.inst,
self.drv.host_uuid,
force_immediate=False)
# Start negative tests
reset_mocks()
# Pretend we didn't find the VM on the system
mock_pvmuuid.side_effect = exc.InstanceNotFound(
instance_id=self.inst.name)
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
assert_not_called()
mock_resp = mock.Mock()
mock_resp.status = 404
mock_resp.reqpath = (
'/rest/api/uom/ManagedSystem/c5d782c7-44e4-3086-ad15-'
'b16fb039d63b/LogicalPartition/1B5FB633-16D1-4E10-A14'
'5-E6FB905161A3?group=None')
mock_pvmuuid.side_effect = pvm_exc.HttpError(mock_resp)
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
assert_not_called()
# Ensure the exception is raised with non-matching path
reset_mocks()
mock_resp.reqpath = (
'/rest/api/uom/ManagedSystem/c5d782c7-44e4-3086-ad15-'
'b16fb039d63b/SomeResource/1B5FB633-16D1-4E10-A14'
'5-E6FB905161A3?group=None')
# Invoke the method.
self.assertRaises(exc.InstanceTerminationFailure,
self.drv.destroy, 'context', self.inst,
mock.Mock(), block_device_info=mock_bdms)
assert_not_called()
# Test generic exception
mock_pvmuuid.side_effect = ValueError('Some error')
# Invoke the method.
self.assertRaises(exc.InstanceTerminationFailure,
self.drv.destroy, 'context', self.inst,
mock.Mock(), block_device_info=mock_bdms)
assert_not_called()
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
@mock.patch('nova_powervm.virt.powervm.vm.get_vm_qp')
def test_destroy(self, mock_getqp, mock_getuuid):
"""Validates the basic PowerVM destroy."""
# BDMs
mock_bdms = self._fake_bdms()
with mock.patch.object(self.drv, '_destroy') as mock_dst_int:
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
mock_dst_int.assert_called_with(
'context', self.inst, block_device_info=mock_bdms,
destroy_disks=True, shutdown=True)
# Test delete during migrate / resize
self.inst.task_state = task_states.RESIZE_REVERTING
mock_getqp.return_value = ('resize_' + self.inst.name)[:31]
with mock.patch.object(self.drv, '_destroy') as mock_dst_int:
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
# We shouldn't delete our resize_ instances
mock_dst_int.assert_not_called()
# Now test migrating...
mock_getqp.return_value = ('migrate_' + self.inst.name)[:31]
with mock.patch.object(self.drv, '_destroy') as mock_dst_int:
# Invoke the method.
self.drv.destroy('context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
# If it is a migrated instance, it should be deleted.
mock_dst_int.assert_called_with(
'context', self.inst, block_device_info=mock_bdms,
destroy_disks=True, shutdown=True)
def test_attach_volume(self):
"""Validates the basic PowerVM attach volume."""
# BDMs
mock_bdm = self._fake_bdms()['block_device_mapping'][0]
with mock.patch.object(self.inst, 'save') as mock_save:
# Invoke the method.
self.drv.attach_volume('context', mock_bdm.get('connection_info'),
self.inst, mock.Mock())
# Verify the connect volume was invoked
self.assertEqual(1, self.vol_drv.connect_volume.call_count)
self.assertTrue(mock_save.called)
@mock.patch('nova_powervm.virt.powervm.vm.instance_exists')
def test_detach_volume(self, mock_inst_exists):
"""Validates the basic PowerVM detach volume."""
# Mock that the instance exists for the first test, then not.
mock_inst_exists.side_effect = [True, False, False]
# BDMs
mock_bdm = self._fake_bdms()['block_device_mapping'][0]
# Invoke the method, good path test.
self.drv.detach_volume(mock_bdm.get('connection_info'), self.inst,
mock.Mock())
# Verify the disconnect volume was invoked
self.assertEqual(1, self.vol_drv.disconnect_volume.call_count)
# Invoke the method, instance doesn't exist, no migration
self.vol_drv.disconnect_volume.reset_mock()
self.drv.detach_volume(mock_bdm.get('connection_info'), self.inst,
mock.Mock())
# Verify the disconnect volume was not invoked
self.assertEqual(0, self.vol_drv.disconnect_volume.call_count)
# Test instance doesn't exist, migration cleanup
self.vol_drv.disconnect_volume.reset_mock()
mig = lpm.LiveMigrationDest(self.drv, self.inst)
self.drv.live_migrations[self.inst.uuid] = mig
with mock.patch.object(mig, 'cleanup_volume') as mock_clnup:
self.drv.detach_volume(mock_bdm.get('connection_info'), self.inst,
mock.Mock())
# The cleanup should have been called since there was a migration
self.assertEqual(1, mock_clnup.call_count)
# Verify the disconnect volume was not invoked
self.assertEqual(0, self.vol_drv.disconnect_volume.call_count)
@mock.patch('nova_powervm.virt.powervm.vm.dlt_lpar')
@mock.patch('nova_powervm.virt.powervm.vm.power_off')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'dlt_vopt')
@mock.patch('nova_powervm.virt.powervm.media.ConfigDrivePowerVM.'
'_validate_vopt_vg')
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
def test_destroy_rollback(
self, mock_get_flv, mock_val_vopt, mock_dlt_vopt, mock_pwroff,
mock_dlt):
"""Validates the basic PowerVM destroy rollback mechanism works."""
# Set up the mocks to the tasks.
mock_get_flv.return_value = self.inst.get_flavor()
# BDMs
mock_bdms = self._fake_bdms()
# Fire a failure in the power off.
mock_dlt.side_effect = exc.Forbidden()
# Have the connect volume fail on the rollback. Should not block the
# full rollback.
self.vol_drv.connect_volume.side_effect = p_exc.VolumeAttachFailed(
volume_id='1', instance_name=self.inst.name, reason='Test Case')
# Invoke the method.
self.assertRaises(exc.InstanceTerminationFailure, self.drv.destroy,
'context', self.inst, mock.Mock(),
block_device_info=mock_bdms)
# Validate that the vopt delete was called
self.assertTrue(mock_dlt_vopt.called)
# Validate that the volume detach was called
self.assertEqual(2, self.vol_drv.disconnect_volume.call_count)
# Delete LPAR was called
mock_dlt.assert_called_with(self.apt, mock.ANY)
# Validate the rollbacks were called.
self.assertEqual(2, self.vol_drv.connect_volume.call_count)
def test_migrate_disk_and_power_off(self):
"""Validates the PowerVM driver migrate / resize operation."""
# Set up the mocks to the migrate / resize operation.
host = self.drv.get_host_ip_addr()
resp = pvm_adp.Response('method', 'path', 'status', 'reason', {})
resp.entry = pvm_lpar.LPAR._bld(None).entry
self.apt.read.return_value = resp
# BDMs
mock_bdms = self._fake_bdms()
# Catch root disk resize smaller.
small_root = objects.Flavor(vcpus=1, memory_mb=2048, root_gb=9)
self.assertRaises(
exc.InstanceFaultRollback, self.drv.migrate_disk_and_power_off,
'context', self.inst, 'dest', small_root, 'network_info',
mock_bdms)
# Boot disk resize
boot_flav = objects.Flavor(vcpus=1, memory_mb=2048, root_gb=12)
# Tasks expected to be added for resize to the same host
expected = [
'pwr_off_lpar',
'extend_disk_boot',
'disconnect_vol_*',
'disconnect_vol_*',
'fake',
'rename_lpar_resize_instance-00000001',
]
with fx.DriverTaskFlow() as taskflow_fix:
self.drv.migrate_disk_and_power_off(
'context', self.inst, host, boot_flav, 'network_info',
mock_bdms)
taskflow_fix.assert_tasks_added(self, expected)
# Check the size set in the resize task
extend_task = taskflow_fix.tasks_added[1]
self.assertEqual(extend_task.size, 12)
@mock.patch('nova.objects.flavor.Flavor.get_by_id')
def test_finish_migration(self, mock_get_flv):
mock_bdms = self._fake_bdms()
mig = objects.Migration(**powervm.TEST_MIGRATION)
mig_same_host = objects.Migration(**powervm.TEST_MIGRATION_SAME_HOST)
disk_info = {}
# The first test is different hosts but local storage, should fail
self.assertRaises(exc.InstanceFaultRollback,
self.drv.finish_migration,
'context', mig, self.inst, disk_info, 'network_info',
powervm.IMAGE1, 'resize_instance', mock_bdms)
# The rest of the test need to pass the shared disk test
self.disk_dvr.validate.return_value = None
# Tasks expected to be added for migration to different host
expected = [
'crt_lpar',
'plug_vifs',
'plug_mgmt_vif',
'find_disk',
'connect_disk',
'connect_vol_*',
'save_bdm_fake_vol1',
'connect_vol_*',
'save_bdm_fake_vol2',
'fake',
'get_lpar',
'pwr_lpar',
]
with fx.DriverTaskFlow() as taskflow_fix:
self.drv.finish_migration(
'context', mig, self.inst, disk_info, 'network_info',
powervm.IMAGE1, 'resize_instance', block_device_info=mock_bdms)
taskflow_fix.assert_tasks_added(self, expected)
# Tasks expected to be added for resize to the same host
expected = [
'resize_lpar',
'connect_vol_*',
'save_bdm_fake_vol1',
'connect_vol_*',
'save_bdm_fake_vol2',
'fake',
'get_lpar',
'pwr_lpar',
]
with fx.DriverTaskFlow() as taskflow_fix:
self.drv.finish_migration(
'context', mig_same_host, self.inst, disk_info, 'network_info',
powervm.IMAGE1, 'resize_instance', block_device_info=mock_bdms)
taskflow_fix.assert_tasks_added(self, expected)
# Tasks expected to be added for resize to the same host, no BDMS,
# and no power_on
expected = [
'resize_lpar',
]
with fx.DriverTaskFlow() as taskflow_fix:
self.drv.finish_migration(
'context', mig_same_host, self.inst, disk_info, 'network_info',
powervm.IMAGE1, 'resize_instance', power_on=False)
taskflow_fix.assert_tasks_added(self, expected)
@mock.patch('nova_powervm.virt.powervm.driver.vm')
@mock.patch('nova_powervm.virt.powervm.tasks.vm.vm')
@mock.patch('nova_powervm.virt.powervm.tasks.vm.power')
def test_rescue(self, mock_task_pwr, mock_task_vm, mock_dvr_vm):
"""Validates the PowerVM driver rescue operation."""
with mock.patch.object(self.drv, 'disk_dvr') as mock_disk_dvr:
# Invoke the method.
self.drv.rescue('context', self.inst, mock.MagicMock(),
powervm.TEST_IMAGE1, 'rescue_psswd')
self.assertTrue(mock_task_vm.power_off.called)
self.assertTrue(mock_disk_dvr.create_disk_from_image.called)
self.assertTrue(mock_disk_dvr.connect_disk.called)
self.assertTrue(mock_task_pwr.power_on.called)
self.assertFalse(mock_task_pwr.power_on.call_args[1]['synchronous'])
@mock.patch('nova_powervm.virt.powervm.driver.vm')
@mock.patch('nova_powervm.virt.powervm.tasks.vm.vm')
@mock.patch('nova_powervm.virt.powervm.tasks.vm.power')
def test_unrescue(self, mock_task_pwr, mock_task_vm, mock_dvr_vm):
"""Validates the PowerVM driver rescue operation."""
with mock.patch.object(self.drv, 'disk_dvr') as mock_disk_dvr:
# Invoke the method.
self.drv.unrescue(self.inst, 'network_info')
self.assertTrue(mock_task_vm.power_off.called)
self.assertTrue(mock_disk_dvr.disconnect_image_disk.called)
self.assertTrue(mock_disk_dvr.delete_disks.called)
self.assertTrue(mock_task_pwr.power_on.called)
self.assertFalse(mock_task_pwr.power_on.call_args[1]['synchronous'])
@mock.patch.object(driver, 'LOG')
def test_log_op(self, mock_log):
"""Validates the log_operations."""
self.drv._log_operation('fake_op', self.inst)
entry = (r'Operation: %(op)s. Virtual machine display '
'name: %(display_name)s, name: %(name)s, '
'UUID: %(uuid)s')
msg_dict = {'uuid': '49629a5c-f4c4-4721-9511-9725786ff2e5',
'display_name': u'Fake Instance',
'name': 'instance-00000001',
'op': 'fake_op'}
mock_log.info.assert_called_with(entry, msg_dict)
def test_host_resources(self):
# Set the return value of None so we use the cached value in the drv
self.apt.read.return_value = None
self.drv.host_wrapper = self.wrapper
stats = self.drv.get_available_resource('nodename')
self.assertIsNotNone(stats)
# Check for the presence of fields added to host stats
fields = ('local_gb', 'local_gb_used')
for fld in fields:
value = stats.get(fld, None)
self.assertIsNotNone(value)
@mock.patch('pypowervm.wrappers.logical_partition.LPAR.can_modify_io')
@mock.patch('nova_powervm.virt.powervm.vm.crt_secure_rmc_vif')
@mock.patch('nova_powervm.virt.powervm.vm.get_secure_rmc_vswitch')
@mock.patch('nova_powervm.virt.powervm.vm.crt_vif')
@mock.patch('nova_powervm.virt.powervm.vm.get_cnas')
def test_plug_vifs(
self, mock_vm_get, mock_vm_crt, mock_get_rmc_vswitch, mock_crt_rmc_vif,
mock_can_modify_io):
# Mock up the CNA response
cnas = [mock.MagicMock(), mock.MagicMock()]
cnas[0].mac = 'AABBCCDDEEFF'
cnas[0].vswitch_uri = 'fake_uri'
cnas[1].mac = 'AABBCCDDEE11'
cnas[1].vswitch_uri = 'fake_mgmt_uri'
mock_vm_get.return_value = cnas
mock_can_modify_io.return_value = True, None
# Mock up the network info. They get sanitized to upper case.
net_info = [
{'address': 'aa:bb:cc:dd:ee:ff'},
{'address': 'aa:bb:cc:dd:ee:22'}
]
# Mock up the rmc vswitch
vswitch_w = mock.MagicMock()
vswitch_w.href = 'fake_mgmt_uri'
mock_get_rmc_vswitch.return_value = vswitch_w
# Run method
self.drv.plug_vifs(self.inst, net_info)
# The create should have only been called once. The other was already
# existing.
self.assertEqual(1, mock_vm_crt.call_count)
self.assertEqual(0, mock_crt_rmc_vif.call_count)
@mock.patch('nova_powervm.virt.powervm.tasks.vm.Get')
def test_plug_vif_failures(self, mock_vm):
# Test instance not found handling
mock_vm.execute.side_effect = exc.InstanceNotFound(
instance_id=self.inst)
# Run method
self.assertRaises(exc.VirtualInterfacePlugException,
self.drv.plug_vifs, self.inst, {})
# Test a random Exception
mock_vm.execute.side_effect = ValueError()
# Run method
self.assertRaises(exc.VirtualInterfacePlugException,
self.drv.plug_vifs, self.inst, {})
@mock.patch('nova_powervm.virt.powervm.tasks.vm.Get')
def test_unplug_vif_failures(self, mock_vm):
# Test instance not found handling
mock_vm.execute.side_effect = exc.InstanceNotFound(
instance_id=self.inst)
# Run method
self.assertRaises(exc.InterfaceDetachFailed,
self.drv.unplug_vifs, self.inst, {})
def test_extract_bdm(self):
"""Tests the _extract_bdm method."""
self.assertEqual([], self.drv._extract_bdm(None))
self.assertEqual([], self.drv._extract_bdm({'fake': 'val'}))
fake_bdi = {'block_device_mapping': ['content']}
self.assertListEqual(['content'], self.drv._extract_bdm(fake_bdi))
def test_get_host_ip_addr(self):
self.assertEqual(self.drv.get_host_ip_addr(), '127.0.0.1')
@mock.patch('nova_powervm.virt.powervm.driver.LOG.warning')
@mock.patch('nova.compute.utils.get_machine_ips')
def test_get_host_ip_addr_failure(self, mock_ips, mock_log):
mock_ips.return_value = ['1.1.1.1']
self.drv.get_host_ip_addr()
mock_log.assert_called_once_with(u'my_ip address (%(my_ip)s) was '
u'not found on any of the '
u'interfaces: %(ifaces)s',
{'ifaces': '1.1.1.1',
'my_ip': mock.ANY})
def test_shared_stg_calls(self):
data = self.drv.check_instance_shared_storage_local('context', 'inst')
self.assertTrue(
self.drv.disk_dvr.check_instance_shared_storage_local.called)
self.drv.check_instance_shared_storage_remote('context', data)
self.assertTrue(
self.drv.disk_dvr.check_instance_shared_storage_remote.called)
self.drv.check_instance_shared_storage_cleanup('context', data)
self.assertTrue(
self.drv.disk_dvr.check_instance_shared_storage_cleanup.called)
@mock.patch('pypowervm.tasks.power.power_on')
@mock.patch('pypowervm.tasks.power.power_off')
def test_reboot(self, mock_pwroff, mock_pwron):
entry = mock.Mock()
self.get_inst_wrap.return_value = entry
# VM is in 'not activated' state
# Validate SOFT vs HARD and power_on called with each.
entry.state = pvm_bp.LPARState.NOT_ACTIVATED
self.assertTrue(self.drv.reboot('context', self.inst, None, 'SOFT'))
# Make sure power off is not called
self.assertEqual(0, mock_pwroff.call_count)
mock_pwron.assert_called_with(entry, self.drv.host_uuid)
self.assertTrue(self.drv.reboot('context', self.inst, None, 'HARD'))
# Make sure power off is not called
self.assertEqual(0, mock_pwroff.call_count)
self.assertEqual(2, mock_pwron.call_count)
mock_pwron.assert_called_with(entry, self.drv.host_uuid)
# VM is not in 'not activated' state
# reset mock_pwron
mock_pwron.reset_mock()
entry.state = 'whatever'
self.assertTrue(self.drv.reboot('context', self.inst, None, 'SOFT'))
mock_pwroff.assert_called_with(entry, self.drv.host_uuid,
restart=True,
force_immediate=False)
self.assertEqual(0, mock_pwron.call_count)
self.assertTrue(self.drv.reboot('context', self.inst, None, 'HARD'))
mock_pwroff.assert_called_with(entry, self.drv.host_uuid,
restart=True,
force_immediate=True)
self.assertEqual(0, mock_pwron.call_count)
# If power_off raises an exception, power_on is not called, and the
# exception percolates up.
entry.state = 'whatever'
mock_pwroff.side_effect = pvm_exc.VMPowerOffFailure(lpar_nm='lpar',
reason='reason')
self.assertRaises(pvm_exc.VMPowerOffFailure, self.drv.reboot,
'context', self.inst, None, 'HARD')
# If power_on raises an exception, it percolates up.
entry.state = pvm_bp.LPARState.NOT_ACTIVATED
mock_pwron.side_effect = pvm_exc.VMPowerOnFailure(lpar_nm='lpar',
reason='reason')
self.assertRaises(pvm_exc.VMPowerOnFailure, self.drv.reboot, 'context',
self.inst, None, 'SOFT')
@mock.patch('pypowervm.tasks.vterm.open_remotable_vnc_vterm')
@mock.patch('nova_powervm.virt.powervm.vm.get_pvm_uuid')
def test_get_vnc_console(self, mock_uuid, mock_vterm):
# Mock response
mock_vterm.return_value = '10'
mock_uuid.return_value = 'uuid'
# Invoke
resp = self.drv.get_vnc_console(mock.ANY, self.inst)
# Validate
self.assertEqual('127.0.0.1', resp.host)
self.assertEqual('10', resp.port)
self.assertEqual('uuid', resp.internal_access_path)
mock_vterm.assert_called_once_with(
mock.ANY, 'uuid', mock.ANY, vnc_path='uuid')
@staticmethod
def _fake_bdms():
def _fake_bdm(volume_id, target_lun):
connection_info = {'driver_volume_type': 'fibre_channel',
'data': {'volume_id': volume_id,
'target_lun': target_lun,
'initiator_target_map':
{'21000024F5': ['50050768']}}}
mapping_dict = {'source_type': 'volume', 'volume_id': volume_id,
'destination_type': 'volume',
'connection_info':
jsonutils.dumps(connection_info),
}
bdm_dict = nova_block_device.BlockDeviceDict(mapping_dict)
bdm_obj = bdmobj.BlockDeviceMapping(**bdm_dict)
return nova_virt_bdm.DriverVolumeBlockDevice(bdm_obj)
bdm_list = [_fake_bdm('fake_vol1', 0), _fake_bdm('fake_vol2', 1)]
block_device_info = {'block_device_mapping': bdm_list}
return block_device_info
@mock.patch('nova_powervm.virt.powervm.tasks.image.UpdateTaskState.'
'execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.InstanceDiskToMgmt.'
'execute')
@mock.patch('nova_powervm.virt.powervm.tasks.image.StreamToGlance.execute')
@mock.patch('nova_powervm.virt.powervm.tasks.storage.'
'RemoveInstanceDiskFromMgmt.execute')
def test_snapshot(self, mock_rm, mock_stream, mock_conn, mock_update):
mock_conn.return_value = 'stg_elem', 'vios_wrap', 'disk_path'
self.drv.snapshot('context', self.inst, 'image_id',
'update_task_state')
self.assertEqual(2, mock_update.call_count)
self.assertEqual(1, mock_conn.call_count)
mock_stream.assert_called_with(disk_path='disk_path')
mock_rm.assert_called_with(stg_elem='stg_elem', vios_wrap='vios_wrap',
disk_path='disk_path')
@mock.patch('nova_powervm.virt.powervm.live_migration.LiveMigrationDest')
def test_can_migrate_dest(self, mock_lpm):
mock_lpm.return_value.check_destination.return_value = 'dest_data'
dest_data = self.drv.check_can_live_migrate_destination(
'context', mock.Mock(), 'src_compute_info', 'dst_compute_info')
self.assertEqual('dest_data', dest_data)
def test_can_live_mig_dest_clnup(self):
self.drv.check_can_live_migrate_destination_cleanup(
'context', 'dest_data')
@mock.patch('nova_powervm.virt.powervm.live_migration.LiveMigrationSrc')
def test_can_live_mig_src(self, mock_lpm):
mock_lpm.return_value.check_source.return_value = (
'src_data')
src_data = self.drv.check_can_live_migrate_source(
'context', mock.Mock(), 'dest_check_data')
self.assertEqual('src_data', src_data)
def test_pre_live_migr(self):
block_device_info = self._fake_bdms()
resp = self.drv.pre_live_migration(
'context', self.lpm_inst, block_device_info, 'network_info',
'disk_info', migrate_data='migrate_data')
self.assertIsNotNone(resp)
def test_live_migration(self):
mock_post_meth = mock.Mock()
mock_rec_meth = mock.Mock()
# Good path
self.drv.live_migration(
'context', self.lpm_inst, 'dest', mock_post_meth, mock_rec_meth,
'block_mig', 'migrate_data')
mock_post_meth.assert_called_once_with(
'context', self.lpm_inst, 'dest', mock.ANY, mock.ANY)
self.assertEqual(0, mock_rec_meth.call_count)
# Abort invocation path
self._setup_lpm()
mock_post_meth.reset_mock()
mock_kwargs = {'operation_name': 'op', 'seconds': 10}
self.lpm.live_migration.side_effect = (
pvm_exc.JobRequestTimedOut(**mock_kwargs))
self.assertRaises(
lpm.LiveMigrationFailed, self.drv.live_migration,
'context', self.lpm_inst, 'dest', mock_post_meth, mock_rec_meth,
'block_mig', 'migrate_data')
self.lpm.migration_abort.assert_called_once_with()
mock_rec_meth.assert_called_once_with(
'context', self.lpm_inst, 'dest', mock.ANY, mock.ANY)
self.lpm.rollback_live_migration.assert_called_once_with('context')
self.assertEqual(0, mock_post_meth.call_count)
# Exception path
self._setup_lpm()
mock_post_meth.reset_mock()
mock_rec_meth.reset_mock()
self.lpm.live_migration.side_effect = ValueError()
self.assertRaises(
lpm.LiveMigrationFailed, self.drv.live_migration,
'context', self.lpm_inst, 'dest', mock_post_meth, mock_rec_meth,
'block_mig', 'migrate_data')
mock_rec_meth.assert_called_once_with(
'context', self.lpm_inst, 'dest', mock.ANY, mock.ANY)
self.lpm.rollback_live_migration.assert_called_once_with('context')
self.assertEqual(0, mock_post_meth.call_count)
# Ensure we get LiveMigrationFailed even if recovery fails.
self._setup_lpm()
mock_post_meth.reset_mock()
mock_rec_meth.reset_mock()
self.lpm.live_migration.side_effect = ValueError()
# Cause the recovery method to fail with an exception.
mock_rec_meth.side_effect = ValueError()
self.assertRaises(
lpm.LiveMigrationFailed, self.drv.live_migration,
'context', self.lpm_inst, 'dest', mock_post_meth, mock_rec_meth,
'block_mig', 'migrate_data')
mock_rec_meth.assert_called_once_with(
'context', self.lpm_inst, 'dest', mock.ANY, mock.ANY)
self.lpm.rollback_live_migration.assert_called_once_with('context')
self.assertEqual(0, mock_post_meth.call_count)
def test_rollbk_lpm_dest(self):
self.drv.rollback_live_migration_at_destination(
'context', self.lpm_inst, 'network_info', 'block_device_info')
self.assertRaises(
KeyError, lambda: self.drv.live_migrations[self.lpm_inst.uuid])
def test_post_live_mig(self):
self.drv.post_live_migration('context', self.lpm_inst, None)
self.lpm.post_live_migration.assert_called_once_with([], None)
def test_post_live_mig_src(self):
self.drv.post_live_migration_at_source('context', self.lpm_inst,
'network_info')
self.lpm.post_live_migration_at_source.assert_called_once_with(
'network_info')
def test_post_live_mig_dest(self):
self.drv.post_live_migration_at_destination(
'context', self.lpm_inst, 'network_info')
self.lpm.post_live_migration_at_destination.assert_called_once_with(
'network_info', [])
@mock.patch('pypowervm.tasks.memory.calculate_memory_overhead_on_host')
def test_estimate_instance_overhead(self, mock_calc_over):
mock_calc_over.return_value = ('2048', '96')
inst_info = self.inst.get_flavor()
inst_info.extra_specs = {}
overhead = self.drv.estimate_instance_overhead(inst_info)
self.assertEqual({'memory_mb': '2048'}, overhead)
# Flavor having extra_specs
inst_info.extra_specs = {'powervm:max_mem': 4096}
overhead = self.drv.estimate_instance_overhead(inst_info)
mock_calc_over.assert_called_with(self.apt, self.drv.host_uuid,
{'max_mem': 4096})
self.assertEqual({'memory_mb': '2048'}, overhead)
# Test when instance passed is dict
inst_info = obj_base.obj_to_primitive(inst_info)
overhead = self.drv.estimate_instance_overhead(inst_info)
self.assertEqual({'memory_mb': '2048'}, overhead)
# When instance_info is None
overhead = self.drv.estimate_instance_overhead(None)
self.assertEqual({'memory_mb': 0}, overhead)
# Test when instance Object is passed
overhead = self.drv.estimate_instance_overhead(self.inst)
self.assertEqual({'memory_mb': '2048'}, overhead)
def test_vol_drv_iter(self):
block_device_info = self._fake_bdms()
vol_adpt = mock.Mock()
def _get_results(block_device_info=None, bdms=None):
# Patch so we get the same mock back each time.
with mock.patch.object(self.drv, '_get_inst_vol_adpt',
return_value=vol_adpt):
return [
(bdm, vol_drv) for bdm, vol_drv in self.drv._vol_drv_iter(
'context', self.inst,
block_device_info=block_device_info, bdms=bdms)]
def validate(results):
# For each good call, we should get back two bdms / vol_adpt
self.assertEqual(
'fake_vol1',
results[0][0]['connection_info']['data']['volume_id'])
self.assertEqual(vol_adpt, results[0][1])
self.assertEqual(
'fake_vol2',
results[1][0]['connection_info']['data']['volume_id'])
self.assertEqual(vol_adpt, results[1][1])
# Send block device info
results = _get_results(block_device_info=block_device_info)
validate(results)
# Same results with bdms
results = _get_results(bdms=self.drv._extract_bdm(block_device_info))
validate(results)
# Empty bdms
self.assertEqual([], _get_results(bdms=[]))
def test_build_vol_drivers(self):
# This utility just returns a list of drivers from the _vol_drv_iter()
# iterator so mock it and ensure the drivers are returned.
vals = [('bdm0', 'drv0'), ('bdm1', 'drv1')]
with mock.patch.object(self.drv, '_vol_drv_iter', return_value=vals):
drivers = self.drv._build_vol_drivers('context', 'instance')
self.assertEqual(['drv0', 'drv1'], drivers)
@mock.patch.object(objects.BlockDeviceMappingList, 'get_by_instance_uuid')
@mock.patch.object(virt_driver, 'get_block_device_info')
def test_get_block_device_info(self, mock_bk_dev, mock_bdml):
mock_bk_dev.return_value = 'info'
self.assertEqual('info',
self.drv._get_block_device_info('ctx', self.inst))
class TestNovaEventHandler(test.TestCase):
def setUp(self):
super(TestNovaEventHandler, self).setUp()
self.mock_driver = mock.Mock()
self.handler = driver.NovaEventHandler(self.mock_driver)
@mock.patch.object(vm, 'get_instance')
@mock.patch.object(vm, 'get_vm_qp')
def test_events(self, mock_qprops, mock_get_inst):
# Test events
event_data = [
{
'EventType': 'NEW_CLIENT',
'EventData': '',
'EventID': '1452692619554',
'EventDetail': '',
},
{
'EventType': 'MODIFY_URI',
'EventData': 'http://localhost:12080/rest/api/uom/Managed'
'System/c889bf0d-9996-33ac-84c5-d16727083a77',
'EventID': '1452692619555',
'EventDetail': 'Other',
},
{
'EventType': 'MODIFY_URI',
'EventData': 'http://localhost:12080/rest/api/uom/Managed'
'System/c889bf0d-9996-33ac-84c5-d16727083a77/'
'LogicalPartition/794654F5-B6E9-4A51-BEC2-'
'A73E41EAA938',
'EventID': '1452692619563',
'EventDetail': 'ReferenceCode,Other',
},
{
'EventType': 'MODIFY_URI',
'EventData': 'http://localhost:12080/rest/api/uom/Managed'
'System/c889bf0d-9996-33ac-84c5-d16727083a77/'
'LogicalPartition/794654F5-B6E9-4A51-BEC2-'
'A73E41EAA938',
'EventID': '1452692619566',
'EventDetail': 'RMCState,PartitionState,Other',
},
]
mock_qprops.return_value = pvm_bp.LPARState.RUNNING
mock_get_inst.return_value = powervm.TEST_INST1
self.handler.process(event_data)
self.assertTrue(self.mock_driver.emit_event.called)