diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 90a966efacf6d71a4a0e849c329a40b4c8ad5ee2..a0d023fdf083bb0f5654d31af63e6c4ed6f6ea92 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -9,10 +9,12 @@ from orchestrator.forms import FormPage from orchestrator.forms.validators import Choice, Label from orchestrator.targets import Target from orchestrator.types import FormGenerator, State, SubscriptionLifecycle, UUIDstr +from orchestrator.utils.errors import ProcessFailureError from orchestrator.utils.json import json_dumps -from orchestrator.workflow import StepList, begin, conditional, done, step, workflow +from orchestrator.workflow import StepList, begin, conditional, done, step, step_group, workflow from orchestrator.workflows.steps import resync, set_status, store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form +from ping3 import ping from pydantic import AfterValidator, ConfigDict, field_validator from pydantic_forms.validators import ReadOnlyField, validate_unique_list from pynetbox.models.dcim import Interfaces @@ -222,6 +224,26 @@ def get_info_from_ipam(subscription: IptrunkInactive) -> State: return {"subscription": subscription} +@step("Ping all hosts in the assigned IPv4 network") +def ping_all_hosts_v4(subscription: IptrunkInactive) -> None: + """Ping all hosts in the IPv4 network to verify they're not in use.""" + unavailable_hosts = [host for host in subscription.iptrunk.iptrunk_ipv4_network if ping(host, timeout=1)] + + if unavailable_hosts: + msg = "One or more hosts in the assigned IPv4 network are responding to ping, please investigate." + raise ProcessFailureError(msg, details=unavailable_hosts) + + +@step("Ping all hosts in the assigned IPv6 network") +def ping_all_hosts_v6(subscription: IptrunkInactive) -> None: + """Ping all hosts in the IPv6 network to verify they're not in use.""" + unavailable_hosts = [host for host in subscription.iptrunk.iptrunk_ipv6_network if ping(host, timeout=1)] + + if unavailable_hosts: + msg = "One or more hosts in the assigned IPv6 network are responding to ping, please investigate." + raise ProcessFailureError(msg, details=unavailable_hosts) + + @step("Initialize subscription") def initialize_subscription( subscription: IptrunkInactive, @@ -527,12 +549,16 @@ def create_iptrunk() -> StepList: side_a_is_nokia = conditional(lambda state: get_router_vendor(state["side_a_node_id"]) == Vendor.NOKIA) side_b_is_nokia = conditional(lambda state: get_router_vendor(state["side_b_node_id"]) == Vendor.NOKIA) + assign_ip_networks = step_group( + name="Assign IP networks", steps=begin >> get_info_from_ipam >> ping_all_hosts_v4 >> ping_all_hosts_v6 + ) + return ( begin >> create_subscription >> store_process_subscription(Target.CREATE) >> initialize_subscription - >> get_info_from_ipam + >> assign_ip_networks >> reserve_interfaces_in_netbox >> lso_interaction(provision_ip_trunk_iface_dry) >> lso_interaction(provision_ip_trunk_iface_real) diff --git a/requirements.txt b/requirements.txt index 8878a45c8d77880d89dfacc73fa89a5cc88cdf5f..b751e9c033cdc1edb98813563e8174876037a363 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,7 @@ celery-redbeat==2.2.0 celery==5.3.6 azure-identity==1.16.0 msgraph-sdk==1.2.0 +ping3==4.0.8 # Test and linting dependencies celery-stubs==0.1.3 diff --git a/setup.py b/setup.py index 65347a14cae3bbc558edae0dd74fc9d0bf351c7f..3eb6376d0268c2b5f6291676a11ac698f24314d1 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ setup( "celery==5.3.6", "azure-identity==1.16.0", "msgraph-sdk==1.2.0", + "ping3==4.0.8", ], include_package_data=True, )