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
This commit is contained in:
LiangLu 2020-12-21 22:08:58 +09:00
parent 8a56f1ec53
commit f88022cdc2
25 changed files with 2862 additions and 183 deletions

View File

@ -94,6 +94,7 @@ pycadf==2.7.0
pycparser==2.18 pycparser==2.18
Pygments==2.2.0 Pygments==2.2.0
pyinotify==0.9.6 pyinotify==0.9.6
PyMySQL==0.10.1
PyNaCl==1.2.1 PyNaCl==1.2.1
pyOpenSSL==17.5.0 pyOpenSSL==17.5.0
pyparsing==2.2.0 pyparsing==2.2.0

View File

@ -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.

View File

@ -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 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 tooz>=1.58.0 # Apache-2.0
PyYAML>=5.1 # MIT PyYAML>=5.1 # MIT
PyMySQL>=0.10.1 # MIT
# Glance Store # Glance Store
glance-store>=2.4.0 # Apache-2.0 glance-store>=2.4.0 # Apache-2.0

View File

@ -61,6 +61,7 @@ tacker.tacker.vnfm.drivers =
tacker.tacker.mgmt.drivers = tacker.tacker.mgmt.drivers =
noop = tacker.vnfm.mgmt_drivers.noop:VnfMgmtNoop noop = tacker.vnfm.mgmt_drivers.noop:VnfMgmtNoop
openwrt = tacker.vnfm.mgmt_drivers.openwrt.openwrt:VnfMgmtOpenWRT openwrt = tacker.vnfm.mgmt_drivers.openwrt.openwrt:VnfMgmtOpenWRT
vnflcm_noop = tacker.vnfm.mgmt_drivers.vnflcm_noop:VnflcmMgmtNoop
tacker.tacker.monitor.drivers = tacker.tacker.monitor.drivers =
ping = tacker.vnfm.monitor_drivers.ping.ping:VNFMonitorPing ping = tacker.vnfm.monitor_drivers.ping.ping:VNFMonitorPing
http_ping = tacker.vnfm.monitor_drivers.http_ping.http_ping:VNFMonitorHTTPPing 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.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.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.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.alarm_receiver = tacker.alarm_receiver:config_opts
tacker.plugins.fenix = tacker.plugins.fenix:config_opts tacker.plugins.fenix = tacker.plugins.fenix:config_opts

View File

@ -376,3 +376,33 @@ class DBAccessError(TackerException):
class SeeOther(TackerException): class SeeOther(TackerException):
code = 303 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.')

View File

@ -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 ]

View File

@ -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

View File

@ -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

View File

@ -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 ]

View File

@ -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

View File

@ -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 ]

View File

@ -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

View File

@ -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 ]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'])

View File

@ -39,6 +39,11 @@ import tacker.conf
CONF = tacker.conf.CONF CONF = tacker.conf.CONF
def return_vnf_interfaces():
vnf_interface = 'vnflcm_noop'
return vnf_interface
def return_default_vim(): def return_default_vim():
default_vim = { default_vim = {
'vim_auth': { 'vim_auth': {

View File

@ -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)

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,9 @@
import copy import copy
from datetime import datetime from datetime import datetime
import functools import functools
import hashlib
import inspect import inspect
import os
import re import re
import time import time
import traceback import traceback
@ -28,10 +30,9 @@ from oslo_serialization import jsonutils
from oslo_utils import encodeutils from oslo_utils import encodeutils
from oslo_utils import excutils from oslo_utils import excutils
from tacker.common import log
from tacker.common import driver_manager from tacker.common import driver_manager
from tacker.common import exceptions from tacker.common import exceptions
from tacker.common import log
from tacker.common import safe_utils from tacker.common import safe_utils
from tacker.common import utils from tacker.common import utils
from tacker.conductor.conductorrpc import vnf_lcm_rpc from tacker.conductor.conductorrpc import vnf_lcm_rpc
@ -40,10 +41,10 @@ from tacker import objects
from tacker.objects import fields from tacker.objects import fields
from tacker.vnflcm import abstract_driver from tacker.vnflcm import abstract_driver
from tacker.vnflcm import utils as vnflcm_utils from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnfm.mgmt_drivers import constants as mgmt_constants
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONF = cfg.CONF CONF = cfg.CONF
DEFAULT_VNFLCM_MGMT_DRIVER = "vnflcm_noop"
@utils.expects_func_args('vnf_info', 'vnf_instance', 'scale_vnf_request') @utils.expects_func_args('vnf_info', 'vnf_instance', 'scale_vnf_request')
@ -281,7 +282,24 @@ def revert_to_error_rollback(function):
return decorated_function return decorated_function
def config_opts():
return [('tacker', VnfLcmDriver.OPTS)]
class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver): 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): def __init__(self):
super(VnfLcmDriver, self).__init__() super(VnfLcmDriver, self).__init__()
@ -289,7 +307,18 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM'] self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
self._vnf_manager = driver_manager.DriverManager( self._vnf_manager = driver_manager.DriverManager(
'tacker.tacker.vnfm.drivers', '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): def _vnf_instance_update(self, context, vnf_instance, **kwargs):
"""Update vnf instance in the database using kwargs as value.""" """Update vnf instance in the database using kwargs as value."""
@ -377,6 +406,57 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
id=vnf_instance.id, id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp)) 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 @log.log
def instantiate_vnf(self, context, vnf_instance, vnf_dict, def instantiate_vnf(self, context, vnf_instance, vnf_dict,
instantiate_vnf_req): instantiate_vnf_req):
@ -394,9 +474,26 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
vim_connection_info = objects.VimConnectionInfo.obj_from_primitive( vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context) 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, self._instantiate_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, instantiate_vnf_req) 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 @log.log
@revert_to_error_task_state @revert_to_error_task_state
def terminate_vnf(self, context, vnf_instance, terminate_vnf_req): 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_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context) 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) LOG.info("Terminating vnf %s", vnf_instance.id)
try: try:
self._delete_vnf_instance_resources(context, vnf_instance, 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. " LOG.error("Unable to terminate vnf '%s' instance. "
"Error: %s", vnf_instance.id, "Error: %s", vnf_instance.id,
encodeutils.exception_to_unicode(exp)) 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, def _delete_vnf_instance_resources(self, context, vnf_instance,
vim_connection_info, terminate_vnf_req=None, 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_connection_info = objects.VimConnectionInfo.obj_from_primitive(
vim_info, context) 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: if not heal_vnf_request.vnfc_instance_id:
self._respawn_vnf(context, vnf_instance, vnf_dict, self._respawn_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, heal_vnf_request) 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 " LOG.info("Request received for healing vnf '%s' is completed "
"successfully", vnf_instance.id) "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, def _scale_vnf_pre(self, context, vnf_info, vnf_instance,
scale_vnf_request, vim_connection_info): scale_vnf_request, vim_connection_info):
@ -600,15 +730,10 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
grp_id = None grp_id = None
vnf_info['policy_name'] = scale_vnf_request.aspect_id vnf_info['policy_name'] = scale_vnf_request.aspect_id
if scale_vnf_request.type == 'SCALE_IN': if scale_vnf_request.type == 'SCALE_IN':
vnfd_yaml = vnf_info['vnfd']['attributes'].get( vnfd_dict = vnflcm_utils._get_vnfd_dict(
'vnfd_' + vnf_instance.instantiated_vnf_info.flavour_id, '') context, vnf_instance.vnfd_id,
vnfd_dict = yaml.safe_load(vnfd_yaml) vnf_instance.instantiated_vnf_info.flavour_id)
# 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']
vnf_info['action'] = 'in' vnf_info['action'] = 'in'
scale_id_list, scale_name_list, grp_id, res_num = \ scale_id_list, scale_name_list, grp_id, res_num = \
self._vnf_manager.invoke( self._vnf_manager.invoke(
@ -627,21 +752,13 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
vnf_info['res_num'] = res_num vnf_info['res_num'] = res_num
# mgmt_driver pre # mgmt_driver pre
if len(scale_id_list) != 0 and vnf_info['vnfd'].get('mgmt_driver'): if len(scale_id_list) != 0:
if len(scale_id_list) > 1: self._mgmt_manager.invoke(
stack_value = [] self._load_vnf_interface(
stack_value = scale_id_list context, 'scale_start', vnf_instance, vnfd_dict),
else: 'scale_start', context=context,
stack_value = scale_id_list[0] vnf_instance=vnf_instance,
kwargs = { additional_params=scale_vnf_request.additional_params)
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)
else: else:
vnf_info['action'] = 'out' vnf_info['action'] = 'out'
scale_id_list = self._vnf_manager.invoke( 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 = vnf_info['vnf_lcm_op_occ']
vnf_lcm_op_occ.error_point = 6 vnf_lcm_op_occ.error_point = 6
if scale_vnf_request.type == 'SCALE_OUT': if scale_vnf_request.type == 'SCALE_OUT':
vnfd_yaml =\ vnfd_dict = vnflcm_utils._get_vnfd_dict(
vnf_info['vnfd']['attributes'].\ context, vnf_instance.vnfd_id,
get('vnfd_' + vnf_instance.instantiated_vnf_info.flavour_id, 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']
scale_id_after = self._vnf_manager.invoke( scale_id_after = self._vnf_manager.invoke(
vim_connection_info.vim_type, vim_connection_info.vim_type,
'get_scale_ids', 'get_scale_ids',
@ -696,21 +805,13 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
id_list = [] id_list = []
id_list = list(set(scale_id_after) - set(scale_id_list)) id_list = list(set(scale_id_after) - set(scale_id_list))
vnf_info['res_num'] = len(scale_id_after) vnf_info['res_num'] = len(scale_id_after)
if len(id_list) != 0 and vnf_info['vnfd'].get('mgmt_driver'): if len(id_list) != 0:
if len(id_list) > 1: self._mgmt_manager.invoke(
stack_value = [] self._load_vnf_interface(
stack_value = id_list context, 'scale_end', vnf_instance, vnfd_dict),
else: 'scale_end', context=context,
stack_value = id_list[0] vnf_instance=vnf_instance,
kwargs = { additional_params=scale_vnf_request.additional_params)
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)
vnf_lcm_op_occ.error_point = 7 vnf_lcm_op_occ.error_point = 7
vnf_instance.instantiated_vnf_info.scale_level =\ vnf_instance.instantiated_vnf_info.scale_level =\
vnf_info['after_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.error_point == 7:
if vnf_lcm_op_occs.operation == 'SCALE': if vnf_lcm_op_occs.operation == 'SCALE':
vnfd_yaml = vnf_info['vnfd']['attributes'].\ vnfd_dict = vnflcm_utils._get_vnfd_dict(
get('vnfd_' + context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id, '') 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']
vnf_info['action'] = 'in' vnf_info['action'] = 'in'
if len(scale_id_list) != 0 and vnf_info['vnfd'].get( if len(scale_id_list) != 0:
'mgmt_driver'): self._mgmt_manager.invoke(
if len(scale_id_list) > 1: self._load_vnf_interface(context, 'scale_start',
stack_value = [] vnf_instance, vnfd_dict),
stack_value = scale_id_list 'scale_start', context=context,
else: vnf_instance=vnf_instance,
stack_value = scale_id_list[0] additional_params=scale_vnf_request.additional_params)
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)
else: else:
vnfd_yaml = vnf_info['vnfd']['attributes'].\ vnfd_dict = vnflcm_utils._get_vnfd_dict(
get('vnfd_' + context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id, '') vnf_instance.instantiated_vnf_info.flavour_id)
vnfd_dict = yaml.safe_load(vnfd_yaml)
# mgmt_driver from vnfd if len(scale_id_list) != 0:
vnf_node = self._get_node_template_for_vnf(vnfd_dict) self._mgmt_manager.invoke(
if vnf_node and vnf_node.get('interfaces'): self._load_vnf_interface(
if vnf_node['interfaces'].get('Vnflcm'): context, 'terminate_start',
if vnf_node['interfaces']['Vnflcm'].get( vnf_instance, vnfd_dict),
'termination_start'): 'terminate_start', context=context,
vnf_info['vnfd']['mgmt_driver'] = vnf_node[ vnf_instance=vnf_instance,
'interfaces']['Vnflcm']['termination_start'] additional_params=scale_vnf_request.additional_params)
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)
vnf_lcm_op_occs.error_point = 6 vnf_lcm_op_occs.error_point = 6
return scale_name_list, grp_id 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): def _update_vnf_rollback_status_err(self, context, vnf_info):
self._vnfm_plugin.update_vnf_rollback_status_err(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( def _rollback_vnf_post(
self, self,
context, context,

View File

@ -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

View File

@ -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

View File

@ -20,3 +20,4 @@ WebTest>=2.0.27 # MIT
python-barbicanclient>=4.5.2 # Apache-2.0 python-barbicanclient>=4.5.2 # Apache-2.0
python-blazarclient>=1.0.1 # Apache-2.0 python-blazarclient>=1.0.1 # Apache-2.0
requests-mock>=1.2.0 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0
PyMySQL>=0.10.1 # MIT