def test_lifecycle_state_machine() -> None:  # noqa: D103
    reservation = Reservation()
    lsm = LifecycleStateMachine(reservation, state_field="lifecycle_state")
    #
    # terminate_request -> terminate_confirmed
    #
    assert reservation.lifecycle_state == LifecycleStateMachine.Created.value
    lsm.terminate_request()
    assert reservation.lifecycle_state == LifecycleStateMachine.Terminating.value
    lsm.terminate_confirmed()
    assert reservation.lifecycle_state == LifecycleStateMachine.Terminated.value
    #
    # forced_end_notification -> terminate_request -> terminate_confirmed
    #
    reservation = Reservation()
    lsm = LifecycleStateMachine(reservation, state_field="lifecycle_state")
    assert reservation.lifecycle_state == LifecycleStateMachine.Created.value
    lsm.forced_end_notification()
    assert reservation.lifecycle_state == LifecycleStateMachine.Failed.value
    lsm.terminate_request()
    assert reservation.lifecycle_state == LifecycleStateMachine.Terminating.value
    lsm.terminate_confirmed()
    assert reservation.lifecycle_state == LifecycleStateMachine.Terminated.value
    #
    # endtime_event -> terminate_request -> terminate_confirmed
    #
    reservation = Reservation()
    lsm = LifecycleStateMachine(reservation, state_field="lifecycle_state")
    assert reservation.lifecycle_state == LifecycleStateMachine.Created.value
    lsm.endtime_event()
    assert reservation.lifecycle_state == LifecycleStateMachine.PassedEndTime.value
    lsm.terminate_request()
    assert reservation.lifecycle_state == LifecycleStateMachine.Terminating.value
    lsm.terminate_confirmed()
    assert reservation.lifecycle_state == LifecycleStateMachine.Terminated.value
def connection_id() -> Column:
    """Create new reservation in db and return connection ID."""
    with db_session() as session:
        reservation = Reservation(
            correlation_id=uuid4(),
            protocol_version="application/vnd.ogf.nsi.cs.v2.provider+soap",
            requester_nsa="urn:ogf:network:example.domain:2021:requester",
            provider_nsa="urn:ogf:network:example.domain:2021:provider",
            reply_to=None,
            session_security_attributes=None,
            global_reservation_id="global reservation id",
            description="reservation 1",
            version=0,
            start_time=datetime.now(timezone.utc) + timedelta(minutes=10),
            end_time=datetime.now(timezone.utc) + timedelta(minutes=20),
            bandwidth=10,
            symmetric=True,
            src_domain="test.domain:2001",
            src_network_type="topology",
            src_port="port1",
            src_vlans=1783,
            dst_domain="test.domain:2001",
            dst_network_type="topology",
            dst_port="port2",
            dst_vlans=1783,
            lifecycle_state="CREATED",
        )
        session.add(reservation)
        session.flush()  # let db generate connection_id

        yield reservation.connection_id

        session.delete(reservation)
def to_p2p_service(reservation: model.Reservation) -> PointToPointService:
    """Create Protobuf ``PointToPointService`` out of DB stored reservation data.

    See Also: warning in :func:`to_header`

    Args:
        reservation: DB Model

    Returns:
        A ``PointToPointService`` object.
    """
    pb_ptps = PointToPointService()
    pb_ptps.capacity = reservation.bandwidth
    pb_ptps.symmetric_path = reservation.symmetric
    pb_ptps.source_stp = str(reservation.src_stp(selected=True))
    pb_ptps.dest_stp = str(reservation.dst_stp(selected=True))
    # The initial version didn't have to support Explicit Routing Objects.
    for param in reservation.parameters:
        pb_ptps.parameters[param.key] = param.value
    return pb_ptps
def test_provision_state_machine() -> None:  # noqa: D103
    reservation = Reservation()
    psm = ProvisionStateMachine(reservation, state_field="provision_state")
    #
    # provision_request -> provision_confirmed -> release_request -> release_confirmed
    #
    assert reservation.provision_state == ProvisionStateMachine.Released.value
    psm.provision_request()
    assert reservation.provision_state == ProvisionStateMachine.Provisioning.value
    psm.provision_confirmed()
    assert reservation.provision_state == ProvisionStateMachine.Provisioned.value
    psm.release_request()
    assert reservation.provision_state == ProvisionStateMachine.Releasing.value
    psm.release_confirmed()
    assert reservation.provision_state == ProvisionStateMachine.Released.value
def to_header(reservation: model.Reservation, *, add_path_segment: bool = False) -> Header:
    """Create Protobuf ``Header`` out of DB stored reservation data.

    .. warning::

        Using a DB model can be tricky.
        This function should either be called within a active SQLAlchemy session.
        Or it should be called with a :class:`~supa.db.model.Reservation` model
        that has been detached for a session.
        In case of the latter
        one should make sure that all relations have been eagerly loaded,
        as a detached model has no ability to load unload attributes.

        See also: https://docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-object-states

    Args:
        reservation: DB model
        add_path_segment: Should we add our own Segment to the PathTrace?

    Returns:
        A Protobuf ``Header`` object.
    """
    pb_header = Header()
    pb_header.protocol_version = reservation.protocol_version
    pb_header.correlation_id = reservation.correlation_id.urn
    pb_header.requester_nsa = reservation.requester_nsa
    pb_header.provider_nsa = reservation.provider_nsa
    if reservation.reply_to is not None:
        pb_header.reply_to = reservation.reply_to
    if reservation.session_security_attributes:
        pb_header.session_security_attributes = reservation.session_security_attributes
    if reservation.path_trace:
        pb_header.path_trace.id = reservation.path_trace.path_trace_id
        pb_header.path_trace.connection_id = reservation.path_trace.ag_connection_id
        path: model.Path

        # A PathTrace can have multiple Paths according to its (WSDL) schema definition.
        # This has been has been carried over to the Protobuf definition.
        # However all examples in the specification always have only one Path!
        # Nor does the specification explain what multiple Paths even mean
        # or how we should deal with them.
        # With the specification inconclusive we have to make decision.
        # In case of multiple Path we will add our Segment to the last one!
        num_paths = len(reservation.path_trace.paths)
        for cur_path_num, path in enumerate(reservation.path_trace.paths, start=1):
            pb_path = pb_header.path_trace.paths.add()
            segment: model.Segment
            for segment in path.segments:
                pb_segment = pb_path.segments.add()
                pb_segment.id = segment.segment.id
                pb_segment.connection_id = segment.upa_connection_id
                stp: model.Stp
                for stp in segment.stps:
                    pb_segment.stps.append(stp.stp_id)
            if add_path_segment and cur_path_num == num_paths:
                pb_segment = Segment()
                pb_segment.id = settings.nsa_id
                pb_segment.connection_id = str(reservation.connection_id)
                pb_segment.stps.extend([reservation.src_stp(selected=True), reservation.dst_stp(selected=True)])
                pb_path.append(pb_segment)
    return pb_header
def test_reservation_state_machine() -> None:  # noqa: D103
    reservation = Reservation()
    rsm = ReservationStateMachine(reservation, state_field="reservation_state")
    #
    # reserve_request -> reserve_failed -> reserve_abort_request -> reserve_abort_confirmed
    #
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    rsm.reserve_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveChecking.value
    rsm.reserve_failed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveFailed.value
    rsm.reserve_abort_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveAborting.value
    rsm.reserve_abort_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    #
    # reserve_request -> reserve_confirmed -> reserve_abort_request -> reserve_abort_confirmed
    #
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    rsm.reserve_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveChecking.value
    rsm.reserve_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveHeld.value
    rsm.reserve_abort_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveAborting.value
    rsm.reserve_abort_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    #
    # reserve_request -> reserve_confirmed -> reserve_commit_request -> reserve_commit_confirmed
    #
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    rsm.reserve_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveChecking.value
    rsm.reserve_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveHeld.value
    rsm.reserve_commit_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveCommitting.value
    rsm.reserve_commit_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    #
    # reserve_request -> reserve_confirmed -> reserve_commit_request -> reserve_commit_failed
    #
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    rsm.reserve_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveChecking.value
    rsm.reserve_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveHeld.value
    rsm.reserve_commit_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveCommitting.value
    rsm.reserve_commit_failed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    #
    # reserve_request -> reserve_confirmed -> reserve_timeout_notification -> reserve_commit_request ->
    #   reserve_commit_confirmed
    #
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    rsm.reserve_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveChecking.value
    rsm.reserve_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveHeld.value
    rsm.reserve_timeout_notification()
    assert reservation.reservation_state == ReservationStateMachine.ReserveTimeout.value
    rsm.reserve_commit_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveCommitting.value
    rsm.reserve_commit_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    #
    # reserve_request -> reserve_confirmed -> reserve_timeout_notification -> reserve_abort_request ->
    #   reserve_abort_confirmed
    #
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value
    rsm.reserve_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveChecking.value
    rsm.reserve_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveHeld.value
    rsm.reserve_timeout_notification()
    assert reservation.reservation_state == ReservationStateMachine.ReserveTimeout.value
    rsm.reserve_abort_request()
    assert reservation.reservation_state == ReservationStateMachine.ReserveAborting.value
    rsm.reserve_abort_confirmed()
    assert reservation.reservation_state == ReservationStateMachine.ReserveStart.value