From f88022cdc24302f7f6a3b7126d00891dbd7bf262 Mon Sep 17 00:00:00 2001 From: LiangLu Date: Mon, 21 Dec 2020 22:08:58 +0900 Subject: [PATCH] Implement SOL001 features to MgmtDriver * Enable VNF vendors to customize configuration methods for applications via MgmtDriver * Load LCM interface from vnfd file * Call LCM methods from vnflcm_driver * Fix unitentional mgmt_calls in vnflcm_driver._scale_vnf_pre Note: * For UT, sqlalchemy is used, which calls functions in pymysql.py, so the dependency module PyMySQL needs to be added. * The previous implementation of ``cfg.CONF.tacker.infra_driver`` depends on the default config in legacy ``VNFMPlugin`` class in tacker/vnfm/plugin.py, so we need to fix this here. * Due to drop unitentional mgmt_calls, it makes compatibility breakage for some scale actions calls and third party driver support. This backward incompatibility was decided to keep since it is not long time since last change(Victoria release) Change-Id: Ib4ad3eb9e0a84d1a5e807d2e6004e6b2c02bf3cf --- lower-constraints.txt | 1 + ...t-driver-with-sol001-a18c3a174062447a.yaml | 10 + requirements.txt | 1 + setup.cfg | 2 + tacker/common/exceptions.py | 30 + .../Definitions/helloworld3_df_simple.yaml | 269 +++++++++ .../Definitions/helloworld3_types.yaml | 65 +++ .../Drivers/vnflcm_noop.py | 68 +++ .../Definitions/helloworld3_df_simple.yaml | 269 +++++++++ .../Drivers/vnflcm_noop_false.py | 88 +++ .../Definitions/helloworld3_df_simple.yaml | 269 +++++++++ .../Drivers/vnflcm_noop.py | 81 +++ .../Definitions/helloworld3_df_simple.yaml | 206 +++++++ .../Definitions/helloworld3_top.vnfd.yaml | 31 + .../Definitions/helloworld3_types.yaml | 53 ++ .../Scripts/vnflcm_noop.py | 68 +++ .../TOSCA-Metadata/TOSCA.meta | 12 + .../functional/sol/vnflcm/test_vnflcm_noop.py | 294 ++++++++++ tacker/tests/unit/vnflcm/fakes.py | 5 + .../unit/vnflcm/test_load_vnf_interfaces.py | 248 ++++++++ .../tests/unit/vnflcm/test_vnflcm_driver.py | 544 +++++++++++++++--- tacker/vnflcm/vnflcm_driver.py | 285 +++++---- .../mgmt_drivers/vnflcm_abstract_driver.py | 77 +++ tacker/vnfm/mgmt_drivers/vnflcm_noop.py | 68 +++ test-requirements.txt | 1 + 25 files changed, 2862 insertions(+), 183 deletions(-) create mode 100644 releasenotes/notes/refactor-mgmt-driver-with-sol001-a18c3a174062447a.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_df_simple.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_types.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Drivers/vnflcm_noop.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Definitions/helloworld3_df_simple.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Drivers/vnflcm_noop_false.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Definitions/helloworld3_df_simple.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Drivers/vnflcm_noop.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_df_simple.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_top.vnfd.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_types.yaml create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Scripts/vnflcm_noop.py create mode 100644 tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/TOSCA-Metadata/TOSCA.meta create mode 100644 tacker/tests/functional/sol/vnflcm/test_vnflcm_noop.py create mode 100644 tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py create mode 100644 tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py create mode 100644 tacker/vnfm/mgmt_drivers/vnflcm_noop.py diff --git a/lower-constraints.txt b/lower-constraints.txt index 3bbe109ac..feb673f33 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -94,6 +94,7 @@ pycadf==2.7.0 pycparser==2.18 Pygments==2.2.0 pyinotify==0.9.6 +PyMySQL==0.10.1 PyNaCl==1.2.1 pyOpenSSL==17.5.0 pyparsing==2.2.0 diff --git a/releasenotes/notes/refactor-mgmt-driver-with-sol001-a18c3a174062447a.yaml b/releasenotes/notes/refactor-mgmt-driver-with-sol001-a18c3a174062447a.yaml new file mode 100644 index 000000000..0f3567663 --- /dev/null +++ b/releasenotes/notes/refactor-mgmt-driver-with-sol001-a18c3a174062447a.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Enable VNF vendors to customize configuration methods + for applications via MgmtDriver. These customizations + are specified by "interface" definition in ETSI + NFV-SOL001 v2.6.1. With MgmtDriver, users can execute + preamble and postamble of the base LCM operation. + Customization of LCM itself is not supported by + MgmtDriver. diff --git a/requirements.txt b/requirements.txt index 9bf12990b..0170a3d9d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -50,6 +50,7 @@ kubernetes>=11.0.0 # Apache-2.0 setuptools!=24.0.0,!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2,!=36.2.0,>=21.0.0 # PSF/ZPL tooz>=1.58.0 # Apache-2.0 PyYAML>=5.1 # MIT +PyMySQL>=0.10.1 # MIT # Glance Store glance-store>=2.4.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 136381d05..d3c33558f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -61,6 +61,7 @@ tacker.tacker.vnfm.drivers = tacker.tacker.mgmt.drivers = noop = tacker.vnfm.mgmt_drivers.noop:VnfMgmtNoop openwrt = tacker.vnfm.mgmt_drivers.openwrt.openwrt:VnfMgmtOpenWRT + vnflcm_noop = tacker.vnfm.mgmt_drivers.vnflcm_noop:VnflcmMgmtNoop tacker.tacker.monitor.drivers = ping = tacker.vnfm.monitor_drivers.ping.ping:VNFMonitorPing http_ping = tacker.vnfm.monitor_drivers.http_ping.http_ping:VNFMonitorHTTPPing @@ -92,6 +93,7 @@ oslo.config.opts = tacker.vnfm.monitor_drivers.ping.ping = tacker.vnfm.monitor_drivers.ping.ping:config_opts tacker.vnfm.monitor_drivers.ceilometer.ceilometer = tacker.vnfm.monitor_drivers.ceilometer.ceilometer:config_opts tacker.vnfm.monitor_drivers.zabbix.zabbix = tacker.vnfm.monitor_drivers.zabbix.zabbix:config_opts + tacker.vnflcm.vnflcm_drivers = tacker.vnflcm.vnflcm_drivers:config_opts tacker.alarm_receiver = tacker.alarm_receiver:config_opts tacker.plugins.fenix = tacker.plugins.fenix:config_opts diff --git a/tacker/common/exceptions.py b/tacker/common/exceptions.py index b938375e5..fa56a1609 100644 --- a/tacker/common/exceptions.py +++ b/tacker/common/exceptions.py @@ -376,3 +376,33 @@ class DBAccessError(TackerException): class SeeOther(TackerException): code = 303 + + +class MgmtDriverHashMatchFailure(TackerException): + message = _('The hash verification of VNF Package MgmtDriver ' + 'and Tacker MgmtDriver does not match.') + + +class MgmtDriverInconsistent(TackerException): + message = _('The %(MgmtDriver)s specified in the VNFD is inconsistent ' + 'with the MgmtDriver in the configuration file.') + + +class MgmtDriverNotFound(TackerException): + message = _('The %(param)s in the additionalParams does not exist.') + + +class MgmtDriverParamInvalid(TackerException): + message = _('The %(param)s in the additionalParams is invalid.') + + +class MgmtDriverRemoteCommandError(TackerException): + message = _('Failed to execute remote command.') + + +class MgmtDriverRemoteCommandTimeOut(TackerException): + message = _('The execution of the remote command timed out.') + + +class MgmtDriverOtherError(TackerException): + message = _('An error occurred in MgmtDriver: %(error_message)s.') diff --git a/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_df_simple.yaml new file mode 100644 index 000000000..abc175541 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_df_simple.yaml @@ -0,0 +1,269 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Simple deployment flavour for Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - helloworld3_types.yaml + +topology_template: + inputs: + id: + type: string + vendor: + type: string + version: + type: version + 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: [ CP1, virtual_link ] + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_description: A simple flavour + interfaces: + Vnflcm: + # supporting only 'instantiate', 'terminate', 'modify' + # not supporting LCM script, supporting only default LCM + instantiate: [] + instantiate_start: + implementation: vnflcm_noop + instantiate_end: + implementation: vnflcm_noop + terminate: [] + terminate_start: + implementation: vnflcm_noop + terminate_end: + implementation: vnflcm_noop + artifacts: + vnflcm_noop: + description: Management driver instantiate + type: tosca.artifacts.Implementation.Python + file: Drivers/vnflcm_noop.py + + 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: 1 + sw_image_data: + name: Software of VDU1 + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d + container_format: bare + disk_format: qcow2 + min_disk: 1 GB + size: 1 GB + + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 1 GB + + VDU2: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU2 + description: VDU2 compute node + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 3 + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 1 GB + requirements: + - virtual_storage: VirtualStorage + + VirtualStorage: + type: tosca.nodes.nfv.Vdu.VirtualBlockStorage + properties: + virtual_block_storage_data: + size_of_storage: 30 GB + rdma_enabled: true + sw_image_data: + name: VrtualStorage + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d + container_format: bare + disk_format: qcow2 + min_disk: 2 GB + min_ram: 8192 MB + size: 2 GB + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + CP1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 0 + vnic_type: direct-physical + requirements: + - virtual_binding: VDU1 + #- virtual_link: # the target node is determined in the NSD + + CP2: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 1 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL2 + + CP3: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 2 + requirements: + - virtual_binding: VDU2 + - virtual_link: internalVL2 + + internalVL2: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: Internal Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 11.11.0.0/24 + + policies: + - scaling_aspects: + type: tosca.policies.nfv.ScalingAspects + properties: + aspects: + worker_instance: + name: worker_instance_aspect + description: worker_instance scaling aspect + max_scale_level: 2 + step_deltas: + - delta_1 + + - 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: worker_instance + deltas: + delta_1: + number_of_instances: 1 + targets: [ VDU2 ] + + - instantiation_levels: + type: tosca.policies.nfv.InstantiationLevels + properties: + levels: + instantiation_level_1: + description: Smallest size + scale_info: + worker_instance: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + worker_instance: + scale_level: 2 + default_level: instantiation_level_1 + + - 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_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 1 + instantiation_level_2: + number_of_instances: 1 + targets: [ VDU2 ] + + - internalVL2_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL2 ] diff --git a/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_types.yaml b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_types.yaml new file mode 100644 index 000000000..abcee8a8a --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Definitions/helloworld3_types.yaml @@ -0,0 +1,65 @@ +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: + id: + type: string + description: ID of this VNF + default: vnf_id + vendor: + type: string + description: name of the vendor who generate this VNF + default: vendor + version: + type: version + description: version of the software for this VNF + default: 1.0 + descriptor_id: + type: string + constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ] + default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000 + 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/refactor_mgmt_driver1/Drivers/vnflcm_noop.py b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Drivers/vnflcm_noop.py new file mode 100644 index 000000000..6e520aa9f --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver1/Drivers/vnflcm_noop.py @@ -0,0 +1,68 @@ +# Copyright (C) 2020 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. + +from tacker.common import log +from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver + + +class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): + def get_type(self): + return 'vnflcm_noop' + + def get_name(self): + return 'vnflcm_noop' + + def get_description(self): + return 'Tacker VNFMgmt VnflcmNoop Driver' + + @log.log + def instantiate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def instantiate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def terminate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def terminate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def scale_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def scale_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def heal_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def heal_end(self, context, vnf_instance, + additional_params, **kwargs): + pass diff --git a/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Definitions/helloworld3_df_simple.yaml new file mode 100644 index 000000000..4f8c176da --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Definitions/helloworld3_df_simple.yaml @@ -0,0 +1,269 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Simple deployment flavour for Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - helloworld3_types.yaml + +topology_template: + inputs: + id: + type: string + vendor: + type: string + version: + type: version + 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: [ CP1, virtual_link ] + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_description: A simple flavour + interfaces: + Vnflcm: + # supporting only 'instantiate', 'terminate', 'modify' + # not supporting LCM script, supporting only default LCM + instantiate: [] + instantiate_start: + implementation: vnflcm_noop_false + instantiate_end: + implementation: vnflcm_noop_false + terminate: [] + terminate_start: + implementation: vnflcm_noop_false + terminate_end: + implementation: vnflcm_noop_false + artifacts: + vnflcm_noop: + description: Management driver instantiate + type: tosca.artifacts.Implementation.Python + file: Drivers/vnflcm_noop_false.py + + 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: 1 + sw_image_data: + name: Software of VDU1 + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d + container_format: bare + disk_format: qcow2 + min_disk: 1 GB + size: 1 GB + + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 1 GB + + VDU2: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU2 + description: VDU2 compute node + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 3 + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 1 GB + requirements: + - virtual_storage: VirtualStorage + + VirtualStorage: + type: tosca.nodes.nfv.Vdu.VirtualBlockStorage + properties: + virtual_block_storage_data: + size_of_storage: 30 GB + rdma_enabled: true + sw_image_data: + name: VrtualStorage + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d + container_format: bare + disk_format: qcow2 + min_disk: 2 GB + min_ram: 8192 MB + size: 2 GB + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + CP1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 0 + vnic_type: direct-physical + requirements: + - virtual_binding: VDU1 + #- virtual_link: # the target node is determined in the NSD + + CP2: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 1 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL2 + + CP3: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 2 + requirements: + - virtual_binding: VDU2 + - virtual_link: internalVL2 + + internalVL2: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: Internal Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 11.11.0.0/24 + + policies: + - scaling_aspects: + type: tosca.policies.nfv.ScalingAspects + properties: + aspects: + worker_instance: + name: worker_instance_aspect + description: worker_instance scaling aspect + max_scale_level: 2 + step_deltas: + - delta_1 + + - 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: worker_instance + deltas: + delta_1: + number_of_instances: 1 + targets: [ VDU2 ] + + - instantiation_levels: + type: tosca.policies.nfv.InstantiationLevels + properties: + levels: + instantiation_level_1: + description: Smallest size + scale_info: + worker_instance: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + worker_instance: + scale_level: 2 + default_level: instantiation_level_1 + + - 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_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 1 + instantiation_level_2: + number_of_instances: 1 + targets: [ VDU2 ] + + - internalVL2_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL2 ] diff --git a/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Drivers/vnflcm_noop_false.py b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Drivers/vnflcm_noop_false.py new file mode 100644 index 000000000..f6b1fd646 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver2/Drivers/vnflcm_noop_false.py @@ -0,0 +1,88 @@ +# Copyright (C) 2020 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. + +from oslo_log import log as logging + +from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver + + +LOG = logging.getLogger(__name__) + + +class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): + def get_type(self): + return 'vnflcm_noop' + + def get_name(self): + return 'vnflcm_noop' + + def get_description(self): + return 'Tacker VNFMgmt VnflcmNoop Driver' + + def instantiate_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('instantiate_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def instantiate_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('instantiate_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def terminate_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('terminate_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def terminate_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('terminate_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def scale_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('scale_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def scale_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('scale_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def heal_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('heal_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def heal_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('heal_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass diff --git a/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Definitions/helloworld3_df_simple.yaml new file mode 100644 index 000000000..abc175541 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Definitions/helloworld3_df_simple.yaml @@ -0,0 +1,269 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Simple deployment flavour for Sample VNF + +imports: + - etsi_nfv_sol001_common_types.yaml + - etsi_nfv_sol001_vnfd_types.yaml + - helloworld3_types.yaml + +topology_template: + inputs: + id: + type: string + vendor: + type: string + version: + type: version + 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: [ CP1, virtual_link ] + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_description: A simple flavour + interfaces: + Vnflcm: + # supporting only 'instantiate', 'terminate', 'modify' + # not supporting LCM script, supporting only default LCM + instantiate: [] + instantiate_start: + implementation: vnflcm_noop + instantiate_end: + implementation: vnflcm_noop + terminate: [] + terminate_start: + implementation: vnflcm_noop + terminate_end: + implementation: vnflcm_noop + artifacts: + vnflcm_noop: + description: Management driver instantiate + type: tosca.artifacts.Implementation.Python + file: Drivers/vnflcm_noop.py + + 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: 1 + sw_image_data: + name: Software of VDU1 + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d + container_format: bare + disk_format: qcow2 + min_disk: 1 GB + size: 1 GB + + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 1 GB + + VDU2: + type: tosca.nodes.nfv.Vdu.Compute + properties: + name: VDU2 + description: VDU2 compute node + vdu_profile: + min_number_of_instances: 1 + max_number_of_instances: 3 + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 1 GB + requirements: + - virtual_storage: VirtualStorage + + VirtualStorage: + type: tosca.nodes.nfv.Vdu.VirtualBlockStorage + properties: + virtual_block_storage_data: + size_of_storage: 30 GB + rdma_enabled: true + sw_image_data: + name: VrtualStorage + version: '0.4.0' + checksum: + algorithm: sha-256 + hash: b9c3036539fd7a5f87a1bf38eb05fdde8b556a1a7e664dbeda90ed3cd74b4f9d + container_format: bare + disk_format: qcow2 + min_disk: 2 GB + min_ram: 8192 MB + size: 2 GB + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + CP1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 0 + vnic_type: direct-physical + requirements: + - virtual_binding: VDU1 + #- virtual_link: # the target node is determined in the NSD + + CP2: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 1 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL2 + + CP3: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 2 + requirements: + - virtual_binding: VDU2 + - virtual_link: internalVL2 + + internalVL2: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: Internal Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 11.11.0.0/24 + + policies: + - scaling_aspects: + type: tosca.policies.nfv.ScalingAspects + properties: + aspects: + worker_instance: + name: worker_instance_aspect + description: worker_instance scaling aspect + max_scale_level: 2 + step_deltas: + - delta_1 + + - 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: worker_instance + deltas: + delta_1: + number_of_instances: 1 + targets: [ VDU2 ] + + - instantiation_levels: + type: tosca.policies.nfv.InstantiationLevels + properties: + levels: + instantiation_level_1: + description: Smallest size + scale_info: + worker_instance: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + worker_instance: + scale_level: 2 + default_level: instantiation_level_1 + + - 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_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 1 + instantiation_level_2: + number_of_instances: 1 + targets: [ VDU2 ] + + - internalVL2_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL2 ] diff --git a/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Drivers/vnflcm_noop.py b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Drivers/vnflcm_noop.py new file mode 100644 index 000000000..d3766064a --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/refactor_mgmt_driver3/Drivers/vnflcm_noop.py @@ -0,0 +1,81 @@ +# Copyright (C) 2020 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. + +from oslo_log import log as logging + +from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver + + +LOG = logging.getLogger(__name__) + + +class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): + + # instantiate start + def instantiate_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('instantiate_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def instantiate_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('instantiate_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def terminate_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('terminate_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def terminate_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('terminate_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def scale_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('scale_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def scale_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('scale_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def heal_start(self, vnf_instance, additional_params, **kwargs): + LOG.debug('heal_start %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass + + def heal_end(self, vnf_instance, additional_params, **kwargs): + LOG.debug('heal_end %(vnf_instance)s ' + '%(additional_params)s %(kwargs)s', + {'vnf_instance': vnf_instance, + 'additional_params': additional_params, 'kwargs': kwargs}) + pass diff --git a/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_df_simple.yaml b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_df_simple.yaml new file mode 100644 index 000000000..2547c3ac8 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_df_simple.yaml @@ -0,0 +1,206 @@ +tosca_definitions_version: tosca_simple_yaml_1_2 + +description: Simple deployment flavour for 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: [ CP1, virtual_link ] + + node_templates: + VNF: + type: company.provider.VNF + properties: + flavour_description: A simple flavour + interfaces: + Vnflcm: + instantiate: [] + terminate: [] + terminate_start: [] + terminate_end: [] + instantiate_start: + implementation: vnflcm_noop + instantiate_end: + implementation: vnflcm_noop + artifacts: + vnflcm_noop: + description: Management driver for vnflcm_noop + type: tosca.artifacts.Implementation.Python + file: Scripts/vnflcm_noop.py + + 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 + + capabilities: + virtual_compute: + properties: + virtual_memory: + virtual_mem_size: 512 MB + virtual_cpu: + num_virtual_cpu: 1 + virtual_local_storage: + - size_of_storage: 3 GB + requirements: + - virtual_storage: VirtualStorage + + VirtualStorage: + type: tosca.nodes.nfv.Vdu.VirtualBlockStorage + properties: + virtual_block_storage_data: + size_of_storage: 3 GB + rdma_enabled: true + sw_image_data: + name: VirtualStorage + version: '0.4.0' + checksum: + algorithm: sha-512 + hash: 6513f21e44aa3da349f248188a44bc304a3653a04122d8fb4535423c8e1d14cd6a153f735bb0982e2161b5b5186106570c17a9e58b64dd39390617cd5a350f78 + container_format: bare + disk_format: qcow2 + min_disk: 2 GB + min_ram: 256 MB + size: 1 GB + artifacts: + sw_image: + type: tosca.artifacts.nfv.SwImage + file: ../Files/images/cirros-0.4.0-x86_64-disk.img + + CP1: + type: tosca.nodes.nfv.VduCp + properties: + layer_protocols: [ ipv4 ] + order: 2 + requirements: + - virtual_binding: VDU1 + - virtual_link: internalVL1 + + internalVL1: + type: tosca.nodes.nfv.VnfVirtualLink + properties: + connectivity_type: + layer_protocols: [ ipv4 ] + description: Internal Virtual link in the VNF + vl_profile: + max_bitrate_requirements: + root: 1048576 + leaf: 1048576 + min_bitrate_requirements: + root: 1048576 + leaf: 1048576 + virtual_link_protocol_data: + - associated_layer_protocol: ipv4 + l3_protocol_data: + ip_version: ipv4 + cidr: 33.33.0.0/24 + + policies: + - scaling_aspects: + type: tosca.policies.nfv.ScalingAspects + properties: + aspects: + worker_instance: + name: worker_instance_aspect + description: worker_instance scaling aspect + max_scale_level: 2 + step_deltas: + - delta_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: worker_instance + 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: + worker_instance: + scale_level: 0 + instantiation_level_2: + description: Largest size + scale_info: + worker_instance: + scale_level: 2 + default_level: instantiation_level_1 + + - 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 ] + + - VDU1_instantiation_levels: + type: tosca.policies.nfv.VduInstantiationLevels + properties: + levels: + instantiation_level_1: + number_of_instances: 1 + instantiation_level_2: + number_of_instances: 1 + targets: [ VDU1 ] + + - internalVL1_instantiation_levels: + type: tosca.policies.nfv.VirtualLinkInstantiationLevels + properties: + levels: + instantiation_level_1: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + instantiation_level_2: + bitrate_requirements: + root: 1048576 + leaf: 1048576 + targets: [ internalVL1 ] diff --git a/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_top.vnfd.yaml b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_top.vnfd.yaml new file mode 100644 index 000000000..18d4fe64b --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_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 + - 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-4840d7000000 + 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_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_types.yaml b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_types.yaml new file mode 100644 index 000000000..16a4ff5bc --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Definitions/helloworld3_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-4840d7000000 ] ] + default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000 + 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: "falvour" + 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_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 new file mode 100644 index 000000000..6e520aa9f --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/Scripts/vnflcm_noop.py @@ -0,0 +1,68 @@ +# Copyright (C) 2020 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. + +from tacker.common import log +from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver + + +class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): + def get_type(self): + return 'vnflcm_noop' + + def get_name(self): + return 'vnflcm_noop' + + def get_description(self): + return 'Tacker VNFMgmt VnflcmNoop Driver' + + @log.log + def instantiate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def instantiate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def terminate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def terminate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def scale_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def scale_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def heal_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def heal_end(self, context, vnf_instance, + additional_params, **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 new file mode 100644 index 000000000..b0413c5d7 --- /dev/null +++ b/tacker/tests/etc/samples/etsi/nfv/test_inst_terminate_vnf_with_vnflcmnoop/TOSCA-Metadata/TOSCA.meta @@ -0,0 +1,12 @@ +TOSCA-Meta-File-Version: 1.0 +Created-by: dummy_user +CSAR-Version: 1.1 +Entry-Definitions: Definitions/helloworld3_top.vnfd.yaml + +Name: Files/images/cirros-0.4.0-x86_64-disk.img +Content-type: application/x-iso9066-image + +Name: Scripts/vnflcm_noop.py +Content-Type: text/x-python +Algorithm: SHA-256 +Hash: ffea638bfdbde3fb01f191bbe75b031859b18d663b127100eb72b19eecd7ed51 diff --git a/tacker/tests/functional/sol/vnflcm/test_vnflcm_noop.py b/tacker/tests/functional/sol/vnflcm/test_vnflcm_noop.py new file mode 100644 index 000000000..0eb034d89 --- /dev/null +++ b/tacker/tests/functional/sol/vnflcm/test_vnflcm_noop.py @@ -0,0 +1,294 @@ +# Copyright (C) 2020 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 os +import time + +from oslo_serialization import jsonutils +from oslo_utils import uuidutils +from sqlalchemy import desc +from sqlalchemy.orm import joinedload + +from tacker.common import exceptions +from tacker import context as t_context +from tacker.db import api as db_api +from tacker.db.db_sqlalchemy import api +from tacker.db.db_sqlalchemy import models +from tacker.objects import fields +from tacker.tests.functional import base +from tacker.tests import utils + +VNF_PACKAGE_UPLOAD_TIMEOUT = 300 +VNF_INSTANTIATE_TIMEOUT = 600 +VNF_TERMINATE_TIMEOUT = 600 +VNF_HEAL_TIMEOUT = 600 +RETRY_WAIT_TIME = 5 + + +def _create_and_upload_vnf_package(tacker_client, csar_package_name, + user_defined_data): + # create vnf package + body = jsonutils.dumps({"userDefinedData": user_defined_data}) + resp, vnf_package = tacker_client.do_request( + '/vnfpkgm/v1/vnf_packages', "POST", body=body) + + # upload vnf package + csar_package_path = "../../../etc/samples/etsi/nfv/%s" % 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, uniqueid = utils.create_csar_with_unique_vnfd_id(file_path) + + with open(file_path, 'rb') as file_object: + resp, resp_body = tacker_client.do_request( + '/vnfpkgm/v1/vnf_packages/{id}/package_content'.format( + id=vnf_package['id']), + "PUT", body=file_object, content_type='application/zip') + + # wait for onboard + timeout = VNF_PACKAGE_UPLOAD_TIMEOUT + start_time = int(time.time()) + show_url = os.path.join('/vnfpkgm/v1/vnf_packages', vnf_package['id']) + vnfd_id = None + while True: + resp, body = tacker_client.do_request(show_url, "GET") + if body['onboardingState'] == "ONBOARDED": + vnfd_id = body['vnfdId'] + break + + if ((int(time.time()) - start_time) > timeout): + raise Exception("Failed to onboard vnf package") + + time.sleep(1) + + # remove temporarily created CSAR file + os.remove(file_path) + return vnf_package['id'], vnfd_id + + +class VnfLcmTest(base.BaseTackerTest): + + @classmethod + def setUpClass(cls): + cls.tacker_client = base.BaseTackerTest.tacker_http_client() + + cls.vnf_package, cls.vnfd_id = \ + _create_and_upload_vnf_package( + cls.tacker_client, "test_inst_terminate_vnf_with_vnflcmnoop", + {"key": "file_functional"}) + + super(VnfLcmTest, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + # Update vnf package operational state to DISABLED + update_req_body = jsonutils.dumps({ + "operationalState": "DISABLED"}) + base_path = "/vnfpkgm/v1/vnf_packages" + for package_id in [cls.vnf_package]: + resp, resp_body = cls.tacker_client.do_request( + '{base_path}/{id}'.format(id=package_id, + base_path=base_path), + "PATCH", content_type='application/json', + body=update_req_body) + + # Delete vnf package + url = '/vnfpkgm/v1/vnf_packages/%s' % package_id + cls.tacker_client.do_request(url, "DELETE") + + super(VnfLcmTest, cls).tearDownClass() + + def setUp(self): + super(VnfLcmTest, self).setUp() + self.base_url = "/vnflcm/v1/vnf_instances" + self.context = t_context.get_admin_context() + + vim_list = self.client.list_vims() + if not vim_list: + self.skipTest("Vims are not configured") + + vim_id = 'VIM0' + vim = self.get_vim(vim_list, vim_id) + if not vim: + self.skipTest("Openstack VIM '%s' is missing" % vim_id) + self.vim_id = vim['id'] + + def _instantiate_vnf_instance_request( + self, flavour_id, vim_id=None, additional_param=None): + request_body = {"flavourId": flavour_id} + + if vim_id: + request_body["vimConnectionInfo"] = [ + {"id": uuidutils.generate_uuid(), + "vimId": vim_id, + "vimType": "openstack"}] + + if additional_param: + request_body["additionalParams"] = additional_param + + return request_body + + def _create_vnf_instance(self, vnfd_id, vnf_instance_name=None, + vnf_instance_description=None): + request_body = {'vnfdId': vnfd_id} + if vnf_instance_name: + request_body['vnfInstanceName'] = vnf_instance_name + + if vnf_instance_description: + request_body['vnfInstanceDescription'] = vnf_instance_description + + resp, response_body = self.http_client.do_request( + self.base_url, "POST", body=jsonutils.dumps(request_body)) + return resp, response_body + + def _delete_wait_vnf_instance(self, id): + timeout = VNF_TERMINATE_TIMEOUT + url = os.path.join(self.base_url, id) + start_time = int(time.time()) + while True: + resp, body = self.http_client.do_request(url, "DELETE") + if 204 == resp.status_code: + break + + if ((int(time.time()) - start_time) > timeout): + error = "Failed to delete vnf instance %s" + self.fail(error % id) + + time.sleep(RETRY_WAIT_TIME) + + def _delete_vnf_instance(self, id): + self._delete_wait_vnf_instance(id) + + # verify vnf instance is deleted + url = os.path.join(self.base_url, id) + resp, body = self.http_client.do_request(url, "GET") + self.assertEqual(404, resp.status_code) + + def _show_vnf_instance(self, id, expected_result=None): + show_url = os.path.join(self.base_url, id) + resp, vnf_instance = self.http_client.do_request(show_url, "GET") + self.assertEqual(200, resp.status_code) + + if expected_result: + self.assertDictSupersetOf(expected_result, vnf_instance) + + return vnf_instance + + def _vnf_instance_wait( + self, id, + instantiation_state=fields.VnfInstanceState.INSTANTIATED, + timeout=VNF_INSTANTIATE_TIMEOUT): + show_url = os.path.join(self.base_url, id) + start_time = int(time.time()) + while True: + resp, body = self.http_client.do_request(show_url, "GET") + if body['instantiationState'] == instantiation_state: + break + + if ((int(time.time()) - start_time) > timeout): + error = ("Vnf instance %(id)s status is %(current)s, " + "expected status should be %(expected)s") + self.fail(error % {"id": id, + "current": body['instantiationState'], + "expected": instantiation_state}) + + time.sleep(RETRY_WAIT_TIME) + + def _instantiate_vnf_instance(self, id, request_body): + url = os.path.join(self.base_url, id, "instantiate") + resp, body = self.http_client.do_request( + url, "POST", body=jsonutils.dumps(request_body)) + self.assertEqual(202, resp.status_code) + self._vnf_instance_wait(id) + + def _terminate_vnf_instance(self, id, request_body): + url = os.path.join(self.base_url, id, "terminate") + resp, body = self.http_client.do_request( + url, "POST", body=jsonutils.dumps(request_body)) + self.assertEqual(202, resp.status_code) + + timeout = request_body.get('gracefulTerminationTimeout') + start_time = int(time.time()) + + self._vnf_instance_wait( + id, instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED, + timeout=VNF_TERMINATE_TIMEOUT) + + # If gracefulTerminationTimeout is set, check whether vnf + # instantiation_state is set to NOT_INSTANTIATED after + # gracefulTerminationTimeout seconds. + if timeout and int(time.time()) - start_time < timeout: + self.fail("Vnf is terminated before graceful termination " + "timeout period") + + @db_api.context_manager.reader + def _vnf_notify_get_by_id(self, context, vnf_instance_id, + columns_to_join=None): + query = api.model_query( + context, models.VnfLcmOpOccs, + read_deleted="no", project_only=True).filter_by( + vnf_instance_id=vnf_instance_id).order_by( + desc("created_at")) + + if columns_to_join: + for column in columns_to_join: + query = query.options(joinedload(column)) + + result = query.first() + + if not result: + raise exceptions.VnfInstanceNotFound(id=vnf_instance_id) + + return result + + def test_instantiate_terminate_vnf_with_vnflcmnoop(self): + # create vnf instance + vnf_instance_name = "vnf_with_instantiation_level-%s" % \ + uuidutils.generate_uuid() + vnf_instance_description = "vnf with instantiation level 1" + resp, vnf_instance = self._create_vnf_instance( + self.vnfd_id, + vnf_instance_name=vnf_instance_name, + vnf_instance_description=vnf_instance_description) + self.assertIsNotNone(vnf_instance['id']) + self.assertEqual(201, resp.status_code) + # instantiate vnf instance + request_body = self._instantiate_vnf_instance_request( + "simple", vim_id=self.vim_id) + self._instantiate_vnf_instance(vnf_instance['id'], request_body) + time.sleep(20) + # show vnf instance + vnf_instance = self._show_vnf_instance(vnf_instance['id']) + self.assertEqual(vnf_instance['instantiationState'], 'INSTANTIATED') + vnflcm_op_occ_ins = self._vnf_notify_get_by_id( + self.context, vnf_instance['id'], columns_to_join=None) + self.assertEqual(vnflcm_op_occ_ins.operation_state, 'COMPLETED') + self.assertEqual(vnflcm_op_occ_ins.operation, 'INSTANTIATE') + time.sleep(20) + # terminate vnf instance + terminate_req_body = { + "terminationType": fields.VnfInstanceTerminationType.FORCEFUL, + } + self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body) + time.sleep(20) + vnflcm_op_occ_term = self._vnf_notify_get_by_id( + self.context, vnf_instance['id'], columns_to_join=None) + self.assertEqual(vnflcm_op_occ_term.operation_state, 'COMPLETED') + self.assertEqual(vnflcm_op_occ_term.operation, 'TERMINATE') + # delete vnf instance + self._delete_vnf_instance(vnf_instance['id']) diff --git a/tacker/tests/unit/vnflcm/fakes.py b/tacker/tests/unit/vnflcm/fakes.py index 05d80c2fe..9de51931e 100644 --- a/tacker/tests/unit/vnflcm/fakes.py +++ b/tacker/tests/unit/vnflcm/fakes.py @@ -39,6 +39,11 @@ import tacker.conf CONF = tacker.conf.CONF +def return_vnf_interfaces(): + vnf_interface = 'vnflcm_noop' + return vnf_interface + + def return_default_vim(): default_vim = { 'vim_auth': { diff --git a/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py b/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py new file mode 100644 index 000000000..6c04a68d2 --- /dev/null +++ b/tacker/tests/unit/vnflcm/test_load_vnf_interfaces.py @@ -0,0 +1,248 @@ +# Copyright (C) 2020 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 fixtures +import os +import shutil + +from oslo_config import cfg +from oslo_utils import uuidutils + +from tacker.common import exceptions +from tacker import context +from tacker.manager import TackerManager +from tacker import objects +from tacker.tests.unit.db import base as db_base +from tacker.tests.unit.nfvo.test_nfvo_plugin import FakeVNFMPlugin +from tacker.tests.unit.vnflcm import fakes +from tacker.tests import utils as test_utils +from tacker.tests import uuidsentinel +from tacker.vnflcm import vnflcm_driver +from tacker.vnflcm.vnflcm_driver import VnfLcmDriver +from unittest import mock + + +vnf_dict = { + 'id': uuidutils.generate_uuid(), + 'mgmt_ip_address': '{"VDU1": "a.b.c.d"}', + 'vim_id': '6261579e-d6f3-49ad-8bc3-a9cb974778ff', + 'instance_id': 'a737497c-761c-11e5-89c3-9cb6541d805d', + 'vnfd': { + 'attributes': { + 'heat_template': { + 'resources': { + 'VDU1': { + 'properties': { + 'networks': [{'port': {'get_resource': 'CP1'}}]} + } + } + } + } + } +} + + +class InfraDriverException(Exception): + pass + + +class FakeDriverManager(mock.Mock): + def __init__(self, fail_method_name=None, vnf_resource_count=1): + super(FakeDriverManager, self).__init__() + self.fail_method_name = fail_method_name + self.vnf_resource_count = vnf_resource_count + + def invoke(self, *args, **kwargs): + if 'pre_instantiation_vnf' in args: + vnf_resource_list = [fakes.return_vnf_resource() for index in + range(self.vnf_resource_count)] + return {'node_name': vnf_resource_list} + if 'instantiate_vnf' in args: + if self.fail_method_name and \ + self.fail_method_name == 'instantiate_vnf': + raise InfraDriverException("instantiate_vnf failed") + + instance_id = uuidsentinel.instance_id + vnfd_dict = kwargs.get('vnfd_dict') + vnfd_dict['instance_id'] = instance_id + return instance_id + if 'create_wait' in args: + if self.fail_method_name and \ + self.fail_method_name == 'create_wait': + raise InfraDriverException("create_wait failed") + elif 'post_vnf_instantiation' in args: + pass + if 'mgmt-drivers-custom' in args: + if self.fail_method_name and \ + self.fail_method_name == 'mgmt-drivers-custom': + raise InfraDriverException("mgmt-drivers-custom failed") + + +class FakeVimClient(mock.Mock): + pass + + +class FakeTackerManager(mock.MagicMock): + pass + + +class MgmtVnfLcmDriverTest(db_base.SqlTestCase): + + def setUp(self): + super(MgmtVnfLcmDriverTest, self).setUp() + self.addCleanup(mock.patch.stopall) + self.context = context.get_admin_context() + self._mock_vim_client() + self._stub_get_vim() + self.temp_dir = self.useFixture(fixtures.TempDir()).path + + def _mock_vnf_manager(self, fail_method_name=None, vnf_resource_count=1): + self._vnf_manager = mock.Mock(wraps=FakeDriverManager( + fail_method_name=fail_method_name, + vnf_resource_count=vnf_resource_count)) + self._vnf_manager.__contains__ = mock.Mock( + return_value=True) + fake_vnf_manager = mock.Mock() + fake_vnf_manager.return_value = self._vnf_manager + self._mock( + 'tacker.common.driver_manager.DriverManager', fake_vnf_manager) + + 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': '6261579e-d6f3-49ad-8bc3-a9cb974778ff', + 'vim_name': 'fake_vim', 'vim_auth': + {'auth_url': 'http://localhost/identity', 'password': + 'test_pw', 'username': 'test_user', 'project_name': + 'test_project'}, 'vim_type': 'openstack'} + self.vim_client.get_vim.return_value = vim_obj + + @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash') + @mock.patch.object(TackerManager, 'get_service_plugins', + return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(objects.VnfResource, 'create') + @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') + @mock.patch.object(objects.VnfInstance, "save") + def test_instantiate_vnf(self, mock_vnf_instance_save, + mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, + mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + vnf_package_vnfd = fakes.return_vnf_package_vnfd() + vnf_package_id = vnf_package_vnfd.package_uuid + mock_vnf_package_vnfd.return_value = vnf_package_vnfd + instantiate_vnf_req_dict = fakes.get_dummy_instantiate_vnf_request() + instantiate_vnf_req_obj = \ + objects.InstantiateVnfRequest.obj_from_primitive( + instantiate_vnf_req_dict, self.context) + vnf_instance_obj = fakes.return_vnf_instance() + + fake_csar = os.path.join(self.temp_dir, vnf_package_id) + cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir, + group='vnf_package') + test_utils.copy_csar_files(fake_csar, "refactor_mgmt_driver1") + self._mock_vnf_manager() + driver = vnflcm_driver.VnfLcmDriver() + vnf_dict = {"vnfd": {"attributes": {}}, "attributes": {}} + driver.instantiate_vnf(self.context, vnf_instance_obj, vnf_dict, + instantiate_vnf_req_obj) + + self.assertEqual(1, mock_vnf_instance_save.call_count) + self.assertEqual(5, self._vnf_manager.invoke.call_count) + shutil.rmtree(fake_csar) + + @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash') + @mock.patch.object(TackerManager, 'get_service_plugins', + return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(objects.VnfResource, 'create') + @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') + @mock.patch.object(objects.VnfInstance, "save") + def test_instantiate_vnf_py_name_false( + self, mock_vnf_instance_save, mock_vnf_package_vnfd, + mock_create, mock_get_service_plugins, mock_init_hash, + mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + vnf_package_vnfd = fakes.return_vnf_package_vnfd() + vnf_package_id = vnf_package_vnfd.package_uuid + mock_vnf_package_vnfd.return_value = vnf_package_vnfd + instantiate_vnf_req_dict = fakes.get_dummy_instantiate_vnf_request() + instantiate_vnf_req_obj = \ + objects.InstantiateVnfRequest.obj_from_primitive( + instantiate_vnf_req_dict, self.context) + vnf_instance_obj = fakes.return_vnf_instance() + + fake_csar = os.path.join(self.temp_dir, vnf_package_id) + cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir, + group='vnf_package') + test_utils.copy_csar_files(fake_csar, "refactor_mgmt_driver2") + self._mock_vnf_manager() + driver = vnflcm_driver.VnfLcmDriver() + vnf_dict = {"vnfd": {"attributes": {}}, "attributes": {}} + self.assertRaises(exceptions.MgmtDriverInconsistent, + driver.instantiate_vnf, self.context, + vnf_instance_obj, vnf_dict, + instantiate_vnf_req_obj) + shutil.rmtree(fake_csar) + + @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, '_init_mgmt_driver_hash') + @mock.patch.object(TackerManager, 'get_service_plugins', + return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(objects.VnfResource, 'create') + @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') + @mock.patch.object(objects.VnfInstance, "save") + def test_instantiate_vnf_py_hash_false( + self, mock_vnf_instance_save, mock_vnf_package_vnfd, + mock_create, mock_get_service_plugins, mock_init_hash, + mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + vnf_package_vnfd = fakes.return_vnf_package_vnfd() + vnf_package_id = vnf_package_vnfd.package_uuid + mock_vnf_package_vnfd.return_value = vnf_package_vnfd + instantiate_vnf_req_dict = fakes.get_dummy_instantiate_vnf_request() + instantiate_vnf_req_obj = \ + objects.InstantiateVnfRequest.obj_from_primitive( + instantiate_vnf_req_dict, self.context) + vnf_instance_obj = fakes.return_vnf_instance() + + fake_csar = os.path.join(self.temp_dir, vnf_package_id) + cfg.CONF.set_override('vnf_package_csar_path', self.temp_dir, + group='vnf_package') + test_utils.copy_csar_files(fake_csar, "refactor_mgmt_driver3") + self._mock_vnf_manager() + driver = vnflcm_driver.VnfLcmDriver() + vnf_dict = {"vnfd": {"attributes": {}}, "attributes": {}} + self.assertRaises(exceptions.MgmtDriverHashMatchFailure, + driver.instantiate_vnf, self.context, + vnf_instance_obj, vnf_dict, + instantiate_vnf_req_obj) + shutil.rmtree(fake_csar) diff --git a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py index 7ed5a0ee1..d21c26036 100644 --- a/tacker/tests/unit/vnflcm/test_vnflcm_driver.py +++ b/tacker/tests/unit/vnflcm/test_vnflcm_driver.py @@ -40,6 +40,7 @@ from tacker.tests.unit.vnflcm import fakes from tacker.tests import utils as test_utils from tacker.tests import uuidsentinel from tacker.vnflcm import vnflcm_driver +from tacker.vnflcm.vnflcm_driver import VnfLcmDriver from tacker.vnfm.infra_drivers.openstack import heat_client from tacker.vnfm.infra_drivers.openstack import openstack as opn from tacker.vnfm import plugin @@ -181,15 +182,25 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.vim_client.get_vim.return_value = vim_obj @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") - def test_instantiate_vnf(self, mock_vnf_instance_save, - mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, - mock_final_vnf_dict): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_instantiate_vnf( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -210,18 +221,29 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(5, self._vnf_manager.invoke.call_count) shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_instantiate_vnf_with_ext_virtual_links( - self, mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, mock_final_vnf_dict): + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -244,18 +266,29 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(5, self._vnf_manager.invoke.call_count) shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_instantiate_vnf_vim_connection_info( - self, mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, mock_final_vnf_dict): + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -278,18 +311,29 @@ class TestVnflcmDriver(db_base.SqlTestCase): instantiate_vnf_req_obj) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(5, self._vnf_manager.invoke.call_count) shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_instantiate_vnf_infra_fails_to_instantiate( - self, mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, mock_final_vnf_dict): + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -319,20 +363,31 @@ class TestVnflcmDriver(db_base.SqlTestCase): vnf_instance_obj.instantiation_state) # 2->1 reason: rollback_vnf_instantiated_resources deleted self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(2, self._vnf_manager.invoke.call_count) + self.assertEqual(3, self._vnf_manager.invoke.call_count) mock_final_vnf_dict.assert_called_once() shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_instantiate_vnf_infra_fails_to_wait_after_instantiate( - self, mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, mock_final_vnf_dict): + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create, + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -369,19 +424,31 @@ class TestVnflcmDriver(db_base.SqlTestCase): # 3->1 reason: rollback_vnf_instantiated_resources deleted self.assertEqual(1, mock_vnf_instance_save.call_count) # 5->3 reason: rollback_vnf_instantiated_resources deleted - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(4, self._vnf_manager.invoke.call_count) shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") - def test_instantiate_vnf_with_short_notation(self, mock_vnf_instance_save, - mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, mock_final_vnf_dict): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_instantiate_vnf_with_short_notation( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, + mock_create, mock_get_service_plugins, mock_init_hash, + mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -408,14 +475,26 @@ class TestVnflcmDriver(db_base.SqlTestCase): shutil.rmtree(fake_csar) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfResource, 'create') @mock.patch.object(objects.VnfPackageVnfd, 'get_by_id') @mock.patch.object(objects.VnfInstance, "save") - def test_instantiate_vnf_with_single_vnfd(self, mock_vnf_instance_save, - mock_vnf_package_vnfd, mock_create, - mock_get_service_plugins, mock_final_vnf_dict): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_instantiate_vnf_with_single_vnfd( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_vnf_instance_save, mock_vnf_package_vnfd, + mock_create, mock_get_service_plugins, mock_init_hash, + mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -441,12 +520,25 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id") @mock.patch.object(objects.VnfResource, "destroy") - def test_terminate_vnf(self, mock_resource_destroy, mock_resource_list, - mock_vim, mock_vnf_instance_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_terminate_vnf( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_resource_destroy, mock_resource_list, + mock_vim, mock_vnf_instance_save, mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) vnf_instance.instantiated_vnf_info.instance_id =\ @@ -461,17 +553,29 @@ class TestVnflcmDriver(db_base.SqlTestCase): driver.terminate_vnf(self.context, vnf_instance, terminate_vnf_req) self.assertEqual(2, mock_vnf_instance_save.call_count) self.assertEqual(1, mock_resource_destroy.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(5, 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, "save") @mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id") @mock.patch.object(objects.VnfResource, "destroy") - def test_terminate_vnf_graceful_no_timeout(self, mock_resource_destroy, + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_terminate_vnf_graceful_no_timeout( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_resource_destroy, mock_resource_list, mock_vim, mock_vnf_instance_save, - mock_get_service_plugins): + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) vnf_instance.instantiated_vnf_info.instance_id =\ @@ -489,10 +593,21 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch.object(vim_client.VimClient, "get_vim") - def test_terminate_vnf_delete_instance_failed(self, mock_vim, - mock_vnf_instance_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_terminate_vnf_delete_instance_failed( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_vim, + mock_vnf_instance_save, mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) vnf_instance.instantiated_vnf_info.instance_id =\ @@ -507,14 +622,26 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.context, vnf_instance, terminate_vnf_req) self.assertEqual("delete failed", str(error)) self.assertEqual(1, mock_vnf_instance_save.call_count) - self.assertEqual(1, self._vnf_manager.invoke.call_count) + 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, "save") @mock.patch.object(vim_client.VimClient, "get_vim") - def test_terminate_vnf_delete_wait_instance_failed(self, mock_vim, - mock_vnf_instance_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_terminate_vnf_delete_wait_instance_failed( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_vim, + mock_vnf_instance_save, mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) vnf_instance.instantiated_vnf_info.instance_id =\ @@ -528,15 +655,28 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.context, vnf_instance, terminate_vnf_req) self.assertEqual("delete_wait failed", str(error)) self.assertEqual(2, mock_vnf_instance_save.call_count) - self.assertEqual(2, self._vnf_manager.invoke.call_count) + self.assertEqual(3, 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, "save") @mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id") - def test_terminate_vnf_delete_vnf_resource_failed(self, mock_resource_list, - mock_vim, mock_vnf_instance_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_terminate_vnf_delete_vnf_resource_failed( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_resource_list, + mock_vim, mock_vnf_instance_save, mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) vnf_instance.instantiated_vnf_info.instance_id =\ @@ -551,9 +691,11 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.context, vnf_instance, terminate_vnf_req) self.assertEqual("delete_vnf_resource failed", str(error)) self.assertEqual(2, mock_vnf_instance_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(4, self._vnf_manager.invoke.call_count) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @@ -564,11 +706,20 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id") @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_without_vnfc_instance(self, mock_log, mock_save, + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_without_vnfc_instance( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_log, mock_save, mock_vnf_resource_list, mock_resource_destroy, mock_resource_create, mock_vim, mock_vnf_package_vnfd, mock_make_final_vnf_dict, mock_get_service_plugins, - mock_final_vnf_dict): + mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -611,7 +762,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): self.assertEqual(1, mock_resource_create.call_count) # Invoke will be called 7 times, 3 for deleting the vnf # resources and 4 during instantiation. - self.assertEqual(7, self._vnf_manager.invoke.call_count) + self.assertEqual(9, self._vnf_manager.invoke.call_count) expected_msg = ("Request received for healing vnf '%s' " "is completed successfully") mock_log.info.assert_called_with(expected_msg, @@ -621,10 +772,21 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_without_vnfc_instance_infra_delete_fail(self, mock_log, - mock_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_without_vnfc_instance_infra_delete_fail( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_log, + mock_save, mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() # Heal as per SOL003 i.e. without vnfcInstanceId heal_vnf_req = objects.HealVnfRequest() @@ -640,7 +802,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): driver.heal_vnf, self.context, vnf_instance, vnf_dict, heal_vnf_req) self.assertEqual(1, mock_save.call_count) - self.assertEqual(1, self._vnf_manager.invoke.call_count) + self.assertEqual(2, self._vnf_manager.invoke.call_count) self.assertEqual(fields.VnfInstanceTaskState.ERROR, vnf_instance.task_state) expected_msg = ('Failed to delete vnf resources for vnf instance %s ' @@ -649,6 +811,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_log.error.assert_called_with(expected_msg % vnf_instance.id) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch('tacker.vnflcm.utils._make_final_vnf_dict') @@ -659,11 +823,20 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id") @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_without_vnfc_instance_infra_instantiate_vnf_fail(self, + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_without_vnfc_instance_infra_instantiate_vnf_fail( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_log, mock_save, mock_vnf_resource_list, mock_resource_destroy, mock_resource_create, mock_vim, mock_vnf_package_vnfd, mock_make_final_vnf_dict, - mock_get_service_plugins, mock_final_vnf_dict): + mock_get_service_plugins, mock_init_hash, mock_final_vnf_dict): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_package_vnfd = fakes.return_vnf_package_vnfd() vnf_package_id = vnf_package_vnfd.package_uuid mock_vnf_package_vnfd.return_value = vnf_package_vnfd @@ -696,7 +869,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): # instantiation. self.assertEqual(1, mock_resource_create.call_count) - self.assertEqual(5, self._vnf_manager.invoke.call_count) + self.assertEqual(6, self._vnf_manager.invoke.call_count) self.assertEqual(fields.VnfInstanceTaskState.ERROR, vnf_instance.task_state) expected_msg = ('Failed to instantiate vnf instance %s ' @@ -709,10 +882,21 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_with_vnfc_instance(self, mock_log, mock_save, - mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_with_vnfc_instance( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_log, mock_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[ uuidsentinel.vnfc_instance_id_1]) @@ -724,7 +908,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): driver = vnflcm_driver.VnfLcmDriver() driver.heal_vnf(self.context, vnf_instance, mock.ANY, heal_vnf_req) self.assertEqual(1, mock_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(5, self._vnf_manager.invoke.call_count) self.assertEqual(None, vnf_instance.task_state) expected_msg = ("Request received for healing vnf '%s' " @@ -734,24 +918,34 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_with_infra_heal_vnf_fail(self, mock_log, mock_save, - mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_with_infra_heal_vnf_fail( + self, mock_vnf_interfaces, mock_vnfd_dict, + mock_log, mock_save, mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[ uuidsentinel.vnfc_instance_id_1]) vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED, task_state=fields.VnfInstanceTaskState.HEALING) - + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() self._mock_vnf_manager(fail_method_name='heal_vnf') driver = vnflcm_driver.VnfLcmDriver() self.assertRaises(exceptions.VnfHealFailed, driver.heal_vnf, self.context, vnf_instance, mock.ANY, heal_vnf_req) self.assertEqual(1, mock_save.call_count) - self.assertEqual(1, self._vnf_manager.invoke.call_count) + self.assertEqual(2, self._vnf_manager.invoke.call_count) self.assertEqual(fields.VnfInstanceTaskState.ERROR, vnf_instance.task_state) @@ -762,10 +956,21 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_with_infra_heal_vnf_wait_fail(self, mock_log, - mock_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_with_infra_heal_vnf_wait_fail( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_log, + mock_save, mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[ uuidsentinel.vnfc_instance_id_1]) @@ -787,7 +992,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): driver.heal_vnf(self.context, vnf_instance, mock.ANY, heal_vnf_req) self.assertEqual(1, mock_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(5, self._vnf_manager.invoke.call_count) self.assertEqual(None, vnf_instance.task_state) expected_msg = ('Failed to update vnf %(id)s resources for ' @@ -799,10 +1004,21 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfInstance, "save") @mock.patch('tacker.vnflcm.vnflcm_driver.LOG') - def test_heal_vnf_with_infra_post_heal_vnf_fail(self, mock_log, - mock_save, mock_get_service_plugins): + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') + def test_heal_vnf_with_infra_post_heal_vnf_fail( + self, mock_vnf_interfaces, mock_vnfd_dict, mock_log, + mock_save, mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() heal_vnf_req = objects.HealVnfRequest(vnfc_instance_id=[ uuidsentinel.vnfc_instance_id_1]) @@ -818,7 +1034,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): driver.heal_vnf, self.context, vnf_instance, mock.ANY, heal_vnf_req) self.assertEqual(1, mock_save.call_count) - self.assertEqual(3, self._vnf_manager.invoke.call_count) + self.assertEqual(4, self._vnf_manager.invoke.call_count) self.assertEqual(fields.VnfInstanceTaskState.ERROR, vnf_instance.task_state) @@ -832,8 +1048,15 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(driver_manager.DriverManager, "invoke") - def test_scale_true(self, mock_invoke, mock_get_service_plugins): + def test_scale_true(self, mock_invoke, mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_info = fakes._get_vnf() vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \ '{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \ @@ -850,10 +1073,17 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(yaml, "safe_load") @mock.patch.object(driver_manager.DriverManager, "invoke") def test_scale_false_in(self, mock_invoke, mock_safe_load, - mock_get_service_plugins): + mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_info = fakes._get_vnf() vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \ '{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \ @@ -873,10 +1103,17 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(yaml, "safe_load") @mock.patch.object(driver_manager.DriverManager, "invoke") def test_scale_false_out_initial(self, mock_invoke, mock_safe_load, - mock_get_service_plugins): + mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_info = fakes._get_vnf() vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \ '{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \ @@ -896,10 +1133,17 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(yaml, "safe_load") @mock.patch.object(driver_manager.DriverManager, "invoke") def test_scale_false_out_level_up(self, mock_invoke, mock_safe_load, - mock_get_service_plugins): + mock_init_hash, + mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_info = fakes._get_vnf() vnf_info['attributes']['scale_group'] = '{\"scaleGroupDict\": ' + \ '{ \"SP1\": { \"vdu\": [\"VDU1\"], \"num\": ' + \ @@ -919,19 +1163,30 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_7( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -953,6 +1208,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -965,7 +1222,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -987,6 +1249,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -999,7 +1263,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1021,6 +1290,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -1033,7 +1304,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1055,6 +1331,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -1067,7 +1345,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1089,19 +1372,30 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_scale( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1127,6 +1421,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -1139,7 +1435,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1165,6 +1466,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -1177,7 +1480,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1203,6 +1511,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -1215,7 +1525,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1241,6 +1556,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(VNFLcmRPCAPI, "send_notification") @mock.patch.object(objects.VnfInstance, "save") @@ -1253,7 +1570,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_insta_save, mock_notification, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1279,9 +1601,15 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") - def test_rollback_vnf_save_error(self, mock_lcm_save, + def test_rollback_vnf_save_error(self, mock_lcm_save, mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1305,6 +1633,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @@ -1322,7 +1652,12 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1351,6 +1686,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @@ -1368,6 +1705,7 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1405,6 +1743,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @@ -1413,8 +1753,11 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(opn.OpenStack, "delete") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_delete_error( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_delete, @@ -1422,7 +1765,13 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1448,6 +1797,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @@ -1457,8 +1808,11 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(opn.OpenStack, "delete_wait") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_delete_wait_error( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_delete_wait, @@ -1467,7 +1821,13 @@ class TestVnflcmDriver(db_base.SqlTestCase): mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1494,6 +1854,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @@ -1501,23 +1863,31 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(heat_client.HeatClient, "resource_get") @mock.patch.object(heat_client.HeatClient, "resource_get_list") @mock.patch.object(opn.OpenStack, "get_rollback_ids") - @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_rollback_mgmt_call") @mock.patch.object(opn.OpenStack, "scale_in_reverse") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_scale_update_error( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_scale, - mock_mgmt, mock_resource_get, mock_resource_get_list, mock_resource, mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() + vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1552,6 +1922,8 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @@ -1559,25 +1931,33 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(heat_client.HeatClient, "resource_get") @mock.patch.object(heat_client.HeatClient, "resource_get_list") @mock.patch.object(opn.OpenStack, "get_rollback_ids") - @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_rollback_mgmt_call") @mock.patch.object(opn.OpenStack, "scale_in_reverse") @mock.patch.object(opn.OpenStack, "scale_update_wait") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_scale_update_wait_error( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_wait, mock_scale, - mock_mgmt, mock_resource_get, mock_resource_get_list, mock_resource, mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() + vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) @@ -1613,30 +1993,40 @@ class TestVnflcmDriver(db_base.SqlTestCase): @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) + @mock.patch.object(VnfLcmDriver, + '_init_mgmt_driver_hash') @mock.patch.object(objects.VnfLcmOpOcc, "save") @mock.patch.object(common_services_db_plugin.CommonServicesPluginDb, "create_event") @mock.patch.object(heat_client.HeatClient, "__init__") @mock.patch.object(opn.OpenStack, "get_rollback_ids") - @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_rollback_mgmt_call") @mock.patch.object(opn.OpenStack, "scale_in_reverse") @mock.patch.object(opn.OpenStack, "scale_update_wait") @mock.patch.object(opn.OpenStack, "scale_resource_update") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback_pre") @mock.patch.object(vnflcm_driver.VnfLcmDriver, "_update_vnf_rollback") + @mock.patch('tacker.vnflcm.utils._get_vnfd_dict') + @mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.' + '_load_vnf_interface') def test_rollback_vnf_scale_resource_get_error( - self, + self, mock_vnf_interfaces, mock_vnfd_dict, mock_update, mock_up, mock_scale_resource, mock_wait, mock_scale, - mock_mgmt, mock_resource_get, mock_init, mock_event, mock_lcm_save, + mock_init_hash, mock_get_service_plugins): + mock_init_hash.return_value = { + "vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859" + "b18d663b127100eb72b19eecd7ed51" + } + mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces() + vnf_instance = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) diff --git a/tacker/vnflcm/vnflcm_driver.py b/tacker/vnflcm/vnflcm_driver.py index 65efaa308..74c9bf855 100644 --- a/tacker/vnflcm/vnflcm_driver.py +++ b/tacker/vnflcm/vnflcm_driver.py @@ -16,7 +16,9 @@ import copy from datetime import datetime import functools +import hashlib import inspect +import os import re import time import traceback @@ -28,10 +30,9 @@ from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslo_utils import excutils -from tacker.common import log - from tacker.common import driver_manager from tacker.common import exceptions +from tacker.common import log from tacker.common import safe_utils from tacker.common import utils from tacker.conductor.conductorrpc import vnf_lcm_rpc @@ -40,10 +41,10 @@ from tacker import objects from tacker.objects import fields from tacker.vnflcm import abstract_driver from tacker.vnflcm import utils as vnflcm_utils -from tacker.vnfm.mgmt_drivers import constants as mgmt_constants LOG = logging.getLogger(__name__) CONF = cfg.CONF +DEFAULT_VNFLCM_MGMT_DRIVER = "vnflcm_noop" @utils.expects_func_args('vnf_info', 'vnf_instance', 'scale_vnf_request') @@ -281,7 +282,24 @@ def revert_to_error_rollback(function): return decorated_function +def config_opts(): + return [('tacker', VnfLcmDriver.OPTS)] + + class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): + OPTS = [ + cfg.ListOpt( + 'vnflcm_infra_driver', default=['openstack', 'kubernetes'], + help=_('Hosting vnf drivers tacker plugin will use') + ), + cfg.ListOpt( + 'vnflcm_mgmt_driver', default=[DEFAULT_VNFLCM_MGMT_DRIVER], + help=_('MGMT driver to communicate with ' + 'Hosting VNF/logical service ' + 'instance tacker plugin will use') + ) + ] + cfg.CONF.register_opts(OPTS, 'tacker') def __init__(self): super(VnfLcmDriver, self).__init__() @@ -289,7 +307,18 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM'] self._vnf_manager = driver_manager.DriverManager( 'tacker.tacker.vnfm.drivers', - cfg.CONF.tacker.infra_driver) + cfg.CONF.tacker.vnflcm_infra_driver) + self._mgmt_manager = driver_manager.DriverManager( + 'tacker.tacker.mgmt.drivers', cfg.CONF.tacker.vnflcm_mgmt_driver) + self._mgmt_driver_hash = self._init_mgmt_driver_hash() + + def _init_mgmt_driver_hash(self): + + driver_hash = {} + for mgmt_driver in cfg.CONF.tacker.vnflcm_mgmt_driver: + path = inspect.getfile(self._mgmt_manager[mgmt_driver].__class__) + driver_hash[mgmt_driver] = self._get_file_hash(path) + return driver_hash def _vnf_instance_update(self, context, vnf_instance, **kwargs): """Update vnf instance in the database using kwargs as value.""" @@ -377,6 +406,57 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): id=vnf_instance.id, error=encodeutils.exception_to_unicode(exp)) + def _get_file_hash(self, path): + hash_obj = hashlib.sha256() + with open(path) as f: + hash_obj.update(f.read().encode('utf-8')) + return hash_obj.hexdigest() + + def _check_mgmt_driver(self, artifact_mgmt_driver, artifacts_value, + vnf_package_path): + # check implementation and artifacts exist in cfg.CONF.tacker + if artifact_mgmt_driver not in self._mgmt_driver_hash: + LOG.error('The {} specified in the VNFD ' + 'is inconsistent with the MgmtDriver in ' + 'the configuration file.'.format(artifact_mgmt_driver)) + raise exceptions.MgmtDriverInconsistent( + MgmtDriver=artifact_mgmt_driver) + + # check file content + pkg_mgmt_driver_path = os.path.join(vnf_package_path, + artifacts_value[artifact_mgmt_driver]['file']) + pkg_mgmt_driver_hash = self._get_file_hash(pkg_mgmt_driver_path) + if pkg_mgmt_driver_hash == \ + self._mgmt_driver_hash[artifact_mgmt_driver]: + return artifact_mgmt_driver + else: + LOG.error('The hash verification of VNF Package MgmtDriver ' + 'and Tacker MgmtDriver does not match.') + raise exceptions.MgmtDriverHashMatchFailure() + + def _load_vnf_interface(self, context, method_name, + vnf_instance, vnfd_dict): + VNF_value = vnfd_dict['topology_template']['node_templates']['VNF'] + tacker_mgmt_driver = DEFAULT_VNFLCM_MGMT_DRIVER + interfaces_vnflcm_value = \ + VNF_value.get('interfaces', {}).get('Vnflcm', {}) + if not interfaces_vnflcm_value: + return tacker_mgmt_driver + artifacts_value = VNF_value.get('artifacts') + if not artifacts_value: + return tacker_mgmt_driver + vnf_package_path = vnflcm_utils._get_vnf_package_path( + context, vnf_instance.vnfd_id) + + if interfaces_vnflcm_value.get(method_name): + artifact_mgmt_driver = interfaces_vnflcm_value.get( + method_name).get('implementation') + if artifact_mgmt_driver: + tacker_mgmt_driver = self._check_mgmt_driver( + artifact_mgmt_driver, artifacts_value, vnf_package_path) + + return tacker_mgmt_driver + @log.log def instantiate_vnf(self, context, vnf_instance, vnf_dict, instantiate_vnf_req): @@ -394,9 +474,26 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( vim_info, context) + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, instantiate_vnf_req.flavour_id) + + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'instantiate_start', vnf_instance, vnfd_dict), + 'instantiate_start', context=context, + vnf_instance=vnf_instance, + additional_params=instantiate_vnf_req.additional_params) + self._instantiate_vnf(context, vnf_instance, vnf_dict, vim_connection_info, instantiate_vnf_req) + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'instantiate_end', vnf_instance, vnfd_dict), + 'instantiate_end', context=context, + vnf_instance=vnf_instance, + additional_params=instantiate_vnf_req.additional_params) + @log.log @revert_to_error_task_state def terminate_vnf(self, context, vnf_instance, terminate_vnf_req): @@ -407,6 +504,16 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( vim_info, context) + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'terminate_start', vnf_instance, vnfd_dict), + 'terminate_start', context=context, + vnf_instance=vnf_instance, + additional_params=terminate_vnf_req.additional_params) LOG.info("Terminating vnf %s", vnf_instance.id) try: self._delete_vnf_instance_resources(context, vnf_instance, @@ -422,6 +529,12 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): LOG.error("Unable to terminate vnf '%s' instance. " "Error: %s", vnf_instance.id, encodeutils.exception_to_unicode(exp)) + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'terminate_end', vnf_instance, vnfd_dict), + 'terminate_end', context=context, + vnf_instance=vnf_instance, + additional_params=terminate_vnf_req.additional_params) def _delete_vnf_instance_resources(self, context, vnf_instance, vim_connection_info, terminate_vnf_req=None, @@ -578,6 +691,17 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( vim_info, context) + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'heal_start', vnf_instance, vnfd_dict), + 'heal_start', context=context, + vnf_instance=vnf_instance, + additional_params=heal_vnf_request.additional_params) + if not heal_vnf_request.vnfc_instance_id: self._respawn_vnf(context, vnf_instance, vnf_dict, vim_connection_info, heal_vnf_request) @@ -587,6 +711,12 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): LOG.info("Request received for healing vnf '%s' is completed " "successfully", vnf_instance.id) + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'heal_end', vnf_instance, vnfd_dict), + 'heal_end', context=context, + vnf_instance=vnf_instance, + additional_params=heal_vnf_request.additional_params) def _scale_vnf_pre(self, context, vnf_info, vnf_instance, scale_vnf_request, vim_connection_info): @@ -600,15 +730,10 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): grp_id = None vnf_info['policy_name'] = scale_vnf_request.aspect_id if scale_vnf_request.type == 'SCALE_IN': - vnfd_yaml = vnf_info['vnfd']['attributes'].get( - 'vnfd_' + vnf_instance.instantiated_vnf_info.flavour_id, '') - vnfd_dict = yaml.safe_load(vnfd_yaml) - # mgmt_driver from vnfd - vnf_node = self._get_node_template_for_vnf(vnfd_dict) - if vnf_node and vnf_node.get('interfaces'): - if vnf_node['interfaces']['Vnflcm']['scale_start']: - vnf_info['vnfd']['mgmt_driver'] = \ - vnf_node['interfaces']['Vnflcm']['scale_start'] + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + vnf_info['action'] = 'in' scale_id_list, scale_name_list, grp_id, res_num = \ self._vnf_manager.invoke( @@ -627,21 +752,13 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vnf_info['res_num'] = res_num # mgmt_driver pre - if len(scale_id_list) != 0 and vnf_info['vnfd'].get('mgmt_driver'): - if len(scale_id_list) > 1: - stack_value = [] - stack_value = scale_id_list - else: - stack_value = scale_id_list[0] - kwargs = { - mgmt_constants.KEY_ACTION: - mgmt_constants.ACTION_SCALE_IN_VNF, - mgmt_constants.KEY_KWARGS: - {'vnf': vnf_info}, - mgmt_constants.KEY_SCALE: - stack_value, - } - self._vnfm_plugin.mgmt_call(context, vnf_info, kwargs) + if len(scale_id_list) != 0: + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'scale_start', vnf_instance, vnfd_dict), + 'scale_start', context=context, + vnf_instance=vnf_instance, + additional_params=scale_vnf_request.additional_params) else: vnf_info['action'] = 'out' scale_id_list = self._vnf_manager.invoke( @@ -672,18 +789,10 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): vnf_lcm_op_occ = vnf_info['vnf_lcm_op_occ'] vnf_lcm_op_occ.error_point = 6 if scale_vnf_request.type == 'SCALE_OUT': - vnfd_yaml =\ - vnf_info['vnfd']['attributes'].\ - get('vnfd_' + vnf_instance.instantiated_vnf_info.flavour_id, - '') - vnf_info['policy_name'] = scale_vnf_request.aspect_id - vnfd_dict = yaml.safe_load(vnfd_yaml) - # mgmt_driver from vnfd - vnf_node = self._get_node_template_for_vnf(vnfd_dict) - if vnf_node and vnf_node.get('interfaces'): - if vnf_node['interfaces']['Vnflcm']['scale_end']: - vnf_info['vnfd']['mgmt_driver'] = \ - vnf_node['interfaces']['Vnflcm']['scale_end'] + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + scale_id_after = self._vnf_manager.invoke( vim_connection_info.vim_type, 'get_scale_ids', @@ -696,21 +805,13 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): id_list = [] id_list = list(set(scale_id_after) - set(scale_id_list)) vnf_info['res_num'] = len(scale_id_after) - if len(id_list) != 0 and vnf_info['vnfd'].get('mgmt_driver'): - if len(id_list) > 1: - stack_value = [] - stack_value = id_list - else: - stack_value = id_list[0] - kwargs = { - mgmt_constants.KEY_ACTION: - mgmt_constants.ACTION_SCALE_OUT_VNF, - mgmt_constants.KEY_KWARGS: - {'vnf': vnf_info}, - mgmt_constants.KEY_SCALE: - stack_value, - } - self._vnfm_plugin.mgmt_call(context, vnf_info, kwargs) + if len(id_list) != 0: + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'scale_end', vnf_instance, vnfd_dict), + 'scale_end', context=context, + vnf_instance=vnf_instance, + additional_params=scale_vnf_request.additional_params) vnf_lcm_op_occ.error_point = 7 vnf_instance.instantiated_vnf_info.scale_level =\ vnf_info['after_scale_level'] @@ -1214,57 +1315,32 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): ) if vnf_lcm_op_occs.error_point == 7: if vnf_lcm_op_occs.operation == 'SCALE': - vnfd_yaml = vnf_info['vnfd']['attributes'].\ - get('vnfd_' + - vnf_instance.instantiated_vnf_info.flavour_id, '') - vnfd_dict = yaml.safe_load(vnfd_yaml) - # mgmt_driver from vnfd - vnf_node = self._get_node_template_for_vnf(vnfd_dict) - if vnf_node and vnf_node.get('interfaces'): - if vnf_node['interfaces'].get('Vnflcm'): - if vnf_node['interfaces']['Vnflcm'].get('scale_start'): - vnf_info['vnfd']['mgmt_driver'] = \ - vnf_node['interfaces']['Vnflcm']['scale_start'] + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + vnf_info['action'] = 'in' - if len(scale_id_list) != 0 and vnf_info['vnfd'].get( - 'mgmt_driver'): - if len(scale_id_list) > 1: - stack_value = [] - stack_value = scale_id_list - else: - stack_value = scale_id_list[0] - kwargs = { - mgmt_constants.KEY_ACTION: - mgmt_constants.ACTION_SCALE_IN_VNF, - mgmt_constants.KEY_KWARGS: - {'vnf': vnf_info}, - mgmt_constants.KEY_SCALE: - stack_value, - } - self._rollback_mgmt_call(context, vnf_info, kwargs) + if len(scale_id_list) != 0: + self._mgmt_manager.invoke( + self._load_vnf_interface(context, 'scale_start', + vnf_instance, vnfd_dict), + 'scale_start', context=context, + vnf_instance=vnf_instance, + additional_params=scale_vnf_request.additional_params) else: - vnfd_yaml = vnf_info['vnfd']['attributes'].\ - get('vnfd_' + - vnf_instance.instantiated_vnf_info.flavour_id, '') - vnfd_dict = yaml.safe_load(vnfd_yaml) - # mgmt_driver from vnfd - vnf_node = self._get_node_template_for_vnf(vnfd_dict) - if vnf_node and vnf_node.get('interfaces'): - if vnf_node['interfaces'].get('Vnflcm'): - if vnf_node['interfaces']['Vnflcm'].get( - 'termination_start'): - vnf_info['vnfd']['mgmt_driver'] = vnf_node[ - 'interfaces']['Vnflcm']['termination_start'] - if len(scale_id_list) != 0 and vnf_info['vnfd'].get( - 'mgmt_driver'): - kwargs = { - mgmt_constants.KEY_ACTION: - mgmt_constants.ACTION_DELETE_VNF, - mgmt_constants.KEY_KWARGS: - {'vnf': vnf_info} - } - self._rollback_mgmt_call(context, vnf_info, kwargs) + vnfd_dict = vnflcm_utils._get_vnfd_dict( + context, vnf_instance.vnfd_id, + vnf_instance.instantiated_vnf_info.flavour_id) + + if len(scale_id_list) != 0: + self._mgmt_manager.invoke( + self._load_vnf_interface( + context, 'terminate_start', + vnf_instance, vnfd_dict), + 'terminate_start', context=context, + vnf_instance=vnf_instance, + additional_params=scale_vnf_request.additional_params) vnf_lcm_op_occs.error_point = 6 return scale_name_list, grp_id @@ -1336,9 +1412,6 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): def _update_vnf_rollback_status_err(self, context, vnf_info): self._vnfm_plugin.update_vnf_rollback_status_err(context, vnf_info) - def _rollback_mgmt_call(self, context, vnf_info, kwargs): - self._vnfm_plugin.mgmt_call(context, vnf_info, kwargs) - def _rollback_vnf_post( self, context, diff --git a/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py b/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py new file mode 100644 index 000000000..ee79c6026 --- /dev/null +++ b/tacker/vnfm/mgmt_drivers/vnflcm_abstract_driver.py @@ -0,0 +1,77 @@ +# Copyright (C) 2020 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 abc + +from oslo_log import log as logging + + +LOG = logging.getLogger(__name__) + + +class VnflcmMgmtAbstractDriver(metaclass=abc.ABCMeta): + @abc.abstractmethod + def get_type(self): + """Return one of predefined type of the hosting vnf drivers.""" + pass + + @abc.abstractmethod + def get_name(self): + """Return a symbolic name for the service VM plugin.""" + pass + + @abc.abstractmethod + def get_description(self): + pass + + @abc.abstractmethod + def instantiate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def instantiate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def terminate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def terminate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def scale_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def scale_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def heal_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @abc.abstractmethod + def heal_end(self, context, vnf_instance, + additional_params, **kwargs): + pass diff --git a/tacker/vnfm/mgmt_drivers/vnflcm_noop.py b/tacker/vnfm/mgmt_drivers/vnflcm_noop.py new file mode 100644 index 000000000..6e520aa9f --- /dev/null +++ b/tacker/vnfm/mgmt_drivers/vnflcm_noop.py @@ -0,0 +1,68 @@ +# Copyright (C) 2020 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. + +from tacker.common import log +from tacker.vnfm.mgmt_drivers import vnflcm_abstract_driver + + +class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver): + def get_type(self): + return 'vnflcm_noop' + + def get_name(self): + return 'vnflcm_noop' + + def get_description(self): + return 'Tacker VNFMgmt VnflcmNoop Driver' + + @log.log + def instantiate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def instantiate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def terminate_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def terminate_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def scale_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def scale_end(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def heal_start(self, context, vnf_instance, + additional_params, **kwargs): + pass + + @log.log + def heal_end(self, context, vnf_instance, + additional_params, **kwargs): + pass diff --git a/test-requirements.txt b/test-requirements.txt index cf804f489..fe0fc3db9 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -20,3 +20,4 @@ WebTest>=2.0.27 # MIT python-barbicanclient>=4.5.2 # Apache-2.0 python-blazarclient>=1.0.1 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0 +PyMySQL>=0.10.1 # MIT