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

Update edge port models, add translations, add domain model migration for GÉANT IP

parent 8eb94c52
Branches
Tags
No related merge requests found
"""The main entrypoint for :term:`GSO`, and the different ways in which it can be run.""" """The main entrypoint for :term:`GSO`, and the different ways in which it can be run."""
import os import os
from typing import Annotated
import sentry_sdk import sentry_sdk
import strawberry
import typer import typer
from celery import Celery from celery import Celery
from orchestrator import OrchestratorCore, app_settings from orchestrator import OrchestratorCore, app_settings
from orchestrator.cli.main import app as cli_app from orchestrator.cli.main import app as cli_app
from orchestrator.graphql import DEFAULT_GRAPHQL_MODELS, SCALAR_OVERRIDES from orchestrator.graphql import SCALAR_OVERRIDES
from orchestrator.services.tasks import initialise_celery from orchestrator.services.tasks import initialise_celery
from orchestrator.settings import ExecutorType from orchestrator.settings import ExecutorType
...@@ -21,7 +19,6 @@ from gso.auth.oidc import oidc_instance ...@@ -21,7 +19,6 @@ from gso.auth.oidc import oidc_instance
from gso.auth.opa import graphql_opa_instance, opa_instance from gso.auth.opa import graphql_opa_instance, opa_instance
from gso.graphql_api.types import GSO_SCALAR_OVERRIDES from gso.graphql_api.types import GSO_SCALAR_OVERRIDES
from gso.settings import load_oss_params from gso.settings import load_oss_params
from gso.utils.types.interfaces import LAGMember
SCALAR_OVERRIDES.update(GSO_SCALAR_OVERRIDES) SCALAR_OVERRIDES.update(GSO_SCALAR_OVERRIDES)
...@@ -38,18 +35,7 @@ def init_gso_app() -> OrchestratorCore: ...@@ -38,18 +35,7 @@ def init_gso_app() -> OrchestratorCore:
app.register_authentication(oidc_instance) app.register_authentication(oidc_instance)
app.register_authorization(opa_instance) app.register_authorization(opa_instance)
app.register_graphql_authorization(graphql_opa_instance) app.register_graphql_authorization(graphql_opa_instance)
app.register_graphql()
@strawberry.experimental.pydantic.type(model=LAGMember)
class LAGMemberGraphql:
self_reference_block: Annotated["LAGMemberGraphql", strawberry.lazy("gso.utils.types.interfaces")] | None = None
interface_name: str
interface_description: str
updated_graphql_models = DEFAULT_GRAPHQL_MODELS | {
"LAGMemberGraphql": LAGMemberGraphql,
}
app.register_graphql(graphql_models=updated_graphql_models)
app.include_router(api_router, prefix="/api") app.include_router(api_router, prefix="/api")
if app_settings.EXECUTOR == ExecutorType.WORKER: if app_settings.EXECUTOR == ExecutorType.WORKER:
......
""":term:`BGP` session product block.""" """:term:`BGP` session product block."""
from ipaddress import IPv4Address, IPv6Address import strawberry
from orchestrator.domain.base import ProductBlockModel from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle from orchestrator.types import SubscriptionLifecycle
from pydantic import Field from pydantic import Field
from pydantic_forms.types import strEnum from pydantic_forms.types import strEnum
from gso.utils.types.ip_address import IPAddress
@strawberry.enum
class IPFamily(strEnum): class IPFamily(strEnum):
"""Possible :term:`IP` families of a :term:`BGP` peering.""" """Possible :term:`IP` families of a :term:`BGP` peering."""
...@@ -20,7 +22,7 @@ class IPFamily(strEnum): ...@@ -20,7 +22,7 @@ class IPFamily(strEnum):
class BGPSessionInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="BGPSession"): class BGPSessionInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="BGPSession"):
"""A :term:`BGP` session that is currently inactive. See :class:`BGPSession`.""" """A :term:`BGP` session that is currently inactive. See :class:`BGPSession`."""
peer_address: IPv4Address | IPv6Address | None = None peer_address: IPAddress | None = None
bfd_enabled: bool | None = None bfd_enabled: bool | None = None
bfd_interval: int | None = None bfd_interval: int | None = None
bfd_multiplier: int | None = None bfd_multiplier: int | None = None
...@@ -35,7 +37,7 @@ class BGPSessionInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INI ...@@ -35,7 +37,7 @@ class BGPSessionInactive(ProductBlockModel, lifecycle=[SubscriptionLifecycle.INI
class BGPSessionProvisioning(BGPSessionInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): class BGPSessionProvisioning(BGPSessionInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
"""A :term:`BGP` session that is currently being provisioned. See :class:`BGPSession`.""" """A :term:`BGP` session that is currently being provisioned. See :class:`BGPSession`."""
peer_address: IPv4Address | IPv6Address peer_address: IPAddress
bfd_enabled: bool bfd_enabled: bool
bfd_interval: int | None = None bfd_interval: int | None = None
bfd_multiplier: int | None = None bfd_multiplier: int | None = None
...@@ -51,7 +53,7 @@ class BGPSession(BGPSessionProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE ...@@ -51,7 +53,7 @@ class BGPSession(BGPSessionProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE
"""A :term:`BGP` session that is currently deployed in the network.""" """A :term:`BGP` session that is currently deployed in the network."""
#: The peering address of the session. #: The peering address of the session.
peer_address: IPv4Address | IPv6Address peer_address: IPAddress
#: Whether :term:`BFD` is enabled. #: Whether :term:`BFD` is enabled.
bfd_enabled: bool bfd_enabled: bool
#: The :term:`BFD` interval, if enabled. #: The :term:`BFD` interval, if enabled.
......
...@@ -6,9 +6,15 @@ domain still managed by GEANT. In other words, an Edge port determines where the ...@@ -6,9 +6,15 @@ domain still managed by GEANT. In other words, an Edge port determines where the
from orchestrator.domain.base import ProductBlockModel from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle, strEnum from orchestrator.types import SubscriptionLifecycle, strEnum
from pydantic import Field
from gso.products.product_blocks.router import RouterBlock, RouterBlockInactive, RouterBlockProvisioning from gso.products.product_blocks.router import RouterBlock, RouterBlockInactive, RouterBlockProvisioning
from gso.utils.types.interfaces import LAGMember, LAGMemberList, PhysicalPortCapacity from gso.products.product_blocks.service_binding_port import (
ServiceBindingPort,
ServiceBindingPortInactive,
ServiceBindingPortProvisioning,
)
from gso.utils.types.interfaces import LAGMemberList, PhysicalPortCapacity
class EncapsulationType(strEnum): class EncapsulationType(strEnum):
...@@ -34,6 +40,29 @@ class EdgePortType(strEnum): ...@@ -34,6 +40,29 @@ class EdgePortType(strEnum):
RE_INTERCONNECT = "RE_INTERCONNECT" RE_INTERCONNECT = "RE_INTERCONNECT"
class EdgePortAEMemberBlockInactive(
ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="EdgePortAEMemberBlock"
):
"""An inactive Edge Port AE interface."""
interface_name: str | None = None
interface_description: str | None = None
class EdgePortAEMemberBlockProvisioning(EdgePortAEMemberBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
"""A provisional Edge Port AE interface."""
interface_name: str
interface_description: str | None = None
class EdgePortAEMemberBlock(EdgePortAEMemberBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
"""An Edge Port AE interface."""
interface_name: str
interface_description: str | None = None
class EdgePortBlockInactive( class EdgePortBlockInactive(
ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="EdgePortBlock" ProductBlockModel, lifecycle=[SubscriptionLifecycle.INITIAL], product_block_name="EdgePortBlock"
): ):
...@@ -50,7 +79,8 @@ class EdgePortBlockInactive( ...@@ -50,7 +79,8 @@ class EdgePortBlockInactive(
edge_port_type: EdgePortType | None = None edge_port_type: EdgePortType | None = None
edge_port_ignore_if_down: bool = False edge_port_ignore_if_down: bool = False
edge_port_geant_ga_id: str | None = None edge_port_geant_ga_id: str | None = None
edge_port_ae_members: LAGMemberList[LAGMember] edge_port_ae_members: LAGMemberList[EdgePortAEMemberBlockInactive]
edge_port_sbp_list: list[ServiceBindingPortInactive] = Field(default_factory=list)
class EdgePortBlockProvisioning(EdgePortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): class EdgePortBlockProvisioning(EdgePortBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
...@@ -67,7 +97,8 @@ class EdgePortBlockProvisioning(EdgePortBlockInactive, lifecycle=[SubscriptionLi ...@@ -67,7 +97,8 @@ class EdgePortBlockProvisioning(EdgePortBlockInactive, lifecycle=[SubscriptionLi
edge_port_type: EdgePortType edge_port_type: EdgePortType
edge_port_ignore_if_down: bool = False edge_port_ignore_if_down: bool = False
edge_port_geant_ga_id: str | None = None edge_port_geant_ga_id: str | None = None
edge_port_ae_members: LAGMemberList[LAGMember] edge_port_ae_members: LAGMemberList[EdgePortAEMemberBlockProvisioning] # type: ignore[assignment]
edge_port_sbp_list: list[ServiceBindingPortProvisioning] # type: ignore[assignment]
class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
...@@ -96,4 +127,6 @@ class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle. ...@@ -96,4 +127,6 @@ class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle.
#: The GEANT GA ID associated with this edge port, if any. #: The GEANT GA ID associated with this edge port, if any.
edge_port_geant_ga_id: str | None = None edge_port_geant_ga_id: str | None = None
#: A list of LAG members associated with this edge port. #: A list of LAG members associated with this edge port.
edge_port_ae_members: LAGMemberList[LAGMember] edge_port_ae_members: LAGMemberList[EdgePortAEMemberBlock] # type: ignore[assignment]
#: A list of Service Binding Ports associated with this Edge Port
edge_port_sbp_list: list[ServiceBindingPort] # type: ignore[assignment]
...@@ -5,11 +5,6 @@ from orchestrator.types import SubscriptionLifecycle ...@@ -5,11 +5,6 @@ from orchestrator.types import SubscriptionLifecycle
from pydantic import Field from pydantic import Field
from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning from gso.products.product_blocks.edge_port import EdgePortBlock, EdgePortBlockInactive, EdgePortBlockProvisioning
from gso.products.product_blocks.service_binding_port import (
ServiceBindingPort,
ServiceBindingPortInactive,
ServiceBindingPortProvisioning,
)
from gso.utils.shared_enums import APType from gso.utils.shared_enums import APType
...@@ -18,22 +13,24 @@ class NRENAccessPortInactive( ...@@ -18,22 +13,24 @@ class NRENAccessPortInactive(
): ):
"""An access port for an R&E :term:`NREN` service that is inactive.""" """An access port for an R&E :term:`NREN` service that is inactive."""
nren_ap_sbp_list: list[ServiceBindingPortInactive] = Field(default_factory=list)
nren_ap_type: APType | None = None nren_ap_type: APType | None = None
geant_ip_ep_list: list[EdgePortBlockInactive] = Field(default_factory=list)
class NRENAccessPortProvisioning(NRENAccessPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): class NRENAccessPortProvisioning(NRENAccessPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
"""An access port for an R&E :term:`NREN` service that is being provisioned.""" """An access port for an R&E :term:`NREN` service that is being provisioned."""
nren_ap_sbp_list: list[ServiceBindingPortProvisioning] # type: ignore[assignment]
nren_ap_type: APType nren_ap_type: APType
geant_ip_ep_list: list[EdgePortBlockProvisioning] # type: ignore[assignment]
class NRENAccessPort(NRENAccessPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): class NRENAccessPort(NRENAccessPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
"""An access port for an R&E :term:`NREN` service.""" """An access port for an R&E :term:`NREN` service."""
nren_ap_sbp_list: list[ServiceBindingPort] # type: ignore[assignment] #: The type of Access Port
nren_ap_type: APType nren_ap_type: APType
#: The list of Edge Ports where this service terminates.
geant_ip_ep_list: list[EdgePortBlock] # type: ignore[assignment]
class GeantIPBlockInactive( class GeantIPBlockInactive(
...@@ -42,14 +39,12 @@ class GeantIPBlockInactive( ...@@ -42,14 +39,12 @@ class GeantIPBlockInactive(
"""A GÉANT IP subscription that is currently inactive. See :class:`GeantIPBlock`.""" """A GÉANT IP subscription that is currently inactive. See :class:`GeantIPBlock`."""
geant_ip_ap_list: list[NRENAccessPortInactive] = Field(default_factory=list) geant_ip_ap_list: list[NRENAccessPortInactive] = Field(default_factory=list)
geant_ip_ep_list: list[EdgePortBlockInactive] = Field(default_factory=list)
class GeantIPBlockProvisioning(GeantIPBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): class GeantIPBlockProvisioning(GeantIPBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
"""A GÉANT IP subscription that is currently being provisioned. See :class:`GeantIPBlock`.""" """A GÉANT IP subscription that is currently being provisioned. See :class:`GeantIPBlock`."""
geant_ip_ap_list: list[NRENAccessPortProvisioning] # type: ignore[assignment] geant_ip_ap_list: list[NRENAccessPortProvisioning] # type: ignore[assignment]
geant_ip_ep_list: list[EdgePortBlockProvisioning] # type: ignore[assignment]
class GeantIPBlock(GeantIPBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): class GeantIPBlock(GeantIPBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
...@@ -57,5 +52,3 @@ class GeantIPBlock(GeantIPBlockProvisioning, lifecycle=[SubscriptionLifecycle.AC ...@@ -57,5 +52,3 @@ class GeantIPBlock(GeantIPBlockProvisioning, lifecycle=[SubscriptionLifecycle.AC
#: The list of Access Points where this service is present. #: The list of Access Points where this service is present.
geant_ip_ap_list: list[NRENAccessPort] # type: ignore[assignment] geant_ip_ap_list: list[NRENAccessPort] # type: ignore[assignment]
#: The list of Edge Ports where this service terminates.
geant_ip_ep_list: list[EdgePortBlock] # type: ignore[assignment]
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
A service binding port is used to logically attach an edge port to a customer service using a :term:`VLAN`. A service binding port is used to logically attach an edge port to a customer service using a :term:`VLAN`.
""" """
from ipaddress import IPv4Address, IPv6Address
from typing import Annotated from typing import Annotated
from orchestrator.domain.base import ProductBlockModel from orchestrator.domain.base import ProductBlockModel
from orchestrator.types import SubscriptionLifecycle from orchestrator.types import SubscriptionLifecycle
from pydantic import Field from pydantic import Field
from gso.products.product_blocks.bgp_session import BGPSession, BGPSessionInactive, BGPSessionProvisioning
from gso.utils.shared_enums import SBPType from gso.utils.shared_enums import SBPType
from gso.utils.types.bgp_session import BGPSessionSet from gso.utils.types.ip_address import IPv4AddressType, IPv6AddressType
VLAN_ID = Annotated[int, Field(gt=0, lt=4096)] VLAN_ID = Annotated[int, Field(gt=0, lt=4096)]
...@@ -24,11 +24,11 @@ class ServiceBindingPortInactive( ...@@ -24,11 +24,11 @@ class ServiceBindingPortInactive(
is_tagged: bool | None = None is_tagged: bool | None = None
vlan_id: VLAN_ID | None = None vlan_id: VLAN_ID | None = None
sbp_type: SBPType | None = None sbp_type: SBPType | None = None
ipv4_address: IPv4Address | None = None ipv4_address: IPv4AddressType | None = None
ipv6_address: IPv6Address | None = None ipv6_address: IPv6AddressType | None = None
custom_firewall_filters: bool | None = None custom_firewall_filters: bool | None = None
geant_sid: str | None = None geant_sid: str | None = None
sbp_bgp_session_list: BGPSessionSet | None = None sbp_bgp_session_list: list[BGPSessionInactive] = Field(default_factory=list)
class ServiceBindingPortProvisioning(ServiceBindingPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]): class ServiceBindingPortProvisioning(ServiceBindingPortInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
...@@ -37,11 +37,11 @@ class ServiceBindingPortProvisioning(ServiceBindingPortInactive, lifecycle=[Subs ...@@ -37,11 +37,11 @@ class ServiceBindingPortProvisioning(ServiceBindingPortInactive, lifecycle=[Subs
is_tagged: bool is_tagged: bool
vlan_id: VLAN_ID | None = None vlan_id: VLAN_ID | None = None
sbp_type: SBPType sbp_type: SBPType
ipv4_address: IPv4Address | None = None ipv4_address: IPv4AddressType | None = None
ipv6_address: IPv6Address | None = None ipv6_address: IPv6AddressType | None = None
custom_firewall_filters: bool custom_firewall_filters: bool
geant_sid: str geant_sid: str
sbp_bgp_session_list: BGPSessionSet sbp_bgp_session_list: list[BGPSessionProvisioning] # type: ignore[assignment]
class ServiceBindingPort(ServiceBindingPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): class ServiceBindingPort(ServiceBindingPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
...@@ -54,12 +54,12 @@ class ServiceBindingPort(ServiceBindingPortProvisioning, lifecycle=[Subscription ...@@ -54,12 +54,12 @@ class ServiceBindingPort(ServiceBindingPortProvisioning, lifecycle=[Subscription
#: Is this service binding port layer 2 or 3? #: Is this service binding port layer 2 or 3?
sbp_type: SBPType sbp_type: SBPType
#: If layer 3, IPv4 resources. #: If layer 3, IPv4 resources.
ipv4_address: IPv4Address | None = None ipv4_address: IPv4AddressType | None = None
#: If layer 3, IPv6 resources. #: If layer 3, IPv6 resources.
ipv6_address: IPv6Address | None = None ipv6_address: IPv6AddressType | None = None
#: Any custom firewall filters that the partner may require. #: Any custom firewall filters that the partner may require.
custom_firewall_filters: bool custom_firewall_filters: bool
#: The GÉANT service ID of this binding port. #: The GÉANT service ID of this binding port.
geant_sid: str geant_sid: str
#: The :term:`BGP` sessions associated with this service binding port. #: The :term:`BGP` sessions associated with this service binding port.
sbp_bgp_session_list: BGPSessionSet sbp_bgp_session_list: list[BGPSession] # type: ignore[assignment]
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
"migrate_to_different_site": "Migrating to a different Site", "migrate_to_different_site": "Migrating to a different Site",
"remove_configuration": "Remove configuration from the router", "remove_configuration": "Remove configuration from the router",
"clean_up_ipam": "Clean up related entries in IPAM", "clean_up_ipam": "Clean up related entries in IPAM",
"restore_isis_metric": "Restore ISIS metric to original value", "restore_isis_metric": "Restore ISIS metric to original value"
"confirm_info": "Please verify this form looks correct."
} }
}, },
"workflow": { "workflow": {
...@@ -44,6 +43,7 @@ ...@@ -44,6 +43,7 @@
"create_router": "Create Router", "create_router": "Create Router",
"create_site": "Create Site", "create_site": "Create Site",
"create_switch": "Create Switch", "create_switch": "Create Switch",
"create_edge_port": "Create Edge Port",
"deploy_twamp": "Deploy TWAMP", "deploy_twamp": "Deploy TWAMP",
"migrate_iptrunk": "Migrate IP Trunk", "migrate_iptrunk": "Migrate IP Trunk",
"modify_isis_metric": "Modify the ISIS metric", "modify_isis_metric": "Modify the ISIS metric",
...@@ -51,10 +51,12 @@ ...@@ -51,10 +51,12 @@
"modify_trunk_interface": "Modify IP Trunk interface", "modify_trunk_interface": "Modify IP Trunk interface",
"modify_connection_strategy": "Modify connection strategy", "modify_connection_strategy": "Modify connection strategy",
"modify_router_kentik_license": "Modify device license in Kentik", "modify_router_kentik_license": "Modify device license in Kentik",
"modify_edge_port": "Modify Edge Port",
"terminate_iptrunk": "Terminate IP Trunk", "terminate_iptrunk": "Terminate IP Trunk",
"terminate_router": "Terminate Router", "terminate_router": "Terminate Router",
"terminate_site": "Terminate Site", "terminate_site": "Terminate Site",
"terminate_switch": "Terminate Switch", "terminate_switch": "Terminate Switch",
"terminate_edge_port": "Terminate Edge Port",
"redeploy_base_config": "Redeploy base config", "redeploy_base_config": "Redeploy base config",
"update_ibgp_mesh": "Update iBGP mesh", "update_ibgp_mesh": "Update iBGP mesh",
"create_imported_site": "NOT FOR HUMANS -- Import existing site", "create_imported_site": "NOT FOR HUMANS -- Import existing site",
...@@ -72,6 +74,7 @@ ...@@ -72,6 +74,7 @@
"validate_iptrunk": "Validate IP Trunk configuration", "validate_iptrunk": "Validate IP Trunk configuration",
"validate_router": "Validate Router configuration", "validate_router": "Validate Router configuration",
"validate_switch": "Validate Switch configuration", "validate_switch": "Validate Switch configuration",
"validate_edge_port": "Validate Edge Port",
"task_validate_geant_products": "Validation task for GEANT products", "task_validate_geant_products": "Validation task for GEANT products",
"task_send_email_notifications": "Send email notifications for failed tasks", "task_send_email_notifications": "Send email notifications for failed tasks",
"task_create_partners": "Create partner task", "task_create_partners": "Create partner task",
......
""":term:`BGP` session sets."""
from typing import Annotated, TypeVar
from annotated_types import Len
from pydantic import AfterValidator
from gso.products.product_blocks.bgp_session import BGPSession, BGPSessionInactive, BGPSessionProvisioning
BGPSessionTypes = TypeVar("BGPSessionTypes", BGPSessionInactive, BGPSessionProvisioning, BGPSession)
def validate_bgp_session_set(bgp_session_set: list[BGPSessionTypes]) -> list[BGPSessionTypes]:
""":term:`BGP` sessions grouped together.
It consists of either a single item, or a pair of IPv4 and v6 sessions. It is not allowed to have two IPv4 or IPv6
:term:`BGP` sessions as part of a set.
"""
if any(bgp_session.peer_address is None for bgp_session in bgp_session_set):
msg = "BGP session is missing a peer address."
raise ValueError(msg)
if len(bgp_session_set) == 2 and bgp_session_set[0].peer_address.version == bgp_session_set[1].peer_address.version: # type: ignore[union-attr] # noqa: PLR2004
msg = (
"When defining two separate BGP sessions, IP families must differ. "
f"Both are IPv{bgp_session_set[0].peer_address.version}." # type: ignore[union-attr]
)
raise ValueError(msg)
return bgp_session_set
BGPSessionSet = Annotated[
list[BGPSessionTypes], Len(min_length=1, max_length=2), AfterValidator(validate_bgp_session_set)
]
"""A creation workflow for adding a new edge port to the network.""" """A creation workflow for adding a new edge port to the network."""
from typing import Annotated, Any, Self from typing import Annotated, Any, Self
from uuid import uuid4
from annotated_types import Len from annotated_types import Len
from orchestrator import step, workflow from orchestrator import step, workflow
...@@ -16,7 +17,7 @@ from pydantic import AfterValidator, ConfigDict, model_validator ...@@ -16,7 +17,7 @@ from pydantic import AfterValidator, ConfigDict, model_validator
from pydantic_forms.validators import validate_unique_list from pydantic_forms.validators import validate_unique_list
from pynetbox.models.dcim import Interfaces from pynetbox.models.dcim import Interfaces
from gso.products.product_blocks.edge_port import EdgePortType, EncapsulationType from gso.products.product_blocks.edge_port import EdgePortAEMemberBlockInactive, EdgePortType, EncapsulationType
from gso.products.product_blocks.router import RouterRole from gso.products.product_blocks.router import RouterRole
from gso.products.product_types.edge_port import EdgePortInactive, EdgePortProvisioning from gso.products.product_types.edge_port import EdgePortInactive, EdgePortProvisioning
from gso.products.product_types.router import Router from gso.products.product_types.router import Router
...@@ -133,7 +134,9 @@ def initialize_subscription( ...@@ -133,7 +134,9 @@ def initialize_subscription(
subscription.description = f"Edge Port {name} on {router.router_fqdn}, {partner}, {geant_ga_id or ""}" subscription.description = f"Edge Port {name} on {router.router_fqdn}, {partner}, {geant_ga_id or ""}"
subscription.edge_port.edge_port_description = description subscription.edge_port.edge_port_description = description
for member in ae_members: for member in ae_members:
subscription.edge_port.edge_port_ae_members.append(LAGMember(**member)) subscription.edge_port.edge_port_ae_members.append(
EdgePortAEMemberBlockInactive.new(subscription_id=uuid4(), **member)
)
subscription = EdgePortProvisioning.from_other_lifecycle(subscription, SubscriptionLifecycle.PROVISIONING) subscription = EdgePortProvisioning.from_other_lifecycle(subscription, SubscriptionLifecycle.PROVISIONING)
return {"subscription": subscription} return {"subscription": subscription}
......
"""Modify an existing edge port subscription.""" """Modify an existing edge port subscription."""
from typing import Annotated, Any, Self from typing import Annotated, Any, Self
from uuid import uuid4
from annotated_types import Len from annotated_types import Len
from orchestrator import workflow from orchestrator import workflow
...@@ -13,7 +14,7 @@ from pydantic import AfterValidator, ConfigDict, model_validator ...@@ -13,7 +14,7 @@ from pydantic import AfterValidator, ConfigDict, model_validator
from pydantic_forms.types import FormGenerator, UUIDstr from pydantic_forms.types import FormGenerator, UUIDstr
from pydantic_forms.validators import ReadOnlyField, validate_unique_list from pydantic_forms.validators import ReadOnlyField, validate_unique_list
from gso.products.product_blocks.edge_port import EncapsulationType from gso.products.product_blocks.edge_port import EdgePortAEMemberBlock, EncapsulationType
from gso.products.product_types.edge_port import EdgePort from gso.products.product_types.edge_port import EdgePort
from gso.services.lso_client import execute_playbook, lso_interaction from gso.services.lso_client import execute_playbook, lso_interaction
from gso.services.netbox_client import NetboxClient from gso.services.netbox_client import NetboxClient
...@@ -146,7 +147,7 @@ def modify_edge_port_subscription( ...@@ -146,7 +147,7 @@ def modify_edge_port_subscription(
) )
subscription.edge_port.edge_port_ae_members.clear() subscription.edge_port.edge_port_ae_members.clear()
for member in ae_members: for member in ae_members:
subscription.edge_port.edge_port_ae_members.append(LAGMember(**member)) subscription.edge_port.edge_port_ae_members.append(EdgePortAEMemberBlock.new(subscription_id=uuid4(), **member))
subscription.save() subscription.save()
return { return {
...@@ -159,19 +160,19 @@ def modify_edge_port_subscription( ...@@ -159,19 +160,19 @@ def modify_edge_port_subscription(
@step("Update interfaces in NetBox") @step("Update interfaces in NetBox")
def update_interfaces_in_netbox( def update_interfaces_in_netbox(
subscription: EdgePort, subscription: EdgePort,
removed_ae_members: list[LAGMember], removed_ae_members: list[dict],
previous_ae_members: list[LAGMember], previous_ae_members: list[dict],
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Update the interfaces in NetBox.""" """Update the interfaces in NetBox."""
nbclient = NetboxClient() nbclient = NetboxClient()
# Free removed interfaces # Free removed interfaces
for member in removed_ae_members: for removed_member in removed_ae_members:
nbclient.free_interface(subscription.edge_port.edge_port_node.router_fqdn, member.interface_name) nbclient.free_interface(subscription.edge_port.edge_port_node.router_fqdn, removed_member["interface_name"])
# Attach physical interfaces to :term:`LAG` # Attach physical interfaces to :term:`LAG`
# Update interface description to subscription ID # Update interface description to subscription ID
# Reserve interfaces # Reserve interfaces
for member in subscription.edge_port.edge_port_ae_members: for member in subscription.edge_port.edge_port_ae_members:
if any(prev_member.interface_name == member.interface_name for prev_member in previous_ae_members): if any(prev_member["interface_name"] == member.interface_name for prev_member in previous_ae_members):
continue continue
nbclient.attach_interface_to_lag( nbclient.attach_interface_to_lag(
device_name=subscription.edge_port.edge_port_node.router_fqdn, device_name=subscription.edge_port.edge_port_node.router_fqdn,
......
"""GÉANT IP workflows."""
"""Create a new GÉANT IP subscription."""
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment