Skip to content
Snippets Groups Projects
Commit 8919a639 authored by JORGE SASIAIN's avatar JORGE SASIAIN
Browse files

Merge branch 'develop' into NAT-185

parents c6b5ba88 43f4ad56
No related branches found
No related tags found
2 merge requests!27Merge develop into NAT-185,!15Nat 185
Showing
with 1006 additions and 423 deletions
...@@ -10,7 +10,7 @@ from alembic import op ...@@ -10,7 +10,7 @@ from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '857225661207' revision = '857225661207'
down_revision = 'f4959f32c866' down_revision = 'd52256e7d715'
branch_labels = None branch_labels = None
depends_on = None depends_on = None
......
"""add IPtrunk create workflows. """add Site create workflow.
Revision ID: 95cd21cb2b05 Revision ID: 21e7bb0e5cad
Revises: 6b8483b46d06 Revises: 60d340427471
Create Date: 2023-04-28 12:37:36.801782 Create Date: 2023-05-04 09:00:36.433715
""" """
import sqlalchemy as sa import sqlalchemy as sa
from alembic import op from alembic import op
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = '95cd21cb2b05' revision = '21e7bb0e5cad'
down_revision = '6b8483b46d06' down_revision = '60d340427471'
branch_labels = None branch_labels = None
depends_on = None depends_on = None
...@@ -19,10 +19,10 @@ from orchestrator.migrations.helpers import create_workflow, delete_workflow ...@@ -19,10 +19,10 @@ from orchestrator.migrations.helpers import create_workflow, delete_workflow
new_workflows = [ new_workflows = [
{ {
"name": "create_iptrunk", "name": "create_site",
"target": "CREATE", "target": "CREATE",
"description": "Create Iptrunk", "description": "Create Site",
"product_type": "Iptrunk" "product_type": "Site"
} }
] ]
......
"""add Terminate device workflow.
Revision ID: 61f8e90581c5
Revises: 21e7bb0e5cad
Create Date: 2023-05-08 10:48:21.655880
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '61f8e90581c5'
down_revision = '21e7bb0e5cad'
branch_labels = None
depends_on = None
from orchestrator.migrations.helpers import create_workflow, delete_workflow
new_workflows = [
{
"name": "terminate_device",
"target": "TERMINATE",
"description": "Terminate device",
"product_type": "Device"
}
]
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 Terminate Iptrunk workflow.
Revision ID: 647e066bc99e
Revises: 61f8e90581c5
Create Date: 2023-05-08 18:59:01.309425
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '647e066bc99e'
down_revision = '61f8e90581c5'
branch_labels = None
depends_on = None
from orchestrator.migrations.helpers import create_workflow, delete_workflow
new_workflows = [
{
"name": "terminate_iptrunk",
"target": "TERMINATE",
"description": "Terminate IPtrunk",
"product_type": "Iptrunk"
}
]
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"])
...@@ -2,9 +2,11 @@ from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY ...@@ -2,9 +2,11 @@ from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY
from gso.products.product_types.device import Device from gso.products.product_types.device import Device
from gso.products.product_types.iptrunk import Iptrunk from gso.products.product_types.iptrunk import Iptrunk
from gso.products.product_types.site import Site
SUBSCRIPTION_MODEL_REGISTRY.update( SUBSCRIPTION_MODEL_REGISTRY.update(
{ {
"Site": Site,
"Router": Device, "Router": Device,
"Switch": Device, "Switch": Device,
"Iptrunk": Iptrunk, "Iptrunk": Iptrunk,
......
from typing import Optional from typing import Optional
from orchestrator.domain.base import ProductBlockModel from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle from orchestrator.types import SubscriptionLifecycle, strEnum
from gso.products.product_blocks.site \
import SiteBlock, SiteBlockInactive, SiteBlockProvisioning
import ipaddress import ipaddress
class DeviceVendor(strEnum):
juniper = "juniper"
nokia = "nokia"
class DeviceRole(strEnum):
p = "p"
pe = "pe"
amt = "amt"
class DeviceBlockInactive(ProductBlockModel, class DeviceBlockInactive(ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL], lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="DeviceBlock"): product_block_name="DeviceBlock"):
fqdn: Optional[str] = None device_fqdn: Optional[str] = None
ts_address: Optional[str] = None device_ts_address: Optional[str] = None
ts_port: Optional[int] = None device_ts_port: Optional[int] = None
lo_ipv4_address: Optional[ipaddress.IPv4Address] = None device_lo_ipv4_address: Optional[ipaddress.IPv4Address] = None
lo_ipv6_address: Optional[ipaddress.IPv6Address] = None device_lo_ipv6_address: Optional[ipaddress.IPv6Address] = None
lo_iso_address: Optional[str] = None device_lo_iso_address: Optional[str] = None
si_ipv4_network: Optional[ipaddress.IPv4Network] = None device_si_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv4_network: Optional[ipaddress.IPv4Network] = None device_ias_lt_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv6_network: Optional[ipaddress.IPv6Network] = None device_ias_lt_ipv6_network: Optional[ipaddress.IPv6Network] = None
site_city: Optional[str] = None device_vendor: Optional[DeviceVendor] = None
site_country: Optional[str] = None device_role: Optional[DeviceRole] = None
site_country_code: Optional[str] = None device_site: Optional[SiteBlockInactive]
site_latitude: Optional[str] = None
site_longitude: Optional[str] = None
snmp_location: Optional[str] = None
class DeviceBlockProvisioning(DeviceBlockInactive, class DeviceBlockProvisioning(DeviceBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]): lifecycle=[SubscriptionLifecycle.PROVISIONING]):
fqdn: str device_fqdn: str
ts_address: str device_ts_address: str
ts_port: str device_ts_port: int
lo_ipv4_address: Optional[ipaddress.IPv4Address] = None device_lo_ipv4_address: Optional[ipaddress.IPv4Address] = None
lo_ipv6_address: Optional[ipaddress.IPv6Address] = None device_lo_ipv6_address: Optional[ipaddress.IPv6Address] = None
lo_iso_address: Optional[str] = None device_lo_iso_address: Optional[str] = None
si_ipv4_network: Optional[ipaddress.IPv4Network] = None device_si_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv4_network: Optional[ipaddress.IPv4Network] = None device_ias_lt_ipv4_network: Optional[ipaddress.IPv4Network] = None
ias_lt_ipv6_network: Optional[ipaddress.IPv6Network] = None device_ias_lt_ipv6_network: Optional[ipaddress.IPv6Network] = None
site_city: Optional[str] = None device_vendor: Optional[DeviceVendor] = None
site_country: Optional[str] = None device_role: Optional[DeviceRole] = None
site_country_code: Optional[str] = None device_site: Optional[SiteBlockProvisioning]
site_latitude: Optional[str] = None
site_longitude: Optional[str] = None
snmp_location: Optional[str] = None
class DeviceBlock(DeviceBlockProvisioning, class DeviceBlock(DeviceBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]): lifecycle=[SubscriptionLifecycle.ACTIVE]):
fqdn: str device_fqdn: str
ts_address: str device_ts_address: str
ts_port: str device_ts_port: int
lo_ipv4_address: ipaddress.IPv4Address device_lo_ipv4_address: ipaddress.IPv4Address
lo_ipv6_address: ipaddress.IPv6Address device_lo_ipv6_address: ipaddress.IPv6Address
lo_iso_address: str device_lo_iso_address: str
si_ipv4_network: ipaddress.IPv4Network device_si_ipv4_network: ipaddress.IPv4Network
ias_lt_ipv4_network: ipaddress.IPv4Network device_ias_lt_ipv4_network: ipaddress.IPv4Network
ias_lt_ipv6_network: ipaddress.IPv6Network device_ias_lt_ipv6_network: ipaddress.IPv6Network
site_city: str device_vendor: DeviceVendor
site_country: str device_role: DeviceRole
site_country_code: str device_site: SiteBlock
site_latitude: str
site_longitude: str
snmp_location: str
...@@ -27,14 +27,16 @@ class IptrunkBlockInactive(ProductBlockModel, ...@@ -27,14 +27,16 @@ class IptrunkBlockInactive(ProductBlockModel,
iptrunk_sideA_node: DeviceBlockInactive iptrunk_sideA_node: DeviceBlockInactive
iptrunk_sideA_ae_iface: Optional[str] = None iptrunk_sideA_ae_iface: Optional[str] = None
iptrunk_sideA_ae_geant_a_sid: Optional[str] = None iptrunk_sideA_ae_geant_a_sid: Optional[str] = None
iptrunk_sideA_ae_members: Optional[list] = None iptrunk_sideA_ae_members: list[str] = Field(default_factory=list)
iptrunk_sideA_ae_members_description: Optional[list] = None iptrunk_sideA_ae_members_description: list[str] \
= Field(default_factory=list)
# #
iptrunk_sideB_node: DeviceBlockInactive iptrunk_sideB_node: DeviceBlockInactive
iptrunk_sideB_ae_iface: Optional[str] = None iptrunk_sideB_ae_iface: Optional[str] = None
iptrunk_sideB_ae_geant_a_sid: Optional[str] = None iptrunk_sideB_ae_geant_a_sid: Optional[str] = None
iptrunk_sideB_ae_members: Optional[list] = None iptrunk_sideB_ae_members: list[str] = Field(default_factory=list)
iptrunk_sideB_ae_members_description: Optional[list] = None iptrunk_sideB_ae_members_description: list[str] \
= Field(default_factory=list)
class IptrunkBlockProvisioning(IptrunkBlockInactive, class IptrunkBlockProvisioning(IptrunkBlockInactive,
......
from typing import Optional
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle, strEnum
class SiteTier(strEnum):
tier1 = 1
tier2 = 2
tier3 = 3
tier4 = 4
class SiteBlockInactive(ProductBlockModel,
lifecycle=[SubscriptionLifecycle.INITIAL],
product_block_name="SiteBlock"):
site_name: Optional[str] = None
site_city: Optional[str] = None
site_country: Optional[str] = None
site_country_code: Optional[str] = None
site_latitude: Optional[float] = None
site_longitude: Optional[float] = None
site_internal_id: Optional[int] = None
site_bgp_community_id: Optional[int] = None
site_tier: Optional[SiteTier] = None
class SiteBlockProvisioning(SiteBlockInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
site_name: Optional[str] = None
site_city: Optional[str] = None
site_country: Optional[str] = None
site_country_code: Optional[str] = None
site_latitude: Optional[float] = None
site_longitude: Optional[float] = None
site_internal_id: Optional[int] = None
site_bgp_community_id: Optional[int] = None
site_tier: Optional[SiteTier] = None
class SiteBlock(SiteBlockProvisioning,
lifecycle=[SubscriptionLifecycle.ACTIVE]):
site_name: str
site_city: str
site_country: str
site_country_code: str
site_latitude: float
site_longitude: float
site_internal_id: int
site_bgp_community_id: int
site_tier: SiteTier
...@@ -10,25 +10,17 @@ class DeviceType(strEnum): ...@@ -10,25 +10,17 @@ class DeviceType(strEnum):
switch = "switch" switch = "switch"
class DeviceVendor(strEnum):
Juniper = "Juniper"
Newvendor = "Newvendor"
class DeviceInactive(SubscriptionModel, is_base=True): class DeviceInactive(SubscriptionModel, is_base=True):
device_type: DeviceType device_type: DeviceType
device_vendor: DeviceVendor
device: DeviceBlockInactive device: DeviceBlockInactive
class DeviceProvisioning(DeviceInactive, class DeviceProvisioning(DeviceInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]): lifecycle=[SubscriptionLifecycle.PROVISIONING]):
device_type: DeviceType device_type: DeviceType
device_vendor: DeviceVendor
device: DeviceBlockProvisioning device: DeviceBlockProvisioning
class Device(DeviceProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): class Device(DeviceProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
device_type: DeviceType device_type: DeviceType
device_vendor: DeviceVendor
device: DeviceBlock device: DeviceBlock
from orchestrator.domain.base import SubscriptionModel
from orchestrator.types import SubscriptionLifecycle
from gso.products.product_blocks.site \
import SiteBlock, SiteBlockInactive, SiteBlockProvisioning
class SiteInactive(SubscriptionModel, is_base=True):
site: SiteBlockInactive
class SiteProvisioning(SiteInactive,
lifecycle=[SubscriptionLifecycle.PROVISIONING]):
site: SiteBlockProvisioning
class Site(SiteProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
site: SiteBlock
import json
import logging import logging
import requests import requests
from orchestrator import inputstep from orchestrator import inputstep
from orchestrator.config.assignee import Assignee from orchestrator.config.assignee import Assignee
from orchestrator.domain import SubscriptionModel
# noinspection PyProtectedMember
from orchestrator.forms import FormPage, ReadOnlyField from orchestrator.forms import FormPage, ReadOnlyField
from orchestrator.forms.validators import Accept, Label, LongText from orchestrator.forms.validators import Accept, Label, LongText
from orchestrator.types import UUIDstr, State from orchestrator.types import UUIDstr, State, strEnum
from orchestrator.utils.json import json_dumps
from gso import settings from gso import settings
from gso.products.product_types.device import DeviceProvisioning from gso.products.product_types.device import DeviceProvisioning
from gso.products.product_types.iptrunk import IptrunkProvisioning, Iptrunk
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def provision_node( class CUDOperation(strEnum):
node_subscription_params: DeviceProvisioning, """
process_id: UUIDstr, Enum for different C(R)UD operations that the provisioning proxy supports.
dry_run: bool = True): Read is not applicable, hence these become CUD and not CRUD operations.
"""
#: Creation is done with a POST request
POST = 'POST'
#: Updating is done with a PUT request
PUT = 'PUT'
#: Removal is done with a DELETE request
DELETE = 'DELETE'
def _send_request(endpoint: str, parameters: dict, process_id: UUIDstr,
operation: CUDOperation):
"""
Internal function for sending a request to LSO. The callback address is
derived using the process ID provided.
:param str endpoint: The LSO-specific endpoint to call, depending on the
type of service object that is acted upon.
:param dict parameters: JSON body for the request, which will almost always
at least consist of a subscription object, and a boolean value to
indicate a dry run.
:param UUIDstr process_id: The process ID that this request is a part of,
used to call back to when the execution of the playbook is completed.
:param :class:`CUDOperation` operation: The specific operation that is
performed with the request.
"""
oss = settings.load_oss_params() oss = settings.load_oss_params()
pp_params = oss.PROVISIONING_PROXY pp_params = oss.PROVISIONING_PROXY
assert pp_params assert pp_params
device_params = node_subscription_params.device
callback_url = f'{settings.load_oss_params().GENERAL.public_hostname}' \ callback_url = f'{settings.load_oss_params().GENERAL.public_hostname}' \
f'/api/processes/{process_id}/resume' f'/api/processes/{process_id}/resume'
logger.debug(f'[disabled] provisioning node {device_params}') logger.debug(f'[provisioning proxy] provisioning for process {process_id}')
parameters.update({'callback': callback_url})
url = f'{pp_params.scheme}://{pp_params.api_base}/api/{endpoint}'
request = None
if operation == CUDOperation.POST:
request = requests.post(url, json=parameters)
elif operation == CUDOperation.PUT:
request = requests.put(url, json=parameters)
elif operation == CUDOperation.DELETE:
request = requests.delete(url, json=parameters)
if request.status_code != 200:
print(request.content)
raise AssertionError(request.content)
def provision_device(
subscription: DeviceProvisioning,
process_id: UUIDstr,
dry_run: bool = True):
"""
Function that provisions a new device using LSO.
:param :class:`DeviceProvisioning` subscription: The subscription object
that is to be provisioned.
:param UUIDstr process_id: The related process ID, used for callback.
:param bool dry_run: A boolean indicating whether this should be a dry run
or not, defaults to ``True``.
"""
parameters = {
'dry_run': dry_run,
'subscription': json.loads(json_dumps(subscription))
}
_send_request('device', parameters, process_id, CUDOperation.POST)
def provision_ip_trunk(subscription: IptrunkProvisioning,
process_id: UUIDstr,
config_object: str,
dry_run: bool = True):
"""
Function that provisions an IP trunk service using LSO.
:param :class:`IptrunkProvisioning` subscription: The subscription object
that is to be provisioned.
:param UUIDstr process_id: The related process ID, used for callback.
:param str config_object: The type of object that is deployed
:param bool dry_run: A boolean indicating whether this should be a dry run
or not, defaults to ``True``.
"""
parameters = {
'subscription': json.loads(json_dumps(subscription)),
'dry_run': dry_run,
'verb': "deploy",
'object': config_object
}
_send_request('ip_trunk', parameters, process_id, CUDOperation.POST)
# def modify_ip_trunk(old_subscription: Iptrunk,
# new_subscription: Iptrunk,
# process_id: UUIDstr,
# dry_run: bool = True):
# """
# Function that modifies an existing IP trunk subscription using LSO.
#
# :param :class:`Iptrunk` old_subscription: The subscription object, before
# its modification.
# :param :class:`Iptrunk` new_subscription: The subscription object, after
# modifications have been made to it.
# :param UUIDstr process_id: The related process ID, used for callback.
# :param bool dry_run: A boolean indicating whether this should be a dry ryn
# or not, defaults to ``True``.
# """
# parameters = {
# 'dry_run': dry_run,
# 'old_subscription': old_subscription,
# 'subscription': new_subscription
# # FIXME missing parameters
# }
#
# _send_request('ip_trunk', parameters, process_id, CUDOperation.PUT)
def deprovision_ip_trunk(subscription: Iptrunk,
process_id: UUIDstr,
dry_run: bool = True):
"""
Function that provisions an IP trunk service using LSO.
:param :class:`IptrunkProvisioning` subscription: The subscription object
that is to be provisioned.
:param UUIDstr process_id: The related process ID, used for callback.
:param bool dry_run: A boolean indicating whether this should be a dry run
or not, defaults to ``True``.
"""
parameters = { parameters = {
'callback': callback_url, 'subscription': json.loads(json_dumps(subscription)),
'dry_run': dry_run, 'dry_run': dry_run,
'device': { 'verb': "remove"
'fqdn': device_params.fqdn,
'lo_address': {
'v4': str(device_params.lo_ipv4_address),
'v6': str(device_params.lo_ipv6_address)
},
'lo_iso_address': device_params.lo_iso_address,
'si_ipv4_network': str(device_params.si_ipv4_network),
'ias_lt_network': {
'v4': str(device_params.ias_lt_ipv4_network),
'v6': str(device_params.ias_lt_ipv6_network)
},
'site_country_code': device_params.site_country_code,
'site_city': device_params.site_city,
'site_latitude': device_params.site_latitude,
'site_longitude': device_params.site_longitude,
'snmp_location': device_params.snmp_location,
'device_type': node_subscription_params.device_type,
'device_vendor': node_subscription_params.device_vendor,
'ts_address': device_params.ts_address,
'ts_port': device_params.ts_port
}
} }
post_request = requests.post( _send_request('ip_trunk', parameters, process_id, CUDOperation.DELETE)
f'{pp_params.scheme}://{pp_params.api_base}'
f'/api/device',
json=parameters)
post_request.raise_for_status()
@inputstep('Await provisioning proxy results', assignee=Assignee('SYSTEM')) @inputstep('Await provisioning proxy results', assignee=Assignee('SYSTEM'))
def await_pp_results() -> State: def await_pp_results(subscription: SubscriptionModel) -> State:
class ProvisioningResultPage(FormPage): class ProvisioningResultPage(FormPage):
class Config: class Config:
title = 'Do NOT click on confirm in this step!' title = f'Deploying {subscription.product.name}...'
warning_label: Label = 'This step relies on an external service to ' \ warning_label: Label = f'{subscription.product.description} is being' \
'send an update to the orchestrator, do not ' \ f' deployed right now. Feel free to refresh ' \
'interfere with this process please.' f'this page every now and again. Just be ' \
pp_run_results: dict = {'state': 'not_ready'} f'sure that you do NOT click submit!'
pp_run_results: dict = None
confirm: Accept = Accept('INCOMPLETE') confirm: Accept = Accept('INCOMPLETE')
result_page = yield ProvisioningResultPage result_page = yield ProvisioningResultPage
...@@ -81,7 +185,9 @@ def await_pp_results() -> State: ...@@ -81,7 +185,9 @@ def await_pp_results() -> State:
def confirm_pp_results(state: State) -> State: def confirm_pp_results(state: State) -> State:
class ConfirmRunPage(FormPage): class ConfirmRunPage(FormPage):
class Config: class Config:
title = 'Execution completed, please confirm the results.' title = f"Execution for " \
f"{state['subscription']['product']['name']} " \
f"completed, please confirm the results below."
run_status: str = ReadOnlyField(state['pp_run_results']['status']) run_status: str = ReadOnlyField(state['pp_run_results']['status'])
run_results: LongText = ReadOnlyField( run_results: LongText = ReadOnlyField(
......
...@@ -5,3 +5,6 @@ LazyWorkflowInstance("gso.workflows.device.terminate_device", ...@@ -5,3 +5,6 @@ LazyWorkflowInstance("gso.workflows.device.terminate_device",
"terminate_device") "terminate_device")
LazyWorkflowInstance("gso.workflows.device.get_facts", "get_facts") LazyWorkflowInstance("gso.workflows.device.get_facts", "get_facts")
LazyWorkflowInstance("gso.workflows.iptrunk.create_iptrunk", "create_iptrunk") LazyWorkflowInstance("gso.workflows.iptrunk.create_iptrunk", "create_iptrunk")
LazyWorkflowInstance("gso.workflows.iptrunk.terminate_iptrunk",
"terminate_iptrunk")
LazyWorkflowInstance("gso.workflows.site.create_site", "create_site")
import ipaddress import ipaddress
import re
from uuid import uuid4 from uuid import uuid4
from orchestrator.db.models import ProductTable, SubscriptionTable
# noinspection PyProtectedMember
from orchestrator.forms import FormPage from orchestrator.forms import FormPage
from orchestrator.forms.validators import Choice, choice_list
from orchestrator.targets import Target from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State from orchestrator.types import FormGenerator, State
from orchestrator.types import SubscriptionLifecycle, UUIDstr from orchestrator.types import SubscriptionLifecycle, UUIDstr
...@@ -10,11 +14,38 @@ from orchestrator.workflows.steps import resync, set_status ...@@ -10,11 +14,38 @@ from orchestrator.workflows.steps import resync, set_status
from orchestrator.workflows.steps import store_process_subscription from orchestrator.workflows.steps import store_process_subscription
from orchestrator.workflows.utils import wrap_create_initial_input_form from orchestrator.workflows.utils import wrap_create_initial_input_form
from gso.products.product_types.device import DeviceVendor, DeviceInactive, \ from gso.products.product_blocks import device as device_pb
from gso.products.product_types import device
from gso.products.product_types.device import DeviceInactive, \
DeviceProvisioning DeviceProvisioning
from gso.products.product_types.site import Site
from gso.services import provisioning_proxy from gso.services import provisioning_proxy
from gso.services.provisioning_proxy import confirm_pp_results, \ from gso.services.provisioning_proxy import await_pp_results, \
await_pp_results confirm_pp_results
def site_selector() -> list:
site_subscriptions = {}
for site_id, site_description in (
SubscriptionTable.query
.join(ProductTable)
.filter(
ProductTable.product_type == 'Site',
SubscriptionTable.status == 'active')
.with_entities(SubscriptionTable.subscription_id,
SubscriptionTable.description)
.all()
):
site_subscriptions[str(site_id)] = site_description
# noinspection PyTypeChecker
return choice_list(
Choice('site_selection',
zip(site_subscriptions.keys(),
site_subscriptions.items())), # type:ignore
min_items=1,
max_items=1,
)
def initial_input_form_generator(product_name: str) -> FormGenerator: def initial_input_form_generator(product_name: str) -> FormGenerator:
...@@ -22,10 +53,12 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ...@@ -22,10 +53,12 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
class Config: class Config:
title = product_name title = product_name
fqdn: str device_site: site_selector()
hostname: str
ts_address: ipaddress.IPv4Address ts_address: ipaddress.IPv4Address
ts_port: int ts_port: int
device_vendor: DeviceVendor device_vendor: device_pb.DeviceVendor
device_role: device_pb.DeviceRole
user_input = yield CreateDeviceForm user_input = yield CreateDeviceForm
...@@ -42,69 +75,66 @@ def create_subscription(product: UUIDstr) -> State: ...@@ -42,69 +75,66 @@ def create_subscription(product: UUIDstr) -> State:
} }
def iso_from_ipv4(ipv4_address):
padded_octets = [f'{x:>03}' for x in ipv4_address.split('.')]
joined_octets = ''.join(padded_octets)
re_split = '.'.join(re.findall('....', joined_octets))
result = '.'.join(['49.51e5.0001', re_split, '00'])
return result
@step('Get information from IPAM') @step('Get information from IPAM')
def get_info_from_ipam(subscription: DeviceInactive) -> State: def get_info_from_ipam(subscription: DeviceInactive) -> State:
# lo = ipam.new_device_lo_address() # lo = ipam.new_device_lo_address()
# subscription.device.lo_ipv4_address = lo.v4 # subscription.device.lo_ipv4_address = lo.v4
# subscription.device.lo_ipv6_address = lo.v6 # subscription.device.lo_ipv6_address = lo.v6
# TODO: get info about how these should be generated # TODO: get info about how these should be generated
subscription.device.lo_ipv4_address = '10.10.10.10' subscription.device.device_lo_ipv4_address = \
subscription.device.lo_ipv6_address = 'fc00:798:10::10' ipaddress.ip_address('10.10.10.20')
subscription.device.lo_iso_address = '49.51e5.0001.0620.4009.6047.00' subscription.device.device_lo_ipv6_address = \
subscription.device.si_ipv4_network = '192.168.0.0/31' ipaddress.ip_address('fc00:798:10::20')
subscription.device.ias_lt_ipv4_network = '192.168.1.0/31' subscription.device.device_lo_iso_address \
subscription.device.ias_lt_ipv6_network = 'fc00:798:1::150/126' = iso_from_ipv4(str(subscription.device.device_lo_ipv4_address))
subscription.device.device_si_ipv4_network = '192.168.0.0/31'
return {'subscription': subscription} subscription.device.device_ias_lt_ipv4_network = '192.168.1.0/31'
subscription.device.device_ias_lt_ipv6_network = 'fc00:798:1::150/126'
@step('Get information about SNMP')
def get_snmp_info(subscription: DeviceInactive) -> State:
country = 'Spain'
city = 'Barcelona'
country_code = 'ES'
latitude = '41.3743'
longitude = '2.1328'
subscription.device.site_country = country
subscription.device.site_city = city
subscription.device.site_country_code = country_code
subscription.device.site_latitude = latitude
subscription.device.site_longitude = longitude
subscription.device.snmp_location = (
f'{city.upper()},{country.upper()}[{latitude},{longitude}]'
)
return {'subscription': subscription} return {'subscription': subscription}
@step('Initialize subscription') @step('Initialize subscription')
def initialize_subscription( def initialize_subscription(
subscription: DeviceInactive, subscription: device.DeviceInactive,
fqdn: str, hostname: str,
ts_address: ipaddress.IPv4Address, ts_address: ipaddress.IPv4Address,
ts_port: str, ts_port: str,
device_vendor: DeviceVendor device_vendor: device_pb.DeviceVendor,
device_site: str,
device_role: device_pb.DeviceRole
) -> State: ) -> State:
subscription.device.fqdn = fqdn subscription.device.device_ts_address = str(ts_address)
subscription.device.ts_address = str(ts_address) subscription.device.device_ts_port = str(ts_port)
subscription.device.ts_port = str(ts_port) subscription.device.device_vendor = device_vendor
subscription.device_vendor = device_vendor subscription.device.device_site \
= Site.from_subscription(device_site[0]).site
fqdn = str(hostname + '.' +
subscription.device.device_site.site_name.lower() + '.' +
subscription.device.device_site.site_country_code.lower() +
'.geant.net')
subscription.device.device_fqdn = fqdn
subscription.device.device_role = device_role
subscription.description = f'Device {fqdn} ' \ subscription.description = f'Device {fqdn} ' \
f'({subscription.device_type})' f'({subscription.device_type})'
subscription = DeviceProvisioning.from_other_lifecycle( subscription = device.DeviceProvisioning.from_other_lifecycle(
subscription, SubscriptionLifecycle.PROVISIONING subscription, SubscriptionLifecycle.PROVISIONING
) )
return {'subscription': subscription} return {'subscription': subscription, 'fqdn': fqdn}
@step('Provision device [DRY RUN]') @step('Provision device [DRY RUN]')
def provision_device_dry(subscription: DeviceProvisioning, def provision_device_dry(subscription: DeviceProvisioning,
process_id: UUIDstr) -> State: process_id: UUIDstr) -> State:
provisioning_proxy.provision_node( provisioning_proxy.provision_device(subscription, process_id)
subscription,
process_id
)
return {'subscription': subscription} return {'subscription': subscription}
...@@ -112,11 +142,7 @@ def provision_device_dry(subscription: DeviceProvisioning, ...@@ -112,11 +142,7 @@ def provision_device_dry(subscription: DeviceProvisioning,
@step('Provision device [FOR REAL]') @step('Provision device [FOR REAL]')
def provision_device_real(subscription: DeviceProvisioning, def provision_device_real(subscription: DeviceProvisioning,
process_id: UUIDstr) -> State: process_id: UUIDstr) -> State:
provisioning_proxy.provision_node( provisioning_proxy.provision_device(subscription, process_id, False)
subscription,
process_id,
False # No dry run this time, run it for real
)
return {'subscription': subscription} return {'subscription': subscription}
...@@ -133,7 +159,6 @@ def create_device(): ...@@ -133,7 +159,6 @@ def create_device():
>> create_subscription >> create_subscription
>> store_process_subscription(Target.CREATE) >> store_process_subscription(Target.CREATE)
>> get_info_from_ipam >> get_info_from_ipam
>> get_snmp_info
>> initialize_subscription >> initialize_subscription
>> provision_device_dry >> provision_device_dry
>> await_pp_results >> await_pp_results
......
...@@ -19,8 +19,8 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> InputForm: ...@@ -19,8 +19,8 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> InputForm:
class TerminateForm(FormPage): class TerminateForm(FormPage):
are_you_sure: Label = ( are_you_sure: Label = (
f"Are you sure you want to remove {subscription.description}?" f'Are you sure you want to remove {subscription.description}?'
) # type:ignore )
return TerminateForm return TerminateForm
...@@ -41,7 +41,7 @@ def deprovision_user(subscription: Device) -> None: ...@@ -41,7 +41,7 @@ def deprovision_user(subscription: Device) -> None:
initial_input_form_generator), initial_input_form_generator),
target=Target.TERMINATE, target=Target.TERMINATE,
) )
def terminate_user(): def terminate_device():
return ( return (
init init
>> store_process_subscription(Target.TERMINATE) >> store_process_subscription(Target.TERMINATE)
......
# import ipaddress import ipaddress
from uuid import uuid4 from uuid import uuid4
from orchestrator.db.models import ProductTable, SubscriptionTable
# noinspection PyProtectedMember
from orchestrator.forms import FormPage from orchestrator.forms import FormPage
from orchestrator.forms.validators import Choice, choice_list
from orchestrator.targets import Target from orchestrator.targets import Target
from orchestrator.workflow import inputstep
from orchestrator.forms.validators import Accept
from orchestrator.types import FormGenerator, State from orchestrator.types import FormGenerator, State
from orchestrator.types import SubscriptionLifecycle, UUIDstr from orchestrator.types import SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import done, init, step, workflow from orchestrator.workflow import done, init, step, workflow
from orchestrator.workflows.steps import resync, set_status from orchestrator.workflows.steps import resync, set_status
from orchestrator.workflows.steps import store_process_subscription from orchestrator.workflows.steps import store_process_subscription
from orchestrator.workflows.utils import wrap_create_initial_input_form from orchestrator.workflows.utils import wrap_create_initial_input_form
from gso.products.product_types import iptrunk
from gso.products.product_blocks import iptrunk as iptrunk_pb
from gso.products.product_types.device import Device
# from gso.services import ipam, provisioning_proxy
# from gso.products.product_types import device from gso.products.product_blocks.iptrunk import IptrunkType
from orchestrator.db.models import ProductTable, SubscriptionTable from gso.products.product_types.device import Device
from orchestrator.forms.validators import Choice, choice_list from gso.products.product_types.iptrunk import IptrunkInactive, \
IptrunkProvisioning
from gso.services import provisioning_proxy
from gso.services.provisioning_proxy import confirm_pp_results, \
await_pp_results
def device_selector(choice_value: str) -> list: def device_selector(choice_value: str) -> list:
device_subscriptions = {} device_subscriptions = {}
for device_id, device_description in ( for device_id, device_description in (
SubscriptionTable.query.join(ProductTable) SubscriptionTable.query.join(ProductTable)
.filter( .filter(
ProductTable.product_type == "Device", ProductTable.product_type == 'Device',
SubscriptionTable.status == "active", SubscriptionTable.status == 'active',
) )
.with_entities(SubscriptionTable.subscription_id, .with_entities(SubscriptionTable.subscription_id,
SubscriptionTable.description) SubscriptionTable.description)
.all() .all()
): ):
device_subscriptions[str(device_id)] = device_description device_subscriptions[str(device_id)] = device_description
# noinspection PyTypeChecker
return choice_list( return choice_list(
Choice(choice_value, zip(device_subscriptions.keys(), Choice(choice_value, zip(device_subscriptions.keys(),
device_subscriptions.items())), # type:ignore device_subscriptions.items())), # type:ignore
...@@ -50,7 +52,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ...@@ -50,7 +52,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
geant_s_sid: str geant_s_sid: str
iptrunk_description: str iptrunk_description: str
iptrunk_type: iptrunk_pb.IptrunkType iptrunk_type: IptrunkType
iptrunk_speed: str # This should be an enum: 1/10/100/400 iptrunk_speed: str # This should be an enum: 1/10/100/400
iptrunk_minimum_links: int iptrunk_minimum_links: int
...@@ -68,52 +70,54 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ...@@ -68,52 +70,54 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
iptrunk_sideB_ae_members: list[str] iptrunk_sideB_ae_members: list[str]
iptrunk_sideB_ae_members_descriptions: list[str] iptrunk_sideB_ae_members_descriptions: list[str]
# TODO: we need additional validation: # TODO: we need additional validation:
# sideA fqdn must be different from sideB fqdn # * sideA fqdn must be different from sideB fqdn
# the lenght of iptrunk_sideA_ae_members should # * the length of iptrunk_sideA_ae_members should
# be the same as iptrunk_sideA_ae_members # be the same as iptrunk_sideB_ae_members
# interface names must be validated # * interface names must be validated
user_input = yield CreateIptrunkForm user_input = yield CreateIptrunkForm
return user_input.dict() return user_input.dict()
@step("Create subscription") @step('Create subscription')
def create_subscription(product: UUIDstr) -> State: def create_subscription(product: UUIDstr) -> State:
subscription = iptrunk.IptrunkInactive.from_product_id(product, uuid4()) subscription = IptrunkInactive.from_product_id(product, uuid4())
return { return {
"subscription": subscription, 'subscription': subscription,
"subscription_id": subscription.subscription_id, 'subscription_id': subscription.subscription_id,
} }
@step("Get information from IPAM ") @step('Get information from IPAM')
def get_info_from_ipam(subscription: iptrunk.IptrunkInactive) -> State: def get_info_from_ipam(subscription: IptrunkInactive) -> State:
# TODO: get info about how these should be generated # TODO: get info about how these should be generated
subscription.iptrunk.iptrunk_ipv4_network = "192.168.1.0/31" subscription.iptrunk.iptrunk_ipv4_network \
subscription.iptrunk.iptrunk_ipv6_network = "fc00:798:1::150/126" = ipaddress.ip_network('192.168.255.0/31')
return {"subscription": subscription} subscription.iptrunk.iptrunk_ipv6_network \
= ipaddress.ip_network('fc00:798:255::150/126')
return {'subscription': subscription}
@step("Initialize subscription") @step('Initialize subscription')
def initialize_subscription( def initialize_subscription(
subscription: iptrunk.IptrunkInactive, subscription: IptrunkInactive,
geant_s_sid: str, geant_s_sid: str,
iptrunk_type: iptrunk_pb.IptrunkType, iptrunk_type: IptrunkType,
iptrunk_description: str, iptrunk_description: str,
iptrunk_speed: str, iptrunk_speed: str,
iptrunk_minimum_links: int, iptrunk_minimum_links: int,
iptrunk_sideA_node_id: str, iptrunk_sideA_node_id: str,
iptrunk_sideA_ae_iface: str, iptrunk_sideA_ae_iface: str,
iptrunk_sideA_ae_geant_a_sid: str, iptrunk_sideA_ae_geant_a_sid: str,
iptrunk_sideA_ae_members: list[str], iptrunk_sideA_ae_members: list[str],
iptrunk_sideA_ae_members_descriptions: list[str], iptrunk_sideA_ae_members_descriptions: list[str],
iptrunk_sideB_node_id: str, iptrunk_sideB_node_id: str,
iptrunk_sideB_ae_iface: str, iptrunk_sideB_ae_iface: str,
iptrunk_sideB_ae_geant_a_sid: str, iptrunk_sideB_ae_geant_a_sid: str,
iptrunk_sideB_ae_members: list[str], iptrunk_sideB_ae_members: list[str],
iptrunk_sideB_ae_members_descriptions: list[str] iptrunk_sideB_ae_members_descriptions: list[str]
) -> State: ) -> State:
subscription.iptrunk.geant_s_sid = geant_s_sid subscription.iptrunk.geant_s_sid = geant_s_sid
subscription.iptrunk.iptrunk_description = iptrunk_description subscription.iptrunk.iptrunk_description = iptrunk_description
...@@ -122,7 +126,7 @@ def initialize_subscription( ...@@ -122,7 +126,7 @@ def initialize_subscription(
subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links subscription.iptrunk.iptrunk_minimum_links = iptrunk_minimum_links
subscription.iptrunk.iptrunk_sideA_node = Device.from_subscription( subscription.iptrunk.iptrunk_sideA_node = Device.from_subscription(
iptrunk_sideA_node_id[0]).device iptrunk_sideA_node_id[0]).device
subscription.iptrunk.iptrunk_sideA_ae_iface = iptrunk_sideA_ae_iface subscription.iptrunk.iptrunk_sideA_ae_iface = iptrunk_sideA_ae_iface
subscription.iptrunk.iptrunk_sideA_ae_geant_a_sid \ subscription.iptrunk.iptrunk_sideA_ae_geant_a_sid \
= iptrunk_sideA_ae_geant_a_sid = iptrunk_sideA_ae_geant_a_sid
...@@ -131,7 +135,7 @@ def initialize_subscription( ...@@ -131,7 +135,7 @@ def initialize_subscription(
= iptrunk_sideA_ae_members_descriptions = iptrunk_sideA_ae_members_descriptions
subscription.iptrunk.iptrunk_sideB_node = Device.from_subscription( subscription.iptrunk.iptrunk_sideB_node = Device.from_subscription(
iptrunk_sideB_node_id[0]).device iptrunk_sideB_node_id[0]).device
subscription.iptrunk.iptrunk_sideB_ae_iface \ subscription.iptrunk.iptrunk_sideB_ae_iface \
= iptrunk_sideB_ae_iface = iptrunk_sideB_ae_iface
subscription.iptrunk.iptrunk_sideB_ae_geant_a_sid \ subscription.iptrunk.iptrunk_sideB_ae_geant_a_sid \
...@@ -140,110 +144,124 @@ def initialize_subscription( ...@@ -140,110 +144,124 @@ def initialize_subscription(
subscription.iptrunk.iptrunk_sideB_ae_members_description \ subscription.iptrunk.iptrunk_sideB_ae_members_description \
= iptrunk_sideB_ae_members_descriptions = iptrunk_sideB_ae_members_descriptions
subscription.description = f"Iptrunk {geant_s_sid}: \ subscription.description = f'IP trunk, geant_s_sid:{geant_s_sid}'
(blablabla)" subscription = IptrunkProvisioning.from_other_lifecycle(
subscription = iptrunk.IptrunkProvisioning.from_other_lifecycle(
subscription, SubscriptionLifecycle.PROVISIONING subscription, SubscriptionLifecycle.PROVISIONING
) )
return {"subscription": subscription} return {'subscription': subscription}
@step("Provision iptrunk [DRY RUN]") @step('Provision IP trunk interface [DRY RUN]')
def provision_iptrunk_dry( def provision_ip_trunk_iface_dry(subscription: IptrunkProvisioning,
subscription: iptrunk.IptrunkProvisioning, process_id: UUIDstr) -> State:
) -> State: provisioning_proxy.provision_ip_trunk(subscription, process_id,
# import ansible_runner 'trunk_interface')
#
# r = ansible_runner.run( return {'subscription': subscription}
# private_data_dir="/opt/geant-gap-ansible",
# playbook="base_config.yaml",
# inventory=subscription.iptrunk.fqdn, @step('Provision IP trunk interface [FOR REAL]')
# extravars={ def provision_ip_trunk_iface_real(subscription: IptrunkProvisioning,
# "lo_ipv4_address": str(subscription.iptrunk.lo_ipv4_address), process_id: UUIDstr) -> State:
# "lo_ipv6_address": str(subscription.iptrunk.lo_ipv6_address), provisioning_proxy.provision_ip_trunk(subscription, process_id,
# "lo_iso_address": subscription.iptrunk.lo_iso_address, 'trunk_interface', False)
# "snmp_location": subscription.iptrunk.snmp_location,
# "si_ipv4_network": str(subscription.iptrunk.si_ipv4_network), return {'subscription': subscription}
# "lt_ipv4_network": str(subscription.iptrunk.ias_lt_ipv4_network),
# "lt_ipv6_network": str(subscription.iptrunk.ias_lt_ipv6_network),
# "site_country_code": subscription.iptrunk.site_country_code, @step('Provision IP trunk ISIS interface [DRY RUN]')
# "verb": "deploy", def provision_ip_trunk_isis_iface_dry(subscription: IptrunkProvisioning,
# }, process_id: UUIDstr) -> State:
# ) provisioning_proxy.provision_ip_trunk(subscription, process_id,
# out = r.stdout.read() 'isis_interface')
# out_splitted = out.splitlines()
# # if r.rc != 0: return {'subscription': subscription}
# # raise ValueError("Ansible has failed")
# return {"dry_run_output": out_splitted, "return_code": r.rc}
# provisioning_proxy.provision_node( @step('Provision IP trunk ISIS interface [FOR REAL]')
# node_subscription_params=subscription, def provision_ip_trunk_isis_iface_real(subscription: IptrunkProvisioning,
# dry_run=True) process_id: UUIDstr) -> State:
# TODO: figure out what to return when we are suspending & waiting provisioning_proxy.provision_ip_trunk(subscription, process_id,
# for the provisioning-proxy to call back 'isis_interface', False)
return {"return_code": 0}
return {'subscription': subscription}
@inputstep("Confirm step", assignee="CHANGES")
def confirm_step() -> FormGenerator: @step('Provision IP trunk LDP interface [DRY RUN]')
class ConfirmForm(FormPage): def provision_ip_trunk_ldp_iface_dry(subscription: IptrunkProvisioning,
confirm: Accept process_id: UUIDstr) -> State:
provisioning_proxy.provision_ip_trunk(subscription, process_id,
user_input = yield ConfirmForm 'ldp_interface')
return {"confirm": user_input.confirm} return {'subscription': subscription}
@step("Provision iptrunk [FOR REAL]") @step('Provision IP trunk LDP interface [FOR REAL]')
def provision_iptrunk_real( def provision_ip_trunk_ldp_iface_real(subscription: IptrunkProvisioning,
subscription: iptrunk.IptrunkProvisioning, process_id: UUIDstr) -> State:
) -> State: provisioning_proxy.provision_ip_trunk(subscription, process_id,
# import ansible_runner 'ldp_interface', False)
#
# r = ansible_runner.run( return {'subscription': subscription}
# private_data_dir="/opt/geant-gap-ansible",
# playbook="base_config.yaml",
# inventory=subscription.iptrunk.fqdn, @step('Provision IP trunk LLDP interface [DRY RUN]')
# extravars={ def provision_ip_trunk_lldp_iface_dry(subscription: IptrunkProvisioning,
# "lo_ipv4_address": str(subscription.iptrunk.lo_ipv4_address), process_id: UUIDstr) -> State:
# "lo_ipv6_address": str(subscription.iptrunk.lo_ipv6_address), provisioning_proxy.provision_ip_trunk(subscription, process_id,
# "lo_iso_address": subscription.iptrunk.lo_iso_address, 'lldp_interface')
# "snmp_location": subscription.iptrunk.snmp_location,
# "si_ipv4_network": str(subscription.iptrunk.si_ipv4_network), return {'subscription': subscription}
# "lt_ipv4_network": str(subscription.iptrunk.ias_lt_ipv4_network),
# "lt_ipv6_network": str(subscription.iptrunk.ias_lt_ipv6_network),
# "site_country_code": subscription.iptrunk.site_country_code, @step('Provision IP trunk LLDP interface [FOR REAL]')
# "verb": "deploy", def provision_ip_trunk_lldp_iface_real(subscription: IptrunkProvisioning,
# }, process_id: UUIDstr) -> State:
# ) provisioning_proxy.provision_ip_trunk(subscription, process_id,
# out = r.stdout.read() 'lldp_interface', False)
# out_splitted = out.splitlines()
# return {'subscription': subscription}
# # return {"real_run_output": out_splitted, "return_code": r.rc}
# provisioning_proxy.provision_node(
# node_subscription_params=subscription)
# TODO: figure out what to return when we are suspending & waiting
# for the provisioning-proxy to call back
return {"return_code": 0}
@workflow( @workflow(
"Create Iptrunk", 'Create IP trunk',
initial_input_form=wrap_create_initial_input_form( initial_input_form=wrap_create_initial_input_form(
initial_input_form_generator), initial_input_form_generator),
target=Target.CREATE, target=Target.CREATE,
) )
def create_iptrunk(): def create_iptrunk():
return ( return (
init init
>> create_subscription >> create_subscription
>> store_process_subscription(Target.CREATE) >> store_process_subscription(Target.CREATE)
>> get_info_from_ipam >> get_info_from_ipam
>> initialize_subscription >> initialize_subscription
>> provision_iptrunk_dry >> provision_ip_trunk_iface_dry
>> confirm_step >> await_pp_results
>> provision_iptrunk_real >> confirm_pp_results
>> set_status(SubscriptionLifecycle.ACTIVE) >> provision_ip_trunk_iface_real
>> resync >> await_pp_results
>> done >> confirm_pp_results
>> provision_ip_trunk_isis_iface_dry
>> await_pp_results
>> confirm_pp_results
>> provision_ip_trunk_isis_iface_real
>> await_pp_results
>> confirm_pp_results
>> provision_ip_trunk_ldp_iface_dry
>> await_pp_results
>> confirm_pp_results
>> provision_ip_trunk_ldp_iface_real
>> await_pp_results
>> confirm_pp_results
>> provision_ip_trunk_lldp_iface_dry
>> await_pp_results
>> confirm_pp_results
>> provision_ip_trunk_lldp_iface_real
>> await_pp_results
>> confirm_pp_results
>> set_status(SubscriptionLifecycle.ACTIVE)
>> resync
>> done
) )
# noinspection PyProtectedMember
from orchestrator.forms import FormPage
from orchestrator.forms.validators import Label
from orchestrator.targets import Target
from orchestrator.types import InputForm, SubscriptionLifecycle, UUIDstr
from orchestrator.types import State
from orchestrator.workflow import done, init, step, workflow
from orchestrator.workflows.steps import (
resync,
set_status,
store_process_subscription,
unsync,
)
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from gso.products.product_types.iptrunk import Iptrunk
from gso.services import provisioning_proxy
from gso.services.provisioning_proxy import confirm_pp_results, \
await_pp_results
def initial_input_form_generator(subscription_id: UUIDstr) -> InputForm:
subscription = Iptrunk.from_subscription(subscription_id)
class TerminateForm(FormPage):
are_you_sure: Label = (
f'Are you sure you want to remove {subscription.description}?'
)
return TerminateForm
@step('Deprovision IP trunk [DRY RUN]')
def deprovision_ip_trunk_dry(subscription: Iptrunk,
process_id: UUIDstr) -> State:
provisioning_proxy.deprovision_ip_trunk(subscription, process_id)
return {'subscription': subscription}
@step('Deprovision IP trunk [FOR REAL]')
def deprovision_ip_trunk_real(subscription: Iptrunk,
process_id: UUIDstr) -> State:
provisioning_proxy.deprovision_ip_trunk(subscription, process_id, False)
return {'subscription': subscription}
@workflow(
'Terminate IPtrunk',
initial_input_form=wrap_modify_initial_input_form(
initial_input_form_generator),
target=Target.TERMINATE,
)
def terminate_iptrunk():
return (
init
>> store_process_subscription(Target.TERMINATE)
>> unsync
>> deprovision_ip_trunk_dry
>> await_pp_results
>> confirm_pp_results
>> deprovision_ip_trunk_real
>> await_pp_results
>> confirm_pp_results
>> set_status(SubscriptionLifecycle.TERMINATED)
>> resync
>> done
)
from uuid import uuid4
from orchestrator.forms import FormPage
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State
from orchestrator.types import SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import done, init, step, workflow
from orchestrator.workflows.steps import resync, set_status
from orchestrator.workflows.steps import store_process_subscription
from orchestrator.workflows.utils import wrap_create_initial_input_form
from gso.products.product_blocks import site as site_pb
from gso.products.product_types import site
def initial_input_form_generator(product_name: str) -> FormGenerator:
class CreateSiteForm(FormPage):
class Config:
title = product_name
site_name: str
site_city: str
site_country: str
site_country_code: str
site_latitude: float
site_longitude: float
site_bgp_community_id: int
site_internal_id: int
site_tier: site_pb.SiteTier
user_input = yield CreateSiteForm
return user_input.dict()
@step("Create subscription")
def create_subscription(product: UUIDstr) -> State:
subscription = site.SiteInactive.from_product_id(product, uuid4())
return {
"subscription": subscription,
"subscription_id": subscription.subscription_id,
}
@step("Initialize subscription")
def initialize_subscription(
subscription: site.SiteInactive,
site_name: str,
site_city: str,
site_country: str,
site_country_code: str,
site_latitude: float,
site_longitude: float,
site_bgp_community_id: int,
site_internal_id: int,
site_tier: site_pb.SiteTier
) -> State:
subscription.site.site_name = site_name
subscription.site.site_city = site_city
subscription.site.site_country = site_country
subscription.site.site_country_code = site_country_code
subscription.site.site_latitude = site_longitude
subscription.site.site_longitude = site_latitude
subscription.site.site_bgp_community_id = site_bgp_community_id
subscription.site.site_internal_id = site_internal_id
subscription.site.site_tier = site_tier
subscription.description = f"Site {site_name}"
subscription = site.SiteProvisioning.from_other_lifecycle(
subscription, SubscriptionLifecycle.PROVISIONING
)
return {"subscription": subscription}
@workflow(
"Create Site",
initial_input_form=wrap_create_initial_input_form(
initial_input_form_generator),
target=Target.CREATE,
)
def create_site():
return (
init
>> create_subscription
>> store_process_subscription(Target.CREATE)
>> initialize_subscription
>> set_status(SubscriptionLifecycle.ACTIVE)
>> resync
>> done
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment