From 223e6810f78809fd9c9f6a9041c86f18bf2e194b Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Fri, 6 Dec 2024 15:15:15 +0100
Subject: [PATCH] Reserve IPv6 resources when creating a LAN Switch
 Interconnect

---
 gso/services/infoblox.py                      | 35 +++++++-
 gso/utils/helpers.py                          | 19 ++++-
 gso/workflows/iptrunk/create_iptrunk.py       |  4 +-
 gso/workflows/iptrunk/migrate_iptrunk.py      |  2 +-
 .../create_lan_switch_interconnect.py         | 80 ++++++++++++++-----
 .../terminate_lan_switch_interconnect.py      |  4 +-
 .../validate_lan_switch_interconnect.py       |  4 +-
 test/cli/test_imports.py                      |  2 +-
 test/fixtures/site_fixtures.py                |  2 +
 test/fixtures/switch_fixtures.py              |  2 +-
 test/utils/test_helpers.py                    | 25 ++++++
 .../test_create_lan_switch_interconnect.py    |  5 +-
 .../site/test_create_imported_site.py         |  1 +
 .../switch/test_create_imported_switch.py     |  2 +-
 test/workflows/switch/test_create_switch.py   |  2 +
 15 files changed, 153 insertions(+), 36 deletions(-)

diff --git a/gso/services/infoblox.py b/gso/services/infoblox.py
index 1068be97..a0f5df25 100644
--- a/gso/services/infoblox.py
+++ b/gso/services/infoblox.py
@@ -10,7 +10,14 @@ from infoblox_client.exceptions import (
 )
 
 from gso.settings import IPAMParams, load_oss_params
-from gso.utils.types.ip_address import IPv4AddressType, IPv4Netmask, IPv4NetworkType, IPv6AddressType, IPv6Netmask
+from gso.utils.types.ip_address import (
+    IPv4AddressType,
+    IPv4Netmask,
+    IPv4NetworkType,
+    IPv6AddressType,
+    IPv6Netmask,
+    IPv6NetworkType,
+)
 
 logger = getLogger(__name__)
 NULL_MAC = "00:00:00:00:00:00"
@@ -76,7 +83,11 @@ def _allocate_network(  # noqa: PLR0917
 def create_v4_network_by_ip(
     dns_view: str, network_view: str, network: IPv4NetworkType, comment: str | None = ""
 ) -> None:
-    """Register an IPv4 network at the given location. Raises an :class:`AllocationError` on failure."""
+    """Register an IPv4 network at the given location.
+
+    Raises:
+        AllocationError on failure.
+    """
     conn, _ = _setup_connection()
     created_net = objects.NetworkV4.create(
         conn, network=network, view=dns_view, network_view=network_view, comment=comment
@@ -88,6 +99,25 @@ def create_v4_network_by_ip(
     logger.debug(msg)
 
 
+def create_v6_network_by_ip(
+    dns_view: str, network_view: str, network: IPv6NetworkType, comment: str | None = ""
+) -> None:
+    """Register an IPv6 network at the given location.
+
+    Raises:
+        AllocationError on failure.
+    """
+    conn, _ = _setup_connection()
+    created_net = objects.NetworkV6.create(
+        conn, network=network, view=dns_view, network_view=network_view, comment=comment
+    )
+    if created_net.response != "Infoblox Object created":
+        msg = f"Failed to allocate network at {network}. Response from Netbox: {created_net.response}"
+        raise AllocationError(msg)
+    msg = f"Successfully registered new network at {network}"
+    logger.debug(msg)
+
+
 def hostname_available(hostname: str) -> bool:
     """Check whether a hostname is still available **in Infoblox**.
 
@@ -246,6 +276,7 @@ def create_host_by_ip(
     hostname: str,
     service_type: str,
     comment: str,
+    *,
     ipv4_address: IPv4AddressType | None = None,
     ipv6_address: IPv6AddressType | None = None,
 ) -> None:
diff --git a/gso/utils/helpers.py b/gso/utils/helpers.py
index 776d4629..aca14107 100644
--- a/gso/utils/helpers.py
+++ b/gso/utils/helpers.py
@@ -2,7 +2,7 @@
 
 import random
 import re
-from ipaddress import IPv4Network
+from ipaddress import IPv4Network, IPv6Network
 from typing import TYPE_CHECKING
 from uuid import UUID
 
@@ -19,7 +19,7 @@ from gso.services.partners import get_all_partners
 from gso.services.subscriptions import is_virtual_circuit_id_available
 from gso.utils.shared_enums import Vendor
 from gso.utils.types.interfaces import PhysicalPortCapacity
-from gso.utils.types.ip_address import IPv4AddressType, IPv4NetworkType
+from gso.utils.types.ip_address import IPv4AddressType, IPv4NetworkType, IPv6NetworkType
 from gso.utils.types.virtual_identifiers import VC_ID
 
 if TYPE_CHECKING:
@@ -127,8 +127,8 @@ def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str:
     return f"{hostname}.{site_name.lower()}.{country_code.lower()}{oss.IPAM.LO.domain_name}"
 
 
-def generate_lan_switch_interconnect_subnet(site_internal_id: int) -> IPv4NetworkType:
-    """Generate an IPv4 network in which a :term:`LAN` Switch Interconnect resides, given a Site internal ID."""
+def generate_lan_switch_interconnect_subnet_v4(site_internal_id: int) -> IPv4NetworkType:
+    """Generate an IPv4 network in which a LAN Switch Interconnect resides, given a Site internal ID."""
     ipam_oss = settings.load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
 
     result = str(ipam_oss.V4.containers[0]).split(".")[:2]  # Take the first two octets from the IPv4 network.
@@ -138,6 +138,17 @@ def generate_lan_switch_interconnect_subnet(site_internal_id: int) -> IPv4Networ
     return IPv4Network(".".join(result))
 
 
+def generate_lan_switch_interconnect_subnet_v6(site_internal_id: int) -> IPv6NetworkType:
+    """Generate an IPv6 network in which a LAN Switch Interconnect resides, given a Site internal ID."""
+    ipam_oss = settings.load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
+
+    result = IPv6Network(ipam_oss.V6.containers[0]).exploded[:17]  # Take the first 56 bits of the network
+    result += str(hex(site_internal_id)[2:])  # Append the site internal id for bytes 57 to 64 as hexadecimal number
+    result += f"::/{ipam_oss.V6.mask}"  # And fill the rest of the network with empty bits
+
+    return IPv6Network(result)
+
+
 def generate_inventory_for_routers(
     router_role: RouterRole,
     exclude_routers: list[str] | None = None,
diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py
index 909da8b2..cb37490e 100644
--- a/gso/workflows/iptrunk/create_iptrunk.py
+++ b/gso/workflows/iptrunk/create_iptrunk.py
@@ -505,7 +505,9 @@ def register_dns_records(subscription: IptrunkInactive) -> State:
         ipv4_addr = subscription.iptrunk.iptrunk_ipv4_network[index]
         ipv6_addr = subscription.iptrunk.iptrunk_ipv6_network[index + 1]
 
-        infoblox.create_host_by_ip(fqdn, "TRUNK", str(subscription.subscription_id), ipv4_addr, ipv6_addr)
+        infoblox.create_host_by_ip(
+            fqdn, "TRUNK", str(subscription.subscription_id), ipv4_address=ipv4_addr, ipv6_address=ipv6_addr
+        )
 
     return {"subscription": subscription}
 
diff --git a/gso/workflows/iptrunk/migrate_iptrunk.py b/gso/workflows/iptrunk/migrate_iptrunk.py
index 50dd2823..1282f636 100644
--- a/gso/workflows/iptrunk/migrate_iptrunk.py
+++ b/gso/workflows/iptrunk/migrate_iptrunk.py
@@ -740,7 +740,7 @@ def update_ipam(subscription: Iptrunk, replace_index: int, new_node: Router, new
     #  And in with the new
     new_fqdn = f"{new_lag_interface}-0.{new_node.router.router_fqdn}"
     comment = str(subscription.subscription_id)
-    infoblox.create_host_by_ip(new_fqdn, "TRUNK", comment, v4_addr, v6_addr)
+    infoblox.create_host_by_ip(new_fqdn, "TRUNK", comment, ipv4_address=v4_addr, ipv6_address=v6_addr)
 
     return {"subscription": subscription}
 
diff --git a/gso/workflows/lan_switch_interconnect/create_lan_switch_interconnect.py b/gso/workflows/lan_switch_interconnect/create_lan_switch_interconnect.py
index 4f92cc6d..15e14ef6 100644
--- a/gso/workflows/lan_switch_interconnect/create_lan_switch_interconnect.py
+++ b/gso/workflows/lan_switch_interconnect/create_lan_switch_interconnect.py
@@ -1,6 +1,6 @@
 """A creation workflow for creating a new interconnect between a switch and a router."""
 
-from ipaddress import IPv4Address
+from ipaddress import IPv4Network, IPv6Network
 from typing import Annotated
 from uuid import uuid4
 
@@ -21,7 +21,7 @@ from gso.products.product_blocks.lan_switch_interconnect import (
 from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnectInactive
 from gso.products.product_types.router import Router
 from gso.products.product_types.switch import Switch
-from gso.services.infoblox import create_host_by_ip, create_v4_network_by_ip
+from gso.services.infoblox import create_host_by_ip, create_v4_network_by_ip, create_v6_network_by_ip
 from gso.services.partners import get_partner_by_name
 from gso.settings import load_oss_params
 from gso.utils.helpers import (
@@ -29,7 +29,8 @@ from gso.utils.helpers import (
     active_switch_selector,
     available_interfaces_choices,
     available_lags_choices,
-    generate_lan_switch_interconnect_subnet,
+    generate_lan_switch_interconnect_subnet_v4,
+    generate_lan_switch_interconnect_subnet_v6,
 )
 from gso.utils.shared_enums import Vendor
 from gso.utils.types.interfaces import (
@@ -162,28 +163,28 @@ def initialize_subscription(
     return {"subscription": subscription}
 
 
-@step("Register network in IPAM")
-def register_dns_records_network(subscription: LanSwitchInterconnectInactive) -> State:
-    """Add :term:`DNS` records in :term:`IPAM`."""
+@step("Register IPv4 network in IPAM")
+def register_dns_records_v4_network(subscription: LanSwitchInterconnectInactive) -> State:
+    """Add DNS records in IPAM."""
     router_site = subscription.lan_switch_interconnect.router_side.node.router_site
     if not router_site or not router_site.site_internal_id:
         msg = "Site internal ID not set. Cannot continue."
         raise ProcessFailureError(msg, details=router_site)
 
-    new_network = generate_lan_switch_interconnect_subnet(router_site.site_internal_id)
+    new_network = generate_lan_switch_interconnect_subnet_v4(router_site.site_internal_id)
     ipam_oss_params = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
     create_v4_network_by_ip(
         ipam_oss_params.dns_view, ipam_oss_params.network_view, new_network, str(subscription.subscription_id)
     )
 
-    return {"ipam_registrations": {"network": new_network}}
+    return {"ipam_registrations": {"v4": {"network": new_network}}}
 
 
-@step("Register devices in IPAM")
-def register_dns_records_devices(
-    subscription: LanSwitchInterconnectInactive, subscription_id: UUIDstr, ipam_registrations: dict[str, str]
+@step("Register IPv4 devices in IPAM")
+def register_dns_records_v4_devices(
+    subscription: LanSwitchInterconnectInactive, subscription_id: UUIDstr, ipam_registrations: dict[str, dict[str, str]]
 ) -> State:
-    """Register :term:`DNS` records for both switch and router side in :term:`IPAM`."""
+    """Register DNS records for both switch and router side in IPAM."""
     switch_hostname = subscription.lan_switch_interconnect.switch_side.switch.fqdn
     router_hostname = (
         f"{subscription.lan_switch_interconnect.router_side.ae_iface}."
@@ -193,14 +194,51 @@ def register_dns_records_devices(
         msg = "Missing switch or router hostname, cannot continue."
         raise ProcessFailureError(msg, details=subscription.lan_switch_interconnect)
 
-    ip_network_prefix = ipam_registrations["network"].split(".")[:3]  # Take the first three octets of the network.
-    switch_side_ip = IPv4Address(".".join([*ip_network_prefix, "10"]))  # Add .10 as the fourth octet of the switch.
-    router_side_ip = IPv4Address(".".join([*ip_network_prefix, "1"]))  # Add .1 as the fourth octet of the router.
+    ip_network = IPv4Network(ipam_registrations["v4"]["network"])
 
-    create_host_by_ip(switch_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, switch_side_ip)
-    create_host_by_ip(router_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, router_side_ip)
+    create_host_by_ip(switch_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, ipv4_address=ip_network[10])
+    create_host_by_ip(router_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, ipv4_address=ip_network[1])
 
-    return {"ipam_registrations": {switch_hostname: switch_side_ip, router_hostname: router_side_ip}}
+    return {"ipam_registrations": {"v4": {switch_hostname: ip_network[10], router_hostname: ip_network[1]}}}
+
+
+@step("Register IPv6 network in IPAM")
+def register_dns_records_v6_network(subscription: LanSwitchInterconnectInactive) -> State:
+    """Add DNS records in IPAM."""
+    router_site = subscription.lan_switch_interconnect.router_side.node.router_site
+    if not router_site or not router_site.site_internal_id:
+        msg = "Site internal ID not set. Cannot continue."
+        raise ProcessFailureError(msg, details=router_site)
+
+    new_network = generate_lan_switch_interconnect_subnet_v6(router_site.site_internal_id)
+    ipam_oss_params = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
+    create_v6_network_by_ip(
+        ipam_oss_params.dns_view, ipam_oss_params.network_view, new_network, str(subscription.subscription_id)
+    )
+
+    return {"ipam_registrations": {"v6": {"network": new_network}}}
+
+
+@step("Register IPv6 devices in IPAM")
+def register_dns_records_v6_devices(
+    subscription: LanSwitchInterconnectInactive, subscription_id: UUIDstr, ipam_registrations: dict[str, dict[str, str]]
+) -> State:
+    """Register DNS records for both switch and router side in IPAM."""
+    switch_hostname = subscription.lan_switch_interconnect.switch_side.switch.fqdn
+    router_hostname = (
+        f"{subscription.lan_switch_interconnect.router_side.ae_iface}."
+        f"{subscription.lan_switch_interconnect.router_side.node.router_fqdn}"
+    )
+    if not (switch_hostname and router_hostname):
+        msg = "Missing switch or router hostname, cannot continue."
+        raise ProcessFailureError(msg, details=subscription.lan_switch_interconnect)
+
+    ip_network = IPv6Network(ipam_registrations["v6"]["network"])
+
+    create_host_by_ip(switch_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, ipv6_address=ip_network[10])
+    create_host_by_ip(router_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, ipv6_address=ip_network[1])
+
+    return {"ipam_registrations": {"v6": {switch_hostname: ip_network[10], router_hostname: ip_network[1]}}}
 
 
 @workflow(
@@ -215,8 +253,10 @@ def create_lan_switch_interconnect() -> StepList:
         >> create_subscription
         >> store_process_subscription(Target.CREATE)
         >> initialize_subscription
-        >> register_dns_records_network
-        >> register_dns_records_devices
+        >> register_dns_records_v4_network
+        >> register_dns_records_v4_devices
+        >> register_dns_records_v6_network
+        >> register_dns_records_v6_devices
         >> set_status(SubscriptionLifecycle.ACTIVE)
         >> resync
         >> done
diff --git a/gso/workflows/lan_switch_interconnect/terminate_lan_switch_interconnect.py b/gso/workflows/lan_switch_interconnect/terminate_lan_switch_interconnect.py
index 8b9549c0..5403d07f 100644
--- a/gso/workflows/lan_switch_interconnect/terminate_lan_switch_interconnect.py
+++ b/gso/workflows/lan_switch_interconnect/terminate_lan_switch_interconnect.py
@@ -12,7 +12,7 @@ from pydantic_forms.validators import Label
 
 from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
 from gso.services.infoblox import delete_host_by_fqdn, delete_network
-from gso.utils.helpers import generate_lan_switch_interconnect_subnet
+from gso.utils.helpers import generate_lan_switch_interconnect_subnet_v4
 from gso.utils.types.tt_number import TTNumber
 
 
@@ -46,7 +46,7 @@ def clean_up_ipam(subscription: LanSwitchInterconnect) -> None:
         f"{subscription.lan_switch_interconnect.router_side.node.router_fqdn}"
     )
     delete_network(
-        generate_lan_switch_interconnect_subnet(
+        generate_lan_switch_interconnect_subnet_v4(
             subscription.lan_switch_interconnect.router_side.node.router_site.site_internal_id
         )
     )
diff --git a/gso/workflows/lan_switch_interconnect/validate_lan_switch_interconnect.py b/gso/workflows/lan_switch_interconnect/validate_lan_switch_interconnect.py
index c54d53c2..b97a601a 100644
--- a/gso/workflows/lan_switch_interconnect/validate_lan_switch_interconnect.py
+++ b/gso/workflows/lan_switch_interconnect/validate_lan_switch_interconnect.py
@@ -11,7 +11,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form
 from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
 from gso.services.infoblox import find_host_by_fqdn, find_network_by_cidr
 from gso.services.lso_client import LSOState, anonymous_lso_interaction
-from gso.utils.helpers import generate_lan_switch_interconnect_subnet
+from gso.utils.helpers import generate_lan_switch_interconnect_subnet_v4
 
 
 @step("Validate IPAM configuration")
@@ -29,7 +29,7 @@ def validate_ipam(subscription: LanSwitchInterconnect) -> None:
             msg = "DNS record is incorrectly configured in IPAM, please investigate this manually!"
             raise ProcessFailureError(msg, details=host_record)
 
-    lan_interconnect_network = generate_lan_switch_interconnect_subnet(
+    lan_interconnect_network = generate_lan_switch_interconnect_subnet_v4(
         subscription.lan_switch_interconnect.router_side.node.router_site.site_internal_id
     )
     network_record = find_network_by_cidr(lan_interconnect_network)
diff --git a/test/cli/test_imports.py b/test/cli/test_imports.py
index 3a915979..a1b0c757 100644
--- a/test/cli/test_imports.py
+++ b/test/cli/test_imports.py
@@ -214,7 +214,7 @@ def switch_data(temp_file, faker, site_subscription_factory):
             "ts_port": faker.port_number(is_user=True),
             "site": site_subscription_factory(),
             "switch_vendor": Vendor.JUNIPER,
-            "switch_model": SwitchModel.EX3400,
+            "switch_model": SwitchModel.EX3400_48T,
         }
         switch_data.update(**kwargs)
 
diff --git a/test/fixtures/site_fixtures.py b/test/fixtures/site_fixtures.py
index d14025e9..c6a884b1 100644
--- a/test/fixtures/site_fixtures.py
+++ b/test/fixtures/site_fixtures.py
@@ -31,6 +31,7 @@ def site_subscription_factory(faker, geant_partner):
         start_date="2023-05-24T00:00:00+00:00",
         *,
         is_imported: bool | None = True,
+        site_contains_optical_equipment: bool | None = True,
     ) -> UUIDstr:
         if partner is None:
             partner = geant_partner
@@ -54,6 +55,7 @@ def site_subscription_factory(faker, geant_partner):
         site_subscription.site.site_internal_id = site_internal_id or faker.pyint(max_value=254)
         site_subscription.site.site_tier = site_tier or SiteTier.TIER1
         site_subscription.site.site_ts_address = site_ts_address or faker.ipv4()
+        site_subscription.site.site_contains_optical_equipment = site_contains_optical_equipment
 
         site_subscription = SubscriptionModel.from_other_lifecycle(site_subscription, SubscriptionLifecycle.ACTIVE)
         site_subscription.description = description or "Site Subscription"
diff --git a/test/fixtures/switch_fixtures.py b/test/fixtures/switch_fixtures.py
index fa7f34d6..c0167a1f 100644
--- a/test/fixtures/switch_fixtures.py
+++ b/test/fixtures/switch_fixtures.py
@@ -41,7 +41,7 @@ def switch_subscription_factory(faker, geant_partner, site_subscription_factory)
         switch_subscription.switch.ts_port = ts_port or faker.port_number(is_user=True)
         switch_subscription.switch.site = site or Site.from_subscription(site_subscription_factory()).site
         switch_subscription.switch.switch_vendor = switch_vendor or Vendor.JUNIPER
-        switch_subscription.switch.switch_model = switch_model or SwitchModel.EX3400
+        switch_subscription.switch.switch_model = switch_model or SwitchModel.EX3400_24T
 
         switch_subscription = SubscriptionModel.from_other_lifecycle(switch_subscription, SubscriptionLifecycle.ACTIVE)
         switch_subscription.insync = True
diff --git a/test/utils/test_helpers.py b/test/utils/test_helpers.py
index e9113378..6b62f2bb 100644
--- a/test/utils/test_helpers.py
+++ b/test/utils/test_helpers.py
@@ -6,9 +6,12 @@ from orchestrator.types import SubscriptionLifecycle
 from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
 from gso.products.product_blocks.router import RouterRole
 from gso.products.product_types.router import Router
+from gso.products.product_types.site import Site
 from gso.utils.helpers import (
     available_interfaces_choices_including_current_members,
     generate_inventory_for_routers,
+    generate_lan_switch_interconnect_subnet_v4,
+    generate_lan_switch_interconnect_subnet_v6,
 )
 from gso.utils.shared_enums import Vendor
 from gso.utils.types.tt_number import validate_tt_number
@@ -139,3 +142,25 @@ def test_generate_inventory_for_active_routers_with_excluded_router(router_subsc
     excluded_routers = [Router.from_subscription(router).router.router_fqdn]
     inventory = generate_inventory_for_routers(RouterRole.P, exclude_routers=excluded_routers)
     assert len(inventory["all"]["hosts"]) == 5  # 6 P routers, the last one is excluded, so 5 P routers are left.
+
+
+@pytest.mark.parametrize("execution_count", range(10))
+def test_generate_lan_switch_interconnect_subnet_v4(execution_count, site_subscription_factory):
+    """Test generating a new subnet for a LAN Switch Interconnect.
+
+    We need to ensure that the third octet of the new subnet is set correctly from the Site internal ID.
+    """
+    site = Site.from_subscription(site_subscription_factory())
+    assert (
+        str(generate_lan_switch_interconnect_subnet_v4(site.site.site_internal_id))
+        == f"10.2.{site.site.site_internal_id}.0/24"
+    )
+
+
+@pytest.mark.parametrize("execution_count", range(10))
+def test_generate_lan_switch_interconnect_subnet_v6(execution_count, site_subscription_factory):
+    site = Site.from_subscription(site_subscription_factory())
+    assert (
+        str(generate_lan_switch_interconnect_subnet_v6(site.site.site_internal_id))
+        == f"beef:cafe:0:{hex(site.site.site_internal_id).split("x")[-1]}::/64"
+    )
diff --git a/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py b/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py
index 9d69017f..a3511f21 100644
--- a/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py
+++ b/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py
@@ -52,11 +52,13 @@ def input_form_data(faker, router_subscription_factory, switch_subscription_fact
 
 
 @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")
 def test_create_lan_switch_interconnect_success(
     mock_create_host,
     mock_create_v4_network,
+    mock_create_v6_network,
     input_form_data,
     _netbox_client_mock,  # noqa: PT019
 ):
@@ -68,4 +70,5 @@ def test_create_lan_switch_interconnect_success(
     subscription = LanSwitchInterconnect.from_subscription(subscription_id)
     assert subscription.status == SubscriptionLifecycle.ACTIVE
     assert mock_create_v4_network.call_count == 1
-    assert mock_create_host.call_count == 2
+    assert mock_create_v6_network.call_count == 1
+    assert mock_create_host.call_count == 4
diff --git a/test/workflows/site/test_create_imported_site.py b/test/workflows/site/test_create_imported_site.py
index 4e8b8e35..e63f44c6 100644
--- a/test/workflows/site/test_create_imported_site.py
+++ b/test/workflows/site/test_create_imported_site.py
@@ -19,6 +19,7 @@ def workflow_input_data(faker):
         "site_internal_id": faker.pyint(),
         "site_tier": SiteTier.TIER1,
         "site_ts_address": faker.ipv4(),
+        "site_contains_optical_equipment": True,
         "partner": "GEANT",
     }
 
diff --git a/test/workflows/switch/test_create_imported_switch.py b/test/workflows/switch/test_create_imported_switch.py
index 76ad61c7..b38a0c94 100644
--- a/test/workflows/switch/test_create_imported_switch.py
+++ b/test/workflows/switch/test_create_imported_switch.py
@@ -19,7 +19,7 @@ def workflow_input_data(faker, site_subscription_factory):
         "ts_port": faker.port_number(is_user=True),
         "site": site_subscription_factory(),
         "switch_vendor": Vendor.JUNIPER,
-        "switch_model": SwitchModel.EX3400,
+        "switch_model": SwitchModel.EX3400_24T,
     }
 
 
diff --git a/test/workflows/switch/test_create_switch.py b/test/workflows/switch/test_create_switch.py
index c11edf80..64006141 100644
--- a/test/workflows/switch/test_create_switch.py
+++ b/test/workflows/switch/test_create_switch.py
@@ -3,6 +3,7 @@ from unittest.mock import patch
 import pytest
 
 from gso.products import ProductName
+from gso.products.product_blocks.switch import SwitchModel
 from gso.products.product_types.switch import Switch
 from gso.services.subscriptions import get_product_id_by_name
 from test import USER_CONFIRM_EMPTY_FORM
@@ -32,6 +33,7 @@ def test_create_switch_success(
             "switch_site": site_subscription_factory(),
             "hostname": faker.domain_word(),
             "ts_port": faker.port_number(is_user=True),
+            "model": SwitchModel.EX3400_24T,
         },
         {},
     ]
-- 
GitLab