From 1dda84cafdeeb4a6affb443e65102a0c8149ec5c Mon Sep 17 00:00:00 2001 From: aticig Date: Sat, 10 Sep 2022 01:56:58 +0300 Subject: [PATCH] Fix Bug 2199 Fixing ns update operation for KNF instances This patch enables ns update operations on KNF instances which are deployed with Helm chart and using .charm zipped files. Change-Id: I6c598868ae81243ef598ae5b7df88e26a5b870c0 Signed-off-by: aticig --- osm_lcm/lcm_utils.py | 84 ++- osm_lcm/ns.py | 10 +- osm_lcm/tests/test_lcm_utils.py | 201 ++++- osm_lcm/tests/test_ns.py | 1260 ++++++++++++++----------------- 4 files changed, 826 insertions(+), 729 deletions(-) diff --git a/osm_lcm/lcm_utils.py b/osm_lcm/lcm_utils.py index 7dac350..956e44f 100644 --- a/osm_lcm/lcm_utils.py +++ b/osm_lcm/lcm_utils.py @@ -19,6 +19,7 @@ import asyncio import checksumdir from collections import OrderedDict +import hashlib import os import shutil import traceback @@ -200,6 +201,54 @@ class LcmBase: # except DbException as e: # self.logger.error("Updating {} _id={} with '{}'. Error: {}".format(item, _id, _desc, e)) + @staticmethod + def calculate_charm_hash(zipped_file): + """Calculate the hash of charm files which ends with .charm + + Args: + zipped_file (str): Existing charm package full path + + Returns: + hex digest (str): The hash of the charm file + """ + filehash = hashlib.md5() + with open(zipped_file, mode="rb") as file: + contents = file.read() + filehash.update(contents) + return filehash.hexdigest() + + @staticmethod + def compare_charm_hash(current_charm, target_charm): + """Compare the existing charm and the target charm if the charms + are given as zip files ends with .charm + + Args: + current_charm (str): Existing charm package full path + target_charm (str): Target charm package full path + + Returns: + True/False (bool): if charm has changed it returns True + """ + return LcmBase.calculate_charm_hash( + current_charm + ) != LcmBase.calculate_charm_hash(target_charm) + + @staticmethod + def compare_charmdir_hash(current_charm_dir, target_charm_dir): + """Compare the existing charm and the target charm if the charms + are given as directories + + Args: + current_charm_dir (str): Existing charm package directory path + target_charm_dir (str): Target charm package directory path + + Returns: + True/False (bool): if charm has changed it returns True + """ + return checksumdir.dirhash(current_charm_dir) != checksumdir.dirhash( + target_charm_dir + ) + def check_charm_hash_changed( self, current_charm_path: str, target_charm_path: str ) -> bool: @@ -214,25 +263,30 @@ class LcmBase: True/False (bool): if charm has changed it returns True """ - # Check if the charm artifacts are available - if os.path.exists(self.fs.path + current_charm_path) and os.path.exists( - self.fs.path + target_charm_path - ): - # Compare the hash of charm folders - if checksumdir.dirhash( - self.fs.path + current_charm_path - ) != checksumdir.dirhash(self.fs.path + target_charm_path): + try: + # Check if the charm artifacts are available + current_charm = self.fs.path + current_charm_path + target_charm = self.fs.path + target_charm_path - return True + if os.path.exists(current_charm) and os.path.exists(target_charm): - return False + # Compare the hash of .charm files + if current_charm.endswith(".charm"): + return LcmBase.compare_charm_hash(current_charm, target_charm) - else: - raise LcmException( - "Charm artifact {} does not exist in the VNF Package".format( - self.fs.path + target_charm_path + # Compare the hash of charm folders + return LcmBase.compare_charmdir_hash(current_charm, target_charm) + + else: + raise LcmException( + "Charm artifact {} does not exist in the VNF Package".format( + self.fs.path + target_charm_path + ) ) - ) + except (IOError, OSError, TypeError) as error: + self.logger.debug(traceback.format_exc()) + self.logger.error(f"{error} occured while checking the charm hashes") + raise LcmException(error) @staticmethod def get_charm_name(charm_metadata_file: str) -> str: diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 9885f40..968e1f3 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -5905,8 +5905,16 @@ class NsLcm(LcmBase): ee_id = vca_deployed.get("ee_id") step = "Getting descriptor config" + if current_vnfd.get("kdu"): + + search_key = "kdu_name" + else: + search_key = "vnfd_id" + + entity_id = vca_deployed.get(search_key) + descriptor_config = get_configuration( - current_vnfd, current_vnfd["id"] + current_vnfd, entity_id ) if "execution-environment-list" in descriptor_config: diff --git a/osm_lcm/tests/test_lcm_utils.py b/osm_lcm/tests/test_lcm_utils.py index 6067b2d..71ebab1 100644 --- a/osm_lcm/tests/test_lcm_utils.py +++ b/osm_lcm/tests/test_lcm_utils.py @@ -16,7 +16,8 @@ # contact: alfonso.tiernosepulveda@telefonica.com ## import logging -from unittest.mock import Mock, patch, MagicMock +import tempfile +from unittest.mock import Mock, patch, MagicMock, mock_open from unittest import TestCase from osm_common.msgkafka import MsgKafka @@ -29,6 +30,10 @@ import yaml from zipfile import BadZipfile +tmpdir = tempfile.mkdtemp()[1] +tmpfile = tempfile.mkstemp()[1] + + class TestLcmBase(TestCase): test_nsr_id = "f48163a6-c807-47bc-9682-f72caef5af85" @@ -52,6 +57,7 @@ class TestLcmBase(TestCase): self.my_ns = LcmBase(self.msg, self.logger) self.my_ns.fs = self.fs self.my_ns.db = self.db + self.hexdigest = "031edd7d41651593c5fe5c006f" def test_get_charm_name_successfully(self): instance = self.my_ns @@ -245,3 +251,196 @@ class TestLcmBase(TestCase): self.assertEqual(mock_charm_path.call_count, 1) self.assertEqual(mock_metadata_file.call_count, 1) self.assertEqual(mock_charm_name.call_count, 1) + + @patch("builtins.open", new_callable=mock_open(read_data="charm content")) + @patch("osm_lcm.lcm_utils.hashlib") + def test_calculate_charm_hash_sucessfully(self, mock_hashlib, mocking_open): + """Calculate charm hash successfully.""" + charm = tmpfile + hexdigest = self.hexdigest + mock_file_hash = MagicMock() + mock_hashlib.md5.return_value = mock_file_hash + mock_file_hash.hexdigest.return_value = hexdigest + result = LcmBase.calculate_charm_hash(charm) + self.assertEqual(result, hexdigest) + self.assertEqual(mocking_open.call_count, 1) + self.assertEqual(mock_file_hash.update.call_count, 1) + self.assertEqual(mock_file_hash.hexdigest.call_count, 1) + self.assertEqual(mock_hashlib.md5.call_count, 1) + + @patch("builtins.open", new_callable=mock_open(read_data="charm content")) + @patch("osm_lcm.lcm_utils.hashlib") + def test_calculate_charm_hash_open_raises(self, mock_hashlib, mocking_open): + """builtins.open raises exception.""" + charm = tmpfile + hexdigest = self.hexdigest + mock_file_hash = MagicMock() + mock_hashlib.md5.return_value = mock_file_hash + mock_file_hash.hexdigest.return_value = hexdigest + mocking_open.side_effect = IOError + with self.assertRaises(IOError): + LcmBase.calculate_charm_hash(charm) + self.assertEqual(mocking_open.call_count, 1) + mock_file_hash.update.assert_not_called() + mock_file_hash.hexdigest.assert_not_called() + self.assertEqual(mock_hashlib.md5.call_count, 1) + + @patch("builtins.open", new_callable=mock_open(read_data="charm content")) + @patch("osm_lcm.lcm_utils.hashlib") + def test_calculate_charm_filehash_update_raises(self, mock_hashlib, mocking_open): + """Filehash update raises exception.""" + charm = tmpfile + hexdigest = self.hexdigest + mock_file_hash = MagicMock() + mock_file_hash.update.side_effect = Exception + mock_hashlib.md5.return_value = mock_file_hash + mock_file_hash.hexdigest.return_value = hexdigest + with self.assertRaises(Exception): + LcmBase.calculate_charm_hash(charm) + self.assertEqual(mocking_open.call_count, 1) + self.assertEqual(mock_file_hash.update.call_count, 1) + mock_file_hash.hexdigest.assert_not_called() + self.assertEqual(mock_hashlib.md5.call_count, 1) + + @patch("builtins.open", new_callable=mock_open(read_data="charm content")) + @patch("osm_lcm.lcm_utils.hashlib") + def test_calculate_charm_filehash_hexdigest_raises( + self, mock_hashlib, mocking_open + ): + """Filehash hexdigest raises exception.""" + charm = tmpfile + mock_file_hash = MagicMock() + mock_hashlib.md5.return_value = mock_file_hash + mock_file_hash.hexdigest.side_effect = Exception + with self.assertRaises(Exception): + LcmBase.calculate_charm_hash(charm) + self.assertEqual(mocking_open.call_count, 1) + self.assertEqual(mock_file_hash.update.call_count, 1) + mock_file_hash.hexdigest.assert_called_once() + self.assertEqual(mock_hashlib.md5.call_count, 1) + mock_file_hash.update.assert_called_once() + + @patch("builtins.open", new_callable=mock_open(read_data="charm content")) + @patch("osm_lcm.lcm_utils.hashlib") + def test_calculate_charm_filehash_hashlib_md5_raises( + self, mock_hashlib, mocking_open + ): + """Filehash hashlib md5 raises exception.""" + charm = tmpfile + mock_hashlib.md5.side_effect = Exception + with self.assertRaises(Exception): + LcmBase.calculate_charm_hash(charm) + self.assertEqual(mock_hashlib.md5.call_count, 1) + mocking_open.assert_not_called() + + @patch("builtins.open", new_callable=mock_open(read_data="charm content")) + @patch("osm_lcm.lcm_utils.hashlib") + def test_calculate_charm_hash_file_does_not_exist(self, mock_hashlib, mocking_open): + """Calculate charm hash, charm file does not exist.""" + file = None + mock_file_hash = MagicMock() + mock_hashlib.md5.return_value = mock_file_hash + mocking_open.side_effect = FileNotFoundError + with self.assertRaises(FileNotFoundError): + LcmBase.calculate_charm_hash(file) + self.assertEqual(mocking_open.call_count, 1) + mock_file_hash.update.assert_not_called() + mock_file_hash.hexdigest.assert_not_called() + self.assertEqual(mock_hashlib.md5.call_count, 1) + + @patch("osm_lcm.lcm_utils.LcmBase.calculate_charm_hash") + def test_compare_charm_hash_charm_changed(self, mock_calculate_charm_hash): + """Compare charm hash, charm files are different.""" + output = True + charm1, charm2 = tmpfile, tmpfile + mock_calculate_charm_hash.side_effect = [ + self.hexdigest, + "0dd7d4173747593c5fe5c006f", + ] + result = LcmBase.compare_charm_hash(charm1, charm2) + self.assertEqual(output, result) + self.assertEqual(mock_calculate_charm_hash.call_count, 2) + + @patch("osm_lcm.lcm_utils.LcmBase.calculate_charm_hash") + def test_compare_charm_hash_charm_is_same(self, mock_calculate_charm_hash): + """Compare charm hash, charm files are same.""" + output = False + charm1 = charm2 = tmpfile + mock_calculate_charm_hash.side_effect = [ + self.hexdigest, + self.hexdigest, + ] + result = LcmBase.compare_charm_hash(charm1, charm2) + self.assertEqual(output, result) + self.assertEqual(mock_calculate_charm_hash.call_count, 2) + + @patch("osm_lcm.lcm_utils.LcmBase.calculate_charm_hash") + def test_compare_charm_hash_one_charm_is_not_valid(self, mock_calculate_charm_hash): + """Compare charm hash, one charm file is not valid.""" + charm1, charm2 = tmpdir, None + mock_calculate_charm_hash.side_effect = [ + self.hexdigest, + FileNotFoundError, + ] + + with self.assertRaises(FileNotFoundError): + LcmBase.compare_charm_hash(charm1, charm2) + self.assertEqual(mock_calculate_charm_hash.call_count, 2) + + @patch("osm_lcm.lcm_utils.LcmBase.calculate_charm_hash") + def test_compare_charm_hash_both_charms_are_not_valid( + self, mock_calculate_charm_hash + ): + """Compare charm hash, both charm files are not valid.""" + charm1, charm2 = None, None + mock_calculate_charm_hash.side_effect = [IOError, IOError] + with self.assertRaises(IOError): + LcmBase.compare_charm_hash(charm1, charm2) + self.assertEqual(mock_calculate_charm_hash.call_count, 1) + + @patch("osm_lcm.lcm_utils.checksumdir") + def test_compare_charmdir_charm_changed(self, mock_checksum): + """Compare charm directory hash, charms are changed.""" + expected_output = True + charm_dir1, charm_dir2 = tmpdir, tmpdir + mock_checksum.dirhash.side_effect = [ + self.hexdigest, + "031eddtrtr651593c5fe5c006f", + ] + result = LcmBase.compare_charmdir_hash(charm_dir1, charm_dir2) + self.assertEqual(expected_output, result) + self.assertEqual(mock_checksum.dirhash.call_count, 2) + + @patch("osm_lcm.lcm_utils.checksumdir") + def test_compare_charmdir_charm_is_same(self, mock_checksum): + """Compare charm directory hash, charms are same.""" + expected_output = False + charm_dir1 = charm_dir2 = tmpdir + mock_checksum.dirhash.side_effect = [ + self.hexdigest, + self.hexdigest, + ] + result = LcmBase.compare_charmdir_hash(charm_dir1, charm_dir2) + self.assertEqual(expected_output, result) + self.assertEqual(mock_checksum.dirhash.call_count, 2) + + @patch("osm_lcm.lcm_utils.checksumdir") + def test_compare_charmdir_one_charmdir_is_not_valid(self, mock_checksum): + """Compare charm directory hash, one charm directory is not valid.""" + charm_dir1, charm_dir2 = tmpdir, None + mock_checksum.dirhash.side_effect = [ + self.hexdigest, + FileNotFoundError, + ] + with self.assertRaises(FileNotFoundError): + LcmBase.compare_charmdir_hash(charm_dir1, charm_dir2) + self.assertEqual(mock_checksum.dirhash.call_count, 2) + + @patch("osm_lcm.lcm_utils.checksumdir") + def test_compare_charmdir_both_charmdirs_are_not_valid(self, mock_checksum): + """Compare charm directory hash, both charm directories are not valid.""" + charm_dir1, charm_dir2 = None, None + mock_checksum.dirhash.side_effect = [FileNotFoundError, FileNotFoundError] + with self.assertRaises(FileNotFoundError): + LcmBase.compare_charmdir_hash(charm_dir1, charm_dir2) + self.assertEqual(mock_checksum.dirhash.call_count, 1) diff --git a/osm_lcm/tests/test_ns.py b/osm_lcm/tests/test_ns.py index 2004701..56d7ffa 100644 --- a/osm_lcm/tests/test_ns.py +++ b/osm_lcm/tests/test_ns.py @@ -18,6 +18,7 @@ import asynctest # pip3 install asynctest --user import asyncio +from copy import deepcopy import yaml import copy from os import getenv @@ -75,6 +76,18 @@ lcm_config = { "ng": True, }, } +nsr_id = descriptors.test_ids["TEST-A"]["ns"] +nslcmop_id = descriptors.test_ids["TEST-A"]["update"] +vnfr_id = "6421c7c9-d865-4fb4-9a13-d4275d243e01" +vnfd_id = "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77" +update_fs = Mock(autospec=True) +update_fs.path.__add__ = Mock() +update_fs.path.side_effect = ["/", "/", "/", "/"] +update_fs.sync.side_effect = [None, None] + + +def callable(a): + return a class TestMyNS(asynctest.TestCase): @@ -87,7 +100,7 @@ class TestMyNS(asynctest.TestCase): params={}, machine_spec={}, callback=None, - *callback_args + *callback_args, ): if callback: for status, message in ( @@ -943,747 +956,570 @@ class TestMyNS(asynctest.TestCase): # self.assertEqual(db_nsr.get("errorDescription "), None, "errorDescription different than None") # self.assertEqual(db_nsr.get("errorDetail"), None, "errorDetail different than None") - # Test update method - - async def test_update(self): - - nsr_id = descriptors.test_ids["TEST-A"]["ns"] - nslcmop_id = descriptors.test_ids["TEST-A"]["update"] - vnfr_id = "6421c7c9-d865-4fb4-9a13-d4275d243e01" - vnfd_id = "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77" - - def mock_reset(): - mock_charm_hash.reset_mock() - mock_juju_bundle.reset_mock() - fs.sync.reset_mock() - mock_charm_upgrade.reset_mock() - mock_software_version.reset_mock() - - with self.subTest( - i=1, - t="Update type: CHANGE_VNFPKG, latest_vnfd revision changed," - "Charm package changed, sw-version is not changed.", - ): - - self.db.set_one( - "vnfds", - q_filter={"_id": vnfd_id}, - update_dict={"_admin.revision": 3, "kdu": []}, - ) - - self.db.set_one( - "vnfds_revisions", - q_filter={"_id": vnfd_id + ":1"}, - update_dict={"_admin.revision": 1, "kdu": []}, - ) - - self.db.set_one( - "vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1} - ) - - mock_charm_hash = Mock(autospec=True) - mock_charm_hash.return_value = True - - mock_juju_bundle = Mock(return_value=None) - - mock_software_version = Mock(autospec=True) - mock_software_version.side_effect = ["1.0", "1.0"] - - mock_charm_upgrade = asynctest.Mock(autospec=True) - task = asyncio.Future() - task.set_result(("COMPLETED", "some_output")) - mock_charm_upgrade.return_value = task - - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - fs.sync.side_effect = [None, None] - - instance = self.my_ns - - expected_operation_state = "COMPLETED" - expected_operation_error = "" - expected_vnfr_revision = 3 - expected_ns_state = "INSTANTIATED" - expected_ns_operational_state = "running" - - with patch.object(instance, "fs", fs), patch( - "osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed", mock_charm_hash - ), patch("osm_lcm.ns.NsLcm._ns_charm_upgrade", mock_charm_upgrade), patch( - "osm_lcm.data_utils.vnfd.find_software_version", mock_software_version - ), patch( - "osm_lcm.lcm_utils.check_juju_bundle_existence", mock_juju_bundle - ): - - await instance.update(nsr_id, nslcmop_id) - return_operation_state = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("operationState") - return_operation_error = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("errorMessage") - return_ns_operational_state = self.db.get_one( - "nsrs", {"_id": nsr_id} - ).get("operational-status") - - return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( - "revision" - ) - - return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( - "nsState" - ) - - mock_charm_hash.assert_called_with( - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:1/hackfest_3charmed_vnfd/charms/simple", - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:3/hackfest_3charmed_vnfd/charms/simple", - ) - - self.assertEqual(fs.sync.call_count, 2) - self.assertEqual(return_ns_state, expected_ns_state) - self.assertEqual(return_operation_state, expected_operation_state) - self.assertEqual(return_operation_error, expected_operation_error) - self.assertEqual( - return_ns_operational_state, expected_ns_operational_state - ) - self.assertEqual(return_vnfr_revision, expected_vnfr_revision) - - mock_reset() - - with self.subTest( - i=2, t="Update type: CHANGE_VNFPKG, latest_vnfd revision not changed" - ): - - self.db.set_one( - "vnfds", q_filter={"_id": vnfd_id}, update_dict={"_admin.revision": 1} - ) - - self.db.set_one( - "vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1} - ) - - mock_charm_hash = Mock(autospec=True) - mock_charm_hash.return_value = True - - mock_juju_bundle = Mock(return_value=None) - mock_software_version = Mock(autospec=True) - - mock_charm_upgrade = asynctest.Mock(autospec=True) - task = asyncio.Future() - task.set_result(("COMPLETED", "some_output")) - mock_charm_upgrade.return_value = task - - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - fs.sync.side_effect = [None, None] - - instance = self.my_ns - - expected_operation_state = "COMPLETED" - expected_operation_error = "" - expected_vnfr_revision = 1 - expected_ns_state = "INSTANTIATED" - expected_ns_operational_state = "running" - - with patch.object(instance, "fs", fs), patch( - "osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed", mock_charm_hash - ), patch("osm_lcm.ns.NsLcm._ns_charm_upgrade", mock_charm_upgrade), patch( - "osm_lcm.lcm_utils.check_juju_bundle_existence", mock_juju_bundle - ): - - await instance.update(nsr_id, nslcmop_id) - - return_operation_state = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("operationState") - - return_operation_error = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("errorMessage") - - return_ns_operational_state = self.db.get_one( - "nsrs", {"_id": nsr_id} - ).get("operational-status") - - return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( - "nsState" - ) - - return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( - "revision" - ) - - mock_charm_hash.assert_not_called() - mock_software_version.assert_not_called() - mock_juju_bundle.assert_not_called() - mock_charm_upgrade.assert_not_called() - fs.sync.assert_not_called() - - self.assertEqual(return_ns_state, expected_ns_state) - self.assertEqual(return_operation_state, expected_operation_state) - self.assertEqual(return_operation_error, expected_operation_error) - self.assertEqual( - return_ns_operational_state, expected_ns_operational_state - ) - self.assertEqual(return_vnfr_revision, expected_vnfr_revision) - - mock_reset() - - with self.subTest( - i=3, - t="Update type: CHANGE_VNFPKG, latest_vnfd revision changed, " - "Charm package is not changed, sw-version is not changed.", - ): - - self.db.set_one( - "vnfds", q_filter={"_id": vnfd_id}, update_dict={"_admin.revision": 3} - ) - - self.db.set_one( - "vnfds_revisions", - q_filter={"_id": vnfd_id + ":1"}, - update_dict={"_admin.revision": 1}, - ) - - self.db.set_one( - "vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1} - ) - - mock_charm_hash = Mock(autospec=True) - mock_charm_hash.return_value = False - - mock_juju_bundle = Mock(return_value=None) - - mock_software_version = Mock(autospec=True) - - mock_charm_upgrade = asynctest.Mock(autospec=True) - task = asyncio.Future() - task.set_result(("COMPLETED", "some_output")) - mock_charm_upgrade.return_value = task - mock_software_version.side_effect = ["1.0", "1.0"] - - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - fs.sync.side_effect = [None, None] - - instance = self.my_ns - - expected_operation_state = "COMPLETED" - expected_operation_error = "" - expected_vnfr_revision = 3 - expected_ns_state = "INSTANTIATED" - expected_ns_operational_state = "running" - - with patch.object(instance, "fs", fs), patch( - "osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed", mock_charm_hash - ), patch("osm_lcm.ns.NsLcm._ns_charm_upgrade", mock_charm_upgrade), patch( - "osm_lcm.lcm_utils.check_juju_bundle_existence", mock_juju_bundle - ): - - await instance.update(nsr_id, nslcmop_id) - - return_operation_state = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("operationState") - - return_operation_error = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("errorMessage") - - return_ns_operational_state = self.db.get_one( - "nsrs", {"_id": nsr_id} - ).get("operational-status") - - return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( - "revision" - ) - - return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( - "nsState" - ) - - mock_charm_hash.assert_called_with( - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:1/hackfest_3charmed_vnfd/charms/simple", - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:3/hackfest_3charmed_vnfd/charms/simple", - ) - - self.assertEqual(fs.sync.call_count, 2) - self.assertEqual(mock_charm_hash.call_count, 1) - - mock_juju_bundle.assert_not_called() - mock_charm_upgrade.assert_not_called() - - self.assertEqual(return_ns_state, expected_ns_state) - self.assertEqual(return_operation_state, expected_operation_state) - self.assertEqual(return_operation_error, expected_operation_error) - self.assertEqual( - return_ns_operational_state, expected_ns_operational_state - ) - self.assertEqual(return_vnfr_revision, expected_vnfr_revision) - - mock_reset() - - with self.subTest( - i=4, - t="Update type: CHANGE_VNFPKG, latest_vnfd revision changed, " - "Charm package exists, sw-version changed.", - ): - - self.db.set_one( - "vnfds", - q_filter={"_id": vnfd_id}, - update_dict={"_admin.revision": 3, "software-version": "3.0"}, - ) - - self.db.set_one( - "vnfds_revisions", - q_filter={"_id": vnfd_id + ":1"}, - update_dict={"_admin.revision": 1}, - ) - - self.db.set_one( - "vnfrs", - q_filter={"_id": vnfr_id}, - update_dict={"revision": 1}, - ) - - mock_charm_hash = Mock(autospec=True) - mock_charm_hash.return_value = False - - mock_juju_bundle = Mock(return_value=None) - - mock_charm_upgrade = asynctest.Mock(autospec=True) - task = asyncio.Future() - task.set_result(("COMPLETED", "some_output")) - mock_charm_upgrade.return_value = task - - mock_charm_artifact = Mock(autospec=True) - mock_charm_artifact.side_effect = [ - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:1/hackfest_3charmed_vnfd/charms/simple", - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77/hackfest_3charmed_vnfd/charms/simple", - ] - - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - fs.sync.side_effect = [None, None] - - instance = self.my_ns - - expected_operation_state = "FAILED" - expected_operation_error = "FAILED Checking if existing VNF has charm: Software version change is not supported as VNF instance 6421c7c9-d865-4fb4-9a13-d4275d243e01 has charm." - expected_vnfr_revision = 1 - expected_ns_state = "INSTANTIATED" - expected_ns_operational_state = "running" - - with patch.object(instance, "fs", fs), patch( - "osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed", mock_charm_hash - ), patch("osm_lcm.ns.NsLcm._ns_charm_upgrade", mock_charm_upgrade), patch( - "osm_lcm.lcm_utils.get_charm_artifact_path", mock_charm_artifact - ): - - await instance.update(nsr_id, nslcmop_id) - - return_operation_state = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("operationState") - - return_operation_error = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("errorMessage") - - return_ns_operational_state = self.db.get_one( - "nsrs", {"_id": nsr_id} - ).get("operational-status") - - return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( - "revision" - ) - - return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( - "nsState" - ) - - self.assertEqual(fs.sync.call_count, 2) - mock_charm_hash.assert_not_called() - - mock_juju_bundle.assert_not_called() - mock_charm_upgrade.assert_not_called() - - self.assertEqual(return_ns_state, expected_ns_state) - self.assertEqual(return_operation_state, expected_operation_state) - self.assertEqual(return_operation_error, expected_operation_error) - self.assertEqual( - return_ns_operational_state, expected_ns_operational_state - ) - self.assertEqual(return_vnfr_revision, expected_vnfr_revision) - - mock_reset() - - with self.subTest( - i=5, - t="Update type: CHANGE_VNFPKG, latest_vnfd revision changed," - "Charm package exists, sw-version not changed, juju-bundle exists", - ): - - self.db.set_one( - "vnfds", - q_filter={"_id": vnfd_id}, - update_dict={ - "_admin.revision": 3, - "software-version": "1.0", - "kdu.0.juju-bundle": "stable/native-kdu", - }, - ) - - self.db.set_one( - "vnfds_revisions", - q_filter={"_id": vnfd_id + ":1"}, - update_dict={ - "_admin.revision": 1, - "software-version": "1.0", - "kdu.0.juju-bundle": "stable/native-kdu", - }, - ) - - self.db.set_one( - "vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1} - ) - - mock_charm_hash = Mock(autospec=True) - mock_charm_hash.return_value = True - - mock_charm_artifact = Mock(autospec=True) - mock_charm_artifact.side_effect = [ - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:1/hackfest_3charmed_vnfd/charms/simple", - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77/hackfest_3charmed_vnfd/charms/simple", - ] - - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - fs.sync.side_effect = [None, None] - - instance = self.my_ns - - expected_operation_state = "FAILED" - expected_operation_error = "FAILED Checking whether VNF uses juju bundle: Charm upgrade is not supported for the instance which uses juju-bundle: stable/native-kdu" - expected_vnfr_revision = 1 - expected_ns_state = "INSTANTIATED" - expected_ns_operational_state = "running" - - with patch.object(instance, "fs", fs), patch( - "osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed", mock_charm_hash - ), patch("osm_lcm.lcm_utils.get_charm_artifact_path", mock_charm_artifact): - - await instance.update(nsr_id, nslcmop_id) - - return_operation_state = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("operationState") - - return_operation_error = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("errorMessage") - - return_ns_operational_state = self.db.get_one( - "nsrs", {"_id": nsr_id} - ).get("operational-status") - - return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( - "revision" - ) - - return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( - "nsState" - ) - - self.assertEqual(fs.sync.call_count, 2) - self.assertEqual(mock_charm_hash.call_count, 1) - self.assertEqual(mock_charm_hash.call_count, 1) - - mock_charm_upgrade.assert_not_called() - - self.assertEqual(return_ns_state, expected_ns_state) - self.assertEqual(return_operation_state, expected_operation_state) - self.assertEqual(return_operation_error, expected_operation_error) - self.assertEqual( - return_ns_operational_state, expected_ns_operational_state - ) - self.assertEqual(return_vnfr_revision, expected_vnfr_revision) - - mock_reset() - - with self.subTest( - i=6, - t="Update type: CHANGE_VNFPKG, latest_vnfd revision changed," - "Charm package exists, sw-version not changed, charm-upgrade failed", - ): - - self.db.set_one( - "vnfds", - q_filter={"_id": vnfd_id}, - update_dict={ - "_admin.revision": 3, - "software-version": "1.0", - "kdu": [], - }, - ) - - self.db.set_one( - "vnfds_revisions", - q_filter={"_id": vnfd_id + ":1"}, - update_dict={ - "_admin.revision": 1, - "software-version": "1.0", - "kdu": [], - }, - ) - - self.db.set_one( - "vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1} - ) - - mock_charm_hash = Mock(autospec=True) - mock_charm_hash.return_value = True - - mock_charm_upgrade = asynctest.Mock(autospec=True) - task = asyncio.Future() - task.set_result(("FAILED", "some_error")) - mock_charm_upgrade.return_value = task - - mock_charm_artifact = Mock(autospec=True) - mock_charm_artifact.side_effect = [ - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:1/hackfest_3charmed_vnfd/charms/simple", - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77/hackfest_3charmed_vnfd/charms/simple", - ] - - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - fs.sync.side_effect = [None, None] - - instance = self.my_ns - - expected_operation_state = "FAILED" - expected_operation_error = "some_error" - expected_vnfr_revision = 1 - expected_ns_state = "INSTANTIATED" - expected_ns_operational_state = "running" - - with patch.object(instance, "fs", fs), patch( - "osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed", mock_charm_hash - ), patch("osm_lcm.ns.NsLcm._ns_charm_upgrade", mock_charm_upgrade): - - await instance.update(nsr_id, nslcmop_id) - - return_operation_state = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("operationState") - - return_operation_error = self.db.get_one( - "nslcmops", {"_id": nslcmop_id} - ).get("errorMessage") - - return_ns_operational_state = self.db.get_one( - "nsrs", {"_id": nsr_id} - ).get("operational-status") - - return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( - "revision" - ) - - return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( - "nsState" - ) - - self.assertEqual(fs.sync.call_count, 2) - self.assertEqual(mock_charm_hash.call_count, 1) - self.assertEqual(mock_charm_upgrade.call_count, 1) - - self.assertEqual(return_ns_state, expected_ns_state) - self.assertEqual(return_operation_state, expected_operation_state) - self.assertEqual(return_operation_error, expected_operation_error) - self.assertEqual( - return_ns_operational_state, expected_ns_operational_state - ) - self.assertEqual(return_vnfr_revision, expected_vnfr_revision) - - mock_reset() - - def test_ns_update_helper_methods(self): - def mock_reset(): - fs.mock_reset() - mock_path.mock_reset() - mock_checksumdir.mock_reset() - - with self.subTest( - i=1, t="Find software version, VNFD does not have have software version" - ): - # Testing method find_software_version - - db_vnfd = self.db.get_one( - "vnfds", {"_id": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77"} - ) - expected_result = "1.0" - result = find_software_version(db_vnfd) - self.assertEqual( - result, expected_result, "Default sw version should be 1.0" - ) - - with self.subTest( - i=2, t="Find software version, VNFD includes software version" - ): - # Testing method find_software_version - - db_vnfd = self.db.get_one( - "vnfds", {"_id": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77"} - ) - db_vnfd["software-version"] = "3.1" - expected_result = "3.1" - result = find_software_version(db_vnfd) - self.assertEqual(result, expected_result, "VNFD software version is wrong") - - with self.subTest(i=3, t="Check charm hash, Hash did not change"): - # Testing method check_charm_hash_changed - - current_path, target_path = "/tmp/charm1", "/tmp/charm1" - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] - - mock_path = Mock(autospec=True) - mock_path.exists.side_effect = [True, True] - - mock_checksumdir = Mock(autospec=True) - mock_checksumdir.dirhash.side_effect = ["hash_value", "hash_value"] + @patch("osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed") + @patch( + "osm_lcm.ns.NsLcm._ns_charm_upgrade", new_callable=asynctest.Mock(autospec=True) + ) + @patch("osm_lcm.data_utils.vnfd.find_software_version") + @patch("osm_lcm.lcm_utils.check_juju_bundle_existence") + async def test_update_change_vnfpkg_sw_version_not_changed( + self, + mock_juju_bundle, + mock_software_version, + mock_charm_upgrade, + mock_charm_hash, + ): + """Update type: CHANGE_VNFPKG, latest_vnfd revision changed, + Charm package changed, sw-version is not changed""" + self.db.set_one( + "vnfds", + q_filter={"_id": vnfd_id}, + update_dict={"_admin.revision": 3, "kdu": []}, + ) - instance = self.my_ns - expected_result = False + self.db.set_one( + "vnfds_revisions", + q_filter={"_id": vnfd_id + ":1"}, + update_dict={"_admin.revision": 1, "kdu": []}, + ) - with patch.object(instance, "fs", fs), patch( - "checksumdir.dirhash", mock_checksumdir.dirhash - ), patch("os.path.exists", mock_path.exists): + self.db.set_one("vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1}) - result = instance.check_charm_hash_changed(current_path, target_path) - self.assertEqual( - result, expected_result, "Wrong charm hash control value" - ) - self.assertEqual(mock_path.exists.call_count, 2) - self.assertEqual(mock_checksumdir.dirhash.call_count, 2) + mock_charm_hash.return_value = True + mock_software_version.side_effect = ["1.0", "1.0"] - mock_reset() + task = asyncio.Future() + task.set_result(("COMPLETED", "some_output")) + mock_charm_upgrade.return_value = task - with self.subTest(i=4, t="Check charm hash, Hash has changed"): - # Testing method check_charm_hash_changed + instance = self.my_ns + fs = deepcopy(update_fs) + instance.fs = fs - current_path, target_path = "/tmp/charm1", "/tmp/charm2" - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] + expected_operation_state = "COMPLETED" + expected_operation_error = "" + expected_vnfr_revision = 3 + expected_ns_state = "INSTANTIATED" + expected_ns_operational_state = "running" - mock_path = Mock(autospec=True) - mock_path.exists.side_effect = [True, True] + await instance.update(nsr_id, nslcmop_id) + return_operation_state = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + return_operation_error = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "errorMessage" + ) + return_ns_operational_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( + "operational-status" + ) + return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( + "revision" + ) + return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get("nsState") + mock_charm_hash.assert_called_with( + f"{vnfd_id}:1/hackfest_3charmed_vnfd/charms/simple", + f"{vnfd_id}:3/hackfest_3charmed_vnfd/charms/simple", + ) + self.assertEqual(fs.sync.call_count, 2) + self.assertEqual(return_ns_state, expected_ns_state) + self.assertEqual(return_operation_state, expected_operation_state) + self.assertEqual(return_operation_error, expected_operation_error) + self.assertEqual(return_ns_operational_state, expected_ns_operational_state) + self.assertEqual(return_vnfr_revision, expected_vnfr_revision) + + @patch("osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed") + @patch( + "osm_lcm.ns.NsLcm._ns_charm_upgrade", new_callable=asynctest.Mock(autospec=True) + ) + @patch("osm_lcm.data_utils.vnfd.find_software_version") + @patch("osm_lcm.lcm_utils.check_juju_bundle_existence") + async def test_update_change_vnfpkg_vnfd_revision_not_changed( + self, + mock_juju_bundle, + mock_software_version, + mock_charm_upgrade, + mock_charm_hash, + ): + """Update type: CHANGE_VNFPKG, latest_vnfd revision not changed""" + self.db.set_one( + "vnfds", q_filter={"_id": vnfd_id}, update_dict={"_admin.revision": 1} + ) + self.db.set_one("vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1}) - mock_checksumdir = Mock(autospec=True) - mock_checksumdir.dirhash.side_effect = ["hash_value", "another_hash_value"] + mock_charm_hash.return_value = True - instance = self.my_ns - expected_result = True + task = asyncio.Future() + task.set_result(("COMPLETED", "some_output")) + mock_charm_upgrade.return_value = task - with patch.object(instance, "fs", fs), patch( - "checksumdir.dirhash", mock_checksumdir.dirhash - ), patch("os.path.exists", mock_path.exists): + instance = self.my_ns - result = instance.check_charm_hash_changed(current_path, target_path) - self.assertEqual( - result, expected_result, "Wrong charm hash control value" - ) - self.assertEqual(mock_path.exists.call_count, 2) - self.assertEqual(mock_checksumdir.dirhash.call_count, 2) + expected_operation_state = "COMPLETED" + expected_operation_error = "" + expected_vnfr_revision = 1 + expected_ns_state = "INSTANTIATED" + expected_ns_operational_state = "running" - mock_reset() + await instance.update(nsr_id, nslcmop_id) + return_operation_state = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + return_operation_error = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "errorMessage" + ) + return_ns_operational_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( + "operational-status" + ) + return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get("nsState") + return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( + "revision" + ) + mock_charm_hash.assert_not_called() + mock_software_version.assert_not_called() + mock_juju_bundle.assert_not_called() + mock_charm_upgrade.assert_not_called() + update_fs.sync.assert_not_called() + self.assertEqual(return_ns_state, expected_ns_state) + self.assertEqual(return_operation_state, expected_operation_state) + self.assertEqual(return_operation_error, expected_operation_error) + self.assertEqual(return_ns_operational_state, expected_ns_operational_state) + self.assertEqual(return_vnfr_revision, expected_vnfr_revision) + + @patch("osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed") + @patch( + "osm_lcm.ns.NsLcm._ns_charm_upgrade", new_callable=asynctest.Mock(autospec=True) + ) + @patch("osm_lcm.data_utils.vnfd.find_software_version") + @patch("osm_lcm.lcm_utils.check_juju_bundle_existence") + async def test_update_change_vnfpkg_charm_is_not_changed( + self, + mock_juju_bundle, + mock_software_version, + mock_charm_upgrade, + mock_charm_hash, + ): + """Update type: CHANGE_VNFPKG, latest_vnfd revision changed + Charm package is not changed, sw-version is not changed""" + self.db.set_one( + "vnfds", + q_filter={"_id": vnfd_id}, + update_dict={"_admin.revision": 3, "kdu": []}, + ) + self.db.set_one( + "vnfds_revisions", + q_filter={"_id": vnfd_id + ":1"}, + update_dict={"_admin.revision": 1, "kdu": []}, + ) + self.db.set_one("vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1}) + + mock_charm_hash.return_value = False + mock_software_version.side_effect = ["1.0", "1.0"] + + instance = self.my_ns + fs = deepcopy(update_fs) + instance.fs = fs + expected_operation_state = "COMPLETED" + expected_operation_error = "" + expected_vnfr_revision = 3 + expected_ns_state = "INSTANTIATED" + expected_ns_operational_state = "running" + + await instance.update(nsr_id, nslcmop_id) + return_operation_state = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + return_operation_error = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "errorMessage" + ) + return_ns_operational_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( + "operational-status" + ) + return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( + "revision" + ) + return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get("nsState") + mock_charm_hash.assert_called_with( + f"{vnfd_id}:1/hackfest_3charmed_vnfd/charms/simple", + f"{vnfd_id}:3/hackfest_3charmed_vnfd/charms/simple", + ) + self.assertEqual(fs.sync.call_count, 2) + self.assertEqual(mock_charm_hash.call_count, 1) + mock_juju_bundle.assert_not_called() + mock_charm_upgrade.assert_not_called() + self.assertEqual(return_ns_state, expected_ns_state) + self.assertEqual(return_operation_state, expected_operation_state) + self.assertEqual(return_operation_error, expected_operation_error) + self.assertEqual(return_ns_operational_state, expected_ns_operational_state) + self.assertEqual(return_vnfr_revision, expected_vnfr_revision) + + @patch("osm_lcm.lcm_utils.check_juju_bundle_existence") + @patch("osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed") + @patch( + "osm_lcm.ns.NsLcm._ns_charm_upgrade", new_callable=asynctest.Mock(autospec=True) + ) + @patch("osm_lcm.lcm_utils.get_charm_artifact_path") + async def test_update_change_vnfpkg_sw_version_changed( + self, mock_charm_artifact, mock_charm_upgrade, mock_charm_hash, mock_juju_bundle + ): + """Update type: CHANGE_VNFPKG, latest_vnfd revision changed + Charm package exists, sw-version changed.""" + self.db.set_one( + "vnfds", + q_filter={"_id": vnfd_id}, + update_dict={"_admin.revision": 3, "software-version": "3.0", "kdu": []}, + ) + self.db.set_one( + "vnfds_revisions", + q_filter={"_id": vnfd_id + ":1"}, + update_dict={"_admin.revision": 1, "kdu": []}, + ) + self.db.set_one( + "vnfrs", + q_filter={"_id": vnfr_id}, + update_dict={"revision": 1}, + ) + mock_charm_hash.return_value = False - with self.subTest(i=5, t="Check charm hash, Charm path does not exists"): - # Testing method check_charm_hash_changed + mock_charm_artifact.side_effect = [ + f"{vnfd_id}:1/hackfest_3charmed_vnfd/charms/simple", + f"{vnfd_id}:3/hackfest_3charmed_vnfd/charms/simple", + ] - current_path, target_path = "/tmp/charm1", "/tmp/charm2" - fs = Mock(autospec=True) - fs.path.__add__ = Mock() - fs.path.side_effect = ["/", "/", "/", "/"] + instance = self.my_ns + fs = deepcopy(update_fs) + instance.fs = fs + expected_operation_state = "FAILED" + expected_operation_error = "FAILED Checking if existing VNF has charm: Software version change is not supported as VNF instance 6421c7c9-d865-4fb4-9a13-d4275d243e01 has charm." + expected_vnfr_revision = 1 + expected_ns_state = "INSTANTIATED" + expected_ns_operational_state = "running" + + await instance.update(nsr_id, nslcmop_id) + return_operation_state = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + return_operation_error = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "errorMessage" + ) + return_ns_operational_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( + "operational-status" + ) + return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( + "revision" + ) + return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get("nsState") + self.assertEqual(fs.sync.call_count, 2) + mock_charm_hash.assert_not_called() + mock_juju_bundle.assert_not_called() + mock_charm_upgrade.assert_not_called() + self.assertEqual(return_ns_state, expected_ns_state) + self.assertEqual(return_operation_state, expected_operation_state) + self.assertEqual(return_operation_error, expected_operation_error) + self.assertEqual(return_ns_operational_state, expected_ns_operational_state) + self.assertEqual(return_vnfr_revision, expected_vnfr_revision) + + @patch("osm_lcm.lcm_utils.check_juju_bundle_existence") + @patch("osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed") + @patch( + "osm_lcm.ns.NsLcm._ns_charm_upgrade", new_callable=asynctest.Mock(autospec=True) + ) + @patch("osm_lcm.data_utils.vnfd.find_software_version") + async def test_update_change_vnfpkg_juju_bundle_exists( + self, + mock_software_version, + mock_charm_upgrade, + mock_charm_hash, + mock_juju_bundle, + ): + """Update type: CHANGE_VNFPKG, latest_vnfd revision changed + Charm package exists, sw-version not changed, juju-bundle exists""" + # Upgrade is not allowed with juju bundles, this will cause TypeError + self.db.set_one( + "vnfds", + q_filter={"_id": vnfd_id}, + update_dict={ + "_admin.revision": 5, + "software-version": "1.0", + "kdu": [{"kdu_name": "native-kdu", "juju-bundle": "stable/native-kdu"}], + }, + ) + self.db.set_one( + "vnfds_revisions", + q_filter={"_id": vnfd_id + ":1"}, + update_dict={ + "_admin.revision": 1, + "software-version": "1.0", + "kdu": [{"kdu_name": "native-kdu", "juju-bundle": "stable/native-kdu"}], + }, + ) + self.db.set_one( + "nsrs", + q_filter={"_id": nsr_id}, + update_dict={ + "_admin.deployed.VCA.0.kdu_name": "native-kdu", + }, + ) + self.db.set_one("vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1}) + + mock_charm_hash.side_effect = [True] + mock_software_version.side_effect = ["1.0", "1.0"] + mock_juju_bundle.return_value = True + instance = self.my_ns + fs = deepcopy(update_fs) + instance.fs = fs + + expected_vnfr_revision = 1 + expected_ns_state = "INSTANTIATED" + expected_ns_operational_state = "running" + + await instance.update(nsr_id, nslcmop_id) + return_ns_operational_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( + "operational-status" + ) + return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( + "revision" + ) + return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get("nsState") + self.assertEqual(fs.sync.call_count, 2) + mock_charm_upgrade.assert_not_called() + mock_charm_hash.assert_not_called() + self.assertEqual(return_ns_state, expected_ns_state) + self.assertEqual(return_ns_operational_state, expected_ns_operational_state) + self.assertEqual(return_vnfr_revision, expected_vnfr_revision) + + @patch("osm_lcm.lcm_utils.LcmBase.check_charm_hash_changed") + @patch( + "osm_lcm.ns.NsLcm._ns_charm_upgrade", new_callable=asynctest.Mock(autospec=True) + ) + async def test_update_change_vnfpkg_charm_upgrade_failed( + self, mock_charm_upgrade, mock_charm_hash + ): + """ "Update type: CHANGE_VNFPKG, latest_vnfd revision changed" + Charm package exists, sw-version not changed, charm-upgrade failed""" + self.db.set_one( + "vnfds", + q_filter={"_id": vnfd_id}, + update_dict={ + "_admin.revision": 3, + "software-version": "1.0", + "kdu": [], + }, + ) + self.db.set_one( + "vnfds_revisions", + q_filter={"_id": vnfd_id + ":1"}, + update_dict={ + "_admin.revision": 1, + "software-version": "1.0", + "kdu": [], + }, + ) + self.db.set_one("vnfrs", q_filter={"_id": vnfr_id}, update_dict={"revision": 1}) - mock_path = Mock(autospec=True) - mock_path.exists.side_effect = [True, False] + mock_charm_hash.return_value = True - mock_checksumdir = Mock(autospec=True) - mock_checksumdir.dirhash.side_effect = ["hash_value", "hash_value"] + task = asyncio.Future() + task.set_result(("FAILED", "some_error")) + mock_charm_upgrade.return_value = task - instance = self.my_ns + instance = self.my_ns + fs = deepcopy(update_fs) + instance.fs = fs + expected_operation_state = "FAILED" + expected_operation_error = "some_error" + expected_vnfr_revision = 1 + expected_ns_state = "INSTANTIATED" + expected_ns_operational_state = "running" - with patch.object(instance, "fs", fs), patch( - "checksumdir.dirhash", mock_checksumdir.dirhash - ), patch("os.path.exists", mock_path.exists): + await instance.update(nsr_id, nslcmop_id) + return_operation_state = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "operationState" + ) + return_operation_error = self.db.get_one("nslcmops", {"_id": nslcmop_id}).get( + "errorMessage" + ) + return_ns_operational_state = self.db.get_one("nsrs", {"_id": nsr_id}).get( + "operational-status" + ) + return_vnfr_revision = self.db.get_one("vnfrs", {"_id": vnfr_id}).get( + "revision" + ) + return_ns_state = self.db.get_one("nsrs", {"_id": nsr_id}).get("nsState") + self.assertEqual(fs.sync.call_count, 2) + self.assertEqual(mock_charm_hash.call_count, 1) + self.assertEqual(mock_charm_upgrade.call_count, 1) + self.assertEqual(return_ns_state, expected_ns_state) + self.assertEqual(return_operation_state, expected_operation_state) + self.assertEqual(return_operation_error, expected_operation_error) + self.assertEqual(return_ns_operational_state, expected_ns_operational_state) + self.assertEqual(return_vnfr_revision, expected_vnfr_revision) + + def test_ns_update_find_sw_version_vnfd_not_includes(self): + """Find software version, VNFD does not have software version""" + + db_vnfd = self.db.get_one("vnfds", {"_id": vnfd_id}) + expected_result = "1.0" + result = find_software_version(db_vnfd) + self.assertEqual(result, expected_result, "Default sw version should be 1.0") + + def test_ns_update_find_sw_version_vnfd_includes(self): + """Find software version, VNFD includes software version""" + + db_vnfd = self.db.get_one("vnfds", {"_id": vnfd_id}) + db_vnfd["software-version"] = "3.1" + expected_result = "3.1" + result = find_software_version(db_vnfd) + self.assertEqual(result, expected_result, "VNFD software version is wrong") + + @patch("os.path.exists") + @patch("osm_lcm.lcm_utils.LcmBase.compare_charmdir_hash") + @patch("osm_lcm.lcm_utils.LcmBase.compare_charm_hash") + def test_ns_update_check_charm_hash_not_changed( + self, mock_compare_charm_hash, mock_compare_charmdir_hash, mock_path_exists + ): + """Check charm hash, Hash did not change""" - with self.assertRaises(LcmException): + current_path, target_path = "/tmp/charm1", "/tmp/charm1" - instance.check_charm_hash_changed(current_path, target_path) - self.assertEqual(mock_path.exists.call_count, 2) - self.assertEqual(mock_checksumdir.dirhash.call_count, 0) + fs = Mock() + fs.path.__add__ = Mock() + fs.path.side_effect = [current_path, target_path] + fs.path.__add__.side_effect = [current_path, target_path] - mock_reset() + mock_path_exists.side_effect = [True, True] - with self.subTest(i=6, t="Check juju bundle existence"): - # Testing method check_juju_bundle_existence + mock_compare_charmdir_hash.return_value = callable(False) + mock_compare_charm_hash.return_value = callable(False) - test_vnfd1 = self.db.get_one( - "vnfds", {"_id": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77"} - ) - test_vnfd2 = self.db.get_one( - "vnfds", {"_id": "d96b1cdf-5ad6-49f7-bf65-907ada989293"} - ) + instance = self.my_ns + instance.fs = fs + expected_result = False - expected_result = None - result = check_juju_bundle_existence(test_vnfd1) - self.assertEqual(result, expected_result, "Wrong juju bundle name") + result = instance.check_charm_hash_changed(current_path, target_path) + self.assertEqual(result, expected_result, "Wrong charm hash control value") + self.assertEqual(mock_path_exists.call_count, 2) + self.assertEqual(mock_compare_charmdir_hash.call_count, 1) + self.assertEqual(mock_compare_charm_hash.call_count, 0) - expected_result = "stable/native-kdu" - result = check_juju_bundle_existence(test_vnfd2) - self.assertEqual(result, expected_result, "Wrong juju bundle name") + @patch("os.path.exists") + @patch("osm_lcm.lcm_utils.LcmBase.compare_charmdir_hash") + @patch("osm_lcm.lcm_utils.LcmBase.compare_charm_hash") + def test_ns_update_check_charm_hash_changed( + self, mock_compare_charm_hash, mock_compare_charmdir_hash, mock_path_exists + ): + """Check charm hash, Hash has changed""" + + current_path, target_path = "/tmp/charm1", "/tmp/charm2" + + fs = Mock() + fs.path.__add__ = Mock() + fs.path.side_effect = [current_path, target_path, current_path, target_path] + fs.path.__add__.side_effect = [ + current_path, + target_path, + current_path, + target_path, + ] - with self.subTest(i=7, t="Check charm artifacts"): - # Testing method check_juju_bundle_existence + mock_path_exists.side_effect = [True, True] + mock_compare_charmdir_hash.return_value = callable(True) + mock_compare_charm_hash.return_value = callable(True) + + instance = self.my_ns + instance.fs = fs + expected_result = True + + result = instance.check_charm_hash_changed(current_path, target_path) + self.assertEqual(result, expected_result, "Wrong charm hash control value") + self.assertEqual(mock_path_exists.call_count, 2) + self.assertEqual(mock_compare_charmdir_hash.call_count, 1) + self.assertEqual(mock_compare_charm_hash.call_count, 0) + + @patch("os.path.exists") + @patch("osm_lcm.lcm_utils.LcmBase.compare_charmdir_hash") + @patch("osm_lcm.lcm_utils.LcmBase.compare_charm_hash") + def test_ns_update_check_no_charm_path( + self, mock_compare_charm_hash, mock_compare_charmdir_hash, mock_path_exists + ): + """Check charm hash, Charm path does not exist""" + + current_path, target_path = "/tmp/charm1", "/tmp/charm2" + + fs = Mock() + fs.path.__add__ = Mock() + fs.path.side_effect = [current_path, target_path, current_path, target_path] + fs.path.__add__.side_effect = [ + current_path, + target_path, + current_path, + target_path, + ] - base_folder = { - "folder": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77", - "pkg-dir": "hackfest_3charmed_vnfd", - } - charm_name = "simple" - charm_type = "lxc_proxy_charm" - revision = 3 + mock_path_exists.side_effect = [True, False] - expected_result = "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77:3/hackfest_3charmed_vnfd/charms/simple" - result = get_charm_artifact_path( - base_folder, charm_name, charm_type, revision - ) - self.assertEqual(result, expected_result, "Wrong charm artifact path") + mock_compare_charmdir_hash.return_value = callable(False) + mock_compare_charm_hash.return_value = callable(False) + instance = self.my_ns + instance.fs = fs - # SOL004 packages - base_folder = { - "folder": "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77", - } - charm_name = "basic" - charm_type = "" - revision = "" + with self.assertRaises(LcmException): + instance.check_charm_hash_changed(current_path, target_path) + self.assertEqual(mock_path_exists.call_count, 2) + self.assertEqual(mock_compare_charmdir_hash.call_count, 0) + self.assertEqual(mock_compare_charm_hash.call_count, 0) - expected_result = ( - "7637bcf8-cf14-42dc-ad70-c66fcf1e6e77/Scripts/helm-charts/basic" - ) - result = get_charm_artifact_path( - base_folder, charm_name, charm_type, revision - ) - self.assertEqual(result, expected_result, "Wrong charm artifact path") + def test_ns_update_check_juju_bundle_existence_bundle_exists(self): + """Check juju bundle existence""" + test_vnfd2 = self.db.get_one( + "vnfds", {"_id": "d96b1cdf-5ad6-49f7-bf65-907ada989293"} + ) + expected_result = "stable/native-kdu" + result = check_juju_bundle_existence(test_vnfd2) + self.assertEqual(result, expected_result, "Wrong juju bundle name") + + def test_ns_update_check_juju_bundle_existence_bundle_does_not_exist(self): + """Check juju bundle existence""" + test_vnfd1 = self.db.get_one("vnfds", {"_id": vnfd_id}) + expected_result = None + result = check_juju_bundle_existence(test_vnfd1) + self.assertEqual(result, expected_result, "Wrong juju bundle name") + + def test_ns_update_check_juju_bundle_existence_empty_vnfd(self): + """Check juju bundle existence""" + test_vnfd1 = {} + expected_result = None + result = check_juju_bundle_existence(test_vnfd1) + self.assertEqual(result, expected_result, "Wrong juju bundle name") + + def test_ns_update_check_juju_bundle_existence_invalid_vnfd(self): + """Check juju bundle existence""" + test_vnfd1 = [{"_id": vnfd_id}] + with self.assertRaises(AttributeError): + check_juju_bundle_existence(test_vnfd1) + + def test_ns_update_check_juju_charm_artifacts_base_folder_wth_pkgdir(self): + """Check charm artifacts""" + base_folder = { + "folder": vnfd_id, + "pkg-dir": "hackfest_3charmed_vnfd", + } + charm_name = "simple" + charm_type = "lxc_proxy_charm" + revision = 3 + expected_result = f"{vnfd_id}:3/hackfest_3charmed_vnfd/charms/simple" + result = get_charm_artifact_path(base_folder, charm_name, charm_type, revision) + self.assertEqual(result, expected_result, "Wrong charm artifact path") + + def test_ns_update_check_juju_charm_artifacts_base_folder_wthout_pkgdir(self): + """Check charm artifacts, SOL004 packages""" + base_folder = { + "folder": vnfd_id, + } + charm_name = "basic" + charm_type, revision = "", "" + expected_result = f"{vnfd_id}/Scripts/helm-charts/basic" + result = get_charm_artifact_path(base_folder, charm_name, charm_type, revision) + self.assertEqual(result, expected_result, "Wrong charm artifact path") if __name__ == "__main__": -- 2.25.1