Пример #1
0
def get_power_state(system_id, hostname, power_type, context, clock=reactor):
    """Return the power state of the given node.

    :return: The string "on", "off" or "unknown".
    :raises PowerActionFail: When there's a failure querying the node's
        power state.
    """

    def check_power_state(state):
        if state not in ("on", "off", "unknown"):
            # This is considered an error.
            raise PowerActionFail(state)

    # Capture errors as we go along.
    exc_info = None, None, None

    power_driver = PowerDriverRegistry.get_item(power_type)
    if power_driver is None:
        raise PowerActionFail("Unknown power_type '%s'" % power_type)
    missing_packages = power_driver.detect_missing_packages()
    if len(missing_packages):
        raise PowerActionFail(
            "'%s' package(s) are not installed" % ", ".join(missing_packages)
        )
    try:
        power_state = yield perform_power_driver_query(
            system_id, hostname, power_type, context
        )
        check_power_state(power_state)
    except Exception:
        # Hold the error; it will be reported later.
        exc_info = sys.exc_info()
    else:
        returnValue(power_state)

    # Reaching here means that things have gone wrong.
    assert exc_info != (None, None, None)
    exc_type, exc_value, exc_trace = exc_info
    raise exc_type(exc_value).with_traceback(exc_trace)
Пример #2
0
 def check_power_state(state):
     if state not in ("on", "off", "unknown"):
         # This is considered an error.
         raise PowerActionFail(state)
Пример #3
0
def maybe_change_power_state(system_id,
                             hostname,
                             power_type,
                             power_change,
                             context,
                             clock=reactor):
    """Attempt to change the power state of a node.

    If there is no power action already in progress, register this
    action and then pass change_power_state() to the reactor to call
    later and then return.

    This function exists to guarantee that PowerActionAlreadyInProgress
    errors will be raised promptly, before any work is done to power the
    node on.

    :raises: PowerActionAlreadyInProgress if there's already a power
        action in progress for this node.
    """
    assert power_change in ("on", "off",
                            "cycle"), ("Unknown power change: %s" %
                                       power_change)

    power_driver = PowerDriverRegistry.get_item(power_type)
    if power_driver is None:
        raise PowerActionFail("Unknown power_type '%s'" % power_type)
    missing_packages = power_driver.detect_missing_packages()
    if len(missing_packages):
        raise PowerActionFail("'%s' package(s) are not installed" %
                              " ".join(missing_packages))

    # There should be one and only one power change for each system ID.
    if system_id in power_action_registry:
        current_power_change, d = power_action_registry[system_id]
    else:
        current_power_change, d = None, None

    if current_power_change is None:
        # Arrange for the power change to happen later; do not make the caller
        # wait, because it might take a long time. We set a timeout so that if
        # the power action doesn't return in a timely fashion (or fails
        # silently or some such) it doesn't block other actions on the node.
        d = deferLater(
            clock,
            0,
            deferWithTimeout,
            CHANGE_POWER_STATE_TIMEOUT,
            change_power_state,
            system_id,
            hostname,
            power_type,
            power_change,
            context,
            clock,
        )

        power_action_registry[system_id] = power_change, d

        # Whether we succeed or fail, we need to remove the action from the
        # registry of actions, otherwise subsequent actions will fail.
        d.addBoth(callOut, power_action_registry.pop, system_id, None)

        # Log cancellations distinctly from other errors.
        def eb_cancelled(failure):
            failure.trap(CancelledError)
            log.msg("%s: Power could not be set to %s; timed out." %
                    (hostname, power_change))
            return power_change_failure(system_id, hostname, power_change,
                                        "Timed out")

        d.addErrback(eb_cancelled)

        # Catch-all log.
        d.addErrback(log.err,
                     "%s: Power %s failed." % (hostname, power_change))

        # LP: 1761600 - Returning d will cause d to be resolved with the
        # caller. This causes power actions in the UI/API such as deploy,
        # commission, or release to wait for the power action to complete
        # before finishing.

    elif current_power_change == power_change:
        # What we want is already happening; let it continue.
        pass

    else:
        # Right now we reject conflicting power changes. However, we have the
        # Deferred (in `d`) along which the current power change is occurring,
        # so the option to cancel is available if we want it.
        raise PowerActionAlreadyInProgress(
            "Unable to change power state to '%s' for node %s: another "
            "action is already in progress for that node." %
            (power_change, hostname))