def test_pwroff_new_opts(self): """Test power_off where add_parms is PowerOffOpts (not legacy).""" part = self.mock_partition() # VSP hard self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=shutdown', 'immediate=true'}) power.power_off(part, None, add_parms=popts.PowerOffOpts().vsp_hard()) self.assertEqual(1, self.run_job.call_count) self.run_job.reset_mock() # VSP normal self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=shutdown'}) power.power_off(part, None, add_parms=popts.PowerOffOpts().vsp_normal()) self.run_job.reset_mock() # OS immediate self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=osshutdown', 'immediate=true'}) power.power_off(part, None, add_parms=popts.PowerOffOpts().os_immediate()) self.run_job.reset_mock() # OS normal self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=osshutdown'}) power.power_off(part, None, add_parms=popts.PowerOffOpts().os_normal())
def test_pwrop_stop(self): """Test PowerOp.stop.""" # If RMC is down, VSP normal - make sure the 'immediate' flag goes away part = self.mock_partition(rmc_state=pvm_bp.RMCState.INACTIVE) self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=shutdown'}) power.PowerOp.stop( part, opts=popts.PowerOffOpts().immediate().soft_detect(part)) self.assertEqual(1, self.run_job.call_count) self.run_job.reset_mock() # Default parameters - the method figures out whether to do OS shutdown part = self.mock_partition() self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=osshutdown', 'immediate=true'}) power.PowerOp.stop( part, opts=popts.PowerOffOpts().immediate().soft_detect(part)) self.assertEqual(1, self.run_job.call_count) self.run_job.reset_mock() # Non-default optional params ignored, timeout self.run_job.side_effect = self.validate_run( part, ex_parms={ 'operation=osshutdown', 'immediate=true', 'restart=true' }, ex_timeout=100, ex_synch=False, result=self.etimeout()) self.assertRaises(pexc.VMPowerOffTimeout, power.PowerOp.stop, part, opts=popts.PowerOffOpts(legacy_add_parms={ 'one': 1, 'foo': 'bar' }).os_immediate().restart(), timeout=100, synchronous=False) self.assertEqual(1, self.run_job.call_count) self.run_job.reset_mock() # VSP normal, fail self.run_job.side_effect = self.validate_run( part, ex_parms={'operation=shutdown'}, result=self.efail()) self.assertRaises(pexc.VMPowerOffFailure, power.PowerOp.stop, part, opts=popts.PowerOffOpts().vsp_normal()) self.assertEqual(1, self.run_job.call_count)
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: if hard: power.PowerOp.stop( entry, opts=popts.PowerOffOpts().vsp_hard().restart()) else: power.power_off_progressive(entry, 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 test_pwrop_stop_no_rmc(self): """Test PowerOp.stop with bad RMC state.""" part = self.mock_partition(rmc_state=pvm_bp.RMCState.INACTIVE) self.assertRaises(pexc.OSShutdownNoRMC, power.PowerOp.stop, part, opts=popts.PowerOffOpts().os_normal()) self.run_job.assert_not_called()
def _pwroff_soft_standard_flow(part, restart, timeout): """Normal (non-hard) power-off retry flow for non-IBMi partitions. START | +---VMPowerOffTimeout-------------------------------------+ V | V ======== VMPowerOffFailure ========== VMPowerOffFailure ============ OS immed ---- or ---> VSP normal ---- or ---> return False ======== OSShutdownNoRMC ========== VMPowerOffTimeout ============ | _________________________/ |/ SUCCESS V =========== return True =========== :param part restart timeout: See power_off. :return: True if the power-off succeeded; False otherwise. :raise VMPowerOffTimeout: If the last power-off attempt timed out. :raise VMPowerOffFailure: If the last power-off attempt failed. """ # For backward compatibility, OS shutdown is always immediate. We don't # let PowerOn decide whether to use OS or VSP; instead we trap # OSShutdownNoRMC (which is very quick) so we can keep this progression # linear. opts = popts.PowerOffOpts().restart(value=restart) # ==> OS immediate try: PowerOp.stop(part, opts=opts.os_immediate(), timeout=timeout) return True except pexc.VMPowerOffTimeout: LOG.warning( _("Non-IBMi OS immediate shutdown timed out. Trying VSP " "hard shutdown. Partition: %s"), part.name) return False except pexc.VMPowerOffFailure: LOG.warning( _("Non-IBMi OS immediate shutdown failed. Trying VSP " "normal shutdown. Partition: %s"), part.name) # Fall through to VSP normal, but with default timeout timeout = CONF.pypowervm_job_request_timeout except pexc.OSShutdownNoRMC as error: LOG.warning(error.args[0]) # Fall through to VSP normal # ==> VSP normal try: PowerOp.stop(part, opts.vsp_normal(), timeout=timeout) return True except pexc.VMPowerOffFailure: LOG.warning("Non-IBMi VSP normal shutdown failed. Partition: %s", part.name) return False
def stop(cls, part, opts=None, timeout=CONF.pypowervm_job_request_timeout, synchronous=True): """Power off a partition. :param part: LPAR/VIOS wrapper indicating the partition to power off. :param opts: An instance of power_opts.PowerOffOpts indicating the type of shutdown to perform, and any additional options. If not specified, PowerOffOpts.soft_detect is used, with no restart. :param timeout: value in seconds for specifying how long to wait for the Job to complete. :param synchronous: If True, this method will not return until the Job completes (whether success or failure) or times out. If False, this method will return as soon as the Job has started on the server (that is, achieved any state beyond NOT_ACTIVE). Note that timeout is still possible in this case. :raise VMPowerOffTimeout: If the Job timed out. :raise VMPowerOffFailure: If the Job failed for some reason other than that the partition was already powered off, and restart was not requested. :return: A PowerOp instance which can be invoked via the run method. :raise OSShutdownNoRMC: OP_PWROFF_OS was requested on a non-IBMi partition with no RMC connection. """ if opts is None: opts = popts.PowerOffOpts().soft_detect(part) if opts.is_os and not opts.can_os_shutdown(part): raise pexc.OSShutdownNoRMC(lpar_nm=part.name) try: cls._run(part, opts, timeout, synchronous=synchronous) except pexc.JobRequestTimedOut as error: LOG.exception(error) raise pexc.VMPowerOffTimeout(lpar_nm=part.name, timeout=timeout) except pexc.JobRequestFailed as error: emsg = six.text_type(error) # If already powered off and not a reboot, don't send exception if (any(err_prefix in emsg for err_prefix in _ALREADY_POWERED_OFF_ERRS) and not opts.is_restart): LOG.warning(_("Partition %s already powered off."), part.name) return LOG.exception(error) raise pexc.VMPowerOffFailure(lpar_nm=part.name, reason=emsg)
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 shut down. :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. :return: True if the instance was stopped. False if it was not in a stoppable state. :raises: InstancePowerOffFailure """ # Synchronize power-on and power-off ops on a given instance with lockutils.lock('power_%s' % instance.uuid): entry = get_instance_wrapper(adapter, instance) # Get the current state and see if we can stop the VM LOG.debug( "Power off requested for instance in state %(state)s. Force " "Immediate Flag: %(force)s.", { 'state': entry.state, 'force': force_immediate }, instance=instance) if entry.state in POWERVM_STOPABLE_STATE: # Now stop the lpar try: LOG.debug("Power off executing.", instance=instance) kwargs = {'timeout': timeout} if timeout else {} if force_immediate: power.PowerOp.stop(entry, opts=popts.PowerOffOpts().vsp_hard(), **kwargs) else: power.power_off_progressive(entry, **kwargs) except Exception as e: LOG.exception("Failed to power off instance.", instance=instance) raise exception.InstancePowerOffFailure( reason=six.text_type(e)) return True else: LOG.debug("Power off not required.", instance=instance) return False
def _power_off_progressive(part, timeout, restart, ibmi_immed=False): # Do the progressive-retry sequence appropriate to the partition type. if part.env == bp.LPARType.OS400: # The IBMi progression. if _pwroff_soft_ibmi_flow(part, restart, ibmi_immed, timeout): return # Fall through to VSP hard else: # The non-IBMi progression. if _pwroff_soft_standard_flow(part, restart, timeout): return # Fall through to VSP hard # If we got here, force_immediate == ON_FAILURE, so fall back to VSP hard. # Let this one finish or raise. # ==> VSP hard LOG.warning(_("VSP hard shutdown with default timeout. Partition: %s"), part.name) PowerOp.stop(part, popts.PowerOffOpts().vsp_hard().restart(value=restart))
def test_power_off_opts(self): # Can OS shutdown? ltyp = bp.LPARType rmcs = bp.RMCState for env, rmc, exp in ((ltyp.AIXLINUX, rmcs.ACTIVE, True), (ltyp.AIXLINUX, rmcs.BUSY, False), (ltyp.AIXLINUX, rmcs.INACTIVE, False), (ltyp.OS400, rmcs.ACTIVE, True), (ltyp.OS400, rmcs.BUSY, True), (ltyp.OS400, rmcs.INACTIVE, True), (ltyp.VIOS, rmcs.ACTIVE, True), (ltyp.VIOS, rmcs.BUSY, False), (ltyp.VIOS, rmcs.INACTIVE, False)): self.assertEqual(exp, popts.PowerOffOpts.can_os_shutdown( mock.Mock(env=env, rmc_state=rmc))) # Default init poo = popts.PowerOffOpts() self.assertEqual('PowerOff()', str(poo)) self.assertEqual('PowerOff', poo.JOB_SUFFIX) self.assertFalse(poo.is_param_set(popts.PowerOffOperation.KEY)) # Legacy add_parms init. Unknown keys are ignored. poo = popts.PowerOffOpts( legacy_add_parms=dict(operation='shutdown', foo=1, restart='true', bar=2, immediate='true')) self.assertEqual( 'PowerOff(immediate=true, operation=shutdown, restart=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertFalse(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Now an "empty" one poo = popts.PowerOffOpts(legacy_add_parms=dict(foo=1, bar=2)) self.assertEqual('PowerOff()', str(poo)) self.assertFalse(poo.is_immediate) self.assertFalse(poo.is_restart) self.assertFalse(poo.is_os) self.assertFalse(poo.is_param_set(popts.PowerOffOperation.KEY)) # Immediate self.assertIs(poo, poo.immediate()) self.assertEqual('PowerOff(immediate=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertFalse(poo.is_restart) self.assertFalse(poo.is_os) self.assertFalse(poo.is_param_set(popts.PowerOffOperation.KEY)) # Restart self.assertIs(poo, poo.restart()) self.assertEqual( 'PowerOff(immediate=true, restart=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertFalse(poo.is_os) self.assertFalse(poo.is_param_set(popts.PowerOffOperation.KEY)) # Operation self.assertIs(poo, poo.operation(popts.PowerOffOperation.DUMPRESTART)) self.assertEqual( 'PowerOff(immediate=true, operation=dumprestart, restart=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertFalse(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # OS shutdown self.assertIs(poo, poo.operation(popts.PowerOffOperation.OS)) self.assertEqual( 'PowerOff(immediate=true, operation=osshutdown, restart=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Booleans can be shut off self.assertIs(poo, poo.immediate(value=False)) self.assertEqual('PowerOff(operation=osshutdown, restart=true)', str(poo)) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_os) self.assertIs(poo, poo.restart(value=False)) self.assertEqual('PowerOff(operation=osshutdown)', str(poo)) self.assertFalse(poo.is_immediate) self.assertFalse(poo.is_restart) self.assertTrue(poo.is_os) # "Smart" methods. Make sure restart is preserved every time we change poo.restart() # OS immediate self.assertIs(poo, poo.os_immediate()) self.assertEqual('PowerOff(immediate=true, operation=osshutdown, ' 'restart=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # OS normal (wipes out immediate) self.assertIs(poo, poo.os_normal()) self.assertEqual('PowerOff(operation=osshutdown, restart=true)', str(poo)) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # VSP hard self.assertIs(poo, poo.vsp_hard()) self.assertEqual('PowerOff(immediate=true, operation=shutdown, ' 'restart=true)', str(poo)) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertFalse(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # VSP normal (wipes out immediate) self.assertIs(poo, poo.vsp_normal()) self.assertEqual('PowerOff(operation=shutdown, restart=true)', str(poo)) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertFalse(poo.is_os) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Soft detect part = mock.Mock(env=ltyp.AIXLINUX, rmc_state=rmcs.ACTIVE) self.assertIs(poo, poo.soft_detect(part)) self.assertTrue(poo.is_os) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Explicit normal shutdown self.assertIs(poo, poo.soft_detect(part, immed_if_os=False)) self.assertTrue(poo.is_os) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Explicit immediate OS shutdown self.assertIs(poo, poo.soft_detect(part, immed_if_os=True)) self.assertTrue(poo.is_os) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Can't OS shutdown part = mock.Mock(env=ltyp.VIOS, rmc_state=rmcs.BUSY) self.assertIs(poo, poo.soft_detect(part)) self.assertFalse(poo.is_os) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # immed_if_os ignored self.assertIs(poo, poo.soft_detect(part, immed_if_os=True)) self.assertFalse(poo.is_os) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertIs(poo, poo.soft_detect(part, immed_if_os=False)) self.assertFalse(poo.is_os) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertEqual('PowerOff(operation=shutdown, restart=true)', str(poo)) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # IBMi defaults to OS normal part = mock.Mock(env=ltyp.OS400, rmc_state=rmcs.INACTIVE) self.assertIs(poo, poo.soft_detect(part)) self.assertTrue(poo.is_os) self.assertFalse(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY)) # Explicit immediate self.assertIs(poo, poo.soft_detect(part, immed_if_os=True)) self.assertTrue(poo.is_os) self.assertTrue(poo.is_immediate) self.assertTrue(poo.is_restart) self.assertTrue(poo.is_param_set(popts.PowerOffOperation.KEY))
def _pwroff_soft_ibmi_flow(part, restart, immediate, timeout): """Normal (non-hard) power-off retry flow for IBMi partitions. ================== opts.is_immediate? <--START ================== | | NO YES V V ========= ============ ========== ============ OS normal -FAIL*-> OS immediate -FAIL*-> VSP normal -FAIL*-> return False ========= ============ ========== ============ |_________________ | ___________________| ||| SUCCESS V *VMPowerOffTimeout OR =========== VMPowerOffFailure return True =========== :param part restart timeout: See power_off. :param immediate: Boolean. Indicates whether to try os-normal first (False, the default) before progressing to os-immediate. If True, skip trying os-normal shutdown. :return: True if the power-off succeeded; False otherwise. :raise VMPowerOffTimeout: If the last power-off attempt timed out. :raise VMPowerOffFailure: If the last power-off attempt failed. """ opts = popts.PowerOffOpts().restart(value=restart) # If immediate was already specified, skip OS-normal. if not immediate: # ==> OS normal try: PowerOp.stop(part, opts=opts.os_normal(), timeout=timeout) return True except pexc.VMPowerOffFailure: LOG.warning( _("IBMi OS normal shutdown failed. Trying OS " "immediate shutdown. Partition: %s"), part.name) # Fall through to OS immediate, with default timeout timeout = CONF.pypowervm_job_request_timeout # ==> OS immediate try: PowerOp.stop(part, opts=opts.os_immediate(), timeout=timeout) return True except pexc.VMPowerOffFailure: LOG.warning( _("IBMi OS immediate shutdown failed. Trying VSP normal " "shutdown. Partition: %s"), part.name) # Fall through to VSP normal # ==> VSP normal try: PowerOp.stop(part, opts=opts.vsp_normal(), timeout=timeout) return True except pexc.VMPowerOffFailure: LOG.warning("IBMi VSP normal shutdown failed. Partition: %s", part.name) return False