def execute(work_item, session, background=False):
    # type: (WorkItem, Session, bool) -> Dict[str, Any]
    """Submit a work item to Batfish.

    :param work_item: work to submit
    :type work_item: :py:class:`~pybatfish.client.WorkItem`
    :param session: Batfish session to use.
    :type session: :py:class:`~pybatfish.client.session.Session`
    :param background: Whether to background the job. If `True`,
        this function only returns the result of submitting the job.
    :type background: bool

    :return: If `background=True`, a dict containing a single key 'result' with
    a string description of the result. If `background=False`, a dict containing
    a single key 'status' with a string describing work status.
    """
    snapshot = work_item.requestParams.get(BfConsts.ARG_TESTRIG)
    if snapshot is None:
        raise ValueError('Work item {} does not include a snapshot name'.format(
            work_item.to_json()))

    json_data = {
        CoordConsts.SVC_KEY_WORKITEM: work_item.to_json(),
        CoordConsts.SVC_KEY_API_KEY: session.apiKey,
    }

    # Submit the work item
    response = resthelper.get_json_response(
        session, CoordConsts.SVC_RSC_QUEUE_WORK, json_data)

    if background:
        # TODO: this is ugly and messes with return types: design and write async replacement
        # After we drop 2.7 support
        return {"result": str(response["result"])}

    try:
        answer = get_work_status(work_item.id, session)
        status = WorkStatusCode(answer[CoordConsts.SVC_KEY_WORKSTATUS])
        task_details = answer[CoordConsts.SVC_KEY_TASKSTATUS]

        while not WorkStatusCode.is_terminated(status):
            print_work_status(session, status, task_details)
            time.sleep(1)
            answer = get_work_status(work_item.id, session)
            status = WorkStatusCode(answer[CoordConsts.SVC_KEY_WORKSTATUS])
            task_details = answer[CoordConsts.SVC_KEY_TASKSTATUS]

        print_work_status(session, status, task_details)

        if status == WorkStatusCode.ASSIGNMENTERROR:
            raise BatfishException(
                "Work finished with status {}\n{}".format(status,
                                                          work_item.to_json()))
        return {"status": status}

    except KeyboardInterrupt:
        response = kill_work(session, work_item.id)
        raise KeyboardInterrupt(
            "Killed ongoing work: {}. Server response: {}".format(
                work_item.id, json.dumps(response)))
Beispiel #2
0
def execute(work_item, session, background=False, extra_args=None):
    # type: (WorkItem, Session, bool, Optional[Dict[str, Any]]) -> Dict[str, Any]
    """Submit a work item to Batfish.

    :param work_item: work to submit
    :type work_item: :py:class:`~pybatfish.client.WorkItem`
    :param session: Batfish session to use.
    :type session: :py:class:`~pybatfish.client.session.Session`
    :param background: Whether to background the job. If `True`,
        this function only returns the result of submitting the job.
    :type background: bool
    :param extra_args: extra arguments to be passed to Batfish.
    :type extra_args: dict

    :return: If `background=True`, a dict containing a single key 'result' with
    a string description of the result. If `background=False`, a dict containing
    a single key 'status' with a string describing work status.
    """
    if extra_args is not None:
        work_item.requestParams.update(extra_args)

    snapshot = work_item.requestParams.get(BfConsts.ARG_TESTRIG)
    if snapshot is None:
        raise ValueError(
            "Work item {} does not include a snapshot name".format(
                work_item.to_json()))

    json_data = {
        CoordConsts.SVC_KEY_WORKITEM: work_item.to_json(),
        CoordConsts.SVC_KEY_API_KEY: session.api_key,
    }

    # Submit the work item
    response = resthelper.get_json_response(session,
                                            CoordConsts.SVC_RSC_QUEUE_WORK,
                                            json_data)

    if background:
        # TODO: this is ugly and messes with return types: design and write async replacement
        # After we drop 2.7 support
        return {"result": str(response["result"])}

    answer = get_work_status(work_item.id, session)
    status = WorkStatusCode(answer[CoordConsts.SVC_KEY_WORKSTATUS])
    task_details = answer[CoordConsts.SVC_KEY_TASKSTATUS]

    cur_sleep = 0.1  # seconds
    while not WorkStatusCode.is_terminated(status):
        _print_work_status(session, status, task_details)
        time.sleep(cur_sleep)
        cur_sleep = min(1.0, cur_sleep * 1.5)
        answer = get_work_status(work_item.id, session)
        status = WorkStatusCode(answer[CoordConsts.SVC_KEY_WORKSTATUS])
        task_details = answer[CoordConsts.SVC_KEY_TASKSTATUS]

    _print_work_status(session, status, task_details)

    # Handle fail conditions not producing logs
    if status in [
            WorkStatusCode.ASSIGNMENTERROR, WorkStatusCode.REQUEUEFAILURE
    ]:
        raise BatfishException(
            "Work finished with status {}\nwork_item: {}\ntask_details: {}".
            format(status, work_item.to_json(), json.loads(task_details)))

    # Handle fail condition with logs
    if status == WorkStatusCode.TERMINATEDABNORMALLY:
        log = restv2helper.get_work_log(session, snapshot, work_item.id)
        log_file_msg = ""
        if len(log) > MAX_LOG_LENGTH:
            log_file = tempfile.NamedTemporaryFile().name
            with open(log_file, "w") as log_file_handle:
                log_file_handle.write(str(log))
            log_file_msg = "Full log written to {}\n".format(log_file)
        raise BatfishException(
            "Work terminated abnormally\nwork_item: {item}\n\n{msg}log: {prefix}{log}"
            .format(
                item=work_item.to_json(),
                msg=log_file_msg,
                log=log[-MAX_LOG_LENGTH:],
                prefix="..." if log_file_msg else "",
            ))

    return {"status": status}
Beispiel #3
0
def execute(work_item, session, background=False):
    # type: (WorkItem, Session, bool) -> Dict[str, str]
    """Submit a work item to Batfish.

    :param work_item: work to submit
    :type work_item: :py:class:`~pybatfish.client.WorkItem`
    :param session: Batfish session to use.
    :type session: :py:class:`~pybatfish.client.session.Session`
    :param background: Whether to background the job. If `True`,
        this function only returns the result of submitting the job.
    :type background: bool

    :return: If `background=True`, a dict containing a single key 'result' with
    a string description of the result. If `background=False`, a dict containing
    "status" and "answer" keys, both strings.
    """
    json_data = {
        CoordConsts.SVC_KEY_WORKITEM: work_item.to_json(),
        CoordConsts.SVC_KEY_API_KEY: session.apiKey
    }

    # Submit the work item
    response = resthelper.get_json_response(session,
                                            CoordConsts.SVC_RSC_QUEUE_WORK,
                                            json_data)

    if background:
        return {"result": str(response["result"])}

    try:
        answer = get_work_status(work_item.id, session)
        status = WorkStatusCode(answer[CoordConsts.SVC_KEY_WORKSTATUS])
        task_details = answer[CoordConsts.SVC_KEY_TASKSTATUS]

        while not WorkStatusCode.is_terminated(status):
            print_work_status(session, status, task_details)
            time.sleep(1)
            answer = get_work_status(work_item.id, session)
            status = WorkStatusCode(answer[CoordConsts.SVC_KEY_WORKSTATUS])
            task_details = answer[CoordConsts.SVC_KEY_TASKSTATUS]

        print_work_status(session, status, task_details)

        if status == WorkStatusCode.ASSIGNMENTERROR:
            raise BatfishException("Work finished with status {}\n{}".format(
                status, work_item.to_json()))

        # get the answer
        answer_file_name = _compute_batfish_answer_file_name(work_item)
        answer_bytes = resthelper.get_object(session, answer_file_name)

        # In Python 3.x, answer needs to be decoded before it can be used
        # for things like json.loads (<= 3.6).
        if six.PY3:
            answer_string = answer_bytes.decode(encoding="utf-8")
        else:
            answer_string = answer_bytes
        return {"status": status, "answer": answer_string}

    except KeyboardInterrupt:
        response = kill_work(session, work_item.id)
        raise KeyboardInterrupt(
            "Killed ongoing work: {}. Server response: {}".format(
                work_item.id, json.dumps(response)))