Adds SOL004 package validator with package examples and tests 81/10181/6
authorgarciaale <agarcia@whitestack.com>
Fri, 15 Jan 2021 16:04:05 +0000 (13:04 -0300)
committergarciaale <agarcia@whitestack.com>
Mon, 1 Feb 2021 13:52:45 +0000 (10:52 -0300)
Change-Id: I173700241415c3cc131c99d221a193e8b60e3ded
Signed-off-by: garciaale <agarcia@whitestack.com>
30 files changed:
osm_common/sol004_package.py [new file with mode: 0644]
osm_common/tests/packages/invalid_package_vnf/Definitions/invalid_package_vnfd.yaml [new file with mode: 0644]
osm_common/tests/packages/invalid_package_vnf/Scripts/charms/simple/src/charm.py [new file with mode: 0755]
osm_common/tests/packages/invalid_package_vnf/Scripts/cloud_init/cloud-config.txt [new file with mode: 0755]
osm_common/tests/packages/invalid_package_vnf/TOSCA-Metadata/TOSCA.meta [new file with mode: 0644]
osm_common/tests/packages/invalid_package_vnf/manifest.mf [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Definitions/native_charm_vnfd.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Files/Changelog.txt [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Files/Licenses/License.lic [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/actions.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/config.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/hooks/install [new file with mode: 0755]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/hooks/start [new file with mode: 0755]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/metadata.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/src/charm.py [new file with mode: 0755]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/cloud_init/cloud-config.txt [new file with mode: 0755]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/TOSCA-Metadata/TOSCA.meta [new file with mode: 0644]
osm_common/tests/packages/native_charm_with_metadata_dir_vnf/manifest.mf [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/ChangeLog.txt [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Licenses/License.lic [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/actions.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/config.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/hooks/install [new file with mode: 0755]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/hooks/start [new file with mode: 0755]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/metadata.yaml [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/src/charm.py [new file with mode: 0755]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/cloud_init/cloud-config.txt [new file with mode: 0755]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/native_charm_vnfd.mf [new file with mode: 0644]
osm_common/tests/packages/native_charm_without_metadata_dir_vnf/native_charm_vnfd.yaml [new file with mode: 0644]
osm_common/tests/test_sol004_package.py [new file with mode: 0644]

diff --git a/osm_common/sol004_package.py b/osm_common/sol004_package.py
new file mode 100644 (file)
index 0000000..7d402f5
--- /dev/null
@@ -0,0 +1,205 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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: agarcia@whitestack.com
+##
+
+"""Python module for interacting with ETSI GS NFV-SOL004 compliant packages
+
+This module provides a SOL004Package class for validating and interacting with
+ETSI SOL004 packages. A valid SOL004 package may have its files arranged according
+to one of the following two structures:
+
+SOL004 with metadata directory    SOL004 without metadata directory
+
+native_charm_vnf/                 native_charm_vnf/
+├── TOSCA-Metadata                ├── native_charm_vnfd.mf
+│   └── TOSCA.meta                ├── native_charm_vnfd.yaml
+├── manifest.mf                   ├── ChangeLog.txt
+├── Definitions                   ├── Licenses
+│   └── native_charm_vnfd.yaml    │   └── license.lic
+├── Files                         ├── Files
+│   ├── icons                     │   └── icons
+│   │   └── osm.png               │       └── osm.png
+│   ├── Licenses                  └── Scripts
+│   │   └── license.lic               ├── cloud_init
+│   └── changelog.txt                 │   └── cloud-config.txt
+└── Scripts                           └── charms
+    ├── cloud_init                        └── simple
+    │   └── cloud-config.txt                  ├── config.yaml
+    └── charms                                ├── hooks
+        └── simple                            │   ├── install
+            ├── config.yaml                  ...
+            ├── hooks                         │
+            │   ├── install                   └── src
+           ...                                    └── charm.py
+            └── src
+                └── charm.py
+"""
+
+import yaml
+import os
+import hashlib
+
+
+_METADATA_FILE_PATH = 'TOSCA-Metadata/TOSCA.meta'
+_METADATA_DESCRIPTOR_FIELD = 'Entry-Definitions'
+_METADATA_MANIFEST_FIELD = 'ETSI-Entry-Manifest'
+_METADATA_CHANGELOG_FIELD = 'ETSI-Entry-Change-Log'
+_METADATA_LICENSES_FIELD = 'ETSI-Entry-Licenses'
+_METADATA_DEFAULT_CHANGELOG_PATH = 'ChangeLog.txt'
+_METADATA_DEFAULT_LICENSES_PATH = 'Licenses'
+_MANIFEST_FILE_PATH_FIELD = 'Source'
+_MANIFEST_FILE_HASH_ALGORITHM_FIELD = 'Algorithm'
+_MANIFEST_FILE_HASH_DIGEST_FIELD = 'Hash'
+
+
+class SOL004PackageException(Exception):
+    pass
+
+
+class SOL004Package:
+    def __init__(self, package_path=''):
+        self._package_path = package_path
+        self._package_metadata = self._parse_package_metadata()
+        self._manifest_data = self._parse_manifest_data()
+
+    def _parse_package_metadata(self):
+        try:
+            return self._parse_package_metadata_with_metadata_dir()
+        except FileNotFoundError:
+            return self._parse_package_metadata_without_metadata_dir()
+
+    def _parse_package_metadata_with_metadata_dir(self):
+        try:
+            return self._parse_file_in_blocks(_METADATA_FILE_PATH)
+        except FileNotFoundError as e:
+            raise e
+        except (Exception, OSError) as e:
+            raise SOL004PackageException('Error parsing {}: {}'.format(_METADATA_FILE_PATH, e))
+
+    def _parse_package_metadata_without_metadata_dir(self):
+        package_root_files = {f for f in os.listdir(self._package_path)}
+        package_root_yamls = [f for f in package_root_files if f.endswith('.yml') or f.endswith('.yaml')]
+        if len(package_root_yamls) != 1:
+            error_msg = 'Error parsing package metadata: there should be exactly 1 descriptor YAML, found {}'
+            raise SOL004PackageException(error_msg.format(len(package_root_yamls)))
+        # TODO: Parse extra metadata from descriptor YAML?
+        return [{
+            _METADATA_DESCRIPTOR_FIELD: package_root_yamls[0],
+            _METADATA_MANIFEST_FIELD: '{}.mf'.format(os.path.splitext(package_root_yamls[0])[0]),
+            _METADATA_CHANGELOG_FIELD: _METADATA_DEFAULT_CHANGELOG_PATH,
+            _METADATA_LICENSES_FIELD: _METADATA_DEFAULT_LICENSES_PATH
+        }]
+
+    def _parse_manifest_data(self):
+        manifest_path = None
+        for tosca_meta in self._package_metadata:
+            if _METADATA_MANIFEST_FIELD in tosca_meta:
+                manifest_path = tosca_meta[_METADATA_MANIFEST_FIELD]
+                break
+        else:
+            error_msg = 'Error parsing {}: no {} field on path'.format(_METADATA_FILE_PATH, _METADATA_MANIFEST_FIELD)
+            raise SOL004PackageException(error_msg)
+
+        try:
+            return self._parse_file_in_blocks(manifest_path)
+        except (Exception, OSError) as e:
+            raise SOL004PackageException('Error parsing {}: {}'.format(manifest_path, e))
+
+    def _get_package_file_full_path(self, file_relative_path):
+        return os.path.join(self._package_path, file_relative_path)
+
+    def _parse_file_in_blocks(self, file_relative_path):
+        file_path = self._get_package_file_full_path(file_relative_path)
+        with open(file_path) as f:
+            blocks = f.read().split('\n\n')
+        parsed_blocks = map(yaml.safe_load, blocks)
+        return [block for block in parsed_blocks if block is not None]
+
+    def _get_package_file_manifest_data(self, file_relative_path):
+        for file_data in self._manifest_data:
+            if file_data.get(_MANIFEST_FILE_PATH_FIELD, '') == file_relative_path:
+                return file_data
+
+        error_msg = 'Error parsing {} manifest data: file not found on manifest file'.format(file_relative_path)
+        raise SOL004PackageException(error_msg)
+
+    def get_package_file_hash_digest_from_manifest(self, file_relative_path):
+        """Returns the hash digest of a file inside this package as specified on the manifest file."""
+        file_manifest_data = self._get_package_file_manifest_data(file_relative_path)
+        try:
+            return file_manifest_data[_MANIFEST_FILE_HASH_DIGEST_FIELD]
+        except Exception as e:
+            raise SOL004PackageException('Error parsing {} hash digest: {}'.format(file_relative_path, e))
+
+    def get_package_file_hash_algorithm_from_manifest(self, file_relative_path):
+        """Returns the hash algorithm of a file inside this package as specified on the manifest file."""
+        file_manifest_data = self._get_package_file_manifest_data(file_relative_path)
+        try:
+            return file_manifest_data[_MANIFEST_FILE_HASH_ALGORITHM_FIELD]
+        except Exception as e:
+            raise SOL004PackageException('Error parsing {} hash digest: {}'.format(file_relative_path, e))
+
+    @staticmethod
+    def _get_hash_function_from_hash_algorithm(hash_algorithm):
+        function_to_algorithm = {
+            'SHA-256': hashlib.sha256,
+            'SHA-512': hashlib.sha512
+        }
+        if hash_algorithm not in function_to_algorithm:
+            error_msg = 'Error checking hash function: hash algorithm {} not supported'.format(hash_algorithm)
+            raise SOL004PackageException(error_msg)
+        return function_to_algorithm[hash_algorithm]
+
+    def _calculate_file_hash(self, file_relative_path, hash_algorithm):
+        file_path = self._get_package_file_full_path(file_relative_path)
+        hash_function = self._get_hash_function_from_hash_algorithm(hash_algorithm)
+        try:
+            with open(file_path, "rb") as f:
+                return hash_function(f.read()).hexdigest()
+        except Exception as e:
+            raise SOL004PackageException('Error hashing {}: {}'.format(file_relative_path, e))
+
+    def validate_package_file_hash(self, file_relative_path):
+        """Validates the integrity of a file using the hash algorithm and digest on the package manifest."""
+        hash_algorithm = self.get_package_file_hash_algorithm_from_manifest(file_relative_path)
+        file_hash = self._calculate_file_hash(file_relative_path, hash_algorithm)
+        expected_file_hash = self.get_package_file_hash_digest_from_manifest(file_relative_path)
+        if file_hash != expected_file_hash:
+            error_msg = 'Error validating {} hash: calculated hash {} is different than manifest hash {}'
+            raise SOL004PackageException(error_msg.format(file_relative_path, file_hash, expected_file_hash))
+
+    def validate_package_hashes(self):
+        """Validates the integrity of all files listed on the package manifest."""
+        for file_data in self._manifest_data:
+            if _MANIFEST_FILE_PATH_FIELD in file_data:
+                file_relative_path = file_data[_MANIFEST_FILE_PATH_FIELD]
+                self.validate_package_file_hash(file_relative_path)
+
+    def get_descriptor_location(self):
+        """Returns this package descriptor location as a relative path from the package root."""
+        for tosca_meta in self._package_metadata:
+            if _METADATA_DESCRIPTOR_FIELD in tosca_meta:
+                return tosca_meta[_METADATA_DESCRIPTOR_FIELD]
+
+        error_msg = 'Error: no {} entry found on {}'.format(_METADATA_DESCRIPTOR_FIELD, _METADATA_FILE_PATH)
+        raise SOL004PackageException(error_msg)
diff --git a/osm_common/tests/packages/invalid_package_vnf/Definitions/invalid_package_vnfd.yaml b/osm_common/tests/packages/invalid_package_vnf/Definitions/invalid_package_vnfd.yaml
new file mode 100644 (file)
index 0000000..cd94158
--- /dev/null
@@ -0,0 +1,97 @@
+#
+# 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.
+#
+
+vnfd:
+  description: A VNF consisting of 1 VDU connected to two external VL, and one for
+    data and another one for management
+  df:
+  - id: default-df
+    instantiation-level:
+    - id: default-instantiation-level
+      vdu-level:
+      - number-of-instances: 1
+        vdu-id: mgmtVM
+    vdu-profile:
+    - id: mgmtVM
+      min-number-of-instances: 1
+      vdu-configuration-id: mgmtVM-vdu-configuration
+  ext-cpd:
+  - id: vnf-mgmt-ext
+    int-cpd:
+      cpd: mgmtVM-eth0-int
+      vdu-id: mgmtVM
+  - id: vnf-data-ext
+    int-cpd:
+      cpd: dataVM-xe0-int
+      vdu-id: mgmtVM
+  id: native_charm-vnf
+  mgmt-cp: vnf-mgmt-ext
+  product-name: native_charm-vnf
+  sw-image-desc:
+  - id: ubuntu18.04
+    image: ubuntu18.04
+    name: ubuntu18.04
+  vdu:
+  - cloud-init-file: cloud-config.txt
+    id: mgmtVM
+    int-cpd:
+    - id: mgmtVM-eth0-int
+      virtual-network-interface-requirement:
+      - name: mgmtVM-eth0
+        position: 1
+        virtual-interface:
+          type: PARAVIRT
+    - id: dataVM-xe0-int
+      virtual-network-interface-requirement:
+      - name: dataVM-xe0
+        position: 2
+        virtual-interface:
+          type: PARAVIRT
+    name: mgmtVM
+    sw-image-desc: ubuntu18.04
+    virtual-compute-desc: mgmtVM-compute
+    virtual-storage-desc:
+    - mgmtVM-storage
+  vdu-configuration:
+  - config-access:
+      ssh-access:
+        default-user: ubuntu
+        required: true
+    config-primitive:
+    - name: touch
+      parameter:
+      - data-type: STRING
+        default-value: /home/ubuntu/touched
+        name: filename
+    id: mgmtVM-vdu-configuration
+    initial-config-primitive:
+    - name: touch
+      parameter:
+      - data-type: STRING
+        name: filename
+        value: /home/ubuntu/first-touch
+      seq: 1
+    juju:
+      charm: simple
+      proxy: false
+  version: 1.0
+  virtual-compute-desc:
+  - id: mgmtVM-compute
+    virtual-cpu:
+      num-virtual-cpu: 1
+    virtual-memory:
+      size: 1.0
+  virtual-storage-desc:
+  - id: mgmtVM-storage
+    size-of-storage: 10
diff --git a/osm_common/tests/packages/invalid_package_vnf/Scripts/charms/simple/src/charm.py b/osm_common/tests/packages/invalid_package_vnf/Scripts/charms/simple/src/charm.py
new file mode 100755 (executable)
index 0000000..409f286
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+sys.path.append("lib")
+
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
diff --git a/osm_common/tests/packages/invalid_package_vnf/Scripts/cloud_init/cloud-config.txt b/osm_common/tests/packages/invalid_package_vnf/Scripts/cloud_init/cloud-config.txt
new file mode 100755 (executable)
index 0000000..7a83e12
--- /dev/null
@@ -0,0 +1,32 @@
+# \r
+# Copyright 2020 Whitestack, LLC\r
+# *************************************************************\r
+#\r
+# This file is part of OSM common repository.\r
+# All Rights Reserved to Whitestack, LLC\r
+#\r
+# Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+# not use this file except in compliance with the License. You may obtain\r
+# a copy of the License at\r
+#\r
+#         http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+# License for the specific language governing permissions and limitations\r
+# under the License.\r
+#\r
+\r
+#cloud-config\r
+password: osm4u\r
+chpasswd: { expire: False }\r
+ssh_pwauth: True\r
+\r
+write_files:\r
+-   content: |\r
+        # My new helloworld file\r
+\r
+    owner: root:root\r
+    permissions: '0644'\r
+    path: /root/helloworld.txt\r
diff --git a/osm_common/tests/packages/invalid_package_vnf/TOSCA-Metadata/TOSCA.meta b/osm_common/tests/packages/invalid_package_vnf/TOSCA-Metadata/TOSCA.meta
new file mode 100644 (file)
index 0000000..ddbdf13
--- /dev/null
@@ -0,0 +1,38 @@
+#
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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: agarcia@whitestack.com
+##
+
+TOSCA-Meta-Version: 1.0
+CSAR-Version: 1.0
+Created-By: Diego Armando Maradona
+Entry-Definitions: Definitions/native_charm_vnfd.yaml # Points to the main descriptor of the package
+ETSI-Entry-Manifest: manifest.mf # Points to the ETSI manifest file
+ETSI-Entry-Change-Log: Files/Changelog.txt # Points to package changelog
+ETSI-Entry-Licenses: Files/Licenses # Points to package licenses folder
+
+# In principle, we could add one block per package file to specify MIME types
+Name: Definitions/native_charm_vnfd.yaml            # path to file within package
+Content-Type: application/yaml                      # MIME type of file
+
+Name: Scripts/cloud_init/cloud-config.txt
+Content-Type: application/yaml
+
diff --git a/osm_common/tests/packages/invalid_package_vnf/manifest.mf b/osm_common/tests/packages/invalid_package_vnf/manifest.mf
new file mode 100644 (file)
index 0000000..dacf77f
--- /dev/null
@@ -0,0 +1,57 @@
+# 
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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: agarcia@whitestack.com
+##
+
+# General definitions of the package
+vnfd_id: native_charm-vnf
+vnf_product_name: native_charm-vnf
+vnf_provider_id: AFA
+vnf_software_version: 1.0
+vnf_package_version: 1.0
+vnf_release_date_time: 2021.12.01T11:36-03:00
+compatible_specification_versions: 3.3.1
+vnfm_info: OSM
+
+Source: Definitions/native_charm_vnfd.yaml
+Algorithm: SHA-256
+Hash: ede8daf9748ac4849e1a1aac955d6c84cafef9ea34067eaef76ee4e5996974c2
+
+Source: Scripts/cloud_init/cloud-config.txt
+Algorithm: SHA-256
+Hash: 7455ca868843cc5da1f0a2255cdedb64a69df3b618c344b83b82848a94540eda
+
+
+# The below sections are all wrong on purpose as they are intended for testing
+
+# Invalid hash algorithm
+Source: Scripts/charms/simple/src/charm.py
+Algorithm: SHA-733
+Hash: 7895f7b9e1b7ed5b5bcd64398950ca95b456d7fc973334351474eed466c2f480
+
+# Wrong hash
+Source: Scripts/charms/simple/hooks/start
+Algorithm: SHA-256
+Hash: 123456aaaaaa123456aaaaaae2bb9d0197f41619165dde6cf205c974f9aa86ae
+
+# Unspecified hash
+Source: Scripts/charms/simple/hooks/upgrade-charm
+Algorithm: SHA-256
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Definitions/native_charm_vnfd.yaml b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Definitions/native_charm_vnfd.yaml
new file mode 100644 (file)
index 0000000..cd94158
--- /dev/null
@@ -0,0 +1,97 @@
+#
+# 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.
+#
+
+vnfd:
+  description: A VNF consisting of 1 VDU connected to two external VL, and one for
+    data and another one for management
+  df:
+  - id: default-df
+    instantiation-level:
+    - id: default-instantiation-level
+      vdu-level:
+      - number-of-instances: 1
+        vdu-id: mgmtVM
+    vdu-profile:
+    - id: mgmtVM
+      min-number-of-instances: 1
+      vdu-configuration-id: mgmtVM-vdu-configuration
+  ext-cpd:
+  - id: vnf-mgmt-ext
+    int-cpd:
+      cpd: mgmtVM-eth0-int
+      vdu-id: mgmtVM
+  - id: vnf-data-ext
+    int-cpd:
+      cpd: dataVM-xe0-int
+      vdu-id: mgmtVM
+  id: native_charm-vnf
+  mgmt-cp: vnf-mgmt-ext
+  product-name: native_charm-vnf
+  sw-image-desc:
+  - id: ubuntu18.04
+    image: ubuntu18.04
+    name: ubuntu18.04
+  vdu:
+  - cloud-init-file: cloud-config.txt
+    id: mgmtVM
+    int-cpd:
+    - id: mgmtVM-eth0-int
+      virtual-network-interface-requirement:
+      - name: mgmtVM-eth0
+        position: 1
+        virtual-interface:
+          type: PARAVIRT
+    - id: dataVM-xe0-int
+      virtual-network-interface-requirement:
+      - name: dataVM-xe0
+        position: 2
+        virtual-interface:
+          type: PARAVIRT
+    name: mgmtVM
+    sw-image-desc: ubuntu18.04
+    virtual-compute-desc: mgmtVM-compute
+    virtual-storage-desc:
+    - mgmtVM-storage
+  vdu-configuration:
+  - config-access:
+      ssh-access:
+        default-user: ubuntu
+        required: true
+    config-primitive:
+    - name: touch
+      parameter:
+      - data-type: STRING
+        default-value: /home/ubuntu/touched
+        name: filename
+    id: mgmtVM-vdu-configuration
+    initial-config-primitive:
+    - name: touch
+      parameter:
+      - data-type: STRING
+        name: filename
+        value: /home/ubuntu/first-touch
+      seq: 1
+    juju:
+      charm: simple
+      proxy: false
+  version: 1.0
+  virtual-compute-desc:
+  - id: mgmtVM-compute
+    virtual-cpu:
+      num-virtual-cpu: 1
+    virtual-memory:
+      size: 1.0
+  virtual-storage-desc:
+  - id: mgmtVM-storage
+    size-of-storage: 10
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Files/Changelog.txt b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Files/Changelog.txt
new file mode 100644 (file)
index 0000000..a9abe70
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# 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.
+#
+
+1.0.0: First version
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Files/Licenses/License.lic b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Files/Licenses/License.lic
new file mode 100644 (file)
index 0000000..d120f18
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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.
+#
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/actions.yaml b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/actions.yaml
new file mode 100644 (file)
index 0000000..53a706b
--- /dev/null
@@ -0,0 +1,25 @@
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# 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.
+##
+touch:
+  description: "Touch a file on the VNF."
+  params:
+    filename:
+      description: "The name of the file to touch."
+      type: string
+      default: ""
+  required:
+    - filename
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/config.yaml b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/config.yaml
new file mode 100644 (file)
index 0000000..2be6231
--- /dev/null
@@ -0,0 +1,17 @@
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# 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.
+##
+options: {}
\ No newline at end of file
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/hooks/install b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/hooks/install
new file mode 100755 (executable)
index 0000000..9ef7d07
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+sys.path.append("lib")
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
+
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/hooks/start b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/hooks/start
new file mode 100755 (executable)
index 0000000..9ef7d07
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+sys.path.append("lib")
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
+
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/metadata.yaml b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/metadata.yaml
new file mode 100644 (file)
index 0000000..5e832fb
--- /dev/null
@@ -0,0 +1,25 @@
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# 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.
+##
+
+name: simple-native
+summary: A simple native charm
+description: |
+  Simple native charm
+series:
+  - bionic
+  - xenial
+  - focal
\ No newline at end of file
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/src/charm.py b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/charms/simple/src/charm.py
new file mode 100755 (executable)
index 0000000..409f286
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+sys.path.append("lib")
+
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/cloud_init/cloud-config.txt b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/Scripts/cloud_init/cloud-config.txt
new file mode 100755 (executable)
index 0000000..f5d56f6
--- /dev/null
@@ -0,0 +1,33 @@
+# \r
+# Copyright 2020 Whitestack, LLC\r
+# *************************************************************\r
+#\r
+# This file is part of OSM common repository.\r
+# All Rights Reserved to Whitestack, LLC\r
+#\r
+# Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+# not use this file except in compliance with the License. You may obtain\r
+# a copy of the License at\r
+#\r
+#         http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+# License for the specific language governing permissions and limitations\r
+# under the License.\r
+#\r
+\r
+\r
+#cloud-config\r
+password: osm4u\r
+chpasswd: { expire: False }\r
+ssh_pwauth: True\r
+\r
+write_files:\r
+-   content: |\r
+        # My new helloworld file\r
+\r
+    owner: root:root\r
+    permissions: '0644'\r
+    path: /root/helloworld.txt\r
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/TOSCA-Metadata/TOSCA.meta b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/TOSCA-Metadata/TOSCA.meta
new file mode 100644 (file)
index 0000000..21b6dea
--- /dev/null
@@ -0,0 +1,31 @@
+#
+# 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: agarcia@whitestack.com
+##
+
+TOSCA-Meta-Version: 1.0
+CSAR-Version: 1.0
+Created-By: Diego Armando Maradona
+Entry-Definitions: Definitions/native_charm_vnfd.yaml # Points to the main descriptor of the package
+ETSI-Entry-Manifest: manifest.mf # Points to the ETSI manifest file
+ETSI-Entry-Change-Log: Files/Changelog.txt # Points to package changelog
+ETSI-Entry-Licenses: Files/Licenses # Points to package licenses folder
+
+# In principle, we could add one block per package file to specify MIME types
+Name: Definitions/native_charm_vnfd.yaml            # path to file within package
+Content-Type: application/yaml                      # MIME type of file
+
+Name: Scripts/cloud_init/cloud-config.txt
+Content-Type: application/yaml
\ No newline at end of file
diff --git a/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/manifest.mf b/osm_common/tests/packages/native_charm_with_metadata_dir_vnf/manifest.mf
new file mode 100644 (file)
index 0000000..b42c240
--- /dev/null
@@ -0,0 +1,72 @@
+# 
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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: agarcia@whitestack.com
+##
+
+# General definitions of the package
+vnfd_id: native_charm-vnf
+vnf_product_name: native_charm-vnf
+vnf_provider_id: AFA
+vnf_software_version: 1.0
+vnf_package_version: 1.0
+vnf_release_date_time: 2021.12.01T11:36-03:00
+compatible_specification_versions: 3.3.1
+vnfm_info: OSM
+
+# One block for every file in the package
+Source: Definitions/native_charm_vnfd.yaml
+Algorithm: SHA-256
+Hash: ede8daf9748ac4849e1a1aac955d6c84cafef9ea34067eaef76ee4e5996974c2
+
+
+
+Source: Scripts/cloud_init/cloud-config.txt
+Algorithm: SHA-256
+Hash: 0eef3f1a642339e2053af48a7e370dac1952f9cb81166e439e8f72afd6f03621
+
+# Charms files
+
+Source: Scripts/charms/simple/src/charm.py
+Algorithm: SHA-256
+Hash: 7895f7b9e1b7ed5b5bcd64398950ca95b456d7fc973334351474eed466c2f480
+
+Source: Scripts/charms/simple/hooks/start
+Algorithm: SHA-256
+Hash: 312490afd82cc86ad823e4d9e2bb9d0197f41619165dde6cf205c974f9aa86ae
+
+Source: Scripts/charms/simple/hooks/install
+Algorithm: SHA-256
+Hash: 312490afd82cc86ad823e4d9e2bb9d0197f41619165dde6cf205c974f9aa86ae
+
+Source: Scripts/charms/simple/actions.yaml
+Algorithm: SHA-256
+Hash: 988ca2653ae6a3977149faaebd664a12858e0025f226b27d2cee1fa954c9462d
+
+Source: Scripts/charms/simple/metadata.yaml
+Algorithm: SHA-256
+Hash: e00cfaf41a518aef0f486e4ae04a5ae19feffa774abfbdb68379bb5b5b102479
+
+Source: Scripts/charms/simple/config.yaml
+Algorithm: SHA-256
+Hash: f5cbf31b9c299504f3b577417b6c82bde5e3eafd74ee11fdeecf8c8bff6cf3e2
+
+
+# And on and on
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/ChangeLog.txt b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/ChangeLog.txt
new file mode 100644 (file)
index 0000000..a9abe70
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# 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.
+#
+
+1.0.0: First version
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Licenses/License.lic b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Licenses/License.lic
new file mode 100644 (file)
index 0000000..d120f18
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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.
+#
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/actions.yaml b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/actions.yaml
new file mode 100644 (file)
index 0000000..53a706b
--- /dev/null
@@ -0,0 +1,25 @@
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# 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.
+##
+touch:
+  description: "Touch a file on the VNF."
+  params:
+    filename:
+      description: "The name of the file to touch."
+      type: string
+      default: ""
+  required:
+    - filename
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/config.yaml b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/config.yaml
new file mode 100644 (file)
index 0000000..2be6231
--- /dev/null
@@ -0,0 +1,17 @@
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# 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.
+##
+options: {}
\ No newline at end of file
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/hooks/install b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/hooks/install
new file mode 100755 (executable)
index 0000000..9ef7d07
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+sys.path.append("lib")
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
+
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/hooks/start b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/hooks/start
new file mode 100755 (executable)
index 0000000..9ef7d07
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+sys.path.append("lib")
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
+
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/metadata.yaml b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/metadata.yaml
new file mode 100644 (file)
index 0000000..5e832fb
--- /dev/null
@@ -0,0 +1,25 @@
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# 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.
+##
+
+name: simple-native
+summary: A simple native charm
+description: |
+  Simple native charm
+series:
+  - bionic
+  - xenial
+  - focal
\ No newline at end of file
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/src/charm.py b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/charms/simple/src/charm.py
new file mode 100755 (executable)
index 0000000..409f286
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/env python3
+##
+# Copyright 2020 Canonical Ltd.
+# All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+##
+
+import sys
+import subprocess
+
+from ops.charm import CharmBase
+from ops.main import main
+from ops.model import ActiveStatus
+
+sys.path.append("lib")
+
+
+class MyNativeCharm(CharmBase):
+
+    def __init__(self, framework, key):
+        super().__init__(framework, key)
+
+        # Listen to charm events
+        self.framework.observe(self.on.config_changed, self.on_config_changed)
+        self.framework.observe(self.on.install, self.on_install)
+        self.framework.observe(self.on.start, self.on_start)
+
+        # Listen to the touch action event
+        self.framework.observe(self.on.touch_action, self.on_touch_action)
+
+    def on_config_changed(self, event):
+        """Handle changes in configuration"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_install(self, event):
+        """Called when the charm is being installed"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_start(self, event):
+        """Called when the charm is being started"""
+        self.model.unit.status = ActiveStatus()
+
+    def on_touch_action(self, event):
+        """Touch a file."""
+
+        filename = event.params["filename"]
+        try:
+            subprocess.run(["touch", filename], check=True)
+            event.set_results({"created": True, "filename": filename})
+        except subprocess.CalledProcessError as e:
+            event.fail("Action failed: {}".format(e))
+        self.model.unit.status = ActiveStatus()
+
+
+if __name__ == "__main__":
+    main(MyNativeCharm)
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/cloud_init/cloud-config.txt b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/Scripts/cloud_init/cloud-config.txt
new file mode 100755 (executable)
index 0000000..f5d56f6
--- /dev/null
@@ -0,0 +1,33 @@
+# \r
+# Copyright 2020 Whitestack, LLC\r
+# *************************************************************\r
+#\r
+# This file is part of OSM common repository.\r
+# All Rights Reserved to Whitestack, LLC\r
+#\r
+# Licensed under the Apache License, Version 2.0 (the "License"); you may\r
+# not use this file except in compliance with the License. You may obtain\r
+# a copy of the License at\r
+#\r
+#         http://www.apache.org/licenses/LICENSE-2.0\r
+#\r
+# Unless required by applicable law or agreed to in writing, software\r
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\r
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\r
+# License for the specific language governing permissions and limitations\r
+# under the License.\r
+#\r
+\r
+\r
+#cloud-config\r
+password: osm4u\r
+chpasswd: { expire: False }\r
+ssh_pwauth: True\r
+\r
+write_files:\r
+-   content: |\r
+        # My new helloworld file\r
+\r
+    owner: root:root\r
+    permissions: '0644'\r
+    path: /root/helloworld.txt\r
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/native_charm_vnfd.mf b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/native_charm_vnfd.mf
new file mode 100644 (file)
index 0000000..d948858
--- /dev/null
@@ -0,0 +1,72 @@
+# 
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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: agarcia@whitestack.com
+##
+
+# General definitions of the package
+vnfd_id: native_charm-vnf
+vnf_product_name: native_charm-vnf
+vnf_provider_id: AFA
+vnf_software_version: 1.0
+vnf_package_version: 1.0
+vnf_release_date_time: 2021.12.01T11:36-03:00
+compatible_specification_versions: 3.3.1
+vnfm_info: OSM
+
+# One block for every file in the package
+Source: native_charm_vnfd.yaml
+Algorithm: SHA-256
+Hash: ae06780c082041676df4ca4130ef223548eee6389007ba259416f59044450a7c
+
+
+
+Source: Scripts/cloud_init/cloud-config.txt
+Algorithm: SHA-256
+Hash: 0eef3f1a642339e2053af48a7e370dac1952f9cb81166e439e8f72afd6f03621
+
+# Charms files
+
+Source: Scripts/charms/simple/src/charm.py
+Algorithm: SHA-256
+Hash: 7895f7b9e1b7ed5b5bcd64398950ca95b456d7fc973334351474eed466c2f480
+
+Source: Scripts/charms/simple/hooks/start
+Algorithm: SHA-256
+Hash: 312490afd82cc86ad823e4d9e2bb9d0197f41619165dde6cf205c974f9aa86ae
+
+Source: Scripts/charms/simple/hooks/install
+Algorithm: SHA-256
+Hash: 312490afd82cc86ad823e4d9e2bb9d0197f41619165dde6cf205c974f9aa86ae
+
+Source: Scripts/charms/simple/actions.yaml
+Algorithm: SHA-256
+Hash: 988ca2653ae6a3977149faaebd664a12858e0025f226b27d2cee1fa954c9462d
+
+Source: Scripts/charms/simple/metadata.yaml
+Algorithm: SHA-256
+Hash: e00cfaf41a518aef0f486e4ae04a5ae19feffa774abfbdb68379bb5b5b102479
+
+Source: Scripts/charms/simple/config.yaml
+Algorithm: SHA-256
+Hash: f5cbf31b9c299504f3b577417b6c82bde5e3eafd74ee11fdeecf8c8bff6cf3e2
+
+
+# And on and on
diff --git a/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/native_charm_vnfd.yaml b/osm_common/tests/packages/native_charm_without_metadata_dir_vnf/native_charm_vnfd.yaml
new file mode 100644 (file)
index 0000000..2af0f0f
--- /dev/null
@@ -0,0 +1,103 @@
+#
+# 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.
+#
+
+metadata:
+  template_name: native_charm-vnf
+  template_author: AFA
+  template_version: 1.1
+
+vnfd:
+  description: A VNF consisting of 1 VDU connected to two external VL, and one for
+    data and another one for management
+  df:
+  - id: default-df
+    instantiation-level:
+    - id: default-instantiation-level
+      vdu-level:
+      - number-of-instances: 1
+        vdu-id: mgmtVM
+    vdu-profile:
+    - id: mgmtVM
+      min-number-of-instances: 1
+      vdu-configuration-id: mgmtVM-vdu-configuration
+  ext-cpd:
+  - id: vnf-mgmt-ext
+    int-cpd:
+      cpd: mgmtVM-eth0-int
+      vdu-id: mgmtVM
+  - id: vnf-data-ext
+    int-cpd:
+      cpd: dataVM-xe0-int
+      vdu-id: mgmtVM
+  id: native_charm-vnf
+  mgmt-cp: vnf-mgmt-ext
+  product-name: native_charm-vnf
+  provider: AFA
+  sw-image-desc:
+  - id: ubuntu18.04
+    image: ubuntu18.04
+    name: ubuntu18.04
+  vdu:
+  - cloud-init-file: cloud-config.txt
+    id: mgmtVM
+    int-cpd:
+    - id: mgmtVM-eth0-int
+      virtual-network-interface-requirement:
+      - name: mgmtVM-eth0
+        position: 1
+        virtual-interface:
+          type: PARAVIRT
+    - id: dataVM-xe0-int
+      virtual-network-interface-requirement:
+      - name: dataVM-xe0
+        position: 2
+        virtual-interface:
+          type: PARAVIRT
+    name: mgmtVM
+    sw-image-desc: ubuntu18.04
+    virtual-compute-desc: mgmtVM-compute
+    virtual-storage-desc:
+    - mgmtVM-storage
+  vdu-configuration:
+  - config-access:
+      ssh-access:
+        default-user: ubuntu
+        required: true
+    config-primitive:
+    - name: touch
+      parameter:
+      - data-type: STRING
+        default-value: /home/ubuntu/touched
+        name: filename
+    id: mgmtVM-vdu-configuration
+    initial-config-primitive:
+    - name: touch
+      parameter:
+      - data-type: STRING
+        name: filename
+        value: /home/ubuntu/first-touch
+      seq: 1
+    juju:
+      charm: simple
+      proxy: false
+  version: 1.0
+  virtual-compute-desc:
+  - id: mgmtVM-compute
+    virtual-cpu:
+      num-virtual-cpu: 1
+    virtual-memory:
+      size: 1.0
+  virtual-storage-desc:
+  - id: mgmtVM-storage
+    size-of-storage: 10
diff --git a/osm_common/tests/test_sol004_package.py b/osm_common/tests/test_sol004_package.py
new file mode 100644 (file)
index 0000000..b9f13af
--- /dev/null
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2020 Whitestack, LLC
+# *************************************************************
+#
+# This file is part of OSM common repository.
+# All Rights Reserved to Whitestack, LLC
+#
+# 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: agarcia@whitestack.com
+##
+
+from osm_common.sol004_package import SOL004Package, SOL004PackageException
+import unittest
+
+
+class SOL004ValidatorTest(unittest.TestCase):
+    def test_get_package_file_hash_algorithm_from_manifest_with_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        algorithm = package.get_package_file_hash_algorithm_from_manifest('Scripts/charms/simple/src/charm.py')
+        self.assertEqual(algorithm, 'SHA-256')
+
+    def test_get_package_file_hash_algorithm_from_manifest_without_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_without_metadata_dir_vnf')
+        algorithm = package.get_package_file_hash_algorithm_from_manifest('Scripts/charms/simple/src/charm.py')
+        self.assertEqual(algorithm, 'SHA-256')
+
+    def test_get_package_file_hash_algorithm_from_manifest_on_non_existent_file(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.get_package_file_hash_algorithm_from_manifest('Non/Existing/file')
+
+    def test_get_package_file_hash_digest_from_manifest_with_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        digest = package.get_package_file_hash_digest_from_manifest('Scripts/charms/simple/src/charm.py')
+        self.assertEqual(digest, '7895f7b9e1b7ed5b5bcd64398950ca95b456d7fc973334351474eed466c2f480')
+
+    def test_get_package_file_hash_digest_from_manifest_without_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_without_metadata_dir_vnf')
+        digest = package.get_package_file_hash_digest_from_manifest('Scripts/charms/simple/src/charm.py')
+        self.assertEqual(digest, '7895f7b9e1b7ed5b5bcd64398950ca95b456d7fc973334351474eed466c2f480')
+
+    def test_get_package_file_hash_digest_from_manifest_on_non_existent_file(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.get_package_file_hash_digest_from_manifest('Non/Existing/file')
+
+    def test_get_package_file_hash_digest_from_manifest_on_non_existing_hash_entry(self):
+        package = SOL004Package('osm_common/tests/packages/invalid_package_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.get_package_file_hash_digest_from_manifest('Scripts/charms/simple/hooks/upgrade-charm')
+
+    def test_validate_package_file_hash_with_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        package.validate_package_file_hash('Scripts/charms/simple/src/charm.py')
+
+    def test_validate_package_file_hash_without_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_without_metadata_dir_vnf')
+        package.validate_package_file_hash('Scripts/charms/simple/src/charm.py')
+
+    def test_validate_package_file_hash_on_non_existing_file(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.validate_package_file_hash('Non/Existing/file')
+
+    def test_validate_package_file_hash_on_wrong_manifest_hash(self):
+        package = SOL004Package('osm_common/tests/packages/invalid_package_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.validate_package_file_hash('Scripts/charms/simple/hooks/start')
+
+    def test_validate_package_file_hash_on_unsupported_hash_algorithm(self):
+        package = SOL004Package('osm_common/tests/packages/invalid_package_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.validate_package_file_hash('Scripts/charms/simple/src/charm.py')
+
+    def test_validate_package_hashes_with_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        package.validate_package_hashes()
+
+    def test_validate_package_hashes_without_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_without_metadata_dir_vnf')
+        package.validate_package_hashes()
+
+    def test_validate_package_hashes_on_invalid_package(self):
+        package = SOL004Package('osm_common/tests/packages/invalid_package_vnf')
+        with self.assertRaises(SOL004PackageException):
+            package.validate_package_hashes()
+
+    def test_get_descriptor_location_with_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_with_metadata_dir_vnf')
+        descriptor_path = package.get_descriptor_location()
+        self.assertEqual(descriptor_path, 'Definitions/native_charm_vnfd.yaml')
+
+    def test_get_descriptor_location_without_metadata_dir(self):
+        package = SOL004Package('osm_common/tests/packages/native_charm_without_metadata_dir_vnf')
+        descriptor_path = package.get_descriptor_location()
+        self.assertEqual(descriptor_path, 'native_charm_vnfd.yaml')