Skip to content

Migrate layer 2 circuit

This workflow migrates an L2 Core Service to a new Edge Port.

It can be triggered by an operator or automatically by the system during Edge Port migration which is a separate workflow.

System-triggered migration: When the system migrates an Edge Port, it runs the workflow automatically. The source and destination Edge Ports are set to the same values. Then here migration only applies the configuration to the router and fill the drift between core DB as source of truth and the actual network since the intent of network has changed in the previous workflow even though the L2 Circuit Service is not changed.

Operator-triggered migration: When an operator initiates the workflow, they are required to specify both the source and destination EdgePorts. During the migration process, the system updates the related edge_port reference to replace the source EdgePort with the destination EdgePort and applies the necessary configuration changes to the router.

Info

Since an L2 Circuit Service has two sides, the workflow must be run separately for each side to fully migrate the service.

input_form_generator(subscription_id)

Generate an input form for migrating a Layer 2 Circuit.

Source code in gso/workflows/l2_circuit/migrate_layer_2_circuit.py
def input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
    """Generate an input form for migrating a Layer 2 Circuit."""
    subscription = Layer2Circuit.from_subscription(subscription_id)

    def circuit_side_selector() -> TypeAlias:
        sides_dict = {
            str(side.sbp.edge_port.owner_subscription_id): EdgePort.from_subscription(
                side.sbp.edge_port.owner_subscription_id
            ).description
            for side in subscription.layer_2_circuit.layer_2_circuit_sides
        }
        return cast(
            type[Choice],
            Choice.__call__("Select one side of the circuit", zip(sides_dict.keys(), sides_dict.items(), strict=True)),
        )

    class MigrateL2CircuitForm(FormPage):
        model_config = ConfigDict(title="Migrating Layer 2 Circuit")

        tt_number: TTNumber
        replace_side: circuit_side_selector()  # type: ignore[valid-type]
        divider: Divider = Field(None, exclude=True)

        label_a: Label = Field("Are we migrating to a different site?", exclude=True)
        migrate_to_different_site: bool = False

        label_b: Label = Field("Execute Ansible playbooks on the OLD side of the circuit?", exclude=True)
        run_old_side_ansible: bool = True
        label_c: Label = Field("Execute Ansible playbooks on the NEW side of the circuit?", exclude=True)
        run_new_side_ansible: bool = True

    initial_user_input = yield MigrateL2CircuitForm
    replace_side_partner = get_partner_by_id(EdgePort.from_subscription(initial_user_input.replace_side).customer_id)

    class SelectNewEdgePortForm(SubmitFormPage):
        model_config = ConfigDict(title="Migrating Layer 2 Circuit")

        new_edge_port: active_edge_port_selector(partner_id=replace_side_partner.partner_id)  # type: ignore[valid-type]
        generate_new_vc_id: bool = False

    user_input = yield SelectNewEdgePortForm

    return {
        "tt_number": initial_user_input.tt_number,
        "run_old_side_ansible": initial_user_input.run_old_side_ansible,
        "run_new_side_ansible": initial_user_input.run_new_side_ansible,
        "subscription": subscription,
        "subscription_id": subscription_id,
        "old_edge_port": initial_user_input.replace_side,
        "new_edge_port": user_input.new_edge_port,
        "generate_new_vc_id": user_input.generate_new_vc_id,
    }

update_subscription_model(subscription, old_edge_port, new_edge_port, generate_new_vc_id)

Replace the old Edge Port with the newly selected one in the subscription model.

Source code in gso/workflows/l2_circuit/migrate_layer_2_circuit.py
@step("Update subscription model")
def update_subscription_model(
    subscription: Layer2Circuit,
    old_edge_port: UUIDstr,
    new_edge_port: UUIDstr,
    generate_new_vc_id: bool,  # noqa: FBT001
) -> State:
    """Replace the old Edge Port with the newly selected one in the subscription model."""
    replace_index = (
        0
        if str(subscription.layer_2_circuit.layer_2_circuit_sides[0].sbp.edge_port.owner_subscription_id)
        == old_edge_port
        else 1
    )
    subscription.layer_2_circuit.layer_2_circuit_sides[replace_index].sbp.edge_port = EdgePort.from_subscription(
        new_edge_port
    ).edge_port

    if generate_new_vc_id:
        vc_id = generate_unique_vc_id(l2c_type=subscription.layer_2_circuit.layer_2_circuit_type)
        if not vc_id:
            msg = "Failed to generate unique Virtual Circuit ID."
            raise ProcessFailureError(msg)
        subscription.layer_2_circuit.virtual_circuit_id = vc_id

    return {"subscription": subscription}

migrate_layer_2_circuit()

Migrate a Layer 2 Circuit.

Source code in gso/workflows/l2_circuit/migrate_layer_2_circuit.py
@workflow(
    "Migrate Layer 2 Circuit",
    initial_input_form=wrap_modify_initial_input_form(input_form_generator),
    target=Target.MODIFY,
)
def migrate_layer_2_circuit() -> StepList:
    """Migrate a Layer 2 Circuit."""
    run_old_side_ansible = conditional(lambda state: state["run_old_side_ansible"])
    run_new_side_ansible = conditional(lambda state: state["run_new_side_ansible"])

    return (
        begin
        >> store_process_subscription(Target.MODIFY)
        >> unsync
        >> run_old_side_ansible(generate_fqdn_list)
        >> run_old_side_ansible(extract_partner_name_from_edge_port)
        >> run_old_side_ansible(lso_interaction(terminate_l2circuit_dry))
        >> run_old_side_ansible(lso_interaction(terminate_l2circuit_real))
        >> update_subscription_model
        >> run_new_side_ansible(generate_fqdn_list)
        >> run_new_side_ansible(extract_partner_name_from_edge_port)
        >> run_new_side_ansible(lso_interaction(provision_l2circuit_dry))
        >> run_new_side_ansible(lso_interaction(provision_l2circuit_real))
        >> resync
        >> done
    )