From c09b842d41437f1c2bb4a931182afbe7088d696f Mon Sep 17 00:00:00 2001 From: Patricia Reinoso Date: Tue, 13 Dec 2022 11:23:37 +0000 Subject: [PATCH] Bug 1234: OSM reports successful deployment when a charm relation fails Fix : raise exception when a problem occurs during charm relation addition. NS is marked as broken if charm relations cannot be added. Change-Id: Ie14f0c0f840e810a945bb603cf6d9360662103fd Signed-off-by: Patricia Reinoso --- osm_lcm/ns.py | 20 ++-- osm_lcm/tests/test_db_descriptors.py | 6 +- osm_lcm/tests/test_ns.py | 135 +++++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 19 deletions(-) diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 3e6bafc..b7df0b6 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -2044,13 +2044,16 @@ class NsLcm(LcmBase): ) # add relations for this VCA (wait for other peers related with this VCA) - await self._add_vca_relations( + is_relation_added = await self._add_vca_relations( logging_text=logging_text, nsr_id=nsr_id, vca_type=vca_type, vca_index=vca_index, ) + if not is_relation_added: + raise LcmException("Relations could not be added to VCA.") + # if SSH access is required, then get execution environment SSH public # if native charm we have waited already to VM be UP if vca_type in ("k8s_proxy_charm", "lxc_proxy_charm", "helm", "helm-v3"): @@ -2245,7 +2248,7 @@ class NsLcm(LcmBase): self._write_configuration_status( nsr_id=nsr_id, vca_index=vca_index, status="BROKEN" ) - raise LcmException("{} {}".format(step, e)) from e + raise LcmException("{}. {}".format(step, e)) from e def _write_ns_status( self, @@ -3213,11 +3216,14 @@ class NsLcm(LcmBase): requirer_vca_id, relation.requirer.endpoint, ) - await self.vca_map[vca_type].add_relation( - provider=provider_relation_endpoint, - requirer=requirer_relation_endpoint, - ) - # remove entry from relations list + try: + await self.vca_map[vca_type].add_relation( + provider=provider_relation_endpoint, + requirer=requirer_relation_endpoint, + ) + except N2VCException as exception: + self.logger.error(exception) + raise LcmException(exception) return True return False diff --git a/osm_lcm/tests/test_db_descriptors.py b/osm_lcm/tests/test_db_descriptors.py index d02b011..7f47231 100644 --- a/osm_lcm/tests/test_db_descriptors.py +++ b/osm_lcm/tests/test_db_descriptors.py @@ -529,10 +529,11 @@ db_nsrs_text = """ type: lxc_proxy_charm vnfd_id: hackfest3charmed-vnf - application: alf-c-ab - ee_id: f48163a6-c807-47bc-9682-f72caef5af85.alf-c-ab + ee_id: "model_name.application_name.machine_id" + ee_descriptor_id: f48163a6-c807-47bc-9682-f72caef5af85.alf-c-ab needed_terminate: True detailed-status: Ready! - member-vnf-index: '2' + member-vnf-index: hackfest_vnf1 model: f48163a6-c807-47bc-9682-f72caef5af85 operational-status: active primitive_id: null @@ -543,6 +544,7 @@ db_nsrs_text = """ vdu_name: null type: lxc_proxy_charm vnfd_id: hackfest3charmed-vnf + config_sw_installed: true VCA-model-name: f48163a6-c807-47bc-9682-f72caef5af85 modified: 1566823354.3716335 nsState: INSTANTIATED diff --git a/osm_lcm/tests/test_ns.py b/osm_lcm/tests/test_ns.py index 1082648..f44dbf6 100644 --- a/osm_lcm/tests/test_ns.py +++ b/osm_lcm/tests/test_ns.py @@ -21,6 +21,7 @@ import asyncio from copy import deepcopy import yaml import copy +from n2vc.exceptions import N2VCException from os import getenv from osm_lcm import ns from osm_common.msgkafka import MsgKafka @@ -30,6 +31,7 @@ from osm_lcm.lcm_utils import TaskRegistry from osm_lcm.ng_ro import NgRoClient from osm_lcm.data_utils.database.database import Database from osm_lcm.data_utils.filesystem.filesystem import Filesystem +from osm_lcm.data_utils.vca import Relation, EERelation from osm_lcm.data_utils.vnfd import find_software_version from osm_lcm.lcm_utils import check_juju_bundle_existence, get_charm_artifact_path from osm_lcm.lcm_utils import LcmException @@ -185,8 +187,17 @@ class TestMyNS(asynctest.TestCase): return str(uuid4()) async def setUp(self): - - # Mock DB + self.mock_db() + self.mock_kafka() + self.mock_filesystem() + self.mock_task_registry() + self.mock_vca_k8s() + self.create_nslcm_class() + self.mock_logging() + self.mock_vca_n2vc() + self.mock_ro() + + def mock_db(self): if not getenv("OSMLCMTEST_DB_NOMOCK"): # Cleanup singleton Database instance Database.instance = None @@ -213,10 +224,10 @@ class TestMyNS(asynctest.TestCase): self.db.create_list("vnfrs", yaml.safe_load(descriptors.db_vnfrs_text)) self.db_vim_accounts = yaml.safe_load(descriptors.db_vim_accounts_text) - # Mock kafka + def mock_kafka(self): self.msg = asynctest.Mock(MsgKafka()) - # Mock filesystem + def mock_filesystem(self): if not getenv("OSMLCMTEST_FS_NOMOCK"): self.fs = asynctest.Mock( Filesystem({"storage": {"driver": "local", "path": "/"}}).instance.fs @@ -228,13 +239,13 @@ class TestMyNS(asynctest.TestCase): # self.fs.file_open.return_value.__enter__.return_value = asynctest.MagicMock() # called on a python "with" # self.fs.file_open.return_value.__enter__.return_value.read.return_value = "" # empty file - # Mock TaskRegistry + def mock_task_registry(self): self.lcm_tasks = asynctest.Mock(TaskRegistry()) self.lcm_tasks.lock_HA.return_value = True self.lcm_tasks.waitfor_related_HA.return_value = None self.lcm_tasks.lookfor_related.return_value = ("", []) - # Mock VCA - K8s + def mock_vca_k8s(self): if not getenv("OSMLCMTEST_VCA_K8s_NOMOCK"): ns.K8sJujuConnector = asynctest.MagicMock(ns.K8sJujuConnector) ns.K8sHelmConnector = asynctest.MagicMock(ns.K8sHelmConnector) @@ -244,17 +255,17 @@ class TestMyNS(asynctest.TestCase): ns.N2VCJujuConnector = asynctest.MagicMock(ns.N2VCJujuConnector) ns.LCMHelmConn = asynctest.MagicMock(ns.LCMHelmConn) - # Create NsLCM class + def create_nslcm_class(self): self.my_ns = ns.NsLcm(self.msg, self.lcm_tasks, lcm_config, self.loop) self.my_ns.fs = self.fs self.my_ns.db = self.db self.my_ns._wait_dependent_n2vc = asynctest.CoroutineMock() - # Mock logging + def mock_logging(self): if not getenv("OSMLCMTEST_LOGGING_NOMOCK"): self.my_ns.logger = asynctest.Mock(self.my_ns.logger) - # Mock VCA - N2VC + def mock_vca_n2vc(self): if not getenv("OSMLCMTEST_VCA_NOMOCK"): pub_key = getenv("OSMLCMTEST_NS_PUBKEY", "ssh-rsa test-pub-key t@osm.com") # self.my_ns.n2vc = asynctest.Mock(N2VC()) @@ -298,8 +309,11 @@ class TestMyNS(asynctest.TestCase): self.my_ns.n2vc.delete_namespace = asynctest.CoroutineMock( return_value=None ) + self.my_ns.n2vc.register_execution_environment = asynctest.CoroutineMock( + return_value="model-name.application-name.k8s" + ) - # Mock RO + def mock_ro(self): if not getenv("OSMLCMTEST_RO_NOMOCK"): self.my_ns.RO = asynctest.Mock( NgRoClient(self.loop, **lcm_config.RO.to_dict()) @@ -1529,5 +1543,106 @@ class TestMyNS(asynctest.TestCase): self.assertEqual(result, expected_result, "Wrong charm artifact path") +class TestInstantiateN2VC(TestMyNS): + async def setUp(self): + await super().setUp() + self.db_nsr = yaml.safe_load(descriptors.db_nsrs_text)[0] + self.db_vnfr = yaml.safe_load(descriptors.db_vnfrs_text)[0] + self.vca_index = 1 + self.my_ns._write_configuration_status = Mock() + + async def call_instantiate_N2VC(self): + logging_text = "N2VC Instantiation" + config_descriptor = {"config-access": {"ssh-access": {"default-user": "admin"}}} + base_folder = {"pkg-dir": "", "folder": "~"} + stage = ["Stage", "Message"] + + await self.my_ns.instantiate_N2VC( + logging_text=logging_text, + vca_index=self.vca_index, + nsi_id="nsi_id", + db_nsr=self.db_nsr, + db_vnfr=self.db_vnfr, + vdu_id=None, + kdu_name=None, + vdu_index=None, + config_descriptor=config_descriptor, + deploy_params={}, + base_folder=base_folder, + nslcmop_id="nslcmop_id", + stage=stage, + vca_type="native_charm", + vca_name="vca_name", + ee_config_descriptor={}, + ) + + def check_config_status(self, expected_status): + self.my_ns._write_configuration_status.assert_called_with( + nsr_id=self.db_nsr["_id"], vca_index=self.vca_index, status=expected_status + ) + + async def call_ns_add_relation(self): + ee_relation = EERelation( + { + "nsr-id": self.db_nsr["_id"], + "vdu-profile-id": None, + "kdu-resource-profile-id": None, + "vnf-profile-id": "hackfest_vnf1", + "execution-environment-ref": "f48163a6-c807-47bc-9682-f72caef5af85.alf-c-ab", + "endpoint": "127.0.0.1", + } + ) + + relation = Relation("relation-name", ee_relation, ee_relation) + cached_vnfrs = {"hackfest_vnf1": self.db_vnfr} + + return await self.my_ns._add_relation( + relation=relation, + vca_type="native_charm", + db_nsr=self.db_nsr, + cached_vnfds={}, + cached_vnfrs=cached_vnfrs, + ) + + async def test_add_relation_ok(self): + await self.call_instantiate_N2VC() + self.check_config_status(expected_status="READY") + + async def test_add_relation_returns_false_raises_exception(self): + self.my_ns._add_vca_relations = asynctest.CoroutineMock(return_value=False) + + with self.assertRaises(LcmException) as exception: + await self.call_instantiate_N2VC() + + exception_msg = "Relations could not be added to VCA." + self.assertTrue(exception_msg in str(exception.exception)) + self.check_config_status(expected_status="BROKEN") + + async def test_add_relation_raises_lcm_exception(self): + exception_msg = "Relations FAILED" + self.my_ns._add_vca_relations = asynctest.CoroutineMock( + side_effect=LcmException(exception_msg) + ) + + with self.assertRaises(LcmException) as exception: + await self.call_instantiate_N2VC() + + self.assertTrue(exception_msg in str(exception.exception)) + self.check_config_status(expected_status="BROKEN") + + async def test_n2vc_add_relation_fails_raises_exception(self): + exception_msg = "N2VC failed to add relations" + self.my_ns.n2vc.add_relation = asynctest.CoroutineMock( + side_effect=N2VCException(exception_msg) + ) + with self.assertRaises(LcmException) as exception: + await self.call_ns_add_relation() + self.assertTrue(exception_msg in str(exception.exception)) + + async def test_n2vc_add_relation_ok_returns_true(self): + self.my_ns.n2vc.add_relation = asynctest.CoroutineMock(return_value=None) + self.assertTrue(await self.call_ns_add_relation()) + + if __name__ == "__main__": asynctest.main() -- 2.25.1