Merge "PowerVM Driver: power_on/off and reboot"
This commit is contained in:
@@ -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)
|
||||
|
@@ -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']
|
||||
|
@@ -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.
|
||||
|
@@ -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.
|
||||
|
||||
|
Reference in New Issue
Block a user