From 0c202d2c37384196aa80fc8d22d67e9a2b6181ca Mon Sep 17 00:00:00 2001 From: Mark Beierl Date: Thu, 6 Apr 2023 13:58:31 +0000 Subject: [PATCH] Reorganising Change-Id: I64066be7b62e107d2126232b481cd5c1a46ef3ec Signed-off-by: Mark Beierl --- osm_lcm/nglcm.py | 3 +- osm_lcm/temporal/juju_paas_activities.py | 204 +++++++++++++++++++++++ osm_lcm/temporal/vdu_workflows.py | 22 +-- osm_lcm/temporal/vim_activities.py | 191 +-------------------- 4 files changed, 215 insertions(+), 205 deletions(-) create mode 100644 osm_lcm/temporal/juju_paas_activities.py diff --git a/osm_lcm/nglcm.py b/osm_lcm/nglcm.py index 30e4b36..d69121c 100644 --- a/osm_lcm/nglcm.py +++ b/osm_lcm/nglcm.py @@ -32,7 +32,8 @@ from osm_lcm.lcm_utils import LcmException from os import path from osm_lcm.temporal.lcm_activities import NsLcmActivity from osm_lcm.temporal.lcm_workflows import NsNoOpWorkflow -from osm_lcm.temporal.vim_activities import VimDbActivity, JujuPaasConnector +from osm_lcm.temporal.vim_activities import VimDbActivity +from osm_lcm.temporal.juju_paas_activities import JujuPaasConnector from osm_lcm.temporal.vim_workflows import ( VimCreateWorkflow, VimDeleteWorkflow, diff --git a/osm_lcm/temporal/juju_paas_activities.py b/osm_lcm/temporal/juju_paas_activities.py new file mode 100644 index 0000000..74d4763 --- /dev/null +++ b/osm_lcm/temporal/juju_paas_activities.py @@ -0,0 +1,204 @@ +####################################################################################### +# 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. +import logging +from temporalio import activity +from n2vc.temporal_libjuju import ConnectionInfo, Libjuju +from osm_common.temporal_constants import ( + ACTIVITY_TEST_VIM_CONNECTIVITY, + ACTIVITY_CREATE_MODEL_IF_DOESNT_EXIST, + ACTIVITY_DEPLOY_CHARM, + ACTIVITY_CHECK_CHARM_STATUS, +) +from osm_common.dataclasses.temporal_dataclasses import ( + TestVimConnectivityInput, + CreateModelInput, + VduInstantiateInput, +) + + +class JujuPaasConnector: + """Handles Juju Controller operations. + + Args: + db (object): Data Access Object + """ + + def __init__(self, db): + self.db = db + self.logger = logging.getLogger(f"lcm.act.{self.__class__.__name__}") + + def _decrypt_password(self, vim_content: dict) -> str: + """Decrypt a password. + vim_content (dict): VIM details as a dictionary + + Returns: + plain text password (str) + """ + return self.db.decrypt( + vim_content["vim_password"], + schema_version=vim_content["schema_version"], + salt=vim_content["_id"], + ) + + def _get_connection_info(self, vim_id: str) -> ConnectionInfo: + """Get VIM details from database using vim_id and returns + the Connection Info to connect Juju Controller. + + Args: + vim_id (str): VIM ID + + Returns: + ConnectionInfo (object) + """ + vim_content = self.db.get_one("vim_accounts", {"_id": vim_id}) + endpoint = vim_content["vim_url"] + username = vim_content["vim_user"] + vim_config = vim_content["config"] + cacert = vim_config["ca_cert_content"] + cloud_name = vim_config["cloud"] + cloud_credentials = vim_config["cloud_credentials"] + password = self._decrypt_password(vim_content) + return ConnectionInfo( + 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 + ) -> None: + """Validates the credentials by attempting to connect to the given Juju Controller. + + Collaborators: + DB Read: vim_accounts + Juju Controller: Connect only + + 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. + """ + vim_id = test_connectivity_input.vim_uuid + controller = None + try: + libjuju = self._get_libjuju(vim_id) + controller = await libjuju.get_controller() + message = f"Connection to juju controller succeeded for {vim_id}" + self.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 diff --git a/osm_lcm/temporal/vdu_workflows.py b/osm_lcm/temporal/vdu_workflows.py index 97a7b2a..ceb19c3 100644 --- a/osm_lcm/temporal/vdu_workflows.py +++ b/osm_lcm/temporal/vdu_workflows.py @@ -19,13 +19,9 @@ import logging from temporalio import workflow from temporalio.common import RetryPolicy -from osm_common.dataclasses.temporal_dataclasses import ( - VduInstantiateInput, - CreateModelInput, -) +from osm_common.dataclasses.temporal_dataclasses import VduInstantiateInput 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, @@ -35,25 +31,17 @@ _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""" + def __init__(self): + self.logger = logging.getLogger(f"lcm.wfl.{self.__class__.__name__}") + @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, @@ -73,5 +61,5 @@ class VduInstantiateWorkflow: ) except Exception as e: - workflow.logger.error(f"{WORKFLOW_VDU_INSTANTIATE} failed with {str(e)}") + self.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 c84713a..4259fa8 100644 --- a/osm_lcm/temporal/vim_activities.py +++ b/osm_lcm/temporal/vim_activities.py @@ -17,201 +17,17 @@ import logging from temporalio import activity from time import time -from n2vc.temporal_libjuju import ConnectionInfo, Libjuju from osm_common.temporal_constants import ( ACTIVITY_DELETE_VIM, - 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") - - -class JujuPaasConnector: - """Handles Juju Controller operations. - - Args: - db (object): Data Access Object - """ - - def __init__(self, db): - self.db = db - - def _decrypt_password(self, vim_content: dict) -> str: - """Decrypt a password. - vim_content (dict): VIM details as a dictionary - - Returns: - plain text password (str) - """ - return self.db.decrypt( - vim_content["vim_password"], - schema_version=vim_content["schema_version"], - salt=vim_content["_id"], - ) - - def _get_connection_info(self, vim_id: str) -> ConnectionInfo: - """Get VIM details from database using vim_id and returns - the Connection Info to connect Juju Controller. - - Args: - vim_id (str): VIM ID - - Returns: - ConnectionInfo (object) - """ - vim_content = self.db.get_one("vim_accounts", {"_id": vim_id}) - endpoint = vim_content["vim_url"] - username = vim_content["vim_user"] - vim_config = vim_content["config"] - cacert = vim_config["ca_cert_content"] - cloud_name = vim_config["cloud"] - cloud_credentials = vim_config["cloud_credentials"] - password = self._decrypt_password(vim_content) - return ConnectionInfo( - 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 - ) -> None: - """Validates the credentials by attempting to connect to the given Juju Controller. - - Collaborators: - DB Read: vim_accounts - Juju Controller: Connect only - - 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. - """ - vim_id = test_connectivity_input.vim_uuid - controller = None - try: - 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. @@ -222,6 +38,7 @@ class VimDbActivity: def __init__(self, db): self.db = db + self.logger = logging.getLogger(f"lcm.act.{self.__class__.__name__}") @activity.defn(name=ACTIVITY_UPDATE_VIM_STATE) async def update_vim_state(self, data: UpdateVimStateInput) -> None: @@ -250,7 +67,7 @@ class VimDbActivity: } self.db.set_one("vim_accounts", {"_id": data.vim_uuid}, update_vim_state) - activity.logger.debug( + self.logger.debug( f"Updated VIM {data.vim_uuid} to {data.operational_state.name}" ) @@ -282,7 +99,7 @@ class VimDbActivity: } self.db.set_one("vim_accounts", {"_id": data.vim_uuid}, update_operation_state) - activity.logger.debug( + self.logger.debug( f"Updated VIM {data.vim_uuid} OP ID {data.op_id} to {data.op_state.name}" ) @@ -306,4 +123,4 @@ class VimDbActivity: """ self.db.del_one("vim_accounts", {"_id": data.vim_uuid}) - activity.logger.debug(f"Removed VIM {data.vim_uuid}") + self.logger.debug(f"Removed VIM {data.vim_uuid}") -- 2.25.1