Skip to content

Commit 205b9f4

Browse files
authored
Merge pull request #35 from workfloworchestrator/1026-example-orchestrator-use-a-modify-workflow-as-a-reconcile-workflow-in-surf-orchestrator
Example of reconcile workflow for L2VPN
2 parents 9ce6f7b + a538e86 commit 205b9f4

File tree

4 files changed

+107
-6
lines changed

4 files changed

+107
-6
lines changed

README.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ A modify workflow also follows a general pattern, like described below.
10471047
The `@modify_workflow` decorator adds some additional steps to the
10481048
workflow that are always needed.
10491049

1050-
```pyrhon
1050+
```python
10511051
@modify_workflow("Modify node", initial_input_form=initial_input_form_generator)
10521052
def modify_node() -> StepList:
10531053
return (
@@ -1191,6 +1191,55 @@ the orchestrator, a task (a workflow with `Target.SYSTEM`) can be
11911191
written that will retrieve a list of all L2VPNs from IMS and compare it
11921192
against a list of all L2VPN subscription from the orchestrator.
11931193

1194+
### Reconcile workflow
1195+
1196+
A reconcile workflow is similar to a modify workflow but without requiring user input via forms.
1197+
This makes the reconcile workflow useful for synchronization the existing configuration of
1198+
subscriptions with OSS and/or BSS.
1199+
1200+
The `@reconcile_workflow` removes the need for user input forms by basically running a
1201+
`modify_workflow` where the `initial_input_form` is automatically set to `None`.
1202+
1203+
```python
1204+
@reconcile_workflow("Reconcile l2vpn")
1205+
def reconcile_l2vpn() -> StepList:
1206+
return begin >> update_l2vpn_in_external_systems
1207+
```
1208+
1209+
1. Minimal required information of the subscription is collected and consists of the subscriptions
1210+
existing configuration.
1211+
2. Necessary subscription administration (`@reconcile_workflow`):
1212+
1. Register reconcile process for this subscription
1213+
2. Set subscription ‘out of sync’ to prevent the start of other processes
1214+
3. Interact with OSS and/or BSS, in this example
1215+
1. Update subscription in external systems (OSS and/or BSS) (`update_l2vpn_in_external_systems`)
1216+
4. Set subscription ‘in sync’ (`@reconcile_workflow`)
1217+
1218+
Because both a `@modify_workflows` and `@reconcile_workflow` need to have the same update steps for
1219+
a similar product (e.g. `l2vpn`), these update steps can be extracted into a separate variable:
1220+
1221+
```python
1222+
# variable containing update steps which are both used in modify and reconcile workflows
1223+
update_l2vpn_in_external_systems = begin >> update_l2vpn_in_nrm
1224+
1225+
1226+
@modify_workflow("Modify l2vpn", initial_input_form=initial_input_form_generator)
1227+
def modify_l2vpn() -> StepList:
1228+
return (
1229+
begin
1230+
>> set_status(SubscriptionLifecycle.PROVISIONING)
1231+
>> update_subscription
1232+
>> update_subscription_description
1233+
>> set_status(SubscriptionLifecycle.ACTIVE)
1234+
>> update_l2vpn_in_external_systems
1235+
)
1236+
1237+
1238+
@reconcile_workflow("Reconcile l2vpn")
1239+
def reconcile_l2vpn() -> StepList:
1240+
return begin >> update_l2vpn_in_external_systems
1241+
```
1242+
11941243
## Services
11951244

11961245
Services are collections of helper functions that deliver a service to
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""Reconcile workflows L2VPN.
2+
3+
Revision ID: 0e8d17ce0f06
4+
Revises: d946c20663d3
5+
Create Date: 2025-08-28 13:02:03.796540
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "0e8d17ce0f06"
14+
down_revision = "d946c20663d3"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
from orchestrator.migrations.helpers import create_workflow, delete_workflow
20+
21+
new_workflows = [
22+
{
23+
"name": "reconcile_l2vpn",
24+
"target": "RECONCILE",
25+
"description": "Reconcile SN8 L2Vpn",
26+
"product_type": "L2vpn",
27+
}
28+
]
29+
30+
31+
def upgrade() -> None:
32+
conn = op.get_bind()
33+
for workflow in new_workflows:
34+
create_workflow(conn, workflow)
35+
36+
37+
def downgrade() -> None:
38+
conn = op.get_bind()
39+
for workflow in new_workflows:
40+
delete_workflow(conn, workflow["name"])

workflows/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
LazyWorkflowInstance("workflows.l2vpn.modify_l2vpn", "modify_l2vpn")
3838
LazyWorkflowInstance("workflows.l2vpn.terminate_l2vpn", "terminate_l2vpn")
3939
LazyWorkflowInstance("workflows.l2vpn.validate_l2vpn", "validate_l2vpn")
40+
LazyWorkflowInstance("workflows.l2vpn.modify_l2vpn", "reconcile_l2vpn")
41+
4042

4143
LazyWorkflowInstance("workflows.tasks.bootstrap_netbox", "task_bootstrap_netbox")
4244
LazyWorkflowInstance("workflows.tasks.wipe_netbox", "task_wipe_netbox")

workflows/l2vpn/modify_l2vpn.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@
1212
# limitations under the License.
1313

1414

15-
from pydantic_forms.types import UUIDstr
1615
from orchestrator.types import SubscriptionLifecycle
1716
from orchestrator.workflow import StepList, begin, step
1817
from orchestrator.workflows.steps import set_status
19-
from orchestrator.workflows.utils import modify_workflow
18+
from orchestrator.workflows.utils import modify_workflow, reconcile_workflow
2019
from pydantic_forms.core import FormPage
21-
from pydantic_forms.types import FormGenerator, State
20+
from pydantic_forms.types import FormGenerator, State, UUIDstr
2221

2322
from products.product_types.l2vpn import L2vpn, L2vpnProvisioning
2423
from products.services.description import description
@@ -37,7 +36,9 @@ class ModifyL2vpnForm(FormPage):
3736
user_input_dict = user_input.model_dump()
3837

3938
summary_fields = ["speed", "speed_policer"]
40-
yield from modify_summary_form(user_input_dict, subscription.virtual_circuit, summary_fields)
39+
yield from modify_summary_form(
40+
user_input_dict, subscription.virtual_circuit, summary_fields
41+
)
4142

4243
return user_input_dict | {"subscription": subscription}
4344

@@ -66,13 +67,22 @@ def update_l2vpn_in_nrm(subscription: L2vpnProvisioning) -> State:
6667
return {"subscription": subscription}
6768

6869

70+
# variable containing update steps which are both used in modify and reconcile workflows
71+
update_l2vpn_in_external_systems = begin >> update_l2vpn_in_nrm
72+
73+
6974
@modify_workflow("Modify l2vpn", initial_input_form=initial_input_form_generator)
7075
def modify_l2vpn() -> StepList:
7176
return (
7277
begin
7378
>> set_status(SubscriptionLifecycle.PROVISIONING)
7479
>> update_subscription
7580
>> update_subscription_description
76-
>> update_l2vpn_in_nrm
7781
>> set_status(SubscriptionLifecycle.ACTIVE)
82+
>> update_l2vpn_in_external_systems
7883
)
84+
85+
86+
@reconcile_workflow("Reconcile l2vpn")
87+
def reconcile_l2vpn() -> StepList:
88+
return begin >> update_l2vpn_in_external_systems

0 commit comments

Comments
 (0)