diff --git a/gso/migrations/versions/2023-05-04_21e7bb0e5cad_add_site_create_workflow.py b/gso/migrations/versions/2023-05-04_21e7bb0e5cad_add_site_create_workflow.py new file mode 100644 index 0000000000000000000000000000000000000000..118d7b125f58279504e7ea14ceac9f18cff0f043 --- /dev/null +++ b/gso/migrations/versions/2023-05-04_21e7bb0e5cad_add_site_create_workflow.py @@ -0,0 +1,39 @@ +"""add Site create workflow. + +Revision ID: 21e7bb0e5cad +Revises: 60d340427471 +Create Date: 2023-05-04 09:00:36.433715 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '21e7bb0e5cad' +down_revision = '60d340427471' +branch_labels = None +depends_on = None + + +from orchestrator.migrations.helpers import create_workflow, delete_workflow + +new_workflows = [ + { + "name": "create_site", + "target": "CREATE", + "description": "Create Site", + "product_type": "Site" + } +] + + +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"]) diff --git a/gso/products/product_blocks/site.py b/gso/products/product_blocks/site.py index a5ca30312cd81b90c1f8ff36a44cad0052d9819d..69651a932fecf9faab61d7dbadbe17d849521d6a 100644 --- a/gso/products/product_blocks/site.py +++ b/gso/products/product_blocks/site.py @@ -2,11 +2,11 @@ from typing import Optional from pydantic import Field from orchestrator.domain.base import ProductBlockModel -from orchestrator.types import SubscriptionLifecycle, Enum +from orchestrator.types import SubscriptionLifecycle, strEnum import ipaddress -class SiteTier(Enum): +class SiteTier(strEnum): tier1 = 1 tier2 = 2 tier3 = 3 @@ -16,27 +16,27 @@ class SiteTier(Enum): class SiteBlockInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="SiteBlock"): - site_name: Optional[str] - site_city: Optional[str] - site_country: Optional[str] - site_country_code: Optional[str] - site_latitude: Optional[float] - site_longitude: Optional[float] - site_internal_id: Optional[int] - site_bgp_community_id: Optional[int] - site_tier: Optional[SiteTier] + 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] - site_city: Optional[str] - site_country: Optional[str] - site_country_code: Optional[str] - site_latitude: Optional[float] - site_longitude: Optional[float] - site_internal_id: Optional[int] - site_bgp_community_id: Optional[int] - site_tier: Optional[SiteTier] + 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]): diff --git a/gso/workflows/device/create_device.py b/gso/workflows/device/create_device.py index d3e0bfd0055f56688cfc01a32b0697e852dee0d8..dc7eff8caa15e8d33f85ba62db95a5ead5005bc5 100644 --- a/gso/workflows/device/create_device.py +++ b/gso/workflows/device/create_device.py @@ -12,18 +12,44 @@ 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_types import device +from gso.products.product_blocks import device as device_pb +from orchestrator.db.models import ProductTable, SubscriptionTable +from orchestrator.forms.validators import Choice, choice_list +from gso.products.product_types.site import Site # from gso.services import ipam, provisioning_proxy +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 + + 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: class CreateDeviceForm(FormPage): class Config: title = product_name + device_site: site_selector() fqdn: str ts_address: ipaddress.IPv4Address ts_port: int - device_vendor: device.DeviceVendor + device_vendor: device_pb.DeviceVendor + device_role: device_pb.DeviceRole user_input = yield CreateDeviceForm @@ -46,33 +72,15 @@ def get_info_from_ipam(subscription: device.DeviceInactive) -> State: # subscription.device.lo_ipv4_address = lo.v4 # subscription.device.lo_ipv6_address = lo.v6 # TODO: get info about how these should be generated - subscription.device.lo_ipv4_address = "10.10.10.10" - subscription.device.lo_ipv6_address = "fc00:798:10::10" - subscription.device.lo_iso_address = "49.51e5.0001.0620.4009.6047.00" - subscription.device.si_ipv4_network = "192.168.0.0/31" - subscription.device.ias_lt_ipv4_network = "192.168.1.0/31" - subscription.device.ias_lt_ipv6_network = "fc00:798:1::150/126" + subscription.device.device_lo_ipv4_address = "10.10.10.10" + subscription.device.device_lo_ipv6_address = "fc00:798:10::10" + subscription.device.device_lo_iso_address = "49.51e5.0001.0620.4009.6047.00" + subscription.device.device_si_ipv4_network = "192.168.0.0/31" + subscription.device.device_ias_lt_ipv4_network = "192.168.1.0/31" + subscription.device.device_ias_lt_ipv6_network = "fc00:798:1::150/126" return {"subscription": subscription} -@step("get information about SNMP") -def get_snmp_info(subscription: device.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} - @step("Initialize subscription") def initialize_subscription( @@ -80,12 +88,16 @@ def initialize_subscription( fqdn: str, ts_address: ipaddress.IPv4Address, ts_port: str, - device_vendor: device.DeviceVendor + device_vendor: device_pb.DeviceVendor, + device_site: str, + device_role: device_pb.DeviceRole ) -> State: - subscription.device.fqdn = fqdn - subscription.device.ts_address = str(ts_address) - subscription.device.ts_port = str(ts_port) - subscription.device_vendor = device_vendor + subscription.device.device_fqdn = fqdn + subscription.device.device_ts_address = str(ts_address) + subscription.device.device_ts_port = str(ts_port) + subscription.device.device_vendor = device_vendor + subscription.device.device_site = Site.from_subscription(device_site[0]).site + subscription.device.device_role = device_role subscription.description = f"Device {fqdn} type \ ({subscription.device_type})" subscription = device.DeviceProvisioning.from_other_lifecycle( @@ -191,7 +203,6 @@ def create_device(): >> create_subscription >> store_process_subscription(Target.CREATE) >> get_info_from_ipam - >> get_snmp_info >> initialize_subscription >> provision_device_dry >> confirm_step diff --git a/gso/workflows/site/create_site.py b/gso/workflows/site/create_site.py index ae683d57592919448ca3d458458bc10d306dd8bc..8ae5400bc5ea6476a776b4dfbf6e045211c9ef9d 100644 --- a/gso/workflows/site/create_site.py +++ b/gso/workflows/site/create_site.py @@ -20,6 +20,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: title = product_name site_name: str + site_city: str site_country: str site_country_code: str site_latitude: float @@ -48,6 +49,7 @@ def create_subscription(product: UUIDstr) -> State: def initialize_subscription( subscription: site.SiteInactive, site_name: str, + site_city: str, site_country: str, site_country_code: str, site_latitude: float, @@ -56,16 +58,19 @@ def initialize_subscription( site_internal_id: int, site_tier: site_pb.SiteTier ) -> State: - subscription.site_name: site_name - subscription.site_country: site_country - subscription.site_country_code: site_country_code - subscription.site_latitude: site_longitude - subscription.site_longitude: site_latitude - subscription.site_bgp_community_id: site_bgp_community_id - subscription.site_internal_id: site_internal_id - subscription.site_tier: site_tier - subscription.description = f"Site {site_name} " - subscription = device.DeviceProvisioning.from_other_lifecycle( + 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 ) @@ -73,12 +78,12 @@ def initialize_subscription( @workflow( - "Create Device", + "Create Site", initial_input_form=wrap_create_initial_input_form( initial_input_form_generator), target=Target.CREATE, ) -def create_device(): +def create_site(): return ( init >> create_subscription