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

Reserve IPv6 resources when creating a LAN Switch Interconnect

parent 449ef173
Branches
Tags
No related merge requests found
Showing
with 153 additions and 36 deletions
...@@ -10,7 +10,14 @@ from infoblox_client.exceptions import ( ...@@ -10,7 +10,14 @@ from infoblox_client.exceptions import (
) )
from gso.settings import IPAMParams, load_oss_params 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__) logger = getLogger(__name__)
NULL_MAC = "00:00:00:00:00:00" NULL_MAC = "00:00:00:00:00:00"
...@@ -76,7 +83,11 @@ def _allocate_network( # noqa: PLR0917 ...@@ -76,7 +83,11 @@ def _allocate_network( # noqa: PLR0917
def create_v4_network_by_ip( def create_v4_network_by_ip(
dns_view: str, network_view: str, network: IPv4NetworkType, comment: str | None = "" dns_view: str, network_view: str, network: IPv4NetworkType, comment: str | None = ""
) -> 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() conn, _ = _setup_connection()
created_net = objects.NetworkV4.create( created_net = objects.NetworkV4.create(
conn, network=network, view=dns_view, network_view=network_view, comment=comment conn, network=network, view=dns_view, network_view=network_view, comment=comment
...@@ -88,6 +99,25 @@ def create_v4_network_by_ip( ...@@ -88,6 +99,25 @@ def create_v4_network_by_ip(
logger.debug(msg) 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: def hostname_available(hostname: str) -> bool:
"""Check whether a hostname is still available **in Infoblox**. """Check whether a hostname is still available **in Infoblox**.
...@@ -246,6 +276,7 @@ def create_host_by_ip( ...@@ -246,6 +276,7 @@ def create_host_by_ip(
hostname: str, hostname: str,
service_type: str, service_type: str,
comment: str, comment: str,
*,
ipv4_address: IPv4AddressType | None = None, ipv4_address: IPv4AddressType | None = None,
ipv6_address: IPv6AddressType | None = None, ipv6_address: IPv6AddressType | None = None,
) -> None: ) -> None:
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import random import random
import re import re
from ipaddress import IPv4Network from ipaddress import IPv4Network, IPv6Network
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from uuid import UUID from uuid import UUID
...@@ -19,7 +19,7 @@ from gso.services.partners import get_all_partners ...@@ -19,7 +19,7 @@ from gso.services.partners import get_all_partners
from gso.services.subscriptions import is_virtual_circuit_id_available from gso.services.subscriptions import is_virtual_circuit_id_available
from gso.utils.shared_enums import Vendor from gso.utils.shared_enums import Vendor
from gso.utils.types.interfaces import PhysicalPortCapacity 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 from gso.utils.types.virtual_identifiers import VC_ID
if TYPE_CHECKING: if TYPE_CHECKING:
...@@ -127,8 +127,8 @@ def generate_fqdn(hostname: str, site_name: str, country_code: str) -> str: ...@@ -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}" 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: def generate_lan_switch_interconnect_subnet_v4(site_internal_id: int) -> IPv4NetworkType:
"""Generate an IPv4 network in which a :term:`LAN` Switch Interconnect resides, given a Site internal ID.""" """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 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. 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 ...@@ -138,6 +138,17 @@ def generate_lan_switch_interconnect_subnet(site_internal_id: int) -> IPv4Networ
return IPv4Network(".".join(result)) 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( def generate_inventory_for_routers(
router_role: RouterRole, router_role: RouterRole,
exclude_routers: list[str] | None = None, exclude_routers: list[str] | None = None,
......
...@@ -507,7 +507,9 @@ def register_dns_records(subscription: IptrunkInactive) -> State: ...@@ -507,7 +507,9 @@ def register_dns_records(subscription: IptrunkInactive) -> State:
ipv4_addr = subscription.iptrunk.iptrunk_ipv4_network[index] ipv4_addr = subscription.iptrunk.iptrunk_ipv4_network[index]
ipv6_addr = subscription.iptrunk.iptrunk_ipv6_network[index + 1] 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} return {"subscription": subscription}
......
...@@ -740,7 +740,7 @@ def update_ipam(subscription: Iptrunk, replace_index: int, new_node: Router, new ...@@ -740,7 +740,7 @@ def update_ipam(subscription: Iptrunk, replace_index: int, new_node: Router, new
# And in with the new # And in with the new
new_fqdn = f"{new_lag_interface}-0.{new_node.router.router_fqdn}" new_fqdn = f"{new_lag_interface}-0.{new_node.router.router_fqdn}"
comment = str(subscription.subscription_id) 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} return {"subscription": subscription}
......
"""A creation workflow for creating a new interconnect between a switch and a router.""" """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 typing import Annotated
from uuid import uuid4 from uuid import uuid4
...@@ -21,7 +21,7 @@ from gso.products.product_blocks.lan_switch_interconnect import ( ...@@ -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.lan_switch_interconnect import LanSwitchInterconnectInactive
from gso.products.product_types.router import Router from gso.products.product_types.router import Router
from gso.products.product_types.switch import Switch 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.services.partners import get_partner_by_name
from gso.settings import load_oss_params from gso.settings import load_oss_params
from gso.utils.helpers import ( from gso.utils.helpers import (
...@@ -29,7 +29,8 @@ from gso.utils.helpers import ( ...@@ -29,7 +29,8 @@ from gso.utils.helpers import (
active_switch_selector, active_switch_selector,
available_interfaces_choices, available_interfaces_choices,
available_lags_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.shared_enums import Vendor
from gso.utils.types.interfaces import ( from gso.utils.types.interfaces import (
...@@ -162,28 +163,28 @@ def initialize_subscription( ...@@ -162,28 +163,28 @@ def initialize_subscription(
return {"subscription": subscription} return {"subscription": subscription}
@step("Register network in IPAM") @step("Register IPv4 network in IPAM")
def register_dns_records_network(subscription: LanSwitchInterconnectInactive) -> State: def register_dns_records_v4_network(subscription: LanSwitchInterconnectInactive) -> State:
"""Add :term:`DNS` records in :term:`IPAM`.""" """Add DNS records in IPAM."""
router_site = subscription.lan_switch_interconnect.router_side.node.router_site router_site = subscription.lan_switch_interconnect.router_side.node.router_site
if not router_site or not router_site.site_internal_id: if not router_site or not router_site.site_internal_id:
msg = "Site internal ID not set. Cannot continue." msg = "Site internal ID not set. Cannot continue."
raise ProcessFailureError(msg, details=router_site) 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 ipam_oss_params = load_oss_params().IPAM.LAN_SWITCH_INTERCONNECT
create_v4_network_by_ip( create_v4_network_by_ip(
ipam_oss_params.dns_view, ipam_oss_params.network_view, new_network, str(subscription.subscription_id) 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") @step("Register IPv4 devices in IPAM")
def register_dns_records_devices( def register_dns_records_v4_devices(
subscription: LanSwitchInterconnectInactive, subscription_id: UUIDstr, ipam_registrations: dict[str, str] subscription: LanSwitchInterconnectInactive, subscription_id: UUIDstr, ipam_registrations: dict[str, dict[str, str]]
) -> State: ) -> 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 switch_hostname = subscription.lan_switch_interconnect.switch_side.switch.fqdn
router_hostname = ( router_hostname = (
f"{subscription.lan_switch_interconnect.router_side.ae_iface}." f"{subscription.lan_switch_interconnect.router_side.ae_iface}."
...@@ -193,14 +194,51 @@ def register_dns_records_devices( ...@@ -193,14 +194,51 @@ def register_dns_records_devices(
msg = "Missing switch or router hostname, cannot continue." msg = "Missing switch or router hostname, cannot continue."
raise ProcessFailureError(msg, details=subscription.lan_switch_interconnect) raise ProcessFailureError(msg, details=subscription.lan_switch_interconnect)
ip_network_prefix = ipam_registrations["network"].split(".")[:3] # Take the first three octets of the network. ip_network = IPv4Network(ipam_registrations["v4"]["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.
create_host_by_ip(switch_hostname, "LAN_SWITCH_INTERCONNECT", subscription_id, switch_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, router_side_ip) 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( @workflow(
...@@ -215,8 +253,10 @@ def create_lan_switch_interconnect() -> StepList: ...@@ -215,8 +253,10 @@ def create_lan_switch_interconnect() -> StepList:
>> create_subscription >> create_subscription
>> store_process_subscription(Target.CREATE) >> store_process_subscription(Target.CREATE)
>> initialize_subscription >> initialize_subscription
>> register_dns_records_network >> register_dns_records_v4_network
>> register_dns_records_devices >> register_dns_records_v4_devices
>> register_dns_records_v6_network
>> register_dns_records_v6_devices
>> set_status(SubscriptionLifecycle.ACTIVE) >> set_status(SubscriptionLifecycle.ACTIVE)
>> resync >> resync
>> done >> done
......
...@@ -12,7 +12,7 @@ from pydantic_forms.validators import Label ...@@ -12,7 +12,7 @@ from pydantic_forms.validators import Label
from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
from gso.services.infoblox import delete_host_by_fqdn, delete_network 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 from gso.utils.types.tt_number import TTNumber
...@@ -46,7 +46,7 @@ def clean_up_ipam(subscription: LanSwitchInterconnect) -> None: ...@@ -46,7 +46,7 @@ def clean_up_ipam(subscription: LanSwitchInterconnect) -> None:
f"{subscription.lan_switch_interconnect.router_side.node.router_fqdn}" f"{subscription.lan_switch_interconnect.router_side.node.router_fqdn}"
) )
delete_network( 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 subscription.lan_switch_interconnect.router_side.node.router_site.site_internal_id
) )
) )
......
...@@ -11,7 +11,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form ...@@ -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.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
from gso.services.infoblox import find_host_by_fqdn, find_network_by_cidr 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.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") @step("Validate IPAM configuration")
...@@ -29,7 +29,7 @@ def validate_ipam(subscription: LanSwitchInterconnect) -> None: ...@@ -29,7 +29,7 @@ def validate_ipam(subscription: LanSwitchInterconnect) -> None:
msg = "DNS record is incorrectly configured in IPAM, please investigate this manually!" msg = "DNS record is incorrectly configured in IPAM, please investigate this manually!"
raise ProcessFailureError(msg, details=host_record) 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 subscription.lan_switch_interconnect.router_side.node.router_site.site_internal_id
) )
network_record = find_network_by_cidr(lan_interconnect_network) network_record = find_network_by_cidr(lan_interconnect_network)
......
...@@ -214,7 +214,7 @@ def switch_data(temp_file, faker, site_subscription_factory): ...@@ -214,7 +214,7 @@ def switch_data(temp_file, faker, site_subscription_factory):
"ts_port": faker.port_number(is_user=True), "ts_port": faker.port_number(is_user=True),
"site": site_subscription_factory(), "site": site_subscription_factory(),
"switch_vendor": Vendor.JUNIPER, "switch_vendor": Vendor.JUNIPER,
"switch_model": SwitchModel.EX3400, "switch_model": SwitchModel.EX3400_48T,
} }
switch_data.update(**kwargs) switch_data.update(**kwargs)
......
...@@ -31,6 +31,7 @@ def site_subscription_factory(faker, geant_partner): ...@@ -31,6 +31,7 @@ def site_subscription_factory(faker, geant_partner):
start_date="2023-05-24T00:00:00+00:00", start_date="2023-05-24T00:00:00+00:00",
*, *,
is_imported: bool | None = True, is_imported: bool | None = True,
site_contains_optical_equipment: bool | None = True,
) -> UUIDstr: ) -> UUIDstr:
if partner is None: if partner is None:
partner = geant_partner partner = geant_partner
...@@ -54,6 +55,7 @@ def site_subscription_factory(faker, 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_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_tier = site_tier or SiteTier.TIER1
site_subscription.site.site_ts_address = site_ts_address or faker.ipv4() 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 = SubscriptionModel.from_other_lifecycle(site_subscription, SubscriptionLifecycle.ACTIVE)
site_subscription.description = description or "Site Subscription" site_subscription.description = description or "Site Subscription"
......
...@@ -41,7 +41,7 @@ def switch_subscription_factory(faker, geant_partner, site_subscription_factory) ...@@ -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.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.site = site or Site.from_subscription(site_subscription_factory()).site
switch_subscription.switch.switch_vendor = switch_vendor or Vendor.JUNIPER 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 = SubscriptionModel.from_other_lifecycle(switch_subscription, SubscriptionLifecycle.ACTIVE)
switch_subscription.insync = True switch_subscription.insync = True
......
...@@ -6,9 +6,12 @@ from orchestrator.types import SubscriptionLifecycle ...@@ -6,9 +6,12 @@ from orchestrator.types import SubscriptionLifecycle
from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock from gso.products.product_blocks.iptrunk import IptrunkInterfaceBlock
from gso.products.product_blocks.router import RouterRole from gso.products.product_blocks.router import RouterRole
from gso.products.product_types.router import Router from gso.products.product_types.router import Router
from gso.products.product_types.site import Site
from gso.utils.helpers import ( from gso.utils.helpers import (
available_interfaces_choices_including_current_members, available_interfaces_choices_including_current_members,
generate_inventory_for_routers, 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.shared_enums import Vendor
from gso.utils.types.tt_number import validate_tt_number 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 ...@@ -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] excluded_routers = [Router.from_subscription(router).router.router_fqdn]
inventory = generate_inventory_for_routers(RouterRole.P, exclude_routers=excluded_routers) 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. 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"
)
...@@ -52,11 +52,13 @@ def input_form_data(faker, router_subscription_factory, switch_subscription_fact ...@@ -52,11 +52,13 @@ def input_form_data(faker, router_subscription_factory, switch_subscription_fact
@pytest.mark.workflow() @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_v4_network_by_ip")
@patch("gso.services.infoblox.create_host_by_ip") @patch("gso.services.infoblox.create_host_by_ip")
def test_create_lan_switch_interconnect_success( def test_create_lan_switch_interconnect_success(
mock_create_host, mock_create_host,
mock_create_v4_network, mock_create_v4_network,
mock_create_v6_network,
input_form_data, input_form_data,
_netbox_client_mock, # noqa: PT019 _netbox_client_mock, # noqa: PT019
): ):
...@@ -68,4 +70,5 @@ def test_create_lan_switch_interconnect_success( ...@@ -68,4 +70,5 @@ def test_create_lan_switch_interconnect_success(
subscription = LanSwitchInterconnect.from_subscription(subscription_id) subscription = LanSwitchInterconnect.from_subscription(subscription_id)
assert subscription.status == SubscriptionLifecycle.ACTIVE assert subscription.status == SubscriptionLifecycle.ACTIVE
assert mock_create_v4_network.call_count == 1 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
...@@ -19,6 +19,7 @@ def workflow_input_data(faker): ...@@ -19,6 +19,7 @@ def workflow_input_data(faker):
"site_internal_id": faker.pyint(), "site_internal_id": faker.pyint(),
"site_tier": SiteTier.TIER1, "site_tier": SiteTier.TIER1,
"site_ts_address": faker.ipv4(), "site_ts_address": faker.ipv4(),
"site_contains_optical_equipment": True,
"partner": "GEANT", "partner": "GEANT",
} }
......
...@@ -19,7 +19,7 @@ def workflow_input_data(faker, site_subscription_factory): ...@@ -19,7 +19,7 @@ def workflow_input_data(faker, site_subscription_factory):
"ts_port": faker.port_number(is_user=True), "ts_port": faker.port_number(is_user=True),
"site": site_subscription_factory(), "site": site_subscription_factory(),
"switch_vendor": Vendor.JUNIPER, "switch_vendor": Vendor.JUNIPER,
"switch_model": SwitchModel.EX3400, "switch_model": SwitchModel.EX3400_24T,
} }
......
...@@ -3,6 +3,7 @@ from unittest.mock import patch ...@@ -3,6 +3,7 @@ from unittest.mock import patch
import pytest import pytest
from gso.products import ProductName from gso.products import ProductName
from gso.products.product_blocks.switch import SwitchModel
from gso.products.product_types.switch import Switch from gso.products.product_types.switch import Switch
from gso.services.subscriptions import get_product_id_by_name from gso.services.subscriptions import get_product_id_by_name
from test import USER_CONFIRM_EMPTY_FORM from test import USER_CONFIRM_EMPTY_FORM
...@@ -32,6 +33,7 @@ def test_create_switch_success( ...@@ -32,6 +33,7 @@ def test_create_switch_success(
"switch_site": site_subscription_factory(), "switch_site": site_subscription_factory(),
"hostname": faker.domain_word(), "hostname": faker.domain_word(),
"ts_port": faker.port_number(is_user=True), "ts_port": faker.port_number(is_user=True),
"model": SwitchModel.EX3400_24T,
}, },
{}, {},
] ]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment