Browse Source

Merge "Revise FT of V2-API"

changes/84/822284/13
Zuul 4 months ago committed by Gerrit Code Review
parent
commit
a60993fc3b
  1. 6
      .zuul.yaml
  2. 21
      tacker/sol_refactored/controller/vnflcm_v2.py
  3. 202
      tacker/tests/functional/sol_v2/base_v2.py
  4. 487
      tacker/tests/functional/sol_v2/paramgen.py
  5. 12
      tacker/tests/functional/sol_v2/samples/sample1/contents/BaseHOT/simple/nested/VDU1.yaml
  6. 29
      tacker/tests/functional/sol_v2/samples/sample1/contents/BaseHOT/simple/sample1.yaml
  7. 62
      tacker/tests/functional/sol_v2/samples/sample1/contents/Definitions/v2_sample1_df_simple.yaml
  8. 67
      tacker/tests/functional/sol_v2/samples/sample1/contents/Scripts/sample_script.py
  9. 36
      tacker/tests/functional/sol_v2/samples/sample1/contents/UserData/userdata.py
  10. 17
      tacker/tests/functional/sol_v2/samples/sample1/pkggen.py
  11. 20
      tacker/tests/functional/sol_v2/samples/sample1/post.py
  12. 21
      tacker/tests/functional/sol_v2/samples/sample1/pre.py
  13. 67
      tacker/tests/functional/sol_v2/samples/sample2/contents/BaseHOT/simple/nested/VDU1.yaml
  14. 84
      tacker/tests/functional/sol_v2/samples/sample2/contents/BaseHOT/simple/sample2.yaml
  15. 170
      tacker/tests/functional/sol_v2/samples/sample2/contents/Definitions/v2_sample2_df_simple.yaml
  16. 7
      tacker/tests/functional/sol_v2/samples/sample2/pkggen.py
  17. 429
      tacker/tests/functional/sol_v2/test_vnflcm_basic.py
  18. 42
      tacker/tests/functional/sol_v2/utils.py

6
.zuul.yaml

@ -273,6 +273,12 @@
Multinodes job for SOL V2 devstack-based functional tests
host-vars:
controller-tacker:
devstack_local_conf:
post-config:
# Skip notification endpoint testing in create subscription
$TACKER_CONF:
v2_nfvo:
test_callback_uri: False
tox_envlist: dsvm-functional-sol-v2
- job:

21
tacker/sol_refactored/controller/vnflcm_v2.py

@ -81,19 +81,24 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
vnfSoftwareVersion=pkg_info.vnfSoftwareVersion,
vnfdVersion=pkg_info.vnfdVersion,
instantiationState='NOT_INSTANTIATED',
# optional fields
# NOTE: it is OK to absent but fill empty value to make them
# handle easy.
vnfInstanceName=body.get('vnfInstanceName', ""),
vnfInstanceDescription=body.get('vnfInstanceDescription', ""),
vnfConfigurableProperties=vnfd_prop['vnfConfigurableProperties'],
metadata=metadata,
extensions=vnfd_prop['extensions']
# not set at the moment. will be set when instantiate.
# vimConnectionInfo
# instantiatedVnfInfo
)
# set optional fields
if body.get('vnfInstanceName'):
inst.vnfInstanceName = body['vnfInstanceName']
if body.get('vnfInstanceDescription'):
inst.vnfInstanceDescription = body['vnfInstanceDescription']
if vnfd_prop.get('vnfConfigurableProperties'):
inst.vnfConfigurableProperties = vnfd_prop[
'vnfConfigurableProperties']
if metadata:
inst.metadata = metadata
if vnfd_prop.get('extensions'):
inst.extensions = vnfd_prop['extensions']
inst.create(context)
self.nfvo_client.send_inst_create_notification(context, inst,

202
tacker/tests/functional/sol_v2/base_v2.py

@ -17,6 +17,7 @@ import os
import shutil
import tempfile
import time
import urllib
import yaml
from oslo_config import cfg
@ -38,7 +39,7 @@ VNF_PACKAGE_UPLOAD_TIMEOUT = 300
class BaseSolV2Test(base.BaseTestCase):
"""Base test case class for SOL v2 functionl tests."""
"""Base test case class for SOL v2 functional tests."""
@classmethod
def setUpClass(cls):
@ -63,6 +64,10 @@ class BaseSolV2Test(base.BaseTestCase):
cls.tacker_client = http_client.HttpClient(auth)
cls.neutron_client = http_client.HttpClient(auth,
service_type='network')
cls.glance_client = http_client.HttpClient(auth,
service_type='image')
cls.nova_client = http_client.HttpClient(auth,
service_type='compute')
cls.heat_client = heat_utils.HeatClient(vim_info)
@classmethod
@ -138,6 +143,11 @@ class BaseSolV2Test(base.BaseTestCase):
cls.tacker_client.do_request(path, "DELETE")
def get_vnf_package(self, pkg_id):
path = "/vnfpkgm/v1/vnf_packages/{}".format(pkg_id)
resp, body = self.tacker_client.do_request(path, "GET")
return body
def get_network_ids(self, networks):
path = "/v2.0/networks"
resp, body = self.neutron_client.do_request(path, "GET")
@ -156,6 +166,100 @@ class BaseSolV2Test(base.BaseTestCase):
subnet_ids[subnet['name']] = subnet['id']
return subnet_ids
def create_network(self, name):
path = "/v2.0/networks"
req_body = {
"network": {
# "admin_state_up": true,
"name": name
}
}
try:
resp, resp_body = self.neutron_client.do_request(
path, "POST", body=req_body)
return resp_body['network']['id']
except Exception as e:
self.fail("Failed, create network for name=<%s>, %s" %
(name, e))
def delete_network(self, net_id):
path = "/v2.0/networks/{}".format(net_id)
try:
self.neutron_client.do_request(path, "DELETE")
except Exception as e:
self.fail("Failed, delete network for id=<%s>, %s" %
(net_id, e))
def create_subnet(self, net_id, sub_name, sub_range, ip_version):
path = "/v2.0/subnets"
req_body = {
"subnet": {
"name": sub_name,
"network_id": net_id,
"cidr": sub_range,
"ip_version": ip_version
}
}
try:
resp, resp_body = self.neutron_client.do_request(
path, "POST", body=req_body)
return resp_body['subnet']['id']
except Exception as e:
self.fail("Failed, create subnet for name=<%s>, %s" %
(sub_name, e))
def delete_subnet(self, sub_id):
path = "/v2.0/subnets/{}".format(sub_id)
try:
self.neutron_client.do_request(path, "DELETE")
except Exception as e:
self.fail("Failed, delete subnet for id=<%s>, %s" %
(sub_id, e))
def create_port(self, network_id, name):
path = "/v2.0/ports"
req_body = {
'port': {
'network_id': network_id,
'name': name
}
}
try:
resp, resp_body = self.neutron_client.do_request(
path, "POST", body=req_body)
return resp_body['port']['id']
except Exception as e:
self.fail("Failed, create port for net_id=<%s>, %s" %
(network_id, e))
def delete_port(self, port_id):
path = "/v2.0/ports/{}".format(port_id)
try:
self.neutron_client.do_request(path, "DELETE")
except Exception as e:
self.fail("Failed, delete port for id=<%s>, %s" %
(port_id, e))
def get_image_id(self, image_name):
path = "/v2.0/images"
resp, resp_body = self.glance_client.do_request(path, "GET")
image_id = None
for image in resp_body.get('images'):
if image_name == image['name']:
image_id = image['id']
return image_id
def get_server_details(self, server_name):
path = "/servers/detail"
resp, resp_body = self.nova_client.do_request(path, "GET")
server_details = None
for server in resp_body.get('servers'):
if server_name == server['name']:
server_details = server
return server_details
def create_vnf_instance(self, req_body):
path = "/vnflcm/v2/vnf_instances"
return self.tacker_client.do_request(
@ -171,6 +275,13 @@ class BaseSolV2Test(base.BaseTestCase):
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def list_vnf_instance(self, filter_expr=None):
path = "/vnflcm/v2/vnf_instances"
if filter_expr:
path = "{}?{}".format(path, urllib.parse.urlencode(filter_expr))
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def instantiate_vnf_instance(self, inst_id, req_body):
path = "/vnflcm/v2/vnf_instances/{}/instantiate".format(inst_id)
return self.tacker_client.do_request(
@ -196,3 +307,92 @@ class BaseSolV2Test(base.BaseTestCase):
continue
else: # FAILED_TEMP or ROLLED_BACK
raise Exception("Operation failed. state: %s" % state)
def wait_lcmocc_failed_temp(self, lcmocc_id):
# NOTE: It is not necessary to set timeout because the operation
# itself set timeout and the state will become 'FAILED_TEMP'.
path = "/vnflcm/v2/vnf_lcm_op_occs/{}".format(lcmocc_id)
while True:
time.sleep(5)
_, body = self.tacker_client.do_request(
path, "GET", expected_status=[200], version="2.0.0")
state = body['operationState']
if state == 'FAILED_TEMP':
return
elif state in ['STARTING', 'PROCESSING']:
continue
elif state == 'COMPLETED':
raise Exception("Operation unexpected COMPLETED.")
else: # ROLLED_BACK
raise Exception("Operation failed. state: %s" % state)
def show_lcmocc(self, lcmocc_id):
path = "/vnflcm/v2/vnf_lcm_op_occs/{}".format(lcmocc_id)
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def list_lcmocc(self, filter_expr=None):
path = "/vnflcm/v2/vnf_lcm_op_occs"
if filter_expr:
path = "{}?{}".format(path, urllib.parse.urlencode(filter_expr))
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def create_subscription(self, req_body):
path = "/vnflcm/v2/subscriptions"
return self.tacker_client.do_request(
path, "POST", body=req_body, version="2.0.0")
def delete_subscription(self, sub_id):
path = "/vnflcm/v2/subscriptions/{}".format(sub_id)
return self.tacker_client.do_request(
path, "DELETE", version="2.0.0")
def show_subscription(self, sub_id):
path = "/vnflcm/v2/subscriptions/{}".format(sub_id)
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def list_subscriptions(self, filter_expr=None):
path = "/vnflcm/v2/subscriptions"
if filter_expr:
path = "{}?{}".format(path, urllib.parse.urlencode(filter_expr))
return self.tacker_client.do_request(
path, "GET", version="2.0.0")
def _check_resp_headers(self, resp, supported_headers):
unsupported_headers = ['Link', 'Retry-After',
'Content-Range', 'WWW-Authenticate']
for s in supported_headers:
if s not in resp.headers:
raise Exception("Supported header doesn't exist: %s" % s)
for u in unsupported_headers:
if u in resp.headers:
raise Exception("Unsupported header exist: %s" % u)
def check_resp_headers_in_create(self, resp):
# includes location header and response body
supported_headers = ['Version', 'Location', 'Content-Type',
'Accept-Ranges']
self._check_resp_headers(resp, supported_headers)
def check_resp_headers_in_operation_task(self, resp):
# includes location header and no response body
supported_headers = ['Version', 'Location']
self._check_resp_headers(resp, supported_headers)
def check_resp_headers_in_get(self, resp):
# includes response body and no location header
supported_headers = ['Version', 'Content-Type',
'Accept-Ranges']
self._check_resp_headers(resp, supported_headers)
def check_resp_headers_in_delete(self, resp):
# no location header and response body
supported_headers = ['Version']
self._check_resp_headers(resp, supported_headers)
def check_resp_body(self, body, expected_attrs):
for attr in expected_attrs:
if attr not in body:
raise Exception("Expected attribute doesn't exist: %s" % attr)

487
tacker/tests/functional/sol_v2/paramgen.py

@ -16,193 +16,403 @@
from oslo_utils import uuidutils
def sub1_create():
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
vnf_provider_1 = {
"vnfProvider": "dummy-vnfProvider-1",
"vnfProducts": [
{
"vnfProductName": "dummy-vnfProductName-1-1",
"versions": [
{
"vnfSoftwareVersion": 1.0,
"vnfdVersions": [1.0, 2.0]
},
{
"vnfSoftwareVersion": 1.1,
"vnfdVersions": [1.1, 2.1]
},
]
},
{
"vnfProductName": "dummy-vnfProductName-1-2",
"versions": [
{
"vnfSoftwareVersion": 1.0,
"vnfdVersions": [1.0, 2.0]
},
{
"vnfSoftwareVersion": 1.1,
"vnfdVersions": [1.1, 2.1]
},
]
}
]
}
vnf_provider_2 = {
"vnfProvider": "dummy-vnfProvider-2",
"vnfProducts": [
{
"vnfProductName": "dummy-vnfProductName-2-1",
"versions": [
{
"vnfSoftwareVersion": 1.0,
"vnfdVersions": [1.0, 2.0]
},
{
"vnfSoftwareVersion": 1.1,
"vnfdVersions": [1.1, 2.1]
},
]
},
{
"vnfProductName": "dummy-vnfProductName-2-2",
"versions": [
{
"vnfSoftwareVersion": 1.0,
"vnfdVersions": [1.0, 2.0]
},
{
"vnfSoftwareVersion": 1.1,
"vnfdVersions": [1.1, 2.1]
},
]
}
]
}
# NOTE: The following is omitted because authType is BASIC in this case
# - "paramsOauth2ClientCredentials"
return {
"filter": {
"vnfInstanceSubscriptionFilter": {
"vnfdIds": [
"dummy-vnfdId-1",
"dummy-vnfdId-2"
],
"vnfProductsFromProviders": [
vnf_provider_1,
vnf_provider_2
],
"vnfInstanceIds": [
"dummy-vnfInstanceId-1",
"dummy-vnfInstanceId-2"
],
"vnfInstanceNames": [
"dummy-vnfInstanceName-1",
"dummy-vnfInstanceName-2"
]
},
"notificationTypes": [
"VnfIdentifierCreationNotification",
"VnfLcmOperationOccurrenceNotification"
],
"operationTypes": [
"INSTANTIATE",
"TERMINATE"
],
"operationStates": [
"COMPLETED",
"FAILED"
]
},
"callbackUri": "http://127.0.0.1/",
"authentication": {
"authType": [
"BASIC"
],
"paramsBasic": {
"password": "test_pass",
"userName": "test_user"
},
# "paramsOauth2ClientCredentials": omitted,
},
"verbosity": "SHORT"
}
def sub2_create():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"callbackUri": "http://127.0.0.1/"
}
def sample1_create(vnfd_id):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
return {
"vnfdId": vnfd_id,
"vnfInstanceName": "sample1",
"vnfInstanceDescription": "test sample1"
"vnfInstanceDescription": "test sample1",
"metadata": {"dummy-key": "dummy-val"}
}
def sample1_terminate():
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
return {
"terminationType": "FORCEFUL"
"terminationType": "GRACEFUL",
"gracefulTerminationTimeout": 5,
"additionalParams": {"dummy-key": "dummy-val"}
}
def sample1_instantiate(net_ids, subnet_ids, auth_url):
def sample1_instantiate(net_ids, subnets, ports, auth_url):
# All attributes are set.
# NOTE: All of the following cardinality attributes are set.
# In addition, 0..N or 1..N attributes are set to 2 or more.
# - 0..1 (1)
# - 0..N (2 or more)
# - 1
# - 1..N (2 or more)
vim_id_1 = uuidutils.generate_uuid()
vim_id_2 = uuidutils.generate_uuid()
link_port_id_1 = uuidutils.generate_uuid()
link_port_id_2 = uuidutils.generate_uuid()
# NOTE: The following is not supported so it is omitted
# - "segmentationId"
# - "addressRange"
# - Multiple "cpProtocolData"
# - Multiple "fixedAddresses"
ext_vl_1 = {
"id": uuidutils.generate_uuid(),
"vimConnectionId": vim_id_1,
"resourceProviderId": "Company",
"resourceId": net_ids['net0'],
"extCps": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1_1": {
"VDU1_CP1": {
"parentCpConfigId": uuidutils.generate_uuid(),
# "linkPortId": omitted,
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1}]}}]}
# "fixedAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets['subnet0']}]}}]
},
# { "VDU1_CP1_2": omitted }
}
},
{
"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": net_ids['net1'],
"extCps": [
{
"cpdId": "VDU1_CP2",
"cpdId": "VDU2_CP1-1",
"cpConfig": {
"VDU1_CP2_1": {
"VDU2_CP1-1": {
"parentCpConfigId": uuidutils.generate_uuid(),
"linkPortId": link_port_id_1,
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
# "fixedAddresses": omitted,
"numDynamicAddresses": 1,
"subnetId": subnet_ids['subnet1']}]}}]}
# "addressRange": omitted,
"subnetId": subnets['subnet0']
}]
}
}]
},
# { "VDU2_CP1_2": omitted }
}
},
{
"cpdId": "VDU2_CP2",
"cpdId": "VDU2_CP1-2",
"cpConfig": {
"VDU2_CP2_1": {
"VDU2_CP1-2": {
"parentCpConfigId": uuidutils.generate_uuid(),
"linkPortId": link_port_id_2,
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.10.1.101"],
"subnetId": subnet_ids['subnet1']}]}}]}
# "fixedAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets['subnet0']
}]
}
}]
},
# { "VDU2_CP1_2": omitted }
}
}
]
}
return {
"flavourId": "simple",
"instantiationLevelId": "instantiation_level_1",
"extVirtualLinks": [
ext_vl_1,
ext_vl_2
],
"extManagedVirtualLinks": [
"extLinkPorts": [
{
"id": uuidutils.generate_uuid(),
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": net_ids['net_mgmt']
},
],
"vimConnectionInfo": {
"vim1": {
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"vimId": uuidutils.generate_uuid(),
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
}
}
}
}
def sample2_create(vnfd_id):
return {
"vnfdId": vnfd_id,
"vnfInstanceName": "sample2",
"vnfInstanceDescription": "test sample2"
}
def sample2_terminate():
return {
"terminationType": "GRACEFUL",
"gracefulTerminationTimeout": 5
}
def sample2_instantiate(net_ids, subnet_ids, auth_url):
ext_vl_1 = {
"id": uuidutils.generate_uuid(),
"resourceId": net_ids['net0'],
"extCps": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1}]}}]}
"id": link_port_id_1,
"resourceHandle": {
"resourceId": ports['VDU2_CP1-1']
}
},
# NOTE: Set dummy value because it is set by "additionalParams"
{
"cpdId": "VDU2_CP1",
"cpConfig": {
"VDU2_CP1_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.10.0.102"]}]}}]}
"id": link_port_id_2,
"resourceHandle": {
"resourceId": "dummy-id"
}
}
],
]
}
# NOTE: The following is not supported so it is omitted
# - "segmentationId"
# - "addressRange"
# - Multiple "cpProtocolData"
# - Multiple "fixedAddresses"
ext_vl_2 = {
"id": uuidutils.generate_uuid(),
"resourceId": net_ids['net1'],
"vimConnectionId": vim_id_1,
"resourceProviderId": "Company",
"resourceId": net_ids['ft-net0'],
"extCps": [
{
"cpdId": "VDU1_CP2",
"cpConfig": {
"VDU1_CP2_1": {
"VDU1_CP2": {
"parentCpConfigId": uuidutils.generate_uuid(),
# "linkPortId": omitted,
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
# "macAddress": omitted,
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
# "fixedAddresses": omitted,
"numDynamicAddresses": 1,
"subnetId": subnet_ids['subnet1']}]}}]}
# "addressRange": omitted,
"subnetId": subnets['ft-ipv4-subnet0']}
]}
}]
},
# { "VDU1_CP2_2": omitted }
}
},
{
"cpdId": "VDU2_CP2",
"cpConfig": {
"VDU2_CP2_1": {
"VDU2_CP2": {
"parentCpConfigId": uuidutils.generate_uuid(),
# "linkPortId": omitted,
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"macAddress": "fa:16:3e:fa:22:75",
# "segmentationId": omitted,
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.10.1.102"],
"subnetId": subnet_ids['subnet1']}]}}]}
"fixedAddresses": [
"100.100.100.11",
# omitted
],
# "numDynamicAddresses": omitted,
# "addressRange": omitted,
"subnetId": subnets['ft-ipv4-subnet0']
}, {
"type": "IPV6",
# "fixedAddresses": omitted,
# "numDynamicAddresses": omitted,
"numDynamicAddresses": 1,
# "addressRange": omitted,
"subnetId": subnets['ft-ipv6-subnet0']
}]
}
}]
},
# { "VDU2_CP2_2": omitted }
}
}
]
# "extLinkPorts": omitted
}
# NOTE: "vnfLinkPort" is omitted because it is not supported
ext_mngd_vl_1 = {
"id": uuidutils.generate_uuid(),
"vnfVirtualLinkDescId": "internalVL1",
"vimConnectionId": vim_id_1,
"resourceProviderId": "Company",
"resourceId": net_ids['net_mgmt'],
# "vnfLinkPort": omitted,
"extManagedMultisiteVirtualLinkId": uuidutils.generate_uuid()
}
# NOTE: "vnfLinkPort" is omitted because it is not supported
ext_mngd_vl_2 = {
"id": uuidutils.generate_uuid(),
"vnfVirtualLinkDescId": "internalVL2",
"vimConnectionId": vim_id_1,
"resourceProviderId": "Company",
"resourceId": net_ids['net1'],
# "vnfLinkPort": omitted,
"extManagedMultisiteVirtualLinkId": uuidutils.generate_uuid()
}
vim_1 = {
"vimId": vim_id_1,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"dummy-key": "dummy-val"}
}
vim_2 = {
"vimId": vim_id_2,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "dummy_user",
"region": "RegionOne",
"password": "dummy_password",
"project": "dummy_project",
"projectDomain": "Default",
"userDomain": "Default"
},
"extra": {"dummy-key": "dummy-val"}
}
addParams = {
"lcm-operation-user-data": "./UserData/userdata.py",
"lcm-operation-user-data-class": "UserData",
"nfv": {"CP": {"VDU2_CP1-2": {"port": ports['VDU2_CP1-2']}}}
}
return {
@ -213,29 +423,44 @@ def sample2_instantiate(net_ids, subnet_ids, auth_url):
ext_vl_2
],
"extManagedVirtualLinks": [
{
"id": uuidutils.generate_uuid(),
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": net_ids['net_mgmt']
},
ext_mngd_vl_1,
ext_mngd_vl_2
],
"vimConnectionInfo": {
"vim1": {
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.V_3",
"vimId": uuidutils.generate_uuid(),
"interfaceInfo": {"endpoint": auth_url},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"project": "nfv",
"projectDomain": "Default",
"userDomain": "Default"
}
}
"vim1": vim_1,
"vim2": vim_2
},
"additionalParams": {
"lcm-operation-user-data": "./UserData/userdata_default.py",
"lcm-operation-user-data-class": "DefaultUserData"
}
"localizationLanguage": "ja",
"additionalParams": addParams,
"extensions": {"dummy-key": "dummy-val"}
}
def sample2_create(vnfd_id):
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"vnfdId": vnfd_id,
}
def sample2_terminate():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"terminationType": "FORCEFUL"
}
def sample2_instantiate():
# Omit except for required attributes
# NOTE: Only the following cardinality attributes are set.
# - 1
# - 1..N (1)
return {
"flavourId": "simple"
}

12
tacker/tests/functional/sol_v2/samples/sample1/contents/BaseHOT/simple/nested/VDU1.yaml

@ -18,7 +18,9 @@ parameters:
type: string
net5:
type: string
subnet:
subnet1:
type: string
subnet2:
type: string
resources:
@ -27,7 +29,7 @@ resources:
properties:
flavor: { get_param: flavor }
name: VDU1
block_device_mapping_v2: [{"volume_id": { get_resource: VirtualStorage }}]
block_device_mapping_v2: [{"volume_id": { get_resource: VDU1-VirtualStorage }}]
networks:
- port:
get_resource: VDU1_CP1
@ -44,7 +46,7 @@ resources:
availability_zone: { get_param: zone }
VirtualStorage:
VDU1-VirtualStorage:
type: OS::Cinder::Volume
properties:
image: { get_param: image }
@ -61,6 +63,8 @@ resources:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
fixed_ips:
- subnet: { get_param: subnet1}
# extVL with numDynamicAddresses and subnet
VDU1_CP2:
@ -68,7 +72,7 @@ resources:
properties:
network: { get_param: net2 }
fixed_ips:
- subnet: { get_param: subnet}
- subnet: { get_param: subnet2}
# delete the following line when extmanagedVLs' Ports are specified in instantiatevnfrequest
VDU1_CP3:

29
tacker/tests/functional/sol_v2/samples/sample1/contents/BaseHOT/simple/sample1.yaml

@ -16,11 +16,12 @@ resources:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VirtualStorage, vcImageId ] }
image: { get_param: [ nfv, VDU, VDU1-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 ]}
subnet1: { get_param: [nfv, CP, VDU1_CP1, fixed_ips, 0, subnet ]}
subnet2: { get_param: [nfv, CP, VDU1_CP2, fixed_ips, 0, subnet ]}
net3: { get_resource: internalVL1 }
net4: { get_resource: internalVL2 }
net5: { get_resource: internalVL3 }
@ -44,11 +45,12 @@ resources:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
name: VDU2
availability_zone: { get_param: [ nfv, VDU, VDU2, locationConstraints ] }
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
networks:
- port:
get_resource: VDU2_CP1
- port: { get_param: [ nfv, CP, VDU2_CP1-1, port ] }
- port: { get_param: [ nfv, CP, VDU2_CP1-2, port ] }
- port:
get_resource: VDU2_CP2
- port:
@ -58,13 +60,17 @@ resources:
- port:
get_resource: VDU2_CP5
# extVL with FixedIP
VDU2_CP1:
type: OS::Neutron::Port
VDU2-VirtualStorage:
type: OS::Cinder::Volume
properties:
network: { get_param: [ nfv, CP, VDU2_CP1, network ] }
fixed_ips:
- ip_address: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, ip_address]}
image: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId] }
size: 1
volume_type: { get_resource: multi }
multi:
type: OS::Cinder::VolumeType
properties:
name: VDU2-multi
metadata: { multiattach: "<is> True" }
# extVL with FixedIP and Subnet
VDU2_CP2:
@ -74,6 +80,7 @@ resources:
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]}
- subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 1, subnet]}
VDU2_CP3:
type: OS::Neutron::Port

62
tacker/tests/functional/sol_v2/samples/sample1/contents/Definitions/v2_sample1_df_simple.yaml

@ -34,7 +34,8 @@ topology_template:
flavour_id: simple
requirements:
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
virtual_link_external1_2: [ VDU2_CP1-1, virtual_link ]
virtual_link_external1_3: [ VDU2_CP1-2, virtual_link ]
virtual_link_external2_1: [ VDU1_CP2, virtual_link ]
virtual_link_external2_2: [ VDU2_CP2, virtual_link ]
@ -79,7 +80,7 @@ topology_template:
virtual_local_storage:
- size_of_storage: 3 GB
requirements:
- virtual_storage: VirtualStorage
- virtual_storage: VDU1-VirtualStorage
VDU2:
type: tosca.nodes.nfv.Vdu.Compute
@ -89,17 +90,6 @@ topology_template:
vdu_profile:
min_number_of_instances: 1
max_number_of_instances: 1
sw_image_data:
name: VDU2-image
version: '0.5.2'
checksum:
algorithm: sha-256
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
capabilities:
virtual_compute:
properties:
@ -115,19 +105,39 @@ topology_template:
num_virtual_cpu: 1
virtual_local_storage:
- size_of_storage: 3 GB
requirements:
- virtual_storage: VDU2-VirtualStorage
VDU1-VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1 GB
rdma_enabled: true
sw_image_data:
name: VDU1-VirtualStorage-image
version: '0.5.2'
checksum:
algorithm: sha-256
hash: 932fcae93574e242dc3d772d5235061747dfe537668443a1f0567d893614b464
container_format: bare
disk_format: qcow2
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
VirtualStorage:
VDU2-VirtualStorage:
type: tosca.nodes.nfv.Vdu.VirtualBlockStorage
properties:
virtual_block_storage_data:
size_of_storage: 1 GB
rdma_enabled: true
sw_image_data:
name: cirros-0.5.2-x86_64-disk
name: VDU2-VirtualStorage-image
version: '0.5.2'
checksum:
algorithm: sha-256
@ -137,6 +147,10 @@ topology_template:
min_disk: 0 GB
min_ram: 256 MB
size: 12 GB
artifacts:
sw_image:
type: tosca.artifacts.nfv.SwImage
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
VDU1_CP1:
type: tosca.nodes.nfv.VduCp
@ -181,7 +195,7 @@ topology_template:
- virtual_binding: VDU1
- virtual_link: internalVL3
VDU2_CP1:
VDU2_CP1-1:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
@ -189,7 +203,7 @@ topology_template:
requirements:
- virtual_binding: VDU2
VDU2_CP2:
VDU2_CP1-2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
@ -197,20 +211,28 @@ topology_template:
requirements:
- virtual_binding: VDU2
VDU2_CP3:
VDU2_CP2:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 2
requirements:
- virtual_binding: VDU2
VDU2_CP3:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL1
VDU2_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 3
order: 4
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL2
@ -219,7 +241,7 @@ topology_template:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 4
order: 5
requirements:
- virtual_binding: VDU2
- virtual_link: internalVL3

67
tacker/tests/functional/sol_v2/samples/sample1/contents/Scripts/sample_script.py

@ -0,0 +1,67 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import pickle
import sys
class SampleScript(object):
def __init__(self, req, inst, grant_req, grant, csar_dir):
self.req = req
self.inst = inst
self.grant_req = grant_req
self.grant = grant
self.csar_dir = csar_dir
def instantiate_start(self):
pass
def instantiate_end(self):
pass
def terminate_start(self):
pass
def terminate_end(self):
pass
def main():
script_dict = pickle.load(sys.stdin.buffer)
operation = script_dict['operation']
req = script_dict['request']
inst = script_dict['vnf_instance']
grant_req = script_dict['grant_request']
grant = script_dict['grant_response']
csar_dir = script_dict['tmp_csar_dir']
script = SampleScript(req, inst, grant_req, grant, csar_dir)
try:
getattr(script, operation)()
except AttributeError:
raise Exception("{} is not included in the script.".format(operation))
if __name__ == "__main__":
try:
main()
os._exit(0)
except Exception as ex:
sys.stderr.write(str(ex))
sys.stderr.flush()
os._exit(1)

36
tacker/tests/functional/sol_v2/samples/sample2/contents/UserData/userdata_default.py → tacker/tests/functional/sol_v2/samples/sample1/contents/UserData/userdata.py

@ -19,10 +19,34 @@ from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
class DefaultUserData(userdata_utils.AbstractUserData):
class UserData(userdata_utils.AbstractUserData):
@staticmethod
def instantiate(req, inst, grant_req, grant, tmp_csar_dir):
def _get_param_port(cp_name, grant, req):
# see grant first then instantiateVnfRequest
vls = grant.get('extVirtualLinks', []) + req.get('extVirtualLinks',
[])
port_ids = []
for vl in vls:
link_port_ids = []
for extcp in vl['extCps']:
if extcp['cpdId'] == cp_name:
link_port_ids = _get_link_port_ids_from_extcp(extcp)
if 'extLinkPorts' not in vl:
continue
for extlp in vl['extLinkPorts']:
if extlp['id'] in link_port_ids:
port_ids.append(extlp['resourceHandle']['resourceId'])
return port_ids
def _get_link_port_ids_from_extcp(extcp):
link_port_ids = []
for cp_conf in extcp['cpConfig'].values():
if 'linkPortId' in cp_conf:
link_port_ids.append(cp_conf['linkPortId'])
return link_port_ids
vnfd = userdata_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
flavour_id = req['flavourId']
@ -65,6 +89,16 @@ class DefaultUserData(userdata_utils.AbstractUserData):
'ip_address')
fixed_ips.append(ips_i)
cp_value['fixed_ips'] = fixed_ips
# NOTE: In the case where multiple cpConfigs corresponding
# to a single cpdId are defined, always get the first element
# of cpConfig. This is because, according to the current
# SOL definitions, the key of cpConfig is the ID managed by
# the API consumer, and it is not possible to uniquely determine
# which element of cpConfig should be selected by cpdId.
# See SOL003 v3.3.1 4.4.1.10 Type: VnfExtCpData.
if 'port' in cp_value:
cp_value['port'] = _get_param_port(
cp_name, grant, req).pop()
userdata_utils.apply_ext_managed_vls(top_hot, req, grant)

17
tacker/tests/functional/sol_v2/samples/sample1/pkggen.py

@ -42,11 +42,22 @@ shutil.rmtree(tmp_dir)
create_req = paramgen.sample1_create(vnfd_id)
terminate_req = paramgen.sample1_terminate()
net_ids = utils.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = utils.get_subnet_ids(['subnet0', 'subnet1'])
print('#####################################################################\n'
'# Run pre.py if an error occurs #\n'
'# - If an error occurs, run the pre.py script in advance #\n'
'# to create the openstack resource required to run this script. #\n'
'# Run post.py when you finish tests #\n'
'# - When you no longer need these openstack resources #\n'
'# after testing, run post.py and delete them. #\n'
'#####################################################################')
net_ids = utils.get_network_ids(['net0', 'net1', 'net_mgmt', 'ft-net0'])
subnet_ids = utils.get_subnet_ids(
['subnet0', 'subnet1', 'ft-ipv4-subnet0', 'ft-ipv6-subnet0'])
port_ids = utils.get_port_ids(['VDU2_CP1-1', 'VDU2_CP1-2'])
instantiate_req = paramgen.sample1_instantiate(
net_ids, subnet_ids, "http://localhost/identity/v3")
net_ids, subnet_ids, port_ids, "http://localhost/identity/v3")
with open("create_req", "w") as f:
f.write(json.dumps(create_req, indent=2))

20
tacker/tests/functional/sol_v2/samples/sample1/post.py

@ -0,0 +1,20 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tacker.tests.functional.sol_v2 import utils
utils.delete_network('ft-net0')
# NOTE: subnet is automatically deleted by network deletion
utils.delete_port('VDU2_CP1-1')
utils.delete_port('VDU2_CP1-2')

21
tacker/tests/functional/sol_v2/samples/sample1/pre.py

@ -0,0 +1,21 @@
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.