Skip to content
Snippets Groups Projects
Commit 4dc8655f authored by Erik Reid's avatar Erik Reid
Browse files

Merge branch 'device_product_and_initial_workflow' into 'develop'

Device product and initial workflow

See merge request !4
parents f35fad05 ebc15e5f
No related branches found
No related tags found
1 merge request!4Device product and initial workflow
Showing
with 362 additions and 748 deletions
......@@ -8,7 +8,7 @@ file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s
# the 'revision' command, regardless of autogenerate
# revision_environment = false
script_location = migrations
version_locations = %(here)s/migrations/versions/schema
version_locations = %(here)s/migrations/versions
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
......@@ -42,4 +42,5 @@ formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
\ No newline at end of file
datefmt = %H:%M:%S
from orchestrator import OrchestratorCore
from orchestrator.cli.main import app as core_cli
from orchestrator.settings import AppSettings
# import products
import geant_service_orchestrator.products # noqa: F401
# import workflows
app = OrchestratorCore(base_settings=AppSettings())
if __name__ == "__main__":
......
"""add DELETE workflows and update CREATE workflows.
Revision ID: 8329fa45157b
Revises: aa49bed10737
Create Date: 2023-03-13 12:12:20.553551
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '8329fa45157b'
down_revision = 'aa49bed10737'
branch_labels = None
depends_on = None
from orchestrator.migrations.helpers import create_workflow, delete_workflow
new_workflows = [
{
"name": "create_trunk",
"target": "CREATE",
"description": "Create trunk",
"product_type": "Trunk"
},
{
"name": "terminate_trunk",
"target": "TERMINATE",
"description": "Terminate trunk",
"product_type": "Trunk"
},
{
"name": "create_trunk_config",
"target": "CREATE",
"description": "Create trunk config",
"product_type": "TrunkConfig"
},
{
"name": "terminate_trunk_config",
"target": "TERMINATE",
"description": "Terminate trunk config",
"product_type": "TrunkConfig"
},
{
"name": "create_trunk_config_common",
"target": "CREATE",
"description": "Create trunk config common",
"product_type": "TrunkConfigCommon"
},
{
"name": "terminate_trunk_config_common",
"target": "TERMINATE",
"description": "Terminate trunk config common",
"product_type": "TrunkConfigCommon"
},
{
"name": "create_trunk_config_side",
"target": "CREATE",
"description": "Create trunk config side",
"product_type": "TrunkConfigSide"
},
{
"name": "terminate_trunk_config_side",
"target": "TERMINATE",
"description": "Terminate trunk config side",
"product_type": "TrunkConfigSide"
}
]
def upgrade() -> None:
conn = op.get_bind()
for workflow in new_workflows:
create_workflow(conn, workflow)
def downgrade() -> None:
conn = op.get_bind()
for workflow in new_workflows:
delete_workflow(conn, workflow["name"])
"""add CREATE workflows.
Revision ID: aa49bed10737
Revises: 431ca061b2ad
Create Date: 2023-03-13 11:18:36.787981
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = 'aa49bed10737'
down_revision = '431ca061b2ad'
branch_labels = None
depends_on = None
from orchestrator.migrations.helpers import create_workflow, delete_workflow
new_workflows = [
{
"name": "create_trunk",
"target": "CREATE",
"description": "Create trunk",
"product_type": "Trunk"
},
{
"name": "create_trunk_config",
"target": "CREATE",
"description": "Create trunk config",
"product_type": "TrunkConfig"
},
{
"name": "create_trunk_config_common",
"target": "CREATE",
"description": "Create trunk config common",
"product_type": "TrunkConfigCommon"
},
{
"name": "create_trunk_config_side",
"target": "CREATE",
"description": "Create trunk config side",
"product_type": "TrunkConfigSide"
}
]
def upgrade() -> None:
conn = op.get_bind()
for workflow in new_workflows:
create_workflow(conn, workflow)
def downgrade() -> None:
conn = op.get_bind()
for workflow in new_workflows:
delete_workflow(conn, workflow["name"])
from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY
from geant_service_orchestrator.products.product_types.trunk \
import Trunk
from geant_service_orchestrator.products.product_types.trunk_config \
import TrunkConfig
from geant_service_orchestrator.products.product_types.trunk_config_common \
import TrunkConfigCommon
from geant_service_orchestrator.products.product_types.trunk_config_side \
import TrunkConfigSide
from geant_service_orchestrator.products.product_types.device import Device
SUBSCRIPTION_MODEL_REGISTRY.update(
{
"Trunk": Trunk,
"Trunk Config": TrunkConfig,
"Trunk Config Common": TrunkConfigCommon,
"Trunk Config Side": TrunkConfigSide,
"Router": Device,
"Switch": Device,
}
)
from typing import Optional
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
import ipaddress
class DeviceBlockInactive(ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="DeviceBlock"):
fqdn: Optional[str] = None
ts_address: Optional[str] = None
ts_port: Optional[int] = None
lo_ipv4_address: Optional[ipaddress.IPv4Address] = None
lo_ipv6_address: Optional[ipaddress.IPv6Address] = None
lo_iso_address: Optional[str] = None
si_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv6_network: Optional[ipaddress.IPv6Network] = None
site_city: Optional[str] = None
site_country: Optional[str] = None
site_country_code: Optional[str] = None
site_latitude: Optional[str] = None
site_longitude: Optional[str] = None
snmp_location: Optional[str] = None
class DeviceBlockProvisioning(DeviceBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
fqdn: str
ts_address: str
ts_port: str
lo_ipv4_address: Optional[ipaddress.IPv4Address] = None
lo_ipv6_address: Optional[ipaddress.IPv6Address] = None
lo_iso_address: Optional[str] = None
si_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv6_network: Optional[ipaddress.IPv6Network] = None
site_city: Optional[str] = None
site_country: Optional[str] = None
site_country_code: Optional[str] = None
site_latitude: Optional[str] = None
site_longitude: Optional[str] = None
snmp_location: Optional[str] = None
class DeviceBlock(DeviceBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
fqdn: str
ts_address: str
ts_port: str
lo_ipv4_address: ipaddress.IPv4Address
lo_ipv6_address: ipaddress.IPv6Address
lo_iso_address: str
si_ipv4_network: ipaddress.IPv4Network
ias_lt_ipv4_network: ipaddress.IPv4Network
ias_lt_ipv6_network: ipaddress.IPv6Network
site_city: str
site_country: str
site_country_code: str
site_latitude: str
site_longitude: str
snmp_location: str
from typing import Optional
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
class TrunkBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="TrunkBlock"):
trunk_id: Optional[int] = None
geant_s_sid: Optional[str] = None
trunk_name: Optional[str] = None
class TrunkBlockProvisioning(
TrunkBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk_id: Optional[int] = None
geant_s_sid: str
trunk_name: str
class TrunkBlock(
TrunkBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk_id: int
geant_s_sid: str
trunk_name: str
from typing import Optional
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk \
import TrunkBlock, TrunkBlockInactive, TrunkBlockProvisioning
class TrunkConfigBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="TrunkConfigBlock"):
trunk: TrunkBlockInactive
trunk_config_id: Optional[int] = None
class TrunkConfigBlockProvisioning(
TrunkConfigBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk: TrunkBlockProvisioning
trunk_config_id: Optional[int] = None
class TrunkConfigBlock(
TrunkConfigBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk: TrunkBlock
trunk_config_id: int
from typing import Optional
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk_config \
import TrunkConfigBlock, TrunkConfigBlockInactive, \
TrunkConfigBlockProvisioning
class TrunkConfigCommonBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="TrunkConfigCommonBlock"):
trunk_config: TrunkConfigBlockInactive
trunk_config_common_id: Optional[int] = None
speed: Optional[str] = None
is_leased_line: Optional[bool] = None
isis_metric: Optional[int] = None
minimum_links: Optional[int] = None
class TrunkConfigCommonBlockProvisioning(
TrunkConfigCommonBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk_config: TrunkConfigBlockProvisioning
trunk_config_common_id: Optional[int] = None
speed: str
is_leased_line: bool
isis_metric: int
minimum_links: int
class TrunkConfigCommonBlock(
TrunkConfigCommonBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk_config: TrunkConfigBlock
trunk_config_common_id: int
speed: str
is_leased_line: bool
isis_metric: int
minimum_links: int
from typing import List, Optional
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk_config \
import TrunkConfigBlock, TrunkConfigBlockInactive,\
TrunkConfigBlockProvisioning
class TrunkConfigSideBlockInactive(
ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="TrunkConfigSideBlock"):
trunk_config: TrunkConfigBlockInactive
trunk_config_side_id: Optional[int] = None
fqdn: Optional[str] = None
ae_name: Optional[str] = None
geant_a_sid: Optional[str] = None
ipv4_address: Optional[str] = None
ipv6_address: Optional[str] = None
members: Optional[List[str]] = None
class TrunkConfigSideBlockProvisioning(
TrunkConfigSideBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk_config: TrunkConfigBlockProvisioning
trunk_config_side_id: Optional[int] = None
fqdn: str
ae_name: str
geant_a_sid: str
ipv4_address: Optional[str] = "127.0.0.1"
ipv6_address: Optional[str] = "::1"
members: List[str]
class TrunkConfigSideBlock(
TrunkConfigSideBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk_config: TrunkConfigBlock
trunk_config_side_id: int
fqdn: str
ae_name: str
geant_a_sid: str
ipv4_address: str
ipv6_address: str
members: List[str]
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle, strEnum
from geant_service_orchestrator.products.product_blocks.device \
import DeviceBlock, DeviceBlockInactive, DeviceBlockProvisioning
class DeviceType(strEnum):
router = "router"
switch = "switch"
class DeviceVendor(strEnum):
Juniper = "juniper"
Newvendor = "newvendor"
class DeviceInactive(SubscriptionModel, is_base=True):
device_type: DeviceType
device_vendor: DeviceVendor
device: DeviceBlockInactive
class DeviceProvisioning(DeviceInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
device_type: DeviceType
device_vendor: DeviceVendor
device: DeviceBlockProvisioning
class Device(DeviceProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
device_type: DeviceType
device_vendor: DeviceVendor
device: DeviceBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk \
import TrunkBlock, TrunkBlockInactive, TrunkBlockProvisioning
class TrunkInactive(
SubscriptionModel, is_base=True,
lifecycle=[SubscriptionLifecycle.INITIAL]):
trunk: TrunkBlockInactive
class TrunkProvisioning(
TrunkInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk: TrunkBlockProvisioning
class Trunk(
TrunkProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk: TrunkBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk_config \
import TrunkConfigBlock, TrunkConfigBlockInactive,\
TrunkConfigBlockProvisioning
class TrunkConfigInactive(
SubscriptionModel,
is_base=True, lifecycle=[SubscriptionLifecycle.INITIAL]):
trunk_config: TrunkConfigBlockInactive
class TrunkConfigProvisioning(
TrunkConfigInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk_config: TrunkConfigBlockProvisioning
class TrunkConfig(
TrunkConfigProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk_config: TrunkConfigBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk_config_common \
import TrunkConfigCommonBlock, TrunkConfigCommonBlockInactive, \
TrunkConfigCommonBlockProvisioning
class TrunkConfigCommonInactive(
SubscriptionModel,
is_base=True,
lifecycle=[SubscriptionLifecycle.INITIAL]):
trunk_config_common: TrunkConfigCommonBlockInactive
class TrunkConfigCommonProvisioning(
TrunkConfigCommonInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk_config_common: TrunkConfigCommonBlockProvisioning
class TrunkConfigCommon(
TrunkConfigCommonProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk_config_common: TrunkConfigCommonBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle
from geant_service_orchestrator.products.product_blocks.trunk_config_side \
import TrunkConfigSideBlock, TrunkConfigSideBlockInactive, \
TrunkConfigSideBlockProvisioning
class TrunkConfigSideInactive(
SubscriptionModel,
is_base=True,
lifecycle=[SubscriptionLifecycle.INITIAL]):
trunk_config_side: TrunkConfigSideBlockInactive
class TrunkConfigSideProvisioning(
TrunkConfigSideInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
trunk_config_side: TrunkConfigSideBlockProvisioning
class TrunkConfigSide(
TrunkConfigSideProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
trunk_config_side: TrunkConfigSideBlock
from orchestrator.workflows import LazyWorkflowInstance
LazyWorkflowInstance(
"workflows.trunk.create_trunk",
"create_trunk")
LazyWorkflowInstance(
"workflows.trunk_config.create_trunk_config",
"create_trunk_config")
LazyWorkflowInstance(
"workflows.trunk_config_common.create_trunk_config_common",
"create_trunk_config_common")
LazyWorkflowInstance(
"workflows.trunk_config_side.create_trunk_config_side",
"create_trunk_config_side")
LazyWorkflowInstance(
"workflows.trunk.terminate_trunk",
"terminate_trunk")
LazyWorkflowInstance(
"workflows.trunk_config.terminate_trunk_config",
"terminate_trunk_config")
LazyWorkflowInstance(
"workflows.trunk_config_common.terminate_trunk_config_common",
"terminate_trunk_config_common")
LazyWorkflowInstance(
"workflows.trunk_config_side.terminate_trunk_config_side",
"terminate_trunk_config_side")
from uuid import uuid4
from orchestrator.forms import FormPage
from orchestrator.targets import Target
from orchestrator.types \
import FormGenerator, State, SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import done, init, step, workflow
from orchestrator.workflows.steps \
import resync, set_status, store_process_subscription
from orchestrator.workflows.utils import wrap_create_initial_input_form
from geant_service_orchestrator.products.product_types.trunk \
import TrunkInactive, TrunkProvisioning
def initial_input_form_generator(product_name: str) -> FormGenerator:
class CreateTrunkForm(FormPage):
class Config:
title = product_name
trunk_name: str
geant_s_sid: str
user_input = yield CreateTrunkForm
return user_input.dict()
def _provision_in_gap(trunk_name: str) -> int:
return abs(hash(trunk_name))
@step("Create subscription")
def create_subscription(product: UUIDstr) -> State:
subscription = TrunkInactive.from_product_id(product, uuid4())
return {
"subscription": subscription,
"subscription_id": subscription.subscription_id
}
@step("Initialize subscription")
def initialize_subscription(
subscription: TrunkInactive,
trunk_name: str,
geant_s_sid: str) -> State:
subscription.trunk.trunk_name = trunk_name
subscription.trunk.geant_s_sid = geant_s_sid
subscription.description = f"Trunk {trunk_name}"
subscription = TrunkProvisioning.from_other_lifecycle(
subscription, SubscriptionLifecycle.PROVISIONING)
return {"subscription": subscription}
@step("Provision trunk")
def provision_trunk(
subscription: TrunkProvisioning,
trunk_name: str) -> State:
trunk_id = _provision_in_gap(trunk_name)
subscription.trunk.trunk_id = trunk_id
return {"subscription": subscription}
@workflow(
"Create trunk",
initial_input_form=wrap_create_initial_input_form(initial_input_form_generator), # noqa: E501
target=Target.CREATE,
)
def create_trunk():
return (
init
>> create_subscription
>> store_process_subscription(Target.CREATE)
>> initialize_subscription
>> provision_trunk
>> set_status(SubscriptionLifecycle.ACTIVE)
>> resync
>> done
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment