def request_cancellation(liveaction, requester):
    """
    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    """
    if liveaction.status == action_constants.LIVEACTION_STATUS_CANCELING:
        return liveaction

    if liveaction.status not in action_constants.LIVEACTION_CANCELABLE_STATES:
        raise Exception(
            'Unable to cancel liveaction "%s" because it is already in a '
            "completed state." % liveaction.id
        )

    result = {"message": "Action canceled by user.", "user": requester}

    # Run cancelation sequence for liveaction that is in running state or
    # if the liveaction is operating under a workflow.
    if (
        "parent" in liveaction.context
        or liveaction.status in action_constants.LIVEACTION_STATUS_RUNNING
    ):
        status = action_constants.LIVEACTION_STATUS_CANCELING
    else:
        status = action_constants.LIVEACTION_STATUS_CANCELED

    liveaction.context["cancelled_by"] = get_requester(requester)
    liveaction = update_status(
        liveaction, status, result=result, context=liveaction.context
    )

    execution = ActionExecution.get(liveaction__id=str(liveaction.id))

    return (liveaction, execution)

    Request cancellation of an action execution.

    :return: (liveaction, execution)
    :rtype: tuple
    
st2common/st2common/services/action.py