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

Add properties for switch and DCN management VLAN IDs to LAN Switch Interconnect

parent 223e6810
No related branches found
No related tags found
1 merge request!302Feature/update lan interconnect
This commit is part of merge request !302. Comments created here will be created in the context of that merge request.
"""Add VLAN IDs to LAN Switch Interconnect.
Revision ID: 818d4ffe65df
Revises: fc7bd696014e
Create Date: 2024-12-09 11:11:35.239599
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = '818d4ffe65df'
down_revision = 'fc7bd696014e'
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
conn.execute(sa.text("""
INSERT INTO resource_types (resource_type, description) VALUES ('switch_management_vlan_id', 'VLAN ID of the switch management network') RETURNING resource_types.resource_type_id
"""))
conn.execute(sa.text("""
INSERT INTO resource_types (resource_type, description) VALUES ('dcn_management_vlan_id', 'VLAN ID of the DCN management network') 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 ('switch_management_vlan_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 ('dcn_management_vlan_id')))
"""))
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 ('switch_management_vlan_id'))
"""))
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 ('switch_management_vlan_id'))
"""))
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 ('dcn_management_vlan_id'))
"""))
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 ('dcn_management_vlan_id'))
"""))
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 ('switch_management_vlan_id', 'dcn_management_vlan_id'))
"""))
conn.execute(sa.text("""
DELETE FROM resource_types WHERE resource_types.resource_type IN ('switch_management_vlan_id', 'dcn_management_vlan_id')
"""))
......@@ -6,6 +6,7 @@ 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.virtual_identifiers import VLAN_ID
class LanSwitchInterconnectInterfaceBlockInactive(
......@@ -110,6 +111,8 @@ class LanSwitchInterconnectBlockInactive(
lan_switch_interconnect_description: str | None = None
minimum_links: int | None = None
switch_management_vlan_id: VLAN_ID | None = None
dcn_management_vlan_id: VLAN_ID | None = None
router_side: LanSwitchInterconnectRouterSideBlockInactive
switch_side: LanSwitchInterconnectSwitchSideBlockInactive
......@@ -121,6 +124,8 @@ class LanSwitchInterconnectBlockProvisioning(
lan_switch_interconnect_description: str | None = None
minimum_links: int | None = None
switch_management_vlan_id: VLAN_ID
dcn_management_vlan_id: VLAN_ID | None
router_side: LanSwitchInterconnectRouterSideBlockProvisioning
switch_side: LanSwitchInterconnectSwitchSideBlockProvisioning
......@@ -132,6 +137,10 @@ class LanSwitchInterconnectBlock(LanSwitchInterconnectBlockProvisioning, lifecyc
lan_switch_interconnect_description: str
#: The minimum amount of links the LAN Switch Interconnect should consist of.
minimum_links: int
#: VLAN ID for the switch management network.
switch_management_vlan_id: VLAN_ID
#: VLAN ID for the DCN management network, if the site of this product contains optical equipment.
dcn_management_vlan_id: VLAN_ID | None
#: The router side of the LAN Switch Interconnect.
router_side: LanSwitchInterconnectRouterSideBlock
#: The switch side of the LAN Switch Interconnect.
......
......@@ -13,3 +13,6 @@ VC_ID = Annotated[
"A Virtual Circuit ID, the upper limit comes from the highest number that a service ID could be in Nokia srOS."
),
]
DEFAULT_SWITCH_MANAGEMENT_VLAN_ID: VLAN_ID = 998
DEFAULT_DCN_MANAGEMENT_VLAN_ID: VLAN_ID = 103
......@@ -21,12 +21,19 @@ from gso.products.product_types.router import Router
from gso.products.product_types.switch import Switch
from gso.services.partners import get_partner_by_name
from gso.services.subscriptions import get_product_id_by_name
from gso.utils.types.virtual_identifiers import (
DEFAULT_DCN_MANAGEMENT_VLAN_ID,
DEFAULT_SWITCH_MANAGEMENT_VLAN_ID,
VLAN_ID,
)
def _initial_input_form_generator() -> FormGenerator:
class ImportLanSwitchInterconnect(FormPage):
lan_switch_interconnect_description: str
minimum_links: int
switch_management_vlan_id: VLAN_ID | None = DEFAULT_SWITCH_MANAGEMENT_VLAN_ID
dcn_management_vlan_id: VLAN_ID | None = DEFAULT_DCN_MANAGEMENT_VLAN_ID
router_side: LanSwitchInterconnectRouterSideImportModel
switch_side: LanSwitchInterconnectSwitchSideImportModel
......@@ -49,12 +56,16 @@ def initialize_subscription(
subscription: ImportedLanSwitchInterconnectInactive,
lan_switch_interconnect_description: str,
minimum_links: int,
switch_management_vlan_id: VLAN_ID,
dcn_management_vlan_id: VLAN_ID,
router_side: dict,
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.minimum_links = minimum_links
subscription.lan_switch_interconnect.switch_management_vlan_id = switch_management_vlan_id
subscription.lan_switch_interconnect.dcn_management_vlan_id = dcn_management_vlan_id
router_block = Router.from_subscription(router_side.pop("node")).router
router_side_interfaces = [
......
......@@ -13,7 +13,7 @@ from orchestrator.workflow import StepList, begin, done, step, workflow
from orchestrator.workflows.steps import resync, set_status, store_process_subscription
from orchestrator.workflows.utils import wrap_create_initial_input_form
from pydantic import AfterValidator, ConfigDict
from pydantic_forms.validators import Divider, ReadOnlyField
from pydantic_forms.validators import ReadOnlyField
from gso.products.product_blocks.lan_switch_interconnect import (
LanSwitchInterconnectInterfaceBlockInactive,
......@@ -42,6 +42,7 @@ from gso.utils.types.interfaces import (
validate_interface_names_are_unique,
)
from gso.utils.types.tt_number import TTNumber
from gso.utils.types.virtual_identifiers import DEFAULT_DCN_MANAGEMENT_VLAN_ID, DEFAULT_SWITCH_MANAGEMENT_VLAN_ID
from gso.workflows.shared import create_summary_form
......@@ -55,8 +56,6 @@ def _initial_input_form(product_name: str) -> FormGenerator:
switch_side: active_switch_selector() # type: ignore[valid-type]
description: str
minimum_link_count: int
divider: Divider
vlan_id: ReadOnlyField(111, default_type=int) # type: ignore[valid-type]
initial_input = yield CreateLANSwitchInterconnectForm
router = Router.from_subscription(initial_input.router_side)
......@@ -113,7 +112,6 @@ def _initial_input_form(product_name: str) -> FormGenerator:
"switch_side",
"description",
"minimum_link_count",
"vlan_id",
"router_side_iface",
"router_side_ae_members",
"switch_side_iface",
......@@ -147,6 +145,7 @@ def initialize_subscription(
"""Update the product model with all input from the operator."""
subscription.lan_switch_interconnect.lan_switch_interconnect_description = description
subscription.lan_switch_interconnect.minimum_links = minimum_link_count
subscription.lan_switch_interconnect.switch_management_vlan_id = DEFAULT_SWITCH_MANAGEMENT_VLAN_ID
subscription.lan_switch_interconnect.router_side.node = Router.from_subscription(router_side).router
subscription.lan_switch_interconnect.router_side.ae_iface = router_side_iface
for member in router_side_ae_members:
......@@ -159,6 +158,8 @@ def initialize_subscription(
subscription.lan_switch_interconnect.switch_side.ae_members.append(
LanSwitchInterconnectInterfaceBlockInactive.new(subscription_id=uuid4(), **member)
)
if subscription.lan_switch_interconnect.router_side.node.router_site.site_contains_optical_equipment:
subscription.lan_switch_interconnect.dcn_management_vlan_id = DEFAULT_DCN_MANAGEMENT_VLAN_ID
return {"subscription": subscription}
......
......@@ -18,6 +18,11 @@ from gso.products.product_types.lan_switch_interconnect import (
from gso.products.product_types.router import Router
from gso.products.product_types.switch import Switch
from gso.services.subscriptions import get_product_id_by_name
from gso.utils.types.virtual_identifiers import (
DEFAULT_DCN_MANAGEMENT_VLAN_ID,
DEFAULT_SWITCH_MANAGEMENT_VLAN_ID,
VLAN_ID,
)
@pytest.fixture()
......@@ -31,6 +36,8 @@ def lan_switch_interconnect_subscription_factory(
start_date: str | None = "2024-01-01T10:20:30+01:02",
lan_switch_interconnect_description: str | None = None,
minimum_links: int | None = None,
switch_management_vlan_id: VLAN_ID | None = None,
dcn_management_vlan_id: VLAN_ID | None = None,
router_side_node: UUIDstr | None = None,
router_side_ae_iface: str | None = None,
router_side_ae_members: list[dict[str, str]] | None = None,
......@@ -78,6 +85,12 @@ def lan_switch_interconnect_subscription_factory(
ae_iface=switch_side_ae_iface or faker.network_interface(),
ae_members=switch_side_ae_members,
)
subscription.lan_switch_interconnect.dcn_management_vlan_id = (
dcn_management_vlan_id or DEFAULT_DCN_MANAGEMENT_VLAN_ID
)
subscription.lan_switch_interconnect.switch_management_vlan_id = (
switch_management_vlan_id or DEFAULT_SWITCH_MANAGEMENT_VLAN_ID
)
subscription = SubscriptionModel.from_other_lifecycle(subscription, SubscriptionLifecycle.ACTIVE)
subscription.insync = True
......
......@@ -3,6 +3,7 @@ from orchestrator.types import SubscriptionLifecycle
from gso.products import ProductName
from gso.products.product_types.lan_switch_interconnect import ImportedLanSwitchInterconnect
from gso.utils.types.virtual_identifiers import DEFAULT_DCN_MANAGEMENT_VLAN_ID, DEFAULT_SWITCH_MANAGEMENT_VLAN_ID
from test.workflows import (
assert_complete,
extract_state,
......@@ -37,3 +38,24 @@ def test_create_imported_lan_switch_interconnect_success(workflow_input_data):
assert_complete(result)
assert subscription.product.name == ProductName.IMPORTED_LAN_SWITCH_INTERCONNECT
assert subscription.status == SubscriptionLifecycle.ACTIVE
assert subscription.lan_switch_interconnect.dcn_management_vlan_id == DEFAULT_DCN_MANAGEMENT_VLAN_ID
assert subscription.lan_switch_interconnect.switch_management_vlan_id == DEFAULT_SWITCH_MANAGEMENT_VLAN_ID
@pytest.mark.workflow()
def test_create_imported_lan_switch_interconnect_custom_vlan_ids(faker, workflow_input_data):
custom_switch_vlan_id = faker.vlan_id()
custom_dcn_vlan_id = faker.vlan_id()
workflow_input_data.update({
"switch_management_vlan_id": custom_switch_vlan_id,
"dcn_management_vlan_id": custom_dcn_vlan_id,
})
result, _, _ = run_workflow("create_imported_lan_switch_interconnect", [workflow_input_data])
state = extract_state(result)
subscription = ImportedLanSwitchInterconnect.from_subscription(state["subscription_id"])
assert_complete(result)
assert subscription.product.name == ProductName.IMPORTED_LAN_SWITCH_INTERCONNECT
assert subscription.status == SubscriptionLifecycle.ACTIVE
assert subscription.lan_switch_interconnect.dcn_management_vlan_id == custom_dcn_vlan_id
assert subscription.lan_switch_interconnect.switch_management_vlan_id == custom_switch_vlan_id
......@@ -6,6 +6,7 @@ from orchestrator.types import SubscriptionLifecycle
from gso.products import ProductName
from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
from gso.services.subscriptions import get_product_id_by_name
from gso.utils.types.virtual_identifiers import DEFAULT_DCN_MANAGEMENT_VLAN_ID
from test.services.conftest import MockedNetboxClient
from test.workflows import assert_complete, extract_state, run_workflow
......@@ -26,43 +27,51 @@ def _netbox_client_mock():
@pytest.fixture()
def input_form_data(faker, router_subscription_factory, switch_subscription_factory):
return [
{
"product": get_product_id_by_name(ProductName.LAN_SWITCH_INTERCONNECT),
},
{
"tt_number": faker.tt_number(),
"router_side": router_subscription_factory(),
"switch_side": switch_subscription_factory(),
"description": faker.sentence(),
"minimum_link_count": 2,
"vlan_id": 111, # VLAN ID for new interconnections is always 111
},
{
"router_side_iface": "lag-1",
"router_side_ae_members": faker.link_members_nokia()[:2],
},
{
"switch_side_iface": faker.network_interface(),
"switch_side_ae_members": faker.link_members_juniper()[:2],
},
{},
]
def input_form_data(faker, router_subscription_factory, switch_subscription_factory, site_subscription_factory):
def _input_form_data(site_contains_optical_equipment):
return [
{
"product": get_product_id_by_name(ProductName.LAN_SWITCH_INTERCONNECT),
},
{
"tt_number": faker.tt_number(),
"router_side": router_subscription_factory(
router_site=site_subscription_factory(
site_contains_optical_equipment=site_contains_optical_equipment
)
),
"switch_side": switch_subscription_factory(),
"description": faker.sentence(),
"minimum_link_count": 2,
},
{
"router_side_iface": "lag-1",
"router_side_ae_members": faker.link_members_nokia()[:2],
},
{
"switch_side_iface": faker.network_interface(),
"switch_side_ae_members": faker.link_members_juniper()[:2],
},
{},
]
return _input_form_data
@pytest.mark.workflow()
@patch("gso.services.infoblox.create_v6_network_by_ip")
@patch("gso.services.infoblox.create_v4_network_by_ip")
@patch("gso.services.infoblox.create_host_by_ip")
@pytest.mark.parametrize("site_contains_optical_equipment", [True, False])
@patch("gso.workflows.lan_switch_interconnect.create_lan_switch_interconnect.create_v6_network_by_ip")
@patch("gso.workflows.lan_switch_interconnect.create_lan_switch_interconnect.create_v4_network_by_ip")
@patch("gso.workflows.lan_switch_interconnect.create_lan_switch_interconnect.create_host_by_ip")
def test_create_lan_switch_interconnect_success(
mock_create_host,
mock_create_v4_network,
mock_create_v6_network,
site_contains_optical_equipment,
input_form_data,
_netbox_client_mock, # noqa: PT019
):
result, _, _ = run_workflow("create_lan_switch_interconnect", input_form_data)
result, _, _ = run_workflow("create_lan_switch_interconnect", input_form_data(site_contains_optical_equipment))
assert_complete(result)
state = extract_state(result)
......@@ -72,3 +81,6 @@ def test_create_lan_switch_interconnect_success(
assert mock_create_v4_network.call_count == 1
assert mock_create_v6_network.call_count == 1
assert mock_create_host.call_count == 4
assert subscription.lan_switch_interconnect.dcn_management_vlan_id == (
DEFAULT_DCN_MANAGEMENT_VLAN_ID if site_contains_optical_equipment else None
)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment