tacker/tacker/tests/functional/sol_v2/test_vnflcm_basic.py

1620 lines
67 KiB
Python

# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# 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 ddt
import os
import time
from tacker.objects import fields
from tacker.tests.functional.sol_v2_common import base_v2
from tacker.tests.functional.sol_v2_common import paramgen
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
@ddt.ddt
class VnfLcmTest(test_vnflcm_basic_common.CommonVnfLcmTest):
@classmethod
def setUpClass(cls):
super(VnfLcmTest, cls).setUpClass()
cur_dir = os.path.dirname(__file__)
# tacker/tests/etc...
# /functional/sol_v2
image_dir = os.path.join(
cur_dir, "../../etc/samples/etsi/nfv/common/Files/images")
image_file = "cirros-0.5.2-x86_64-disk.img"
image_path = os.path.abspath(os.path.join(image_dir, image_file))
# for basic lcms tests max pattern
basic_lcms_max_path = os.path.join(cur_dir, "../sol_v2_common/"
"samples/basic_lcms_max")
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
basic_lcms_max_path, image_path=image_path)
# for basic lcms tests min pattern
basic_lcms_min_path = os.path.join(cur_dir, "../sol_v2_common/"
"samples/basic_lcms_min")
# no image contained
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
basic_lcms_min_path)
# for update vnf test
update_vnf_path = os.path.join(cur_dir, "../sol_v2_common/"
"samples/update_vnf")
# no image contained
cls.vnf_pkg_3, cls.vnfd_id_3 = cls.create_vnf_package(update_vnf_path)
@classmethod
def tearDownClass(cls):
super(VnfLcmTest, cls).tearDownClass()
cls.delete_vnf_package(cls.vnf_pkg_1)
cls.delete_vnf_package(cls.vnf_pkg_2)
cls.delete_vnf_package(cls.vnf_pkg_3)
def setUp(self):
super().setUp()
def test_api_versions(self):
"""Test version operations
* About version operations:
This test includes the following operations.
- 1. List VNFLCM API versions
- 2. Show VNFLCM API versions
"""
path = "/vnflcm/api_versions"
resp, body = self.tacker_client.do_request(
path, "GET", version="2.0.0")
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
expected_body = {
"uriPrefix": "/vnflcm",
"apiVersions": [
{'version': '1.3.0', 'isDeprecated': False},
{'version': '2.0.0', 'isDeprecated': False}
]
}
self.assertEqual(body, expected_body)
path = "/vnflcm/v2/api_versions"
resp, body = self.tacker_client.do_request(
path, "GET", version="2.0.0")
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
expected_body = {
"uriPrefix": "/vnflcm/v2",
"apiVersions": [
{'version': '2.0.0', 'isDeprecated': False}
]
}
self.assertEqual(body, expected_body)
@ddt.data(True, False)
def test_subscriptions(self, is_all):
"""Test subscription operations
* About attributes:
- is_all=True
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
- is_all=False
Omit except for required attributes.
Only the following cardinality attributes are set.
- 1
- 1..N (1)
* About subscription operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create a new subscription
- 2. Show subscription
- 3. List subscription with attribute-based filtering
- 4. Delete a subscription
"""
# 0. Pre-setting
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_min(callback_uri)
if is_all:
sub_req = paramgen.sub_create_max(callback_uri)
# 1. Create a new subscription
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Show subscription
expected_attrs = [
'id', 'callbackUri', 'verbosity', '_links'
]
if is_all:
additional_attrs = ['filter']
expected_attrs.extend(additional_attrs)
resp, body = self.show_subscription(sub_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 3. List subscription with attribute-based filtering
filter_expr = {'filter': '(eq,id,%s)' % sub_id}
resp, body = self.list_subscriptions(filter_expr)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
for sbsc in body:
self.check_resp_body(sbsc, expected_attrs)
# 4. Delete a subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_basic_lcms_max(self):
self.basic_lcms_max_common_test()
def test_basic_lcms_min(self):
self.basic_lcms_min_common_test()
def test_update_scale_lcm(self):
"""Test the sequence of update VNF and scale out
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Update VNF
- 6. Show VNF instance
- 7. Scale out operation
- 8. Terminate VNF
- 9. Delete VNF instance
- 10. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.vnfd_id_1)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Update VNF
# check attribute value before update VNF
# check usageState of VNF Package 1
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check usageState of VNF Package 3
usage_state = self.get_vnf_package(self.vnf_pkg_3)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# check vnfd id
self.assertEqual(self.vnfd_id_1, body['vnfdId'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.vnfd_id_3, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check usageState of VNF Package 1
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# check usageState of VNF Package 3
usage_state = self.get_vnf_package(self.vnf_pkg_3)['usageState']
self.assertEqual('IN_USE', usage_state)
# check the specified attribute after update VNF
self.assertEqual(self.vnfd_id_3, body['vnfdId'])
self.assertEqual('new name', body['vnfInstanceName'])
self.assertEqual('new description', body['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, body['metadata'])
self.assertEqual(dummy_key_value, body['extensions'])
self.assertEqual(dummy_key_value, body['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
body['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 7. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 8. Terminate a VNF instance
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# wait a bit because there is a bit time lag between lcmocc DB
# update and terminate completion.
time.sleep(10)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 9. Delete a VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 10. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_update_heal_lcm(self):
"""Test the sequence of update VNF and heal VNF
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Update VNF
- 6. Show VNF instance
- 7. Heal VNF(all with omit all parameter)
- 8. Heal VNF(all with all=False parameter)
- 9. Heal VNF(all with all=True parameter)
- 10. Terminate VNF
- 11. Delete VNF instance
- 12. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.vnfd_id_1)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
# check creation of Heat-stack
stack_name = f'vnf-{inst_id}'
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Update VNF
# check attribute value before update VNF
# check usageState of VNF Package 1
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check usageState of VNF Package 3
usage_state = self.get_vnf_package(self.vnf_pkg_3)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# check vnfd id
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(self.vnfd_id_1, body['vnfdId'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_ids = [vnfc['id'] for vnfc in vnfc_info]
for vnfc in vnfc_info:
self.assertIn('id', vnfc)
self.assertIn('vduId', vnfc)
self.assertIsNotNone(vnfc.get('vnfcState'))
self.assertIsNone(vnfc.get('vnfcConfigurableProperties'))
update_req = paramgen.update_vnf_max(self.vnfd_id_3, vnfc_ids)
resp, body = self.update_vnf_instance(inst_id, update_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Show VNF instance
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check usageState of VNF Package 1
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('NOT_IN_USE', usage_state)
# check usageState of VNF Package 3
usage_state = self.get_vnf_package(self.vnf_pkg_3)['usageState']
self.assertEqual('IN_USE', usage_state)
# check the specified attribute after update VNF
self.assertEqual(self.vnfd_id_3, body['vnfdId'])
self.assertEqual('new name', body['vnfInstanceName'])
self.assertEqual('new description', body['vnfInstanceDescription'])
dummy_key_value = {'dummy-key': 'dummy-value'}
self.assertEqual(dummy_key_value, body['metadata'])
self.assertEqual(dummy_key_value, body['extensions'])
self.assertEqual(dummy_key_value, body['vnfConfigurableProperties'])
vim_connection_info = {
"vim2": {
"vimId": "ac2d2ece-5e49-4b15-b92d-b681e9c096d8",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity/v3"
},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {
"dummy-key": "dummy-val"
}
}
}
self.assertEqual(vim_connection_info['vim2'],
body['vimConnectionInfo']['vim2'])
# check vnfc info
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertEqual(vnfc_ids[0], vnfc_info[0]['id'])
self.assertEqual(vnfc_ids[1], vnfc_info[1]['id'])
self.assertEqual(dummy_key_value,
vnfc_info[0]['vnfcConfigurableProperties'])
self.assertEqual(dummy_key_value,
vnfc_info[1]['vnfcConfigurableProperties'])
# 7. Heal VNF(all with omit all parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
heal_req = paramgen.heal_vnf_all_max_with_parameter()
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
# 8. Heal VNF(all with all=False parameter)
vdu1_stack_before_heal = vdu1_stack_after_heal
vdu2_stack_before_heal = vdu2_stack_after_heal
heal_req = paramgen.heal_vnf_all_max_with_parameter(False)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
# 9. Heal VNF(all with all=True parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
stack_id_before_heal = self.heat_client.get_stack_resource(stack_name)[
'stack']['id']
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_id_after_heal = self.heat_client.get_stack_resource(stack_name)[
'stack']['id']
self.assertNotEqual(stack_id_before_heal, stack_id_after_heal)
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
network_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage1_stack_before_heal['physical_resource_id'],
storage1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage2_stack_before_heal['physical_resource_id'],
storage2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(network_stack_before_heal['physical_resource_id'],
network_stack_after_heal['physical_resource_id'])
# 10. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# wait a bit because there is a bit time lag between lcmocc DB
# update and terminate completion.
time.sleep(10)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 11. Delete VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 12. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_scale_heal_lcm(self):
"""Test the sequence of scale out/in and heal VNF
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Scale out operation
- 6. Heal VNF(vnfc)
- 7. Scale out operation
- 8. Scale in operation
- 9. Scale in operation
- 10. Heal VNF(vnfc)
- 11. Terminate VNF
- 12. Delete VNF instance
- 13. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.vnfd_id_1)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
# check creation of Heat-stack
stack_name = f'vnf-{inst_id}'
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo',
'extensions',
'vnfConfigurableProperties'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Heal VNF(vnfc)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] == 'VDU1')]
vdu1_stack_before_heal = temp_stacks[0]
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# There was 2 VDU1 because scale out
vnfc_id = f'VDU1-{vdu1_stack_before_heal["physical_resource_id"]}'
heal_req = paramgen.heal_vnf_vnfc_max(vnfc_id)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] == 'VDU1')]
vdu1_stack_after_heal = temp_stacks[0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
# 7. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 8. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 9. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 10. Heal VNF(vnfc)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] == 'VDU1')]
vdu1_stack_before_heal = temp_stacks[0]
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
vnfc_info = body['instantiatedVnfInfo']['vnfcInfo']
self.assertGreater(len(vnfc_info), 1)
vnfc_id = [vnfc['id'] for vnfc in vnfc_info if (
"VDU1" == vnfc['vduId'])][0]
self.assertIsNotNone(vnfc_id)
heal_req = paramgen.heal_vnf_vnfc_max(vnfc_id)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] == 'VDU1')]
vdu1_stack_after_heal = temp_stacks[0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
# 11. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# wait a bit because there is a bit time lag between lcmocc DB
# update and terminate completion.
time.sleep(10)
# check deletion of Heat-stack
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertIsNone(stack_status)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 12. Delete VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 13. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
def test_scale_other_lcm(self):
"""Test the sequence of scale out/in and the other LCM operations
The change_ext_conn can't be tested in test_basic_lcms_min method
because VNF package 2 don't have external connectivity.
So moved it here to test min pattern change_ext_conn.
* About attributes:
All of the following cardinality attributes are set.
In addition, 0..N or 1..N attributes are set to 2 or more.
0..1 is set to 1.
- 0..1 (1)
- 0..N (2 or more)
- 1..N (2 or more)
* About LCM operations:
This test includes the following operations.
- 0. Pre-setting
- 1. Create subscription
- 2. Create VNF instance
- 3. Instantiate VNF
- 4. Show VNF instance
- 5. Scale out operation
- 6. Scale out operation
- 7. Show VNF instance
- 8. Scale in operation
- 9. Show VNF instance
- 10. Heal VNF(all with all=True parameter)
- 11. Scale in operation
- 12. Scale out operation
- 13. Heal VNF(all with all=True parameter)
- 14. Change external connectivity
- 15. Show VNF LCM operation occurrence
- 16. Terminate VNF
- 17. Delete VNF instance
- 18. Delete subscription
"""
# 0. Pre-setting
# Create a new network and subnet to check the IP allocation of
# IPv4 and IPv6
ft_net0_name = 'ft-net0'
ft_net0_subs = {
'ft-ipv4-subnet0': {
'range': '100.100.100.0/24',
'ip_version': 4
},
'ft-ipv6-subnet0': {
'range': '1111:2222:3333::/64',
'ip_version': 6
}
}
ft_net0_id = self.create_network(ft_net0_name)
self.addCleanup(self.delete_network, ft_net0_id)
for sub_name, val in ft_net0_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net0_id, sub_name, val['range'], val['ip_version'])
# Create a new network for change external connectivity
ft_net1_name = 'ft-net1'
ft_net1_subs = {
'ft-ipv4-subnet1': {
'range': '22.22.22.0/24',
'ip_version': 4
},
'ft-ipv6-subnet1': {
'range': '1111:2222:4444::/64',
'ip_version': 6
}
}
ft_net1_id = self.create_network(ft_net1_name)
self.addCleanup(self.delete_network, ft_net1_id)
for sub_name, val in ft_net1_subs.items():
# subnet is automatically deleted with network deletion
self.create_subnet(
ft_net1_id, sub_name, val['range'], val['ip_version'])
net_ids = self.get_network_ids(
['net0', 'net1', 'net_mgmt', 'ft-net0', 'ft-net1'])
subnet_ids = self.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0',
'ft-ipv4-subnet1', 'ft-ipv6-subnet1'])
port_names = ['VDU2_CP1-1', 'VDU2_CP1-2']
port_ids = {}
for port_name in port_names:
port_id = self.create_port(net_ids['net0'], port_name)
port_ids[port_name] = port_id
self.addCleanup(self.delete_port, port_id)
# 1. Create subscription
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
callback_uri = ('http://localhost:'
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
f'{callback_url}')
sub_req = paramgen.sub_create_max(callback_uri)
resp, body = self.create_subscription(sub_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
sub_id = body['id']
# 2. Create VNF instance
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance
# NOTE: extensions and vnfConfigurableProperties are omitted
# because they are commented out in etsi_nfv_sol001.
expected_inst_attrs = [
'id',
'vnfInstanceName',
'vnfInstanceDescription',
'vnfdId',
'vnfProvider',
'vnfProductName',
'vnfSoftwareVersion',
'vnfdVersion',
# 'vnfConfigurableProperties', # omitted
# 'vimConnectionInfo', # omitted
'instantiationState',
# 'instantiatedVnfInfo', # omitted
'metadata',
# 'extensions', # omitted
'_links'
]
create_req = paramgen.create_vnf_max(self.vnfd_id_1)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
self.check_resp_body(body, expected_inst_attrs)
inst_id = body['id']
# check usageState of VNF Package
usage_state = self.get_vnf_package(self.vnf_pkg_1)['usageState']
self.assertEqual('IN_USE', usage_state)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 3. Instantiate VNF instance
instantiate_req = paramgen.instantiate_vnf_max(
net_ids, subnet_ids, port_ids, self.auth_url)
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 4. Show VNF instance
# check creation of Heat-stack
stack_name = f'vnf-{inst_id}'
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
additional_inst_attrs = [
'vimConnectionInfo',
'instantiatedVnfInfo',
'extensions',
'vnfConfigurableProperties'
]
expected_inst_attrs.extend(additional_inst_attrs)
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check instantiationState of VNF
self.assertEqual(fields.VnfInstanceState.INSTANTIATED,
body['instantiationState'])
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 5. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 6. Scale out operation
# get nested stack count before scale out
nested_stacks = self.heat_client.get_resources(stack_name)
count_before_scaleout = len(nested_stacks)
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 7. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check scaleStatus
scale_status = body['instantiatedVnfInfo']['scaleStatus']
self.assertGreater(len(scale_status), 0)
for status in scale_status:
self.assertIn('aspectId', status)
self.assertIn('scaleLevel', status)
# check creation of Heat-stack
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
# get nested stack count after scale out
nested_stacks = self.heat_client.get_resources(stack_name)
count_after_scaleout = len(nested_stacks)
# check nested stack was created
# 9 was the sum of 1 VM, 1 Volume, 1 VolumeType, 5 CPs,
# 1 stack(VDU1.yaml)
self.assertEqual(9, count_after_scaleout - count_before_scaleout)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# 8. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 9. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_inst_attrs)
# check vnfState of VNF
self.assertEqual(fields.VnfOperationalStateType.STARTED,
body['instantiatedVnfInfo']['vnfState'])
# check scaleStatus
scale_status = body['instantiatedVnfInfo']['scaleStatus']
self.assertGreater(len(scale_status), 0)
for status in scale_status:
self.assertIn('aspectId', status)
self.assertIn('scaleLevel', status)
# check creation of Heat-stack
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("UPDATE_COMPLETE", stack_status)
# get nested stack count after scale in
nested_stacks = self.heat_client.get_resources(stack_name)
count_after_scalein = len(nested_stacks)
# check nested stack was deleted
# 9 was the sum of 1 VM, 1 Volume, 1 VolumeType, 5 CPs,
# 1 stack(VDU1.yaml)
self.assertEqual(9, count_after_scaleout - count_after_scalein)
# 10. Heal VNF(all with all=True parameter))
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
stack_id_before_heal = self.heat_client.get_stack_resource(stack_name)[
'stack']['id']
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_id_after_heal = self.heat_client.get_stack_resource(stack_name)[
'stack']['id']
self.assertNotEqual(stack_id_before_heal, stack_id_after_heal)
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
network_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage1_stack_before_heal['physical_resource_id'],
storage1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage2_stack_before_heal['physical_resource_id'],
storage2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(network_stack_before_heal['physical_resource_id'],
network_stack_after_heal['physical_resource_id'])
# 11. Scale in operation
scalein_req = paramgen.scalein_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scalein_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 12. Scale out operation
scaleout_req = paramgen.scaleout_vnf_max()
resp, body = self.scale_vnf_instance(inst_id, scaleout_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# 13. Heal VNF(all with all=True parameter)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_before_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
stack_id_before_heal = self.heat_client.get_stack_resource(stack_name)[
'stack']['id']
heal_req = paramgen.heal_vnf_all_max_with_parameter(True)
resp, body = self.heal_vnf_instance(inst_id, heal_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# check stack info
stack_id_after_heal = self.heat_client.get_stack_resource(stack_name)[
'stack']['id']
self.assertNotEqual(stack_id_before_heal, stack_id_after_heal)
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertEqual("CREATE_COMPLETE", stack_status)
nested_stacks = self.heat_client.get_resources(stack_name)
temp_stacks = [stack for stack in nested_stacks if
(stack['resource_name'] in ['VDU1', 'VDU2', 'VDU1-VirtualStorage',
'VDU2-VirtualStorage', 'internalVL3'])]
vdu1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1')][0]
vdu2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2')][0]
storage1_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU1-VirtualStorage')][0]
storage2_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'VDU2-VirtualStorage')][0]
network_stack_after_heal = [stack for stack in temp_stacks if
(stack['resource_name'] == 'internalVL3')][0]
self.assertEqual("CREATE_COMPLETE",
vdu1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
vdu2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage1_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
storage2_stack_after_heal['resource_status'])
self.assertEqual("CREATE_COMPLETE",
network_stack_after_heal['resource_status'])
self.assertNotEqual(vdu1_stack_before_heal['physical_resource_id'],
vdu1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(vdu2_stack_before_heal['physical_resource_id'],
vdu2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage1_stack_before_heal['physical_resource_id'],
storage1_stack_after_heal['physical_resource_id'])
self.assertNotEqual(storage2_stack_before_heal['physical_resource_id'],
storage2_stack_after_heal['physical_resource_id'])
self.assertNotEqual(network_stack_before_heal['physical_resource_id'],
network_stack_after_heal['physical_resource_id'])
# 14. Change external connectivity
stack_id = self.heat_client.get_stack_resource(stack_name)['stack'][
'id']
port_info = self.heat_client.get_resource_info(
f"{stack_name}/{stack_id}", 'VDU2_CP2')
before_physical_resource_id = port_info['physical_resource_id']
before_fixed_ips = port_info['attributes']['fixed_ips']
change_ext_conn_req = paramgen.change_ext_conn_min(net_ids, subnet_ids)
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
port_info = self.heat_client.get_resource_info(
f"{stack_name}/{stack_id}", 'VDU2_CP2')
after_physical_resource_id = port_info['physical_resource_id']
after_fixed_ips = port_info['attributes']['fixed_ips']
self.assertNotEqual(before_physical_resource_id,
after_physical_resource_id)
self.assertNotEqual(before_fixed_ips, after_fixed_ips)
# 15. Show VNF LCM operation occurrence
# ETSI NFV SOL003 v3.3.1 5.5.2.13 VnfLcmOpOcc
# NOTE: omitted values are not supported at that time
expected_attrs = [
'id',
'operationState',
'stateEnteredTime',
'startTime',
'vnfInstanceId',
# 'grantId', # omitted
'operation',
'isAutomaticInvocation',
'operationParams',
'isCancelPending',
# 'cancelMode', # omitted
# 'error', # omitted
'resourceChanges',
# 'changedInfo', # omitted
# 'changedExtConnectivity', # omitted
# 'modificationsTriggeredByVnfPkgChange', # omitted
# 'vnfSnapshotInfoId', # omitted
'_links'
]
resp, body = self.show_lcmocc(lcmocc_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.check_resp_body(body, expected_attrs)
# 16. Terminate VNF
terminate_req = paramgen.terminate_vnf_max()
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
self.assertEqual(202, resp.status_code)
self.check_resp_headers_in_operation_task(resp)
lcmocc_id = os.path.basename(resp.headers['Location'])
self.wait_lcmocc_complete(lcmocc_id)
# wait a bit because there is a bit time lag between lcmocc DB
# update and terminate completion.
time.sleep(10)
# check deletion of Heat-stack
stack_status, _ = self.heat_client.get_status(stack_name)
self.assertIsNone(stack_status)
# check instantiationState of VNF
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
body['instantiationState'])
# 17. Delete VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
# 18. Delete subscription
resp, body = self.delete_subscription(sub_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)