diff --git a/gso/services/provisioning_proxy.py b/gso/services/provisioning_proxy.py index ea96507da8fe9dad6685029ee075473a2afcec5e..1189647133fde96d469c71f32f188bdc186fa8b0 100644 --- a/gso/services/provisioning_proxy.py +++ b/gso/services/provisioning_proxy.py @@ -1,11 +1,13 @@ import logging -from orchestrator.types import UUIDstr - -from gso.products.product_blocks.device import DeviceBlockProvisioning -from gso import settings import requests +from orchestrator import inputstep +from orchestrator.config.assignee import Assignee +from orchestrator.forms import FormPage, ReadOnlyField +from orchestrator.forms.validators import Accept, Label, LongText +from orchestrator.types import UUIDstr, State +from gso import settings from gso.products.product_types.device import DeviceProvisioning logger = logging.getLogger(__name__) @@ -21,7 +23,7 @@ def provision_node( device_params = node_subscription_params.device callback_url = f'{settings.load_oss_params().GENERAL.public_hostname}' \ - f'/api/process/{process_id}/resume' # TODO: verify this URL + f'/api/processes/{process_id}/resume' logger.debug(f'[disabled] provisioning node {device_params}') parameters = { @@ -56,3 +58,36 @@ def provision_node( f'/api/device', json=parameters) post_request.raise_for_status() + + +@inputstep('Await provisioning proxy results', assignee=Assignee('SYSTEM')) +def await_pp_results() -> State: + class ProvisioningResultPage(FormPage): + class Config: + title = 'Do NOT click on confirm in this step!' + + warning_label: Label = 'This step relies on an external service to ' \ + 'send an update to the orchestrator, do not ' \ + 'interfere with this process please.' + pp_run_results: dict = {'state': 'not_ready'} + confirm: Accept = Accept('INCOMPLETE') + + result_page = yield ProvisioningResultPage + + return result_page.dict() + + +@inputstep('Confirm provisioning proxy results', assignee=Assignee('SYSTEM')) +def confirm_pp_results(state: State) -> State: + class ConfirmRunPage(FormPage): + class Config: + title = 'Execution completed, please confirm the results.' + + run_status: str = ReadOnlyField(state['pp_run_results']['status']) + run_results: LongText = ReadOnlyField( + f"{state['pp_run_results']['output']}") + confirm: Accept = Accept('INCOMPLETE') + + yield ConfirmRunPage + + return state diff --git a/gso/workflows/device/create_device.py b/gso/workflows/device/create_device.py index 51fced6409e1c7face78f305bfd2537a3758d349..c59c2866b679520fbd91e227ac829136bc0408c2 100644 --- a/gso/workflows/device/create_device.py +++ b/gso/workflows/device/create_device.py @@ -1,22 +1,20 @@ import ipaddress from uuid import uuid4 -from orchestrator.config.assignee import Assignee from orchestrator.forms import FormPage -from orchestrator.forms.validators import Accept from orchestrator.targets import Target from orchestrator.types import FormGenerator, State from orchestrator.types import SubscriptionLifecycle, UUIDstr from orchestrator.workflow import done, init, step, workflow -from orchestrator.workflow import inputstep from orchestrator.workflows.steps import resync, set_status from orchestrator.workflows.steps import store_process_subscription from orchestrator.workflows.utils import wrap_create_initial_input_form -from gso import settings from gso.products.product_types.device import DeviceVendor, DeviceInactive, \ DeviceProvisioning from gso.services import provisioning_proxy +from gso.services.provisioning_proxy import confirm_pp_results, \ + await_pp_results def initial_input_form_generator(product_name: str) -> FormGenerator: @@ -103,111 +101,47 @@ def initialize_subscription( @step('Provision device [DRY RUN]') def provision_device_dry(subscription: DeviceProvisioning, process_id: UUIDstr) -> State: - provisioning_proxy.provision_node( subscription, process_id ) - # import ansible_runner - # - # r = ansible_runner.run( - # private_data_dir='/opt/geant-gap-ansible', - # playbook='base_config.yaml', - # inventory=subscription.device.fqdn, - # extravars={ - # 'lo_ipv4_address': str(subscription.device.lo_ipv4_address), - # 'lo_ipv6_address': str(subscription.device.lo_ipv6_address), - # 'lo_iso_address': subscription.device.lo_iso_address, - # 'snmp_location': subscription.device.snmp_location, - # 'si_ipv4_network': str(subscription.device.si_ipv4_network), - # 'lt_ipv4_network': str(subscription.device.ias_lt_ipv4_network), - # 'lt_ipv6_network': str(subscription.device.ias_lt_ipv6_network), - # 'site_country_code': subscription.device.site_country_code, - # 'verb': 'deploy', - # }, - # ) - # out = r.stdout.read() - # out_splitted = out.splitlines() - # # if r.rc != 0: - # # raise ValueError('Ansible has failed') - # return {'dry_run_output': out_splitted, 'return_code': r.rc} - # provisioning_proxy.provision_node( - # node_subscription_params=subscription, - # dry_run=True) - # TODO: figure out what to return when we are suspending & waiting - # for the provisioning-proxy to call back - return {'subscription': subscription} -@inputstep('Verify dry run results', assignee=Assignee('SYSTEM')) -def confirm_step(subscription: DeviceProvisioning) -> State: - class ConfirmForm(FormPage): - confirm: Accept - - user_input = yield ConfirmForm - - return {'subscription': subscription, 'confirm': user_input} - - @step('Provision device [FOR REAL]') def provision_device_real(subscription: DeviceProvisioning, process_id: UUIDstr) -> State: - provisioning_proxy.provision_node( subscription, process_id, - False + False # No dry run this time, run it for real ) - # import ansible_runner - # - # r = ansible_runner.run( - # private_data_dir='/opt/geant-gap-ansible', - # playbook='base_config.yaml', - # inventory=subscription.device.fqdn, - # extravars={ - # 'lo_ipv4_address': str(subscription.device.lo_ipv4_address), - # 'lo_ipv6_address': str(subscription.device.lo_ipv6_address), - # 'lo_iso_address': subscription.device.lo_iso_address, - # 'snmp_location': subscription.device.snmp_location, - # 'si_ipv4_network': str(subscription.device.si_ipv4_network), - # 'lt_ipv4_network': str(subscription.device.ias_lt_ipv4_network), - # 'lt_ipv6_network': str(subscription.device.ias_lt_ipv6_network), - # 'site_country_code': subscription.device.site_country_code, - # 'verb': 'deploy', - # }, - # ) - # out = r.stdout.read() - # out_splitted = out.splitlines() - # - # return {'real_run_output': out_splitted, 'return_code': r.rc} - # provisioning_proxy.provision_node( - # node_subscription_params=subscription) - # TODO: figure out what to return when we are suspending & waiting - # for the provisioning-proxy to call back return {'subscription': subscription} @workflow( - 'Create Device', + 'Create device', initial_input_form=wrap_create_initial_input_form( initial_input_form_generator), target=Target.CREATE, ) def create_device(): return ( - init - >> create_subscription - >> store_process_subscription(Target.CREATE) - >> get_info_from_ipam - >> get_snmp_info - >> initialize_subscription - >> provision_device_dry - >> confirm_step - >> provision_device_real - >> set_status(SubscriptionLifecycle.ACTIVE) - >> resync - >> done + init + >> create_subscription + >> store_process_subscription(Target.CREATE) + >> get_info_from_ipam + >> get_snmp_info + >> initialize_subscription + >> provision_device_dry + >> await_pp_results + >> confirm_pp_results + >> provision_device_real + >> await_pp_results + >> confirm_pp_results + >> set_status(SubscriptionLifecycle.ACTIVE) + >> resync + >> done ) diff --git a/tox.ini b/tox.ini index 7973b25a04d0a5a1e560b7fe9fe03fea63887bc3..dd02509de947f75dab25e672ed28a3801dc3b28d 100644 --- a/tox.ini +++ b/tox.ini @@ -15,4 +15,3 @@ commands = # coverage report --fail-under 80 coverage report flake8 -