Feature 10944 Change naming of charms 08/12408/7
authoraticig <gulsum.atici@canonical.com>
Wed, 27 Jul 2022 06:32:06 +0000 (09:32 +0300)
committercubag <gcuba@whitestack.com>
Thu, 18 Aug 2022 23:35:00 +0000 (01:35 +0200)
Adding find_charm_name method and
changing _deploy_n2vc and _heal_n2vc methods in LCM

Change-Id: I62fdf3e66493c26e07e80ba7874badbe7c74c95e
Signed-off-by: aticig <gulsum.atici@canonical.com>
osm_lcm/lcm_utils.py
osm_lcm/ns.py
osm_lcm/tests/test_db_descriptors.py
osm_lcm/tests/test_lcm.py
osm_lcm/tests/test_lcm_utils.py [new file with mode: 0644]
osm_lcm/tests/test_ns.py

index 1854da7..6d20318 100644 (file)
@@ -20,9 +20,15 @@ import asyncio
 import checksumdir
 from collections import OrderedDict
 import os
+import shutil
+import traceback
 from time import time
+
+from osm_common.fsbase import FsException
 from osm_lcm.data_utils.database.database import Database
 from osm_lcm.data_utils.filesystem.filesystem import Filesystem
+import yaml
+from zipfile import ZipFile, BadZipfile
 
 # from osm_common.dbbase import DbException
 
@@ -217,6 +223,133 @@ class LcmBase:
                 )
             )
 
+    @staticmethod
+    def get_charm_name(charm_metadata_file: str) -> str:
+        """Get the charm name from metadata file.
+
+        Args:
+            charm_metadata_file    (str):  charm metadata file full path
+
+        Returns:
+            charm_name    (str):  charm name
+
+        """
+        # Read charm metadata.yaml to get the charm name
+        with open(charm_metadata_file, "r") as metadata_file:
+            content = yaml.safe_load(metadata_file)
+            charm_name = content["name"]
+            return str(charm_name)
+
+    def _get_charm_path(
+        self, nsd_package_path: str, nsd_package_name: str, charm_folder_name: str
+    ) -> str:
+        """Get the full path of charm folder.
+
+        Args:
+            nsd_package_path    (str):  NSD package full path
+            nsd_package_name    (str):  NSD package name
+            charm_folder_name   (str):  folder name
+
+        Returns:
+            charm_path    (str):  charm folder full path
+        """
+        charm_path = (
+            self.fs.path
+            + nsd_package_path
+            + "/"
+            + nsd_package_name
+            + "/charms/"
+            + charm_folder_name
+        )
+        return charm_path
+
+    def _get_charm_metadata_file(
+        self,
+        charm_folder_name: str,
+        nsd_package_path: str,
+        nsd_package_name: str,
+        charm_path: str = None,
+    ) -> str:
+        """Get the path of charm metadata file.
+
+        Args:
+            charm_folder_name   (str):  folder name
+            nsd_package_path    (str):  NSD package full path
+            nsd_package_name    (str):  NSD package name
+            charm_path  (str):  Charm full path
+
+        Returns:
+            charm_metadata_file_path    (str):  charm metadata file full path
+
+        """
+        # Locate the charm metadata.yaml
+        if charm_folder_name.endswith(".charm"):
+            extract_path = (
+                self.fs.path
+                + nsd_package_path
+                + "/"
+                + nsd_package_name
+                + "/charms/"
+                + charm_folder_name.split(".")[0]
+            )
+            # Extract .charm to extract path
+            with ZipFile(charm_path, "r") as zipfile:
+                zipfile.extractall(extract_path)
+            return extract_path + "/metadata.yaml"
+        else:
+            return charm_path + "/metadata.yaml"
+
+    def find_charm_name(self, db_nsr: dict, charm_folder_name: str) -> str:
+        """Get the charm name from metadata.yaml of charm package.
+
+        Args:
+            db_nsr  (dict): NS record as a dictionary
+            charm_folder_name   (str): charm folder name
+
+        Returns:
+             charm_name (str):  charm name
+        """
+        try:
+            if not charm_folder_name:
+                raise LcmException("charm_folder_name should be provided.")
+
+            # Find nsd_package details: path, name
+            revision = db_nsr.get("revision", "")
+            nsd_package_path = (
+                db_nsr["nsd-id"] + ":" + str(revision) if revision else db_nsr["nsd-id"]
+            )
+            nsd_package_name = os.listdir(self.fs.path + nsd_package_path)[0]
+
+            # Remove the existing nsd package and sync from FsMongo
+            shutil.rmtree(self.fs.path + nsd_package_path, ignore_errors=True)
+            self.fs.sync(from_path=nsd_package_path)
+
+            # Get the charm path
+            charm_path = self._get_charm_path(
+                nsd_package_path, nsd_package_name, charm_folder_name
+            )
+
+            # Find charm metadata file full path
+            charm_metadata_file = self._get_charm_metadata_file(
+                charm_folder_name, nsd_package_path, nsd_package_name, charm_path
+            )
+
+            # Return charm name
+            return self.get_charm_name(charm_metadata_file)
+
+        except (
+            yaml.YAMLError,
+            IOError,
+            FsException,
+            KeyError,
+            TypeError,
+            FileNotFoundError,
+            BadZipfile,
+        ) as error:
+            self.logger.debug(traceback.format_exc())
+            self.logger.error(f"{error} occured while getting the charm name")
+            raise LcmException(error)
+
 
 class TaskRegistry(LcmBase):
     """
index 118b31e..b1c027a 100644 (file)
@@ -3735,10 +3735,16 @@ class NsLcm(LcmBase):
         self.logger.debug(
             logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id)
         )
+
+        charm_name = ""
+        get_charm_name = False
         if "execution-environment-list" in descriptor_config:
             ee_list = descriptor_config.get("execution-environment-list", [])
         elif "juju" in descriptor_config:
             ee_list = [descriptor_config]  # ns charms
+            if "execution-environment-list" not in descriptor_config:
+                # charm name is only required for ns charms
+                get_charm_name = True
         else:  # other types as script are not supported
             ee_list = []
 
@@ -3752,6 +3758,8 @@ class NsLcm(LcmBase):
             ee_descriptor_id = ee_item.get("id")
             if ee_item.get("juju"):
                 vca_name = ee_item["juju"].get("charm")
+                if get_charm_name:
+                    charm_name = self.find_charm_name(db_nsr, str(vca_name))
                 vca_type = (
                     "lxc_proxy_charm"
                     if ee_item["juju"].get("charm") is not None
@@ -3810,6 +3818,7 @@ class NsLcm(LcmBase):
                     "vdu_name": vdu_name,
                     "type": vca_type,
                     "ee_descriptor_id": ee_descriptor_id,
+                    "charm_name": charm_name,
                 }
                 vca_index += 1
 
@@ -7832,10 +7841,16 @@ class NsLcm(LcmBase):
         self.logger.debug(
             logging_text + "_deploy_n2vc vnfd_id={}, vdu_id={}".format(vnfd_id, vdu_id)
         )
+
+        charm_name = ""
+        get_charm_name = False
         if "execution-environment-list" in descriptor_config:
             ee_list = descriptor_config.get("execution-environment-list", [])
         elif "juju" in descriptor_config:
             ee_list = [descriptor_config]  # ns charms
+            if "execution-environment-list" not in descriptor_config:
+                # charm name is only required for ns charms
+                get_charm_name = True
         else:  # other types as script are not supported
             ee_list = []
 
@@ -7849,6 +7864,8 @@ class NsLcm(LcmBase):
             ee_descriptor_id = ee_item.get("id")
             if ee_item.get("juju"):
                 vca_name = ee_item["juju"].get("charm")
+                if get_charm_name:
+                    charm_name = self.find_charm_name(db_nsr, str(vca_name))
                 vca_type = (
                     "lxc_proxy_charm"
                     if ee_item["juju"].get("charm") is not None
@@ -7907,6 +7924,7 @@ class NsLcm(LcmBase):
                     "vdu_name": vdu_name,
                     "type": vca_type,
                     "ee_descriptor_id": ee_descriptor_id,
+                    "charm_name": charm_name,
                 }
                 vca_index += 1
 
index 9304908..d02b011 100644 (file)
@@ -1235,6 +1235,254 @@ db_nsrs_text = """
       vim-network-name: mgmt
     vnfd-id:
     - d96b1cdf-5ad6-49f7-bf65-907ada989293
+
+-   _id: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+    name: ha_charm-ns2
+    name-ref: ha_charm-ns2
+    short-name: ha_charm-ns2
+    admin-status: ENABLED
+    nsState: BROKEN
+    currentOperation: IDLE
+    currentOperationID:
+    errorDescription: 'Operation: INSTANTIATING.8e72f2b5-f466-4382-88a4-4575c9e07eb8,
+      Stage 5/5: running Day-1 primitives for NS.'
+    deploymentStatus:
+    configurationStatus:
+    - elementType: VDU
+      elementUnderConfiguration: userVM-0
+      status: READY
+    - elementType: VDU
+      elementUnderConfiguration: policyVM-0
+      status: READY
+    - elementType: NS
+      elementUnderConfiguration: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+      status: BROKEN
+    vcaStatus:
+    nsd:
+      _id: a557cb0f-0dc9-494c-a9bd-69e8079767e7
+      id: nscharm-ns
+      version: '1.0'
+      name: nscharm-ns
+      vnfd-id:
+      - nscharm-user-vnf
+      - nscharm-policy-vnf
+      virtual-link-desc:
+      - id: mgmtnet
+        mgmt-network: true
+        vim-network-name: osm-ext
+      df:
+      - id: default-df
+        vnf-profile:
+        - id: '1'
+          virtual-link-connectivity:
+          - constituent-cpd-id:
+            - constituent-base-element-id: '1'
+              constituent-cpd-id: vnf-mgmt-ext
+            virtual-link-profile-id: mgmtnet
+          vnfd-id: nscharm-user-vnf
+        - id: '2'
+          virtual-link-connectivity:
+          - constituent-cpd-id:
+            - constituent-base-element-id: '2'
+              constituent-cpd-id: vnf-mgmt-ext
+            virtual-link-profile-id: mgmtnet
+          vnfd-id: nscharm-policy-vnf
+      ns-configuration:
+        juju:
+          charm: ns_ubuntu-18.04-amd64.charm
+        config-primitive:
+        - name: add-user
+          parameter:
+          - name: username
+            data-type: STRING
+        initial-config-primitive:
+        - seq: '1'
+          name: config
+          parameter:
+          - name: juju-username
+            value: admin
+          - name: juju-password
+            value: a5611fc6452349cc6e45705d34c501d4
+        - seq: '2'
+          name: add-user
+          parameter:
+          - name: username
+            value: root
+      description: NS with 2 VNFs
+      _admin:
+        userDefinedData: {}
+        revision: 1
+        created: 1658868548.2641
+        modified: 1658868548.89253
+        projects_read:
+        - 51e0e80fe533469d98766caa16552a3e
+        projects_write:
+        - 51e0e80fe533469d98766caa16552a3e
+        onboardingState: ONBOARDED
+        operationalState: ENABLED
+        usageState: NOT_IN_USE
+        storage:
+          fs: mongo
+          path: "/app/storage/"
+          folder: a557cb0f-0dc9-494c-a9bd-69e8079767e7:1
+          pkg-dir: nscharm_ns
+          descriptor: nscharm_ns/nscharm_nsd.yaml
+          zipfile: nscharm_ns.tar.gz
+    datacenter: bad7338b-ae46-43d4-a434-c3337a8054ac
+    resource-orchestrator: osmopenmano
+    description: default description
+    constituent-vnfr-ref:
+    - 303a6ccd-e6f2-4127-96a4-1e3b97956850
+    - 0d0cd621-47db-4eef-a9e8-8edb71a34ea1
+    operational-status: running
+    config-status: failed
+    orchestration-progress: {}
+    create-time: 1658868607.27119
+    nsd-name-ref: nscharm-ns
+    operational-events: []
+    nsd-ref: nscharm-ns
+    nsd-id: a557cb0f-0dc9-494c-a9bd-69e8079767e7
+    vnfd-id:
+    - b5068dc9-a3cd-4a1e-b051-e36c3a9f10a4
+    - 4aa63021-c816-456b-9998-804c5285a85d
+    instantiate_params:
+      nsdId: a557cb0f-0dc9-494c-a9bd-69e8079767e7
+      nsName: ha_charm-ns2
+      nsDescription: default description
+      vimAccountId: bad7338b-ae46-43d4-a434-c3337a8054ac
+      vld:
+      - name: mgmtnet
+        vim-network-name: osm-ext
+    additionalParamsForNs:
+    ns-instance-config-ref: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+    id: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+    ssh-authorized-key:
+    flavor:
+    - id: '0'
+      memory-mb: 1024
+      name: userVM-flv
+      storage-gb: '10'
+      vcpu-count: 1
+      vim_info:
+        vim:bad7338b-ae46-43d4-a434-c3337a8054ac:
+          vim_details:
+          vim_id: 17a9ba76-beb7-4ad4-a481-97de37174866
+          vim_message:
+          vim_status: DONE
+    - id: '1'
+      memory-mb: 1024
+      name: policyVM-flv
+      storage-gb: '10'
+      vcpu-count: 1
+      vim_info:
+        vim:bad7338b-ae46-43d4-a434-c3337a8054ac:
+          vim_details:
+          vim_id: 17a9ba76-beb7-4ad4-a481-97de37174866
+          vim_message:
+          vim_status: DONE
+    image:
+    - id: '0'
+      image: ubuntu18.04
+      vim_info:
+        vim:bad7338b-ae46-43d4-a434-c3337a8054ac:
+          vim_details:
+          vim_id: 919fc71a-6acd-4ee3-8123-739a9abbc2e7
+          vim_message:
+          vim_status: DONE
+    - image: ubuntu/images/hvm-ssd/ubuntu-artful-17.10-amd64-server-20180509
+      vim-type: aws
+      id: '1'
+    - image: Canonical:UbuntuServer:18.04-LTS:latest
+      vim-type: azure
+      id: '2'
+    - image: ubuntu-os-cloud:image-family:ubuntu-1804-lts
+      vim-type: gcp
+      id: '3'
+    affinity-or-anti-affinity-group: []
+    revision: 1
+    vld:
+    - id: mgmtnet
+      mgmt-network: true
+      name: mgmtnet
+      type:
+      vim_info:
+        vim:bad7338b-ae46-43d4-a434-c3337a8054ac:
+          vim_account_id: bad7338b-ae46-43d4-a434-c3337a8054ac
+          vim_network_name: osm-ext
+          vim_details: |
+            {admin_state_up: true, availability_zone_hints: [], availability_zones: [nova], created_at: '2019-10-17T23:44:03Z', description: '', encapsulation: vlan, encapsulation_id: 2148, encapsulation_type: vlan, id: 21ea5d92-24f1-40ab-8d28-83230e277a49, ipv4_address_scope: null,
+              ipv6_address_scope: null, is_default: false, mtu: 1500, name: osm-ext, port_security_enabled: true, project_id: 456b6471010b4737b47a0dd599c920c5, 'provider:network_type': vlan, 'provider:physical_network': physnet1, 'provider:segmentation_id': 2148, revision_number: 1009,
+              'router:external': true, segmentation_id: 2148, shared: true, status: ACTIVE, subnets: [{subnet: {allocation_pools: [{end: 172.21.249.255, start: 172.21.248.1}], cidr: 172.21.248.0/22, created_at: '2019-10-17T23:44:07Z', description: '', dns_nameservers: [],
+                    enable_dhcp: true, gateway_ip: 172.21.251.254, host_routes: [], id: d14f68b7-8287-41fe-b533-dafb2240680a, ip_version: 4, ipv6_address_mode: null, ipv6_ra_mode: null, name: osm-ext-subnet, network_id: 21ea5d92-24f1-40ab-8d28-83230e277a49, project_id: 456b6471010b4737b47a0dd599c920c5,
+                    revision_number: 5, service_types: [], subnetpool_id: null, tags: [], tenant_id: 456b6471010b4737b47a0dd599c920c5, updated_at: '2020-09-14T15:15:06Z'}}], tags: [], tenant_id: 456b6471010b4737b47a0dd599c920c5, type: data, updated_at: '2022-07-05T18:39:02Z'}
+          vim_id: 21ea5d92-24f1-40ab-8d28-83230e277a49
+          vim_message:
+          vim_status: ACTIVE
+    _admin:
+      created: 1658868607.2804
+      modified: 1658868966.10105
+      projects_read:
+      - 51e0e80fe533469d98766caa16552a3e
+      projects_write:
+      - 51e0e80fe533469d98766caa16552a3e
+      nsState: INSTANTIATED
+      current-operation:
+      nslcmop:
+      operation-type:
+      deployed:
+        RO:
+          vnfd: []
+          operational-status: running
+        VCA:
+        - target_element: vnf/1/vdu/userVM/0
+          member-vnf-index: '1'
+          vdu_id: userVM
+          kdu_name:
+          vdu_count_index: 0
+          operational-status: init
+          detailed-status: ''
+          step: initial-deploy
+          vnfd_id: nscharm-user-vnf
+          vdu_name:
+          type: lxc_proxy_charm
+          ee_descriptor_id: vnf-user-ee
+          ee_id: b63aa1ba-996e-43a7-921a-1aca5ccbc63f.app-vnf-3b97956850-z0-vdu-uservm-cnt-z0-eh2hc.2
+          application: app-vnf-3b97956850-z0-vdu-uservm-cnt-z0-eh2hc
+          model: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+          config_sw_installed: true
+        - target_element: vnf/2/vdu/policyVM/0
+          member-vnf-index: '2'
+          vdu_id: policyVM
+          kdu_name:
+          vdu_count_index: 0
+          operational-status: init
+          detailed-status: ''
+          step: initial-deploy
+          vnfd_id: nscharm-policy-vnf
+          vdu_name:
+          type: lxc_proxy_charm
+          ee_descriptor_id: vnf-policy-ee
+          ee_id: b63aa1ba-996e-43a7-921a-1aca5ccbc63f.app-vnf-db71a34ea1-z0-vdu-policyvm-cnt-z0-tr1oc.0
+          application: app-vnf-db71a34ea1-z0-vdu-policyvm-cnt-z0-tr1oc
+          model: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+          config_sw_installed: true
+        - target_element: ns
+          member-vnf-index:
+          vdu_id:
+          kdu_name:
+          vdu_count_index: 0
+          operational-status: init
+          detailed-status: ''
+          step: initial-deploy
+          vnfd_id:
+          vdu_name:
+          type: lxc_proxy_charm
+          ee_descriptor_id:
+          ee_id: b63aa1ba-996e-43a7-921a-1aca5ccbc63f.app-qmfbp.1
+          application: app-qmfbp
+          model: b63aa1ba-996e-43a7-921a-1aca5ccbc63f
+          config_sw_installed: true
+        K8s: []
 """
 
 ro_ns_text = """
index af6f81d..bdd9a8d 100644 (file)
@@ -63,7 +63,6 @@ def check_file_content(health_check_file: str) -> str:
 
 
 class TestLcm(TestCase):
-
     def setUp(self):
         self.config_file = os.getcwd() + "/osm_lcm/tests/test_lcm_config_file.yaml"
         self.config_file_without_storage_path = tempfile.mkstemp()[1]
diff --git a/osm_lcm/tests/test_lcm_utils.py b/osm_lcm/tests/test_lcm_utils.py
new file mode 100644 (file)
index 0000000..13788e2
--- /dev/null
@@ -0,0 +1,267 @@
+# Copyright 2022 Canonical Ltd.
+#
+# 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.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact: alfonso.tiernosepulveda@telefonica.com
+##
+import logging
+import os
+from unittest.mock import Mock, patch, MagicMock
+from unittest import TestCase
+
+from osm_common.msgkafka import MsgKafka
+from osm_common import fslocal
+from osm_lcm.data_utils.database.database import Database
+from osm_lcm.data_utils.filesystem.filesystem import Filesystem
+from osm_lcm.lcm_utils import LcmBase, LcmException
+from osm_lcm.tests import test_db_descriptors as descriptors
+import yaml
+from zipfile import BadZipfile
+
+
+class TestLcmBase(TestCase):
+
+    test_nsr_id = "b63aa1ba-996e-43a7-921a-1aca5ccbc63f"
+    nsd_package_path = "/" + test_nsr_id
+    nsd_package_name = "test_nsd"
+    charm_metadata_file = "/path/charm/metadata.yaml"
+
+    def setUp(self):
+        # DB
+        Database.instance = None
+        self.db = Database({"database": {"driver": "memory"}}).instance.db
+        self.db.create_list("nsds", yaml.safe_load(descriptors.db_nsds_text))
+        self.db.create_list("nsrs", yaml.safe_load(descriptors.db_nsrs_text))
+        # Filesystem
+        self.fs = Filesystem({"storage": {"driver": "local", "path": "/"}}).instance.fs
+        # Create LCMBase class
+        self.msg = Mock(MsgKafka())
+        self.logger = Mock(logging)
+        self.my_ns = LcmBase(self.msg, self.logger)
+        self.my_ns.fs = self.fs
+        self.my_ns.db = self.db
+
+    def test_get_charm_name_successfully(self):
+        instance = self.my_ns
+        mock_open = MagicMock(open)
+        mock_yaml = MagicMock(yaml)
+        mock_yaml.safe_load.return_value = {"name": "test_charm"}
+        expected_result = "test_charm"
+
+        with patch("osm_lcm.lcm_utils.open", mock_open), patch(
+            "osm_lcm.lcm_utils.yaml.safe_load", mock_yaml.safe_load
+        ):
+            result = instance.get_charm_name(TestLcmBase.charm_metadata_file)
+            self.assertEqual(result, expected_result, "wrong charm name")
+            self.assertEqual(mock_yaml.safe_load.call_count, 1)
+            self.assertEqual(mock_open.call_count, 1)
+
+    def test_get_charm_name_can_not_open_metadata_file(self):
+        instance = self.my_ns
+        mock_open = MagicMock(open)
+        mock_open.side_effect = IOError
+        mock_yaml = MagicMock(create_autospec=True)
+
+        with patch("osm_lcm.lcm_utils.open", mock_open), patch(
+            "osm_lcm.lcm_utils.yaml.safe_load", mock_yaml.safe_load
+        ):
+            with self.assertRaises(IOError):
+                instance.get_charm_name(TestLcmBase.charm_metadata_file)
+                mock_yaml.safe_load.assert_not_called()
+                self.assertEqual(mock_open.call_count, 1)
+
+    def test_get_charm_name_wrong_metadata_file_format(self):
+        instance = self.my_ns
+        mock_open = MagicMock(open)
+        mock_yaml = MagicMock(create_autospec=True)
+        mock_yaml.safe_load.return_value = {}
+
+        with patch("osm_lcm.lcm_utils.open", mock_open), patch(
+            "osm_lcm.lcm_utils.yaml.safe_load", mock_yaml.safe_load
+        ):
+            with self.assertRaises(KeyError):
+                instance.get_charm_name(TestLcmBase.charm_metadata_file)
+                self.assertEqual(mock_open.call_count, 1)
+                self.assertEqual(mock_yaml.safe_load.call_count, 1)
+
+    def test_get_charm_path_successfully(self):
+        instance = self.my_ns
+        fs = fslocal.FsLocal()
+        fs.path = "/app/storage"
+        instance.fs = fs
+        charm_folder_name = "simple_charm"
+        expected_result = "/app/storage/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple_charm"
+        result = instance._get_charm_path(
+            TestLcmBase.nsd_package_path, TestLcmBase.nsd_package_name, charm_folder_name
+        )
+        self.assertEqual(result, expected_result, "wrong_charm_path")
+
+    def test_get_charm_metadata_file_charm_is_not_zipped(self):
+        instance = self.my_ns
+        fs = fslocal.FsLocal()
+        fs.path = "/app/storage"
+        instance.fs = fs
+        mock_zipfile = MagicMock(create_autospec=True)
+        charm_folder_name = "simple_charm"
+        charm_path = "/app/storage/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple_charm"
+        expected_result = "/app/storage/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple_charm/metadata.yaml"
+
+        with patch("osm_lcm.lcm_utils.ZipFile", mock_zipfile):
+            result = instance._get_charm_metadata_file(
+                charm_folder_name,
+                TestLcmBase.nsd_package_path,
+                TestLcmBase.nsd_package_name,
+                charm_path=charm_path,
+            )
+            self.assertEqual(result, expected_result, "wrong charm metadata path")
+            mock_zipfile.assert_not_called()
+
+    def test_get_charm_metadata_file_charm_is_zipped(self):
+        instance = self.my_ns
+        fs = fslocal.FsLocal()
+        fs.path = "/app/storage"
+        instance.fs = fs
+        mock_zipfile = MagicMock(create_autospec=True)
+        mock_zipfile.side_effect = None
+        charm_folder_name = "simple_charm2.charm"
+        charm_path = "/app/storage/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple_charm"
+        expected_result = "/app/storage/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple_charm2/metadata.yaml"
+
+        with patch("osm_lcm.lcm_utils.ZipFile", mock_zipfile):
+            result = instance._get_charm_metadata_file(
+                charm_folder_name,
+                TestLcmBase.nsd_package_path,
+                TestLcmBase.nsd_package_name,
+                charm_path=charm_path,
+            )
+            self.assertEqual(result, expected_result, "wrong charm metadata path")
+            self.assertEqual(mock_zipfile.call_count, 1)
+
+    def test_find_charm_name_successfully(self):
+        db_nsr = self.db.get_one("nsrs", {"_id": TestLcmBase.test_nsr_id})
+        instance = self.my_ns
+        mock_listdir = MagicMock(os.listdir())
+        mock_listdir.side_effect = None
+        mock_charm_path = MagicMock()
+        mock_metadata_file = MagicMock()
+        mock_metadata_file.return_value = (
+            "/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple/metadata.yaml"
+        )
+        mock_charm_name = MagicMock()
+        mock_charm_name.return_value = "test_charm"
+        expected_result = "test_charm"
+
+        with patch("osm_lcm.lcm_utils.LcmBase._get_charm_path", mock_charm_path), patch(
+                "osm_lcm.lcm_utils.LcmBase._get_charm_metadata_file", mock_metadata_file
+            ), patch("osm_lcm.lcm_utils.LcmBase.get_charm_name", mock_charm_name), patch(
+                "osm_lcm.lcm_utils.os.listdir", mock_listdir
+        ):
+
+            result = instance.find_charm_name(db_nsr, "simple")
+            self.assertEqual(result, expected_result, "Wrong charm name")
+            mock_charm_path.assert_called_once()
+            mock_metadata_file.assert_called_once()
+            mock_charm_name.assert_called_once_with(
+                "/b63aa1ba-996e-43a7-921a-1aca5ccbc63f/test_nsd/charms/simple/metadata.yaml"
+            )
+            self.assertEqual(mock_listdir.call_count, 1)
+
+    def test_find_charm_name_charm_bad_zipfile(self):
+        db_nsr = self.db.get_one("nsrs", {"_id": TestLcmBase.test_nsr_id})
+        instance = self.my_ns
+        mock_listdir = MagicMock(os.listdir())
+        mock_listdir.side_effect = None
+        mock_charm_path = MagicMock()
+        mock_metadata_file = MagicMock()
+        mock_metadata_file.side_effect = BadZipfile
+        mock_charm_name = MagicMock()
+
+        with patch("osm_lcm.lcm_utils.LcmBase._get_charm_path", mock_charm_path), patch(
+                "osm_lcm.lcm_utils.LcmBase._get_charm_metadata_file", mock_metadata_file
+            ), patch("osm_lcm.lcm_utils.LcmBase.get_charm_name", mock_charm_name), patch(
+                "osm_lcm.lcm_utils.os.listdir", mock_listdir
+        ):
+
+            with self.assertRaises(LcmException):
+
+                instance.find_charm_name(db_nsr, "simple")
+                self.assertEqual(mock_listdir.call_count, 1)
+                self.assertEqual(mock_charm_path.call_count, 1)
+                self.assertEqual(mock_metadata_file.call_count, 1)
+                mock_charm_name.assert_not_called()
+
+    def test_find_charm_name_charm_nsd_package_does_not_exist(self):
+        db_nsr = self.db.get_one("nsrs", {"_id": TestLcmBase.test_nsr_id})
+        instance = self.my_ns
+        mock_listdir = MagicMock(os.listdir())
+        mock_listdir.side_effect = FileNotFoundError
+        mock_charm_path = MagicMock()
+        mock_metadata_file = MagicMock()
+        mock_charm_name = MagicMock()
+
+        with patch("osm_lcm.lcm_utils.LcmBase._get_charm_path", mock_charm_path), patch(
+                "osm_lcm.lcm_utils.LcmBase._get_charm_metadata_file", mock_metadata_file
+            ), patch("osm_lcm.lcm_utils.LcmBase.get_charm_name", mock_charm_name), patch(
+                "osm_lcm.lcm_utils.os.listdir", mock_listdir
+        ):
+
+            with self.assertRaises(LcmException):
+                instance.find_charm_name(db_nsr, "simple")
+                self.assertEqual(mock_listdir.call_count, 1)
+                mock_charm_path.assert_not_called()
+                mock_metadata_file.assert_not_called()
+                mock_charm_name.assert_not_called()
+
+    def test_find_charm_name_missing_input_charm_folder_name(self):
+        db_nsr = self.db.get_one("nsrs", {"_id": TestLcmBase.test_nsr_id})
+        instance = self.my_ns
+        mock_listdir = MagicMock(os.listdir())
+        mock_charm_path = MagicMock()
+        mock_metadata_file = MagicMock()
+        mock_charm_name = MagicMock()
+
+        with patch("osm_lcm.lcm_utils.LcmBase._get_charm_path", mock_charm_path), patch(
+                "osm_lcm.lcm_utils.LcmBase._get_charm_metadata_file", mock_metadata_file
+            ), patch("osm_lcm.lcm_utils.LcmBase.get_charm_name", mock_charm_name), patch(
+                "osm_lcm.lcm_utils.os.listdir", mock_listdir
+        ):
+
+            with self.assertRaises(LcmException):
+                instance.find_charm_name(db_nsr, "")
+                mock_charm_path.assert_not_called()
+                mock_metadata_file.assert_not_called()
+                mock_charm_name.assert_not_called()
+                mock_listdir.assert_not_called()
+
+    def test_find_charm_name_can_not_open_metadata_file(self):
+        db_nsr = self.db.get_one("nsrs", {"_id":  TestLcmBase.test_nsr_id})
+        instance = self.my_ns
+        mock_listdir = MagicMock(os.listdir())
+        mock_charm_path = MagicMock()
+        mock_metadata_file = MagicMock()
+        mock_charm_name = MagicMock()
+        mock_charm_name.side_effect = yaml.YAMLError
+
+        with patch("osm_lcm.lcm_utils.LcmBase._get_charm_path", mock_charm_path), patch(
+                "osm_lcm.lcm_utils.LcmBase._get_charm_metadata_file", mock_metadata_file
+            ), patch("osm_lcm.lcm_utils.LcmBase.get_charm_name", mock_charm_name), patch(
+                "osm_lcm.lcm_utils.os.listdir", mock_listdir
+        ):
+
+            with self.assertRaises(LcmException):
+                instance.find_charm_name(db_nsr, "simple")
+                self.assertEqual(mock_charm_path.call_count, 1)
+                self.assertEqual(mock_metadata_file.call_count, 1)
+                self.assertEqual(mock_charm_name.call_count, 1)
+                self.assertEqual(mock_listdir.call_count, 1)
index 1441e72..560edc9 100644 (file)
@@ -1557,7 +1557,7 @@ class TestMyNS(asynctest.TestCase):
             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 has did not change"):
+        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"