Update settings during deploying an IBMi VM.
IBMi VM depends on tagged io, ipl source and keylock position to determine PowerOn mode. During deploying an new IBMi VM, tagged io, IPL source and keylock position must be set. This change set will add a new task in spawn flow to set those required attributes for IBMi. Change-Id: Idd5a305ff9aa6de11920fbc61099a10a0b2df748
This commit is contained in:
parent
2af57a1835
commit
20fd990195
@ -187,6 +187,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
@ -222,6 +223,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""Validates the PowerVM spawn w/ config drive operations."""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = True
|
||||
@ -263,6 +265,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
@ -325,6 +328,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
@ -374,6 +378,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
@ -420,6 +425,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""Validates the PowerVM driver operations. Will do a rollback."""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
@ -444,6 +450,114 @@ class TestPowerVMDriver(test.TestCase):
|
||||
# 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.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'ibmi'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_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()
|
||||
image_meta = {}
|
||||
# Invoke the method.
|
||||
self.drv.spawn('context', inst, image_meta,
|
||||
'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,
|
||||
inst, my_flavor)
|
||||
|
||||
self.assertTrue(mock_boot_conn_type.called)
|
||||
self.assertTrue(mock_update_lod_src.called)
|
||||
|
||||
# Power on was called
|
||||
self.assertTrue(mock_pwron.called)
|
||||
|
||||
# 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.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'ibmi'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_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', inst, mock.Mock(),
|
||||
'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, inst, my_flavor)
|
||||
self.assertTrue(mock_update_lod_src.called)
|
||||
self.assertTrue(mock_pwron.called)
|
||||
# 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.'
|
||||
@ -459,6 +573,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""Validates the rollback if failure occurs on disk create."""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
@ -493,6 +608,7 @@ class TestPowerVMDriver(test.TestCase):
|
||||
"""Validates the rollbacks on a volume connect failure."""
|
||||
# Set up the mocks to the tasks.
|
||||
inst = objects.Instance(**powervm.TEST_INSTANCE)
|
||||
inst.system_metadata = {'image_os_distro': 'rhel'}
|
||||
my_flavor = inst.get_flavor()
|
||||
mock_get_flv.return_value = my_flavor
|
||||
mock_cfg_drv.return_value = False
|
||||
|
@ -429,3 +429,21 @@ class TestVM(test.TestCase):
|
||||
self.assertEqual(EXPECTED, vm.norm_mac("1234567890ab"))
|
||||
self.assertEqual(EXPECTED, vm.norm_mac("12:34:56:78:90:AB"))
|
||||
self.assertEqual(EXPECTED, vm.norm_mac("1234567890AB"))
|
||||
|
||||
@mock.patch('pypowervm.tasks.ibmi.update_ibmi_settings')
|
||||
@mock.patch('nova_powervm.virt.powervm.vm.get_instance_wrapper')
|
||||
def test_update_ibmi_settings(self, mock_lparw, mock_ibmi):
|
||||
instance = mock.MagicMock()
|
||||
# Test update load source with vscsi boot
|
||||
boot_type = 'vscsi'
|
||||
vm.update_ibmi_settings(
|
||||
self.apt, instance, 'host-uuid', boot_type)
|
||||
mock_ibmi.assert_called_once_with(
|
||||
self.apt, mock.ANY, 'vscsi')
|
||||
mock_ibmi.reset_mock()
|
||||
# Test update load source with npiv boot
|
||||
boot_type = 'npiv'
|
||||
vm.update_ibmi_settings(
|
||||
self.apt, instance, 'host-uuid', boot_type)
|
||||
mock_ibmi.assert_called_once_with(
|
||||
self.apt, mock.ANY, 'npiv')
|
||||
|
@ -49,6 +49,7 @@ from pypowervm.wrappers import virtual_io_server as pvm_vios
|
||||
|
||||
from nova_powervm.virt.powervm.disk import driver as disk_dvr
|
||||
from nova_powervm.virt.powervm import host as pvm_host
|
||||
from nova_powervm.virt.powervm import image as img
|
||||
from nova_powervm.virt.powervm import live_migration as lpm
|
||||
from nova_powervm.virt.powervm import mgmt
|
||||
from nova_powervm.virt.powervm.tasks import image as tf_img
|
||||
@ -270,6 +271,14 @@ class PowerVMDriver(driver.ComputeDriver):
|
||||
# connection' tasks. This will run all the connections in parallel.
|
||||
flow_spawn.add(stg_ftsk)
|
||||
|
||||
# Update load source of IBMi VM
|
||||
distro = instance.system_metadata.get('image_os_distro', '')
|
||||
if distro.lower() == img.OSDistro.OS400:
|
||||
boot_type = self._get_boot_connectivity_type(
|
||||
context, bdms, block_device_info)
|
||||
flow_spawn.add(tf_vm.UpdateIBMiSettings(
|
||||
self.adapter, instance, self.host_uuid, boot_type))
|
||||
|
||||
# Last step is to power on the system.
|
||||
flow_spawn.add(tf_vm.PowerOn(self.adapter, self.host_uuid, instance))
|
||||
|
||||
@ -1321,6 +1330,30 @@ class PowerVMDriver(driver.ComputeDriver):
|
||||
return vol_cls(self.adapter, self.host_uuid,
|
||||
instance, conn_info, stg_ftsk=stg_ftsk)
|
||||
|
||||
def _get_boot_connectivity_type(self, context, bdms, block_device_info):
|
||||
"""Get connectivity information for the instance.
|
||||
|
||||
:param context: security context
|
||||
:param bdms: The BDMs for the operation. If boot volume of
|
||||
the instance is ssp lu or local disk, the bdms is None.
|
||||
:param block_device_info: Instance volume block device info.
|
||||
:return: Returns the boot connectivity type,
|
||||
If boot volume is a npiv volume, returns 'npiv'
|
||||
Otherwise, return 'vscsi'.
|
||||
"""
|
||||
# Set default boot_conn_type as 'vscsi'
|
||||
boot_conn_type = 'vscsi'
|
||||
if self._is_booted_from_volume(block_device_info) and bdms is not None:
|
||||
for bdm in bdms:
|
||||
if bdm.get('boot_index') == 0:
|
||||
conn_info = bdm.get('connection_info')
|
||||
connectivity_type = conn_info['data']['connection-type']
|
||||
boot_conn_type = ('vscsi' if connectivity_type ==
|
||||
'pv_vscsi' else connectivity_type)
|
||||
return boot_conn_type
|
||||
else:
|
||||
return boot_conn_type
|
||||
|
||||
|
||||
def _inst_dict(input_dict):
|
||||
"""Builds a dictionary with instances as values based on the input classes.
|
||||
|
@ -19,6 +19,17 @@
|
||||
from nova import utils
|
||||
|
||||
|
||||
class OSDistro(object):
|
||||
"""Mirror of image os distro.Enum."""
|
||||
AIX = 'aix'
|
||||
RHEL = 'rhel'
|
||||
OS400 = 'ibmi'
|
||||
SLES = 'sles'
|
||||
UBUNTU = 'ubuntu'
|
||||
UNKNOWN = 'Unknown'
|
||||
ALL_VALUES = (AIX, RHEL, OS400, SLES, UBUNTU, UNKNOWN)
|
||||
|
||||
|
||||
def stream_blockdev_to_glance(context, image_api, image_id, metadata, devpath):
|
||||
"""Stream the entire contents of a block device to a glance image.
|
||||
|
||||
|
@ -165,3 +165,28 @@ class Delete(task.Task):
|
||||
def execute(self):
|
||||
LOG.info(_LI('Deleting instance %s from system.'), self.instance.name)
|
||||
vm.dlt_lpar(self.adapter, self.lpar_uuid)
|
||||
|
||||
|
||||
class UpdateIBMiSettings(task.Task):
|
||||
"""The task to update settings of an ibmi instance."""
|
||||
|
||||
def __init__(self, adapter, instance, host_uuid, boot_type):
|
||||
"""Create the Task to update settings of the IBMi VM.
|
||||
|
||||
:param adapter: The adapter for the pypowervm API.
|
||||
:param instance: The nova instance.
|
||||
:param host_uuid: The host's PowerVM UUID.
|
||||
:param boot_type: The boot type of the instance.
|
||||
"""
|
||||
super(UpdateIBMiSettings, self).__init__(
|
||||
name='update_ibmi_settings')
|
||||
self.adapter = adapter
|
||||
self.instance = instance
|
||||
self.host_uuid = host_uuid
|
||||
self.boot_type = boot_type
|
||||
|
||||
def execute(self):
|
||||
LOG.info(_LI('Update settings of instance %s.'),
|
||||
self.instance.name)
|
||||
vm.update_ibmi_settings(
|
||||
self.adapter, self.instance, self.host_uuid, self.boot_type)
|
||||
|
@ -26,6 +26,7 @@ from nova.virt import hardware
|
||||
from pypowervm import exceptions as pvm_exc
|
||||
from pypowervm.helpers import log_helper as pvm_log
|
||||
from pypowervm.tasks import cna
|
||||
from pypowervm.tasks import ibmi
|
||||
from pypowervm.tasks import power
|
||||
from pypowervm.tasks import vterm
|
||||
from pypowervm.utils import lpar_builder as lpar_bldr
|
||||
@ -637,3 +638,16 @@ def norm_mac(mac):
|
||||
"""
|
||||
mac = mac.lower().replace(':', '')
|
||||
return ':'.join(mac[i:i + 2] for i in range(0, len(mac), 2))
|
||||
|
||||
|
||||
def update_ibmi_settings(adapter, instance, host_uuid, boot_type):
|
||||
"""Update settings of IBMi VMs on the instance.
|
||||
|
||||
:param adapter: The pypowervm adapter.
|
||||
:param instance: The nova instance.
|
||||
:param host_uuid: The host system UUID.
|
||||
:param boot_type: The boot connectivity type of the instance.
|
||||
"""
|
||||
lpar_wrap = get_instance_wrapper(adapter, instance, host_uuid)
|
||||
entry = ibmi.update_ibmi_settings(adapter, lpar_wrap, boot_type)
|
||||
entry.update()
|
||||
|
Loading…
Reference in New Issue
Block a user