Merge "PowerVM Driver: power_on/off and reboot"

This commit is contained in:
Jenkins
2017-04-21 17:43:44 +00:00
committed by Gerrit Code Review
4 changed files with 149 additions and 8 deletions

View File

@@ -134,3 +134,33 @@ class TestPowerVMDriver(test.NoDBTestCase):
self.assertRaises(ValueError,
self.drv.destroy, 'context', self.inst, [],
block_device_info={})
@mock.patch('nova.virt.powervm.vm.power_on')
def test_power_on(self, mock_power_on):
self.drv.power_on('context', self.inst, 'network_info')
mock_power_on.assert_called_once_with(self.adp, self.inst)
@mock.patch('nova.virt.powervm.vm.power_off')
def test_power_off(self, mock_power_off):
self.drv.power_off(self.inst)
mock_power_off.assert_called_once_with(
self.adp, self.inst, force_immediate=True, timeout=None)
@mock.patch('nova.virt.powervm.vm.power_off')
def test_power_off_timeout(self, mock_power_off):
# Long timeout (retry interval means nothing on powervm)
self.drv.power_off(self.inst, timeout=500, retry_interval=10)
mock_power_off.assert_called_once_with(
self.adp, self.inst, force_immediate=False, timeout=500)
@mock.patch('nova.virt.powervm.vm.reboot')
def test_reboot_soft(self, mock_reboot):
inst = mock.Mock()
self.drv.reboot('context', inst, 'network_info', 'SOFT')
mock_reboot.assert_called_once_with(self.adp, inst, False)
@mock.patch('nova.virt.powervm.vm.reboot')
def test_reboot_hard(self, mock_reboot):
inst = mock.Mock()
self.drv.reboot('context', inst, 'network_info', 'HARD')
mock_reboot.assert_called_once_with(self.adp, inst, True)

View File

@@ -308,9 +308,9 @@ class TestVM(test.NoDBTestCase):
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
mock_power_off.reset_mock()
mock_lock.reset_mock()
vm.power_off(None, self.inst, force_immediate=True)
mock_power_off.assert_called_once_with(entry, None,
force_immediate=True)
vm.power_off(None, self.inst, force_immediate=True, timeout=5)
mock_power_off.assert_called_once_with(
entry, None, force_immediate=True, timeout=5)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
@mock.patch('pypowervm.tasks.power.power_off', autospec=True)
@@ -330,6 +330,41 @@ class TestVM(test.NoDBTestCase):
mock_power_off.side_effect = ValueError()
self.assertRaises(ValueError, vm.power_off, None, self.inst)
@mock.patch('pypowervm.tasks.power.power_on', autospec=True)
@mock.patch('pypowervm.tasks.power.power_off', autospec=True)
@mock.patch('oslo_concurrency.lockutils.lock', autospec=True)
@mock.patch('nova.virt.powervm.vm.get_instance_wrapper')
def test_reboot(self, mock_wrap, mock_lock, mock_pwroff, mock_pwron):
entry = mock.Mock(state=pvm_bp.LPARState.NOT_ACTIVATED)
mock_wrap.return_value = entry
# No power_off
vm.reboot('adap', self.inst, False)
mock_lock.assert_called_once_with('power_%s' % self.inst.uuid)
mock_wrap.assert_called_once_with('adap', self.inst)
mock_pwron.assert_called_once_with(entry, None)
self.assertEqual(0, mock_pwroff.call_count)
mock_pwron.reset_mock()
# power_off (no power_on)
entry.state = pvm_bp.LPARState.RUNNING
for force in (False, True):
mock_pwroff.reset_mock()
vm.reboot('adap', self.inst, force)
self.assertEqual(0, mock_pwron.call_count)
mock_pwroff.assert_called_once_with(
entry, None, force_immediate=force, restart=True)
# PowerVM error is converted
mock_pwroff.side_effect = pvm_exc.TimeoutError("Timed out")
self.assertRaises(exception.InstanceRebootFailure,
vm.reboot, 'adap', self.inst, True)
# Non-PowerVM error is raised directly
mock_pwroff.side_effect = ValueError
self.assertRaises(ValueError, vm.reboot, 'adap', self.inst, True)
@mock.patch('oslo_serialization.jsonutils.loads')
def test_get_vm_qp(self, mock_loads):
self.apt.helpers = ['helper1', pvm_log.log_helper, 'helper3']

View File

@@ -189,3 +189,49 @@ class PowerVMDriver(driver.ComputeDriver):
LOG.exception("PowerVM error during destroy.", instance=instance)
# Convert to a Nova exception
raise exc.InstanceTerminationFailure(reason=six.text_type(e))
def power_off(self, instance, timeout=0, retry_interval=0):
"""Power off the specified instance.
:param instance: nova.objects.instance.Instance
:param timeout: time to wait for GuestOS to shutdown
:param retry_interval: How often to signal guest while
waiting for it to shutdown
"""
self._log_operation('power_off', instance)
force_immediate = (timeout == 0)
timeout = timeout or None
vm.power_off(self.adapter, instance, force_immediate=force_immediate,
timeout=timeout)
def power_on(self, context, instance, network_info,
block_device_info=None):
"""Power on the specified instance.
:param instance: nova.objects.instance.Instance
"""
self._log_operation('power_on', instance)
vm.power_on(self.adapter, instance)
def reboot(self, context, instance, network_info, reboot_type,
block_device_info=None, bad_volumes_callback=None):
"""Reboot the specified instance.
After this is called successfully, the instance's state
goes back to power_state.RUNNING. The virtualization
platform should ensure that the reboot action has completed
successfully even in cases in which the underlying domain/vm
is paused or halted/stopped.
:param instance: nova.objects.instance.Instance
:param network_info:
:py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
:param reboot_type: Either a HARD or SOFT reboot
:param block_device_info: Info pertaining to attached volumes
:param bad_volumes_callback: Function to handle any bad volumes
encountered
"""
self._log_operation(reboot_type + ' reboot', instance)
vm.reboot(self.adapter, instance, reboot_type == 'HARD')
# pypowervm exceptions are sufficient to indicate real failure.
# Otherwise, pypowervm thinks the instance is up.

View File

@@ -128,12 +128,15 @@ def power_on(adapter, instance):
raise exc.InstancePowerOnFailure(reason=six.text_type(e))
def power_off(adapter, instance, force_immediate=False):
def power_off(adapter, instance, force_immediate=False, timeout=None):
"""Powers off a VM.
:param adapter: A pypowervm.adapter.Adapter.
:param instance: The nova instance to power off.
:param force_immediate: (Optional, Default False) Should it be immediately
:param timeout: (Optional, Default None) How long to wait for the job
to complete. By default, is None which indicates it should
use the default from pypowervm's power off method.
:param force_immediate: (Optional, Default False) Should it be immediately
shut down.
:raises: InstancePowerOffFailure
"""
@@ -150,9 +153,10 @@ def power_off(adapter, instance, force_immediate=False):
try:
LOG.debug("Power off executing for instance %(inst)s.",
{'inst': instance.name})
force_flag = (power.Force.TRUE if force_immediate
else power.Force.ON_FAILURE)
power.power_off(entry, None, force_immediate=force_flag)
kwargs = {'timeout': timeout} if timeout else {}
force = (power.Force.TRUE if force_immediate
else power.Force.ON_FAILURE)
power.power_off(entry, None, force_immediate=force, **kwargs)
except pvm_exc.Error as e:
LOG.exception("PowerVM error during power_off.",
instance=instance)
@@ -162,6 +166,32 @@ def power_off(adapter, instance, force_immediate=False):
{'inst': instance.name})
def reboot(adapter, instance, hard):
"""Reboots a VM.
:param adapter: A pypowervm.adapter.Adapter.
:param instance: The nova instance to reboot.
:param hard: Boolean True if hard reboot, False otherwise.
:raises: InstanceRebootFailure
"""
# Synchronize power-on and power-off ops on a given instance
with lockutils.lock('power_%s' % instance.uuid):
try:
entry = get_instance_wrapper(adapter, instance)
if entry.state != pvm_bp.LPARState.NOT_ACTIVATED:
power.power_off(entry, None, force_immediate=hard,
restart=True)
else:
# pypowervm does NOT throw an exception if "already down".
# Any other exception from pypowervm is a legitimate failure;
# let it raise up.
# If we get here, pypowervm thinks the instance is down.
power.power_on(entry, None)
except pvm_exc.Error as e:
LOG.exception("PowerVM error during reboot.", instance=instance)
raise exc.InstanceRebootFailure(reason=six.text_type(e))
def delete_lpar(adapter, instance):
"""Delete an LPAR.