Support LCM operation with user data

Implement new method of VNF lifecycle management using LCM operation
user data.

Change-Id: Ib2fbe341b5d26758f0b48dc19e3e05810c2c830f
Blueprint: support-etsi-nfv-specs
changes/12/710012/14
Hiroo Kitamura 2 years ago
parent 428096a247
commit 62813239e1
  1. 4
      tacker/extensions/vnfm.py
  2. 94
      tacker/tests/etc/samples/etsi/nfv/user_data_common/Definitions/sample_lcm_with_user_data_df_simple.yaml
  3. 31
      tacker/tests/etc/samples/etsi/nfv/user_data_common/Definitions/sample_lcm_with_user_data_top.vnfd.yaml
  4. 53
      tacker/tests/etc/samples/etsi/nfv/user_data_common/Definitions/sample_lcm_with_user_data_types.yaml
  5. 32
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_basehot_invalid/BaseHOT/sample_lcm_with_user_data_invalid_hot.yaml
  6. 7
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_basehot_invalid/TOSCA-Metadata/TOSCA.meta
  7. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_basehot_invalid/UserData/__init__.py
  8. 39
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_basehot_invalid/UserData/lcm_user_data.py
  9. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_non_dict/UserData/__init__.py
  10. 26
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_non_dict/UserData/lcm_user_data_non_dict.py
  11. 32
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_normal/BaseHOT/sample_lcm_with_user_data_hot.yaml
  12. 7
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_normal/TOSCA-Metadata/TOSCA.meta
  13. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_normal/UserData/__init__.py
  14. 39
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_normal/UserData/lcm_user_data.py
  15. 32
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_hot_param/BaseHOT/sample_lcm_with_user_data_hot.yaml
  16. 7
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_hot_param/TOSCA-Metadata/TOSCA.meta
  17. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_hot_param/UserData/__init__.py
  18. 39
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_hot_param/UserData/lcm_user_data_invalid_hot_param.py
  19. 32
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_script/BaseHOT/sample_lcm_with_user_data_hot.yaml
  20. 7
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_script/TOSCA-Metadata/TOSCA.meta
  21. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_script/UserData/__init__.py
  22. 28
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_invalid_script/UserData/lcm_user_data_invalid_script.py
  23. 32
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_none/BaseHOT/sample_lcm_with_user_data_hot.yaml
  24. 7
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_none/TOSCA-Metadata/TOSCA.meta
  25. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_none/UserData/__init__.py
  26. 32
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_timeout/BaseHOT/sample_lcm_with_user_data_hot.yaml
  27. 7
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_timeout/TOSCA-Metadata/TOSCA.meta
  28. 0
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_timeout/UserData/__init__.py
  29. 51
      tacker/tests/etc/samples/etsi/nfv/user_data_sample_userdata_timeout/UserData/lcm_user_data_sleeping.py
  30. 32
      tacker/tests/etc/samples/hot_lcm_user_data.yaml
  31. 34
      tacker/tests/etc/samples/lcm_instantiate_request/instantiate_vnf_request_lcm_userdata.json
  32. 94
      tacker/tests/etc/samples/vnfd_lcm_user_data.yaml
  33. 501
      tacker/tests/functional/vnflcm/test_vnf_instance_with_user_data.py
  34. 0
      tacker/tests/unit/vnflcm/vnflcm_driver/__init__.py
  35. 438
      tacker/tests/unit/vnfm/infra_drivers/openstack/test_openstack_driver.py
  36. 0
      tacker/tests/unit/vnfm/lcm_user_data/__init__.py
  37. 0
      tacker/tests/unit/vnfm/lcm_user_data/utils/__init__.py
  38. 192
      tacker/tests/unit/vnfm/lcm_user_data/utils/test_utils.py
  39. 25
      tacker/vnflcm/utils.py
  40. 8
      tacker/vnflcm/vnflcm_driver.py
  41. 145
      tacker/vnfm/infra_drivers/openstack/openstack.py
  42. 0
      tacker/vnfm/lcm_user_data/__init__.py
  43. 30
      tacker/vnfm/lcm_user_data/abstract_user_data.py
  44. 14
      tacker/vnfm/lcm_user_data/constants.py
  45. 172
      tacker/vnfm/lcm_user_data/utils.py

@ -108,6 +108,10 @@ class VNFNotFound(exceptions.NotFound):
message = _('VNF %(vnf_id)s could not be found')
class LCMUserDataFailed(exceptions.TackerException):
message = _('LCM user data %(reason)s')
class ParamYAMLNotWellFormed(exceptions.InvalidInput):
message = _("Parameter YAML not well formed - %(error_msg_details)s")

@ -0,0 +1,94 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: >
Template for test _generate_hot_from_tosca().
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- sample_lcm_with_user_data_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: ntt.nslab.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external: [ CP1, virtual_link ]
node_templates:
VNF:
type: ntt.nslab.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
instantiate: []
instantiate_start: []
instantiate_end: []
terminate: []
terminate_start: []
terminate_end: []
modify_information: []
modify_information_start: []
modify_information_end: []
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-512
hash: 6513f21e44aa3da349f248188a44bc304a3653a04122d8fb4535423c8e1d14cd6a153f735bb0982e2161b5b5186106570c17a9e58b64dd39390617cd5a350f78
container_format: bare
disk_format: qcow2
min_disk: 1 GiB
size: 1 GiB
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 MiB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 GiB
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1

@ -0,0 +1,31 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Sample VNFD for LCM with user data
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- sample_lcm_with_user_data_types.yaml
- sample_lcm_with_user_data_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: ntt.nslab.VNF
properties:
flavour_id: { get_input: selected_flavour }
descriptor_id: eeeeeeee-ebca-4fa7-95ed-4840d70a1111
provider: NTT NS lab
product_name: Sample VNF for LCM with user data
software_version: '1.0'
descriptor_version: '1.0'
vnfm_info:
- Tacker
requirements:
#- virtual_link_external # mapped in lower-level templates
#- virtual_link_internal # mapped in lower-level templates

@ -0,0 +1,53 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: VNF type definition
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
node_types:
ntt.nslab.VNF:
derived_from: tosca.nodes.nfv.VNF
properties:
descriptor_id:
type: string
constraints: [ valid_values: [ eeeeeeee-ebca-4fa7-95ed-4840d70a1111 ] ]
default: eeeeeeee-ebca-4fa7-95ed-4840d70a1111
descriptor_version:
type: string
constraints: [ valid_values: [ '1.0' ] ]
default: '1.0'
provider:
type: string
constraints: [ valid_values: [ 'NTT NS lab' ] ]
default: 'NTT NS lab'
product_name:
type: string
constraints: [ valid_values: [ 'Sample VNF for LCM with user data' ] ]
default: 'Sample VNF for LCM with user data'
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

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
# flavor:
# get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_lcm_with_user_data_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

@ -0,0 +1,39 @@
#
# 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 tacker.vnfm.lcm_user_data.utils as UserDataUtil
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
# Create HOT input parameter using util functions.
initial_param_dict = UserDataUtil.create_initial_param_dict(
base_hot_dict)
vdu_flavor_dict = UserDataUtil.create_vdu_flavor_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_vdu_image_dict(grant_info)
cpd_vl_dict = UserDataUtil.create_cpd_vl_dict(
base_hot_dict, inst_req_info)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return final_param_dict

@ -0,0 +1,26 @@
#
# 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.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
# return non dict value
return 'DUMMY'

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor:
get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_lcm_with_user_data_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

@ -0,0 +1,39 @@
#
# 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 tacker.vnfm.lcm_user_data.utils as UserDataUtil
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
# Create HOT input parameter using util functions.
initial_param_dict = UserDataUtil.create_initial_param_dict(
base_hot_dict)
vdu_flavor_dict = UserDataUtil.create_vdu_flavor_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_vdu_image_dict(grant_info)
cpd_vl_dict = UserDataUtil.create_cpd_vl_dict(
base_hot_dict, inst_req_info)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return final_param_dict

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor:
get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_lcm_with_user_data_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

@ -0,0 +1,39 @@
#
# 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 tacker.vnfm.lcm_user_data.utils as UserDataUtil
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
# Create HOT input parameter using util functions.
initial_param_dict = UserDataUtil.create_initial_param_dict(
base_hot_dict)
# vdu_flavor_dict = UserDataUtil.create_vdu_flavor_dict(vnfd_dict)
# vdu_image_dict = UserDataUtil.create_vdu_image_dict(grant_info)
# cpd_vl_dict = UserDataUtil.create_cpd_vl_dict(
# base_hot_dict, inst_req_info)
#
# final_param_dict = UserDataUtil.create_final_param_dict(
# initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return initial_param_dict

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor:
get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_lcm_with_user_data_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

@ -0,0 +1,28 @@
#
# 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.extensions import vnfm
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
error_reason = _(
"invalid user data script.")
raise vnfm.LCMUserDataFailed(reason=error_reason)

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor:
get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_lcm_with_user_data_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor:
get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,7 @@
TOSCA-Meta-File-Version: 1.0
Created-by: Dummy User
CSAR-Version: 1.1
Entry-Definitions: Definitions/sample_lcm_with_user_data_top.vnfd.yaml
Name: Files/images/cirros-0.4.0-x86_64-disk.img
Content-type: application/x-iso9066-image

@ -0,0 +1,51 @@
#
# 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 time
from oslo_log import log as logging
import tacker.vnfm.lcm_user_data.utils as UserDataUtil
from tacker.vnfm.lcm_user_data.abstract_user_data import AbstractUserData
from tacker.vnfm.lcm_user_data.constants import USER_DATA_TIMEOUT
LOG = logging.getLogger(__name__)
class SampleUserData(AbstractUserData):
@staticmethod
def instantiate(base_hot_dict=None,
vnfd_dict=None,
inst_req_info=None,
grant_info=None):
# Sleep more than timeout.
LOG.debug('Sleep start.')
time.sleep(USER_DATA_TIMEOUT + 60)
LOG.debug('Sleep end.')
# Create HOT input parameter using util functions.
initial_param_dict = UserDataUtil.create_initial_param_dict(
base_hot_dict)
vdu_flavor_dict = UserDataUtil.create_vdu_flavor_dict(vnfd_dict)
vdu_image_dict = UserDataUtil.create_vdu_image_dict(grant_info)
cpd_vl_dict = UserDataUtil.create_cpd_vl_dict(
base_hot_dict, inst_req_info)
final_param_dict = UserDataUtil.create_final_param_dict(
initial_param_dict, vdu_flavor_dict, vdu_image_dict, cpd_vl_dict)
return final_param_dict

@ -0,0 +1,32 @@
heat_template_version: 2013-05-23
description: 'Template for test _generate_hot_from_tosca().'
parameters:
nfv:
type: json
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor:
get_resource: VDU1_flavor
name: VDU1
image: { get_param: [ nfv, VDU, VDU1, image ] }
networks:
- port:
get_resource: CP1
CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, CP1, network ] }
VDU1_flavor:
type: OS::Nova::Flavor
properties:
ram: { get_param: [ nfv, VDU, VDU1, flavor, ram ] }
vcpus: { get_param: [ nfv, VDU, VDU1, flavor, vcpus ] }
disk: { get_param: [ nfv, VDU, VDU1, flavor, disk ] }
outputs: {}

@ -0,0 +1,34 @@
{
"flavourId": "simple",
"instantiationLevelId": "instantiation_level_1",
"extVirtualLinks": [
{
"id": "ext-vl-uuid-VL1",
"resourceId": "neutron-network-uuid_VL1",
"extCps": [
{
"cpdId": "CP1",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET"
}
]
}
]
}
]
}
],
"vimConnectionInfo": [
{
"id": "vim-uuid",
"vimType": "openstack"
}
],
"additionalParams":{
"lcm-operation-user-data":"./UserData/lcm_user_data.py",
"lcm-operation-user-data-class":"SampleUserData"
}
}

@ -0,0 +1,94 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: >
Template for test _generate_hot_from_tosca().
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- sample_lcm_with_user_data_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: ntt.nslab.VNF
properties:
flavour_id: simple
requirements:
virtual_link_external: [ CP1, virtual_link ]
node_templates:
VNF:
type: ntt.nslab.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
instantiate: []
instantiate_start: []
instantiate_end: []
terminate: []
terminate_start: []
terminate_end: []
modify_information: []
modify_information_start: []
modify_information_end: []
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: cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e
container_format: bare
disk_format: qcow2
min_disk: 1 GiB
size: 1 GiB
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 MiB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 1 GiB
CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1

@ -0,0 +1,501 @@
#
# 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 tempfile
import time
import yaml
import zipfile
from oslo_serialization import jsonutils
from oslo_utils import uuidutils
from tacker.objects import fields
from tacker.tests.functional import base
from tacker.tests import utils
VNF_PACKAGE_UPLOAD_TIMEOUT = 60
VNF_INSTANTIATE_TIMEOUT = 60
VNF_TERMINATE_TIMEOUT = 60
VNF_INSTANTIATE_ERROR_WAIT = 80
VNF_DELETE_COMPLETION_WAIT = 60
def _get_external_virtual_links(net0_id):
return [
{
"id": "net0",
"resourceId": net0_id,
"extCps": [{
"cpdId": "CP1",
"cpConfig": [{
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
}]
}]
}]
}
]
def _create_csar_with_unique_vnfd_id(csar_dir):
unique_id = uuidutils.generate_uuid()
tempfd, tempname = tempfile.mkstemp(suffix=".zip",
dir=os.path.dirname(csar_dir))
os.close(tempfd)
common_dir = os.path.join(csar_dir, "../common/")
ud_common_dir = os.path.join(csar_dir, "../user_data_common/")
zcsar = zipfile.ZipFile(tempname, 'w')
target_dir_list = [csar_dir, common_dir, ud_common_dir]
_write_zipfile(zcsar, unique_id, target_dir_list)
zcsar.close()
return tempname, unique_id
def _write_zipfile(zcsar, unique_id, target_dir_list):
for target_dir in target_dir_list:
for (dpath, _, fnames) in os.walk(target_dir):
if not fnames:
continue
for fname in fnames:
src_file = os.path.join(dpath, fname)
dst_file = os.path.relpath(
os.path.join(dpath, fname), target_dir)
if fname.endswith('.yaml') or fname.endswith('.yml'):
with open(src_file, 'rb') as yfile:
data = yaml.safe_load(yfile)
utils._update_unique_id_in_yaml(data, unique_id)
zcsar.writestr(dst_file, yaml.dump(
data, default_flow_style=False,
allow_unicode=True))
else:
zcsar.write(src_file, dst_file)
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 = _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 VnfLcmWithUserDataTest(base.BaseTackerTest):
def setUp(self):
super(VnfLcmWithUserDataTest, self).setUp()
self.tacker_client = base.BaseTackerTest.tacker_http_client()
self.base_url = "/vnflcm/v1/vnf_instances"
vim_list = self.client.list_vims()
self.vim = self.get_vim(vim_list, 'VIM0')
if not self.vim:
assert False, "vim_list is Empty: Default VIM is missing"
neutron_client = self.neutronclient()
net = neutron_client.list_networks()
networks = {}
for network in net['networks']:
networks[network['name']] = network['id']
net0_id = networks.get('net0')
if not net0_id:
self.fail("net0 network is not available")
self.ext_vl = _get_external_virtual_links(net0_id)
def _create_instantiate_vnf_request_body(self, flavour_id,
instantiation_level_id=None, vim_id=None, ext_vl=None,
add_params=None):
request_body = {"flavourId": flavour_id}
if instantiation_level_id:
request_body["instantiationLevelId"] = instantiation_level_id
if ext_vl:
request_body["extVirtualLinks"] = ext_vl
if vim_id:
request_body["vimConnectionInfo"] = [
{"id": uuidutils.generate_uuid(),
"vimId": vim_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2"}]
if add_params:
request_body["additionalParams"] = add_params
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_vnf_instance(self, id):
url = os.path.join(self.base_url, id)
resp, body = self.http_client.do_request(url, "DELETE")
self.assertEqual(204, resp.status_code)
# 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(5)
def _vnf_instance_wait_until_fail_detected(self, id,
instantiation_state=fields.VnfInstanceState.NOT_INSTANTIATED,
timeout=VNF_INSTANTIATE_ERROR_WAIT):
show_url = os.path.join(self.base_url, id)
time.sleep(VNF_INSTANTIATE_ERROR_WAIT)
resp, body = self.http_client.do_request(show_url, "GET")
if body['instantiationState'] != instantiation_state:
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})
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 _instantiate_vnf_instance_fail(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)
# Confirm that the state doesn't change from NOT_INSTANTIATED.
self._vnf_instance_wait_until_fail_detected(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")
# wait for status completion
time.sleep(VNF_DELETE_COMPLETION_WAIT)
def _delete_vnf_package(self, vnf_package_id):
url = '/vnfpkgm/v1/vnf_packages/%s' % vnf_package_id
# Update vnf package before delete
req_body = jsonutils.dumps({"operationalState": "DISABLED"})
self.tacker_client.do_request(url, "PATCH", body=req_body)
# Delete vnf package before delete
self.tacker_client.do_request(url, "DELETE")
def test_instantiate_vnf_normal(self):
# Create vnf package
sample_name = "user_data_sample_normal"
vnf_package_id, vnfd_id = _create_and_upload_vnf_package(
self.tacker_client, sample_name, {"key": sample_name})
# Reserve deleting vnf package
self.addCleanup(self._delete_vnf_package, vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_normal"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(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)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = self._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'], ext_vl=self.ext_vl, add_params=add_params)
self._instantiate_vnf_instance(vnf_instance['id'], request_body)
vnf_instance = self._show_vnf_instance(vnf_instance['id'])
vdu_count = len(vnf_instance['instantiatedVnfInfo']
['vnfcResourceInfo'])
self.assertEqual(1, vdu_count)
# Terminate vnf forcefully
terminate_req_body = {
"terminationType": fields.VnfInstanceTerminationType.FORCEFUL
}
self._terminate_vnf_instance(vnf_instance['id'], terminate_req_body)
def test_instantiate_vnf_basehot_invalid(self):
# Create vnf package
sample_name = "user_data_sample_basehot_invalid"
vnf_package_id, vnfd_id = _create_and_upload_vnf_package(
self.tacker_client, sample_name, {"key": sample_name})
# Reserve deleting vnf package
self.addCleanup(self._delete_vnf_package, vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_basehot_invalid"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(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)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = self._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'], ext_vl=self.ext_vl, add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_timeout(self):
# Create vnf package
sample_name = "user_data_sample_userdata_timeout"
vnf_package_id, vnfd_id = _create_and_upload_vnf_package(
self.tacker_client, sample_name, {"key": sample_name})
# Reserve deleting vnf package
self.addCleanup(self._delete_vnf_package, vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data_sleeping.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(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)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = self._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'], ext_vl=self.ext_vl, add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_invalid_hot_param(self):
# Create vnf package
sample_name = "user_data_sample_userdata_invalid_hot_param"
vnf_package_id, vnfd_id = _create_and_upload_vnf_package(
self.tacker_client, sample_name, {"key": sample_name})
# Reserve deleting vnf package
self.addCleanup(self._delete_vnf_package, vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/"
"lcm_user_data_invalid_hot_param.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(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)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = self._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'], ext_vl=self.ext_vl, add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_none(self):
# Create vnf package
sample_name = "user_data_sample_userdata_none"
vnf_package_id, vnfd_id = _create_and_upload_vnf_package(
self.tacker_client, sample_name, {"key": sample_name})
# Reserve deleting vnf package
self.addCleanup(self._delete_vnf_package, vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/lcm_user_data.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(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)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = self._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'], ext_vl=self.ext_vl, add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)
def test_instantiate_vnf_userdata_invalid_script(self):
# Create vnf package
sample_name = "user_data_sample_userdata_invalid_script"
vnf_package_id, vnfd_id = _create_and_upload_vnf_package(
self.tacker_client, sample_name, {"key": sample_name})
# Reserve deleting vnf package
self.addCleanup(self._delete_vnf_package, vnf_package_id)
# Settings
vnf_instance_name = "vnf_with_user_data-%s" % \
uuidutils.generate_uuid()
vnf_instance_description = "vnf_with_user_data_timeout"
add_params = {
"lcm-operation-user-data": "./UserData/"
"lcm_user_data_invalid_script.py",
"lcm-operation-user-data-class": "SampleUserData"}
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance(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)
# Reserve deleting vnf instance
self.addCleanup(self._delete_vnf_instance, vnf_instance['id'])
request_body = self._create_instantiate_vnf_request_body("simple",
vim_id=self.vim['id'], ext_vl=self.ext_vl, add_params=add_params)
self._instantiate_vnf_instance_fail(vnf_instance['id'], request_body)

@ -14,16 +14,20 @@
# under the License.
import ddt
import importlib
import json
import mock
import os
import requests
import tempfile
import yaml
from tacker.common import exceptions
from tacker import context
from tacker.extensions import vnfm