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

Add documentation for GÉANT IP service related code

parent 2d0cd31f
No related branches found
No related tags found
No related merge requests found
Showing
with 45 additions and 35 deletions
``gso.workflows.edge_port.terminate_edge_port`` ``gso.workflows.edge_port.terminate_edge_port``
============================================== ===============================================
.. automodule:: gso.workflows.edge_port.terminate_edge_port .. automodule:: gso.workflows.edge_port.terminate_edge_port
:members: :members:
......
...@@ -25,6 +25,7 @@ custom.Contractions = YES ...@@ -25,6 +25,7 @@ custom.Contractions = YES
; Using a "regular" - instead of an en dash is totally fine ; Using a "regular" - instead of an en dash is totally fine
Microsoft.Negative = NO Microsoft.Negative = NO
Microsoft.RangeFormat = NO Microsoft.RangeFormat = NO
Microsoft.We = suggestion
TokenIgnores = (:term:`\S+`), (:param \S+(?: \S+)?:), (:type \S+:), (:return \S+:), (:rtype: \S+), (:class:`\S+`) TokenIgnores = (:term:`\S+`), (:param \S+(?: \S+)?:), (:type \S+:), (:return \S+:), (:rtype: \S+), (:class:`\S+`)
......
...@@ -31,3 +31,7 @@ OPA ...@@ -31,3 +31,7 @@ OPA
OIDC OIDC
HTTPBearer HTTPBearer
Kentik Kentik
UTC
EARL
SURF
[Ee]nsure
...@@ -7,7 +7,6 @@ swap: ...@@ -7,7 +7,6 @@ swap:
can't: cannot can't: cannot
couldn't: could not couldn't: could not
didn't: did not didn't: did not
don't: do not
doesn't: does not doesn't: does not
hasn't: has not hasn't: has not
haven't: have not haven't: have not
......
...@@ -114,7 +114,7 @@ class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle. ...@@ -114,7 +114,7 @@ class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle.
edge_port_enable_lacp: bool edge_port_enable_lacp: bool
#: The type of encapsulation used on this edge port, by default DOT1Q. #: The type of encapsulation used on this edge port, by default DOT1Q.
edge_port_encapsulation: EncapsulationType = EncapsulationType.DOT1Q edge_port_encapsulation: EncapsulationType = EncapsulationType.DOT1Q
#: The MAC address assigned to this edge port, if applicable. #: The :term:`MAC` address assigned to this edge port, if applicable.
edge_port_mac_address: str | None = None edge_port_mac_address: str | None = None
#: The speed capacity of each member in the physical port. #: The speed capacity of each member in the physical port.
edge_port_member_speed: PhysicalPortCapacity edge_port_member_speed: PhysicalPortCapacity
...@@ -126,7 +126,7 @@ class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle. ...@@ -126,7 +126,7 @@ class EdgePortBlock(EdgePortBlockProvisioning, lifecycle=[SubscriptionLifecycle.
edge_port_ignore_if_down: bool = False edge_port_ignore_if_down: bool = False
#: 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 :term:`LAG` members associated with this edge port.
edge_port_ae_members: LAGMemberList[EdgePortAEMemberBlock] # type: ignore[assignment] edge_port_ae_members: LAGMemberList[EdgePortAEMemberBlock] # type: ignore[assignment]
#: A list of Service Binding Ports associated with this Edge Port #: A list of Service Binding Ports associated with this Edge Port
edge_port_sbp_list: list[ServiceBindingPort] # type: ignore[assignment] edge_port_sbp_list: list[ServiceBindingPort] # type: ignore[assignment]
...@@ -21,7 +21,7 @@ class NRENAccessPortProvisioning(NRENAccessPortInactive, lifecycle=[Subscription ...@@ -21,7 +21,7 @@ class NRENAccessPortProvisioning(NRENAccessPortInactive, lifecycle=[Subscription
"""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_type: APType nren_ap_type: APType
geant_ip_ep: EdgePortBlockProvisioning # type: ignore[assignment] geant_ip_ep: EdgePortBlockProvisioning
class NRENAccessPort(NRENAccessPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]): class NRENAccessPort(NRENAccessPortProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
...@@ -30,7 +30,7 @@ class NRENAccessPort(NRENAccessPortProvisioning, lifecycle=[SubscriptionLifecycl ...@@ -30,7 +30,7 @@ class NRENAccessPort(NRENAccessPortProvisioning, lifecycle=[SubscriptionLifecycl
#: The type of Access Port #: The type of Access Port
nren_ap_type: APType nren_ap_type: APType
#: The list of Edge Ports where this service terminates. #: The list of Edge Ports where this service terminates.
geant_ip_ep: EdgePortBlock # type: ignore[assignment] geant_ip_ep: EdgePortBlock
class GeantIPBlockInactive( class GeantIPBlockInactive(
......
...@@ -43,9 +43,9 @@ class OpengearBlock(OpengearBlockProvisioning, lifecycle=[SubscriptionLifecycle. ...@@ -43,9 +43,9 @@ class OpengearBlock(OpengearBlockProvisioning, lifecycle=[SubscriptionLifecycle.
opengear_hostname: str opengear_hostname: str
#: The site where the Opengear device is located. #: The site where the Opengear device is located.
opengear_site: SiteBlock opengear_site: SiteBlock
#: The WAN address of the Opengear device. #: The :term:`WAN` address of the Opengear device.
opengear_wan_address: ipaddress.IPv4Address opengear_wan_address: ipaddress.IPv4Address
#: The WAN netmask of the Opengear device. #: The :term:`WAN` netmask of the Opengear device.
opengear_wan_netmask: ipaddress.IPv4Address opengear_wan_netmask: ipaddress.IPv4Address
#: The WAN gateway of the Opengear device. #: The :term:`WAN` gateway of the Opengear device.
opengear_wan_gateway: ipaddress.IPv4Address opengear_wan_gateway: ipaddress.IPv4Address
...@@ -99,6 +99,8 @@ class KentikClient: ...@@ -99,6 +99,8 @@ class KentikClient:
If the site is not found, return an empty dict. If the site is not found, return an empty dict.
.. vale off
:param str site_slug: The name of the site, should be a three-letter slug like COR or POZ. :param str site_slug: The name of the site, should be a three-letter slug like COR or POZ.
""" """
sites = self.get_sites() sites = self.get_sites()
......
...@@ -295,7 +295,7 @@ class NetboxClient: ...@@ -295,7 +295,7 @@ class NetboxClient:
router_name = Router.from_subscription(router_id).router.router_fqdn router_name = Router.from_subscription(router_id).router.router_fqdn
device = self.get_device_by_name(router_name) device = self.get_device_by_name(router_name)
# Get the existing LAG interfaces for the device # Get the existing :term:`LAG` interfaces for the device
lag_interface_names = [ lag_interface_names = [
interface["name"] for interface in self.netbox.dcim.interfaces.filter(device=device.name, type="lag") interface["name"] for interface in self.netbox.dcim.interfaces.filter(device=device.name, type="lag")
] ]
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
"create_site": "Create Site", "create_site": "Create Site",
"create_switch": "Create Switch", "create_switch": "Create Switch",
"create_edge_port": "Create Edge Port", "create_edge_port": "Create Edge Port",
"create_geant_ip": "Create GÉANT IP",
"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",
......
...@@ -209,15 +209,18 @@ def active_edge_port_selector(*, geant_only: bool | None = None) -> Choice: ...@@ -209,15 +209,18 @@ def active_edge_port_selector(*, geant_only: bool | None = None) -> Choice:
if geant_only is not None: if geant_only is not None:
# ``geant_only`` is set, so we will filter accordingly. # ``geant_only`` is set, so we will filter accordingly.
geant_partner_id = get_partner_by_name("GEANT")["partner_id"] geant_partner_id = get_partner_by_name("GEANT")["partner_id"]
edge_port_subscriptions = filter( edge_port_subscriptions = list(
lambda subscription: geant_only ^ bool(subscription["customer_id"] != geant_partner_id), filter(
edge_port_subscriptions, lambda subscription: geant_only ^ bool(subscription["customer_id"] != geant_partner_id),
edge_port_subscriptions,
)
) )
edge_port_subscriptions = {str(port["subscription_id"]): port["description"] for port in edge_port_subscriptions} edge_ports = {str(port["subscription_id"]): port["description"] for port in edge_port_subscriptions}
return Choice( return Choice(
"Select an Edge Port", zip(edge_port_subscriptions.keys(), edge_port_subscriptions.items(), strict=True) "Select an Edge Port",
zip(edge_ports.keys(), edge_ports.items(), strict=True), # type: ignore[arg-type]
) )
...@@ -229,11 +232,11 @@ def partner_choice() -> Choice: ...@@ -229,11 +232,11 @@ def partner_choice() -> Choice:
def validate_edge_port_number_of_members_based_on_lacp(*, number_of_members: int, enable_lacp: bool) -> None: def validate_edge_port_number_of_members_based_on_lacp(*, number_of_members: int, enable_lacp: bool) -> None:
"""Validate the number of edge port members based on the LACP setting. """Validate the number of edge port members based on the :term:`LACP` setting.
:param number_of_members: The number of members to validate. :param number_of_members: The number of members to validate.
:param enable_lacp: Whether LACP is enabled or not. :param enable_lacp: Whether :term:`LACP` is enabled or not.
:raises ValueError: If the number of members is greater than 1 and LACP is disabled. :raises ValueError: If the number of members is greater than 1 and :term:`LACP` is disabled.
""" """
if number_of_members > 1 and not enable_lacp: if number_of_members > 1 and not enable_lacp:
err_msg = "Number of members must be 1 if LACP is disabled." err_msg = "Number of members must be 1 if LACP is disabled."
......
...@@ -239,7 +239,7 @@ def create_edge_port() -> StepList: ...@@ -239,7 +239,7 @@ def create_edge_port() -> StepList:
* Create and initialise the subscription object in the service database * Create and initialise the subscription object in the service database
* Deploy configuration on the new edge port, first as a dry run * Deploy configuration on the new edge port, first as a dry run
* allocate LAG and LAG members in the Netbox. * allocate :term:`LAG` and :term:`LAG` members in the Netbox.
""" """
return ( return (
begin begin
......
...@@ -70,7 +70,7 @@ def remove_edge_port_real( ...@@ -70,7 +70,7 @@ def remove_edge_port_real(
@step("Netbox Clean Up") @step("Netbox Clean Up")
def netbox_clean_up(subscription: EdgePort) -> None: def netbox_clean_up(subscription: EdgePort) -> None:
"""Update Netbox to remove the edge port LAG interface and all the LAG members.""" """Update Netbox to remove the edge port :term:`LAG` interface and all the :term:`LAG` members."""
nbclient = NetboxClient() nbclient = NetboxClient()
for member in subscription.edge_port.edge_port_ae_members: for member in subscription.edge_port.edge_port_ae_members:
......
...@@ -36,15 +36,15 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ...@@ -36,15 +36,15 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
model_config = ConfigDict(title=f"{product_name} - Select partner") model_config = ConfigDict(title=f"{product_name} - Select partner")
tt_number: TTNumber tt_number: TTNumber
partner: partner_choice() partner: partner_choice() # type: ignore[valid-type]
initial_user_input = yield CreateGeantIPForm initial_user_input = yield CreateGeantIPForm
class EdgePortSelection(BaseModel): class EdgePortSelection(BaseModel):
edge_port: active_edge_port_selector() edge_port: active_edge_port_selector() # type: ignore[valid-type]
ap_type: APType ap_type: APType
def __hash__(self): def __hash__(self) -> int:
return self.edge_port.__hash__() return self.edge_port.__hash__()
class EdgePortSelectionForm(FormPage): class EdgePortSelectionForm(FormPage):
......
...@@ -255,7 +255,7 @@ def dig_all_hosts_v6(new_ipv6_network: str) -> None: ...@@ -255,7 +255,7 @@ def dig_all_hosts_v6(new_ipv6_network: str) -> None:
@step("Ping all hosts in the assigned IPv4 network") @step("Ping all hosts in the assigned IPv4 network")
def ping_all_hosts_v4(new_ipv4_network: str) -> None: def ping_all_hosts_v4(new_ipv4_network: str) -> None:
"""Ping all hosts in the IPv4 network to verify they're not in use.""" """Ping all hosts in the IPv4 network to verify they are not in use."""
unavailable_hosts = [host for host in IPv4Network(new_ipv4_network) if ping(str(host), timeout=1)] unavailable_hosts = [host for host in IPv4Network(new_ipv4_network) if ping(str(host), timeout=1)]
if unavailable_hosts: if unavailable_hosts:
...@@ -265,7 +265,7 @@ def ping_all_hosts_v4(new_ipv4_network: str) -> None: ...@@ -265,7 +265,7 @@ def ping_all_hosts_v4(new_ipv4_network: str) -> None:
@step("Ping all hosts in the assigned IPv6 network") @step("Ping all hosts in the assigned IPv6 network")
def ping_all_hosts_v6(new_ipv6_network: str) -> State: def ping_all_hosts_v6(new_ipv6_network: str) -> State:
"""Ping all hosts in the IPv6 network to verify they're not in use.""" """Ping all hosts in the IPv6 network to verify they are not in use."""
unavailable_hosts = [host for host in IPv6Network(new_ipv6_network) if ping(str(host), timeout=1)] unavailable_hosts = [host for host in IPv6Network(new_ipv6_network) if ping(str(host), timeout=1)]
if unavailable_hosts: if unavailable_hosts:
......
...@@ -89,7 +89,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ...@@ -89,7 +89,7 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
): ):
# We want to stay on the same site, so all routers that are in different sites get skipped. # We want to stay on the same site, so all routers that are in different sites get skipped.
continue continue
# If migrate_to_different_site is true, we can add ALL routers to the result map # If migrate_to_different_site is true, we can add *all* routers to the result map
routers[str(router_id)] = router["description"] routers[str(router_id)] = router["description"]
new_router_enum = Choice("Select a new router", zip(routers.keys(), routers.items(), strict=True)) # type: ignore[arg-type] new_router_enum = Choice("Select a new router", zip(routers.keys(), routers.items(), strict=True)) # type: ignore[arg-type]
...@@ -204,7 +204,7 @@ def calculate_old_side_data(subscription: Iptrunk, replace_index: int) -> State: ...@@ -204,7 +204,7 @@ def calculate_old_side_data(subscription: Iptrunk, replace_index: int) -> State:
@step("Check Optical PRE levels on the trunk endpoint") @step("Check Optical PRE levels on the trunk endpoint")
def check_ip_trunk_optical_levels_pre(subscription: Iptrunk) -> LSOState: def check_ip_trunk_optical_levels_pre(subscription: Iptrunk) -> LSOState:
"""Check Optical PRE levels on the trunk.""" """Check Optical levels on the trunk before migration."""
extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "optical_pre"} extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "optical_pre"}
return { return {
...@@ -252,7 +252,7 @@ def check_ip_trunk_optical_levels_post( ...@@ -252,7 +252,7 @@ def check_ip_trunk_optical_levels_post(
def check_ip_trunk_lldp( def check_ip_trunk_lldp(
subscription: Iptrunk, new_node: Router, new_lag_member_interfaces: list[dict], replace_index: int subscription: Iptrunk, new_node: Router, new_lag_member_interfaces: list[dict], replace_index: int
) -> LSOState: ) -> LSOState:
"""Check LLDP on the new trunk endpoints.""" """Check :term:`LLDP` on the new trunk endpoints."""
extra_vars = { extra_vars = {
"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "wfo_ip_trunk_json": json.loads(json_dumps(subscription)),
"new_node": json.loads(json_dumps(new_node)), "new_node": json.loads(json_dumps(new_node)),
...@@ -489,7 +489,7 @@ def update_remaining_side_bfd_real( ...@@ -489,7 +489,7 @@ def update_remaining_side_bfd_real(
@step("Check BFD session over trunk") @step("Check BFD session over trunk")
def check_ip_trunk_bfd(subscription: Iptrunk, new_node: Router, replace_index: int) -> LSOState: def check_ip_trunk_bfd(subscription: Iptrunk, new_node: Router, replace_index: int) -> LSOState:
"""Check BFD session across the new trunk.""" """Check :term:`BFD` session across the new trunk."""
extra_vars = { extra_vars = {
"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "wfo_ip_trunk_json": json.loads(json_dumps(subscription)),
"new_node": json.loads(json_dumps(new_node)), "new_node": json.loads(json_dumps(new_node)),
...@@ -830,7 +830,7 @@ def migrate_iptrunk() -> StepList: ...@@ -830,7 +830,7 @@ def migrate_iptrunk() -> StepList:
* Deploy a new :term:`ISIS` interface between routers A and C * Deploy a new :term:`ISIS` interface between routers A and C
* Wait for operator confirmation that :term:`ISIS` is behaving as expected * Wait for operator confirmation that :term:`ISIS` is behaving as expected
* Restore the old :term:`ISIS` metric on the new trunk * Restore the old :term:`ISIS` metric on the new trunk
* Delete the old, disabled configuration on the routers, first as a dry run * Delete the old configuration from the routers, first as a dry run
* Reflect the changes made in :term:`IPAM` * Reflect the changes made in :term:`IPAM`
* Update the subscription model in the database * Update the subscription model in the database
* Update the reserved interfaces in Netbox * Update the reserved interfaces in Netbox
......
...@@ -205,7 +205,7 @@ def check_ip_trunk_connectivity(subscription: Iptrunk) -> LSOState: ...@@ -205,7 +205,7 @@ def check_ip_trunk_connectivity(subscription: Iptrunk) -> LSOState:
@step("Check LLDP on the trunk endpoints") @step("Check LLDP on the trunk endpoints")
def check_ip_trunk_lldp(subscription: Iptrunk) -> LSOState: def check_ip_trunk_lldp(subscription: Iptrunk) -> LSOState:
"""Check LLDP on trunk endpoints.""" """Check :term:`LLDP` on trunk endpoints."""
extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "lldp"} extra_vars = {"wfo_ip_trunk_json": json.loads(json_dumps(subscription)), "check": "lldp"}
return { return {
......
...@@ -210,7 +210,7 @@ def validate_iptrunk() -> StepList: ...@@ -210,7 +210,7 @@ def validate_iptrunk() -> StepList:
* Verify that the :term:`LAG` interfaces are correctly configured in :term:`IPAM`. * Verify that the :term:`LAG` interfaces are correctly configured in :term:`IPAM`.
* Check correct configuration of interfaces in NetBox. * Check correct configuration of interfaces in NetBox.
* Verify the configuration on both sides of the trunk is intact. * Verify the configuration on both sides of the trunk is intact.
* Check the ISIS metric of the trunk. * Check the :term:`ISIS` metric of the trunk.
* Verify that TWAMP configuration is correct. * Verify that TWAMP configuration is correct.
If a trunk has a Juniper router on both sides, it is considered legacy and does not require validation. If a trunk has a Juniper router on both sides, it is considered legacy and does not require validation.
......
...@@ -44,7 +44,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator: ...@@ -44,7 +44,7 @@ def initial_input_form_generator(product_name: str) -> FormGenerator:
tt_number: TTNumber tt_number: TTNumber
partner: ReadOnlyField("GEANT", default_type=str) # type: ignore[valid-type] partner: ReadOnlyField("GEANT", default_type=str) # type: ignore[valid-type]
vendor: Vendor vendor: Vendor
router_site: active_site_selector() router_site: active_site_selector() # type: ignore[valid-type]
hostname: str hostname: str
ts_port: PortNumber ts_port: PortNumber
router_role: RouterRole router_role: RouterRole
......
...@@ -34,8 +34,8 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: ...@@ -34,8 +34,8 @@ def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator:
def update_subscription_model(subscription: Router, connection_strategy: str) -> State: def update_subscription_model(subscription: Router, connection_strategy: str) -> State:
"""Update the database model to reflect the new connection strategy. """Update the database model to reflect the new connection strategy.
If the connection strategy is set to IN-BAND, then access_via_ts should be set to False. If the connection strategy is set to in-band, then access_via_ts should be set to False.
Conversely, if the connection strategy is set to OUT-OF-BAND, access_via_ts should be set to True. Conversely, if the connection strategy is set to out-of-band, access_via_ts should be set to True.
""" """
subscription.router.router_access_via_ts = connection_strategy == ConnectionStrategy.OUT_OF_BAND subscription.router.router_access_via_ts = connection_strategy == ConnectionStrategy.OUT_OF_BAND
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment