diff --git a/releasenotes/notes/support-vnf-modify-for-container-update-3e73c53352558dec.yaml b/releasenotes/notes/support-vnf-modify-for-container-update-3e73c53352558dec.yaml new file mode 100644 index 000000000..3340fe760 --- /dev/null +++ b/releasenotes/notes/support-vnf-modify-for-container-update-3e73c53352558dec.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Support configuration changes to VNF instances by adding + ``modify_start`` and ``modify_end`` to MgmtDriver for the Modify VNF + operation. + We provide the sample of MgmtDriver which changes the configuration + of ConfigMap and Secret in Kubernetes and changes the image parameters + in the Pod and Deployment manifests. diff --git a/roles/setup-default-vim/tasks/main.yaml b/roles/setup-default-vim/tasks/main.yaml index 87524d36a..d03ce7f5b 100644 --- a/roles/setup-default-vim/tasks/main.yaml +++ b/roles/setup-default-vim/tasks/main.yaml @@ -188,3 +188,49 @@ when: - inventory_hostname == 'controller-tacker' - kuryr_k8s_api_url is defined + +- block: + - name: Copy tools/test-setup-mgmt.sh + copy: + remote_src=True + src={{ devstack_base_dir }}/tacker/tools/test-setup-mgmt.sh + dest={{ zuul_work_dir }}/tools/test-setup-mgmt.sh + mode=0755 + + - name: Check if project's tools/test-setup-mgmt.sh exists + stat: + path: "{{ zuul_work_dir }}/tools/test-setup-mgmt.sh" + register: p + - fail: + msg: > + {{ zuul_work_dir }}/tools/test-setup-mgmt.sh doesn't exists + or it doesn't have execute permission. + when: p.stat.exists != True or p.stat.executable != True + + - name: Get stackenv from devstack environment + slurp: + src: "{{ devstack_base_dir }}/devstack/.stackenv" + register: stackenv + + - name: Set a keystone authentication uri + set_fact: + auth_uri: "{{ + stackenv.content + | b64decode + | regex_replace('\n', ' ') + | regex_replace('^.*KEYSTONE_SERVICE_URI=([^ ]+).*$', '\\1') + }}" + when: + - p.stat.exists + + - name: Run tools/test-setup-mgmt.sh + command: tools/test-setup-mgmt.sh + args: + chdir: "{{ zuul_work_dir }}" + when: + - p.stat.exists + - p.stat.executable + + when: + - inventory_hostname == 'controller-tacker' + - kuryr_k8s_api_url is defined diff --git a/samples/mgmt_driver/kubernetes/container_update/container_update_mgmt.py b/samples/mgmt_driver/kubernetes/container_update/container_update_mgmt.py new file mode 100644 index 000000000..b6d0a1301 --- /dev/null +++ b/samples/mgmt_driver/kubernetes/container_update/container_update_mgmt.py @@ -0,0 +1,399 @@ +# Copyright (C) 2022 FUJITSU +# 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 copy +import os +import re +import time +import urllib.request as urllib2 +import yaml + +from oslo_log import log as logging +from oslo_utils import encodeutils +from tacker.common.container import kubernetes_utils +from tacker.common import exceptions +from tacker.common import log +from tacker import objects +from tacker.vnflcm import utils as vnflcm_utils +from tacker.vnfm.infra_drivers.kubernetes.k8s import translate_outputs +from tacker.vnfm.infra_drivers.kubernetes.kubernetes_driver import Kubernetes +from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver +from toscaparser import tosca_template +from urllib.parse import urlparse + +LOG = logging.getLogger(__name__) + + +class ContainerUpdateMgmtDriver(vnflcm_abstract_driver. + VnflcmMgmtAbstractDriver): + + def __init__(self): + pass + + def get_type(self): + return "mgmt-container-update" + + def get_name(self): + return "mgmt-container-update" + + def get_description(self): + return 'Tacker Container Update VNF Mgmt Driver' + + def modify_information_start(self, context, vnf_instance, + modify_vnf_request=None, **kwargs): + pass + + def _get_kind_and_name(self, file, vnf_package_path): + # kind_and_names ----> configmap_secrets list or resources list + kind_and_names = [] + # Read the contents of the manifest file and get the name and kind + if ((urlparse(file).scheme == 'file') or + (bool(urlparse(file).scheme) and + bool(urlparse(file).netloc))): + file_content = urllib2.urlopen(file).read() + else: + manifest_file = os.path.join(vnf_package_path, file) + with open(manifest_file, 'r', encoding='utf-8') as f: + file_content = f.read() + file_contents = yaml.safe_load_all(file_content) + for file_content in file_contents: + kind = file_content.get('kind', '') + name = file_content.get('metadata', {}).get('name', '') + kind_and_names.append({'kind': kind, 'name': name}) + return kind_and_names + + def _initialize_k8s_client(self, auth_cred): + k8s_clients = (kubernetes_utils.KubernetesHTTPAPI(). + get_k8s_client_dict(auth_cred)) + return k8s_clients + + def _get_k8s_objs(self, target_k8s_files, vnf_package_path, namespace, + k8s_clients): + transformer = translate_outputs.Transformer( + None, None, None, k8s_clients) + k8s_objs = transformer.get_k8s_objs_from_yaml( + target_k8s_files, vnf_package_path, namespace) + return k8s_objs + + def _modify_container_img(self, old_containers, new_containers): + for old_container in old_containers: + for new_container in new_containers: + if (old_container.name == new_container.name and + old_container.image != new_container.image): + # Replace the old image with the new image + old_container.image = new_container.image + break + + def _replace_api(self, k8s_clients, namespace, k8s_obj): + # get api + name = k8s_obj['object'].metadata.name + kind = k8s_obj['object'].kind + api_version = k8s_obj['object'].api_version + body = k8s_obj['object'] + + def convert(name): + name_with_underscores = re.sub( + '(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', + name_with_underscores).lower() + + snake_case_kind = convert(kind) + try: + replace_api = eval(f'k8s_clients["{api_version}"].' + f'replace_namespaced_{snake_case_kind}') + response = replace_api(name=name, namespace=namespace, + body=body) + except Exception as exp: + raise exceptions.MgmtDriverOtherError( + error_message=encodeutils.exception_to_unicode(exp)) + + return response + + def _replace_wait_k8s(self, kube_driver, k8s_pod_objs, + core_v1_api_client, vnf_instance): + try: + time.sleep(kube_driver.STACK_RETRY_WAIT) + status = 'Pending' + stack_retries = kube_driver.STACK_RETRIES + + while status == 'Pending' and stack_retries > 0: + pods_information = [] + for k8s_pod_obj in k8s_pod_objs: + kind = k8s_pod_obj['object'].kind + namespace = k8s_pod_obj.get('namespace') + if k8s_pod_obj['object'].metadata: + name = k8s_pod_obj['object'].metadata.name + else: + name = '' + + response = core_v1_api_client.list_namespaced_pod( + namespace=namespace) + for pod in response.items: + match_result = kube_driver.is_match_pod_naming_rule( + kind, name, pod.metadata.name) + if match_result: + pods_information.append(pod) + status = kube_driver.get_pod_status(pods_information) + if status == 'Unknown': + wait = (kube_driver.STACK_RETRIES * + kube_driver.STACK_RETRY_WAIT) + error_reason = ( + f"Resource creation is not completed within" + f" {wait} seconds as creation of CNF " + f"{vnf_instance.id} is not completed") + raise exceptions.MgmtDriverOtherError( + error_message=error_reason) + if status == 'Pending': + stack_retries = stack_retries - 1 + time.sleep(kube_driver.STACK_RETRY_WAIT) + if stack_retries == 0 and status != 'Running': + LOG.error('It is time out, When modify cnf,' + 'waiting for resource creation.') + wait = (kube_driver.STACK_RETRIES * + kube_driver.STACK_RETRY_WAIT) + error_reason = (f"Resource creation is not completed within" + f" {wait} seconds as creation of CNF " + f"{vnf_instance.id} is not completed") + raise exceptions.MgmtDriverOtherError( + error_message=error_reason) + return k8s_pod_objs + except Exception as exp: + raise exceptions.MgmtDriverOtherError( + error_message=encodeutils.exception_to_unicode(exp)) + + @log.log + def modify_information_end(self, context, vnf_instance, + modify_vnf_request=None, **kwargs): + kube_driver = Kubernetes() + # get old_vnf_package_path + old_vnf_package_path = kwargs['old_vnf_package_path'] + + # get configmap_secret_paths + configmap_secret_paths = kwargs['configmap_secret_paths'] + # Get the path of the VNF Package according to vnf_instance.vnfd_id + new_vnf_package_path = vnflcm_utils.get_vnf_package_path( + context, vnf_instance.vnfd_id) + + # Get the input parameters of instantiate + inst_vnf_info = vnf_instance.instantiated_vnf_info + + # Make a deep copy of inst_vnf_info + new_inst_vnf_info = copy.deepcopy(inst_vnf_info) + + # Get vim_connection_info from vnf_instance + vim_info = vnflcm_utils.get_vim(context, + vnf_instance.vim_connection_info) + vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( + vim_info, context) + for configmap_secret_path in configmap_secret_paths: + # Read the contents of the manifest file and get name and kind + configmap_secrets = self._get_kind_and_name(configmap_secret_path, + new_vnf_package_path) + for index, manifest_path in enumerate( + inst_vnf_info.additional_params[ + 'lcm-kubernetes-def-files']): + # Read the contents of the manifest file and get name and kind + resources = self._get_kind_and_name(manifest_path, + old_vnf_package_path) + resource = [obj for obj in resources if + obj in configmap_secrets] + if resource: + if len(resource) == len(configmap_secrets): + # Update the manifest file path of ConfigMap/Secret + new_inst_vnf_info.additional_params[ + 'lcm-kubernetes-def-files'][ + index] = configmap_secret_path + break + raise exceptions.MgmtDriverOtherError( + error_message='The number of resources in the ' + 'manifest file of the changed ' + 'ConfigMap/Secret is inconsistent ' + 'with the previous one.') + # Initialize k8s client api + auth_attr = vim_connection_info.access_info + auth_cred, _ = kube_driver.get_auth_creds(auth_attr) + k8s_clients = self._initialize_k8s_client(auth_cred) + + # Get the namespace of this CNF + namespace = vnf_instance.vnf_metadata['namespace'] + + # Get old_k8s_objs + target_k8s_files = inst_vnf_info.additional_params[ + 'lcm-kubernetes-def-files'] + old_k8s_objs = self._get_k8s_objs(target_k8s_files, + old_vnf_package_path, namespace, + k8s_clients) + + # Get new_k8s_objs + target_k8s_files = new_inst_vnf_info.additional_params[ + 'lcm-kubernetes-def-files'] + new_k8s_objs = self._get_k8s_objs(target_k8s_files, + new_vnf_package_path, namespace, + k8s_clients) + # Initialize k8s_pod_objs and k8s_config_objs + k8s_pod_objs = [] + k8s_config_objs = [] + + for old_k8s_obj in old_k8s_objs: + old_k8s_obj_kind = old_k8s_obj['object'].kind + old_k8s_obj_name = old_k8s_obj['object'].metadata.name + if old_k8s_obj_kind in ['Pod', 'Deployment']: + for new_k8s_obj in new_k8s_objs: + # If the old and new k8s_obj have the same kind and name + new_k8s_obj_kind = new_k8s_obj['object'].kind + new_k8s_obj_name = new_k8s_obj['object'].metadata.name + if old_k8s_obj_kind == new_k8s_obj_kind and ( + old_k8s_obj_name == new_k8s_obj_name): + # Call the read API + old_k8s_resource_info = ( + kube_driver.select_k8s_obj_read_api( + k8s_client_dict=k8s_clients, + namespace=namespace, + name=old_k8s_obj_name, + kind=old_k8s_obj_kind, + api_version=old_k8s_obj['object'].api_version + )) + # Assign old_k8s_resource_info to old_k8s_obj['object'] + old_k8s_obj['object'] = old_k8s_resource_info + + if old_k8s_obj_kind == 'Deployment': + old_containers = (old_k8s_obj['object'].spec + .template.spec.containers) + new_containers = (new_k8s_obj['object'].spec + .template.spec.containers) + elif old_k8s_obj_kind == 'Pod': + old_containers = (old_k8s_obj['object'] + .spec.containers) + new_containers = (new_k8s_obj['object'] + .spec.containers) + # Replace the old image with the new image + self._modify_container_img(old_containers, + new_containers) + break + + # Append old_k8s_obj to k8s_pod_objs + k8s_pod_objs.append(old_k8s_obj) + elif old_k8s_obj_kind in ['ConfigMap', 'Secret']: + for new_k8s_obj in new_k8s_objs: + # If the old and new k8s_obj have the same kind and name + new_k8s_obj_kind = new_k8s_obj['object'].kind + new_k8s_obj_name = new_k8s_obj['object'].metadata.name + if old_k8s_obj_kind == new_k8s_obj_kind and ( + old_k8s_obj_name == new_k8s_obj_name): + # Append old_k8s_obj to k8s_pod_objs + k8s_config_objs.append(new_k8s_obj) + break + for k8s_config_obj in k8s_config_objs: + # Call the replace API + self._replace_api(k8s_clients, namespace, k8s_config_obj) + for k8s_pod_obj in k8s_pod_objs: + # Call the replace API + self._replace_api(k8s_clients, namespace, k8s_pod_obj) + + # _replace_wait_k8s + core_v1_api_client = k8s_clients['v1'] + self._replace_wait_k8s(kube_driver, k8s_pod_objs, core_v1_api_client, + vnf_instance) + # Get all pod information under the specified namespace + pods = core_v1_api_client.list_namespaced_pod(namespace=namespace) + + # get TOSCA node templates + vnfd_dict = vnflcm_utils.get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + tosca = tosca_template.ToscaTemplate( + parsed_params={}, a_file=False, yaml_dict_tpl=vnfd_dict) + tosca_node_tpls = tosca.topology_template.nodetemplates + # get vdu_names dict {vdu_id: vdu_name} + vdu_names = {} + for node_tpl in tosca_node_tpls: + for node_name, node_value in node_tpl.templates.items(): + if node_value.get('type') == "tosca.nodes.nfv.Vdu.Compute": + vdu_id = node_name + vdu_name = node_value.get('properties').get('name') + vdu_names[vdu_id] = vdu_name + + for vnfc_resource in new_inst_vnf_info.vnfc_resource_info: + for pod in pods.items: + # the name of the pod matches the resource_id in vnfc_resource + match_result = kube_driver.is_match_pod_naming_rule( + vnfc_resource.compute_resource.vim_level_resource_type, + vdu_names[vnfc_resource.vdu_id], + pod.metadata.name) + if match_result: + # Update the name of the pod in the resourceId + vnfc_resource.compute_resource.resource_id = ( + pod.metadata.name) + # Delete the current pod from pods.items + pods.items.remove(pod) + break + # the ConfigMap/Secret file path in inst_vnf_info.additional_params + vnf_instance.instantiated_vnf_info.vnfc_resource_info = ( + new_inst_vnf_info.vnfc_resource_info) + vnf_instance.instantiated_vnf_info.additional_params = ( + new_inst_vnf_info.additional_params) + vnf_instance.save() + + def instantiate_start(self, context, vnf_instance, + instantiate_vnf_request, grant, + grant_request, **kwargs): + pass + + def instantiate_end(self, context, vnf_instance, + instantiate_vnf_request, grant, + grant_request, **kwargs): + pass + + def terminate_start(self, context, vnf_instance, + terminate_vnf_request, grant, + grant_request, **kwargs): + pass + + def terminate_end(self, context, vnf_instance, + terminate_vnf_request, grant, + grant_request, **kwargs): + pass + + def scale_start(self, context, vnf_instance, + scale_vnf_request, grant, + grant_request, **kwargs): + pass + + def scale_end(self, context, vnf_instance, + scale_vnf_request, grant, + grant_request, **kwargs): + pass + + def heal_start(self, context, vnf_instance, + heal_vnf_request, grant, + grant_request, **kwargs): + pass + + def heal_end(self, context, vnf_instance, + heal_vnf_request, grant, + grant_request, **kwargs): + pass + + def change_external_connectivity_start( + self, context, vnf_instance, + change_ext_conn_request, grant, + grant_request, **kwargs): + pass + + def change_external_connectivity_end( + self, context, vnf_instance, + change_ext_conn_request, grant, + grant_request, **kwargs): + pass diff --git a/samples/mgmt_driver/kubernetes_mgmt.py b/samples/mgmt_driver/kubernetes_mgmt.py index e1c83d8d9..f965865f0 100644 --- a/samples/mgmt_driver/kubernetes_mgmt.py +++ b/samples/mgmt_driver/kubernetes_mgmt.py @@ -2776,6 +2776,14 @@ class KubernetesMgmtDriver(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): grant_request, **kwargs): pass + def modify_information_start(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + + def modify_information_end(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + def _check_envi(self, commander): ssh_command = 'cat /etc/os-release | grep "PRETTY_NAME=" | ' \ 'grep -c "Ubuntu 20.04"; arch | grep -c x86_64' diff --git a/samples/mgmt_driver/kubespray/kubespray_mgmt.py b/samples/mgmt_driver/kubespray/kubespray_mgmt.py index 3d9f5d1d1..588dac52c 100644 --- a/samples/mgmt_driver/kubespray/kubespray_mgmt.py +++ b/samples/mgmt_driver/kubespray/kubespray_mgmt.py @@ -1528,3 +1528,13 @@ class KubesprayMgmtDriver(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): change_ext_conn_request, grant, grant_request, **kwargs): pass + + @log.log + def modify_information_start(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + + @log.log + def modify_information_end(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass diff --git a/samples/mgmt_driver/private_registry_mgmt.py b/samples/mgmt_driver/private_registry_mgmt.py index 62653d929..a621c9aeb 100644 --- a/samples/mgmt_driver/private_registry_mgmt.py +++ b/samples/mgmt_driver/private_registry_mgmt.py @@ -470,3 +470,11 @@ class PrivateRegistryMgmtDriver( change_ext_conn_request, grant, grant_request, **kwargs): pass + + def modify_information_start(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + + def modify_information_end(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass diff --git a/tacker/conductor/conductor_server.py b/tacker/conductor/conductor_server.py index 68f6123ed..b6a3326ed 100644 --- a/tacker/conductor/conductor_server.py +++ b/tacker/conductor/conductor_server.py @@ -2311,17 +2311,9 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook): self.send_notification(context, notification_data) - # update vnf_instances - try: - ins_obj = objects.vnf_instance.VnfInstance(context=context) - result = ins_obj.update( - context, - vnf_lcm_opoccs, - body_data, - vnfd_pkg_data, - vnfd_id) - except Exception as msg: - raise Exception(str(msg)) + # update vnf_instance + state_entered_time = self.vnflcm_driver.modify_vnf( + context, vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id) # update lcm_op_occs if vnfd_pkg_data and len(vnfd_pkg_data) > 0: @@ -2340,7 +2332,7 @@ class Conductor(manager.Manager, v2_hook.ConductorV2Hook): now = timeutils.utcnow() lcm_op_obj.id = vnf_lcm_opoccs.get('id') lcm_op_obj.operation_state = fields.LcmOccsOperationState.COMPLETED - lcm_op_obj.state_entered_time = result + lcm_op_obj.state_entered_time = state_entered_time lcm_op_obj.updated_at = now lcm_op_obj.changed_info = changed_info diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_df_simple.yaml new file mode 100644 index 000000000..2142be980 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_df_simple.yaml @@ -0,0 +1,178 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - sample_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 + interfaces: + Vnflcm: + modify_information_start: + implementation: mgmt-container-update + modify_information_end: + implementation: mgmt-container-update + artifacts: + mgmt-container-update: + description: Management driver for container update + type: tosca.artifacts.Implementation.Python + file: Scripts/container_update_mgmt.py + + 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 + + VDU3: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: env-test + description: kubernetes resource as VDU3 + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 1 + + VDU4: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: volume-test + description: kubernetes resource as VDU4 + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 1 + 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 ] \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_top.vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_top.vnfd.yaml new file mode 100644 index 000000000..4713c1b91 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_top.vnfd.yaml @@ -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 + - sample_types.yaml + - sample_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-4840d70a8883 + 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 diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_types.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_types.yaml new file mode 100644 index 000000000..858757767 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Definitions/sample_types.yaml @@ -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-4840d70a8883 ] ] + default: b1bb0ce7-ebca-4fa7-95ed-4840d70a8883 + 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 diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/configmap_2.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/configmap_2.yaml new file mode 100644 index 000000000..6fe37feb2 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/configmap_2.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm-data +data: + cmKey1.txt: | + configmap2 data2 + foo2 + bar2 \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/deployment.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/deployment.yaml new file mode 100644 index 000000000..c28c1b82f --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vdu1 +spec: + replicas: 1 + selector: + matchLabels: + app: webserver + template: + metadata: + labels: + app: webserver + spec: + containers: + - name: nginx + image: cirros + imagePullPolicy: IfNotPresent + ports: + - containerPort: 80 + protocol: TCP + env: + - name: CMENV + valueFrom: + configMapKeyRef: + name: cm-data + key: cmKey1.txt + - name: SECENV + valueFrom: + secretKeyRef: + name: secret-data + key: password + envFrom: + - prefix: CM_ + configMapRef: + name: cm-data + - prefix: SEC_ + secretRef: + name: secret-data diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/pod_env.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/pod_env.yaml new file mode 100644 index 000000000..0f447adef --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/pod_env.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Pod +metadata: + name: env-test +spec: + containers: + - image: tomcat + name: nginx + env: + - name: CMENV + valueFrom: + configMapKeyRef: + name: cm-data + key: cmKey1.txt + - name: SECENV + valueFrom: + secretKeyRef: + name: secret-data + key: password + envFrom: + - prefix: CM_ + configMapRef: + name: cm-data + - prefix: SEC_ + secretRef: + name: secret-data diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/pod_volume.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/pod_volume.yaml new file mode 100644 index 000000000..0cd6d79bc --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/pod_volume.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Pod +metadata: + name: volume-test +spec: + containers: + - image: cirros + name: nginx diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/replicaset.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/replicaset.yaml new file mode 100644 index 000000000..b9a241822 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/replicaset.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: ReplicaSet +metadata: + name: vdu2 +spec: + replicas: 1 + selector: + matchLabels: + app: webserver + template: + metadata: + labels: + app: webserver + spec: + containers: + - name: nginx + image: celebdor/kuryr-demo + imagePullPolicy: IfNotPresent + ports: + - containerPort: 180 + protocol: TCP + volumeMounts: + - name: cm-volume + mountPath: /config + - name: sec-volume + mountPath: /etc/secrets + volumes: + - name: cm-volume + configMap: + name: cm-data + defaultMode: 0666 + items: + - key: cmKey1.txt + path: cm/config.txt + - name: sec-volume + secret: + secretName: secret-data + defaultMode: 0600 + items: + - key: secKey1.txt + path: creds/secret.txt diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/secret_2.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/secret_2.yaml new file mode 100644 index 000000000..af4df62f8 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/Files/kubernetes/secret_2.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-data +stringData: + password: 1mbb1G968fb1CUg2 + secKey1.txt: | + secret2 data2 + baz2 \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/TOSCA-Metadata/TOSCA.meta b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 000000000..f75d8b666 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_after/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,39 @@ +TOSCA-Meta-File-Version: 1.0 +Created-by: dummy_user +CSAR-Version: 1.1 +Entry-Definitions: Definitions/sample_top.vnfd.yaml + +Name: Files/kubernetes/configmap_2.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 6f34907da12cb5ad82b25d8e7dd6a7dd4219bd0cbad65e678b58b8466be220e0 + +Name: Files/kubernetes/deployment.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 290debe315bb5b098f73d63049ba850cc5df3723447637bddc1cabe3d0e151d7 + +Name: Files/kubernetes/pod_env.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: b8674c55cd387fd2ab3d550bc622b314bed151d0afffe4f6bc44a553280d400c + +Name: Files/kubernetes/pod_volume.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: e74cd59b3e91ca1a8fb103b13ef15965581639883cc6d8a531f959b4ae89e688 + +Name: Files/kubernetes/replicaset.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 2d9e48ab5e1794bd9b220fcac75ec7f595d72e04ca287bab30a78748aa852c30 + +Name: Files/kubernetes/secret_2.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: f57cbb1822c57e523e5c6d48feea37ee1b1bbd976d720baf8491d47331249e11 + +Name: Scripts/container_update_mgmt.py +Content-Type: text/x-python +Algorithm: SHA-256 +Hash: 9e272402dd0f6f6b39ffc750590eedef028ce22506eb7436a1a1fcfa76c66423 \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_df_simple.yaml new file mode 100644 index 000000000..2142be980 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_df_simple.yaml @@ -0,0 +1,178 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - sample_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 + interfaces: + Vnflcm: + modify_information_start: + implementation: mgmt-container-update + modify_information_end: + implementation: mgmt-container-update + artifacts: + mgmt-container-update: + description: Management driver for container update + type: tosca.artifacts.Implementation.Python + file: Scripts/container_update_mgmt.py + + 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 + + VDU3: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: env-test + description: kubernetes resource as VDU3 + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 1 + + VDU4: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: volume-test + description: kubernetes resource as VDU4 + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 1 + 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 ] \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_top.vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_top.vnfd.yaml new file mode 100644 index 000000000..26e174268 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_top.vnfd.yaml @@ -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 + - sample_types.yaml + - sample_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-4840d70a7774 + 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 diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_types.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_types.yaml new file mode 100644 index 000000000..96f90386f --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Definitions/sample_types.yaml @@ -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-4840d70a7774 ] ] + default: b1bb0ce7-ebca-4fa7-95ed-4840d70a7774 + 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 diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/configmap_1.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/configmap_1.yaml new file mode 100644 index 000000000..8efb046cb --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/configmap_1.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm-data +data: + cmKey1.txt: | + configmap data + foo + bar \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/deployment.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/deployment.yaml new file mode 100644 index 000000000..61a4d0428 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/deployment.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: vdu1 +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 + env: + - name: CMENV + valueFrom: + configMapKeyRef: + name: cm-data + key: cmKey1.txt + - name: SECENV + valueFrom: + secretKeyRef: + name: secret-data + key: password + envFrom: + - prefix: CM_ + configMapRef: + name: cm-data + - prefix: SEC_ + secretRef: + name: secret-data diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/pod_env.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/pod_env.yaml new file mode 100644 index 000000000..d4c2edc3b --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/pod_env.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Pod +metadata: + name: env-test +spec: + containers: + - image: nginx + name: nginx + env: + - name: CMENV + valueFrom: + configMapKeyRef: + name: cm-data + key: cmKey1.txt + - name: SECENV + valueFrom: + secretKeyRef: + name: secret-data + key: password + envFrom: + - prefix: CM_ + configMapRef: + name: cm-data + - prefix: SEC_ + secretRef: + name: secret-data diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/pod_volume.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/pod_volume.yaml new file mode 100644 index 000000000..1f1eacdc6 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/pod_volume.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Pod +metadata: + name: volume-test +spec: + containers: + - image: nginx + name: nginx + volumeMounts: + - name: cm-volume + mountPath: /config + - name: sec-volume + mountPath: /etc/secrets + volumes: + - name: cm-volume + configMap: + name: cm-data + defaultMode: 0666 + items: + - key: cmKey1.txt + path: cm/config.txt + - name: sec-volume + secret: + secretName: secret-data + defaultMode: 0600 + items: + - key: secKey1.txt + path: creds/secret.txt \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/replicaset.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/replicaset.yaml new file mode 100644 index 000000000..8dbb1a612 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/replicaset.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: ReplicaSet +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 + volumeMounts: + - name: cm-volume + mountPath: /config + - name: sec-volume + mountPath: /etc/secrets + volumes: + - name: cm-volume + configMap: + name: cm-data + defaultMode: 0666 + items: + - key: cmKey1.txt + path: cm/config.txt + - name: sec-volume + secret: + secretName: secret-data + defaultMode: 0600 + items: + - key: secKey1.txt + path: creds/secret.txt diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/secret_1.yaml b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/secret_1.yaml new file mode 100644 index 000000000..0585ce591 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/Files/kubernetes/secret_1.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-data +stringData: + password: 1mbb1G968fb1CUg + secKey1.txt: | + secret data + baz \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/TOSCA-Metadata/TOSCA.meta b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/TOSCA-Metadata/TOSCA.meta new file mode 100644 index 000000000..18920480f --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_cnf_container_update_before/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,39 @@ +TOSCA-Meta-File-Version: 1.0 +Created-by: dummy_user +CSAR-Version: 1.1 +Entry-Definitions: Definitions/sample_top.vnfd.yaml + +Name: Files/kubernetes/configmap_1.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: b914022a12c7a0f3a6a7d14013020f1271645ab44b17942a014fdc8ad96f42fe + +Name: Files/kubernetes/deployment.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 03b61e9646925ddf414489212736b7d799710b26dc70f96641c50e699f0a1d5f + +Name: Files/kubernetes/pod_env.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 2e69400cf742fd88cb9ba5abd0b83ba9dd12f46f36fa615d5b93e07af47a2b5d + +Name: Files/kubernetes/pod_volume.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 93bd20b9c2111115131a96769b710e09001248ba9c3d3f33e1d616d0b9546728 + +Name: Files/kubernetes/replicaset.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 5ed8f7acab4ff3f2e32ac00c7d1bb981197877c654bed8e0a6d1b2f603cebf5b + +Name: Files/kubernetes/secret_1.yaml +Content-Type: application/yaml +Algorithm: SHA-256 +Hash: 8c170000410e72c1b81784061928cde26d1f9ea027fbece4da34ced08da8c4b4 + +Name: Scripts/container_update_mgmt.py +Content-Type: text/x-python +Algorithm: SHA-256 +Hash: 9e272402dd0f6f6b39ffc750590eedef028ce22506eb7436a1a1fcfa76c66423 \ No newline at end of file diff --git a/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Scripts/vnflcm_noop.py b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Scripts/vnflcm_noop.py index bf75395b9..4bedc4bfc 100644 --- a/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Scripts/vnflcm_noop.py +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Scripts/vnflcm_noop.py @@ -88,3 +88,13 @@ class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): change_ext_conn_request, grant, grant_request, **kwargs): pass + + @log.log + def modify_information_start(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + + @log.log + def modify_information_end(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass diff --git a/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/TOSCA-Metadata/TOSCA.meta b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/TOSCA-Metadata/TOSCA.meta index 9b6b11ad9..45184ad2a 100644 --- a/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/TOSCA-Metadata/TOSCA.meta +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/TOSCA-Metadata/TOSCA.meta @@ -9,4 +9,4 @@ Content-type: application/x-iso9066-image Name: Scripts/vnflcm_noop.py Content-Type: text/x-python Algorithm: SHA-256 -Hash: 950422e356c3eb6a7c92f424d975e2d3f295f480321bd91a97501045eddeab4a +Hash: 559837eda52829630f90c803be30c82141aa4c7441736a6827495e7fdb768d0a diff --git a/tacker/tests/functional/sol_kubernetes/vnflcm/base.py b/tacker/tests/functional/sol_kubernetes/vnflcm/base.py index e33d6dc71..dcaa06dbe 100644 --- a/tacker/tests/functional/sol_kubernetes/vnflcm/base.py +++ b/tacker/tests/functional/sol_kubernetes/vnflcm/base.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. import os +import shutil import time from oslo_serialization import jsonutils @@ -56,7 +57,8 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest): "terminate": timeout, "heal_sol002": timeout, "heal_sol003": timeout * 2, - "scale": timeout + "scale": timeout, + "modify": timeout } cls.vnf_package_ids = [] @@ -81,6 +83,63 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest): self.skipTest(f"Kubernetes VIM '{vim_name}' is missing") self.vim_id = vim['id'] + def _create_and_upload_vnf_package_add_mgmt( + self, tacker_client, csar_package_name, + user_defined_data, mgmt_rela_path): + # create vnf package + body = jsonutils.dumps({"userDefinedData": user_defined_data}) + _, vnf_package = tacker_client.do_request( + self.base_vnf_package_url, "POST", body=body) + vnf_pkg_id = vnf_package['id'] + + # cp MgmtDriver to package + mgmt_abs_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), mgmt_rela_path)) + mgmt_package_abs_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), + f"../../../etc/samples/etsi/nfv/" + f"{csar_package_name}/Scripts/")) + os.mkdir(mgmt_package_abs_path) + shutil.copy(mgmt_abs_path, mgmt_package_abs_path) + + # upload vnf package + csar_package_path = ("../../../etc/samples/etsi/nfv/" + f"{csar_package_name}") + file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), csar_package_path)) + + # Generating unique vnfd id. This is required when multiple workers + # are running concurrently. The call below creates a new temporary + # CSAR with unique vnfd id. + file_path, _ = utils.create_csar_with_unique_vnfd_id(file_path) + + with open(file_path, 'rb') as file_object: + tacker_client.do_request( + f"{self.base_vnf_package_url}/{vnf_pkg_id}/package_content", + "PUT", body=file_object, content_type='application/zip') + + # wait for onboard + start_time = int(time.time()) + show_url = os.path.join(self.base_vnf_package_url, vnf_pkg_id) + vnfd_id = None + while True: + _, body = tacker_client.do_request(show_url, "GET") + if body['onboardingState'] == "ONBOARDED": + vnfd_id = body['vnfdId'] + break + + if (int(time.time()) - start_time) > VNF_PACKAGE_UPLOAD_TIMEOUT: + raise Exception(WAIT_TIMEOUT_ERR_MSG % + {"action": "onboard vnf package", + "timeout": VNF_PACKAGE_UPLOAD_TIMEOUT}) + + time.sleep(RETRY_WAIT_TIME) + + # remove temporarily created CSAR file + os.remove(file_path) + shutil.rmtree(mgmt_package_abs_path) + return vnf_package['id'], vnfd_id + def _create_and_upload_vnf_package(self, tacker_client, csar_package_name, user_defined_data): # create vnf package @@ -168,6 +227,7 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest): resp, response_body = self.http_client.do_request( self.base_vnf_instances_url, "POST", body=jsonutils.dumps(request_body)) + self.assertEqual(201, resp.status_code) return resp, response_body def _delete_wait_vnf_instance(self, id): @@ -228,6 +288,8 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest): _, vnf_instance = self._create_vnf_instance( vnfd_id, vnf_instance_name=inst_name, vnf_instance_description=inst_desc) + self.assertEqual( + 'NOT_INSTANTIATED', vnf_instance['instantiationState']) # instantiate vnf instance additional_param = additional_params @@ -237,8 +299,31 @@ class BaseVnfLcmKubernetesTest(base.BaseTackerTest): self._instantiate_vnf_instance(vnf_instance['id'], request_body) vnf_instance = self._show_vnf_instance(vnf_instance['id']) + self.assertEqual( + 'INSTANTIATED', vnf_instance['instantiationState']) + vnflcm_op_occ = self._get_vnflcm_op_occs_by_id( + self.context, vnf_instance['id']) + self.assertEqual('COMPLETED', vnflcm_op_occ.operation_state) + self.assertEqual('INSTANTIATE', vnflcm_op_occ.operation) + return vnf_instance + def _modify_vnf_instance(self, vnf_instance_id, request_body): + # modify vnf instance + url = os.path.join(self.base_vnf_instances_url, vnf_instance_id) + resp, _ = self.http_client.do_request( + url, "PATCH", body=jsonutils.dumps(request_body)) + self.assertEqual(202, resp.status_code) + time.sleep(5) + self._wait_vnflcm_op_occs( + self.context, vnf_instance_id, self.lcm_timeout['modify']) + vnflcm_op_occ = self._get_vnflcm_op_occs_by_id( + self.context, vnf_instance_id) + self.assertEqual('MODIFY_INFO', vnflcm_op_occ.operation) + vnf_instance = self._show_vnf_instance(vnf_instance_id) + vnfc_rscs = self._get_vnfc_resource_info(vnf_instance) + return vnf_instance, vnfc_rscs + def _terminate_vnf_instance(self, id, request_body=None): if request_body is None: # Terminate vnf forcefully diff --git a/tacker/tests/functional/sol_kubernetes/vnflcm/test_kubernetes_container_update.py b/tacker/tests/functional/sol_kubernetes/vnflcm/test_kubernetes_container_update.py new file mode 100644 index 000000000..f0a6d0685 --- /dev/null +++ b/tacker/tests/functional/sol_kubernetes/vnflcm/test_kubernetes_container_update.py @@ -0,0 +1,102 @@ +# 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 VnfLcmKubernetesContainerUpdate(vnflcm_base.BaseVnfLcmKubernetesTest): + + @classmethod + def setUpClass(cls): + super(VnfLcmKubernetesContainerUpdate, cls).setUpClass() + mgmt_rela_path = ("../../../../../samples/mgmt_driver/kubernetes/" + "container_update/container_update_mgmt.py") + vnf_package_id_before, cls.vnfd_id_before = ( + cls._create_and_upload_vnf_package_add_mgmt( + cls, cls.tacker_client, "test_cnf_container_update_before", + {"key": "sample_container_update_before_functional"}, + mgmt_rela_path)) + cls.vnf_package_ids.append(vnf_package_id_before) + + vnf_package_id_after, cls.vnfd_id_after = ( + cls._create_and_upload_vnf_package_add_mgmt( + cls, cls.tacker_client, "test_cnf_container_update_after", + {"key": "sample_container_update_after_functional"}, + mgmt_rela_path)) + cls.vnf_package_ids.append(vnf_package_id_after) + + @classmethod + def tearDownClass(cls): + super(VnfLcmKubernetesContainerUpdate, cls).tearDownClass() + + def test_container_update_multi_kinds(self): + vnf_instance_name = "container_update_multi_kinds" + vnf_instance_description = "container update multi kinds" + files = ["Files/kubernetes/configmap_1.yaml", + "Files/kubernetes/deployment.yaml", + "Files/kubernetes/pod_env.yaml", + "Files/kubernetes/pod_volume.yaml", + "Files/kubernetes/replicaset.yaml", + "Files/kubernetes/secret_1.yaml"] + additional_param = { + "lcm-kubernetes-def-files": files, + "namespace": "default"} + + # instantiate + vnf_instance = self._create_and_instantiate_vnf_instance( + self.vnfd_id_before, "simple", vnf_instance_name, + vnf_instance_description, additional_param) + + before_vnfc_rscs = self._get_vnfc_resource_info(vnf_instance) + self.assertEqual(4, len(before_vnfc_rscs)) + + # modify + vnf_instance_name = "modify_vnf_after" + configmap_secret_paths = [ + "Files/kubernetes/configmap_2.yaml", + "Files/kubernetes/secret_2.yaml"] + metadata = {"configmap_secret_paths": configmap_secret_paths} + modify_request_body = { + "vnfdId": self.vnfd_id_after, + "vnfInstanceName": vnf_instance_name, + "metadata": metadata + } + + vnf_instance_after, after_vnfc_rscs = self._modify_vnf_instance( + vnf_instance['id'], modify_request_body) + + self.assertEqual(4, len(after_vnfc_rscs)) + + for after_vnfc_rsc in after_vnfc_rscs: + for before_vnfc_rsc in before_vnfc_rscs: + after_resource = after_vnfc_rsc['computeResource'] + before_resource = before_vnfc_rsc['computeResource'] + if after_vnfc_rsc['id'] == before_vnfc_rsc['id']: + if after_resource['vimLevelResourceType'] == 'Deployment': + # check stored pod name is changed (Deployment) + self.assertNotEqual(before_resource['resourceId'], + after_resource['resourceId']) + else: + # check stored pod name is not changed (other) + self.assertEqual(before_resource['resourceId'], + after_resource['resourceId']) + + self.assertEqual( + self.vnfd_id_after, vnf_instance_after['vnfdId']) + self.assertEqual( + vnf_instance_name, vnf_instance_after['vnfInstanceName']) + + # terminate + self._terminate_vnf_instance(vnf_instance['id']) + self._delete_vnf_instance(vnf_instance['id']) diff --git a/tacker/tests/unit/conductor/test_conductor_server.py b/tacker/tests/unit/conductor/test_conductor_server.py index 0b5b3957f..3dfebdccb 100644 --- a/tacker/tests/unit/conductor/test_conductor_server.py +++ b/tacker/tests/unit/conductor/test_conductor_server.py @@ -69,7 +69,10 @@ CONF = tacker.conf.CONF class FakeVnfLcmDriver(mock.Mock): - pass + def modify_vnf(self, context, vnf_lcm_opoccs, body_data, + vnfd_pkg_data, vnfd_id): + return datetime.datetime( + 1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC) class FakeVNFMPlugin(mock.Mock): @@ -3235,13 +3238,10 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase): @mock.patch.object(conductor_server, 'revert_update_lcm') @mock.patch.object(t_context.get_admin_context().session, "add") @mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "save") - @mock.patch.object(objects.VnfInstance, "update") @mock.patch.object(objects.vnf_lcm_op_occs.VnfLcmOpOcc, "create") - def test_update(self, mock_create, mock_update, mock_save, mock_add, + def test_update(self, mock_create, mock_save, mock_add, mock_revert): mock_create.return_value = "OK" - mock_update.return_value = datetime.datetime( - 1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC) mock_add.return_value = "OK" mock_save.return_value = "OK" vnfd_id = "2c69a161-0000-4b0f-bcf8-391f8fc76600" diff --git a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py index bc1281422..5e3133ca4 100644 --- a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py +++ b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py @@ -192,6 +192,86 @@ class TestVnflcmDriver(db_base.SqlTestCase): 'tenant': uuidsentinel.tenant_id} self.vim_client.get_vim.return_value = vim_obj + @mock.patch.object(TackerManager, 'get_service_plugins', + return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash') + @mock.patch.object(objects.VnfInstance, "update") + @mock.patch('tacker.vnflcm.utils._get_vnf_package_path') + @mock.patch.object(objects.VimConnectionInfo, "obj_from_primitive") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + @mock.patch.object(objects.VnfInstance, "get_by_id") + def test_modify_vnf(self, mock_get_by_id, mock_load, mock_vnfd_dict, + mock_vim, mock_get_vnf_package_path, mock_update, + mock_init_hash, mock_get_service_plugins): + vim_connection_info = vim_connection.VimConnectionInfo( + vim_type="kubernetes") + mock_vim.return_value = vim_connection_info + vnf_instance = fakes.return_vnf_instance( + fields.VnfInstanceState.INSTANTIATED) + mock_get_by_id.return_value = vnf_instance + mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf() + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + vnf_lcm_opoccs = { + 'vnf_instance_id': 'c5d64d28-c868-4348-8a96-1a6976ce465f', + 'operationParams': '{"metadata": {"configmap_secret_paths":' + '["Files/kubernetes/configmap_2.yaml",' + '"Files/kubernetes/secret_2.yaml"]}}'} + body_data = {} + vnfd_pkg_data = {} + vnfd_id = '122cbedf-0b50-4706-aad1-7106d590c0a7' + mock_load.return_value = fakes.return_vnf_interfaces() + self._mock_vnf_manager() + driver = vnflcm_driver.VnfLcmDriver() + driver.modify_vnf(self.context, vnf_lcm_opoccs, + body_data, vnfd_pkg_data, vnfd_id) + + self.assertEqual(2, self._vnf_manager.invoke.call_count) + + @mock.patch.object(TackerManager, 'get_service_plugins', + return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash') + @mock.patch.object(objects.VnfInstance, "update") + @mock.patch('tacker.vnflcm.utils._get_vnf_package_path') + @mock.patch.object(objects.VimConnectionInfo, "obj_from_primitive") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + @mock.patch.object(objects.VnfInstance, "get_by_id") + def test_modify_vnf_params_error( + self, mock_get_by_id, mock_load, mock_vnfd_dict, mock_vim, + mock_get_vnf_package_path, mock_update, mock_init_hash, + mock_get_service_plugins): + vim_connection_info = vim_connection.VimConnectionInfo( + vim_type="kubernetes") + mock_vim.return_value = vim_connection_info + vnf_instance = fakes.return_vnf_instance( + fields.VnfInstanceState.INSTANTIATED) + mock_get_by_id.return_value = vnf_instance + mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf() + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + vnf_lcm_opoccs = { + 'vnf_instance_id': 'c5d64d28-c868-4348-8a96-1a6976ce465f', + 'operationParams': 'error_params' + } + body_data = {} + vnfd_pkg_data = {} + vnfd_id = '122cbedf-0b50-4706-aad1-7106d590c0a7' + mock_load.return_value = fakes.return_vnf_interfaces() + self._mock_vnf_manager() + driver = vnflcm_driver.VnfLcmDriver() + self.assertRaises( + exceptions.InvalidInput, driver.modify_vnf, self.context, + vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id) + self.assertEqual(1, self._vnf_manager.invoke.call_count) + @mock.patch('tacker.vnflcm.utils.get_default_scale_status') @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @mock.patch.object(VnfLcmDriver, diff --git a/tacker/tests/unit/vnfm/mgmt_drivers/fakes.py b/tacker/tests/unit/vnfm/mgmt_drivers/fakes.py new file mode 100644 index 000000000..ebb34a505 --- /dev/null +++ b/tacker/tests/unit/vnfm/mgmt_drivers/fakes.py @@ -0,0 +1,234 @@ +# Copyright (C) 2022 FUJITSU +# 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 datetime +from kubernetes import client +import os + +from tacker import objects +from tacker.objects import fields +from tacker.tests import uuidsentinel + + +def get_vnf_instance_object( + instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED): + + inst_vnf_info = get_vnf_instantiated_info() + + vnf_instance = objects.VnfInstance( + id=uuidsentinel.vnf_instance_id, + vnf_instance_name="Test-Vnf-Instance", + vnf_instance_description="vnf instance description", + instantiation_state=instantiation_state, vnfd_id=uuidsentinel.vnfd_id, + vnf_provider="sample provider", vnf_product_name="vnf product name", + vnf_software_version='1.0', vnfd_version="2", + instantiated_vnf_info=inst_vnf_info, + vnf_metadata={"namespace": "default"} + ) + + return vnf_instance + + +def get_vnf_instantiated_info(flavour_id='simple', + instantiation_level_id=None, vnfc_resource_info=None, + virtual_storage_resource_info=None, + vnf_virtual_link_resource_info=None, + ext_managed_virtual_link_info=None, + ext_virtual_link_info=None, + ext_cp_info=None): + + vnfc_resource_info = vnfc_resource_info or [] + vnf_virtual_link_resource_info = vnf_virtual_link_resource_info or [] + virtual_storage_resource_info = virtual_storage_resource_info or [] + ext_managed_virtual_link_info = ext_managed_virtual_link_info or [] + ext_virtual_link_info = ext_virtual_link_info or [] + ext_cp_info = ext_cp_info or [] + + inst_vnf_info = objects.InstantiatedVnfInfo( + flavour_id=flavour_id, + instantiation_level_id=instantiation_level_id, + instance_id='9693ab84-6da8-5926-5e7a-3a1b963156c0', + vnfc_resource_info=vnfc_resource_info, + vnf_virtual_link_resource_info=vnf_virtual_link_resource_info, + virtual_storage_resource_info=virtual_storage_resource_info, + ext_managed_virtual_link_info=ext_managed_virtual_link_info, + ext_virtual_link_info=ext_virtual_link_info, + ext_cp_info=ext_cp_info, + additional_params=return_cnf_additional_params()) + + return inst_vnf_info + + +def fake_pod(): + return client.V1Pod( + api_version='v1', + kind='Pod', + metadata=client.V1ObjectMeta( + name='curry-test001', + namespace='curryns' + ), + spec=client.V1PodSpec( + containers=[ + client.V1Container( + image="curry", + name="curry" + ) + ] + ), + status=client.V1PodStatus( + phase='Running', + ) + ) + + +def fake_list_pod(): + return client.V1PodList( + items=[ + client.V1Pod( + api_version='v1', + kind='Pod', + metadata=client.V1ObjectMeta( + name='curry-test001', + namespace='curryns' + ), + spec=client.V1PodSpec( + containers=[ + client.V1Container( + image="curry", + name="curry" + ) + ] + ), + status=client.V1PodStatus( + phase='Pending', + ) + ) + ] + ) + + +def get_fake_pod_info(kind, name='fake_name', pod_status='Running', + pod_name=None): + if not pod_name: + if kind == 'Deployment': + pod_name = _('{name}-1234567890-abcde').format(name=name) + elif kind in ('ReplicaSet', 'DaemonSet'): + pod_name = _('{name}-12345').format(name=name) + elif kind == 'StatefulSet': + pod_name = _('{name}-1').format(name=name) + elif kind == 'Pod': + pod_name = name + return client.V1Pod( + metadata=client.V1ObjectMeta( + name=pod_name, + creation_timestamp=datetime.datetime.now().isoformat('T')), + status=client.V1PodStatus(phase=pod_status)) + + +def vnfd_dict_cnf(): + tacker_dir = os.getcwd() + def_dir = tacker_dir + "/samples/vnf_packages/Definitions/" + vnfd_dict = { + "tosca_definitions_version": "tosca_simple_yaml_1_2", + "description": "Sample VNF flavour for Sample VNF", + "imports": [ + def_dir + "etsi_nfv_sol001_common_types.yaml", + def_dir + "etsi_nfv_sol001_vnfd_types.yaml", + def_dir + "helloworld3_types.yaml"], + "topology_template": { + "node_templates": { + "VNF": { + "type": "company.provider.VNF", + "properties": { + "flavour_description": "A simple flavour"}}, + "VDU1": { + "type": "tosca.nodes.nfv.Vdu.Compute", + "properties": { + "name": "vdu1", + "description": "vdu1 compute node", + "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"]}}}}}, + { + "vdu1_initial_delta": { + "type": "tosca.policies.nfv.VduInitialDelta", + "properties": { + "initial_delta": { + "number_of_instances": 0}}, + "targets": ["VDU1"]}}, + { + "vdu1_scaling_aspect_deltas": { + "type": "tosca.policies.nfv.VduScalingAspectDeltas", + "properties": { + "aspect": "vdu1_aspect", + "deltas": { + "delta_1": { + "number_of_instances": 1}}}, + "targets": ["VDU1"]}}, + { + "instantiation_levels": { + "type": "tosca.policies.nfv.InstantiationLevels", + "properties": { + "levels": { + "instantiation_level_1": { + "description": "Smallest size", + "scale_info": { + "vdu1_aspect": { + "scale_level": 0}}}, + "instantiation_level_2": { + "description": "Largest size", + "scale_info": { + "vdu1_aspect": { + "scale_level": 2}}} + }, + "default_level": "instantiation_level_1"}}}, + { + "vdu1_instantiation_levels": { + "type": "tosca.policies.nfv.VduInstantiationLevels", + "properties": { + "levels": { + "instantiation_level_1": { + "number_of_instances": 0}, + "instantiation_level_2": { + "number_of_instances": 2}}}, + "targets": ["VDU1"]}} + ] + } + } + return vnfd_dict + + +def return_cnf_additional_params(): + additional_params = { + "lcm-kubernetes-def-files": [ + "Files/kubernetes/configmap_1.yaml", + "Files/kubernetes/pod_volume.yaml", + "Files/kubernetes/replicaset.yaml", + "Files/kubernetes/secret_1.yaml" + ], + "namespace": "default" + } + return additional_params diff --git a/tacker/tests/unit/vnfm/mgmt_drivers/test_container_update_mgmt.py b/tacker/tests/unit/vnfm/mgmt_drivers/test_container_update_mgmt.py new file mode 100644 index 000000000..7dcc1f785 --- /dev/null +++ b/tacker/tests/unit/vnfm/mgmt_drivers/test_container_update_mgmt.py @@ -0,0 +1,156 @@ +# Copyright (c) 2022 FUJITSU +# +# 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 os + +from kubernetes import client +from samples.mgmt_driver.kubernetes.container_update import ( + container_update_mgmt as mgmt_driver) +from tacker.common import exceptions +from tacker import context +from tacker.tests.unit import base +from tacker.tests.unit.vnflcm import fakes +from tacker.tests.unit.vnfm.mgmt_drivers import fakes as mgmt_fakes +from tacker.vnflcm import utils as vnflcm_utils +from tacker.vnfm.infra_drivers.kubernetes.kubernetes_driver import Kubernetes +from unittest import mock + + +class FakeVimClient(mock.Mock): + pass + + +class TestContainerUpdate(base.TestCase): + + def setUp(self): + super(TestContainerUpdate, self).setUp() + self.context = context.get_admin_context() + self.cntr_update_mgmt = mgmt_driver.ContainerUpdateMgmtDriver() + self.vnf_instance = mgmt_fakes.get_vnf_instance_object() + self.modify_vnf_request = None + self._mock_vim_client() + self._stub_get_vim() + self._mock_get_vnf_package_path() + self.yaml_path_before = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "../../../etc/samples/etsi/nfv/" + "test_cnf_container_update_before/") + + def _mock_vim_client(self): + self.vim_client = mock.Mock(wraps=FakeVimClient()) + fake_vim_client = mock.Mock() + fake_vim_client.return_value = self.vim_client + self._mock( + 'tacker.vnfm.vim_client.VimClient', fake_vim_client) + + def _stub_get_vim(self): + vim_obj = {'vim_id': '15053249-6979-8ee5-67d2-189fa9379534', + 'vim_name': 'fake_vim', 'vim_auth': + {'auth_url': 'http://localhost/identity', 'password': + 'test_pw', 'username': 'test_user', 'project_name': + 'test_project'}, 'vim_type': 'openstack', 'tenant': + '15053249-6979-8ee5-67d2-189fa9379534'} + self.vim_client.get_vim.return_value = vim_obj + + def _mock_get_vnf_package_path(self): + vnflcm_utils.get_vnf_package_path = mock.Mock( + return_value=os.path.join( + os.path.abspath(os.path.dirname(__file__)), + "../../../etc/samples/etsi/nfv/" + "test_cnf_container_update_after/") + ) + + def test_container_update_get_type(self): + get_type = self.cntr_update_mgmt.get_type() + self.assertEqual('mgmt-container-update', get_type) + + def test_container_update_get_name(self): + get_name = self.cntr_update_mgmt.get_name() + self.assertEqual('mgmt-container-update', get_name) + + def test_container_update_get_description(self): + get_description = self.cntr_update_mgmt.get_description() + self.assertEqual( + 'Tacker Container Update VNF Mgmt Driver', get_description) + + def test_container_update_modify_information_start(self): + modify_start = self.cntr_update_mgmt.modify_information_start( + self.context, self.vnf_instance, self.modify_vnf_request) + self.assertIsNone(modify_start) + + def test_container_update_modify_container_img(self): + old_containers = [ + client.V1Container(image="curry", name="curry") + ] + + new_containers = [ + client.V1Container(image="curry1", name="curry") + ] + self.cntr_update_mgmt._modify_container_img( + old_containers, new_containers) + self.assertEqual('curry1', old_containers[0].image) + + @mock.patch.object(client.CoreV1Api, 'list_namespaced_pod') + def test_container_update_replace_wait_k8s_timeout(self, mock_list_pod): + k8s_pod_objs = [ + {'namespace': 'default', 'object': mgmt_fakes.fake_pod()} + ] + kube_driver = Kubernetes() + kube_driver.STACK_RETRIES = 1 + kube_driver.STACK_RETRY_WAIT = 5 + mock_list_pod.return_value = mgmt_fakes.fake_list_pod() + self.assertRaises( + exceptions.MgmtDriverOtherError, + self.cntr_update_mgmt._replace_wait_k8s, kube_driver, + k8s_pod_objs, client.CoreV1Api, self.vnf_instance) + + @mock.patch.object(client.CoreV1Api, 'list_namespaced_pod') + def test_container_update_replace_wait_k8s_unknown(self, mock_list_pod): + k8s_pod_objs = [ + {'namespace': 'default', 'object': mgmt_fakes.fake_pod()} + ] + kube_driver = Kubernetes() + pod_list = mgmt_fakes.fake_list_pod() + pod_list.items[0].status.phase = 'Unknown' + mock_list_pod.return_value = pod_list + self.assertRaises( + exceptions.MgmtDriverOtherError, + self.cntr_update_mgmt._replace_wait_k8s, kube_driver, + k8s_pod_objs, client.CoreV1Api, self.vnf_instance) + + @mock.patch('tacker.objects.vnf_instance.VnfInstance.save') + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch.object(client.CoreV1Api, 'list_namespaced_pod') + @mock.patch.object(client.CoreV1Api, 'replace_namespaced_secret') + @mock.patch.object(client.CoreV1Api, 'replace_namespaced_config_map') + @mock.patch.object(client.CoreV1Api, 'replace_namespaced_pod') + @mock.patch.object(client.CoreV1Api, 'read_namespaced_pod') + def test_container_update_modify_information_end( + self, mock_read_pod, mock_replace_pod, mock_replace_config_map, + mock_replace_secret, mock_list_pod, mock_vnfd_dict, mock_save): + mock_read_pod.return_value = mgmt_fakes.fake_pod() + mock_list_pod.return_value = client.V1PodList(items=[ + mgmt_fakes.get_fake_pod_info(kind='Pod', name='vdu1')]) + mock_vnfd_dict.return_value = fakes.vnfd_dict_cnf() + kwargs = { + 'old_vnf_package_path': self.yaml_path_before, + 'configmap_secret_paths': [ + "Files/kubernetes/configmap_2.yaml", + "Files/kubernetes/secret_2.yaml" + ] + } + self.cntr_update_mgmt.modify_information_end( + self.context, self.vnf_instance, self.modify_vnf_request, **kwargs) + self.assertEqual(1, mock_save.call_count) diff --git a/tacker/vnflcm/utils.py b/tacker/vnflcm/utils.py index eb673c878..fe2303094 100644 --- a/tacker/vnflcm/utils.py +++ b/tacker/vnflcm/utils.py @@ -35,6 +35,12 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF +# TODO(fengyi): change _get_vim to a public method +# and then delete this method. +def get_vim(context, vim_connection_info): + return _get_vim(context, vim_connection_info) + + def _get_vim(context, vim_connection_info): vim_client_obj = vim_client.VimClient() @@ -1163,6 +1169,12 @@ def _convert_desired_capacity(inst_level_id, vnfd_dict, vdu): return desired_capacity +# TODO(fengyi): change _get_vnf_package_path to a public method +# and then delete this method. +def get_vnf_package_path(context, vnfd_id): + return _get_vnf_package_path(context, vnfd_id) + + def _get_vnf_package_path(context, vnfd_id): return os.path.join(CONF.vnf_package.vnf_package_csar_path, _get_vnf_package_id(context, vnfd_id)) diff --git a/tacker/vnflcm/vnflcm_driver.py b/tacker/vnflcm/vnflcm_driver.py index 2b36823e0..986ec31dc 100644 --- a/tacker/vnflcm/vnflcm_driver.py +++ b/tacker/vnflcm/vnflcm_driver.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import ast import copy from datetime import datetime import functools @@ -501,6 +502,67 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): return tacker_mgmt_driver + @log.log + def modify_vnf(self, context, vnf_lcm_opoccs, + body_data, vnfd_pkg_data, vnfd_id): + # Get vnf_instance from DB by vnf_instance_id + vnf_instance_id = vnf_lcm_opoccs.get('vnf_instance_id') + vnf_instance = objects.VnfInstance.get_by_id(context, vnf_instance_id) + vnfd_dict = vnflcm_utils.get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + + # modify_information_start(context, vnf_instance) + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'modify_information_start', vnf_instance, vnfd_dict), + 'modify_information_start', context=context, + modify_vnf_request=None, vnf_instance=vnf_instance) + + # Get the old vnf package path according to vnfd_id + old_vnf_package_path = vnflcm_utils.get_vnf_package_path( + context, vnf_instance.vnfd_id) + + # Update vnf_instance + try: + _vnf_instance = objects.VnfInstance(context=context) + updated_time = _vnf_instance.update( + context, vnf_lcm_opoccs, body_data, vnfd_pkg_data, vnfd_id) + except Exception as msg: + raise Exception(str(msg)) + vnf_instance = objects.VnfInstance.get_by_id(context, vnf_instance_id) + + vim_info = vnflcm_utils.get_vim( + context, vnf_instance.vim_connection_info) + + vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( + vim_info, context) + + kwargs = {} + if vim_connection_info.vim_type == 'kubernetes': + # If the file path of ConfigMap/Secret is changed + cm_secret_paths = [] + # Get the metadata from vnf_lcm_opoccs + operation_params = vnf_lcm_opoccs.get('operationParams') + if operation_params: + try: + cm_secret_paths = (ast.literal_eval(operation_params) + .get('metadata', {}) + .get('configmap_secret_paths', [])) + except Exception as e: + LOG.error('Invalid format operationParams') + raise exceptions.InvalidInput(str(e)) + kwargs = {"old_vnf_package_path": old_vnf_package_path, + "configmap_secret_paths": cm_secret_paths} + + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'modify_information_end', vnf_instance, vnfd_dict), + 'modify_information_end', context=context, + modify_vnf_request=None, + vnf_instance=vnf_instance, **kwargs) + return updated_time + @log.log def instantiate_vnf(self, context, vnf_instance, vnf_dict, instantiate_vnf_req): diff --git a/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py b/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py index cb0ba2bff..b58c31827 100644 --- a/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py +++ b/tacker/vnfm/infra_drivers/kubernetes/kubernetes_driver.py @@ -124,7 +124,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, """ LOG.debug('vnf %s', vnf) # initialize Kubernetes APIs - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: core_v1_api_client = self.kubernetes.get_core_v1_api_client( auth=auth_cred) @@ -153,7 +153,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, """ # initialize Kubernetes APIs if '{' not in vnf_id and '}' not in vnf_id: - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: core_v1_api_client = \ self.kubernetes.get_core_v1_api_client(auth=auth_cred) @@ -162,7 +162,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, pods_information = self._get_pods_information( core_v1_api_client=core_v1_api_client, deployment_info=deployment_info) - status = self._get_pod_status(pods_information) + status = self.get_pod_status(pods_information) stack_retries = self.STACK_RETRIES error_reason = None while status == 'Pending' and stack_retries > 0: @@ -171,7 +171,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, self._get_pods_information( core_v1_api_client=core_v1_api_client, deployment_info=deployment_info) - status = self._get_pod_status(pods_information) + status = self.get_pod_status(pods_information) LOG.debug('status: %s', status) stack_retries = stack_retries - 1 @@ -553,7 +553,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, pods_information.append(item) return pods_information - def _get_pod_status(self, pods_information): + def get_pod_status(self, pods_information): pending_flag = False unknown_flag = False for pod_info in pods_information: @@ -578,7 +578,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, ConfigMap data """ # initialize Kubernetes APIs - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: core_v1_api_client = \ self.kubernetes.get_core_v1_api_client(auth=auth_cred) @@ -841,7 +841,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, def delete(self, plugin, context, vnf_id, auth_attr, region_name=None, vnf_instance=None, terminate_vnf_req=None): """Delete function""" - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: if not vnf_instance: # execute legacy delete method @@ -977,8 +977,8 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, LOG.error('Deleting wait VNF got an error due to %s', e) raise - def _select_k8s_obj_read_api(self, k8s_client_dict, namespace, name, - kind, api_version): + def select_k8s_obj_read_api(self, k8s_client_dict, namespace, name, + kind, api_version): """select kubernetes read api and call""" def convert(name): name_with_underscores = re.sub( @@ -1039,7 +1039,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, marked as deleted. """ # initialize Kubernetes APIs - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: if not vnf_instance: @@ -1068,7 +1068,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, if not k8s_client_dict.get(api_version): continue try: - self._select_k8s_obj_read_api( + self.select_k8s_obj_read_api( k8s_client_dict=k8s_client_dict, namespace=namespace, name=name, @@ -1265,7 +1265,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, of policy scaling when user define VNF descriptor. """ # initialize Kubernetes APIs - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: if not policy.get('vnf_instance_id'): # execute legacy scale method @@ -1358,7 +1358,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, pods_information = self._get_pods_information( core_v1_api_client=core_v1_api_client, deployment_info=deployment_info) - status = self._get_pod_status(pods_information) + status = self.get_pod_status(pods_information) stack_retries = self.STACK_RETRIES error_reason = None @@ -1368,7 +1368,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, pods_information = self._get_pods_information( core_v1_api_client=core_v1_api_client, deployment_info=deployment_info) - status = self._get_pod_status(pods_information) + status = self.get_pod_status(pods_information) # LOG.debug('status: %s', status) stack_retries = stack_retries - 1 @@ -1390,7 +1390,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, elif stack_retries != 0 and status != 'Running': raise vnfm.VNFCreateWaitFailed(reason=error_reason) - def _is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name): + def is_match_pod_naming_rule(self, rsc_kind, rsc_name, pod_name): match_result = None if rsc_kind == 'Pod': # Expected example: name @@ -1429,7 +1429,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, from Pod objects is RUNNING. """ # initialize Kubernetes APIs - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) try: if not policy.get('vnf_instance_id'): # execute legacy scale_wait method @@ -1481,12 +1481,12 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, respone = core_v1_api_client.list_namespaced_pod( namespace=namespace) for pod in respone.items: - match_result = self._is_match_pod_naming_rule( + match_result = self.is_match_pod_naming_rule( kind, name, pod.metadata.name) if match_result: pods_information.append(pod) - status = self._get_pod_status(pods_information) + status = self.get_pod_status(pods_information) if status == 'Running' and \ scale_info.spec.replicas != len(pods_information): status = 'Pending' @@ -1523,7 +1523,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, # TODO(phuoc): will update it for other components pass - def _get_auth_creds(self, auth_cred): + def get_auth_creds(self, auth_cred): file_descriptor = self._create_ssl_ca_file(auth_cred) if ('username' not in auth_cred) and ('password' not in auth_cred): auth_cred['username'] = 'None' @@ -1821,7 +1821,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, None, context, vnf_instance, auth_attr) return instance_id else: - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) k8s_client_dict = self.kubernetes.get_k8s_client_dict(auth_cred) transformer = translate_outputs.Transformer( None, None, None, k8s_client_dict) @@ -1887,7 +1887,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, and metadata, and vdu id. """ auth_attr = vim_connection_info.access_info - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) namespace = vnf_instance.vnf_metadata['namespace'] try: # get Kubernetes object files @@ -1943,7 +1943,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, # get initially store VnfcResourceInfo after instantiation for pod in pod_list.items: pod_name = pod.metadata.name - match_result = self._is_match_pod_naming_rule( + match_result = self.is_match_pod_naming_rule( rsc_kind, rsc_name, pod_name) if match_result: # get metadata @@ -2005,7 +2005,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, # Get the associated pod name that runs with the actual kubernetes actual_pod_names = list() for pod in sorted_pod_list: - match_result = self._is_match_pod_naming_rule( + match_result = self.is_match_pod_naming_rule( rsc_kind, rsc_name, pod.metadata.name) if match_result: actual_pod_names.append(pod.metadata.name) @@ -2033,7 +2033,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, """ # initialize Kubernetes APIs auth_attr = vim_connection_info.access_info - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) inst_vnf_info = vnf_instance.instantiated_vnf_info namespace = vnf_instance.vnf_metadata['namespace'] try: @@ -2189,7 +2189,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, """ # initialize Kubernetes APIs auth_attr = vim_connection_info.access_info - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) namespace = vnf_instance.vnf_metadata['namespace'] try: core_v1_api_client = self.kubernetes.get_core_v1_api_client( @@ -2251,7 +2251,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, pod_list_dict[namespace] = pod_list tmp_pods_info = list() for pod in pod_list.items: - match_result = self._is_match_pod_naming_rule( + match_result = self.is_match_pod_naming_rule( k8s_resource.get('kind'), k8s_resource.get('name'), pod.metadata.name) @@ -2274,7 +2274,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, 'actual_pod_num': str(len(tmp_pods_info))}) is_unmatch_pods_num = True pods_information.extend(tmp_pods_info) - status = self._get_pod_status(pods_information) + status = self.get_pod_status(pods_information) if status == 'Unknown': error_reason = _("Pod status is found Unknown") @@ -2305,7 +2305,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, """Update VnfcResourceInfo after healing""" # initialize Kubernetes APIs auth_attr = vim_connection_info.access_info - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) inst_vnf_info = vnf_instance.instantiated_vnf_info namespace = vnf_instance.vnf_metadata['namespace'] try: @@ -2417,7 +2417,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, vim_connection_info): """Update VnfcResourceInfo after scaling""" auth_attr = vim_connection_info.access_info - auth_cred, file_descriptor = self._get_auth_creds(auth_attr) + auth_cred, file_descriptor = self.get_auth_creds(auth_attr) inst_vnf_info = vnf_instance.instantiated_vnf_info try: # initialize Kubernetes APIs @@ -2471,7 +2471,7 @@ class Kubernetes(abstract_driver.VnfAbstractDriver, namespace=namespace) actual_pod_list = [] for pod in pod_list.items: - match_result = self._is_match_pod_naming_rule( + match_result = self.is_match_pod_naming_rule( rsc_kind, rsc_name, pod.metadata.name) if match_result: actual_pod_list.append(pod.metadata.name) diff --git a/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py b/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py index ba22f9300..1077ef019 100644 --- a/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py +++ b/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py @@ -97,3 +97,13 @@ class VnflcmMgmtAbstractDriver(metaclass=abc.ABCMeta): change_ext_conn_request, grant, grant_request, **kwargs): pass + + @abc.abstractmethod + def modify_information_start(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + + @abc.abstractmethod + def modify_information_end(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass diff --git a/tacker/vnfm/mgmt_drivers/vnflcm_noop.py b/tacker/vnfm/mgmt_drivers/vnflcm_noop.py index bf75395b9..4bedc4bfc 100644 --- a/tacker/vnfm/mgmt_drivers/vnflcm_noop.py +++ b/tacker/vnfm/mgmt_drivers/vnflcm_noop.py @@ -88,3 +88,13 @@ class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): change_ext_conn_request, grant, grant_request, **kwargs): pass + + @log.log + def modify_information_start(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass + + @log.log + def modify_information_end(self, context, vnf_instance, + modify_vnf_request, **kwargs): + pass diff --git a/tools/test-setup-k8s-vim.sh b/tools/test-setup-k8s-vim.sh index b3d1fc5ff..13973db6e 100755 --- a/tools/test-setup-k8s-vim.sh +++ b/tools/test-setup-k8s-vim.sh @@ -24,4 +24,4 @@ openstack vim register \ --os-user-domain-name Default \ --description "Kubernetes VIM" \ --config-file /opt/stack/tacker/tacker/tests/etc/samples/local-k8s-vim.yaml \ - vim-kubernetes + vim-kubernetes \ No newline at end of file diff --git a/tools/test-setup-mgmt.sh b/tools/test-setup-mgmt.sh new file mode 100755 index 000000000..cd6b77686 --- /dev/null +++ b/tools/test-setup-mgmt.sh @@ -0,0 +1,29 @@ +#!/bin/bash -xe + +# The purpose of this script is to copy the MgmtDriver +# of container update in the sample folder to the +# tacker code and run it. + +# Copy the MgmtDriver of container_update to the tacker. +sudo cp /opt/stack/tacker/samples/mgmt_driver/kubernetes/container_update/\ +container_update_mgmt.py /opt/stack/tacker/tacker/vnfm/mgmt_drivers/ +sudo chown stack:stack /opt/stack/tacker/tacker/vnfm/mgmt_drivers/\ +container_update_mgmt.py + +# In the configuration file of the tacker, +# add the MgmtDriver of container_update. +sudo sed -i "/VnflcmMgmtNoop/a \ \ \ \ mgmt-container-update = \ +tacker.vnfm.mgmt_drivers.container_update_mgmt:ContainerUpdateMgmtDriver" \ +/opt/stack/tacker/setup.cfg +sudo sed -i "/vnflcm_mgmt_driver = vnflcm_noop/a vnflcm_mgmt_driver = \ +vnflcm_noop,mgmt-container-update" /etc/tacker/tacker.conf + +# Reload the tacker configuration file. +cd /opt/stack/tacker/ +sudo python3 setup.py build +sudo chown -R stack:stack /opt/stack/tacker/ + +# Restart the tacker service for the +# configuration file to take effect. +sudo systemctl restart devstack@tacker-conductor +sleep 10s