Skip to content
Snippets Groups Projects
Verified Commit 82268d29 authored by Karel van Klink's avatar Karel van Klink :smiley_cat:
Browse files

Add workflows for Switch and LAN switch interconnect: create, import, terminate, validate, modify

parent aa3c5621
No related branches found
No related tags found
1 merge request!300Feature/lan switch interconnect
Showing
with 619 additions and 80 deletions
......@@ -12,7 +12,7 @@ import typer
import yaml
from orchestrator.db import db
from orchestrator.services.processes import start_process
from orchestrator.types import SubscriptionLifecycle
from orchestrator.types import SubscriptionLifecycle, UUIDstr
from pydantic import BaseModel, ValidationError, field_validator, model_validator
from sqlalchemy.exc import SQLAlchemyError
......@@ -23,6 +23,7 @@ from gso.products.product_blocks.edge_port import EdgePortType, EncapsulationTyp
from gso.products.product_blocks.iptrunk import IptrunkType
from gso.products.product_blocks.router import RouterRole
from gso.products.product_blocks.service_binding_port import VLAN_ID
from gso.products.product_blocks.switch import SwitchModel
from gso.products.product_types.nren_l3_core_service import NRENL3CoreServiceType
from gso.services.partners import (
PartnerEmail,
......@@ -39,7 +40,16 @@ from gso.services.subscriptions import (
from gso.utils.shared_enums import SBPType, Vendor
from gso.utils.types.base_site import BaseSiteValidatorModel
from gso.utils.types.interfaces import LAGMember, LAGMemberList, PhysicalPortCapacity
from gso.utils.types.ip_address import IPAddress, IPv4AddressType, IPV4Netmask, IPv6AddressType, IPV6Netmask, PortNumber
from gso.utils.types.ip_address import (
AddressSpace,
IPAddress,
IPv4AddressType,
IPV4Netmask,
IPv4NetworkType,
IPv6AddressType,
IPV6Netmask,
PortNumber,
)
app: typer.Typer = typer.Typer()
......@@ -69,6 +79,16 @@ class RouterImportModel(BaseModel):
router_lo_iso_address: str
class SwitchImportModel(BaseModel):
"""Required fields for importing an existing :class:`gso.product.product_types.switch`."""
fqdn: str
ts_port: PortNumber
site: UUIDstr
switch_vendor: Vendor
switch_model: SwitchModel
class SuperPopSwitchImportModel(BaseModel):
"""Required fields for importing an existing :class:`gso.product.product_types.super_pop_switch`."""
......@@ -278,16 +298,47 @@ class NRENL3CoreServiceImportModel(BaseModel):
return value
class LanSwitchInterconnectRouterSideImportModel(BaseModel):
"""Import LAN Switch Interconnect Router side model."""
node: UUIDstr
ae_iface: str
ae_members: LAGMemberList[LAGMember]
ipv4_address: IPv4AddressType
class LanSwitchInterconnectSwitchSideImportModel(BaseModel):
"""Import LAN Switch Interconnect Switch side model."""
switch: UUIDstr
ae_iface: str
ae_members: LAGMemberList[LAGMember]
ipv4_address: IPv4AddressType
class LanSwitchInterconnectImportModel(BaseModel):
"""Import LAN Switch Interconnect model."""
lan_switch_interconnect_description: str
lan_switch_interconnect_ip_network: IPv4NetworkType | None
address_space: AddressSpace
minimum_links: int
router_side: LanSwitchInterconnectRouterSideImportModel
switch_side: LanSwitchInterconnectSwitchSideImportModel
T = TypeVar(
"T",
SiteImportModel,
RouterImportModel,
SwitchImportModel,
IptrunkImportModel,
SuperPopSwitchImportModel,
OfficeRouterImportModel,
OpenGearImportModel,
EdgePortImportModel,
NRENL3CoreServiceImportModel,
LanSwitchInterconnectImportModel,
)
common_filepath_option = typer.Option(
......@@ -380,6 +431,12 @@ def import_routers(filepath: str = common_filepath_option) -> None:
_generic_import_product(Path(filepath), ProductType.IMPORTED_ROUTER, "router", "hostname", RouterImportModel)
@app.command()
def import_switches(filepath: str = common_filepath_option) -> None:
"""Import switches into GSO."""
_generic_import_product(Path(filepath), ProductType.IMPORTED_SWITCH, "switch", "fqdn", SwitchImportModel)
@app.command()
def import_super_pop_switches(filepath: str = common_filepath_option) -> None:
"""Import Super PoP Switches into GSO."""
......@@ -581,3 +638,15 @@ def import_nren_l3_core_service(filepath: str = common_filepath_option) -> None:
typer.echo("Successfully created imported NREN L3 Core Services:")
for item in successfully_imported_data:
typer.echo(f"- {item}")
@app.command()
def import_lan_switch_interconnect(filepath: str = common_filepath_option) -> None:
"""Import :term:`LAN` Switch Interconnect services into :term:`GSO`."""
_generic_import_product(
Path(filepath),
ProductType.IMPORTED_LAN_SWITCH_INTERCONNECT,
"lan_switch_interconnect",
"lan_switch_interconnect_description",
LanSwitchInterconnectImportModel,
)
"""Add switch workflows.
Revision ID: 0e7e7d749617
Revises: 7412c5b7ebe4
Revises: 94d9de9246fe
Create Date: 2024-08-29 15:45:57.581710
"""
......@@ -10,7 +10,7 @@ from alembic import op
# revision identifiers, used by Alembic.
revision = '0e7e7d749617'
down_revision = '7412c5b7ebe4'
down_revision = '94d9de9246fe'
branch_labels = None
depends_on = None
......@@ -41,6 +41,54 @@ new_workflows = [
"target": "SYSTEM",
"description": "Validate switch subscription",
"product_type": "Switch"
},
{
"name": "create_imported_switch",
"target": "CREATE",
"description": "Create Imported Switch",
"product_type": "ImportedSwitch"
},
{
"name": "import_switch",
"target": "MODIFY",
"description": "Import Switch",
"product_type": "ImportedSwitch"
},
{
"name": "create_lan_switch_interconnect",
"target": "CREATE",
"description": "Create LAN Switch Interconnect",
"product_type": "LanSwitchInterconnect"
},
{
"name": "terminate_lan_switch_interconnect",
"target": "TERMINATE",
"description": "Terminate LAN Switch Interconnect",
"product_type": "LanSwitchInterconnect"
},
{
"name": "validate_lan_switch_interconnect",
"target": "SYSTEM",
"description": "Validate LAN Switch Interconnect",
"product_type": "LanSwitchInterconnect"
},
{
"name": "validate_lan_switch_interconnect",
"target": "SYSTEM",
"description": "Validate LAN Switch Interconnect",
"product_type": "LanSwitchInterconnect"
},
{
"name": "create_imported_lan_switch_interconnect",
"target": "CREATE",
"description": "Create Imported LAN Switch Interconnect",
"product_type": "ImportedLanSwitchInterconnect"
},
{
"name": "import_lan_switch_interconnect",
"target": "MODIFY",
"description": "Import LAN Switch Interconnect",
"product_type": "ImportedLanSwitchInterconnect"
}
]
......
"""Update Switch, PoP VLAN, and Switch interconnect product models.
Revision ID: 2bbe91a01715
Revises: 0e7e7d749617
Create Date: 2024-10-28 15:18:54.138252
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '2bbe91a01715'
down_revision = '0e7e7d749617'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='ts_port' WHERE resource_types.resource_type = 'switch_ts_port'
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='fqdn' WHERE resource_types.resource_type = 'switch_hostname'
"""))
conn.execute(sa.text("""
INSERT INTO resource_types (resource_type, description) VALUES ('lan_switch_interconnect_ip_network', 'IP resources for a LAN Switch Interconnect') RETURNING resource_types.resource_type_id
"""))
conn.execute(sa.text("""
INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network')))
"""))
def downgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network')
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='switch_ts_port' WHERE resource_types.resource_type = 'ts_port'
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='switch_hostname' WHERE resource_types.resource_type = 'fqdn'
"""))
"""Add Imported Switches, update domain models.
Revision ID: 94d9de9246fe
Revises: 7412c5b7ebe4
Create Date: 2024-10-29 15:45:28.294699
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '94d9de9246fe'
down_revision = '7412c5b7ebe4'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='ts_port' WHERE resource_types.resource_type = 'switch_ts_port'
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='fqdn' WHERE resource_types.resource_type = 'switch_hostname'
"""))
conn.execute(sa.text("""
INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported Switch', 'An existing Switch that is imported into the subscription database', 'ImportedSwitch', 'IMP_SWITCH', 'active') RETURNING products.product_id
"""))
conn.execute(sa.text("""
INSERT INTO resource_types (resource_type, description) VALUES ('lan_switch_interconnect_ip_network', 'IP resources for a LAN Switch interconnect') RETURNING resource_types.resource_type_id
"""))
conn.execute(sa.text("""
INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Imported Switch')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock')))
"""))
conn.execute(sa.text("""
INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network')))
"""))
conn.execute(sa.text("""
INSERT INTO products (name, description, product_type, tag, status) VALUES ('Imported LAN Switch Interconnect', 'An existing LAN Switch Interconnect that is imported into the subscription database', 'ImportedLanSwitchInterconnect', 'IMP_LSI', 'active') RETURNING products.product_id
"""))
conn.execute(sa.text("""
INSERT INTO product_product_blocks (product_id, product_block_id) VALUES ((SELECT products.product_id FROM products WHERE products.name IN ('Imported LAN Switch Interconnect')), (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')))
"""))
conn.execute(sa.text("""
INSERT INTO product_block_resource_types (product_block_id, resource_type_id) VALUES ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address'))), ((SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectRouterSideBlock')), (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address')))
"""))
def downgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectSwitchSideBlock', 'LanSwitchInterconnectRouterSideBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('ipv4_address'))
"""))
conn.execute(sa.text("""
DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported LAN Switch Interconnect')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))
"""))
conn.execute(sa.text("""
DELETE FROM processes WHERE processes.pid IN (SELECT processes_subscriptions.pid FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported LAN Switch Interconnect'))))
"""))
conn.execute(sa.text("""
DELETE FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported LAN Switch Interconnect')))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instances WHERE subscription_instances.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported LAN Switch Interconnect')))
"""))
conn.execute(sa.text("""
DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported LAN Switch Interconnect'))
"""))
conn.execute(sa.text("""
DELETE FROM products WHERE products.name IN ('Imported LAN Switch Interconnect')
"""))
conn.execute(sa.text("""
DELETE FROM product_block_resource_types WHERE product_block_resource_types.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock')) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values USING product_block_resource_types WHERE subscription_instance_values.subscription_instance_id IN (SELECT subscription_instances.subscription_instance_id FROM subscription_instances WHERE subscription_instances.subscription_instance_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('LanSwitchInterconnectBlock'))) AND product_block_resource_types.resource_type_id = (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instance_values WHERE subscription_instance_values.resource_type_id IN (SELECT resource_types.resource_type_id FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network'))
"""))
conn.execute(sa.text("""
DELETE FROM resource_types WHERE resource_types.resource_type IN ('lan_switch_interconnect_ip_network')
"""))
conn.execute(sa.text("""
DELETE FROM product_product_blocks WHERE product_product_blocks.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported Switch')) AND product_product_blocks.product_block_id IN (SELECT product_blocks.product_block_id FROM product_blocks WHERE product_blocks.name IN ('SwitchBlock'))
"""))
conn.execute(sa.text("""
DELETE FROM processes WHERE processes.pid IN (SELECT processes_subscriptions.pid FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported Switch'))))
"""))
conn.execute(sa.text("""
DELETE FROM processes_subscriptions WHERE processes_subscriptions.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported Switch')))
"""))
conn.execute(sa.text("""
DELETE FROM subscription_instances WHERE subscription_instances.subscription_id IN (SELECT subscriptions.subscription_id FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported Switch')))
"""))
conn.execute(sa.text("""
DELETE FROM subscriptions WHERE subscriptions.product_id IN (SELECT products.product_id FROM products WHERE products.name IN ('Imported Switch'))
"""))
conn.execute(sa.text("""
DELETE FROM products WHERE products.name IN ('Imported Switch')
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='switch_ts_port' WHERE resource_types.resource_type = 'ts_port'
"""))
conn.execute(sa.text("""
UPDATE resource_types SET resource_type='switch_hostname' WHERE resource_types.resource_type = 'fqdn'
"""))
......@@ -10,7 +10,7 @@ from pydantic_forms.types import strEnum
from gso.products.product_types.edge_port import EdgePort, ImportedEdgePort
from gso.products.product_types.iptrunk import ImportedIptrunk, Iptrunk
from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
from gso.products.product_types.lan_switch_interconnect import ImportedLanSwitchInterconnect, LanSwitchInterconnect
from gso.products.product_types.nren_l3_core_service import ImportedNRENL3CoreService, NRENL3CoreService
from gso.products.product_types.office_router import ImportedOfficeRouter, OfficeRouter
from gso.products.product_types.opengear import ImportedOpengear, Opengear
......@@ -18,7 +18,7 @@ from gso.products.product_types.pop_vlan import PopVlan
from gso.products.product_types.router import ImportedRouter, Router
from gso.products.product_types.site import ImportedSite, Site
from gso.products.product_types.super_pop_switch import ImportedSuperPopSwitch, SuperPopSwitch
from gso.products.product_types.switch import Switch
from gso.products.product_types.switch import ImportedSwitch, Switch
class ProductName(strEnum):
......@@ -30,7 +30,9 @@ class ProductName(strEnum):
SUPER_POP_SWITCH = "Super PoP switch"
OFFICE_ROUTER = "Office router"
SWITCH = "Switch"
IMPORTED_SWITCH = "Imported Switch"
LAN_SWITCH_INTERCONNECT = "LAN Switch Interconnect"
IMPORTED_LAN_SWITCH_INTERCONNECT = "Imported LAN Switch Interconnect"
POP_VLAN = "Pop VLAN"
IMPORTED_IP_TRUNK = "Imported IP trunk"
IMPORTED_ROUTER = "Imported router"
......@@ -56,7 +58,9 @@ class ProductType(strEnum):
SUPER_POP_SWITCH = SuperPopSwitch.__name__
OFFICE_ROUTER = OfficeRouter.__name__
SWITCH = Switch.__name__
IMPORTED_SWITCH = ImportedSwitch.__name__
LAN_SWITCH_INTERCONNECT = LanSwitchInterconnect.__name__
IMPORTED_LAN_SWITCH_INTERCONNECT = ImportedLanSwitchInterconnect.__name__
POP_VLAN = PopVlan.__name__
IMPORTED_IP_TRUNK = ImportedIptrunk.__name__
IMPORTED_ROUTER = ImportedRouter.__name__
......@@ -81,7 +85,9 @@ SUBSCRIPTION_MODEL_REGISTRY.update(
ProductName.SUPER_POP_SWITCH.value: SuperPopSwitch,
ProductName.OFFICE_ROUTER.value: OfficeRouter,
ProductName.SWITCH.value: Switch,
ProductName.IMPORTED_SWITCH.value: ImportedSwitch,
ProductName.LAN_SWITCH_INTERCONNECT.value: LanSwitchInterconnect,
ProductName.IMPORTED_LAN_SWITCH_INTERCONNECT.value: ImportedLanSwitchInterconnect,
ProductName.POP_VLAN.value: PopVlan,
ProductName.IMPORTED_IP_TRUNK.value: ImportedIptrunk,
ProductName.IMPORTED_ROUTER.value: ImportedRouter,
......
"""LAN Switch Interconnect product block that has all parameters of a subscription throughout its lifecycle."""
from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle, strEnum
from orchestrator.types import SubscriptionLifecycle
from gso.products.product_blocks.router import RouterBlock, RouterBlockInactive, RouterBlockProvisioning
from gso.products.product_blocks.switch import SwitchBlock, SwitchBlockInactive, SwitchBlockProvisioning
from gso.utils.types.interfaces import LAGMemberList
from gso.utils.types.ip_address import IPv4NetworkType
class LanSwitchInterconnectAddressSpace(strEnum):
"""Types of :term:`LAN` Switch Interconnect. Can be private or public."""
PRIVATE = "Private"
PUBLIC = "Public"
from gso.utils.types.ip_address import AddressSpace, IPv4AddressType, IPv4NetworkType
class LanSwitchInterconnectInterfaceBlockInactive(
......@@ -55,6 +48,7 @@ class LanSwitchInterconnectRouterSideBlockInactive(
node: RouterBlockInactive
ae_iface: str | None = None
ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockInactive]
ipv4_address: IPv4AddressType | None = None
class LanSwitchInterconnectRouterSideBlockProvisioning(
......@@ -65,6 +59,7 @@ class LanSwitchInterconnectRouterSideBlockProvisioning(
node: RouterBlockProvisioning
ae_iface: str | None = None
ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockProvisioning] # type: ignore[assignment]
ipv4_address: IPv4AddressType | None
class LanSwitchInterconnectRouterSideBlock(
......@@ -75,6 +70,7 @@ class LanSwitchInterconnectRouterSideBlock(
node: RouterBlock
ae_iface: str
ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlock] # type: ignore[assignment]
ipv4_address: IPv4AddressType | None
class LanSwitchInterconnectSwitchSideBlockInactive(
......@@ -87,6 +83,7 @@ class LanSwitchInterconnectSwitchSideBlockInactive(
switch: SwitchBlockInactive
ae_iface: str | None = None
ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockInactive]
ipv4_address: IPv4AddressType | None = None
class LanSwitchInterconnectSwitchSideBlockProvisioning(
......@@ -97,6 +94,7 @@ class LanSwitchInterconnectSwitchSideBlockProvisioning(
switch: SwitchBlockProvisioning
ae_iface: str | None = None
ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlockProvisioning] # type: ignore[assignment]
ipv4_address: IPv4AddressType | None
class LanSwitchInterconnectSwitchSideBlock(
......@@ -107,6 +105,7 @@ class LanSwitchInterconnectSwitchSideBlock(
switch: SwitchBlock
ae_iface: str
ae_members: LAGMemberList[LanSwitchInterconnectInterfaceBlock] # type: ignore[assignment]
ipv4_address: IPv4AddressType | None
class LanSwitchInterconnectBlockInactive(
......@@ -118,7 +117,7 @@ class LanSwitchInterconnectBlockInactive(
lan_switch_interconnect_description: str | None = None
lan_switch_interconnect_ip_network: IPv4NetworkType | None = None
address_space: LanSwitchInterconnectAddressSpace | None = None
address_space: AddressSpace | None = None
minimum_links: int | None = None
router_side: LanSwitchInterconnectRouterSideBlockInactive
switch_side: LanSwitchInterconnectSwitchSideBlockInactive
......@@ -131,7 +130,7 @@ class LanSwitchInterconnectBlockProvisioning(
lan_switch_interconnect_description: str | None = None
lan_switch_interconnect_ip_network: IPv4NetworkType | None
address_space: LanSwitchInterconnectAddressSpace | None = None
address_space: AddressSpace | None = None
minimum_links: int | None = None
router_side: LanSwitchInterconnectRouterSideBlockProvisioning
switch_side: LanSwitchInterconnectSwitchSideBlockProvisioning
......@@ -145,7 +144,7 @@ class LanSwitchInterconnectBlock(LanSwitchInterconnectBlockProvisioning, lifecyc
#: The :term:`IP` resources for this :term:`VLAN` Switch Interconnect.
lan_switch_interconnect_ip_network: IPv4NetworkType | None
#: The address space of the :term:`VLAN` Switch Interconnect. It can be private or public.
address_space: LanSwitchInterconnectAddressSpace
address_space: AddressSpace
#: The minimum amount of links the :term:`LAN` Switch Interconnect should consist of.
minimum_links: int
#: The router side of the :term:`LAN` Switch Interconnect.
......
......@@ -26,3 +26,17 @@ class LanSwitchInterconnect(LanSwitchInterconnectProvisioning, lifecycle=[Subscr
"""An LAN Switch Interconnect that is active."""
lan_switch_interconnect: LanSwitchInterconnectBlock
class ImportedLanSwitchInterconnectInactive(SubscriptionModel, is_base=True):
"""An imported, inactive :term:`LAN` Switch Interconnect."""
lan_switch_interconnect: LanSwitchInterconnectBlockInactive
class ImportedLanSwitchInterconnect(
ImportedLanSwitchInterconnectInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE]
):
"""An imported :term:`LAN` Switch Interconnect."""
lan_switch_interconnect: LanSwitchInterconnectBlock
......@@ -22,3 +22,17 @@ class Switch(SwitchProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
"""A switch that is currently active."""
switch: SwitchBlock
class ImportedSwitchInactive(SubscriptionModel, is_base=True):
"""An imported, inactive switch."""
switch: SwitchBlockInactive
class ImportedSwitch(
ImportedSwitchInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE]
):
"""An imported switch that is currently active."""
switch: SwitchBlock
......@@ -53,6 +53,7 @@
"create_switch": "Create Switch",
"create_edge_port": "Create Edge Port",
"create_nren_l3_core_service": "Create NREN L3 Core Service",
"create_lan_switch_interconnect": "Create LAN Switch Interconnect",
"deploy_twamp": "Deploy TWAMP",
"migrate_iptrunk": "Migrate IP Trunk",
"migrate_nren_l3_core_service": "Migrate NREN L3 Core Service",
......@@ -68,6 +69,7 @@
"terminate_site": "Terminate Site",
"terminate_switch": "Terminate Switch",
"terminate_edge_port": "Terminate Edge Port",
"terminate_lan_switch_interconnect": "Terminate LAN Switch Interconnect",
"redeploy_base_config": "Redeploy base config",
"update_ibgp_mesh": "Update iBGP mesh",
"create_imported_site": "NOT FOR HUMANS -- Import existing site",
......@@ -78,6 +80,8 @@
"create_imported_opengear": "NOT FOR HUMANS -- Import existing OpenGear",
"create_imported_edge_port": "NOT FOR HUMANS -- Import existing Edge Port",
"create_imported_nren_l3_core_service": "NOT FOR HUMANS -- Import existing NREN L3 Core Service",
"create_imported_switch": "NOT FOR HUMANS -- Import existing Switch",
"create_imported_lan_switch_interconnect": "NOT FOR HUMANS -- Import existing LAN Switch Interconnect",
"import_site": "NOT FOR HUMANS -- Finalize import into a Site product",
"import_router": "NOT FOR HUMANS -- Finalize import into a Router product",
"import_iptrunk": "NOT FOR HUMANS -- Finalize import into an IP trunk product",
......@@ -86,10 +90,13 @@
"import_opengear": "NOT FOR HUMANS -- Finalize import into an OpenGear",
"import_edge_port": "NOT FOR HUMANS -- Finalize import into an Edge Port",
"import_nren_l3_core_service": "NOT FOR HUMANS -- Finalize import into a NREN L3 Core Service",
"import_switch": "NOT FOR HUMANS -- Finalize import into a Switch",
"import_lan_switch_interconnect": "NOT FOR HUMANS -- Finalize import into a LAN Switch Interconnect",
"validate_iptrunk": "Validate IP Trunk configuration",
"validate_router": "Validate Router configuration",
"validate_switch": "Validate Switch configuration",
"validate_edge_port": "Validate Edge Port",
"validate_lan_switch_interconnect": "Validate LAN Switch Interconnect",
"task_validate_geant_products": "Validation task for GEANT products",
"task_send_email_notifications": "Send email notifications for failed tasks",
"task_create_partners": "Create partner task",
......
......@@ -4,6 +4,7 @@ import ipaddress
from typing import Annotated, Any
from pydantic import AfterValidator, Field, PlainSerializer
from pydantic_forms.types import strEnum
from typing_extensions import Doc
......@@ -52,3 +53,10 @@ PortNumber = Annotated[
"and can therefore not be selected for permanent allocation."
),
]
class AddressSpace(strEnum):
"""Types of address space. Can be private or public."""
PRIVATE = "PRIVATE"
PUBLIC = "PRIVATE"
......@@ -20,6 +20,8 @@ WF_USABLE_MAP.update({
"terminate_site": ALL_ALIVE_STATES,
"terminate_router": ALL_ALIVE_STATES,
"terminate_iptrunk": ALL_ALIVE_STATES,
"terminate_switch": ALL_ALIVE_STATES,
"terminate_lan_switch_interconnect": ALL_ALIVE_STATES,
"promote_p_to_pe": [SubscriptionLifecycle.ACTIVE],
"validate_router": [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE],
"validate_iptrunk": [SubscriptionLifecycle.PROVISIONING, SubscriptionLifecycle.ACTIVE],
......@@ -55,11 +57,19 @@ LazyWorkflowInstance("gso.workflows.switch.create_switch", "create_switch")
LazyWorkflowInstance("gso.workflows.switch.activate_switch", "activate_switch")
LazyWorkflowInstance("gso.workflows.switch.terminate_switch", "terminate_switch")
LazyWorkflowInstance("gso.workflows.switch.validate_switch", "validate_switch")
LazyWorkflowInstance("gso.workflows.switch.create_imported_switch", "create_imported_switch")
LazyWorkflowInstance("gso.workflows.switch.import_switch", "import_switch")
# LAN Switch Interconnect workflows
LazyWorkflowInstance(
"gso.workflows.lan_switch_interconnect.create_lan_switch_interconnect", "create_lan_switch_interconnect"
)
LazyWorkflowInstance(
"gso.workflows.lan_switch_interconnect.terminate_lan_switch_interconnect", "terminate_lan_switch_interconnect"
)
LazyWorkflowInstance(
"gso.workflows.lan_switch_interconnect.validate_lan_switch_interconnect", "validate_lan_switch_interconnect"
)
# Site workflows
LazyWorkflowInstance("gso.workflows.site.create_site", "create_site")
......
"""Import an existing :term:`LAN` Switch Interconnect into the subscription database."""
from uuid import uuid4
from orchestrator import step, workflow
from orchestrator.forms import FormPage
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State, SubscriptionLifecycle
from orchestrator.workflow import StepList, begin, done
from orchestrator.workflows.steps import resync, set_status, store_process_subscription
from products.product_types.lan_switch_interconnect import ImportedLanSwitchInterconnectInactive
from gso.cli.imports import LanSwitchInterconnectRouterSideImportModel, LanSwitchInterconnectSwitchSideImportModel
from gso.products import ProductName
from gso.products.product_blocks.lan_switch_interconnect import (
LanSwitchInterconnectRouterSideBlockInactive,
LanSwitchInterconnectSwitchSideBlockInactive,
)
from gso.services.partners import get_partner_by_name
from gso.services.subscriptions import get_product_id_by_name
from gso.utils.types.ip_address import AddressSpace, IPv4NetworkType
def _initial_input_form_generator() -> FormGenerator:
class ImportLanSwitchInterconnect(FormPage):
lan_switch_interconnect_description: str
lan_switch_interconnect_ip_network: IPv4NetworkType | None
address_space: AddressSpace
minimum_links: int
router_side: LanSwitchInterconnectRouterSideImportModel
switch_side: LanSwitchInterconnectSwitchSideImportModel
user_input = yield ImportLanSwitchInterconnect
return user_input.model_dump() | {"partner": "GEANT"}
@step("Create subscription")
def create_subscription(partner: str) -> State:
"""Create a new subscription object."""
partner_id = get_partner_by_name(partner)["partner_id"]
product_id = get_product_id_by_name(ProductName.IMPORTED_LAN_SWITCH_INTERCONNECT)
subscription = ImportedLanSwitchInterconnectInactive.from_product_id(product_id, partner_id)
return {"subscription": subscription, "subscription_id": subscription.subscription_id}
@step("Initialize subscription")
def initialize_subscription(
subscription: ImportedLanSwitchInterconnectInactive,
lan_switch_interconnect_description: str,
lan_switch_interconnect_ip_network: IPv4NetworkType | None,
address_space: AddressSpace,
minimum_links: int,
lan_switch_interconnect_router_side: dict,
lan_switch_interconnect_switch_side: dict,
) -> State:
"""Initialize the subscription using input data."""
subscription.lan_switch_interconnect.lan_switch_interconnect_description = lan_switch_interconnect_description
subscription.lan_switch_interconnect.lan_switch_interconnect_ip_network = lan_switch_interconnect_ip_network
subscription.lan_switch_interconnect.address_space = address_space
subscription.lan_switch_interconnect.minimum_links = minimum_links
subscription.lan_switch_interconnect.router_side = LanSwitchInterconnectRouterSideBlockInactive.new(
uuid4(), **lan_switch_interconnect_router_side
)
subscription.lan_switch_interconnect.switch_side = LanSwitchInterconnectSwitchSideBlockInactive.new(
uuid4(), **lan_switch_interconnect_switch_side
)
return {"subscription": subscription}
@workflow(
"Create Imported LAN Switch Interconnect", initial_input_form=_initial_input_form_generator, target=Target.CREATE
)
def create_imported_lan_switch_interconnect() -> StepList:
"""Create an imported :term:`LAN` Switch Interconnect without provisioning it."""
return (
begin
>> create_subscription
>> store_process_subscription(Target.CREATE)
>> initialize_subscription
>> set_status(SubscriptionLifecycle.ACTIVE)
>> resync
>> done
)
......@@ -14,7 +14,6 @@ from pydantic import AfterValidator, ConfigDict
from pydantic_forms.validators import Divider, ReadOnlyField
from gso.products.product_blocks.lan_switch_interconnect import (
LanSwitchInterconnectAddressSpace,
LanSwitchInterconnectInterfaceBlockInactive,
)
from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnectInactive
......@@ -36,6 +35,7 @@ from gso.utils.types.interfaces import (
PhysicalPortCapacity,
validate_interface_names_are_unique,
)
from gso.utils.types.ip_address import AddressSpace
from gso.utils.types.tt_number import TTNumber
......@@ -47,7 +47,7 @@ def _initial_input_form(product_name: str) -> FormGenerator:
partner: ReadOnlyField("GEANT", default_type=str) # type: ignore[valid-type]
router_side: active_router_selector() # type: ignore[valid-type]
switch_side: active_switch_selector() # type: ignore[valid-type]
address_space: LanSwitchInterconnectAddressSpace
address_space: AddressSpace
description: str
minimum_link_count: int
divider: Divider
......@@ -116,7 +116,7 @@ def create_subscription(product: UUIDstr, partner: str) -> State:
def initialize_subscription(
subscription: LanSwitchInterconnectInactive,
description: str,
address_space: LanSwitchInterconnectAddressSpace,
address_space: AddressSpace,
minimum_link_count: int,
router_side: UUIDstr,
router_side_iface: JuniperPhyInterface,
......@@ -146,7 +146,7 @@ def initialize_subscription(
@workflow(
"Create LAN switch interconnect",
"Create LAN Switch Interconnect",
initial_input_form=wrap_create_initial_input_form(_initial_input_form),
target=Target.CREATE,
)
......
"""Import an existing :term:`LAN` Switch Interconnect into the service database."""
from orchestrator import step, workflow
from orchestrator.targets import Target
from orchestrator.types import State, UUIDstr
from orchestrator.workflow import StepList, begin, done
from orchestrator.workflows.steps import resync, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from gso.products import ProductName
from gso.products.product_types.lan_switch_interconnect import ImportedLanSwitchInterconnect, LanSwitchInterconnect
from gso.services.subscriptions import get_product_id_by_name
@step("Create new switch subscription")
def import_lan_switch_interconnect_subscription(subscription_id: UUIDstr) -> State:
"""Take an imported subscription, and turn it into a full subscription."""
old_subscription = ImportedLanSwitchInterconnect.from_subscription(subscription_id)
new_subscription_id = get_product_id_by_name(ProductName.LAN_SWITCH_INTERCONNECT)
new_subscription = LanSwitchInterconnect.from_other_product(old_subscription, new_subscription_id) # type: ignore[arg-type]
return {"subscription": new_subscription}
@workflow(
"Import LAN Switch Interconnect", target=Target.MODIFY, initial_input_form=wrap_modify_initial_input_form(None)
)
def import_lan_switch_interconnect() -> StepList:
"""Modify an ImportedLanSwitchInterconnect subscription into subscription to complete the import."""
return (
begin
>> store_process_subscription(Target.MODIFY)
>> unsync
>> import_lan_switch_interconnect_subscription
>> resync
>> done
)
"""Workflow for terminating a LAN Switch interconnect."""
from orchestrator import begin, workflow
from orchestrator.forms import FormPage
from orchestrator.targets import Target
from orchestrator.types import SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import StepList, done
from orchestrator.workflows.steps import resync, set_status, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from pydantic_forms.types import FormGenerator
from pydantic_forms.validators import Label
from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
from gso.utils.types.tt_number import TTNumber
def _input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
lan_switch_interconnect = LanSwitchInterconnect.from_subscription(subscription_id)
class TerminateForm(FormPage):
if lan_switch_interconnect.status == SubscriptionLifecycle.INITIAL:
info_label: Label = (
"This will immediately mark the subscription as terminated, preventing any other workflows from "
"interacting with this product subscription."
)
info_label_2: Label = "ONLY EXECUTE THIS WORKFLOW WHEN YOU ARE ABSOLUTELY SURE WHAT YOU ARE DOING."
tt_number: TTNumber
yield TerminateForm
return {"subscription": lan_switch_interconnect}
@workflow(
"Terminate LAN Switch Interconnect",
initial_input_form=wrap_modify_initial_input_form(_input_form_generator),
target=Target.TERMINATE,
)
def terminate_lan_switch_interconnect() -> StepList:
"""Terminate a :term:`LAN` Switch Interconnect."""
return (
begin
>> store_process_subscription(Target.TERMINATE)
>> unsync
>> set_status(SubscriptionLifecycle.TERMINATED)
>> resync
>> done
)
"""Validation workflow for :term:`LAN` Switch Interconnect subscription objects."""
from typing import Any
from orchestrator.targets import Target
from orchestrator.workflow import StepList, begin, done, step, workflow
from orchestrator.workflows.steps import resync, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from gso.services.lso_client import LSOState, anonymous_lso_interaction
@step("Check config for drift")
def validate_config(subscription: dict[str, Any]) -> LSOState:
"""Workflow step for running a playbook that checks whether config has drifted."""
return {
"playbook_name": "lan_switch_interconnect.yaml",
"inventory": {"all": {"hosts": {None: None}}},
"extra_vars": {
"subscription_json": subscription,
"verb": "deploy",
"dry_run": "true",
"is_verification_workflow": "true",
},
}
@workflow(
"Validate LAN Switch Interconnect", target=Target.SYSTEM, initial_input_form=(wrap_modify_initial_input_form(None))
)
def validate_lan_switch_interconnect() -> StepList:
"""Validate an existing :term:`LAN` Switch Interconnect."""
return (
begin
>> store_process_subscription(Target.SYSTEM)
>> unsync
>> anonymous_lso_interaction(validate_config)
>> resync
>> done
)
......@@ -25,10 +25,7 @@ def create_subscription(partner: str) -> State:
product_id = get_product_id_by_name(ProductName.IMPORTED_ROUTER)
subscription = ImportedRouterInactive.from_product_id(product_id, partner_id)
return {
"subscription": subscription,
"subscription_id": subscription.subscription_id,
}
return {"subscription": subscription, "subscription_id": subscription.subscription_id}
def initial_input_form_generator() -> FormGenerator:
......
"""A creation workflow that adds an existing switch to the service database."""
from orchestrator import step, workflow
from orchestrator.forms import FormPage
from orchestrator.targets import Target
from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr
from orchestrator.workflow import StepList, begin, done
from orchestrator.workflows.steps import resync, set_status, store_process_subscription
from gso.products import ProductName
from gso.products.product_blocks.switch import SwitchModel
from gso.products.product_types.site import Site
from gso.products.product_types.switch import ImportedSwitchInactive
from gso.services.partners import get_partner_by_name
from gso.services.subscriptions import get_product_id_by_name
from gso.utils.shared_enums import Vendor
from gso.utils.types.ip_address import PortNumber
def _initial_input_form_generator() -> FormGenerator:
class ImportSwitch(FormPage):
fqdn: str
ts_port: PortNumber
site: UUIDstr
switch_vendor: Vendor
switch_model: SwitchModel
user_input = yield ImportSwitch
return user_input.model_dump() | {"partner": "GEANT"}
@step("Create subscription")
def create_subscription(partner: str) -> State:
"""Create a new subscription object."""
partner_id = get_partner_by_name(partner)["partner_id"]
product_id = get_product_id_by_name(ProductName.IMPORTED_SWITCH)
subscription = ImportedSwitchInactive.from_product_id(product_id, partner_id)
return {"subscription": subscription, "subscription_id": subscription.subscription_id}
@step("Initialize subscription")
def initialize_subscription(
subscription: ImportedSwitchInactive,
fqdn: str,
ts_port: PortNumber,
site: UUIDstr,
switch_vendor: Vendor,
switch_model: SwitchModel,
) -> State:
"""Initialize the switch subscription using input data."""
subscription.switch.fqdn = fqdn
subscription.switch.ts_port = ts_port
subscription.switch.site = Site.from_subscription(site).site
subscription.switch.switch_vendor = switch_vendor
subscription.switch.switch_model = switch_model
return {"subscription": subscription}
@workflow("Create Imported Switch", initial_input_form=_initial_input_form_generator, target=Target.CREATE)
def create_imported_switch() -> StepList:
"""Create an imported switch without provisioning it."""
return (
begin
>> create_subscription
>> store_process_subscription(Target.CREATE)
>> initialize_subscription
>> set_status(SubscriptionLifecycle.ACTIVE)
>> resync
>> done
)
"""Import an existing Switch into the service database."""
from orchestrator import step, workflow
from orchestrator.targets import Target
from orchestrator.types import State, UUIDstr
from orchestrator.workflow import StepList, begin, done
from orchestrator.workflows.steps import resync, store_process_subscription, unsync
from orchestrator.workflows.utils import wrap_modify_initial_input_form
from gso.products import ProductName
from gso.products.product_types.switch import ImportedSwitch, Switch
from gso.services.subscriptions import get_product_id_by_name
@step("Create new switch subscription")
def import_switch_subscription(subscription_id: UUIDstr) -> State:
"""Take an ImportedSwitch subscription, and turn it into a switch subscription."""
old_switch = ImportedSwitch.from_subscription(subscription_id)
new_subscription_id = get_product_id_by_name(ProductName.SWITCH)
new_subscription = Switch.from_other_product(old_switch, new_subscription_id) # type: ignore[arg-type]
return {"subscription": new_subscription}
@workflow("Import Switch", target=Target.MODIFY, initial_input_form=wrap_modify_initial_input_form(None))
def import_switch() -> StepList:
"""Modify an ImportedSwitch subscription into a Switch subscription to complete the import."""
return begin >> store_process_subscription(Target.MODIFY) >> unsync >> import_switch_subscription >> resync >> done
......@@ -12,7 +12,7 @@ from pydantic_forms.types import FormGenerator, UUIDstr
from pydantic_forms.validators import Label
from gso.products.product_types.switch import Switch
from gso.utils.types import TTNumber
from gso.utils.types.tt_number import TTNumber
def _input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
......
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