Browse Source
This patch implements that in the instantiate operation of ETSI NFV-SOL003 VNF Lifecycle Management 2.6.1, users can specify the target namespace in InstantiateVnfRequest to deploy CNF on Kubernetes VIM. Implements: blueprint k8s-namespace Change-Id: I292256bc58b54e248da54ccbf3c7e82bd28fd022changes/47/825047/5
20 changed files with 1404 additions and 726 deletions
@ -0,0 +1,7 @@
|
||||
--- |
||||
features: |
||||
- | |
||||
The user specifies the target namespace in the InstantiateVnfRequest to |
||||
deploy the CNF on the Kubernetes VIM. The additionalParams field provides |
||||
the new parameter ``namespace`` for the target namespace. The Kubernetes |
||||
resources in the VNF must be in the same namespace. |
@ -0,0 +1,150 @@
|
||||
tosca_definitions_version: tosca_simple_yaml_1_2 |
||||
|
||||
description: Sample VNF |
||||
|
||||
imports: |
||||
- etsi_nfv_sol001_common_types.yaml |
||||
- etsi_nfv_sol001_vnfd_types.yaml |
||||
- helloworld3_types.yaml |
||||
|
||||
topology_template: |
||||
inputs: |
||||
descriptor_id: |
||||
type: string |
||||
descriptor_version: |
||||
type: string |
||||
provider: |
||||
type: string |
||||
product_name: |
||||
type: string |
||||
software_version: |
||||
type: string |
||||
vnfm_info: |
||||
type: list |
||||
entry_schema: |
||||
type: string |
||||
flavour_id: |
||||
type: string |
||||
flavour_description: |
||||
type: string |
||||
|
||||
substitution_mappings: |
||||
node_type: company.provider.VNF |
||||
properties: |
||||
flavour_id: simple |
||||
requirements: |
||||
virtual_link_external: [] |
||||
|
||||
node_templates: |
||||
VNF: |
||||
type: company.provider.VNF |
||||
properties: |
||||
flavour_description: A simple flavour |
||||
|
||||
VDU1: |
||||
type: tosca.nodes.nfv.Vdu.Compute |
||||
properties: |
||||
name: vdu1 |
||||
description: kubernetes controller resource as VDU |
||||
vdu_profile: |
||||
min_number_of_instances: 1 |
||||
max_number_of_instances: 3 |
||||
|
||||
VDU2: |
||||
type: tosca.nodes.nfv.Vdu.Compute |
||||
properties: |
||||
name: vdu2 |
||||
description: kubernetes controller resource as VDU |
||||
vdu_profile: |
||||
min_number_of_instances: 1 |
||||
max_number_of_instances: 3 |
||||
|
||||
policies: |
||||
- scaling_aspects: |
||||
type: tosca.policies.nfv.ScalingAspects |
||||
properties: |
||||
aspects: |
||||
vdu1_aspect: |
||||
name: vdu1_aspect |
||||
description: vdu1 scaling aspect |
||||
max_scale_level: 2 |
||||
step_deltas: |
||||
- delta_1 |
||||
vdu2_aspect: |
||||
name: vdu2_aspect |
||||
description: vdu2 scaling aspect |
||||
max_scale_level: 2 |
||||
step_deltas: |
||||
- delta_1 |
||||
|
||||
- instantiation_levels: |
||||
type: tosca.policies.nfv.InstantiationLevels |
||||
properties: |
||||
levels: |
||||
instantiation_level_1: |
||||
description: Smallest size |
||||
scale_info: |
||||
vdu1_aspect: |
||||
scale_level: 0 |
||||
vdu2_aspect: |
||||
scale_level: 0 |
||||
instantiation_level_2: |
||||
description: Largest size |
||||
scale_info: |
||||
vdu1_aspect: |
||||
scale_level: 2 |
||||
vdu2_aspect: |
||||
scale_level: 2 |
||||
default_level: instantiation_level_1 |
||||
|
||||
- vdu1_initial_delta: |
||||
type: tosca.policies.nfv.VduInitialDelta |
||||
properties: |
||||
initial_delta: |
||||
number_of_instances: 1 |
||||
targets: [ VDU1 ] |
||||
|
||||
- vdu1_scaling_aspect_deltas: |
||||
type: tosca.policies.nfv.VduScalingAspectDeltas |
||||
properties: |
||||
aspect: vdu1_aspect |
||||
deltas: |
||||
delta_1: |
||||
number_of_instances: 1 |
||||
targets: [ VDU1 ] |
||||
|
||||
- vdu1_instantiation_levels: |
||||
type: tosca.policies.nfv.VduInstantiationLevels |
||||
properties: |
||||
levels: |
||||
instantiation_level_1: |
||||
number_of_instances: 1 |
||||
instantiation_level_2: |
||||
number_of_instances: 3 |
||||
targets: [ VDU1 ] |
||||
|
||||
- vdu2_initial_delta: |
||||
type: tosca.policies.nfv.VduInitialDelta |
||||
properties: |
||||
initial_delta: |
||||
number_of_instances: 1 |
||||
targets: [ VDU2 ] |
||||
|
||||
- vdu2_scaling_aspect_deltas: |
||||
type: tosca.policies.nfv.VduScalingAspectDeltas |
||||
properties: |
||||
aspect: vdu2_aspect |
||||
deltas: |
||||
delta_1: |
||||
number_of_instances: 1 |
||||
targets: [ VDU2 ] |
||||
|
||||
- vdu2_instantiation_levels: |
||||
type: tosca.policies.nfv.VduInstantiationLevels |
||||
properties: |
||||
levels: |
||||
instantiation_level_1: |
||||
number_of_instances: 1 |
||||
instantiation_level_2: |
||||
number_of_instances: 3 |
||||
targets: [ VDU2 ] |
@ -0,0 +1,31 @@
|
||||
tosca_definitions_version: tosca_simple_yaml_1_2 |
||||
|
||||
description: Sample VNF |
||||
|
||||
imports: |
||||
- etsi_nfv_sol001_common_types.yaml |
||||
- etsi_nfv_sol001_vnfd_types.yaml |
||||
- helloworld3_types.yaml |
||||
- helloworld3_df_simple.yaml |
||||
|
||||
topology_template: |
||||
inputs: |
||||
selected_flavour: |
||||
type: string |
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API |
||||
|
||||
node_templates: |
||||
VNF: |
||||
type: company.provider.VNF |
||||
properties: |
||||
flavour_id: { get_input: selected_flavour } |
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d70a8993 |
||||
provider: Company |
||||
product_name: Sample VNF |
||||
software_version: '1.0' |
||||
descriptor_version: '1.0' |
||||
vnfm_info: |
||||
- Tacker |
||||
requirements: |
||||
#- virtual_link_external # mapped in lower-level templates |
||||
#- virtual_link_internal # mapped in lower-level templates |
@ -0,0 +1,53 @@
|
||||
tosca_definitions_version: tosca_simple_yaml_1_2 |
||||
|
||||
description: VNF type definition |
||||
|
||||
imports: |
||||
- etsi_nfv_sol001_common_types.yaml |
||||
- etsi_nfv_sol001_vnfd_types.yaml |
||||
|
||||
node_types: |
||||
company.provider.VNF: |
||||
derived_from: tosca.nodes.nfv.VNF |
||||
properties: |
||||
descriptor_id: |
||||
type: string |
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d70a8993 ] ] |
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d70a8993 |
||||
descriptor_version: |
||||
type: string |
||||
constraints: [ valid_values: [ '1.0' ] ] |
||||
default: '1.0' |
||||
provider: |
||||
type: string |
||||
constraints: [ valid_values: [ 'Company' ] ] |
||||
default: 'Company' |
||||
product_name: |
||||
type: string |
||||
constraints: [ valid_values: [ 'Sample VNF' ] ] |
||||
default: 'Sample VNF' |
||||
software_version: |
||||
type: string |
||||
constraints: [ valid_values: [ '1.0' ] ] |
||||
default: '1.0' |
||||
vnfm_info: |
||||
type: list |
||||
entry_schema: |
||||
type: string |
||||
constraints: [ valid_values: [ Tacker ] ] |
||||
default: [ Tacker ] |
||||
flavour_id: |
||||
type: string |
||||
constraints: [ valid_values: [ simple ] ] |
||||
default: simple |
||||
flavour_description: |
||||
type: string |
||||
default: "" |
||||
requirements: |
||||
- virtual_link_external: |
||||
capability: tosca.capabilities.nfv.VirtualLinkable |
||||
- virtual_link_internal: |
||||
capability: tosca.capabilities.nfv.VirtualLinkable |
||||
interfaces: |
||||
Vnflcm: |
||||
type: tosca.interfaces.nfv.Vnflcm |
@ -0,0 +1,22 @@
|
||||
apiVersion: apps/v1 |
||||
kind: Deployment |
||||
metadata: |
||||
name: vdu1 |
||||
namespace: multi-namespace02 |
||||
spec: |
||||
replicas: 1 |
||||
selector: |
||||
matchLabels: |
||||
app: webserver |
||||
template: |
||||
metadata: |
||||
labels: |
||||
app: webserver |
||||
spec: |
||||
containers: |
||||
- name: nginx |
||||
image: nginx |
||||
imagePullPolicy: IfNotPresent |
||||
ports: |
||||
- containerPort: 80 |
||||
protocol: TCP |
@ -0,0 +1,21 @@
|
||||
apiVersion: apps/v1 |
||||
kind: Deployment |
||||
metadata: |
||||
name: vdu2 |
||||
spec: |
||||
replicas: 1 |
||||
selector: |
||||
matchLabels: |
||||
app: webserver |
||||
template: |
||||
metadata: |
||||
labels: |
||||
app: webserver |
||||
spec: |
||||
containers: |
||||
- name: nginx |
||||
image: nginx |
||||
imagePullPolicy: IfNotPresent |
||||
ports: |
||||
- containerPort: 80 |
||||
protocol: TCP |
@ -0,0 +1,4 @@
|
||||
apiVersion: v1 |
||||
kind: Namespace |
||||
metadata: |
||||
name: multi-namespace01 |
@ -0,0 +1,4 @@
|
||||
apiVersion: v1 |
||||
kind: Namespace |
||||
metadata: |
||||
name: multi-namespace02 |
@ -0,0 +1,24 @@
|
||||
TOSCA-Meta-File-Version: 1.0 |
||||
Created-by: dummy_user |
||||
CSAR-Version: 1.1 |
||||
Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml |
||||
|
||||
Name: Files/kubernetes/deployment_has_namespace.yaml |
||||
Content-Type: application/yaml |
||||
Algorithm: SHA-256 |
||||
Hash: c33b1ec0793619e4752fb0bca2c5c20c9ae2e1717ade94e7d4466efc80a7c6f4 |
||||
|
||||
Name: Files/kubernetes/deployment_no_namespace.yaml |
||||
Content-Type: application/yaml |
||||
Algorithm: SHA-256 |
||||
Hash: 064c5f54722d74dd7faae6a3f83a504eed579135b366b1251e6d2f024951a7a0 |
||||
|
||||
Name: Files/kubernetes/namespace01.yaml |
||||
Content-Type: application/yaml |
||||
Algorithm: SHA-256 |
||||
Hash: 6cee66e9786c158edae7da6b1c4639246070cb6ccbfcfbe7f5cec4184552b07d |
||||
|
||||
Name: Files/kubernetes/namespace02.yaml |
||||
Content-Type: application/yaml |
||||
Algorithm: SHA-256 |
||||
Hash: 04a7db97412b8d88fe6390c34489ac8e963c70d828c11737592723a7fbd30d8c |
@ -0,0 +1,146 @@
|
||||
# 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. |
||||
|
||||
from tacker.tests.functional.sol_kubernetes.vnflcm import base as vnflcm_base |
||||
|
||||
|
||||
class VnfLcmKubernetesMultiNsTest(vnflcm_base.BaseVnfLcmKubernetesTest): |
||||
|
||||
@classmethod |
||||
def setUpClass(cls): |
||||
super(VnfLcmKubernetesMultiNsTest, cls).setUpClass() |
||||
vnf_package_id, cls.vnfd_id = cls._create_and_upload_vnf_package( |
||||
cls, cls.tacker_client, "test_cnf_multi_ns", |
||||
{"key": "sample_multi_ns_functional"}) |
||||
cls.vnf_package_ids.append(vnf_package_id) |
||||
|
||||
@classmethod |
||||
def tearDownClass(cls): |
||||
super(VnfLcmKubernetesMultiNsTest, cls).tearDownClass() |
||||
|
||||
def _test_cnf_scale(self, vnf_instance, aspect_id, |
||||
number_of_steps=1, error=False): |
||||
scale_level = self._get_scale_level_by_aspect_id( |
||||
vnf_instance, aspect_id) |
||||
|
||||
# test scale out |
||||
scale_level = self._test_scale( |
||||
vnf_instance['id'], 'SCALE_OUT', aspect_id, scale_level, |
||||
number_of_steps, error) |
||||
if error: |
||||
return scale_level |
||||
|
||||
# test scale in |
||||
scale_level = self._test_scale( |
||||
vnf_instance['id'], 'SCALE_IN', aspect_id, scale_level, |
||||
number_of_steps) |
||||
|
||||
return scale_level |
||||
|
||||
def test_multi_tenant_k8s_additional_params(self): |
||||
vnf_instance_name = "multi_tenant_k8s_additional_params" |
||||
vnf_instance_description = "multi tenant k8s additional params" |
||||
files = ["Files/kubernetes/deployment_has_namespace.yaml", |
||||
"Files/kubernetes/namespace01.yaml"] |
||||
additional_param = { |
||||
"lcm-kubernetes-def-files": files, |
||||
"namespace": "multi-namespace01"} |
||||
# instantiate |
||||
vnf_instance = self._create_and_instantiate_vnf_instance( |
||||
self.vnfd_id, "simple", vnf_instance_name, |
||||
vnf_instance_description, additional_param) |
||||
# scale |
||||
self._test_cnf_scale(vnf_instance, "vdu1_aspect", number_of_steps=1) |
||||
|
||||
before_vnfc_rscs = self._get_vnfc_resource_info(vnf_instance) |
||||
deployment_target_vnfc = None |
||||
for vnfc_rsc in before_vnfc_rscs: |
||||
if vnfc_rsc['vduId'] == 'VDU1': |
||||
deployment_target_vnfc = vnfc_rsc |
||||
vnfc_instance_id = [deployment_target_vnfc['id']] |
||||
# heal |
||||
after_vnfc_rscs = self._test_heal(vnf_instance, vnfc_instance_id) |
||||
for vnfc_rsc in after_vnfc_rscs: |
||||
after_pod_name = vnfc_rsc['computeResource']['resourceId'] |
||||
if vnfc_rsc['id'] == deployment_target_vnfc['id']: |
||||
after_resource = deployment_target_vnfc |
||||
compute_resource = after_resource['computeResource'] |
||||
before_pod_name = compute_resource['resourceId'] |
||||
self.assertNotEqual(after_pod_name, before_pod_name) |
||||
# terminate |
||||
self._terminate_vnf_instance(vnf_instance['id']) |
||||
self._delete_vnf_instance(vnf_instance['id']) |
||||
|
||||
def test_multi_tenant_k8s_manifest(self): |
||||
vnf_instance_name = "multi_tenant_k8s_manifest" |
||||
vnf_instance_description = "multi tenant k8s manifest" |
||||
files = ["Files/kubernetes/deployment_has_namespace.yaml", |
||||
"Files/kubernetes/namespace02.yaml"] |
||||
additional_param = {"lcm-kubernetes-def-files": files} |
||||
# instantiate |
||||
vnf_instance = self._create_and_instantiate_vnf_instance( |
||||
self.vnfd_id, "simple", vnf_instance_name, |
||||
vnf_instance_description, additional_param) |
||||
# scale |
||||
self._test_cnf_scale(vnf_instance, "vdu1_aspect", number_of_steps=1) |
||||
|
||||
before_vnfc_rscs = self._get_vnfc_resource_info(vnf_instance) |
||||
deployment_target_vnfc = None |
||||
for vnfc_rsc in before_vnfc_rscs: |
||||
if vnfc_rsc['vduId'] == 'VDU1': |
||||
deployment_target_vnfc = vnfc_rsc |
||||
vnfc_instance_id = [deployment_target_vnfc['id']] |
||||
# heal |
||||
after_vnfc_rscs = self._test_heal(vnf_instance, vnfc_instance_id) |
||||
for vnfc_rsc in after_vnfc_rscs: |
||||
after_pod_name = vnfc_rsc['computeResource']['resourceId'] |
||||
if vnfc_rsc['id'] == deployment_target_vnfc['id']: |
||||
after_resource = deployment_target_vnfc |
||||
compute_resource = after_resource['computeResource'] |
||||
before_pod_name = compute_resource['resourceId'] |
||||
self.assertNotEqual(after_pod_name, before_pod_name) |
||||
# terminate |
||||
self._terminate_vnf_instance(vnf_instance['id']) |
||||
self._delete_vnf_instance(vnf_instance['id']) |
||||
|
||||
def test_multi_tenant_k8s_default(self): |
||||
vnf_instance_name = "multi_tenant_k8s_default" |
||||
vnf_instance_description = "multi tenant k8s default" |
||||
files = ["Files/kubernetes/deployment_no_namespace.yaml"] |
||||
additional_param = {"lcm-kubernetes-def-files": files} |
||||
# instantiate |
||||
vnf_instance = self._create_and_instantiate_vnf_instance( |
||||
self.vnfd_id, "simple", vnf_instance_name, |
||||
vnf_instance_description, additional_param) |
||||
# scale |
||||
self._test_cnf_scale(vnf_instance, "vdu2_aspect", number_of_steps=1) |
||||
|
||||
before_vnfc_rscs = self._get_vnfc_resource_info(vnf_instance) |
||||
deployment_target_vnfc = None |
||||
for vnfc_rsc in before_vnfc_rscs: |
||||
if vnfc_rsc['vduId'] == 'VDU2': |
||||
deployment_target_vnfc = vnfc_rsc |
||||
vnfc_instance_id = [deployment_target_vnfc['id']] |
||||
# heal |
||||
after_vnfc_rscs = self._test_heal(vnf_instance, vnfc_instance_id) |
||||
for vnfc_rsc in after_vnfc_rscs: |
||||
after_pod_name = vnfc_rsc['computeResource']['resourceId'] |
||||
if vnfc_rsc['id'] == deployment_target_vnfc['id']: |
||||
after_resource = deployment_target_vnfc |
||||
compute_resource = after_resource['computeResource'] |
||||
before_pod_name = compute_resource['resourceId'] |
||||
self.assertNotEqual(after_pod_name, before_pod_name) |
||||
# terminate |
||||
self._terminate_vnf_instance(vnf_instance['id']) |
||||
self._delete_vnf_instance(vnf_instance['id']) |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,69 @@
|
||||
# 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. |
||||
|
||||
from tacker.common import exceptions |
||||
from tacker import objects |
||||
from tacker.tests.unit import base |
||||
from tacker.tests.unit.vnfm.infra_drivers.openstack.fixture_data import \ |
||||
fixture_data_utils as fd_utils |
||||
from tacker.vnfm.infra_drivers.kubernetes import utils as k8s_utils |
||||
from unittest import mock |
||||
|
||||
|
||||
class KubernetesUtilsTestCase(base.TestCase): |
||||
|
||||
@mock.patch('tacker.objects.vnf_instance.VnfInstance.save') |
||||
def test_check_and_save_namespace_multi_namespace(self, mock_save): |
||||
instantiate_vnf_req = objects.InstantiateVnfRequest( |
||||
additional_params=None) |
||||
chk_namespaces = [{"namespace": "a", "kind": "CronJob"}, |
||||
{"namespace": "b", "kind": "Deployment"}, |
||||
{"namespace": "c", "kind": "Service"}] |
||||
vnf_instance = fd_utils.get_vnf_instance_object() |
||||
|
||||
self.assertRaises( |
||||
exceptions.NamespaceIsNotUnique, |
||||
k8s_utils.check_and_save_namespace, None, instantiate_vnf_req, |
||||
chk_namespaces, vnf_instance) |
||||
|
||||
@mock.patch('tacker.objects.vnf_instance.VnfInstance.save') |
||||
def test_check_and_save_namespace_no_namespace(self, mock_save): |
||||
instantiate_vnf_req = objects.InstantiateVnfRequest( |
||||
additional_params=None) |
||||
chk_namespaces = [] |
||||
vnf_instance = fd_utils.get_vnf_instance_object() |
||||
vnf_instance.vnf_metadata['namespace'] = '' |
||||
k8s_utils.check_and_save_namespace( |
||||
None, instantiate_vnf_req, chk_namespaces, vnf_instance) |
||||
self.assertEqual('default', vnf_instance.vnf_metadata['namespace']) |
||||
|
||||
@mock.patch('tacker.objects.vnf_instance.VnfInstance.save') |
||||
def test_check_and_save_namespace_additional_params(self, mock_save): |
||||
instantiate_vnf_req = objects.InstantiateVnfRequest( |
||||
additional_params={'namespace': 'ns1'}) |
||||
chk_namespaces = [] |
||||
vnf_instance = fd_utils.get_vnf_instance_object() |
||||
vnf_instance.vnf_metadata['namespace'] = '' |
||||
k8s_utils.check_and_save_namespace( |
||||
None, instantiate_vnf_req, chk_namespaces, vnf_instance) |
||||
self.assertEqual('ns1', vnf_instance.vnf_metadata['namespace']) |
||||
|
||||
@mock.patch('tacker.objects.vnf_instance.VnfInstance.save') |
||||
def test_check_and_save_namespace_manifests(self, mock_save): |
||||
instantiate_vnf_req = objects.InstantiateVnfRequest( |
||||
additional_params=None) |
||||
chk_namespaces = [{"namespace": "ns2", "kind": "Deployment"}] |
||||
vnf_instance = fd_utils.get_vnf_instance_object() |
||||
vnf_instance.vnf_metadata = {} |
||||
k8s_utils.check_and_save_namespace( |
||||
None, instantiate_vnf_req, chk_namespaces, vnf_instance) |
||||
self.assertEqual('ns2', vnf_instance.vnf_metadata['namespace']) |