From 1fa7b6d02c5cffc36665aad34be2c07c01ebebf0 Mon Sep 17 00:00:00 2001 From: Patricia Reinoso Date: Wed, 5 Apr 2023 15:27:20 +0000 Subject: [PATCH] Instantiate VDU Worfklow and Activities Using Juju Change-Id: I5c0ab16e348f6f66573085269b8729bbc8854f15 Signed-off-by: Patricia Reinoso --- osm_lcm/nglcm.py | 13 ++-- osm_lcm/temporal/vdu_workflows.py | 77 ++++++++++++++++++++++ osm_lcm/temporal/vim_activities.py | 100 ++++++++++++++++++++++++++++- 3 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 osm_lcm/temporal/vdu_workflows.py diff --git a/osm_lcm/nglcm.py b/osm_lcm/nglcm.py index 0e8811b..30e4b36 100644 --- a/osm_lcm/nglcm.py +++ b/osm_lcm/nglcm.py @@ -38,6 +38,7 @@ from osm_lcm.temporal.vim_workflows import ( VimDeleteWorkflow, VimUpdateWorkflow, ) +from osm_lcm.temporal.vdu_workflows import VduInstantiateWorkflow from temporalio.client import Client from temporalio.worker import Worker @@ -127,7 +128,7 @@ class NGLcm: f"{self.main_config.temporal.host}:{str(self.main_config.temporal.port)}" ) client = await Client.connect(temporal_api) - data_activity_instance = VimDbActivity(self.db) + vim_data_activity_instance = VimDbActivity(self.db) paas_connector_instance = JujuPaasConnector(self.db) nslcm_activity_instance = NsLcmActivity(self.db) @@ -136,14 +137,18 @@ class NGLcm: VimCreateWorkflow, VimDeleteWorkflow, VimUpdateWorkflow, + VduInstantiateWorkflow, ] activities = [ - data_activity_instance.update_vim_operation_state, - data_activity_instance.update_vim_state, - data_activity_instance.delete_vim_record, + vim_data_activity_instance.update_vim_operation_state, + vim_data_activity_instance.update_vim_state, + vim_data_activity_instance.delete_vim_record, nslcm_activity_instance.update_ns_lcm_operation_state, nslcm_activity_instance.no_op, paas_connector_instance.test_vim_connectivity, + paas_connector_instance.create_model_if_doesnt_exist, + paas_connector_instance.deploy_charm, + paas_connector_instance.check_charm_status, ] # Check if we are running under a debugger diff --git a/osm_lcm/temporal/vdu_workflows.py b/osm_lcm/temporal/vdu_workflows.py new file mode 100644 index 0000000..97a7b2a --- /dev/null +++ b/osm_lcm/temporal/vdu_workflows.py @@ -0,0 +1,77 @@ +####################################################################################### +# Copyright ETSI Contributors and Others. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from datetime import timedelta +import logging +from temporalio import workflow +from temporalio.common import RetryPolicy + +from osm_common.dataclasses.temporal_dataclasses import ( + VduInstantiateInput, + CreateModelInput, +) +from osm_common.temporal_constants import ( + WORKFLOW_VDU_INSTANTIATE, + ACTIVITY_CREATE_MODEL_IF_DOESNT_EXIST, + ACTIVITY_DEPLOY_CHARM, + ACTIVITY_CHECK_CHARM_STATUS, + LCM_TASK_QUEUE, +) + +_SANDBOXED = False +retry_policy = RetryPolicy(maximum_attempts=3) +default_schedule_to_close_timeout = timedelta(minutes=10) + +workflow.logger = logging.getLogger("lcm.temporal.vdu_workflows") + + +@workflow.defn(name=WORKFLOW_VDU_INSTANTIATE, sandboxed=_SANDBOXED) +class VduInstantiateWorkflow: + """Instantiate a VDU""" + + @workflow.run + async def run(self, input: VduInstantiateInput) -> None: + try: + await workflow.execute_activity( + activity=ACTIVITY_CREATE_MODEL_IF_DOESNT_EXIST, + arg=CreateModelInput(input.vim_uuid, input.model_name), + activity_id=f"{ACTIVITY_CREATE_MODEL_IF_DOESNT_EXIST}-{input.vim_uuid}", + task_queue=LCM_TASK_QUEUE, + schedule_to_close_timeout=default_schedule_to_close_timeout, + retry_policy=retry_policy, + ) + + await workflow.execute_activity( + activity=ACTIVITY_DEPLOY_CHARM, + arg=input, + activity_id=f"{ACTIVITY_DEPLOY_CHARM}-{input.vim_uuid}", + task_queue=LCM_TASK_QUEUE, + schedule_to_close_timeout=default_schedule_to_close_timeout, + retry_policy=retry_policy, + ) + + await workflow.execute_activity( + activity=ACTIVITY_CHECK_CHARM_STATUS, + arg=input, + activity_id=f"{ACTIVITY_CHECK_CHARM_STATUS}-{input.vim_uuid}", + task_queue=LCM_TASK_QUEUE, + schedule_to_close_timeout=default_schedule_to_close_timeout, + retry_policy=retry_policy, + ) + + except Exception as e: + workflow.logger.error(f"{WORKFLOW_VDU_INSTANTIATE} failed with {str(e)}") + raise e diff --git a/osm_lcm/temporal/vim_activities.py b/osm_lcm/temporal/vim_activities.py index 6307704..c84713a 100644 --- a/osm_lcm/temporal/vim_activities.py +++ b/osm_lcm/temporal/vim_activities.py @@ -23,12 +23,17 @@ from osm_common.temporal_constants import ( ACTIVITY_TEST_VIM_CONNECTIVITY, ACTIVITY_UPDATE_VIM_OPERATION_STATE, ACTIVITY_UPDATE_VIM_STATE, + ACTIVITY_CREATE_MODEL_IF_DOESNT_EXIST, + ACTIVITY_DEPLOY_CHARM, + ACTIVITY_CHECK_CHARM_STATUS, ) from osm_common.dataclasses.temporal_dataclasses import ( DeleteVimInput, TestVimConnectivityInput, UpdateVimOperationStateInput, UpdateVimStateInput, + CreateModelInput, + VduInstantiateInput, ) activity.logger = logging.getLogger("lcm.temporal.vim_activities") @@ -79,6 +84,10 @@ class JujuPaasConnector: endpoint, username, password, cacert, cloud_name, cloud_credentials ) + def _get_libjuju(self, vim_uuid): + connection_info = self._get_connection_info(vim_uuid) + return Libjuju(connection_info) + @activity.defn(name=ACTIVITY_TEST_VIM_CONNECTIVITY) async def test_vim_connectivity( self, test_connectivity_input: TestVimConnectivityInput @@ -108,14 +117,101 @@ class JujuPaasConnector: vim_id = test_connectivity_input.vim_uuid controller = None try: - connection_info = self._get_connection_info(vim_id) - libjuju = Libjuju(connection_info) + libjuju = self._get_libjuju(vim_id) controller = await libjuju.get_controller() message = f"Connection to juju controller succeeded for {vim_id}" activity.logger.info(message) finally: await libjuju.disconnect_controller(controller) + @activity.defn(name=ACTIVITY_CREATE_MODEL_IF_DOESNT_EXIST) + async def create_model_if_doesnt_exist( + self, create_model_input: CreateModelInput + ) -> None: + """Connects to Juju Controller. Create a new model if model_name does not exist + + Collaborators: + DB Read: vim_accounts + Juju Controller: Connect and create model. + + Raises (Retryable): + ApplicationError If Juju controller is not reachable + + Activity Lifecycle: + This activity should complete relatively quickly (in a few seconds). + However, it would be reasonable to wait more than 72 seconds (network timeout) + incase there are network issues. + + This activity will not report a heartbeat due to its + short-running nature. + + It is recommended, although not necessary to implement a + back-off strategy for this activity, as it will naturally block + and wait on each connection attempt. + """ + model_name = create_model_input.model_name + libjuju = self._get_libjuju(create_model_input.vim_uuid) + await libjuju.add_model(model_name) + + @activity.defn(name=ACTIVITY_DEPLOY_CHARM) + async def deploy_charm(self, deploy_charm_input: VduInstantiateInput) -> None: + """Deploys a charm. + + Collaborators: + DB Read: vim_accounts + Juju Controller: Connect and deploy charm + + Raises (Retryable): + ApplicationError If Juju controller is not reachable + + Activity Lifecycle: + This activity should complete relatively quickly (in a few seconds). + However, it would be reasonable to wait more than 72 seconds (network timeout) + incase there are network issues. + + This activity will not report a heartbeat due to its + short-running nature. + + It is recommended, although not necessary to implement a + back-off strategy for this activity, as it will naturally block + and wait on each connection attempt. + """ + model_name = deploy_charm_input.model_name + libjuju = self._get_libjuju(deploy_charm_input.vim_uuid) + charm_info = deploy_charm_input.charm_info + await libjuju.deploy_charm( + application_name=charm_info.app_name, + path=charm_info.entity_url, + model_name=model_name, + channel=charm_info.channel, + ) + + @activity.defn(name=ACTIVITY_CHECK_CHARM_STATUS) + async def check_charm_status(self, check_charm_status: VduInstantiateInput) -> None: + """Validates the credentials by attempting to connect to the given Juju Controller. + + Collaborators: + DB Read: vim_accounts + Juju Controller: Connect to controller and check charm status. + + Raises (Retryable): + ApplicationError If any of password, cacert, cloud_credentials is invalid + or Juju controller is not reachable + + Activity Lifecycle: + This activity should complete relatively quickly (in a few seconds). + However, it would be reasonable to wait more than 72 seconds (network timeout) + incase there are network issues. + + This activity will not report a heartbeat due to its + short-running nature. + + It is recommended, although not necessary to implement a + back-off strategy for this activity, as it will naturally block + and wait on each connection attempt. + """ + pass + class VimDbActivity: """Perform Database operations for VIM accounts. -- 2.25.1