Skip to content

Importing Existing Subscriptions

When adding new products to Workflow Orchestrator, these products can rely on existing estate in the network that first needs to be added to the service database. This page describes a procedure that can be used to have a separate creation and import workflow for one product.

As a requirement, each product type must have exactly one workflow where target=Target.CREATE. It is therefore not possible to have for example two workflows create_node and import_node. It is possible however, to have a separate product type, that relies on the same product block.

Using Separate Product Types

In the code example below, an example is given for a Node product type that needs to be imported into Orchestrator.

class Node(NodeProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
    """A node that is currently active."""
    node: NodeBlock

class ImportedNode(ImportedNodeInactive, lifecycle=[SubscriptionLifecycle.ACTIVE]):
    """An imported node that is currently active."""
    node: NodeBlock

In this example, both types contain the same product block, which is required for this approach to import existing products. The creation workflow for a Node will remain create_node, which is most likely a workflow that takes user input from a form page, and then interacts with external systems to provision a new node.

For the ImportedNode, the creation workflow is create_imported_node. This workflow can be designed to still take user input from a form page, but could also be provided programmatically by either a CLI command or API endpoint that is called. Data sources can include external API resources, CSV- or YAML files, etc. If desired, interaction with all external provisioning systems can be skipped, resulting in a creation workflow that could be as simple as follows.

from orchestrator.workflow import StepList, begin
from orchestrator.workflows.steps import store_process_subscription
from orchestrator.workflows.utils import create_workflow

@create_workflow("Create imported Node")
def create_imported_node() -> StepList:
    """Workflow to import a Node without provisioning it."""
    return (
        >> create_subscription
        >> store_process_subscription(Target.CREATE)
        >> initialize_subscription

Importing Products

With the ImportedNode part of the service database, we need a modification workflow to take the imported product to a Node subscription. This is done using a modification workflow import_node that is added to the ImportedNode product.

This workflow is another place where external provisioning could take place, but it could also be a straightforward workflow alike the example given earlier. For the modification of importing the Node product, the following serves as an example.

from orchestrator.workflow import StepList, begin, step
from orchestrator.workflows.utils import modify_workflow

@step("Create new Node subscription")
def import_node_subscription(subscription_id: UUIDstr) -> State:
    """Take an ImportedNode subscription, and turn it into a Node subscription."""
    imported_node = ImportedNode.from_subscription(subscription_id)
    new_product_id = get_product_id_by_name(ProductName.NODE)
    new_subscription = Node.from_other_product(imported_node, new_product_id)

    return {"subscription": new_subscription}

@modify_workflow("Import Node", target=Target.MODIFY)
def import_node() -> StepList:
    """Modify into a Node subscription to complete the import."""
    return begin >> import_node_subscription

In this workflow, the existing ImportedNode subscription is modified into a Node subscription, and is stored in the service database. Now, the import has been completed, and the imported variety of the product has been replaced with a "regular" Node subscription.

In short, the procedure is visualized in the following flowchart.

graph LR
  A[CSV file with existing nodes] -->|create_imported_node| B[ImportedNode];
  B -->|import_node| C[Node];

With create_imported_node as the creation workflow, and import_node as a modification workflow of the ImportedNode product.