Improve Unit Test for V2 API

In the existing v2 unit test, there are many public methods that
are not tested. This led to bugs in some code and also increased
rework in FT.

This patch adds tests to some public methods and improves some
existing tests. Also fixed several minor issues checked out in UT.

Change-Id: I15b45d99646babbb57606dbb46aa156aa9dfb51c
This commit is contained in:
Shuwen Cai 2022-07-04 14:51:44 +09:00
parent fec4f537fb
commit 1d9321498f
21 changed files with 6410 additions and 709 deletions

View File

@ -171,7 +171,7 @@ def match_inst_subsc_filter(inst_filter, inst):
return False
if inst_filter.obj_attr_is_set('vnfInstanceNames'):
if inst.vnfInstanceNames not in inst_filter.vnfInstanceNames:
if inst.vnfInstanceName not in inst_filter.vnfInstanceNames:
return False
return True

View File

@ -77,7 +77,10 @@ class Vnfd(object):
def delete(self):
if self.csar_dir_is_tmp:
shutil.rmtree(self.csar_dir)
try:
shutil.rmtree(self.csar_dir)
except Exception:
LOG.exception("rmtree %s failed", self.csar_dir)
def get_vnfd_flavour(self, flavour_id):
if flavour_id in self.vnfd_flavours:
@ -336,6 +339,8 @@ class Vnfd(object):
def get_interface_script(self, flavour_id, operation):
vnfd = self.get_vnfd_flavour(flavour_id)
if not vnfd:
return
nodes = (vnfd.get('topology_template', {})
.get('node_templates', {}))
for node in nodes.values():

View File

@ -39,7 +39,9 @@ class TestCommontScriptUtils(base.BaseTestCase):
expected_result = {
'VDU': {
'VDU1': {'computeFlavourId': None},
'VDU1': {'computeFlavourId': None,
'desired_capacity': None,
'locationConstraints': None},
'VirtualStorage': {'vcImageId': None},
'VDU2': {'computeFlavourId': None, 'vcImageId': None}
},
@ -56,6 +58,9 @@ class TestCommontScriptUtils(base.BaseTestCase):
}
result = common_script_utils.init_nfv_dict(top_hot)
self.assertEqual(expected_result, result)
self.assertIsNone(result['CP'].get('VDU1_CP6'))
self.assertIsNone(result['CP'].get('VDU1_CP7'))
self.assertIsNone(result['CP'].get('VDU1_CP8'))
def test_get_param_flavor(self):
flavor = 'm1.large'
@ -79,6 +84,24 @@ class TestCommontScriptUtils(base.BaseTestCase):
self.vnfd_1, grant)
self.assertEqual('m1.tiny', result)
def test_get_param_flavor_no_compute_resource_flavours(self):
grant = {
'vimAssets': {
}
}
# if not exist in grant, get from VNFD
result = common_script_utils.get_param_flavor(
'VDU1', SAMPLE_FLAVOUR_ID,
self.vnfd_1, grant)
self.assertEqual('m1.tiny', result)
# if not exist in grant, get from VNFD
result = common_script_utils.get_param_flavor(
'VDU2', SAMPLE_FLAVOUR_ID,
self.vnfd_1, grant)
self.assertEqual('m1.tiny', result)
def test_get_param_image(self):
image_id = 'f30e149d-b3c7-497a-8b19-a092bc81e47b'
grant = {
@ -96,6 +119,33 @@ class TestCommontScriptUtils(base.BaseTestCase):
self.vnfd_1, grant)
self.assertEqual(image_id, result)
def test_get_param_image_no_software_images(self):
grant = {
'vimAssets': {
}
}
result = common_script_utils.get_param_image('VDU2', SAMPLE_FLAVOUR_ID,
self.vnfd_1, grant)
self.assertEqual('VDU2-image', result)
def test_get_param_image_no_match_image(self):
image_id = 'f30e149d-b3c7-497a-8b19-a092bc81e47b'
grant = {
'vimAssets': {
'softwareImages': [
{'vnfdSoftwareImageId': 'VDU3',
'vimSoftwareImageId': image_id},
{'vnfdSoftwareImageId': 'VirtualStorage',
'vimSoftwareImageId': 'image-1.0.0-x86_64-disk'}
]
}
}
result = common_script_utils.get_param_image('VDU2', SAMPLE_FLAVOUR_ID,
self.vnfd_1, grant)
self.assertEqual('VDU2-image', result)
def test_get_param_zone(self):
grant_req = {
'addResources': [
@ -118,6 +168,75 @@ class TestCommontScriptUtils(base.BaseTestCase):
result = common_script_utils.get_param_zone('VDU1', grant_req, grant)
self.assertEqual('nova', result)
def test_get_param_zone_no_zones(self):
grant_req = {
'addResources': [
{'id': 'dd60c89a-29a2-43bc-8cff-a534515523df',
'type': 'COMPUTE', 'resourceTemplateId': 'VDU1'}
]
}
grant = {
'addResources': [
{'resourceDefinitionId':
'dd60c89a-29a2-43bc-8cff-a534515523df',
'zoneId': '717f6ae9-3094-46b6-b070-89ede8337571'}
]
}
common_script_utils.get_param_zone('VDU1', grant_req, grant)
def test_get_param_zone_no_add_resources(self):
grant_req = {
'addResources': [
{'id': 'dd60c89a-29a2-43bc-8cff-a534515523df',
'type': 'COMPUTE', 'resourceTemplateId': 'VDU1'}
]
}
grant = {
'zones': [
{'id': '717f6ae9-3094-46b6-b070-89ede8337571',
'zoneId': 'nova'}
]
}
common_script_utils.get_param_zone('VDU1', grant_req, grant)
def test_get_param_zone_no_zone_id(self):
grant_req = {
'addResources': [
{'id': 'dd60c89a-29a2-43bc-8cff-a534515523df',
'type': 'COMPUTE', 'resourceTemplateId': 'VDU1'},
{'id': 'e3ac628c-29a4-2878-b4a2-29aa685dcd70',
'type': 'COMPUTE', 'resourceTemplateId': 'VDU1'},
{'id': '36628ed5-6821-6f55-8c99-cbab0890fc71',
'type': 'COMPUTE', 'resourceTemplateId': 'VDU2'},
{'id': 'eed6860c-e9b2-ef79-5deb-89dee39785ec',
'type': 'COMPUTE', 'resourceTemplateId': 'VDU3'},
]
}
grant = {
'zones': [
{'id': '717f6ae9-3094-46b6-b070-89ede8337571',
'zoneId': 'nova'}
],
'addResources': [
{'resourceDefinitionId': 'eed6860c-e9b2-'
'ef79-5deb-89dee39785ec'},
{'resourceDefinitionId': 'e3ac628c-29a4-'
'2878-b4a2-29aa685dcd70',
'zoneId': '99171a93-d0b2-d2cd-83c1-b0694a3f771b'},
{'resourceDefinitionId': '36628ed5-6821-'
'6f55-8c99-cbab0890fc71',
'zoneId': '717f6ae9-3094-46b6-b070-89ede8337571'},
{'resourceDefinitionId': 'dd60c89a-29a2-'
'43bc-8cff-a534515523df',
'zoneId': '717f6ae9-3094-46b6-b070-89ede8337571'}
]
}
result = common_script_utils.get_param_zone('VDU1', grant_req, grant)
self.assertEqual('nova', result)
def test_get_param_capacity(self):
# test get_current_capacity at the same time
grant_req = {
@ -214,6 +333,81 @@ class TestCommontScriptUtils(base.BaseTestCase):
result = common_script_utils.get_param_fixed_ips('VDU2_CP2', {}, req)
self.assertEqual(expected_result, result)
def test_get_param_fixed_ips_other_cases(self):
ip_address = "10.10.1.101"
subnet_id = "9defebca-3e9c-4bd2-9fa0-c4210c56ece6"
ext_cp = {
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
ip_address
],
"subnetId": subnet_id
}
]
}
}
]
},
"VDU2_CP2_2": {
},
"VDU2_CP2_3": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET"
}
]
},
"VDU2_CP2_4": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {}
}
]
},
"VDU2_CP2_5": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4"
}
]
}
}
]
},
}
}
req = {
"extVirtualLinks": [
{
"id": "8b49f4b6-1ff9-4a03-99cf-ff445b788436",
"resourceId": "4c54f742-5f1d-4287-bb81-37bf2e6ddc3e",
"extCps": [ext_cp]
}
]
}
expected_result = [{'ip_address': ip_address, 'subnet': subnet_id}]
# no vls
common_script_utils.get_param_fixed_ips('VDU2_CP2', {}, {})
# with other cases
result = common_script_utils.get_param_fixed_ips('VDU2_CP2', {}, req)
self.assertEqual(expected_result, result)
def _inst_example_get_network_fixed_ips_from_inst(self):
ext_cp = {
"cpdId": "VDU2_CP2",

View File

@ -12,8 +12,10 @@
# 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 unittest import mock
from tacker import context
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
from tacker.sol_refactored import objects
from tacker.sol_refactored.objects.v2 import fields
@ -2108,6 +2110,31 @@ _expected_resource_changes_change_vnfpkg = {
]
}
# lcmocc_info_example
_error = {
'status': 1,
'detail': 'error'
}
_lcmocc_modify_value = {
'id': 'test-1',
'vnfInstanceId': 'instance-1',
'operation': 'MODIFY_INFO',
'operationState': 'PROCESSING',
'isAutomaticInvocation': False,
'startTime': '2021-01-22 13:41:03+00:00'
}
_lcmocc_inst_value = {
'id': 'test-2',
'vnfInstanceId': 'instance-2',
'operation': 'INSTANTIATE',
'operationState': 'COMPLETED',
'isAutomaticInvocation': False,
'startTime': '2021-01-23 13:41:03+00:00',
'resourceChanges': _expected_resource_changes_instantiate,
'changedInfo': _expected_changedInfo
}
class TestLcmOpOccUtils(base.BaseTestCase):
@ -2312,3 +2339,149 @@ class TestLcmOpOccUtils(base.BaseTestCase):
self.assertEqual(
_expected_resource_changes_change_vnfpkg,
self._sort_resource_changes(lcmocc['resourceChanges']))
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_lcmocc(self, mock_lcmocc):
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
operation='INSTANTIATE')
expected_result = objects.VnfLcmOpOccV2(
operation='INSTANTIATE')
result = lcmocc_utils.get_lcmocc(context, 'lcmocc_id')
self.assertEqual(expected_result.operation, result.operation)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_lcmocc_error(self, mock_lcmocc):
mock_lcmocc.return_value = None
self.assertRaises(
sol_ex.VnfLcmOpOccNotFound,
lcmocc_utils.get_lcmocc, context, 'lcmocc_id')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_lcmocc_all(self, mock_lcmocc):
mock_lcmocc.return_value = [objects.VnfLcmOpOccV2(
operation='INSTANTIATE')]
expected_result = [objects.VnfLcmOpOccV2(
operation='INSTANTIATE')]
result = lcmocc_utils.get_lcmocc_all(context)
self.assertEqual(expected_result[0].operation, result[0].operation)
def test_make_lcmocc_links(self):
lcmocc = objects.VnfLcmOpOccV2(
id='test-1', vnfInstanceId='instance-1', operation='INSTANTIATE')
endpoint = 'http://127.0.0.1:9890'
expected_result = objects.VnfLcmOpOccV2_Links()
expected_result.self = objects.Link(
href=f'{endpoint}/vnflcm/v2/vnf_lcm_op_occs/{lcmocc.id}')
expected_result.vnfInstance = objects.Link(
href=f'{endpoint}/vnflcm/v2/vnf_instances/{lcmocc.vnfInstanceId}')
expected_result.retry = objects.Link(
href=f'{endpoint}/vnflcm/v2/vnf_lcm_op_occs/{lcmocc.id}/retry')
expected_result.rollback = objects.Link(
href=f'{endpoint}/vnflcm/v2/vnf_lcm_op_occs/{lcmocc.id}/rollback')
expected_result.fail = objects.Link(
href=f'{endpoint}/vnflcm/v2/vnf_lcm_op_occs/{lcmocc.id}/fail')
result = lcmocc_utils.make_lcmocc_links(lcmocc, endpoint)
self.assertEqual(expected_result.self.href, result.self.href)
self.assertEqual(expected_result.vnfInstance.href,
result.vnfInstance.href)
self.assertEqual(expected_result.retry.href, result.retry.href)
self.assertEqual(expected_result.rollback.href, result.rollback.href)
self.assertEqual(expected_result.fail.href, result.fail.href)
def test_make_lcmocc_notif_data(self):
subsc_modify = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT')
subsc_inst = objects.LccnSubscriptionV2(
id='sub-1', verbosity='FULL')
lcmocc_modify = objects.VnfLcmOpOccV2.from_dict(_lcmocc_modify_value)
lcmocc_modify.error = objects.ProblemDetails.from_dict(_error)
lcmocc_inst = objects.VnfLcmOpOccV2.from_dict(_lcmocc_inst_value)
endpoint = 'http://127.0.0.1:9890'
# execute modify lcmocc
modify_result = lcmocc_utils.make_lcmocc_notif_data(
subsc_modify, lcmocc_modify, endpoint)
# execute inst lcmocc
inst_result = lcmocc_utils.make_lcmocc_notif_data(
subsc_inst, lcmocc_inst, endpoint)
self.assertEqual('START', modify_result.notificationStatus)
self.assertEqual('error', modify_result.error.detail)
self.assertIsNotNone(inst_result.affectedVnfcs)
self.assertIsNotNone(inst_result.changedInfo)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_filter')
def test_get_inst_lcmocc(self, mock_value):
inst = objects.VnfInstanceV2(id='test-instance')
value_1 = {
'id': 'test-1',
'vnfInstanceId': 'instance-1',
'operation': 'INSTANTIATE',
'operationState': 'COMPLETED',
'startTime': '2021-01-22 13:41:03+00:00'
}
value_2 = {
'id': 'test-2',
'vnfInstanceId': 'instance-2',
'operation': 'INSTANTIATE',
'operationState': 'COMPLETED',
'startTime': '2021-01-23 13:41:03+00:00'
}
mock_value.return_value = [
objects.VnfLcmOpOccV2.from_dict(value_1),
objects.VnfLcmOpOccV2.from_dict(value_2)
]
expected_result = objects.VnfLcmOpOccV2.from_dict(value_2)
result = lcmocc_utils.get_inst_lcmocc(context, inst)
self.assertEqual(expected_result.id, result.id)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_filter')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_grant_req_and_grant(self, mock_grant, mock_grant_reqs):
lcmocc_modify = objects.VnfLcmOpOccV2.from_dict(_lcmocc_modify_value)
lcmocc_inst = objects.VnfLcmOpOccV2.from_dict(_lcmocc_inst_value)
# execute modify lcmocc
modify_grant_req, modify_grant = lcmocc_utils.get_grant_req_and_grant(
context, lcmocc_modify)
self.assertIsNone(modify_grant_req)
self.assertIsNone(modify_grant)
# execute inst lcmocc
lcmocc_inst.grantId = 'grant-1'
mock_grant_reqs.return_value = [objects.GrantRequestV1(
vnfInstanceId='inst-1', vnfLcmOpOccId='lcmocc-1',
vnfdId='vnfd-1', operation='INSTANTIATE',
isAutomaticInvocation=False)]
mock_grant.return_value = objects.GrantV1(
id='grant-1', vnfInstanceId='inst-1', vnfLcmOpOccId='lcmocc-1')
inst_grant_req, inst_grant = lcmocc_utils.get_grant_req_and_grant(
context, lcmocc_inst)
self.assertEqual('inst-1', inst_grant_req.vnfInstanceId)
self.assertEqual('grant-1', inst_grant.id)
@mock.patch.object(objects.base.TackerPersistentObject,
'get_by_filter')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_grant_req_and_grant_error(self, mock_grant_reqs, mock_grant):
lcmocc_modify = objects.VnfLcmOpOccV2.from_dict(_lcmocc_modify_value)
lcmocc_inst = objects.VnfLcmOpOccV2.from_dict(_lcmocc_inst_value)
# execute modify lcmocc
modify_grant_req, modify_grant = lcmocc_utils.get_grant_req_and_grant(
context, lcmocc_modify)
self.assertIsNone(modify_grant_req)
self.assertIsNone(modify_grant)
# execute inst lcmocc
lcmocc_inst.grantId = 'grant-1'
mock_grant_reqs.return_value = None
mock_grant.return_value = objects.GrantV1(
id='grant-1', vnfInstanceId='inst-1', vnfLcmOpOccId='lcmocc-1')
self.assertRaises(
sol_ex.GrantRequestOrGrantNotFound,
lcmocc_utils.get_grant_req_and_grant, context, lcmocc_inst)

View File

@ -0,0 +1,383 @@
# Copyright (C) 2022 FUJITSU
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import requests
from unittest import mock
from oslo_utils import uuidutils
from tacker import context
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import http_client
from tacker.sol_refactored.common import subscription_utils as subsc_utils
from tacker.sol_refactored import objects
from tacker.tests import base
class TestSubscriptionUtils(base.BaseTestCase):
def setUp(self):
super(TestSubscriptionUtils, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
self.context.api_version = api_version.APIVersion('2.0.0')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_subsc(self, mock_subsc):
mock_subsc.return_value = objects.LccnSubscriptionV2(id='subsc-1')
result = subsc_utils.get_subsc(context, 'subsc-1')
self.assertEqual('subsc-1', result.id)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_subsc_error(self, mock_subsc):
mock_subsc.return_value = None
self.assertRaises(
sol_ex.LccnSubscriptionNotFound,
subsc_utils.get_subsc, context, 'subsc-1')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_subsc_all(self, mock_subsc):
mock_subsc.return_value = [objects.LccnSubscriptionV2(id='subsc-1')]
result = subsc_utils.get_subsc_all(context)
self.assertEqual('subsc-1', result[0].id)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_send_notification(self, mock_resp):
subsc_no_auth = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback')
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
id=uuidutils.generate_uuid()
)
resp_no_auth = requests.Response()
resp_no_auth.status_code = 204
mock_resp.return_value = (resp_no_auth, None)
# execute no_auth
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
subsc_basic_auth = objects.LccnSubscriptionV2(
id='sub-2', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback',
authentication=objects.SubscriptionAuthentication(
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
userName='test', password='test')))
# execute basic_auth
subsc_utils.send_notification(subsc_basic_auth, notif_data_no_auth)
subsc_oauth2 = objects.LccnSubscriptionV2(
id='sub-3', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback',
authentication=objects.SubscriptionAuthentication(
paramsOauth2ClientCredentials=(
objects.SubscriptionAuthentication_ParamsOauth2(
clientId='test', clientPassword='test',
tokenEndpoint='http://127.0.0.1/token'))))
# execute oauth2
subsc_utils.send_notification(subsc_oauth2, notif_data_no_auth)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_send_notification_error_code(self, mock_resp):
subsc_no_auth = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback')
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
id=uuidutils.generate_uuid()
)
resp_no_auth = requests.Response()
resp_no_auth.status_code = 200
mock_resp.return_value = (resp_no_auth, None)
# execute no_auth
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_send_notification_error(self, mock_resp):
subsc_no_auth = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback')
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
id=uuidutils.generate_uuid()
)
resp_no_auth = Exception()
mock_resp.return_value = (resp_no_auth, None)
# execute no_auth
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_test_notification(self, mock_resp):
subsc_no_auth = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback')
resp_no_auth = requests.Response()
resp_no_auth.status_code = 204
mock_resp.return_value = (resp_no_auth, None)
# execute no_auth
subsc_utils.test_notification(subsc_no_auth)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_test_notification_error_code(self, mock_resp):
subsc_no_auth = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback')
resp_no_auth = requests.Response()
resp_no_auth.status_code = 200
mock_resp.return_value = (resp_no_auth, None)
# execute no_auth
self.assertRaises(sol_ex.TestNotificationFailed,
subsc_utils.test_notification, subsc_no_auth)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_test_notification_error(self, mock_resp):
subsc_no_auth = objects.LccnSubscriptionV2(
id='sub-1', verbosity='SHORT',
callbackUri='http://127.0.0.1/callback')
resp_no_auth = Exception()
mock_resp.return_value = (resp_no_auth, None)
# execute no_auth
self.assertRaises(sol_ex.TestNotificationFailed,
subsc_utils.test_notification, subsc_no_auth)
def test_match_version(self):
inst = objects.VnfInstanceV2(
id='test-instance', vnfSoftwareVersion='1.1.1', vnfdVersion='1.2')
version_mismatch = (
objects._VnfProductsFromProviders_VnfProducts_Versions(
vnfSoftwareVersion='1.1.2'))
result_1 = subsc_utils.match_version(version_mismatch, inst)
self.assertEqual(False, result_1)
version_match = (
objects._VnfProductsFromProviders_VnfProducts_Versions(
vnfSoftwareVersion='1.1.1'))
result_2 = subsc_utils.match_version(version_match, inst)
self.assertEqual(True, result_2)
version_vnfd = (
objects._VnfProductsFromProviders_VnfProducts_Versions(
vnfSoftwareVersion='1.1.1', vnfdVersions=['1.2']))
result_3 = subsc_utils.match_version(version_vnfd, inst)
self.assertEqual(True, result_3)
def test_match_products_per_provider(self):
inst = objects.VnfInstanceV2(
id='test-instance', vnfProvider='company',
vnfSoftwareVersion='1.1.1', vnfdVersion='1.2',
vnfProductName='test')
products_mismatch = objects._VnfProductsFromProviders(
vnfProvider='test')
result_1 = subsc_utils.match_products_per_provider(
products_mismatch, inst)
self.assertEqual(False, result_1)
products_vnfproducts_no_exist = objects._VnfProductsFromProviders(
vnfProvider='company')
result_2 = subsc_utils.match_products_per_provider(
products_vnfproducts_no_exist, inst)
self.assertEqual(True, result_2)
products_vnf_mismatch = objects._VnfProductsFromProviders(
vnfProvider='company', vnfProducts=[
objects._VnfProductsFromProviders_VnfProducts(
vnfProductName='error'),
objects._VnfProductsFromProviders_VnfProducts(
vnfProductName='test', versions=[
objects._VnfProductsFromProviders_VnfProducts_Versions(
vnfSoftwareVersion='1.1.2'
)])
])
result_3 = subsc_utils.match_products_per_provider(
products_vnf_mismatch, inst)
self.assertEqual(False, result_3)
products_vnf_match_with_no_versions = (
objects._VnfProductsFromProviders(
vnfProvider='company', vnfProducts=[
objects._VnfProductsFromProviders_VnfProducts(
vnfProductName='test')]))
result_4 = subsc_utils.match_products_per_provider(
products_vnf_match_with_no_versions, inst)
self.assertEqual(True, result_4)
products_vnf_match_with_versions = objects._VnfProductsFromProviders(
vnfProvider='company', vnfProducts=[
objects._VnfProductsFromProviders_VnfProducts(
vnfProductName='test', versions=[
objects._VnfProductsFromProviders_VnfProducts_Versions(
vnfSoftwareVersion='1.1.1'
)])
])
result_5 = subsc_utils.match_products_per_provider(
products_vnf_match_with_versions, inst)
self.assertEqual(True, result_5)
def test_match_inst_subsc_filter(self):
inst = objects.VnfInstanceV2(
id='test-instance', vnfProvider='company',
vnfSoftwareVersion='1.1.1', vnfdVersion='1.2',
vnfProductName='test', vnfdId='vnfdid-1',
vnfInstanceName='test')
inst_filter_mismatch_vnfdid = objects.VnfInstanceSubscriptionFilter(
vnfdIds=['error'])
result_1 = subsc_utils.match_inst_subsc_filter(
inst_filter_mismatch_vnfdid, inst)
self.assertEqual(False, result_1)
products_vnfproducts_no_exist = objects._VnfProductsFromProviders(
vnfProvider='company')
inst_filter_match_products = objects.VnfInstanceSubscriptionFilter(
vnfProductsFromProviders=[products_vnfproducts_no_exist])
result_2 = subsc_utils.match_inst_subsc_filter(
inst_filter_match_products, inst)
self.assertEqual(True, result_2)
products_mismatch = objects._VnfProductsFromProviders(
vnfProvider='test')
inst_filter_mismatch_products = objects.VnfInstanceSubscriptionFilter(
vnfProductsFromProviders=[products_mismatch])
result_3 = subsc_utils.match_inst_subsc_filter(
inst_filter_mismatch_products, inst)
self.assertEqual(False, result_3)
inst_filter_mismatch_inst_id = objects.VnfInstanceSubscriptionFilter(
vnfInstanceIds=['instanceid-2'])
result_4 = subsc_utils.match_inst_subsc_filter(
inst_filter_mismatch_inst_id, inst)
self.assertEqual(False, result_4)
inst_filter_mismatch_inst_name = objects.VnfInstanceSubscriptionFilter(
vnfInstanceNames=['instance_name-2'])
result_5 = subsc_utils.match_inst_subsc_filter(
inst_filter_mismatch_inst_name, inst)
self.assertEqual(False, result_5)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_inst_create_subscs(self, mock_subscs):
inst = objects.VnfInstanceV2(id='test-instance')
mock_subscs.return_value = [objects.LccnSubscriptionV2(id='subsc-1')]
result = subsc_utils.get_inst_create_subscs(context, inst)
self.assertEqual('subsc-1', result[0].id)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_inst_delete_subscs(self, mock_subscs):
inst = objects.VnfInstanceV2(id='test-instance')
mock_subscs.return_value = [objects.LccnSubscriptionV2(id='subsc-1')]
result = subsc_utils.get_inst_delete_subscs(context, inst)
self.assertEqual('subsc-1', result[0].id)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_lcmocc_subscs(self, mock_subscs):
inst = objects.VnfInstanceV2(id='test-instance')
lcmocc = objects.VnfLcmOpOccV2(operationState='COMPLETED',
operation='INSTANTIATE')
mock_subscs.return_value = [objects.LccnSubscriptionV2(id='subsc-1')]
result = subsc_utils.get_lcmocc_subscs(context, lcmocc, inst)
self.assertEqual('subsc-1', result[0].id)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_matched_subscs(self, mock_subscs):
inst = objects.VnfInstanceV2(id='test-instance', vnfProvider='company')
notif_type = 'VnfLcmOperationOccurrenceNotification'
op_type = 'INSTANTIATE'
op_status = 'COMPLETED'
subscs_no_fileter = objects.LccnSubscriptionV2(id='subsc-1')
products_vnfproducts_no_exist = objects._VnfProductsFromProviders(
vnfProvider='company')
inst_filter_match_products = objects.VnfInstanceSubscriptionFilter(
vnfProductsFromProviders=[products_vnfproducts_no_exist])
subscs_filter_match = objects.LccnSubscriptionV2(
id='subsc-2',
filter=objects.LifecycleChangeNotificationsFilterV2(
vnfInstanceSubscriptionFilter=inst_filter_match_products))
products_mismatch = objects._VnfProductsFromProviders(
vnfProvider='test')
inst_filter_mismatch_products = objects.VnfInstanceSubscriptionFilter(
vnfProductsFromProviders=[products_mismatch])
subscs_filter_mismatch = objects.LccnSubscriptionV2(
id='subsc-3',
filter=objects.LifecycleChangeNotificationsFilterV2(
vnfInstanceSubscriptionFilter=inst_filter_mismatch_products))
subscs_noti_type_match = objects.LccnSubscriptionV2(
id='subsc-4', filter=objects.LifecycleChangeNotificationsFilterV2(
notificationTypes=['VnfLcmOperationOccurrenceNotification']))
subscs_noti_type_mismatch = objects.LccnSubscriptionV2(
id='subsc-5', filter=objects.LifecycleChangeNotificationsFilterV2(
notificationTypes=['VnfIdentifierCreationNotification']))
subscs_op_type_match = objects.LccnSubscriptionV2(
id='subsc-6', filter=objects.LifecycleChangeNotificationsFilterV2(
operationTypes=['INSTANTIATE']))
subscs_op_type_mismatch = objects.LccnSubscriptionV2(
id='subsc-7', filter=objects.LifecycleChangeNotificationsFilterV2(
operationTypes=['TERMINATE']))
subscs_op_status_match = objects.LccnSubscriptionV2(
id='subsc-8', filter=objects.LifecycleChangeNotificationsFilterV2(
operationStatus=['COMPLETED']))
subscs_op_status_mismatch = objects.LccnSubscriptionV2(
id='subsc-9', filter=objects.LifecycleChangeNotificationsFilterV2(
operationStatus=['FAILED_TEMP']))
mock_subscs.return_value = [
subscs_no_fileter, subscs_filter_match, subscs_filter_mismatch,
subscs_noti_type_match, subscs_noti_type_mismatch,
subscs_op_type_match, subscs_op_type_mismatch,
subscs_op_status_match, subscs_op_status_mismatch]
result = subsc_utils.get_matched_subscs(
context, inst, notif_type, op_type, op_status)
expected_ids = ['subsc-1', 'subsc-2', 'subsc-4', 'subsc-6', 'subsc-8']
result_ids = [sub.id for sub in result]
self.assertEqual(expected_ids, result_ids)
def test_make_create_inst_notif_data(self):
subsc = objects.LccnSubscriptionV2(id='subsc-1')
inst = objects.VnfInstanceV2(id='test-instance')
endpoint = 'http://127.0.0.1:9890'
result = subsc_utils.make_create_inst_notif_data(subsc, inst, endpoint)
self.assertEqual('subsc-1', result.subscriptionId)
self.assertEqual('test-instance', result.vnfInstanceId)
def test_make_delete_inst_notif_data(self):
subsc = objects.LccnSubscriptionV2(id='subsc-1')
inst = objects.VnfInstanceV2(id='test-instance')
endpoint = 'http://127.0.0.1:9890'
result = subsc_utils.make_delete_inst_notif_data(subsc, inst, endpoint)
self.assertEqual('subsc-1', result.subscriptionId)
self.assertEqual('test-instance', result.vnfInstanceId)

View File

@ -0,0 +1,114 @@
# Copyright (C) 2022 FUJITSU
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from tacker import context
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import vim_utils
from tacker.sol_refactored import objects
from tacker.tests import base
from tacker.vnfm import vim_client
_vim_openstack = {
"placement_attr": {
"regions": ['RegionOne']
},
"vim_auth": {
"username": "nfv_user",
"password": "devstack",
"project_name": "nfv",
"project_domain_name": "Default",
"user_domain_name": "Default",
"auth_url": "http://127.0.0.1/identity"
},
"vim_type": "openstack",
"vim_id": "openstack-1"
}
_vim_kubernetes_bearer_token = {
"placement_attr": {
"regions": ['RegionOne']
},
"vim_auth": {
"username": None,
"password": None,
"bearer_token": "test_token",
"auth_url": "https://127.0.0.1:6443",
"ssl_ca_cert": "test_ssl"
},
"vim_type": "kubernetes",
"vim_id": "kubernetes-1"
}
_vim_kubernetes_user = {
"vim_auth": {
"username": "admin",
"password": "admin",
"auth_url": "https://127.0.0.1:6443"
},
"vim_type": "kubernetes",
"vim_id": "kubernetes-2"
}
class TestVimUtils(base.BaseTestCase):
def setUp(self):
super(TestVimUtils, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
self.context.api_version = api_version.APIVersion('2.0.0')
@mock.patch.object(vim_client.VimClient, 'get_vim')
def test_get_default_vim(self, mock_vim):
mock_vim.return_value = _vim_openstack
result = vim_utils.get_default_vim(context)
self.assertEqual('openstack-1', result.vimId)
@mock.patch.object(vim_client.VimClient, 'get_vim')
def test_get_default_vim_error(self, mock_vim):
mock_vim.return_value = Exception
vim_utils.get_default_vim(context)
@mock.patch.object(vim_client.VimClient, 'get_vim')
def test_get_vim(self, mock_vim):
mock_vim.return_value = _vim_kubernetes_bearer_token
result = vim_utils.get_vim(context, 'kubernetes-1')
self.assertEqual('kubernetes-1', result.vimId)
@mock.patch.object(vim_client.VimClient, 'get_vim')
def test_get_vim_error(self, mock_vim):
mock_vim.return_value = Exception
self.assertRaises(
sol_ex.VimNotFound, vim_utils.get_vim, context, 'test')
def test_vim_to_conn_info(self):
vim_openstack = _vim_openstack
vim_kubernetes_1 = _vim_kubernetes_bearer_token
vim_kubernetes_2 = _vim_kubernetes_user
result_1 = vim_utils.vim_to_conn_info(vim_openstack)
self.assertEqual('openstack-1', result_1.vimId)
result_2 = vim_utils.vim_to_conn_info(vim_kubernetes_1)
self.assertEqual('kubernetes-1', result_2.vimId)
result_3 = vim_utils.vim_to_conn_info(vim_kubernetes_2)
self.assertEqual('kubernetes-2', result_3.vimId)
self.assertRaises(
sol_ex.SolException, vim_utils.vim_to_conn_info,
{'vim_type': 'test', 'vim_auth': 'test'})

View File

@ -12,23 +12,31 @@
# 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 unittest import mock
from tacker import context
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
from tacker.sol_refactored import objects
from tacker.tests import base
class TestVnfInstanceUtils(base.BaseTestCase):
def setUp(self):
super(TestVnfInstanceUtils, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
def test_json_merge_patch(self):
# patch is not dict.
target = {"key1", "value1"}
target = {"key1": "value1"}
patch = "text"
result = inst_utils.json_merge_patch(target, patch)
self.assertEqual(patch, result)
# target is not dict.
target = "text"
patch = {"key1", "value1"}
patch = {"key1": "value1"}
result = inst_utils.json_merge_patch(target, patch)
self.assertEqual(patch, result)
@ -65,3 +73,24 @@ class TestVnfInstanceUtils(base.BaseTestCase):
}
result = inst_utils.json_merge_patch(target, patch)
self.assertEqual(expected_result, result)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_inst(self, mock_inst):
mock_inst.return_value = objects.VnfInstanceV2(id='inst-1')
result = inst_utils.get_inst(context, 'inst-1')
self.assertEqual('inst-1', result.id)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
def test_get_inst_error(self, mock_inst):
mock_inst.return_value = None
self.assertRaises(
sol_ex.VnfInstanceNotFound,
inst_utils.get_inst, context, 'inst-1')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
def test_get_inst_all(self, mock_inst):
mock_inst.return_value = [objects.VnfInstanceV2(id='inst-1')]
result = inst_utils.get_inst_all(context)
self.assertEqual('inst-1', result[0].id)

View File

@ -12,8 +12,13 @@
# 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 shutil
import tempfile
from oslo_log import log as logging
from oslo_utils import uuidutils
import yaml
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import vnfd_utils
@ -22,6 +27,7 @@ from tacker.tests import base
SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
SAMPLE_FLAVOUR_ID = "simple"
LOG = logging.getLogger()
class TestVnfd(base.BaseTestCase):
@ -179,3 +185,126 @@ class TestVnfd(base.BaseTestCase):
result = self.vnfd_1.get_max_scale_level(SAMPLE_FLAVOUR_ID,
'VDU1_scale')
self.assertEqual(2, result)
def test_init_from_zip_file(self):
vnfd_id = uuidutils.generate_uuid()
tmp_dir = tempfile.mkdtemp()
cur_dir = os.path.dirname(__file__)
sample_path = os.path.join(cur_dir, "../samples/sample1")
shutil.make_archive(tmp_dir, 'zip', root_dir=sample_path)
vnfd = vnfd_utils.Vnfd(vnfd_id)
with open(f'{tmp_dir}.zip', "rb") as f:
content = f.read()
vnfd.init_from_zip_file(content)
with open(f'{sample_path}/TOSCA-Metadata/TOSCA.meta', 'r') as f:
tosca_content = yaml.safe_load(f.read())
with open(f'{sample_path}/Definitions/'
f'ut_sample1_df_simple.yaml', 'r') as f:
definition_content = yaml.safe_load(f.read())
self.assertEqual(tosca_content, vnfd.tosca_meta)
self.assertEqual(True, vnfd.csar_dir_is_tmp)
self.assertEqual(
definition_content, vnfd.definitions['ut_sample1_df_simple.yaml'])
def test_init_vnfd_error(self):
vnfd_id = uuidutils.generate_uuid()
vnfd = vnfd_utils.Vnfd(vnfd_id)
vnfd.csar_dir = 'test'
self.assertRaises(sol_ex.InvalidVnfdFormat, vnfd.init_vnfd)
def test_delete(self):
os.mkdir("/tmp/test")
vnfd_id = uuidutils.generate_uuid()
vnfd = vnfd_utils.Vnfd(vnfd_id)
vnfd.csar_dir_is_tmp = True
vnfd.csar_dir = "/tmp/test"
vnfd.delete()
result = os.path.isdir(vnfd.csar_dir)
self.assertEqual(False, result)
def test_get_vnfd_properties(self):
vnfd_id = uuidutils.generate_uuid()
vnfd = vnfd_utils.Vnfd(vnfd_id)
expected_result = {
'vnfConfigurableProperties': {},
'extensions': {},
'metadata': {}
}
result = vnfd.get_vnfd_properties()
self.assertEqual(expected_result, result)
def test_get_base_hot_abnormal(self):
flavour_id = 'simple'
vnfd_id = uuidutils.generate_uuid()
vnfd = vnfd_utils.Vnfd(vnfd_id)
vnfd.csar_dir = '/test/'
base_hot_result = vnfd.get_base_hot(flavour_id)
self.assertEqual({}, base_hot_result)
flavour_id = 'error'
base_hot_result = self.vnfd_1.get_base_hot(flavour_id)
self.assertIsNotNone(base_hot_result['template'])
def test_remove_tmp_csar_dir(self):
os.mkdir("/tmp/test")
vnfd_id = uuidutils.generate_uuid()
vnfd = vnfd_utils.Vnfd(vnfd_id)
tmp_dir = "/tmp/test"
vnfd.remove_tmp_csar_dir(tmp_dir)
result = os.path.isdir(tmp_dir)
self.assertEqual(False, result)
def test_remove_tmp_csar_dir_error(self):
vnfd_id = uuidutils.generate_uuid()
vnfd = vnfd_utils.Vnfd(vnfd_id)
log_name = "tacker.sol_refactored.common.vnfd_utils"
with self.assertLogs(logger=log_name, level=logging.DEBUG) as cm:
vnfd.remove_tmp_csar_dir('test')
msg = (f'ERROR:{log_name}:rmtree test failed')
self.assertIn(f'{msg}', cm.output[0].split('\n')[0])
def test_get_policy_values_by_type(self):
result = self.vnfd_1.get_policy_values_by_type(
'error', 'tosca.policies.nfv.AntiAffinityRule')
self.assertEqual('nfvi_node', result[0]['properties']['scope'])
def test_get_vdu_num_none(self):
result = self.vnfd_1.get_vdu_num('error', 'VDU1', 'default')
self.assertEqual(0, result)
def test_get_affinity_targets(self):
result = self.vnfd_1.get_affinity_targets('error')
expected_result = [(['VDU3'], 'zone')]
self.assertEqual(expected_result, result)
def test_get_interface_script_abnormal(self):
result = self.vnfd_1.get_interface_script('error', 'instantiate_start')
self.assertEqual(None, result)
result = self.vnfd_1.get_interface_script('error', 'instantiate_end')
self.assertEqual(None, result)
self.vnfd_1.get_interface_script('error', 'instantiate_end')
self.assertRaises(
sol_ex.SolHttpError422, self.vnfd_1.get_interface_script,
'error', 'terminate_start')
def test_get_scale_vdu_and_num_abnormal(self):
result = self.vnfd_1.get_scale_vdu_and_num('error', 'VDU1_scale')
self.assertEqual({}, result)
def test_get_scale_info_from_inst_level_abnormal(self):
result = self.vnfd_1.get_scale_info_from_inst_level('error', 'default')
self.assertEqual({}, result)
def test_get_max_scale_level_abnormal(self):
result = self.vnfd_1.get_max_scale_level('error', 'VDU1_scale')
self.assertEqual(0, result)
def test_get_vnf_artifact_files_manifest(self):
result = self.vnfd_1.get_vnf_artifact_files()
expected_result = ['Scripts/install.sh',
'Files/kubernetes/deployment.yaml']
self.assertEqual(expected_result, result)

View File

@ -39,8 +39,9 @@ class TestConductorV2(db_base.SqlTestCase):
self.conductor = conductor_v2.ConductorV2()
self.context = context.get_admin_context()
def _create_inst_and_lcmocc(self,
op_state=fields.LcmOperationStateType.STARTING):
def _create_inst_and_lcmocc(
self, op_state=fields.LcmOperationStateType.STARTING,
is_change_vnfpkg=False):
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
@ -65,6 +66,9 @@ class TestConductorV2(db_base.SqlTestCase):
isCancelPending=False,
operationParams=req)
if is_change_vnfpkg:
lcmocc.operation = fields.LcmOperationType.CHANGE_VNFPKG
inst.create(self.context)
lcmocc.create(self.context)
@ -530,3 +534,98 @@ class TestConductorV2(db_base.SqlTestCase):
lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id)
expected = ex.make_problem_details()
self.assertEqual(expected, lcmocc.error.to_dict())
def test_start_lcm_op_abnormal(self):
# prepare
lcmocc = self._create_inst_and_lcmocc(
op_state=fields.LcmOperationStateType.PROCESSING)
result = self.conductor.start_lcm_op(self.context, lcmocc.id)
self.assertEqual(None, result)
@mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification')
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd')
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'post_grant')
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'process')
def test_retry_lcm_op_abnormal(self, mocked_process, mocked_post_grant,
mocked_get_vnfd, mocked_send_lcmocc_notification):
# operation state incorrect
lcmocc = self._create_inst_and_lcmocc(
op_state=fields.LcmOperationStateType.PROCESSING)
result = self.conductor.retry_lcm_op(self.context, lcmocc.id)
self.assertEqual(None, result)
# operation is change_vnfpkg
# prepare
lcmocc = self._create_inst_and_lcmocc(
op_state=fields.LcmOperationStateType.FAILED_TEMP)
lcmocc.operation = fields.LcmOperationType.CHANGE_VNFPKG
lcmocc.operationParams = objects.ChangeCurrentVnfPkgRequest(
vnfdId='test-vnfdid')
self._create_grant_req_and_grant(lcmocc)
mocked_get_vnfd.return_value = mock.Mock()
op_state = []
def _store_state(context, lcmocc, inst, endpoint):
op_state.append(lcmocc.operationState)
mocked_send_lcmocc_notification.side_effect = _store_state
# run retry_lcm_op
self.conductor.retry_lcm_op(self.context, lcmocc.id)
# check operationState transition
self.assertEqual(2, mocked_send_lcmocc_notification.call_count)
self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[0])
self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[1])
# check grant_req and grant are deleted
self.assertRaises(sol_ex.GrantRequestOrGrantNotFound,
lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc)
@mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification')
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd')
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'post_grant')
@mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'rollback')
def test_rollback_lcm_op_abnormal(self, mocked_rollback,
mocked_post_grant, mocked_get_vnfd,
mocked_send_lcmocc_notification):
# operation state incorrect
lcmocc = self._create_inst_and_lcmocc(
op_state=fields.LcmOperationStateType.PROCESSING)
result = self.conductor.rollback_lcm_op(self.context, lcmocc.id)
self.assertEqual(None, result)
# operation is change_vnfpkg
lcmocc = self._create_inst_and_lcmocc(
op_state=fields.LcmOperationStateType.FAILED_TEMP,
is_change_vnfpkg=True)
self._create_grant_req_and_grant(lcmocc)
mocked_get_vnfd.return_value = mock.Mock()
op_state = []
def _store_state(context, lcmocc, inst, endpoint):
op_state.append(lcmocc.operationState)
mocked_send_lcmocc_notification.side_effect = _store_state
# run rollback_lcm_op
self.conductor.rollback_lcm_op(self.context, lcmocc.id)
# check operationState transition
self.assertEqual(2, mocked_send_lcmocc_notification.call_count)
self.assertEqual(fields.LcmOperationStateType.ROLLING_BACK,
op_state[0])
self.assertEqual(fields.LcmOperationStateType.ROLLED_BACK, op_state[1])
# check grant_req and grant are deleted
self.assertRaises(sol_ex.GrantRequestOrGrantNotFound,
lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc)
def test_modify_vnfinfo_abnormal(self):
lcmocc = self._create_inst_and_lcmocc()
result = self.conductor.modify_vnfinfo(self.context, lcmocc.id)
self.assertEqual(None, result)

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,8 @@ from oslo_utils import uuidutils
from unittest import mock
from tacker import context
from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import vnfd_utils
from tacker.sol_refactored.infra_drivers.openstack import openstack
from tacker.sol_refactored import objects
@ -35,13 +37,6 @@ SAMPLE_FLAVOUR_ID = "simple"
_vim_connection_info_example = {
"vimId": "vim_id_1",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
# "interfaceInfo": omitted
# "accessInfo": omitted
}
_vim_connection_info_for_change_vnfpkg = {
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"vimId": uuidutils.generate_uuid(),
"interfaceInfo": {"endpoint": "http://127.0.0.1/identity"},
"accessInfo": {
"username": "nfv_user",
@ -51,7 +46,6 @@ _vim_connection_info_for_change_vnfpkg = {
"projectDomain": "Default",
"userDomain": "Default"
}
}
_instantiate_req_example = {
@ -2639,6 +2633,110 @@ mock_resource_list_3 = {
]
}
_heat_parameters = {
'VDU': {
'VDU1': {
'desired_capacity': 1,
'computeFlavourId': 'm1.tiny',
'locationConstraints': None
},
'VirtualStorage': {
'vcImageId': 'image-1.0.0-x86_64-disk'
},
'VDU2': {
'computeFlavourId': 'm1.small',
'vcImageId': 'image-VDU2'
}
},
'CP': {
'VDU1_CP1': {
'network': 'res_id_ext_vl_1'
},
'VDU1_CP2': {
'network': 'res_id_id_ext_vl_2',
'fixed_ips': [{
'subnet': 'res_id_subnet_1'
}]
},
'VDU2_CP1': {
'network': 'res_id_ext_vl_1',
'fixed_ips': [{
'ip_address': '10.10.0.102'
}]
},
'VDU2_CP2': {
'network': 'res_id_id_ext_vl_2',
'fixed_ips': []
}
}
}
_grant_req_example = {
'operation': 'SCALE',
'removeResources': [{
'id': 'ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1',
'resource': {
'resourceId': 'res_id_VDU1_1',
'vimLevelResourceType': 'OS::Nova::Server'
}
}, {
'id': 'VDU1_CP1-ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'LINKPORT',
'resourceTemplateId': 'VDU1_CP1',
'resource': {
'resourceId': 'res_id_VDU1_1_CP1',
'vimLevelResourceType': 'OS::Neutron::Port'
}
}, {
'id': 'VDU1_CP2-ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'LINKPORT',
'resourceTemplateId': 'VDU1_CP2',
'resource': {
'resourceId': 'res_id_VDU1_1_CP2',
'vimLevelResourceType': 'OS::Neutron::Port'
}
}, {
'id': 'VDU1_CP3-ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'LINKPORT',
'resourceTemplateId': 'VDU1_CP3',
'resource': {
'resourceId': 'res_id_VDU1_1_CP3',
'vimLevelResourceType': 'OS::Neutron::Port'
}
}, {
'id': 'VDU1_CP4-ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'LINKPORT',
'resourceTemplateId': 'VDU1_CP4',
'resource': {
'resourceId': 'res_id_VDU1_1_CP4',
'vimLevelResourceType': 'OS::Neutron::Port'
}
}, {
'id': 'VDU1_CP5-ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'LINKPORT',
'resourceTemplateId': 'VDU1_CP5',
'resource': {
'resourceId': 'res_id_VDU1_1_CP5',
'vimLevelResourceType': 'OS::Neutron::Port'
}
}, {
'id': 'VirtualStorage-ed52e40b-922f-4e01-bc30-d4bf80029280',
'type': 'STORAGE',
'resourceTemplateId': 'VirtualStorage',
'resource': {
'resourceId': 'res_id_VirtualStorage_1',
'vimLevelResourceType': 'OS::Cinder::Volume'
}
}],
'additionalParams': {
'key': 'value'
}
}
CONF = config.CONF
class TestOpenstack(base.BaseTestCase):
@ -2647,6 +2745,7 @@ class TestOpenstack(base.BaseTestCase):
objects.register_all()
self.driver = openstack.Openstack()
self.context = context.get_admin_context()
CONF.v2_vnfm.default_graceful_termination_timeout = 0
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "../..", "samples")
@ -2811,3 +2910,353 @@ class TestOpenstack(base.BaseTestCase):
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_change_ext_conn, result)
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_status')
@mock.patch.object(openstack.heat_utils.HeatClient, 'create_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'update_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_resources')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_parameters')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_template')
def test_instantiate(self, mock_template, mock_parameters, mock_resources,
mock_update_stack, mock_create_stack, mock_status):
# prepare
req = objects.InstantiateVnfRequest.from_dict(_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.INSTANTIATE
)
grant = objects.GrantV1()
mock_status.return_value = (None, 'test')
mock_resources.return_value = _heat_reses_example
mock_parameters.return_value = _heat_get_parameters_example
mock_template.return_value = _heat_get_template_example
# execute
self.driver.instantiate(req, inst, grant_req, grant, self.vnfd_1)
mock_create_stack.assert_called_once()
mock_status.return_value = ('Create_Failed', 'test')
# execute
self.driver.instantiate(req, inst, grant_req, grant, self.vnfd_1)
mock_update_stack.assert_called_once()
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_status')
@mock.patch.object(openstack.heat_utils.HeatClient, 'delete_stack')
def test_instantiate_rollback(self, mock_delete_stack, mock_status):
# prepare
req = objects.InstantiateVnfRequest.from_dict(_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.INSTANTIATE
)
grant = objects.GrantV1()
mock_status.return_value = (None, 'test')
# execute
self.driver.instantiate_rollback(
req, inst, grant_req, grant, self.vnfd_1)
mock_delete_stack.assert_not_called()
mock_status.return_value = ('Create_Failed', 'test')
# execute
self.driver.instantiate_rollback(
req, inst, grant_req, grant, self.vnfd_1)
mock_delete_stack.assert_called_once()
@mock.patch.object(openstack.heat_utils.HeatClient, 'delete_stack')
def test_terminate(self, mock_delete_stack):
# prepare
req_inst = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example)
req = objects.TerminateVnfRequest(
terminationType='GRACEFUL', gracefulTerminationTimeout=0)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_inst_info_example))
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.TERMINATE
)
grant = objects.GrantV1()
# graceful
self.driver.terminate(req, inst, grant_req, grant, self.vnfd_1)
self.assertEqual(1, mock_delete_stack.call_count)
# graceful with no time
req = objects.TerminateVnfRequest(terminationType='GRACEFUL')
self.driver.terminate(req, inst, grant_req, grant, self.vnfd_1)
self.assertEqual(2, mock_delete_stack.call_count)
# forceful
req = objects.TerminateVnfRequest(terminationType='FORCEFUL')
self.driver.terminate(req, inst, grant_req, grant, self.vnfd_1)
self.assertEqual(3, mock_delete_stack.call_count)
@mock.patch.object(openstack.heat_utils.HeatClient, 'update_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_resources')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_parameters')
@mock.patch.object(openstack.heat_utils.HeatClient, 'mark_unhealthy')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_template')
def test_scale(self, mock_template, mock_unhealthy, mock_parameters,
mock_reses, mock_stack):
# prepare
req_inst = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_expected_inst_info_vnfc_updated))
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.SCALE
)
grant = objects.GrantV1()
mock_parameters.return_value = {'nfv': json.dumps(_heat_parameters)}
mock_template.return_value = _heat_get_template_example
# scale-out
req = objects.ScaleVnfRequest(
type='SCALE_OUT', aspectId='VDU1_scale', numberOfSteps=1)
mock_reses.return_value = _heat_reses_example
self.driver.scale(req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_vnfc_updated, result)
# scale-in
req = objects.ScaleVnfRequest(
type='SCALE_IN', aspectId='VDU1_scale', numberOfSteps=1)
grant_req = objects.GrantRequestV1.from_dict(_grant_req_example)
mock_reses.return_value = _heat_reses_example
self.driver.scale(req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_vnfc_updated, result)
# error
req = objects.ScaleVnfRequest(
type='SCALE_IN', aspectId='VDU1_scale', numberOfSteps=1)
grant_req = objects.GrantRequestV1.from_dict(_grant_req_example)
del inst.instantiatedVnfInfo.vnfcResourceInfo[1]['metadata'][
'parent_stack_id']
self.assertRaises(
sol_ex.UnexpectedParentResourceDefinition, self.driver.scale,
req, inst, grant_req, grant, self.vnfd_1)
@mock.patch.object(openstack.heat_utils.HeatClient, 'update_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_resources')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_parameters')
@mock.patch.object(openstack.heat_utils.HeatClient, 'mark_unhealthy')
def test_scale_rollback(
self, mock_unhealthy, mock_parameters,
mock_reses, mock_stack):
# prepare
req_inst = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_expected_inst_info_vnfc_updated))
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.SCALE
)
grant = objects.GrantV1()
mock_parameters.return_value = {'nfv': json.dumps(_heat_parameters)}
# scale-out
req = objects.ScaleVnfRequest(
type='SCALE_OUT', aspectId='VDU1_scale', numberOfSteps=1)
mock_reses.return_value = _heat_reses_example
mock_reses.return_value[13]['physical_resource_id'] = (
'res_id_VDU1_1_new')
self.driver.scale_rollback(req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_vnfc_updated, result)
# error
del mock_reses.return_value[13]['parent_resource']
self.assertRaises(
sol_ex.UnexpectedParentResourceDefinition,
self.driver.scale_rollback, req, inst,
grant_req, grant, self.vnfd_1)
@mock.patch.object(openstack.heat_utils.HeatClient, 'update_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_resources')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_parameters')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_template')
def test_change_ext_conn(self, mock_template, mock_parameters,
mock_reses, mock_stack):
req_inst = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_expected_inst_info_change_ext_conn))
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.CHANGE_EXT_CONN
)
grant = objects.GrantV1()
req = objects.ChangeExtVnfConnectivityRequest.from_dict(
_change_ext_conn_req_example)
mock_parameters.return_value = {'nfv': json.dumps(_heat_parameters)}
mock_reses.return_value = _heat_reses_example_change_ext_conn
mock_template.return_value = _heat_get_template_example
self.driver.change_ext_conn(req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_change_ext_conn, result)
@mock.patch.object(openstack.heat_utils.HeatClient, 'update_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_resources')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_parameters')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_template')
def test_change_ext_conn_rollback(
self, mock_template, mock_parameters, mock_reses, mock_stack):
req_inst = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_expected_inst_info_change_ext_conn))
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.CHANGE_EXT_CONN
)
grant = objects.GrantV1()
req = objects.ChangeExtVnfConnectivityRequest.from_dict(
_change_ext_conn_req_example)
mock_parameters.return_value = {'nfv': json.dumps(_heat_parameters)}
mock_reses.return_value = _heat_reses_example_change_ext_conn
mock_template.return_value = _heat_get_template_example
self.driver.change_ext_conn_rollback(
req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_change_ext_conn, result)
@mock.patch.object(openstack.heat_utils.HeatClient, 'update_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'mark_unhealthy')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_resources')
@mock.patch.object(openstack.heat_utils.HeatClient, 'delete_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'create_stack')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_parameters')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_template')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_files')
def test_heal(self, mock_files, mock_template,
mock_parameters, mock_create, mock_delete,
mock_reses, mock_unhealthy, mock_update):
req_inst = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
instantiatedVnfInfo=(
objects.VnfInstanceV2_InstantiatedVnfInfo.from_dict(
_expected_inst_info_vnfc_updated))
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.HEAL
)
grant = objects.GrantV1()
mock_parameters.return_value = {'nfv': json.dumps(_heat_parameters)}
mock_template.return_value = _heat_get_template_example
# re-create
req = objects.HealVnfRequest(
additionalParams={
"all": True
}
)
mock_reses.return_value = _heat_reses_example
self.driver.heal(req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_vnfc_updated, result)
# no re-create
grant_req = objects.GrantRequestV1.from_dict(_grant_req_example)
req = objects.HealVnfRequest()
mock_reses.return_value = _heat_reses_example
self.driver.heal(req, inst, grant_req, grant, self.vnfd_1)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_vnfc_updated, result)

View File

@ -0,0 +1,763 @@
# Copyright (C) 2022 FUJITSU
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
from oslo_utils import uuidutils
from tacker import context
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.infra_drivers.openstack import userdata_default
from tacker.sol_refactored import objects
from tacker.sol_refactored.objects.v2 import fields
from tacker.tests import base
SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
# InstantiateVnfRequest example
_ext_vl_1 = {
"id": uuidutils.generate_uuid(),
"resourceId": 'net0_id',
"extCps": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1}]}}]}
}
},
{
"cpdId": "VDU2_CP1",
"cpConfig": {
"VDU2_CP1_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.10.0.101"]}]}}]}
}
}
],
}
_ext_vl_2 = {
"id": uuidutils.generate_uuid(),
"resourceId": 'net1_id',
"extCps": [
{
"cpdId": "VDU1_CP2",
"cpConfig": {
"VDU1_CP2_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": 'subnet1_id'}]}}]}
}
},
{
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.10.1.101"],
"subnetId": 'subnet1_id'}]}}]}
}
}
]
}
_inst_req_example = {
"flavourId": "simple",
"instantiationLevelId": "instantiation_level_2",
"extVirtualLinks": [
_ext_vl_1,
_ext_vl_2
],
"extManagedVirtualLinks": [
{
"id": uuidutils.generate_uuid(),
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": 'net_mgmt_id'
},
],
"vimConnectionInfo": {
"vim1": {
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"vimId": uuidutils.generate_uuid(),
"interfaceInfo": {"endpoint": "http://localhost/identity/v3"},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
}
}
}
}
_scale_req_example = {
'type': 'SCALE_OUT',
'aspectId': 'VDU1_scale',
'numberOfSteps': 1
}
_change_ext_conn_req_example = {
"extVirtualLinks": [
{
"id": "id_ext_vl_3",
"resourceId": "res_id_ext_vl_3",
"extCps": [
{
"cpdId": "VDU2_CP1",
"cpConfig": {
"VDU2_CP1_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
"20.10.0.102"
]
}
]
}
}
]
}
}
}
]
},
{
"id": "id_ext_vl_4",
"resourceId": "res_id_id_ext_vl_4",
"extCps": [
{
"cpdId": "VDU1_CP2",
"cpConfig": {
"VDU1_CP2_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": "res_id_subnet_4"
}
]
}
}
]
}
}
},
{
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2_1": {
"linkPortId": "link_port_id_VDU2_CP2_modified"
}
}
}
],
"extLinkPorts": [
{
"id": "link_port_id_VDU2_CP2_modified",
"resourceHandle": {
"resourceId": "res_id_VDU2_CP2_modified"
}
}
]
}
]
}
_inst_info_example = {
"flavourId": "simple",
"vnfState": "STARTED",
'scaleStatus': [],
"extCpInfo": [
{
"id": "90561570-264c-4472-b84f-1fff98513475",
"cpdId": "VDU2_CP1",
"cpConfigId": "VDU2_CP1_1",
"extLinkPortId": "ac27c99b-73c8-4e91-b730-90deade72af4",
"associatedVnfcCpId": "be955786-a0c7-4b61-8cd8-9bb8bcb1c6e3"
},
{
"id": "f9f4b4b2-50e2-4c73-b89b-e0665e65ffbe",
"cpdId": "VDU2_CP2",
"cpConfigId": "VDU2_CP2_1",
"extLinkPortId": "12567a13-9fbd-4803-ad9f-d94ced266cd8",
"associatedVnfcCpId": "c54fa2fc-185a-49a7-bb89-f30f7c3be6a4"
},
{
"id": "05474d0b-a1f7-4be5-b57e-ef6873e1f3b6",
"cpdId": "VDU1_CP2",
"cpConfigId": "VDU1_CP2_1",
"extLinkPortId": "aa6646da-2e59-4de9-9b72-c62e7c4d9142",
"associatedVnfcCpId": "fdbb289f-87c8-40d0-bf06-da07b41ba124"
},
{
"id": "42ede9a6-c2b8-4c0d-a337-26342ffb236c",
"cpdId": "VDU1_CP1",
"cpConfigId": "VDU1_CP1_1",
"extLinkPortId": "efd0eb4e-4e55-4ac8-8b9b-403ec79faf2d",
"associatedVnfcCpId": "235f920c-8b49-4894-9c36-73f5a3b9f74d"
},
{
"id": "4f0ab1ad-b7de-482f-a69b-1093c71d2ceb",
"cpdId": "VDU1_CP2",
"cpConfigId": "VDU1_CP2_1",
"extLinkPortId": "a6c4c043-e082-4873-a871-02467af66224",
"associatedVnfcCpId": "e23d970d-9ea9-4c26-9d67-8f244383ea3c"
},
{
"id": "7a7fa30f-a303-4856-bc8b-b836cb682892",
"cpdId": "VDU1_CP1",
"cpConfigId": "VDU1_CP1_1",
"extLinkPortId": "f58df4d9-08ff-41b7-ab73-95ebfb8103c4",
"associatedVnfcCpId": "259c5895-7be6-4bed-8a94-221c41b3d08f"
}
],
"extVirtualLinkInfo": [
{
"id": "137bdf0b-835c-43f0-b0d2-5c002599118a",
"resourceHandle": {
"resourceId": "6f97f400-2861-482a-ba78-65b652aaf8fc"
},
"extLinkPorts": [
{
"id": "ac27c99b-73c8-4e91-b730-90deade72af4",
"resourceHandle": {
"resourceId": "res_id_VDU2_CP1",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "90561570-264c-4472-b84f-1fff98513475"
},
{
"id": "efd0eb4e-4e55-4ac8-8b9b-403ec79faf2d",
"resourceHandle": {
"resourceId": "res_id_VDU1_1_CP1",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "42ede9a6-c2b8-4c0d-a337-26342ffb236c"
},
{
"id": "f58df4d9-08ff-41b7-ab73-95ebfb8103c4",
"resourceHandle": {
"resourceId": "res_id_VDU1_2_CP1",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "7a7fa30f-a303-4856-bc8b-b836cb682892"
}
],
"currentVnfExtCpData": [
{
"cpdId": "VDU2_CP1",
"cpConfig": {
"VDU2_CP2_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
'10.10.0.2'
],
"subnetId": 'test'
}
]
}
}
]
}
}
}
]
},
{
"id": "d8141a5a-6b6e-4dab-9bf5-158f23a617d7",
"resourceHandle": {
"resourceId": "02bc95e0-3d43-4d11-83b8-f7b15d8661a9"
},
"extLinkPorts": [
{
"id": "12567a13-9fbd-4803-ad9f-d94ced266cd8",
"resourceHandle": {
"resourceId": "res_id_VDU2_CP2",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "f9f4b4b2-50e2-4c73-b89b-e0665e65ffbe"
},
{
"id": "aa6646da-2e59-4de9-9b72-c62e7c4d9142",
"resourceHandle": {
"resourceId": "res_id_VDU1_1_CP2",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "05474d0b-a1f7-4be5-b57e-ef6873e1f3b6"
},
{
"id": "a6c4c043-e082-4873-a871-02467af66224",
"resourceHandle": {
"resourceId": "res_id_VDU1_2_CP2",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "4f0ab1ad-b7de-482f-a69b-1093c71d2ceb"
}
],
"currentVnfExtCpData": [
{
"cpdId": "VDU1_CP2",
"cpConfig": {
"VDU1_CP2_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
'10.10.0.3'
],
"subnetId": 'test'
}
]
}
}
]
}
}
},
{
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
'10.10.0.4'
],
"subnetId": 'test'
}
]
}
}
]
}
}
},
]
}
],
"extManagedVirtualLinkInfo": [
{
"id": "bad53df7-f1fa-482d-91b1-caec382aeec2",
"vnfVirtualLinkDescId": "internalVL1",
"networkResource": {
"resourceId": "56730009-169c-4f96-8141-828acf1ee067"
},
"vnfLinkPorts": [
{
"id": "74f387fe-6355-4af3-adc7-cdb507d5fa5f",
"resourceHandle": {
"resourceId": "res_id_VDU2_CP3",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "5b0b336b-c207-4fa8-8b41-a5ad87d85cd0",
"cpInstanceType": "VNFC_CP"
},
{
"id": "4064ec55-b862-4527-a911-8752d3aa765a",
"resourceHandle": {
"resourceId": "res_id_VDU1_1_CP3",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "b0732fb7-a42a-4077-aebc-d22b67b64f13",
"cpInstanceType": "VNFC_CP"
},
{
"id": "f3c9e62d-0f31-4a36-bd99-eecd8def0871",
"resourceHandle": {
"resourceId": "res_id_VDU1_2_CP3",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "9c65f67e-feb2-447c-b0e7-a4f896185b4f",
"cpInstanceType": "VNFC_CP"
}
]
}
],
"vnfcResourceInfo": [
{
"id": "vnfc_res_info_id_VDU2",
"vduId": "VDU2",
"computeResource": {
"resourceId": "res_id_VDU2",
"vimLevelResourceType": "OS::Nova::Server"
},
"vnfcCpInfo": [
{
"id": "be955786-a0c7-4b61-8cd8-9bb8bcb1c6e3",
"cpdId": "VDU2_CP1",
"vnfExtCpId": "90561570-264c-4472-b84f-1fff98513475"
},
{
"id": "c54fa2fc-185a-49a7-bb89-f30f7c3be6a4",
"cpdId": "VDU2_CP2",
"vnfExtCpId": "f9f4b4b2-50e2-4c73-b89b-e0665e65ffbe"
},
{
"id": "5b0b336b-c207-4fa8-8b41-a5ad87d85cd0",
"cpdId": "VDU2_CP3",
"vnfLinkPortId": "74f387fe-6355-4af3-adc7-cdb507d5fa5f"
},
{
"id": "2cb2b3a8-a7a0-41da-b3b8-4b82f576b090",
"cpdId": "VDU2_CP4",
"vnfLinkPortId": "8e01813f-35fc-4a35-8f64-0da08a45ea21"
},
{
"id": "39a7d895-3b19-4330-b6ec-ae3557ea9c01",
"cpdId": "VDU2_CP5",
"vnfLinkPortId": "4dd7cadd-b9a1-484f-b2f2-1ff50ef0d90f"
},
{
"id": "315a938a-b194-0c20-6398-ba92cce39c18",
"cpdId": "VDU2_CP6",
"vnfLinkPortId": "6dafdda1-db6c-492e-6dc2-4e5d323d6f98"
}
]
},
{
"id": "vnfc_res_info_id_VDU1_1",
"vduId": "VDU1",
"computeResource": {
"resourceId": "res_id_VDU1_1",
"vimLevelResourceType": "OS::Nova::Server"
},
"storageResourceIds": ["2135b13c-e630-4700-8f8d-85b6e48f7871"],
"vnfcCpInfo": [
{
"id": "235f920c-8b49-4894-9c36-73f5a3b9f74d",
"cpdId": "VDU1_CP1",
"vnfExtCpId": "42ede9a6-c2b8-4c0d-a337-26342ffb236c"
},
{
"id": "fdbb289f-87c8-40d0-bf06-da07b41ba124",
"cpdId": "VDU1_CP2",
"vnfExtCpId": "05474d0b-a1f7-4be5-b57e-ef6873e1f3b6"
},
{
"id": "b0732fb7-a42a-4077-aebc-d22b67b64f13",
"cpdId": "VDU1_CP3",
"vnfLinkPortId": "4064ec55-b862-4527-a911-8752d3aa765a"
},
{
"id": "a49f8fb8-6fd9-4e9f-a6dd-0d268e51c83c",
"cpdId": "VDU1_CP4",
"vnfLinkPortId": "1666a0f7-6a34-474e-87a2-07fb0c30ecdb"
},
{
"id": "33194d65-ecd6-48d9-8ef7-c15ce9fef46c",
"cpdId": "VDU1_CP5",
"vnfLinkPortId": "ace663cd-431b-402a-b2ae-d0824c996edb"
}
]
},
{
"id": "vnfc_res_info_id_VDU1_2",
"vduId": "VDU1",
"computeResource": {
"resourceId": "res_id_VDU1_2",
"vimLevelResourceType": "OS::Nova::Server"
},
"storageResourceIds": ["739f7012-7973-485b-b34f-b006bc336150"],
"vnfcCpInfo": [
{
"id": "259c5895-7be6-4bed-8a94-221c41b3d08f",
"cpdId": "VDU1_CP1",
# when extLinkPorts of extVirtualLinks specified, there is
# no vnfExtCpId nor vnfLinkPortId.
},
{
"id": "e23d970d-9ea9-4c26-9d67-8f244383ea3c",
"cpdId": "VDU1_CP2",
"vnfExtCpId": "4f0ab1ad-b7de-482f-a69b-1093c71d2ceb"
},
{
"id": "9c65f67e-feb2-447c-b0e7-a4f896185b4f",
"cpdId": "VDU1_CP3",
"vnfLinkPortId": "f3c9e62d-0f31-4a36-bd99-eecd8def0871"
},
{
"id": "0716ac20-612a-4ac2-8c87-d83be31dd4b5",
"cpdId": "VDU1_CP4",
"vnfLinkPortId": "bce3159b-caca-45b7-8bb7-88015e951e56"
},
{
"id": "c9112298-61eb-4bba-b285-ed3419593b1b",
"cpdId": "VDU1_CP5",
"vnfLinkPortId": "e0f98917-70ff-4f79-8747-9d7fc22827a4"
}
]
},
{
"id": "vnfc_res_no_cp_info",
"vduId": "VDU1",
"computeResource": {
"resourceId": "res_id_VDU1_3",
"vimLevelResourceType": "OS::Nova::Server"
}
}
],
"vnfVirtualLinkResourceInfo": [
{
"id": "18bd0111-d5e1-4aa3-b2d8-5b89833c6351",
"vnfVirtualLinkDescId": "internalVL3",
"networkResource": {
"resourceId": "res_id_internalVL3",
"vimLevelResourceType": "OS::Neutron::Net"
},
"vnfLinkPorts": [
{
"id": "4dd7cadd-b9a1-484f-b2f2-1ff50ef0d90f",
"resourceHandle": {
"resourceId": "res_id_VDU2_CP5",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "39a7d895-3b19-4330-b6ec-ae3557ea9c01",
"cpInstanceType": "VNFC_CP"
},
{
"id": "ace663cd-431b-402a-b2ae-d0824c996edb",
"resourceHandle": {
"resourceId": "res_id_VDU1_1_CP5",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "33194d65-ecd6-48d9-8ef7-c15ce9fef46c",
"cpInstanceType": "VNFC_CP"
},
{
"id": "e0f98917-70ff-4f79-8747-9d7fc22827a4",
"resourceHandle": {
"resourceId": "res_id_VDU1_2_CP5",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "c9112298-61eb-4bba-b285-ed3419593b1b",
"cpInstanceType": "VNFC_CP"
}
]
},
{
"id": "047aa313-b591-4529-aa98-cb8ce2b82e28",
"vnfVirtualLinkDescId": "internalVL2",
"networkResource": {
"resourceId": "res_id_internalVL2",
"vimLevelResourceType": "OS::Neutron::Net"
},
"vnfLinkPorts": [
{
"id": "8e01813f-35fc-4a35-8f64-0da08a45ea21",
"resourceHandle": {
"resourceId": "res_id_VDU2_CP4",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "2cb2b3a8-a7a0-41da-b3b8-4b82f576b090",
"cpInstanceType": "VNFC_CP"
},
{
"id": "1666a0f7-6a34-474e-87a2-07fb0c30ecdb",
"resourceHandle": {
"resourceId": "res_id_VDU1_1_CP4",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "a49f8fb8-6fd9-4e9f-a6dd-0d268e51c83c",
"cpInstanceType": "VNFC_CP"
},
{
"id": "bce3159b-caca-45b7-8bb7-88015e951e56",
"resourceHandle": {
"resourceId": "res_id_VDU1_2_CP4",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "0716ac20-612a-4ac2-8c87-d83be31dd4b5",
"cpInstanceType": "VNFC_CP"
}
]
}
],
"virtualStorageResourceInfo": [
{
"id": "2135b13c-e630-4700-8f8d-85b6e48f7871",
"virtualStorageDescId": "VirtualStorage",
"storageResource": {
"resourceId": "res_id_VirtualStorage_1",
"vimLevelResourceType": "OS::Cinder::Volume"
}
},
{
"id": "739f7012-7973-485b-b34f-b006bc336150",
"virtualStorageDescId": "VirtualStorage",
"storageResource": {
"resourceId": "res_id_VirtualStorage_2",
"vimLevelResourceType": "OS::Cinder::Volume"
}
}
],
"vnfcInfo": [
{
"id": "VDU2-vnfc_res_info_id_VDU2",
"vduId": "VDU2",
"vnfcResourceInfoId": "vnfc_res_info_id_VDU2",
"vnfcState": "STARTED"
},
{
"id": "VDU1-vnfc_res_info_id_VDU1_1",
"vduId": "VDU1",
"vnfcResourceInfoId": "vnfc_res_info_id_VDU1_1",
"vnfcState": "STARTED"
},
{
"id": "VDU1-vnfc_res_info_id_VDU1_2",
"vduId": "VDU1",
"vnfcResourceInfoId": "vnfc_res_info_id_VDU1_2",
"vnfcState": "STARTED"
}
]
}
_vnf_instance_example = {
"id": uuidutils.generate_uuid(),
"vnfdId": SAMPLE_VNFD_ID,
"vnfProvider": "provider",
"vnfProductName": "product name",
"vnfSoftwareVersion": "software version",
"vnfdVersion": "vnfd version",
"instantiationState": "NOT_INSTANTIATED",
"vimConnectionInfo": _inst_req_example['vimConnectionInfo']
}
class TestUserdataDefault(base.BaseTestCase):
def setUp(self):
super(TestUserdataDefault, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
self.context.api_version = api_version.APIVersion('2.0.0')
self.grant = {}
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "../..", "samples")
self.tmp_csar_dir = os.path.join(sample_dir, "sample1")
self.userdata_default = userdata_default.DefaultUserData()
def test_instantiate(self):
req = _inst_req_example
req['additionalParams'] = {"nfv": "req"}
inst = _vnf_instance_example
grant_req = {
"operation": fields.LcmOperationType.INSTANTIATE
}
self.grant['additionalParams'] = {"nfv": "grant"}
result = self.userdata_default.instantiate(
req, inst, grant_req, self.grant, self.tmp_csar_dir)
self.assertIsNotNone(result['template'])
self.assertIsNotNone(result['parameters'])
self.assertNotIn('req', result['parameters']['nfv'])
self.assertIn('grant', result['parameters']['nfv'])
self.assertIsNotNone(result['files'])
def test_scale(self):
req = _scale_req_example
inst = _vnf_instance_example
inst['instantiationState'] = 'INSTANTIATED'
inst['instantiatedVnfInfo'] = _inst_info_example
grant_req = {
"operation": fields.LcmOperationType.SCALE
}
result = self.userdata_default.scale(
req, inst, grant_req, self.grant, self.tmp_csar_dir)
self.assertIn('VDU1', result['parameters']['nfv']['VDU'])
def test_scale_rollback(self):
req = _scale_req_example
inst = _vnf_instance_example
inst['instantiationState'] = 'INSTANTIATED'
inst['instantiatedVnfInfo'] = _inst_info_example
grant_req = {
"operation": fields.LcmOperationType.SCALE
}
result = self.userdata_default.scale_rollback(
req, inst, grant_req, self.grant, self.tmp_csar_dir)
self.assertIn('VDU1', result['parameters']['nfv']['VDU'])
def test_change_ext_conn(self):
req = _change_ext_conn_req_example
inst = _vnf_instance_example
inst['instantiationState'] = 'INSTANTIATED'
inst['instantiatedVnfInfo'] = _inst_info_example
grant_req = {
"operation": fields.LcmOperationType.CHANGE_EXT_CONN
}
result = self.userdata_default.change_ext_conn(
req, inst, grant_req, self.grant, self.tmp_csar_dir)
self.assertIn('VDU1_CP2', result['parameters']['nfv']['CP'])
self.assertIn('VDU2_CP1', result['parameters']['nfv']['CP'])
self.assertIn('VDU2_CP2', result['parameters']['nfv']['CP'])
def test_change_ext_conn_rollback(self):
req = _change_ext_conn_req_example
inst = _vnf_instance_example
inst['instantiationState'] = 'INSTANTIATED'
inst['instantiatedVnfInfo'] = _inst_info_example
grant_req = {
"operation": fields.LcmOperationType.CHANGE_EXT_CONN
}
result = self.userdata_default.change_ext_conn_rollback(
req, inst, grant_req, self.grant, self.tmp_csar_dir)
self.assertIn('VDU1_CP2', result['parameters']['nfv']['CP'])
self.assertIn('VDU2_CP1', result['parameters']['nfv']['CP'])
self.assertIn('VDU2_CP2', result['parameters']['nfv']['CP'])
def test_heal(self):
result = self.userdata_default.heal(None, None, None, None, None)
self.assertEqual({}, result['parameters']['nfv'])

View File

@ -0,0 +1,749 @@
# Copyright (C) 2022 FUJITSU
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from datetime import datetime
import os
from unittest import mock
from oslo_utils import uuidutils
from tacker import context
from tacker.objects import vnf_package
from tacker.objects import vnf_package_vnfd
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
from tacker.sol_refactored.nfvo import glance_utils
from tacker.sol_refactored.nfvo import local_nfvo
from tacker.sol_refactored import objects
from tacker.sol_refactored.objects.v2 import fields
from tacker.tests import base
SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
SAMPLE_VNFPKG_ID = "d04753f1-493e-17dc-e4a9-65f73b3ccc24"
_inst_grant_req_example = {
"vnfInstanceId": "2d004394-d0f0-406d-845a-2b148f91039a",
"vnfLcmOpOccId": "34da9ba7-6ab1-4d8d-a68a-68892e56642c",
"vnfdId": "b1bb0ce7-ebca-4fa7-95ed-4840d7000000",
"flavourId": "simple",
"operation": "INSTANTIATE",
"isAutomaticInvocation": False,
"instantiationLevelId": "instantiation_level_2",
"addResources": [{
"id": "6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "COMPUTE",
"resourceTemplateId": "VDU1"
}, {
"id": "VDU1_CP1-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP1"
}, {
"id": "VDU1_CP2-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP2"
}, {
"id": "VDU1_CP3-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP3"
}, {
"id": "VDU1_CP5-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP5"
}, {
"id": "VDU1_CP4-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP4"
}, {
"id": "VirtualStorage-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "STORAGE",
"resourceTemplateId": "VirtualStorage"
}, {
"id": "39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "COMPUTE",
"resourceTemplateId": "VDU1"
}, {
"id": "VDU1_CP1-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP1"
}, {
"id": "VDU1_CP2-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP2"
}, {
"id": "VDU1_CP3-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP3"
}, {
"id": "VDU1_CP5-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP5"
}, {
"id": "VDU1_CP4-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP4"
}, {
"id": "VirtualStorage-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "STORAGE",
"resourceTemplateId": "VirtualStorage"
}, {
"id": "8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "COMPUTE",
"resourceTemplateId": "VDU1"
}, {
"id": "VDU1_CP1-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP1"
}, {
"id": "VDU1_CP2-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP2"
}, {
"id": "VDU1_CP3-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP3"
}, {
"id": "VDU1_CP5-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP5"
}, {
"id": "VDU1_CP4-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP4"
}, {
"id": "VirtualStorage-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "STORAGE",
"resourceTemplateId": "VirtualStorage"
}, {
"id": "3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "COMPUTE",
"resourceTemplateId": "VDU2"
}, {
"id": "VDU2_CP5-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP5"
}, {
"id": "VDU2_CP2-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP2"
}, {
"id": "VDU2_CP1-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP1"
}, {
"id": "VDU2_CP3-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP3"
}, {
"id": "VDU2_CP4-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP4"
}, {
"id": "a411a968-c00b-4d46-8553-a37f3537391e",
"type": "VL",
"resourceTemplateId": "internalVL2"
}, {
"id": "f06ba7cf-f5af-4856-8d3a-56b331d1b9a5",
"type": "VL",
"resourceTemplateId": "internalVL3"
}],
"placementConstraints": [{
"affinityOrAntiAffinity": "ANTI_AFFINITY",
"scope": "NFVI_NODE",
"resource": [{
"idType": "GRANT",
"resourceId": "6396a2a1-7e36-49bd-8fff-2d46394a6b29"
}, {
"idType": "GRANT",
"resourceId": "39e0af93-aba5-4a15-9231-cdaa4149738e"
}, {
"idType": "GRANT",
"resourceId": "8a3ab83f-187c-4feb-b1b5-a6bf587130fa"
}, {
"idType": "GRANT",
"resourceId": "3f2385fa-3f13-436d-b93a-47c62ef237e0"
}]
}],
"_links": {
"vnfLcmOpOcc": {
"href": "http://127.0.0.1:9890/vnflcm/v2/vnf_lcm_op_occs/"
"34da9ba7-6ab1-4d8d-a68a-68892e56642c"
},
"vnfInstance": {
"href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/"
"2d004394-d0f0-406d-845a-2b148f91039a"
}
}
}
_inst_req_example = {
"vimConnectionInfo": {
"vim1": {
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"vimId": uuidutils.generate_uuid(),
"interfaceInfo": {"endpoint": "http://localhost/identity/v3"},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
}
}
}
}
_change_vnfkg_grant_req_example = {
'vnfInstanceId': '5eb4b136-6dac-4af1-b880-4e5eea6ec358',
'vnfLcmOpOccId': '4ed07654-d459-4ae7-85c5-bed63c563646',
'vnfdId': '1440b128-be42-4eed-9c4b-7df4b59c35d2',
'dstVnfdId': 'b1bb0ce7-ebca-4fa7-95ed-4840d7000000',
'flavourId': 'simple',
'operation': 'CHANGE_VNFPKG',
'isAutomaticInvocation': False,
'addResources': [{
'id': 'aab5201a-7516-41d9-9bf3-6a2579df7004',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU2'
}, {
'id': '36027157-a86b-4294-95e8-4ebd4bd91abf',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1'
}, {
'id': 'VirtualStorage-36027157-a86b-4294-95e8-4ebd4bd91abf',
'type': 'STORAGE',
'resourceTemplateId': 'VirtualStorage'
}, {
'id': 'cf40bb5a-046d-4113-b69b-9e34878ba781',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1'
}, {
'id': 'VirtualStorage-cf40bb5a-046d-4113-b69b-9e34878ba781',
'type': 'STORAGE',
'resourceTemplateId': 'VirtualStorage'
}, {
'id': '7c093596-c98c-40a0-b2cb-f0c30ce6fb87',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1'
}],
'removeResources': [{
'id': '0c8c248d-69cd-4dde-8b75-9791a02899c8',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU2',
'resource': {
'resourceId': 'res_id_VDU2',
'vimLevelResourceType': 'OS::Nova::Server'
}
}, {
'id': '374dfdae-6add-4a96-a37f-95b8e1b3f79b',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1',
'resource': {
'resourceId': 'res_id_VDU1_1',
'vimLevelResourceType': 'OS::Nova::Server'
}
}, {
'id': 'VirtualStorage-374dfdae-6add-4a96-a37f-95b8e1b3f79b',
'type': 'STORAGE',
'resourceTemplateId': 'VirtualStorage',
'resource': {
'resourceId': 'res_id_VirtualStorage_1',
'vimLevelResourceType': 'OS::Cinder::Volume'
}
}, {
'id': '4e601248-185f-4405-91bb-ea579c1b866b',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1',
'resource': {
'resourceId': 'res_id_VDU1_2',
'vimLevelResourceType': 'OS::Nova::Server'
}
}, {
'id': 'VirtualStorage-4e601248-185f-4405-91bb-ea579c1b866b',
'type': 'STORAGE',
'resourceTemplateId': 'VirtualStorage',
'resource': {
'resourceId': 'res_id_VirtualStorage_2',
'vimLevelResourceType': 'OS::Cinder::Volume'
}
}, {
'id': 'cd8bee39-a7b1-4652-8de4-56f5863c9157',
'type': 'COMPUTE',
'resourceTemplateId': 'VDU1',
'resource': {
'resourceId': 'res_id_VDU1_3',
'vimLevelResourceType': 'OS::Nova::Server'
}
}],
'_links': {
'vnfLcmOpOcc': {
'href': 'http://127.0.0.1:9890/vnflcm/v2/vnf_lcm_op_occs/'
'4ed07654-d459-4ae7-85c5-bed63c563646'
},
'vnfInstance': {
'href': 'http://127.0.0.1:9890/vnflcm/v2/vnf_instances/'
'5eb4b136-6dac-4af1-b880-4e5eea6ec358'
}
}
}
_change_vnfpkg_example = {
"vnfdId": '61723406-6634-2fc0-060a-0b11104d2667',
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-old-vnf": "./Scripts/coordinate_old_vnf.py",
"lcm-operation-coordinate-new-vnf": "./Scripts/coordinate_new_vnf.py",
"vdu_params": [{
"vdu_id": "VDU1",
"old_vnfc_param": {
"cp_name": "CP1",
"username": "ubuntu",
"password": "ubuntu"},
"new_vnfc_param": {
"cp_name": "CP1",
"username": "ubuntu",
"password": "ubuntu"},
}]
}
}
CONF = config.CONF
class TestLocalNfvo(base.BaseTestCase):
def setUp(self):
super(TestLocalNfvo, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
CONF.vnf_package.vnf_package_csar_path = (
'/opt/stack/data/tacker/vnfpackage/')
self.context.api_version = api_version.APIVersion('2.0.0')
self.local_nfvo = local_nfvo.LocalNfvo()
def _get_vnfpkg_or_vnfd(
self, type, state=fields.PackageOnboardingStateType.ONBOARDED):
if type == 'vnfd':
return vnf_package_vnfd.VnfPackageVnfd(
id=uuidutils.generate_uuid(),
package_uuid=SAMPLE_VNFPKG_ID, vnfd_id=SAMPLE_VNFD_ID,
vnf_provider='provider', vnf_product_name='product',
vnf_software_version='2.0', vnfd_version='3.3.1')
else:
return vnf_package.VnfPackage(
id=SAMPLE_VNFPKG_ID,
onboarding_state=state,
operational_state=fields.PackageOperationalStateType.DISABLED)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'get_by_id')
def test_onboarded_show(self, mock_vnfpackage, mock_vnfd):
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
mock_vnfpackage.return_value = self._get_vnfpkg_or_vnfd('vnfpkg')
result = self.local_nfvo.onboarded_show(self.context, SAMPLE_VNFD_ID)
self.assertEqual(SAMPLE_VNFPKG_ID, result.id)
self.assertEqual(SAMPLE_VNFD_ID, result.vnfdId)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'get_by_id')
def test_onboarded_show_vnfpackage_error(self, mock_vnfpackage, mock_vnfd):
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
self.assertRaises(
sol_ex.VnfdIdNotFound, self.local_nfvo.onboarded_show,
self.context, SAMPLE_VNFD_ID)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'get_by_id')
def test_onboarded_show_error(self, mock_vnfpackage, mock_vnfd):
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
mock_vnfpackage.return_value = self._get_vnfpkg_or_vnfd(
'vnfpkg', fields.PackageOnboardingStateType.CREATED)
self.assertRaises(
sol_ex.VnfdIdNotFound, self.local_nfvo.onboarded_show,
self.context, SAMPLE_VNFD_ID)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(os.path, 'isdir')
def test_get_csar_dir(self, mock_isdir, mock_vnfd):
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
mock_isdir.return_value = True
result = self.local_nfvo.get_csar_dir(self.context, SAMPLE_VNFD_ID)
self.assertEqual(
f'/opt/stack/data/tacker/vnfpackage/{SAMPLE_VNFPKG_ID}', result)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
def test_get_csar_dir_vnfd_error(self, mock_vnfd):
self.assertRaises(
sol_ex.VnfdIdNotFound, self.local_nfvo.get_csar_dir,
self.context, SAMPLE_VNFD_ID)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(os.path, 'isdir')
def test_get_csar_dir_path_error(self, mock_isdir, mock_vnfd):
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
mock_isdir.return_value = False
self.assertRaises(
sol_ex.VnfdIdNotFound, self.local_nfvo.get_csar_dir,
self.context, SAMPLE_VNFD_ID)
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
def test_get_vnfd(self, mock_dir):
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
result = self.local_nfvo.get_vnfd(self.context, SAMPLE_VNFD_ID)
self.assertEqual(SAMPLE_VNFD_ID, result.vnfd_id)
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(lcmocc_utils, 'get_lcmocc')
@mock.patch.object(glance_utils.GlanceClient, 'create_image')
def test_instantiate_grant(self, mock_image, mock_lcmocc, mock_dir):
grant_req = objects.GrantRequestV1.from_dict(_inst_grant_req_example)
grant_res = objects.GrantV1(
id=uuidutils.generate_uuid(),
vnfInstanceId=grant_req.vnfInstanceId,
vnfLcmOpOccId=grant_req.vnfLcmOpOccId
)
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
req = objects.InstantiateVnfRequest.from_dict(_inst_req_example)
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
# only set used members in the method
operation=fields.LcmOperationType.INSTANTIATE,
operationParams=req)
mock_image.return_value = objects.GrantV1(
id=uuidutils.generate_uuid()
)
self.local_nfvo.instantiate_grant(self.context, grant_req, grant_res)
result = grant_res.to_dict()
self.assertIsNotNone(result['addResources'])
self.assertEqual(mock_image.return_value.id, result[
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(local_nfvo.LocalNfvo, '_get_vim_info')
def test_instantiate_grant_no_vim_info(self, mock_vim_info, mock_dir):
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
grant_req = objects.GrantRequestV1.from_dict(_inst_grant_req_example)
grant_res = objects.GrantV1(
id=uuidutils.generate_uuid(),
vnfInstanceId=grant_req.vnfInstanceId,
vnfLcmOpOccId=grant_req.vnfLcmOpOccId
)
mock_vim_info.return_value = None
self.assertRaises(
sol_ex.LocalNfvoGrantFailed, self.local_nfvo.instantiate_grant,
self.context, grant_req, grant_res)
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(lcmocc_utils, 'get_lcmocc')
@mock.patch.object(glance_utils.GlanceClient, 'create_image')
def test_instantiate_grant_no_image(
self, mock_image, mock_lcmocc, mock_dir):
grant_req = objects.GrantRequestV1.from_dict(_inst_grant_req_example)
grant_res = objects.GrantV1(
id=uuidutils.generate_uuid(),
vnfInstanceId=grant_req.vnfInstanceId,
vnfLcmOpOccId=grant_req.vnfLcmOpOccId
)
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
req = objects.InstantiateVnfRequest.from_dict(_inst_req_example)
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
# only set used members in the method
operation=fields.LcmOperationType.INSTANTIATE,
operationParams=req)
mock_image.return_value = Exception()
self.assertRaises(
sol_ex.LocalNfvoGrantFailed, self.local_nfvo.instantiate_grant,
self.context, grant_req, grant_res)
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(lcmocc_utils, 'get_lcmocc')
@mock.patch.object(glance_utils.GlanceClient, 'create_image')
@mock.patch.object(inst_utils, 'get_inst')
def test_change_vnfpkg_grant(self, mock_inst, mock_image,
mock_lcmocc, mock_dir):
grant_req = objects.GrantRequestV1.from_dict(
_change_vnfkg_grant_req_example)
grant_res = objects.GrantV1(
id=uuidutils.generate_uuid(),
vnfInstanceId=grant_req.vnfInstanceId,
vnfLcmOpOccId=grant_req.vnfLcmOpOccId
)
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
_change_vnfpkg_example)
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
operation=fields.LcmOperationType.CHANGE_VNFPKG,
operationParams=req)
mock_image.return_value = objects.GrantV1(
id=uuidutils.generate_uuid()
)
req = objects.InstantiateVnfRequest.from_dict(
_inst_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
mock_inst.return_value = inst
self.local_nfvo.change_vnfpkg_grant(self.context, grant_req, grant_res)
result = grant_res.to_dict()
self.assertEqual(mock_image.return_value.id, result[
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(local_nfvo.LocalNfvo, '_get_vim_info')
def test_change_vnfpkg_grant_no_vim_info(self, mock_vim_info, mock_dir):
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
grant_req = objects.GrantRequestV1.from_dict(
_change_vnfkg_grant_req_example)
grant_res = objects.GrantV1(
id=uuidutils.generate_uuid(),
vnfInstanceId=grant_req.vnfInstanceId,
vnfLcmOpOccId=grant_req.vnfLcmOpOccId
)
mock_vim_info.return_value = None
self.assertRaises(
sol_ex.LocalNfvoGrantFailed, self.local_nfvo.change_vnfpkg_grant,
self.context, grant_req, grant_res)
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(lcmocc_utils, 'get_lcmocc')
@mock.patch.object(glance_utils.GlanceClient, 'create_image')
@mock.patch.object(inst_utils, 'get_inst')
def test_change_vnfpkg_grant_no_image(self, mock_inst, mock_image,
mock_lcmocc, mock_dir):
grant_req = objects.GrantRequestV1.from_dict(
_change_vnfkg_grant_req_example)
grant_res = objects.GrantV1(
id=uuidutils.generate_uuid(),
vnfInstanceId=grant_req.vnfInstanceId,
vnfLcmOpOccId=grant_req.vnfLcmOpOccId
)
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
_change_vnfpkg_example)
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
operation=fields.LcmOperationType.CHANGE_VNFPKG,
operationParams=req)
req = objects.InstantiateVnfRequest.from_dict(
_inst_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
mock_inst.return_value = inst
mock_image.return_value = Exception()
self.assertRaises(
sol_ex.LocalNfvoGrantFailed, self.local_nfvo.change_vnfpkg_grant,
self.context, grant_req, grant_res)
@mock.patch.object(local_nfvo.LocalNfvo, 'get_csar_dir')
@mock.patch.object(lcmocc_utils, 'get_lcmocc')
@mock.patch.object(glance_utils.GlanceClient, 'create_image')
@mock.patch.object(inst_utils, 'get_inst')
def test_grant(self, mock_inst, mock_image, mock_lcmocc, mock_dir):
# instantiate
grant_req = objects.GrantRequestV1.from_dict(_inst_grant_req_example)
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
req = objects.InstantiateVnfRequest.from_dict(_inst_req_example)
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
# only set used members in the method
operation=fields.LcmOperationType.INSTANTIATE,
operationParams=req)
mock_image.return_value = objects.GrantV1(
id=uuidutils.generate_uuid()
)
grant_res = self.local_nfvo.grant(self.context, grant_req)
result = grant_res.to_dict()
self.assertIsNotNone(result['addResources'])
self.assertEqual(mock_image.return_value.id, result[
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
# change_vnfpkg
grant_req = objects.GrantRequestV1.from_dict(
_change_vnfkg_grant_req_example)
cur_dir = os.path.dirname(__file__)
sample_dir = os.path.join(cur_dir, "..", "samples")
mock_dir.return_value = os.path.join(sample_dir, "sample1")
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
_change_vnfpkg_example)
mock_lcmocc.return_value = objects.VnfLcmOpOccV2(
operation=fields.LcmOperationType.CHANGE_VNFPKG,
operationParams=req
)
mock_image.return_value = objects.GrantV1(
id=uuidutils.generate_uuid()
)
req = objects.InstantiateVnfRequest.from_dict(
_inst_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
mock_inst.return_value = inst
grant_res = self.local_nfvo.grant(self.context, grant_req)
result = grant_res.to_dict()
self.assertEqual(mock_image.return_value.id, result[
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'save')
def test_recv_inst_create_notification(
self, mock_save, mock_vnfpackage, mock_vnfd):
req = objects.InstantiateVnfRequest.from_dict(
_inst_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='NOT_INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
mock_vnfpackage.return_value = self._get_vnfpkg_or_vnfd('vnfpkg')
self.local_nfvo.recv_inst_create_notification(self.context, inst)
@mock.patch.object(vnf_package_vnfd.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'get_by_id')
@mock.patch.object(vnf_package.VnfPackage, 'save')
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_filter')
def test_recv_inst_delete_notification(
self, mock_inst, mock_save, mock_vnfpackage, mock_vnfd):
req = objects.InstantiateVnfRequest.from_dict(
_inst_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req.vimConnectionInfo
)
mock_inst.return_value = None
mock_vnfd.return_value = self._get_vnfpkg_or_vnfd('vnfd')
mock_vnfpackage.return_value = self._get_vnfpkg_or_vnfd('vnfpkg')
self.local_nfvo.recv_inst_delete_notification(self.context, inst)
@mock.patch.object(local_nfvo.LocalNfvo, '_glance_delete_images')
@mock.patch.object(local_nfvo.LocalNfvo, '_update_vnf_pkg_usage_state')
def test_recv_lcmocc_notification(self, mock_delete_image, mock_update):
# terminate-processing
req_inst = objects.InstantiateVnfRequest.from_dict(_inst_req_example)
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=SAMPLE_VNFD_ID,
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED',
vimConnectionInfo=req_inst.vimConnectionInfo,
)
req = objects.TerminateVnfRequest(terminationType='FORCEFUL')
lcmocc = objects.VnfLcmOpOccV2(
# required fields
id=uuidutils.generate_uuid(),
operationState=fields.LcmOperationStateType.PROCESSING,
stateEnteredTime=datetime.utcnow(),
startTime=datetime.utcnow(),
vnfInstanceId=inst.id,
operation=fields.LcmOperationType.TERMINATE,
isAutomaticInvocation=False,
isCancelPending=False,
operationParams=req)
self.local_nfvo.recv_lcmocc_notification(self.context, lcmocc, inst)
# terminate-failed_temp
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
self.local_nfvo.recv_lcmocc_notification(self.context, lcmocc, inst)
# terminate-completed
self.local_nfvo.inst_vim_info = {
inst.id: req_inst.vimConnectionInfo['vim1']
}
lcmocc.operationState = fields.LcmOperationStateType.COMPLETED
self.local_nfvo.recv_lcmocc_notification(self.context, lcmocc, inst)
# change_vnfpkg-processing
req = objects.ChangeCurrentVnfPkgRequest.from_dict(
_change_vnfpkg_example)
lcmocc = objects.VnfLcmOpOccV2(
# required fields
id=uuidutils.generate_uuid(),
operationState=fields.LcmOperationStateType.PROCESSING,
stateEnteredTime=datetime.utcnow(),
startTime=datetime.utcnow(),
vnfInstanceId=inst.id,
operation=fields.LcmOperationType.CHANGE_VNFPKG,
isAutomaticInvocation=False,
isCancelPending=False,
operationParams=req)
self.local_nfvo.recv_lcmocc_notification(self.context, lcmocc, inst)
# change_vnfpkg-failed_temp
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
self.local_nfvo.recv_lcmocc_notification(self.context, lcmocc, inst)
# change_vnfpkg-completed
self.local_nfvo.inst_vnfd_id = {inst.id: req.vnfdId}
lcmocc.operationState = fields.LcmOperationStateType.COMPLETED
self.local_nfvo.recv_lcmocc_notification(self.context, lcmocc, inst)

View File

@ -0,0 +1,421 @@
# Copyright (C) 2022 FUJITSU
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import requests
from unittest import mock
from oslo_utils import uuidutils
from tacker import context
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import http_client
from tacker.sol_refactored.common import subscription_utils as subsc_utils
from tacker.sol_refactored.common import vnfd_utils
from tacker.sol_refactored.nfvo import local_nfvo
from tacker.sol_refactored.nfvo import nfvo_client
from tacker.sol_refactored import objects
from tacker.tests import base
CONF = config.CONF
SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
_vnfpkg_body_example = {
"id": uuidutils.generate_uuid(),
"vnfdId": SAMPLE_VNFD_ID,
"vnfProvider": "Company",
"vnfProductName": "Sample VNF",
"vnfSoftwareVersion": "1.0",
"vnfdVersion": "1.0",
"onboardingState": "ONBOARDED",
"operationalState": "ENABLED",
"usageState": "NOT_IN_USE"
}
_grant_res = {
'id': 'b94d05c9-eb45-4855-9132-20e1d3e7cecf',
'vnfInstanceId': '2d004394-d0f0-406d-845a-2b148f91039a',
'vnfLcmOpOccId': '34da9ba7-6ab1-4d8d-a68a-68892e56642c',
'zones': [{
'id': 'b4eba78c-e957-4e66-8bd7-14822f954413',
'zoneId': 'nova'
}],
'addResources': [{
'resourceDefinitionId': '6396a2a1-7e36-49bd-8fff-2d46394a6b29',
'zoneId': 'b4eba78c-e957-4e66-8bd7-14822f954413'
}, {
'resourceDefinitionId': 'VDU1_CP1-6396a2a1-7e36-49bd-8fff-2d46394a6b29'
}, {
'resourceDefinitionId': 'VDU1_CP2-6396a2a1-7e36-49bd-8fff-2d46394a6b29'
}, {
'resourceDefinitionId': 'VDU1_CP3-6396a2a1-7e36-49bd-8fff-2d46394a6b29'
}, {
'resourceDefinitionId': 'VDU1_CP5-6396a2a1-7e36-49bd-8fff-2d46394a6b29'
}, {
'resourceDefinitionId': 'VDU1_CP4-6396a2a1-7e36-49bd-8fff-2d46394a6b29'
}, {
'resourceDefinitionId': 'VirtualStorage-6396a2a1-7e36-'
'49bd-8fff-2d46394a6b29'
}, {
'resourceDefinitionId': '39e0af93-aba5-4a15-9231-cdaa4149738e',
'zoneId': 'b4eba78c-e957-4e66-8bd7-14822f954413'
}, {
'resourceDefinitionId': 'VDU1_CP1-39e0af93-aba5-4a15-9231-cdaa4149738e'
}, {
'resourceDefinitionId': 'VDU1_CP2-39e0af93-aba5-4a15-9231-cdaa4149738e'
}, {
'resourceDefinitionId': 'VDU1_CP3-39e0af93-aba5-4a15-9231-cdaa4149738e'
}, {
'resourceDefinitionId': 'VDU1_CP5-39e0af93-aba5-4a15-9231-cdaa4149738e'
}, {
'resourceDefinitionId': 'VDU1_CP4-39e0af93-aba5-4a15-9231-cdaa4149738e'
}, {
'resourceDefinitionId': 'VirtualStorage-39e0af93-aba5-'
'4a15-9231-cdaa4149738e'
}, {
'resourceDefinitionId': '8a3ab83f-187c-4feb-b1b5-a6bf587130fa',
'zoneId': 'b4eba78c-e957-4e66-8bd7-14822f954413'
}, {
'resourceDefinitionId': 'VDU1_CP1-8a3ab83f-187c-4feb-b1b5-a6bf587130fa'
}, {
'resourceDefinitionId': 'VDU1_CP2-8a3ab83f-187c-4feb-b1b5-a6bf587130fa'
}, {
'resourceDefinitionId': 'VDU1_CP3-8a3ab83f-187c-4feb-b1b5-a6bf587130fa'
}, {
'resourceDefinitionId': 'VDU1_CP5-8a3ab83f-187c-4feb-b1b5-a6bf587130fa'
}, {
'resourceDefinitionId': 'VDU1_CP4-8a3ab83f-187c-4feb-b1b5-a6bf587130fa'
}, {
'resourceDefinitionId': 'VirtualStorage-8a3ab83f-187c-'
'4feb-b1b5-a6bf587130fa'
}, {
'resourceDefinitionId': '3f2385fa-3f13-436d-b93a-47c62ef237e0',
'zoneId': 'b4eba78c-e957-4e66-8bd7-14822f954413'
}, {
'resourceDefinitionId': 'VDU2_CP5-3f2385fa-3f13-436d-b93a-47c62ef237e0'
}, {
'resourceDefinitionId': 'VDU2_CP2-3f2385fa-3f13-436d-b93a-47c62ef237e0'
}, {
'resourceDefinitionId': 'VDU2_CP1-3f2385fa-3f13-436d-b93a-47c62ef237e0'
}, {
'resourceDefinitionId': 'VDU2_CP3-3f2385fa-3f13-436d-b93a-47c62ef237e0'
}, {
'resourceDefinitionId': 'VDU2_CP4-3f2385fa-3f13-436d-b93a-47c62ef237e0'
}, {
'resourceDefinitionId': 'a411a968-c00b-4d46-8553-a37f3537391e'
}, {
'resourceDefinitionId': 'f06ba7cf-f5af-4856-8d3a-56b331d1b9a5'
}],
'vimAssets': {
'softwareImages': [{
'vnfdSoftwareImageId': 'VDU2',
'vimSoftwareImageId': '44fd5841-ce98-4d54-a828-6ef51f941c8a'
}, {
'vnfdSoftwareImageId': 'VirtualStorage',
'vimSoftwareImageId': 'image-1.0.0-x86_64-disk'
}]
}
}
_inst_grant_req_example = {
"vnfInstanceId": "2d004394-d0f0-406d-845a-2b148f91039a",
"vnfLcmOpOccId": "34da9ba7-6ab1-4d8d-a68a-68892e56642c",
"vnfdId": "b1bb0ce7-ebca-4fa7-95ed-4840d7000000",
"flavourId": "simple",
"operation": "INSTANTIATE",
"isAutomaticInvocation": False,
"instantiationLevelId": "instantiation_level_2",
"addResources": [{
"id": "6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "COMPUTE",
"resourceTemplateId": "VDU1"
}, {
"id": "VDU1_CP1-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP1"
}, {
"id": "VDU1_CP2-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP2"
}, {
"id": "VDU1_CP3-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP3"
}, {
"id": "VDU1_CP5-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP5"
}, {
"id": "VDU1_CP4-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP4"
}, {
"id": "VirtualStorage-6396a2a1-7e36-49bd-8fff-2d46394a6b29",
"type": "STORAGE",
"resourceTemplateId": "VirtualStorage"
}, {
"id": "39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "COMPUTE",
"resourceTemplateId": "VDU1"
}, {
"id": "VDU1_CP1-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP1"
}, {
"id": "VDU1_CP2-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP2"
}, {
"id": "VDU1_CP3-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP3"
}, {
"id": "VDU1_CP5-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP5"
}, {
"id": "VDU1_CP4-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP4"
}, {
"id": "VirtualStorage-39e0af93-aba5-4a15-9231-cdaa4149738e",
"type": "STORAGE",
"resourceTemplateId": "VirtualStorage"
}, {
"id": "8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "COMPUTE",
"resourceTemplateId": "VDU1"
}, {
"id": "VDU1_CP1-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP1"
}, {
"id": "VDU1_CP2-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP2"
}, {
"id": "VDU1_CP3-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP3"
}, {
"id": "VDU1_CP5-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP5"
}, {
"id": "VDU1_CP4-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "LINKPORT",
"resourceTemplateId": "VDU1_CP4"
}, {
"id": "VirtualStorage-8a3ab83f-187c-4feb-b1b5-a6bf587130fa",
"type": "STORAGE",
"resourceTemplateId": "VirtualStorage"
}, {
"id": "3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "COMPUTE",
"resourceTemplateId": "VDU2"
}, {
"id": "VDU2_CP5-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP5"
}, {
"id": "VDU2_CP2-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP2"
}, {
"id": "VDU2_CP1-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP1"
}, {
"id": "VDU2_CP3-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP3"
}, {
"id": "VDU2_CP4-3f2385fa-3f13-436d-b93a-47c62ef237e0",
"type": "LINKPORT",
"resourceTemplateId": "VDU2_CP4"
}, {
"id": "a411a968-c00b-4d46-8553-a37f3537391e",
"type": "VL",
"resourceTemplateId": "internalVL2"
}, {
"id": "f06ba7cf-f5af-4856-8d3a-56b331d1b9a5",
"type": "VL",
"resourceTemplateId": "internalVL3"
}],
"placementConstraints": [{
"affinityOrAntiAffinity": "ANTI_AFFINITY",
"scope": "NFVI_NODE",
"resource": [{
"idType": "GRANT",
"resourceId": "6396a2a1-7e36-49bd-8fff-2d46394a6b29"
}, {
"idType": "GRANT",
"resourceId": "39e0af93-aba5-4a15-9231-cdaa4149738e"
}, {
"idType": "GRANT",
"resourceId": "8a3ab83f-187c-4feb-b1b5-a6bf587130fa"
}, {
"idType": "GRANT",
"resourceId": "3f2385fa-3f13-436d-b93a-47c62ef237e0"
}]
}],
"_links": {
"vnfLcmOpOcc": {
"href": "http://127.0.0.1:9890/vnflcm/v2/vnf_lcm_op_occs/"
"34da9ba7-6ab1-4d8d-a68a-68892e56642c"
},
"vnfInstance": {
"href": "http://127.0.0.1:9890/vnflcm/v2/vnf_instances/"
"2d004394-d0f0-406d-845a-2b148f91039a"
}
}
}
_lcmocc_inst_value = {
'id': 'test-1',
'vnfInstanceId': 'instance-1',
'operation': 'INSTANTIATE',
'operationState': 'COMPLETED',
'isAutomaticInvocation': False,
'startTime': '2021-01-23 13:41:03+00:00'
}
class TestNfvoClient(base.BaseTestCase):
def setUp(self):
super(TestNfvoClient, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
CONF.vnf_package.vnf_package_csar_path = (
'/opt/stack/data/tacker/vnfpackage/')
self.context.api_version = api_version.APIVersion('2.0.0')
self.nfvo_client = nfvo_client.NfvoClient()
self.nfvo_client.endpoint = 'http://127.0.0.1:9990'
auth_handle = http_client.OAuth2AuthHandle(
self.nfvo_client.endpoint,
'http://127.0.0.1:9990/token',
'test',
'test'
)
self.nfvo_client.client = http_client.HttpClient(auth_handle)
self.nfvo_client.grant_api_version = '1.4.0'
self.nfvo_client.vnfpkgm_api_version = '2.1.0'
@mock.patch.object(local_nfvo.LocalNfvo, 'onboarded_show')
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_get_vnf_package_info_vnfd(
self, mock_request, mock_onboarded_show):
# local nfvo
self.nfvo_client.is_local = True
mock_onboarded_show.return_value = vnfd_utils.Vnfd(
vnfd_id=SAMPLE_VNFD_ID)
result = self.nfvo_client.get_vnf_package_info_vnfd(
self.context, SAMPLE_VNFD_ID)
self.assertEqual(SAMPLE_VNFD_ID, result.vnfd_id)
# external nfvo
self.nfvo_client.is_local = False
mock_request.return_value = (requests.Response(), _vnfpkg_body_example)
result = self.nfvo_client.get_vnf_package_info_vnfd(
self.context, SAMPLE_VNFD_ID)
self.assertEqual(SAMPLE_VNFD_ID, result.vnfdId)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_onboarded_show_vnfd(self, mock_request):
# local nfvo
self.nfvo_client.is_local = True
result = self.nfvo_client.onboarded_show_vnfd(
self.context, SAMPLE_VNFD_ID)
self.assertIsNone(result)
# external nfvo
self.nfvo_client.is_local = False
mock_request.return_value = (requests.Response(), 'test')
result = self.nfvo_client.onboarded_show_vnfd(
self.context, SAMPLE_VNFD_ID)
self.assertEqual('test', result)
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_onboarded_package_content(self, mock_request):
# local nfvo
self.nfvo_client.is_local = True
result = self.nfvo_client.onboarded_package_content(
self.context, SAMPLE_VNFD_ID)
self.assertIsNone(result)
# external nfvo
self.nfvo_client.is_local = False
mock_request.return_value = (requests.Response(), 'test')
result = self.nfvo_client.onboarded_package_content(
self.context, SAMPLE_VNFD_ID)
self.assertEqual('test', result)
@mock.patch.object(local_nfvo.LocalNfvo, 'grant')
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_grant(self, mock_request, mock_grant):
# local nfvo
self.nfvo_client.is_local = True
mock_grant.return_value = objects.GrantV1.from_dict(_grant_res)
grant_req = objects.GrantRequestV1.from_dict(_inst_grant_req_example)
grant_res = self.nfvo_client.grant(self.context, grant_req)
result = grant_res.to_dict()
self.assertIsNotNone(result['addResources'])
self.assertEqual('44fd5841-ce98-4d54-a828-6ef51f941c8a', result[
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
# external nfvo
self.nfvo_client.is_local = False
mock_request.return_value = (requests.Response(), _grant_res)
grant_res = self.nfvo_client.grant(self.context, grant_req)
result = grant_res.to_dict()
self.assertIsNotNone(result['addResources'])
self.assertEqual('44fd5841-ce98-4d54-a828-6ef51f941c8a', result[
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
@mock.patch.object(subsc_utils, 'send_notification')
@mock.patch.object(local_nfvo.LocalNfvo, 'recv_inst_create_notification')
def test_send_inst_create_notification(
self, mock_recv, mock_send, mock_subscs):
self.nfvo_client.is_local = True
inst = objects.VnfInstanceV2(id='test-instance')
mock_subscs.return_value = [objects.LccnSubscriptionV2(id='subsc-1')]
self.nfvo_client.send_inst_create_notification(
self.context, inst, 'http://127.0.0.1:9890')
self.assertEqual(1, mock_recv.call_count)
self.assertEqual(1, mock_send.call_count)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
@mock.patch.object(subsc_utils, 'send_notification')
@mock.patch.object(local_nfvo.LocalNfvo, 'recv_inst_delete_notification')
def test_send_inst_delete_notification(
self, mock_recv, mock_send, mock_subscs):
self.nfvo_client.is_local = True
inst = objects.VnfInstanceV2(id='test-instance')
mock_subscs.return_value = [objects.LccnSubscriptionV2(id='subsc-1')]
self.nfvo_client.send_inst_delete_notification(
self.context, inst, 'http://127.0.0.1:9890')
self.assertEqual(1, mock_recv.call_count)
self.assertEqual(1, mock_send.call_count)
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
@mock.patch.object(subsc_utils, 'send_notification')
@mock.patch.object(local_nfvo.LocalNfvo, 'recv_lcmocc_notification')
def test_send_lcmocc_notification(self, mock_recv, mock_send, mock_subscs):
inst = objects.VnfInstanceV2(id='test-instance')
lcmocc = objects.VnfLcmOpOccV2.from_dict(_lcmocc_inst_value)
mock_subscs.return_value = [objects.LccnSubscriptionV2(
id='subsc-1', verbosity='FULL')]
self.nfvo_client.send_lcmocc_notification(
self.context, lcmocc, inst, 'http://127.0.0.1:9890')
self.assertEqual(1, mock_recv.call_count)
self.assertEqual(1, mock_send.call_count)

View File

@ -0,0 +1,122 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1_scale_group:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 3
desired_capacity: 1
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VirtualStorage, vcImageId ] }
net1: { get_param: [ nfv, CP, VDU1_CP1, network ] }
net2: { get_param: [ nfv, CP, VDU1_CP2, network ] }
subnet: { get_param: [nfv, CP, VDU1_CP2, fixed_ips, 0, subnet ]}
net3: { get_resource: internalVL1 }
net4: { get_resource: internalVL2 }
net5: { get_resource: internalVL3 }
net6: { get_param: { nfv, CP, VDU1_CP6, network } }
net7: { get_param: [ nfv, CP, VDU1_CP7 ] }
net8: { get_param: [ nfv_1, CP, VDU1_CP8, network ] }
affinity: { get_resource: nfvi_node_affinity }
VDU2:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
networks:
- port:
get_resource: VDU2_CP1
- port:
get_resource: VDU2_CP2
- port:
get_resource: VDU2_CP3
- port:
get_resource: VDU2_CP4
- port:
get_resource: VDU2_CP5
scheduler_hints:
group: {get_resource: nfvi_node_affinity }
# extVL with FixedIP
VDU2_CP1:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
fixed_ips:
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
# extvVL with FixedIP and Subnet
VDU2_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, VDU2_CP2, network ] }
fixed_ips:
- ip_address: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, ip_address]}
subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, subnet]}
VDU2_CP3:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL1 }
VDU2_CP4:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL2 }
VDU2_CP5:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL3 }
# delete the following lines when extmanagedVLs are specified in instantiatevnfrequest
internalVL1:
type: OS::Neutron::Net
internalVL2:
type: OS::Neutron::Net
internalVL3:
type: OS::Neutron::Net
internalVL1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL1
cidr: 192.168.3.0/24
internalVL2_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL2
cidr: 192.168.4.0/24
internalVL3_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL3
cidr: 192.168.5.0/24
nfvi_node_affinity:
type: OS::Nova::ServerGroup
properties:
name: nfvi_node_affinity
policies: [ 'affinity' ]
outputs: {}

View File

@ -11,18 +11,22 @@ resources:
properties:
min_size: 1
max_size: 3
desired_capacity: 1
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VirtualStorage, vcImageId ] }
zone: { get_param: [ nfv, VDU, VDU1, locationConstraints] }
net1: { get_param: [ nfv, CP, VDU1_CP1, network ] }
net2: { get_param: [ nfv, CP, VDU1_CP2, network ] }
subnet: { get_param: [nfv, CP, VDU1_CP2, fixed_ips, 0, subnet ]}
net3: { get_resource: internalVL1 }
net4: { get_resource: internalVL2 }
net5: { get_resource: internalVL3 }
net6: { get_param: { nfv, CP, VDU1_CP6, network } }
net7: { get_param: [ nfv, CP, VDU1_CP7 ] }
net8: { get_param: [ nfv_1, CP, VDU1_CP8, network ] }
affinity: { get_resource: nfvi_node_affinity }
VDU2:

View File

@ -0,0 +1,310 @@
tosca_definitions_version: tosca_simple_yaml_1_2
description: Simple deployment flavour for Sample VNF
imports:
- etsi_nfv_sol001_common_types.yaml
- etsi_nfv_sol001_vnfd_types.yaml
- v2_sample2_types.yaml
topology_template:
inputs:
descriptor_id:
type: string
descriptor_version:
type: string
provider:
type: string
product_name:
type: string
software_version:
type: string
vnfm_info:
type: list
entry_schema:
type: string
flavour_id:
type: string
flavour_description:
type: string
substitution_mappings:
node_type: company.provider.VNF
properties:
flavour_id: error
requirements:
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
virtual_link_external2_1: [ VDU1_CP2, virtual_link ]
virtual_link_external2_2: [ VDU2_CP2, virtual_link ]
node_templates:
VDU1:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
capabilities:
virtual_compute:
properties:
requested_additional_capabilities:
properties:
requested_additional_capability_name: m1.tiny
support_mandatory: true
target_performance_parameters:
entry_schema: test
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 3 GB
requirements:
- virtual_storage: VirtualStorage
VNF:
type: company.provider.VNF
properties:
flavour_description: A simple flavour
interfaces:
Vnflcm:
instantiate_start:
implementation:
instantiate_end:
implementation: sample-script
terminate_start:
implementation: error-script
terminate_end:
implementation: sample-script
scale_start: [ ]
artifacts:
sample-script:
description: Sample script
type: tosca.artifacts.Implementation.Python
file:
error-script:
description: Sample script
type: tosca.artifacts.Implementation.Error
file: 'test/sample_script.sh'
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
properties:
name: VDU1
description: VDU1 compute node
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 3
capabilities:
virtual_compute:
properties:
requested_additional_capabilities:
properties:
requested_additional_capability_name: m1.tiny
support_mandatory: true
target_performance_parameters:
entry_schema: test
virtual_memory:
virtual_mem_size: 512 MB
virtual_cpu:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 3 GB
requirements:
- virtual_storage: VirtualStorage-2
VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1gb GB
rdma_enabled: true
VirtualStorage-2:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1gb GB
rdma_enabled: true
sw_image_data:
name: image-1.0.0-x86_64-disk
version: '1.0.0'
checksum:
algorithm: sha-256
hash: a8dd75ecffd4cdd96072d60c2237b448e0c8b2bc94d57f10fdbc8c481d9005b8
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
VDU1_CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1
VDU1_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU1
VDU1_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL1
VDU1_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL2
VDU1_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU1
- virtual_link: internalVL3
VDU2_CP1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU2
VDU2_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 1
requirements:
- virtual_binding: VDU2
VDU2_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL1
VDU2_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
VDU2_CP5:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL3
internalVL1:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: External Managed Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 192.168.3.0/24
internalVL2:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: External Managed Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 192.168.4.0/24
internalVL3:
type: tosca.nodes.nfv.VnfVirtualLink
properties:
connectivity_type:
layer_protocols: [ ipv4 ]
description: Internal Virtual link in the VNF
vl_profile:
max_bitrate_requirements:
root: 1048576
leaf: 1048576
min_bitrate_requirements:
root: 1048576
leaf: 1048576
virtual_link_protocol_data:
- associated_layer_protocol: ipv4
l3_protocol_data:
ip_version: ipv4
cidr: 192.168.5.0/24
groups:
affinityOrAntiAffinityGroup1:
type: tosca.groups.nfv.PlacementGroup
members: [ VDU1, VDU2 ]
policies:
policy_antiaffinity_group:
type: tosca.policies.nfv.AntiAffinityRule
targets: [ affinityOrAntiAffinityGroup1 ]
properties:
scope: nfvi_node
policy_affinity_group:
type: tosca.policies.nfv.AffinityRule
targets: [ affinityOrAntiAffinityGroup1 ]
properties:
scope: error
policy_affinity_group_2:
type: tosca.policies.nfv.AffinityRule
targets: [VDU3]
properties:
scope: zone

View File

@ -2,3 +2,4 @@ TOSCA-Meta-File-Version: 1.0
CSAR-Version: 1.1
Created-by: Onboarding portal
Entry-Definitions: Definitions/v2_sample2_top.vnfd.yaml
ETSI-Entry-Manifest: manifest.mf

View File

@ -0,0 +1,7 @@
Source: Scripts/install.sh
Algorithm: SHA-256
Hash: 27bbdb25d8f4ed6d07d6f6581b86515e8b2f0059b236ef7b6f50d6674b34f02a
Source: Files/kubernetes/deployment.yaml
Algorithm: SHA-256
Hash: e23cc3433835cea32ce790b4823313dc6d0744dce02e27b1b339c87ee993b8c2