diff --git a/gso/services/provisioning_proxy.py b/gso/services/provisioning_proxy.py index 13d90249b50917c899ef12c05f1d15758662b30c..2fd1cce9f66376605b4557bc0989a112f453b490 100644 --- a/gso/services/provisioning_proxy.py +++ b/gso/services/provisioning_proxy.py @@ -14,7 +14,7 @@ from orchestrator.forms import FormPage, ReadOnlyField from orchestrator.forms.validators import Accept, Label, LongText from orchestrator.types import FormGenerator, State, UUIDstr, strEnum from orchestrator.utils.json import json_dumps -from orchestrator.workflow import Step, StepList +from orchestrator.workflow import Step, StepList, abort from pydantic import validator from gso import settings @@ -159,6 +159,7 @@ def _await_pp_results(subscription: SubscriptionModel, label_text: str = DEFAULT boolean set to `True`. :rtype: {class}`orchestrator.types.FormGenerator` """ + class ProvisioningResultPage(FormPage): class Config: title = f"Deploying {subscription.product.name}..." @@ -179,7 +180,7 @@ def _await_pp_results(subscription: SubscriptionModel, label_text: str = DEFAULT @step("Reset Provisioning Proxy state") -def reset_pp_success_state() -> State: +def _reset_pp_success_state() -> State: """Reset the boolean that indicates a successful provisioning proxy result in the state of a running workflow. :return: A new state of the workflow, where the key `pp_did_succeed` has been (re)set to false. @@ -224,8 +225,8 @@ def _confirm_pp_results(state: State) -> FormGenerator: return {"pp_did_succeed": successful_run} -def pp_interaction(provisioning_step: Step) -> StepList: - """Wrapper function for an interaction with the provisioning proxy +def pp_interaction(provisioning_step: Step, attempts: int) -> StepList: + """Interaction with the provisioning proxy. This method returns the three steps that make up an interaction with the provisioning proxy: - The provisioning step itself, given by the user as input. @@ -236,15 +237,28 @@ def pp_interaction(provisioning_step: Step) -> StepList: already successful, these steps are skipped. This mechanism is quite a dirty hack, and it is planned to be addressed in a later release. + The parameter `attempts` indicates how many times a provisioning may be attempted. When this amount is exceeded, and + it is still not successful, the workflow will be aborted. + :param provisioning_step: The step that executes an interaction with the provisioning proxy. :type provisioning_step: {class}`orchestrator.workflow.Step` + :param attempts: The maximum amount of times that a provisioning can be retried. + :type attempts: int :return: A list of three steps that form one interaction with the provisioning proxy. :rtype: {class}`orchestrator.workflow.StepList` """ should_retry_pp_steps = conditional(lambda state: not state.get("pp_did_succeed")) - return ( - should_retry_pp_steps(provisioning_step) - >> should_retry_pp_steps(_await_pp_results) - >> should_retry_pp_steps(_confirm_pp_results) - ) + pp_steps = StepList([_reset_pp_success_state]) + + for _ in range(attempts): + pp_steps >>= ( + should_retry_pp_steps(provisioning_step) + >> should_retry_pp_steps(_await_pp_results) + >> should_retry_pp_steps(_confirm_pp_results) + ) + + # Abort a workflow if provisioning has failed too many times + pp_steps >>= should_retry_pp_steps(abort) + + return pp_steps diff --git a/gso/workflows/device/create_device.py b/gso/workflows/device/create_device.py index a06ee8cf37a3579afc2279f849d2fed053e3b6bc..e0a865f794e7ba7e56fab1985a934a1c51a1cfd1 100644 --- a/gso/workflows/device/create_device.py +++ b/gso/workflows/device/create_device.py @@ -18,7 +18,7 @@ from gso.products.product_types import device from gso.products.product_types.device import DeviceInactive, DeviceProvisioning from gso.products.product_types.site import Site from gso.services import _ipam, provisioning_proxy -from gso.services.provisioning_proxy import pp_interaction, reset_pp_success_state +from gso.services.provisioning_proxy import pp_interaction def site_selector() -> Choice: @@ -165,17 +165,8 @@ def create_device() -> StepList: >> store_process_subscription(Target.CREATE) >> initialize_subscription >> get_info_from_ipam - # - >> reset_pp_success_state - >> pp_interaction(provision_device_dry) - >> pp_interaction(provision_device_dry) - >> pp_interaction(provision_device_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_device_real) - >> pp_interaction(provision_device_real) - >> pp_interaction(provision_device_real) - # + >> pp_interaction(provision_device_dry, 3) + >> pp_interaction(provision_device_real, 3) >> set_status(SubscriptionLifecycle.ACTIVE) >> resync >> done diff --git a/gso/workflows/iptrunk/create_iptrunk.py b/gso/workflows/iptrunk/create_iptrunk.py index 3b1e4231743da65c3fb8e4c01d0503f259624422..e52ac500a2e733d3febbf932b06c8cd5cf05285f 100644 --- a/gso/workflows/iptrunk/create_iptrunk.py +++ b/gso/workflows/iptrunk/create_iptrunk.py @@ -14,7 +14,7 @@ from gso.products.product_blocks.iptrunk import IptrunkType from gso.products.product_types.device import Device from gso.products.product_types.iptrunk import IptrunkInactive, IptrunkProvisioning from gso.services import _ipam, provisioning_proxy -from gso.services.provisioning_proxy import pp_interaction, reset_pp_success_state +from gso.services.provisioning_proxy import pp_interaction def initial_input_form_generator(product_name: str) -> FormGenerator: @@ -245,47 +245,14 @@ def create_iptrunk() -> StepList: >> store_process_subscription(Target.CREATE) >> initialize_subscription >> get_info_from_ipam - >> reset_pp_success_state - # - >> pp_interaction(provision_ip_trunk_iface_dry) - >> pp_interaction(provision_ip_trunk_iface_dry) - >> pp_interaction(provision_ip_trunk_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_iface_real) - >> pp_interaction(provision_ip_trunk_iface_real) - >> pp_interaction(provision_ip_trunk_iface_real) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_isis_iface_dry) - >> pp_interaction(provision_ip_trunk_isis_iface_dry) - >> pp_interaction(provision_ip_trunk_isis_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_isis_iface_real) - >> pp_interaction(provision_ip_trunk_isis_iface_real) - >> pp_interaction(provision_ip_trunk_isis_iface_real) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_ldp_iface_dry) - >> pp_interaction(provision_ip_trunk_ldp_iface_dry) - >> pp_interaction(provision_ip_trunk_ldp_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_ldp_iface_real) - >> pp_interaction(provision_ip_trunk_ldp_iface_real) - >> pp_interaction(provision_ip_trunk_ldp_iface_real) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_lldp_iface_dry) - >> pp_interaction(provision_ip_trunk_lldp_iface_dry) - >> pp_interaction(provision_ip_trunk_lldp_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_lldp_iface_real) - >> pp_interaction(provision_ip_trunk_lldp_iface_real) - >> pp_interaction(provision_ip_trunk_lldp_iface_real) - # + >> pp_interaction(provision_ip_trunk_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_iface_real, 3) + >> pp_interaction(provision_ip_trunk_isis_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_isis_iface_real, 3) + >> pp_interaction(provision_ip_trunk_ldp_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_ldp_iface_real, 3) + >> pp_interaction(provision_ip_trunk_lldp_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_lldp_iface_real, 3) >> set_status(SubscriptionLifecycle.ACTIVE) >> resync >> done diff --git a/gso/workflows/iptrunk/modify_generic.py b/gso/workflows/iptrunk/modify_generic.py index f0439a9e34e87fe1086e431ce3d935afc31389b9..68294b2ed43ccf70e8f31d4e83facc4a286bd01d 100644 --- a/gso/workflows/iptrunk/modify_generic.py +++ b/gso/workflows/iptrunk/modify_generic.py @@ -12,7 +12,7 @@ from gso.products.product_blocks import PhyPortCapacity from gso.products.product_blocks.iptrunk import IptrunkType from gso.products.product_types.iptrunk import Iptrunk from gso.services import provisioning_proxy -from gso.services.provisioning_proxy import pp_interaction, reset_pp_success_state +from gso.services.provisioning_proxy import pp_interaction def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @@ -153,27 +153,10 @@ def modify_generic() -> StepList: >> store_process_subscription(Target.MODIFY) >> unsync >> modify_iptrunk_subscription - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_iface_dry) - >> pp_interaction(provision_ip_trunk_iface_dry) - >> pp_interaction(provision_ip_trunk_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_iface_real) - >> pp_interaction(provision_ip_trunk_iface_real) - >> pp_interaction(provision_ip_trunk_iface_real) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_lldp_iface_dry) - >> pp_interaction(provision_ip_trunk_lldp_iface_dry) - >> pp_interaction(provision_ip_trunk_lldp_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_lldp_iface_real) - >> pp_interaction(provision_ip_trunk_lldp_iface_real) - >> pp_interaction(provision_ip_trunk_lldp_iface_real) - # + >> pp_interaction(provision_ip_trunk_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_iface_real, 3) + >> pp_interaction(provision_ip_trunk_lldp_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_lldp_iface_real, 3) >> resync >> done ) diff --git a/gso/workflows/iptrunk/modify_isis_metric.py b/gso/workflows/iptrunk/modify_isis_metric.py index 21af4c57b30676781b1349ca34ea327347cb2b2d..d7affd6a215db53caaf0cf7ac40bb2d5ae6bfee5 100644 --- a/gso/workflows/iptrunk/modify_isis_metric.py +++ b/gso/workflows/iptrunk/modify_isis_metric.py @@ -7,7 +7,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_types.iptrunk import Iptrunk from gso.services import provisioning_proxy -from gso.services.provisioning_proxy import pp_interaction, reset_pp_success_state +from gso.services.provisioning_proxy import pp_interaction def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @@ -71,17 +71,8 @@ def modify_isis_metric() -> StepList: >> store_process_subscription(Target.MODIFY) >> unsync >> modify_iptrunk_subscription - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_isis_iface_dry) - >> pp_interaction(provision_ip_trunk_isis_iface_dry) - >> pp_interaction(provision_ip_trunk_isis_iface_dry) - # - >> reset_pp_success_state - >> pp_interaction(provision_ip_trunk_isis_iface_real) - >> pp_interaction(provision_ip_trunk_isis_iface_real) - >> pp_interaction(provision_ip_trunk_isis_iface_real) - # + >> pp_interaction(provision_ip_trunk_isis_iface_dry, 3) + >> pp_interaction(provision_ip_trunk_isis_iface_real, 3) >> resync >> done ) diff --git a/gso/workflows/iptrunk/terminate_iptrunk.py b/gso/workflows/iptrunk/terminate_iptrunk.py index 2436c8e804a553bfb341ad64d2bf67a8b195216a..474ae707e493b862bc516917c8c40fdeba77186e 100644 --- a/gso/workflows/iptrunk/terminate_iptrunk.py +++ b/gso/workflows/iptrunk/terminate_iptrunk.py @@ -12,7 +12,7 @@ from orchestrator.workflows.utils import wrap_modify_initial_input_form from gso.products.product_types.iptrunk import Iptrunk from gso.services import ipam, provisioning_proxy from gso.services.ipam import V4ServiceNetwork, V6ServiceNetwork -from gso.services.provisioning_proxy import pp_interaction, reset_pp_success_state +from gso.services.provisioning_proxy import pp_interaction def initial_input_form_generator(subscription_id: UUIDstr) -> FormGenerator: @@ -92,22 +92,8 @@ def terminate_iptrunk() -> StepList: >> store_process_subscription(Target.TERMINATE) >> unsync >> modify_iptrunk_subscription - # - >> reset_pp_success_state - >> pp_interaction(drain_traffic_from_ip_trunk) - >> pp_interaction(drain_traffic_from_ip_trunk) - >> pp_interaction(drain_traffic_from_ip_trunk) - # - >> reset_pp_success_state - >> pp_interaction(deprovision_ip_trunk_dry) - >> pp_interaction(deprovision_ip_trunk_dry) - >> pp_interaction(deprovision_ip_trunk_dry) - # - >> reset_pp_success_state - >> pp_interaction(deprovision_ip_trunk_real) - >> pp_interaction(deprovision_ip_trunk_real) - >> pp_interaction(deprovision_ip_trunk_real) - # + >> pp_interaction(drain_traffic_from_ip_trunk, 3) + >> pp_interaction(deprovision_ip_trunk_dry, 3) >> deprovision_ip_trunk_ipv4 >> deprovision_ip_trunk_ipv6 >> set_status(SubscriptionLifecycle.TERMINATED)