@@ -36,12 +36,12 @@ Make sure you have docker installed and run:
36
36
docker compose up
37
37
```
38
38
39
- This will start the ` orchestrator ` , ` orchestrator-ui ` , ` netbox ` , ` postgres ` and ` redis ` .
39
+ This will start the ` orchestrator ` , ` orchestrator-ui ` , ` netbox ` , ` federation ` , ` postgres ` and ` redis ` .
40
40
41
41
To include LSO, run the following command instead:
42
42
43
43
```
44
- COMPOSE_PROFILES=lso docker compose up
44
+ COMPOSE_PROFILES=lso docker compose up
45
45
```
46
46
47
47
This will build the Docker image for LSO locally, and make the orchestrator use the included Ansible playbooks.
@@ -302,7 +302,7 @@ to define the helper functions as locally as possible.
302
302
The ` main.py ` can be as simple as shown below, and can be deployed by a
303
303
ASGI server like Uvicorn[ ^ 5 ] .
304
304
305
- ``` python
305
+ ``` python
306
306
from orchestrator import OrchestratorCore
307
307
from orchestrator.cli.main import app as core_cli
308
308
from orchestrator.settings import AppSettings
@@ -439,8 +439,8 @@ is not supported.
439
439
440
440
The Orchestrator uses the concept of a Product to describe what can be built to the end user. When
441
441
a user runs a workflow to create a Product, this results in a unique instance of that product called a Subscription.
442
- A Subscription is always tied to a certain lifecycle state (eg. Initial, Provisioning, Active, Terminated, etc) and
443
- is unique per customer. In other words a Subscription contains all the information needed to uniquely identify a certain
442
+ A Subscription is always tied to a certain lifecycle state (eg. Initial, Provisioning, Active, Terminated, etc) and
443
+ is unique per customer. In other words a Subscription contains all the information needed to uniquely identify a certain
444
444
resource owned by a user/customer that conforms to a certain definition, namely a Product.
445
445
446
446
### Product description in Python
@@ -451,12 +451,12 @@ additional functionality to dynamically cast variables from the
451
451
database, where they are stored as a string, to their correct type in
452
452
Python at runtime. Pydantic uses Python type hints to validate that the
453
453
correct type is assigned. The use of typing, when used together with
454
- type checkers, already helps to make the code more robust, furthermore the use of Pydantic makes it possible to check
454
+ type checkers, already helps to make the code more robust, furthermore the use of Pydantic makes it possible to check
455
455
variables at runtime which greatly improves reliability.
456
456
457
457
#### Example of "Runtime typecasting/safety"
458
- In the example below we attempt to access a resource that has been stored in an instance of a product
459
- (subscription instance). It shows how it can be done directly through the ORM and it shows the added value of Domain
458
+ In the example below we attempt to access a resource that has been stored in an instance of a product
459
+ (subscription instance). It shows how it can be done directly through the ORM and it shows the added value of Domain
460
460
Models on top of the ORM.
461
461
462
462
** Serialisation direct from the database**
@@ -502,7 +502,7 @@ lifting, and makes sure the database remains generic and it's schema remains sta
502
502
#### Product Structure
503
503
A Product definition has two parts in its structure. The Higher order product type that contains information describing
504
504
the product in a more general sense, and multiple layers of product blocks that logically describe the set of resources
505
- that make up the product definition. The product type describes the fixed inputs and the top-level product blocks.
505
+ that make up the product definition. The product type describes the fixed inputs and the top-level product blocks.
506
506
The fixed inputs are used to differentiate between variants of the same product, for example the speed of a network
507
507
port. There is always at least one top level product block that contains
508
508
the resource types to administer the customer facing input. Beside
@@ -577,10 +577,10 @@ this product.
577
577
578
578
#### Wiring it up in the Orchestrator
579
579
<details >
580
- <summary >This section contains advanced information about how to configure the Orchestrator. It is also possible to use
580
+ <summary >This section contains advanced information about how to configure the Orchestrator. It is also possible to use
581
581
a more user friendly tool available <a href="https://workfloworchestrator .
582
582
org/orchestrator-core/reference-docs/cli/#generate">here</a >.
583
- This tool uses a configuration file to generate the boilerplate, migrations and configuration necessary to make use of
583
+ This tool uses a configuration file to generate the boilerplate, migrations and configuration necessary to make use of
584
584
the product straight away.
585
585
</summary >
586
586
@@ -644,7 +644,7 @@ Every time a subscription is transitioned from one lifecycle to another,
644
644
an automatic check is performed to ensure that resource types that are
645
645
not optional are in fact present on that instantiation of the product
646
646
block. This safeguards for incomplete administration for that lifecycle
647
- state.
647
+ state.
648
648
649
649
# ### Resource Type lifecycle. When to use `None`
650
650
The resource types on an inactive product block are usually all
@@ -733,11 +733,11 @@ while displaying detailed subscription information.
733
733
734
734
# # Workflows - Basics
735
735
736
- Workflows are used to orchestrate the lifecycle of a Product Subscription and process the user or systems intent and
737
- apply that to the service. As mentioned above a Subscription is created, then modified `N` number of times, after
738
- which it is terminated. During it' s life a Subscription may also be validated on a regular basis to check whether
739
- there is any drift between the state captured in the Orchestrator and actual state on the system. This workflow is
740
- slightly different compared to the workflows that process intent and apply that to a system, as it does not modify
736
+ Workflows are used to orchestrate the lifecycle of a Product Subscription and process the user or systems intent and
737
+ apply that to the service. As mentioned above a Subscription is created, then modified `N` number of times, after
738
+ which it is terminated. During it' s life a Subscription may also be validated on a regular basis to check whether
739
+ there is any drift between the state captured in the Orchestrator and actual state on the system. This workflow is
740
+ slightly different compared to the workflows that process intent and apply that to a system, as it does not modify
741
741
the system.
742
742
743
743
Four types of workflows are defined, three lifecycle related ones to
@@ -764,9 +764,9 @@ types is done automatically. That is why it is important to correctly
764
764
type the step function parameters.
765
765
766
766
# ### Example
767
- Given this function, when a user correctly makes use of the step decorator it is very easy to extract variables and
768
- make a calculation. It creates readable code, that is easy to understand and reason about. Furthermore the variables
769
- become available in the step in their correct type according to the domain model. Logic errors due wrong type
767
+ Given this function, when a user correctly makes use of the step decorator it is very easy to extract variables and
768
+ make a calculation. It creates readable code, that is easy to understand and reason about. Furthermore the variables
769
+ become available in the step in their correct type according to the domain model. Logic errors due wrong type
770
770
interpretation are much less prone to happen.
771
771
772
772
** Bad use of the step decorator**
@@ -776,7 +776,7 @@ def my_ugly_step(state: State) -> State:
776
776
variable_1 = int (state[" variable_1" ])
777
777
variable_2 = str (state[" varialble_2" ])
778
778
subscription = SubscriptionModel.from_subscription_id(state[" subscription_id" ])
779
-
779
+
780
780
if variable_1 > 42 :
781
781
subscription.product_block_model.variable_1 = - 1
782
782
subscription.product_block_model.variable_2 = " Infinity"
@@ -787,7 +787,7 @@ def my_ugly_step(state: State) -> State:
787
787
state[" subscription" ] = subscription
788
788
return state
789
789
```
790
- In the above example you see we do a simple calculation based on `variable_1` . When computing with even more
790
+ In the above example you see we do a simple calculation based on `variable_1` . When computing with even more
791
791
variables, you van imagine how unreadable the function will be. Now consider the next example.
792
792
793
793
** Good use of the step decorator**
@@ -800,18 +800,18 @@ def my_beautiful_step(variable_1: int, variable_2: str, subscription: Subscripti
800
800
else :
801
801
subscription.product_block_model.variable_1 = variable_1
802
802
subscription.product_block_model.variable_2 = variable_2
803
-
803
+
804
804
return state | {" subscriotion" : subscription}
805
805
```
806
806
807
- As you can see the Orchestrator the orchestrator helps you a lot to condense the logic in your function. The `@ step`
807
+ As you can see the Orchestrator the orchestrator helps you a lot to condense the logic in your function. The `@ step`
808
808
decorator does the following:
809
809
810
810
* Loads the previous steps state from the database.
811
811
* Inspects the step functions signature
812
812
* Finds the arguments in the state and injects them as function arguments to the step function
813
813
* It casts them to the correct type by using the type hints of the step function.
814
- * Finally it updates the state of the workflow and persists all model changes to the database upon reaching the
814
+ * Finally it updates the state of the workflow and persists all model changes to the database upon reaching the
815
815
`return ` of the step function.
816
816
817
817
# ## Forms
@@ -852,10 +852,10 @@ subscription with minimal or no impact to the customer.
852
852
< / details>
853
853
854
854
# ### Form _Magic_
855
- As mentioned before, forms are dynamically created from the backend. This means, ** little to no** frontend coding is
856
- needed to make complex wizard like input forms available to the user. When selecting an action in the UI . The first
857
- thing the frontend does is make an api call to load a form from the backend. The resulting `JSONschema` is parsed
858
- and the correct widgets are loaded in the frontend. Upon submit this is posted to the backend that does all
855
+ As mentioned before, forms are dynamically created from the backend. This means, ** little to no** frontend coding is
856
+ needed to make complex wizard like input forms available to the user. When selecting an action in the UI . The first
857
+ thing the frontend does is make an api call to load a form from the backend. The resulting `JSONschema` is parsed
858
+ and the correct widgets are loaded in the frontend. Upon submit this is posted to the backend that does all
859
859
validation and signals to the user if there are any errors. The following forms are supported:
860
860
861
861
* Multiselect
@@ -865,8 +865,8 @@ validation and signals to the user if there are any errors. The following forms
865
865
* Radio
866
866
867
867
# # Workflow examples
868
- What follows are a few examples of how workflows implement the best common practices implemented by SURF . It
869
- explains in detail what a typical workflow could look like for provision in network element. These examples can be
868
+ What follows are a few examples of how workflows implement the best common practices implemented by SURF . It
869
+ explains in detail what a typical workflow could look like for provision in network element. These examples can be
870
870
examined in greater detail by exploring the `.workflows.node` directory.
871
871
872
872
# ## Create workflow
0 commit comments