Merge "Support individual VNFc management using HOT"
This commit is contained in:
commit
213f5f59ba
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Support individual VNFC management using OpenStack Heat.
|
||||
New sample BaseHOT, corresponding UserData script,
|
||||
and utility functions are added to UserData class
|
||||
for v2 VNF Lifecycle management API.
|
|
@ -94,7 +94,7 @@ class HeatClient(object):
|
|||
LOG.info("%s %s done.", operation, stack_name)
|
||||
raise loopingcall.LoopingCallDone()
|
||||
elif status in failed_status:
|
||||
LOG.error("% %s failed.", operation, stack_name)
|
||||
LOG.error("%s %s failed.", operation, stack_name)
|
||||
sol_title = "%s failed" % operation
|
||||
raise sol_ex.StackOperationFailed(sol_title=sol_title,
|
||||
sol_detail=status_reason)
|
||||
|
|
|
@ -18,6 +18,7 @@ import json
|
|||
import os
|
||||
import pickle
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
from dateutil import parser
|
||||
import eventlet
|
||||
|
@ -67,6 +68,19 @@ def _make_combination_id(a, b):
|
|||
return '{}-{}'.format(a, b)
|
||||
|
||||
|
||||
def _get_vdu_idx(vdu_with_idx):
|
||||
part = vdu_with_idx.rpartition('-')
|
||||
if part[1] == '':
|
||||
return None
|
||||
return int(part[2])
|
||||
|
||||
|
||||
def _vdu_with_idx(vdu, vdu_idx):
|
||||
if vdu_idx is None:
|
||||
return vdu
|
||||
return f'{vdu}-{vdu_idx}'
|
||||
|
||||
|
||||
class Openstack(object):
|
||||
|
||||
def __init__(self):
|
||||
|
@ -75,7 +89,6 @@ class Openstack(object):
|
|||
def instantiate(self, req, inst, grant_req, grant, vnfd):
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd)
|
||||
LOG.debug("stack fields: %s", fields)
|
||||
|
||||
# create or update stack
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
|
@ -113,21 +126,27 @@ class Openstack(object):
|
|||
stack_name = heat_utils.get_stack_name(inst)
|
||||
heat_client.delete_stack(stack_name)
|
||||
|
||||
def _update_nfv_dict(self, heat_client, stack_name, fields):
|
||||
parameters = heat_client.get_parameters(stack_name)
|
||||
LOG.debug("ORIG parameters: %s", parameters)
|
||||
# NOTE: parameters['nfv'] is string
|
||||
orig_nfv_dict = json.loads(parameters.get('nfv', '{}'))
|
||||
if 'nfv' in fields['parameters']:
|
||||
def _update_fields(self, heat_client, stack_name, fields):
|
||||
if 'nfv' in fields.get('parameters', {}):
|
||||
parameters = heat_client.get_parameters(stack_name)
|
||||
LOG.debug("ORIG parameters: %s", parameters)
|
||||
# NOTE: parameters['nfv'] is string
|
||||
orig_nfv_dict = json.loads(parameters.get('nfv', '{}'))
|
||||
fields['parameters']['nfv'] = inst_utils.json_merge_patch(
|
||||
orig_nfv_dict, fields['parameters']['nfv'])
|
||||
LOG.debug("NEW parameters: %s", fields['parameters'])
|
||||
|
||||
if 'template' in fields:
|
||||
orig_template = heat_client.get_template(stack_name)
|
||||
template = inst_utils.json_merge_patch(
|
||||
orig_template, yaml.safe_load(fields['template']))
|
||||
fields['template'] = yaml.safe_dump(template)
|
||||
|
||||
LOG.debug("NEW fields: %s", fields)
|
||||
return fields
|
||||
|
||||
def scale(self, req, inst, grant_req, grant, vnfd):
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd)
|
||||
LOG.debug("stack fields: %s", fields)
|
||||
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
|
@ -139,17 +158,19 @@ class Openstack(object):
|
|||
if res_def.type == 'COMPUTE']
|
||||
for vnfc in inst.instantiatedVnfInfo.vnfcResourceInfo:
|
||||
if vnfc.computeResource.resourceId in vnfc_res_ids:
|
||||
if 'parent_stack_id' not in vnfc.metadata:
|
||||
if 'parent_stack_id' in vnfc.metadata:
|
||||
# AutoScalingGroup
|
||||
heat_client.mark_unhealthy(
|
||||
vnfc.metadata['parent_stack_id'],
|
||||
vnfc.metadata['parent_resource_name'])
|
||||
elif 'vdu_idx' not in vnfc.metadata:
|
||||
# It means definition of VDU in the BaseHOT
|
||||
# is inappropriate.
|
||||
raise sol_ex.UnexpectedParentResourceDefinition()
|
||||
heat_client.mark_unhealthy(
|
||||
vnfc.metadata['parent_stack_id'],
|
||||
vnfc.metadata['parent_resource_name'])
|
||||
|
||||
# update stack
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
fields = self._update_nfv_dict(heat_client, stack_name, fields)
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
heat_client.update_stack(stack_name, fields)
|
||||
|
||||
# make instantiated_vnf_info
|
||||
|
@ -158,6 +179,10 @@ class Openstack(object):
|
|||
|
||||
def scale_rollback(self, req, inst, grant_req, grant, vnfd):
|
||||
# NOTE: rollback is supported for scale out only
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd,
|
||||
is_rollback=True)
|
||||
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
|
@ -169,19 +194,18 @@ class Openstack(object):
|
|||
for res in heat_utils.get_server_reses(heat_reses):
|
||||
if res['physical_resource_id'] not in vnfc_ids:
|
||||
metadata = self._make_vnfc_metadata(res, heat_reses)
|
||||
if 'parent_stack_id' not in metadata:
|
||||
if 'parent_stack_id' in metadata:
|
||||
# AutoScalingGroup
|
||||
heat_client.mark_unhealthy(
|
||||
metadata['parent_stack_id'],
|
||||
metadata['parent_resource_name'])
|
||||
elif 'vdu_idx' not in metadata:
|
||||
# It means definition of VDU in the BaseHOT
|
||||
# is inappropriate.
|
||||
raise sol_ex.UnexpectedParentResourceDefinition()
|
||||
heat_client.mark_unhealthy(
|
||||
metadata['parent_stack_id'],
|
||||
metadata['parent_resource_name'])
|
||||
|
||||
# update (put back) 'desired_capacity' parameter
|
||||
fields = self._update_nfv_dict(heat_client, stack_name,
|
||||
userdata_default.DefaultUserData.scale_rollback(
|
||||
req, inst, grant_req, grant, vnfd.csar_dir))
|
||||
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
heat_client.update_stack(stack_name, fields)
|
||||
|
||||
# NOTE: instantiatedVnfInfo is not necessary to update since it
|
||||
|
@ -190,13 +214,12 @@ class Openstack(object):
|
|||
def change_ext_conn(self, req, inst, grant_req, grant, vnfd):
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd)
|
||||
LOG.debug("stack fields: %s", fields)
|
||||
|
||||
# update stack
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
fields = self._update_nfv_dict(heat_client, stack_name, fields)
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
heat_client.update_stack(stack_name, fields)
|
||||
|
||||
# make instantiated_vnf_info
|
||||
|
@ -204,31 +227,33 @@ class Openstack(object):
|
|||
heat_client)
|
||||
|
||||
def change_ext_conn_rollback(self, req, inst, grant_req, grant, vnfd):
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd,
|
||||
is_rollback=True)
|
||||
|
||||
# update stack
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
fields = self._update_nfv_dict(heat_client, stack_name,
|
||||
userdata_default.DefaultUserData.change_ext_conn_rollback(
|
||||
req, inst, grant_req, grant, vnfd.csar_dir))
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
heat_client.update_stack(stack_name, fields)
|
||||
|
||||
# NOTE: it is necessary to re-create instantiatedVnfInfo because
|
||||
# ports may be changed.
|
||||
self._make_instantiated_vnf_info(req, inst, grant_req, grant, vnfd,
|
||||
heat_client)
|
||||
heat_client, is_rollback=True)
|
||||
|
||||
def heal(self, req, inst, grant_req, grant, vnfd):
|
||||
# make HOT
|
||||
# NOTE: _make_hot() is called as other operations, but it returns
|
||||
# empty 'nfv' dict by default. Therefore _update_nfv_dict() returns
|
||||
# empty 'nfv' dict by default. Therefore _update_fields() returns
|
||||
# current heat parameters as is.
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd)
|
||||
LOG.debug("stack fields: %s", fields)
|
||||
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
fields = self._update_nfv_dict(heat_client, stack_name, fields)
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
|
||||
# "re_create" is set to True only when SOL003 heal(without
|
||||
# vnfcInstanceId) and "all=True" in additionalParams.
|
||||
|
@ -284,7 +309,6 @@ class Openstack(object):
|
|||
def change_vnfpkg(self, req, inst, grant_req, grant, vnfd):
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd)
|
||||
LOG.debug("stack fields: %s", fields)
|
||||
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
|
@ -298,13 +322,35 @@ class Openstack(object):
|
|||
|
||||
# update stack
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
fields = self._update_nfv_dict(heat_client, stack_name, fields)
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
heat_client.update_stack(stack_name, fields)
|
||||
|
||||
# make instantiated_vnf_info
|
||||
self._make_instantiated_vnf_info(req, inst, grant_req, grant, vnfd,
|
||||
heat_client)
|
||||
|
||||
def _get_flavor_from_vdu_dict(self, vnfc, vdu_dict):
|
||||
vdu_name = _vdu_with_idx(vnfc.vduId, vnfc.metadata.get('vdu_idx'))
|
||||
return vdu_dict.get(vdu_name, {}).get('computeFlavourId')
|
||||
|
||||
def _get_images_from_vdu_dict(self, vnfc, storage_infos, vdu_dict):
|
||||
vdu_idx = vnfc.metadata.get('vdu_idx')
|
||||
vdu_name = _vdu_with_idx(vnfc.vduId, vdu_idx)
|
||||
vdu_names = [vdu_name]
|
||||
if vnfc.obj_attr_is_set('storageResourceIds'):
|
||||
vdu_names += [
|
||||
_vdu_with_idx(storage_info.virtualStorageDescId, vdu_idx)
|
||||
for storage_info in storage_infos
|
||||
if storage_info.id in vnfc.storageResourceIds
|
||||
]
|
||||
images = {}
|
||||
for vdu_name in vdu_names:
|
||||
image = vdu_dict.get(vdu_name, {}).get('vcImageId')
|
||||
if image:
|
||||
images[vdu_name] = image
|
||||
|
||||
return images
|
||||
|
||||
def _change_vnfpkg_rolling_update(self, req, inst, grant_req, grant,
|
||||
vnfd, fields, heat_client, is_rollback):
|
||||
if not grant_req.obj_attr_is_set('removeResources'):
|
||||
|
@ -348,30 +394,25 @@ class Openstack(object):
|
|||
LOG.debug("stack fields: %s", vdu_fields)
|
||||
heat_client.update_stack(parent_stack_id, vdu_fields)
|
||||
else:
|
||||
# handle single VM
|
||||
# pickup 'vcImageId' and 'computeFlavourId'
|
||||
# pickup 'vcImageId' and 'computeFlavourId' from vdu_dict
|
||||
vdu_name = _vdu_with_idx(vnfc.vduId,
|
||||
vnfc.metadata.get('vdu_idx'))
|
||||
params = {}
|
||||
if vdu_dict[vnfc.vduId].get('computeFlavourId'):
|
||||
params[vnfc.vduId] = {'computeFlavourId':
|
||||
vdu_dict[vnfc.vduId]['computeFlavourId']}
|
||||
vdu_names = [vnfc.vduId]
|
||||
if vnfc.obj_attr_is_set('storageResourceIds'):
|
||||
storage_infos = (
|
||||
inst.instantiatedVnfInfo.virtualStorageResourceInfo)
|
||||
vdu_names += [
|
||||
storage_info.virtualStorageDescId
|
||||
for storage_info in storage_infos
|
||||
if storage_info.id in vnfc.storageResourceIds
|
||||
]
|
||||
for vdu_name in vdu_names:
|
||||
image = vdu_dict.get(vdu_name, {}).get('vcImageId')
|
||||
if image:
|
||||
params.setdefault(vdu_name, {})
|
||||
params[vdu_name]['vcImageId'] = image
|
||||
flavor = self._get_flavor_from_vdu_dict(vnfc, vdu_dict)
|
||||
if flavor:
|
||||
params[vdu_name] = {'computeFlavourId': flavor}
|
||||
storage_infos = (
|
||||
inst.instantiatedVnfInfo.virtualStorageResourceInfo
|
||||
if vnfc.obj_attr_is_set('storageResourceIds') else [])
|
||||
images = self._get_images_from_vdu_dict(vnfc,
|
||||
storage_infos, vdu_dict)
|
||||
for vdu_name, image in images.items():
|
||||
params.setdefault(vdu_name, {})
|
||||
params[vdu_name]['vcImageId'] = image
|
||||
update_nfv_dict = {'nfv': {'VDU': params}}
|
||||
update_fields = {'parameters': update_nfv_dict}
|
||||
|
||||
update_fields = self._update_nfv_dict(heat_client, stack_name,
|
||||
update_fields = self._update_fields(heat_client, stack_name,
|
||||
update_fields)
|
||||
LOG.debug("stack fields: %s", update_fields)
|
||||
heat_client.update_stack(stack_name, update_fields)
|
||||
|
@ -429,8 +470,9 @@ class Openstack(object):
|
|||
|
||||
def change_vnfpkg_rollback(self, req, inst, grant_req, grant, vnfd,
|
||||
lcmocc):
|
||||
fields = userdata_default.DefaultUserData.change_vnfpkg_rollback(
|
||||
req, inst, grant_req, grant, vnfd.csar_dir)
|
||||
# make HOT
|
||||
fields = self._make_hot(req, inst, grant_req, grant, vnfd,
|
||||
is_rollback=True)
|
||||
|
||||
vim_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
|
||||
heat_client = heat_utils.HeatClient(vim_info)
|
||||
|
@ -444,15 +486,15 @@ class Openstack(object):
|
|||
|
||||
# stack update
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
fields = self._update_nfv_dict(heat_client, stack_name, fields)
|
||||
fields = self._update_fields(heat_client, stack_name, fields)
|
||||
heat_client.update_stack(stack_name, fields)
|
||||
|
||||
# NOTE: it is necessary to re-create instantiatedVnfInfo because
|
||||
# some resources may be changed.
|
||||
self._make_instantiated_vnf_info(req, inst, grant_req, grant, vnfd,
|
||||
heat_client)
|
||||
heat_client, is_rollback=True)
|
||||
|
||||
def _make_hot(self, req, inst, grant_req, grant, vnfd):
|
||||
def _make_hot(self, req, inst, grant_req, grant, vnfd, is_rollback=False):
|
||||
if grant_req.operation == v2fields.LcmOperationType.INSTANTIATE:
|
||||
flavour_id = req.flavourId
|
||||
else:
|
||||
|
@ -470,10 +512,12 @@ class Openstack(object):
|
|||
'lcm-operation-user-data-class')
|
||||
|
||||
if userdata is None and userdata_class is None:
|
||||
LOG.debug("Processing default userdata %s", grant_req.operation)
|
||||
operation = grant_req.operation.lower()
|
||||
if is_rollback:
|
||||
operation = operation + '_rollback'
|
||||
LOG.debug("Processing default userdata %s", operation)
|
||||
# NOTE: objects used here are dict compat.
|
||||
method = getattr(userdata_default.DefaultUserData,
|
||||
grant_req.operation.lower())
|
||||
method = getattr(userdata_default.DefaultUserData, operation)
|
||||
fields = method(req, inst, grant_req, grant, vnfd.csar_dir)
|
||||
elif userdata is None or userdata_class is None:
|
||||
# Both must be specified.
|
||||
|
@ -488,7 +532,8 @@ class Openstack(object):
|
|||
'vnf_instance': inst.to_dict(),
|
||||
'grant_request': grant_req.to_dict(),
|
||||
'grant_response': grant.to_dict(),
|
||||
'tmp_csar_dir': tmp_csar_dir
|
||||
'tmp_csar_dir': tmp_csar_dir,
|
||||
'is_rollback': is_rollback
|
||||
}
|
||||
script_path = os.path.join(
|
||||
os.path.dirname(__file__), "userdata_main.py")
|
||||
|
@ -510,6 +555,8 @@ class Openstack(object):
|
|||
fields['timeout_mins'] = (
|
||||
CONF.v2_vnfm.openstack_vim_stack_create_timeout)
|
||||
|
||||
LOG.debug("stack fields: %s", fields)
|
||||
|
||||
return fields
|
||||
|
||||
def _get_checked_reses(self, nodes, reses):
|
||||
|
@ -895,9 +942,16 @@ class Openstack(object):
|
|||
}
|
||||
parent_res = heat_utils.get_parent_resource(server_res, heat_reses)
|
||||
if parent_res:
|
||||
metadata['parent_stack_id'] = (
|
||||
heat_utils.get_resource_stack_id(parent_res))
|
||||
metadata['parent_resource_name'] = parent_res['resource_name']
|
||||
parent_parent_res = heat_utils.get_parent_resource(parent_res,
|
||||
heat_reses)
|
||||
if (parent_parent_res and
|
||||
parent_parent_res['resource_type'] ==
|
||||
'OS::Heat::AutoScalingGroup'):
|
||||
metadata['parent_stack_id'] = (
|
||||
heat_utils.get_resource_stack_id(parent_res))
|
||||
metadata['parent_resource_name'] = parent_res['resource_name']
|
||||
else:
|
||||
metadata['vdu_idx'] = _get_vdu_idx(parent_res['resource_name'])
|
||||
|
||||
return metadata
|
||||
|
||||
|
@ -917,22 +971,16 @@ class Openstack(object):
|
|||
if k.startswith('image-'):
|
||||
metadata[k] = v
|
||||
else:
|
||||
properties = nfv_dict['VDU'][vnfc_res_info.vduId]
|
||||
metadata['flavor'] = properties['computeFlavourId']
|
||||
vdu_names = [vnfc_res_info.vduId]
|
||||
if vnfc_res_info.obj_attr_is_set('storageResourceIds'):
|
||||
vdu_names += [
|
||||
storage_info.virtualStorageDescId
|
||||
for storage_info in storage_infos
|
||||
if storage_info.id in vnfc_res_info.storageResourceIds
|
||||
]
|
||||
for vdu_name in vdu_names:
|
||||
image = nfv_dict['VDU'].get(vdu_name, {}).get('vcImageId')
|
||||
if image:
|
||||
metadata[f'image-{vdu_name}'] = image
|
||||
# assume it is found
|
||||
metadata['flavor'] = self._get_flavor_from_vdu_dict(
|
||||
vnfc_res_info, nfv_dict['VDU'])
|
||||
images = self._get_images_from_vdu_dict(vnfc_res_info,
|
||||
storage_infos, nfv_dict['VDU'])
|
||||
for vdu_name, image in images.items():
|
||||
metadata[f'image-{vdu_name}'] = image
|
||||
|
||||
def _make_instantiated_vnf_info(self, req, inst, grant_req, grant, vnfd,
|
||||
heat_client):
|
||||
heat_client, is_rollback=False):
|
||||
# get heat resources
|
||||
stack_name = heat_utils.get_stack_name(inst)
|
||||
heat_reses = heat_client.get_resources(stack_name)
|
||||
|
@ -1029,7 +1077,8 @@ class Openstack(object):
|
|||
flavour_id, req, grant)
|
||||
else:
|
||||
old_inst_vnf_info = inst.instantiatedVnfInfo
|
||||
if op == v2fields.LcmOperationType.CHANGE_EXT_CONN:
|
||||
if (op == v2fields.LcmOperationType.CHANGE_EXT_CONN and
|
||||
not is_rollback):
|
||||
ext_vl_infos = self._make_ext_vl_info_from_req_and_inst(
|
||||
req, grant, old_inst_vnf_info, ext_cp_infos)
|
||||
else:
|
||||
|
@ -1107,7 +1156,9 @@ class Openstack(object):
|
|||
# grant request.
|
||||
|
||||
def _get_key(vnfc):
|
||||
return parser.isoparse(vnfc.metadata['creation_time'])
|
||||
vdu_idx = vnfc.metadata.get('vdu_idx', 0)
|
||||
creation_time = parser.isoparse(vnfc.metadata['creation_time'])
|
||||
return (vdu_idx, creation_time)
|
||||
|
||||
sorted_vnfc_res_infos = sorted(vnfc_res_infos, key=_get_key,
|
||||
reverse=True)
|
||||
|
|
|
@ -39,7 +39,10 @@ def main():
|
|||
module = importlib.import_module(class_module)
|
||||
klass = getattr(module, userdata_class)
|
||||
|
||||
method = getattr(klass, grant_req['operation'].lower())
|
||||
operation = grant_req['operation'].lower()
|
||||
if script_dict['is_rollback']:
|
||||
operation = operation + '_rollback'
|
||||
method = getattr(klass, operation)
|
||||
stack_dict = method(req, inst, grant_req, grant, tmp_csar_dir)
|
||||
|
||||
pickle.dump(stack_dict, sys.stdout.buffer)
|
||||
|
|
|
@ -0,0 +1,544 @@
|
|||
# Copyright (C) 2022 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 copy
|
||||
import yaml
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.infra_drivers.openstack import userdata_utils
|
||||
|
||||
|
||||
def add_idx(name, index):
|
||||
return f'{name}-{index}'
|
||||
|
||||
|
||||
def rm_idx(name_idx):
|
||||
return name_idx.rpartition('-')[0]
|
||||
|
||||
|
||||
def add_idx_to_vdu_template(vdu_template, vdu_idx):
|
||||
"""Add index to the third element of get_param
|
||||
|
||||
ex. input VDU template:
|
||||
---
|
||||
VDU1:
|
||||
type: VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image-VDU1: { get_param: [ nfv, VDU, VDU1, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network ] }
|
||||
---
|
||||
|
||||
output VDU template:
|
||||
---
|
||||
VDU1:
|
||||
type: VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1-1, computeFlavourId ] }
|
||||
image-VDU1: { get_param: [ nfv, VDU, VDU1-1, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1-1, network ] }
|
||||
---
|
||||
"""
|
||||
res = copy.deepcopy(vdu_template)
|
||||
for prop_value in res.get('properties', {}).values():
|
||||
get_param = prop_value.get('get_param')
|
||||
if (get_param is not None and
|
||||
isinstance(get_param, list) and len(get_param) >= 4):
|
||||
get_param[2] = add_idx(get_param[2], vdu_idx)
|
||||
return res
|
||||
|
||||
|
||||
class StandardUserData(userdata_utils.AbstractUserData):
|
||||
|
||||
@staticmethod
|
||||
def instantiate(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = req['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
# first modify VDU resources
|
||||
poped_vdu = {}
|
||||
vdu_idxes = {}
|
||||
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
|
||||
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
|
||||
vdu_idxes[vdu_name] = 0
|
||||
|
||||
for res in grant_req['addResources']:
|
||||
if res['type'] != 'COMPUTE':
|
||||
continue
|
||||
vdu_name = res['resourceTemplateId']
|
||||
if vdu_name not in poped_vdu:
|
||||
continue
|
||||
vdu_idx = vdu_idxes[vdu_name]
|
||||
vdu_idxes[vdu_name] += 1
|
||||
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
|
||||
top_hot['resources'][add_idx(vdu_name, vdu_idx)] = res
|
||||
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
vdu_name = rm_idx(vdu_name)
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value['locationConstraints'] = (
|
||||
common_script_utils.get_param_zone(
|
||||
vdu_name, grant_req, grant))
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
for cp_name, cp_value in cps.items():
|
||||
cp_name = rm_idx(cp_name)
|
||||
if 'network' in cp_value:
|
||||
cp_value['network'] = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
if i not in cp_value['fixed_ips']:
|
||||
break
|
||||
ips_i = cp_value['fixed_ips'][i]
|
||||
if 'subnet' in ips_i:
|
||||
ips_i['subnet'] = ext_fixed_ips[i].get('subnet')
|
||||
if 'ip_address' in ips_i:
|
||||
ips_i['ip_address'] = ext_fixed_ips[i].get(
|
||||
'ip_address')
|
||||
fixed_ips.append(ips_i)
|
||||
cp_value['fixed_ips'] = fixed_ips
|
||||
|
||||
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
|
||||
|
||||
if 'nfv' in req.get('additionalParams', {}):
|
||||
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
|
||||
req['additionalParams']['nfv'])
|
||||
if 'nfv' in grant.get('additionalParams', {}):
|
||||
nfv_dict = inst_utils.json_merge_patch(nfv_dict,
|
||||
grant['additionalParams']['nfv'])
|
||||
|
||||
fields = {
|
||||
'template': yaml.safe_dump(top_hot),
|
||||
'parameters': {'nfv': nfv_dict},
|
||||
'files': {}
|
||||
}
|
||||
for key, value in hot_dict.get('files', {}).items():
|
||||
fields['files'][key] = yaml.safe_dump(value)
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def scale(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
if req['type'] == 'SCALE_OUT':
|
||||
return StandardUserData._scale_out(req, inst, grant_req, grant,
|
||||
tmp_csar_dir)
|
||||
else:
|
||||
return StandardUserData._scale_in(req, inst, grant_req, grant,
|
||||
tmp_csar_dir)
|
||||
|
||||
@staticmethod
|
||||
def _scale_out(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
# first modify VDU resources
|
||||
poped_vdu = {}
|
||||
vdu_idxes = {}
|
||||
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
|
||||
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
|
||||
vdu_idxes[vdu_name] = common_script_utils.get_current_capacity(
|
||||
vdu_name, inst)
|
||||
|
||||
for res in grant_req['addResources']:
|
||||
if res['type'] != 'COMPUTE':
|
||||
continue
|
||||
vdu_name = res['resourceTemplateId']
|
||||
if vdu_name not in poped_vdu:
|
||||
continue
|
||||
vdu_idx = vdu_idxes[vdu_name]
|
||||
vdu_idxes[vdu_name] += 1
|
||||
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
|
||||
top_hot['resources'][add_idx(vdu_name, vdu_idx)] = res
|
||||
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
vdu_name = rm_idx(vdu_name)
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value['locationConstraints'] = (
|
||||
common_script_utils.get_param_zone(
|
||||
vdu_name, grant_req, grant))
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
for cp_name, cp_value in cps.items():
|
||||
cp_name = rm_idx(cp_name)
|
||||
if 'network' in cp_value:
|
||||
cp_value['network'] = (
|
||||
common_script_utils.get_param_network_from_inst(
|
||||
cp_name, inst))
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = (
|
||||
common_script_utils.get_param_fixed_ips_from_inst(
|
||||
cp_name, inst))
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
if i not in cp_value['fixed_ips']:
|
||||
break
|
||||
ips_i = cp_value['fixed_ips'][i]
|
||||
if 'subnet' in ips_i:
|
||||
ips_i['subnet'] = ext_fixed_ips[i].get('subnet')
|
||||
if 'ip_address' in ips_i:
|
||||
ips_i['ip_address'] = ext_fixed_ips[i].get(
|
||||
'ip_address')
|
||||
fixed_ips.append(ips_i)
|
||||
cp_value['fixed_ips'] = fixed_ips
|
||||
|
||||
fields = {
|
||||
'template': yaml.safe_dump(top_hot),
|
||||
'parameters': {'nfv': nfv_dict}
|
||||
}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def _scale_in(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
vdu_nodes = vnfd.get_vdu_nodes(flavour_id)
|
||||
template = {'resources': {}}
|
||||
vdus = {}
|
||||
cps = {}
|
||||
for res in grant_req['removeResources']:
|
||||
if res['type'] != 'COMPUTE':
|
||||
continue
|
||||
for inst_vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
|
||||
if (inst_vnfc['computeResource']['resourceId'] ==
|
||||
res['resource']['resourceId']):
|
||||
# must be found
|
||||
vdu_idx = inst_vnfc['metadata']['vdu_idx']
|
||||
break
|
||||
vdu_name = res['resourceTemplateId']
|
||||
template['resources'][add_idx(vdu_name, vdu_idx)] = None
|
||||
vdus[add_idx(vdu_name, vdu_idx)] = None
|
||||
|
||||
for str_name in vnfd.get_vdu_storages(vdu_nodes[vdu_name]):
|
||||
# may overspec, but no problem
|
||||
vdus[add_idx(str_name, vdu_idx)] = None
|
||||
|
||||
for cp_name in vnfd.get_vdu_cps(flavour_id, vdu_name):
|
||||
# may overspec, but no problem
|
||||
cps[add_idx(cp_name, vdu_idx)] = None
|
||||
|
||||
fields = {
|
||||
'template': yaml.safe_dump(template),
|
||||
'parameters': {'nfv': {'VDU': vdus, 'CP': cps}}
|
||||
}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def scale_rollback(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
vdu_nodes = vnfd.get_vdu_nodes(flavour_id)
|
||||
vdu_idxes = {}
|
||||
for vdu_name in vdu_nodes.keys():
|
||||
vdu_idxes[vdu_name] = common_script_utils.get_current_capacity(
|
||||
vdu_name, inst)
|
||||
|
||||
template = {'resources': {}}
|
||||
vdus = {}
|
||||
cps = {}
|
||||
for res in grant_req['addResources']:
|
||||
if res['type'] != 'COMPUTE':
|
||||
continue
|
||||
vdu_name = res['resourceTemplateId']
|
||||
vdu_idx = vdu_idxes[vdu_name]
|
||||
vdu_idxes[vdu_name] += 1
|
||||
template['resources'][add_idx(vdu_name, vdu_idx)] = None
|
||||
vdus[add_idx(vdu_name, vdu_idx)] = None
|
||||
|
||||
for str_name in vnfd.get_vdu_storages(vdu_nodes[vdu_name]):
|
||||
# may overspec, but no problem
|
||||
vdus[add_idx(str_name, vdu_idx)] = None
|
||||
|
||||
for cp_name in vnfd.get_vdu_cps(flavour_id, vdu_name):
|
||||
# may overspec, but no problem
|
||||
cps[add_idx(cp_name, vdu_idx)] = None
|
||||
|
||||
fields = {
|
||||
'template': yaml.safe_dump(template),
|
||||
'parameters': {'nfv': {'VDU': vdus, 'CP': cps}}
|
||||
}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def change_ext_conn(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
# change_ext_conn is interested in 'CP' only.
|
||||
# This method returns only 'CP' part in the 'nfv' dict from
|
||||
# ChangeExtVnfConnectivityRequest.
|
||||
# It is applied to json merge patch against the existing 'nfv'
|
||||
# dict by the caller.
|
||||
# NOTE: complete 'nfv' dict can not be made at the moment
|
||||
# since InstantiateVnfRequest is necessary to make it.
|
||||
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
# first modify VDU resources
|
||||
poped_vdu = {}
|
||||
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
|
||||
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
|
||||
|
||||
for inst_vnfc in inst['instantiatedVnfInfo'].get(
|
||||
'vnfcResourceInfo', []):
|
||||
vdu_idx = inst_vnfc['metadata'].get('vdu_idx')
|
||||
if vdu_idx is None:
|
||||
continue
|
||||
vdu_name = inst_vnfc['vduId']
|
||||
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
|
||||
top_hot['resources'][add_idx(vdu_name, vdu_idx)] = res
|
||||
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
new_cps = {}
|
||||
for cp_name_idx, cp_value in cps.items():
|
||||
cp_name = rm_idx(cp_name_idx)
|
||||
if 'network' in cp_value:
|
||||
network = common_script_utils.get_param_network(
|
||||
cp_name, grant, req)
|
||||
if network is None:
|
||||
continue
|
||||
new_cps.setdefault(cp_name_idx, {})
|
||||
new_cps[cp_name_idx]['network'] = network
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = common_script_utils.get_param_fixed_ips(
|
||||
cp_name, grant, req)
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
if i not in cp_value['fixed_ips']:
|
||||
break
|
||||
ips_i = cp_value['fixed_ips'][i]
|
||||
if 'subnet' in ips_i:
|
||||
ips_i['subnet'] = ext_fixed_ips[i].get('subnet')
|
||||
if 'ip_address' in ips_i:
|
||||
ips_i['ip_address'] = ext_fixed_ips[i].get(
|
||||
'ip_address')
|
||||
fixed_ips.append(ips_i)
|
||||
new_cps.setdefault(cp_name_idx, {})
|
||||
new_cps[cp_name_idx]['fixed_ips'] = fixed_ips
|
||||
|
||||
fields = {'parameters': {'nfv': {'CP': new_cps}}}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def change_ext_conn_rollback(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
# first modify VDU resources
|
||||
poped_vdu = {}
|
||||
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
|
||||
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
|
||||
|
||||
for inst_vnfc in inst['instantiatedVnfInfo'].get(
|
||||
'vnfcResourceInfo', []):
|
||||
vdu_idx = inst_vnfc['metadata'].get('vdu_idx')
|
||||
if vdu_idx is None:
|
||||
continue
|
||||
vdu_name = inst_vnfc['vduId']
|
||||
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
|
||||
top_hot['resources'][add_idx(vdu_name, vdu_idx)] = res
|
||||
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
cps = nfv_dict.get('CP', {})
|
||||
new_cps = {}
|
||||
for cp_name_idx, cp_value in cps.items():
|
||||
cp_name = rm_idx(cp_name_idx)
|
||||
if 'network' in cp_value:
|
||||
network = common_script_utils.get_param_network_from_inst(
|
||||
cp_name, inst)
|
||||
if network is None:
|
||||
continue
|
||||
new_cps.setdefault(cp_name_idx, {})
|
||||
new_cps[cp_name_idx]['network'] = network
|
||||
if 'fixed_ips' in cp_value:
|
||||
ext_fixed_ips = (
|
||||
common_script_utils.get_param_fixed_ips_from_inst(
|
||||
cp_name, inst))
|
||||
fixed_ips = []
|
||||
for i in range(len(ext_fixed_ips)):
|
||||
if i not in cp_value['fixed_ips']:
|
||||
break
|
||||
ips_i = cp_value['fixed_ips'][i]
|
||||
if 'subnet' in ips_i:
|
||||
ips_i['subnet'] = ext_fixed_ips[i].get('subnet')
|
||||
if 'ip_address' in ips_i:
|
||||
ips_i['ip_address'] = ext_fixed_ips[i].get(
|
||||
'ip_address')
|
||||
fixed_ips.append(ips_i)
|
||||
new_cps.setdefault(cp_name_idx, {})
|
||||
new_cps[cp_name_idx]['fixed_ips'] = fixed_ips
|
||||
|
||||
fields = {'parameters': {'nfv': {'CP': new_cps}}}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def heal(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
# It is not necessary to change parameters at heal basically.
|
||||
|
||||
fields = {'parameters': {'nfv': {}}}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def change_vnfpkg(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(grant_req['dstVnfdId'],
|
||||
tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
# first modify VDU resources
|
||||
poped_vdu = {}
|
||||
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
|
||||
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
|
||||
|
||||
for res in grant_req['removeResources']:
|
||||
if res['type'] != 'COMPUTE':
|
||||
continue
|
||||
for inst_vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
|
||||
if (inst_vnfc['computeResource']['resourceId'] ==
|
||||
res['resource']['resourceId']):
|
||||
# must be found
|
||||
vdu_idx = inst_vnfc['metadata']['vdu_idx']
|
||||
break
|
||||
vdu_name = res['resourceTemplateId']
|
||||
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
|
||||
top_hot['resources'][add_idx(vdu_name, vdu_idx)] = res
|
||||
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
vdu_name = rm_idx(vdu_name)
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
vdu_value['computeFlavourId'] = (
|
||||
common_script_utils.get_param_flavor(
|
||||
vdu_name, flavour_id, vnfd, grant))
|
||||
if 'vcImageId' in vdu_value:
|
||||
vdu_value['vcImageId'] = common_script_utils.get_param_image(
|
||||
vdu_name, flavour_id, vnfd, grant)
|
||||
if 'locationConstraints' in vdu_value:
|
||||
vdu_value.pop('locationConstraints')
|
||||
|
||||
fields = {
|
||||
'parameters': {'nfv': {'VDU': vdus}}
|
||||
}
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def change_vnfpkg_rollback(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
|
||||
flavour_id = inst['instantiatedVnfInfo']['flavourId']
|
||||
|
||||
hot_dict = vnfd.get_base_hot(flavour_id)
|
||||
top_hot = hot_dict['template']
|
||||
|
||||
# first modify VDU resources
|
||||
poped_vdu = {}
|
||||
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
|
||||
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
|
||||
|
||||
for res in grant_req['removeResources']:
|
||||
if res['type'] != 'COMPUTE':
|
||||
continue
|
||||
for inst_vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
|
||||
if (inst_vnfc['computeResource']['resourceId'] ==
|
||||
res['resource']['resourceId']):
|
||||
# must be found
|
||||
vdu_idx = inst_vnfc['metadata']['vdu_idx']
|
||||
break
|
||||
vdu_name = res['resourceTemplateId']
|
||||
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
|
||||
top_hot['resources'][add_idx(vdu_name, vdu_idx)] = res
|
||||
|
||||
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
|
||||
|
||||
images = {}
|
||||
flavors = {}
|
||||
for inst_vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
|
||||
vdu_idx = inst_vnfc['metadata'].get('vdu_idx')
|
||||
if vdu_idx is None:
|
||||
continue
|
||||
vdu_name = inst_vnfc['vduId']
|
||||
if vdu_name in flavors:
|
||||
continue
|
||||
for key, value in inst_vnfc['metadata'].items():
|
||||
if key == 'flavor':
|
||||
flavors[add_idx(vdu_name, vdu_idx)] = value
|
||||
elif key.startswith('image-'):
|
||||
image_vdu = key.replace('image-', '')
|
||||
images[image_vdu] = value
|
||||
|
||||
vdus = nfv_dict.get('VDU', {})
|
||||
new_vdus = {}
|
||||
for vdu_name, vdu_value in vdus.items():
|
||||
if 'computeFlavourId' in vdu_value:
|
||||
new_vdus.setdefault(vdu_name, {})
|
||||
new_vdus[vdu_name]['computeFlavourId'] = flavors.get(vdu_name)
|
||||
if 'vcImageId' in vdu_value:
|
||||
new_vdus.setdefault(vdu_name, {})
|
||||
new_vdus[vdu_name]['vcImageId'] = images.get(vdu_name)
|
||||
|
||||
fields = {
|
||||
'parameters': {'nfv': {'VDU': new_vdus}}
|
||||
}
|
||||
|
||||
return fields
|
|
@ -44,12 +44,32 @@ class AbstractUserData(metaclass=abc.ABCMeta):
|
|||
def scale(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def scale_rollback(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def change_ext_conn(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def change_ext_conn_rollback(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def heal(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def change_vnfpkg(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
||||
@staticmethod
|
||||
@abc.abstractmethod
|
||||
def change_vnfpkg_rollback(req, inst, grant_req, grant, tmp_csar_dir):
|
||||
raise sol_ex.UserDataClassNotImplemented()
|
||||
|
|
|
@ -0,0 +1,433 @@
|
|||
# Copyright (C) 2022 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 time
|
||||
|
||||
from tacker.tests.functional.sol_v2_common import paramgen
|
||||
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
|
||||
|
||||
|
||||
class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(IndividualVnfcMgmtTest, cls).setUpClass()
|
||||
cur_dir = os.path.dirname(__file__)
|
||||
# tacker/tests/functional/sol_v2(here)
|
||||
# /etc
|
||||
image_dir = os.path.join(
|
||||
cur_dir, "../../etc/samples/etsi/nfv/common/Files/images")
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(os.path.join(image_dir, image_file))
|
||||
|
||||
# tacker/tests/functional/sol_v2(here)
|
||||
# /sol_refactored
|
||||
userdata_dir = os.path.join(
|
||||
cur_dir, "../../../sol_refactored/infra_drivers/openstack")
|
||||
userdata_file = "userdata_standard.py"
|
||||
userdata_path = os.path.abspath(
|
||||
os.path.join(userdata_dir, userdata_file))
|
||||
|
||||
# main vnf package for StandardUserData test
|
||||
pkg_path_1 = os.path.join(cur_dir,
|
||||
"../sol_v2_common/samples/userdata_standard")
|
||||
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
|
||||
pkg_path_1, image_path=image_path, userdata_path=userdata_path)
|
||||
|
||||
# for change_vnfpkg test
|
||||
pkg_path_2 = os.path.join(cur_dir,
|
||||
"../sol_v2_common/samples/userdata_standard_change_vnfpkg")
|
||||
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
|
||||
pkg_path_2, image_path=image_path, userdata_path=userdata_path)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(IndividualVnfcMgmtTest, cls).tearDownClass()
|
||||
cls.delete_vnf_package(cls.vnf_pkg_1)
|
||||
cls.delete_vnf_package(cls.vnf_pkg_2)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def _put_fail_file(self, operation):
|
||||
with open(f'/tmp/{operation}', 'w'):
|
||||
pass
|
||||
|
||||
def _rm_fail_file(self, operation):
|
||||
os.remove(f'/tmp/{operation}')
|
||||
|
||||
def _get_vdu_indexes(self, inst, vdu):
|
||||
return {
|
||||
vnfc['metadata'].get('vdu_idx')
|
||||
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
if vnfc['vduId'] == vdu
|
||||
}
|
||||
|
||||
def _get_vnfc_by_vdu_index(self, inst, vdu, index):
|
||||
for vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
|
||||
if (vnfc['vduId'] == vdu and
|
||||
vnfc['metadata'].get('vdu_idx') == index):
|
||||
return vnfc
|
||||
|
||||
def _get_vnfc_id(self, inst, vdu, index):
|
||||
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
|
||||
return vnfc['id']
|
||||
|
||||
def _get_vnfc_cp_net_id(self, inst, vdu, index, cp):
|
||||
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
|
||||
for cp_info in vnfc['vnfcCpInfo']:
|
||||
if cp_info['cpdId'] == cp:
|
||||
# must be found
|
||||
ext_cp_id = cp_info['vnfExtCpId']
|
||||
break
|
||||
for ext_vl in inst['instantiatedVnfInfo']['extVirtualLinkInfo']:
|
||||
for port in ext_vl['extLinkPorts']:
|
||||
if port['cpInstanceId'] == ext_cp_id:
|
||||
# must be found
|
||||
return ext_vl['resourceHandle']['resourceId']
|
||||
|
||||
def _get_vnfc_image(self, inst, vdu, index):
|
||||
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
|
||||
for key, value in vnfc['metadata'].items():
|
||||
if key.startswith('image-'):
|
||||
# must be found
|
||||
return value
|
||||
|
||||
def _delete_instance(self, inst_id):
|
||||
for _ in range(3):
|
||||
resp, body = self.delete_vnf_instance(inst_id)
|
||||
if resp.status_code == 204: # OK
|
||||
return
|
||||
elif resp.status_code == 409:
|
||||
# may happen. there is a bit time between lcmocc become
|
||||
# COMPLETED and lock of terminate is freed.
|
||||
time.sleep(3)
|
||||
else:
|
||||
break
|
||||
self.assertTrue(False)
|
||||
|
||||
def test_basic_operations(self):
|
||||
"""Test basic operations using StandardUserData
|
||||
|
||||
* Note:
|
||||
This test focuses whether StandardUserData works well.
|
||||
This test does not check overall items of APIs at all.
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- Create VNF instance
|
||||
- 1. Instantiate VNF instance
|
||||
- Show VNF instance / check
|
||||
- 2. Scale out operation
|
||||
- Show VNF instance / check
|
||||
- 3. Heal operation
|
||||
- Show VNF instance / check
|
||||
- 4. Scale in operation
|
||||
- Show VNF instance / check
|
||||
- 5. Change_ext_conn operation
|
||||
- Show VNF instance / check
|
||||
- 6. Change_vnfpkg operation
|
||||
- Show VNF instance / check
|
||||
- Terminate VNF instance
|
||||
- Delete VNF instance
|
||||
"""
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
|
||||
# Create VNF instance
|
||||
create_req = paramgen.sample3_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
inst_id = body['id']
|
||||
|
||||
# 1. Instantiate VNF instance
|
||||
instantiate_req = paramgen.sample3_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url)
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_1 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check number of VDUs and indexes
|
||||
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU1'))
|
||||
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
|
||||
|
||||
# 2. Scale out operation
|
||||
scale_out_req = paramgen.sample3_scale_out()
|
||||
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_2 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check number of VDUs and indexes
|
||||
self.assertEqual({0, 1, 2}, self._get_vdu_indexes(inst_2, 'VDU1'))
|
||||
self.assertEqual({0}, self._get_vdu_indexes(inst_2, 'VDU2'))
|
||||
|
||||
# 3. Heal operation
|
||||
heal_req = paramgen.sample3_heal()
|
||||
# pick up VDU1-1 to heal
|
||||
vnfc_id = self._get_vnfc_id(inst_2, 'VDU1', 1)
|
||||
heal_req['vnfcInstanceId'] = [f'VDU1-{vnfc_id}']
|
||||
resp, body = self.heal_vnf_instance(inst_id, heal_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_3 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check id of VDU1-1 is changed and other are not.
|
||||
self.assertEqual(self._get_vnfc_id(inst_2, 'VDU1', 0),
|
||||
self._get_vnfc_id(inst_3, 'VDU1', 0))
|
||||
self.assertNotEqual(self._get_vnfc_id(inst_2, 'VDU1', 1),
|
||||
self._get_vnfc_id(inst_3, 'VDU1', 1))
|
||||
self.assertEqual(self._get_vnfc_id(inst_2, 'VDU1', 2),
|
||||
self._get_vnfc_id(inst_3, 'VDU1', 2))
|
||||
self.assertEqual(self._get_vnfc_id(inst_2, 'VDU2', 0),
|
||||
self._get_vnfc_id(inst_3, 'VDU2', 0))
|
||||
|
||||
# 4. Scale in operation
|
||||
scale_in_req = paramgen.sample3_scale_in()
|
||||
resp, body = self.scale_vnf_instance(inst_id, scale_in_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_4 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check VDU1-2 is removed. other are not changed.
|
||||
self.assertEqual({0, 1}, self._get_vdu_indexes(inst_4, 'VDU1'))
|
||||
self.assertEqual({0}, self._get_vdu_indexes(inst_4, 'VDU2'))
|
||||
self.assertEqual(self._get_vnfc_id(inst_3, 'VDU1', 0),
|
||||
self._get_vnfc_id(inst_4, 'VDU1', 0))
|
||||
self.assertEqual(self._get_vnfc_id(inst_3, 'VDU1', 1),
|
||||
self._get_vnfc_id(inst_4, 'VDU1', 1))
|
||||
self.assertEqual(self._get_vnfc_id(inst_3, 'VDU2', 0),
|
||||
self._get_vnfc_id(inst_4, 'VDU2', 0))
|
||||
|
||||
# 5. Change_ext_conn operation
|
||||
change_ext_conn_req = paramgen.sample3_change_ext_conn(net_ids)
|
||||
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_5 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check VDU1_CP1 is changed to net0 from net1. other are not changed.
|
||||
self.assertEqual(net_ids['net1'],
|
||||
self._get_vnfc_cp_net_id(inst_4, 'VDU1', 0, 'VDU1_CP1'))
|
||||
self.assertEqual(net_ids['net1'],
|
||||
self._get_vnfc_cp_net_id(inst_4, 'VDU1', 1, 'VDU1_CP1'))
|
||||
self.assertEqual(net_ids['net1'],
|
||||
self._get_vnfc_cp_net_id(inst_4, 'VDU2', 0, 'VDU2_CP1'))
|
||||
self.assertEqual(net_ids['net0'],
|
||||
self._get_vnfc_cp_net_id(inst_5, 'VDU1', 0, 'VDU1_CP1'))
|
||||
self.assertEqual(net_ids['net0'],
|
||||
self._get_vnfc_cp_net_id(inst_5, 'VDU1', 1, 'VDU1_CP1'))
|
||||
self.assertEqual(net_ids['net1'],
|
||||
self._get_vnfc_cp_net_id(inst_5, 'VDU2', 0, 'VDU2_CP1'))
|
||||
|
||||
# 6. Change_vnfpkg operation
|
||||
change_vnfpkg_req = paramgen.sample4_change_vnfpkg(self.vnfd_id_2)
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_6 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check vnfdId is changed
|
||||
self.assertEqual(self.vnfd_id_2, inst_6['vnfdId'])
|
||||
# check images are changed
|
||||
self.assertNotEqual(self._get_vnfc_image(inst_5, 'VDU1', 0),
|
||||
self._get_vnfc_image(inst_6, 'VDU1', 0))
|
||||
self.assertNotEqual(self._get_vnfc_image(inst_5, 'VDU1', 1),
|
||||
self._get_vnfc_image(inst_6, 'VDU1', 1))
|
||||
self.assertNotEqual(self._get_vnfc_image(inst_5, 'VDU2', 0),
|
||||
self._get_vnfc_image(inst_6, 'VDU2', 0))
|
||||
|
||||
# Terminate VNF instance
|
||||
terminate_req = paramgen.sample4_terminate()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Delete VNF instance
|
||||
self._delete_instance(inst_id)
|
||||
|
||||
def test_rollback_operations(self):
|
||||
"""Test rollback operations using StandardUserData
|
||||
|
||||
* Note:
|
||||
This test focuses whether StandardUserData works well.
|
||||
This test does not check overall items of APIs at all.
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- Create VNF instance
|
||||
- Instantiate VNF instance
|
||||
- Show VNF instance
|
||||
- 1. Scale out operation => FAILED_TEMP
|
||||
- Rollback
|
||||
- Show VNF instance / check
|
||||
- 2. Change_ext_conn operation => FAILED_TEMP
|
||||
- Rollback
|
||||
- Show VNF instance / check
|
||||
- 3. Change_vnfpkg operation => FAILED_TEMP
|
||||
- Rollback
|
||||
- Show VNF instance / check
|
||||
- Terminate VNF instance
|
||||
- Delete VNF instance
|
||||
"""
|
||||
|
||||
net_ids = self.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = self.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
|
||||
# Create VNF instance
|
||||
create_req = paramgen.sample3_create(self.vnfd_id_1)
|
||||
resp, body = self.create_vnf_instance(create_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
inst_id = body['id']
|
||||
|
||||
# Instantiate VNF instance
|
||||
instantiate_req = paramgen.sample3_instantiate(
|
||||
net_ids, subnet_ids, self.auth_url)
|
||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_0 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# 1. Scale out operation
|
||||
self._put_fail_file('scale_end')
|
||||
scale_out_req = paramgen.sample3_scale_out()
|
||||
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||
self._rm_fail_file('scale_end')
|
||||
|
||||
# Rollback
|
||||
resp, body = self.rollback_lcmocc(lcmocc_id)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_1 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check number of vnfc and its id are not changed.
|
||||
self.assertEqual(self._get_vdu_indexes(inst_0, 'VDU1'),
|
||||
self._get_vdu_indexes(inst_1, 'VDU1'))
|
||||
self.assertEqual(self._get_vdu_indexes(inst_0, 'VDU2'),
|
||||
self._get_vdu_indexes(inst_1, 'VDU2'))
|
||||
self.assertEqual(self._get_vnfc_id(inst_0, 'VDU1', 0),
|
||||
self._get_vnfc_id(inst_1, 'VDU1', 0))
|
||||
self.assertEqual(self._get_vnfc_id(inst_0, 'VDU2', 0),
|
||||
self._get_vnfc_id(inst_1, 'VDU2', 0))
|
||||
|
||||
# 2. Change_ext_conn operation
|
||||
self._put_fail_file('change_external_connectivity_end')
|
||||
change_ext_conn_req = paramgen.sample3_change_ext_conn(net_ids)
|
||||
resp, body = self.change_ext_conn(inst_id, change_ext_conn_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||
self._rm_fail_file('change_external_connectivity_end')
|
||||
|
||||
# Rollback
|
||||
resp, body = self.rollback_lcmocc(lcmocc_id)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_2 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check network of extVL cps are not changed. (i.e. net1)
|
||||
self.assertEqual(net_ids['net1'],
|
||||
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 0, 'VDU1_CP1'))
|
||||
self.assertEqual(net_ids['net1'],
|
||||
self._get_vnfc_cp_net_id(inst_2, 'VDU2', 0, 'VDU2_CP1'))
|
||||
|
||||
# 3. Change_vnfpkg operation
|
||||
self._put_fail_file('change_vnfpkg')
|
||||
change_vnfpkg_req = paramgen.sample4_change_vnfpkg(self.vnfd_id_2)
|
||||
resp, body = self.change_vnfpkg(inst_id, change_vnfpkg_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_failed_temp(lcmocc_id)
|
||||
self._rm_fail_file('change_vnfpkg')
|
||||
|
||||
# Rollback
|
||||
resp, body = self.rollback_lcmocc(lcmocc_id)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.wait_lcmocc_rolled_back(lcmocc_id)
|
||||
|
||||
# Show VNF instance
|
||||
resp, inst_3 = self.show_vnf_instance(inst_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
|
||||
# check vnfdId is not changed
|
||||
self.assertEqual(self.vnfd_id_1, inst_3['vnfdId'])
|
||||
# check images are not changed
|
||||
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU1', 0),
|
||||
self._get_vnfc_image(inst_3, 'VDU1', 0))
|
||||
self.assertEqual(self._get_vnfc_image(inst_2, 'VDU2', 0),
|
||||
self._get_vnfc_image(inst_3, 'VDU2', 0))
|
||||
|
||||
# Terminate VNF instance
|
||||
terminate_req = paramgen.sample3_terminate()
|
||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# Delete VNF instance
|
||||
self._delete_instance(inst_id)
|
|
@ -107,11 +107,12 @@ class BaseSolV2Test(base.BaseTestCase):
|
|||
|
||||
@classmethod
|
||||
def create_vnf_package(cls, sample_path, user_data={},
|
||||
image_path=None, nfvo=False):
|
||||
image_path=None, nfvo=False, userdata_path=None):
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
|
||||
utils.make_zip(sample_path, tmp_dir, vnfd_id, image_path)
|
||||
utils.make_zip(sample_path, tmp_dir, vnfd_id, image_path,
|
||||
userdata_path)
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(sample_path)) + ".zip"
|
||||
zip_file_path = os.path.join(tmp_dir, zip_file_name)
|
||||
|
|
|
@ -933,3 +933,197 @@ def change_vnfpkg(vnfd_id):
|
|||
}]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# sample3 is used for tests of StandardUserData
|
||||
#
|
||||
def sample3_create(vnfd_id):
|
||||
return {
|
||||
"vnfdId": vnfd_id,
|
||||
"vnfInstanceName": "sample3",
|
||||
"vnfInstanceDescription": "test for StandardUserData"
|
||||
}
|
||||
|
||||
|
||||
def sample3_terminate():
|
||||
return {
|
||||
"terminationType": "FORCEFUL"
|
||||
}
|
||||
|
||||
|
||||
def sample3_instantiate(net_ids, subnet_ids, auth_url):
|
||||
ext_vl_1 = {
|
||||
"id": uuidutils.generate_uuid(),
|
||||
"resourceId": net_ids['net1'],
|
||||
"extCps": [
|
||||
{
|
||||
"cpdId": "VDU1_CP1",
|
||||
"cpConfig": {
|
||||
"VDU1_CP2_1": {
|
||||
"cpProtocolData": [{
|
||||
"layerProtocol": "IP_OVER_ETHERNET",
|
||||
"ipOverEthernet": {
|
||||
"ipAddresses": [{
|
||||
"type": "IPV4",
|
||||
"numDynamicAddresses": 1}]}}]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cpdId": "VDU2_CP1",
|
||||
"cpConfig": {
|
||||
"VDU2_CP2_1": {
|
||||
"cpProtocolData": [{
|
||||
"layerProtocol": "IP_OVER_ETHERNET",
|
||||
"ipOverEthernet": {
|
||||
"ipAddresses": [{
|
||||
"type": "IPV4",
|
||||
"numDynamicAddresses": 1,
|
||||
"subnetId": subnet_ids['subnet1']}]}}]}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return {
|
||||
"flavourId": "simple",
|
||||
"instantiationLevelId": "instantiation_level_1",
|
||||
"extVirtualLinks": [ext_vl_1],
|
||||
"extManagedVirtualLinks": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalParams": {
|
||||
"lcm-operation-user-data": "./UserData/userdata_standard.py",
|
||||
"lcm-operation-user-data-class": "StandardUserData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def sample3_scale_out():
|
||||
return {
|
||||
"type": "SCALE_OUT",
|
||||
"aspectId": "VDU1_scale",
|
||||
"numberOfSteps": 2,
|
||||
"additionalParams": {
|
||||
"lcm-operation-user-data": "./UserData/userdata_standard.py",
|
||||
"lcm-operation-user-data-class": "StandardUserData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def sample3_scale_in():
|
||||
return {
|
||||
"type": "SCALE_IN",
|
||||
"aspectId": "VDU1_scale",
|
||||
"numberOfSteps": 1,
|
||||
"additionalParams": {
|
||||
"lcm-operation-user-data": "./UserData/userdata_standard.py",
|
||||
"lcm-operation-user-data-class": "StandardUserData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def sample3_heal():
|
||||
return {
|
||||
"vnfcInstanceId": [], # should be filled
|
||||
"additionalParams": {
|
||||
"lcm-operation-user-data": "./UserData/userdata_standard.py",
|
||||
"lcm-operation-user-data-class": "StandardUserData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def sample3_change_ext_conn(net_ids):
|
||||
return {
|
||||
"extVirtualLinks": [
|
||||
{
|
||||
"id": uuidutils.generate_uuid(),
|
||||
"resourceId": net_ids['net0'],
|
||||
"extCps": [
|
||||
{
|
||||
"cpdId": "VDU1_CP1",
|
||||
"cpConfig": {
|
||||
"VDU1_CP2_1": {
|
||||
"cpProtocolData": [{
|
||||
"layerProtocol": "IP_OVER_ETHERNET",
|
||||
"ipOverEthernet": {
|
||||
"ipAddresses": [{
|
||||
"type": "IPV4",
|
||||
"numDynamicAddresses": 1}]}}]}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalParams": {
|
||||
"lcm-operation-user-data": "./UserData/userdata_standard.py",
|
||||
"lcm-operation-user-data-class": "StandardUserData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# sample4 is for change_vnfpkg test of StandardUserData
|
||||
#
|
||||
def sample4_change_vnfpkg(vnfd_id):
|
||||
return {
|
||||
"vnfdId": vnfd_id,
|
||||
"additionalParams": {
|
||||
"upgrade_type": "RollingUpdate",
|
||||
"lcm-operation-coordinate-new-vnf": "./Scripts/coordinate_vnf.py",
|
||||
"lcm-operation-coordinate-old-vnf": "./Scripts/coordinate_vnf.py",
|
||||
"vdu_params": [
|
||||
{
|
||||
"vdu_id": "VDU1",
|
||||
"old_vnfc_param": {
|
||||
"cp_name": "VDU1_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
},
|
||||
"new_vnfc_param": {
|
||||
"cp_name": "VDU1_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
}
|
||||
},
|
||||
{
|
||||
"vdu_id": "VDU2",
|
||||
"old_vnfc_param": {
|
||||
"cp_name": "VDU2_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
},
|
||||
"new_vnfc_param": {
|
||||
"cp_name": "VDU2_CP1",
|
||||
"username": "ubuntu",
|
||||
"password": "ubuntu"
|
||||
}
|
||||
}
|
||||
],
|
||||
"lcm-operation-user-data": "./UserData/userdata_standard.py",
|
||||
"lcm-operation-user-data-class": "StandardUserData"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def sample4_terminate():
|
||||
return {
|
||||
"terminationType": "FORCEFUL"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image-VDU1:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
net2:
|
||||
type: string
|
||||
net3:
|
||||
type: string
|
||||
affinity:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
image: { get_param: image-VDU1 }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
# replace the following line to Port ID when extmanagedVLs' Ports are
|
||||
# specified in instantiatevnfrequest
|
||||
- port:
|
||||
get_resource: VDU1_CP2
|
||||
- port:
|
||||
get_resource: VDU1_CP3
|
||||
scheduler_hints:
|
||||
group: { get_param: affinity }
|
||||
|
||||
# extVL without FixedIP or with numDynamicAddresses
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
||||
# CPs of internal VLs are deleted when extmanagedVLs and port are
|
||||
# specified in instantiatevnfrequest
|
||||
VDU1_CP2:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net2 }
|
||||
|
||||
VDU1_CP3:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net3 }
|
|
@ -0,0 +1,69 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU2 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image-VDU2-VirtualStorage:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
net2:
|
||||
type: string
|
||||
net3:
|
||||
type: string
|
||||
subnet1:
|
||||
type: string
|
||||
affinity:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU2
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
# replace the following line to Port ID when extmanagedVLs' Ports are
|
||||
# specified in instantiatevnfrequest
|
||||
- port:
|
||||
get_resource: VDU2_CP2
|
||||
- port:
|
||||
get_resource: VDU2_CP3
|
||||
scheduler_hints:
|
||||
group: {get_param: affinity }
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: image-VDU2-VirtualStorage }
|
||||
size: 1
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: VDU2-multi
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
||||
# extVL with numDynamicAddresses and subnet
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
fixed_ips:
|
||||
- subnet: { get_param: subnet1 }
|
||||
|
||||
# CPs of internal VLs are deleted when extmanagedVLs and port are
|
||||
# specified in instantiatevnfrequest
|
||||
VDU2_CP2:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net2 }
|
||||
|
||||
VDU2_CP3:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net3 }
|
|
@ -0,0 +1,57 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image-VDU1: { get_param: [ nfv, VDU, VDU1, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network ] }
|
||||
net2: { get_resource: internalVL1 }
|
||||
net3: { get_resource: internalVL2 }
|
||||
affinity: { get_resource: nfvi_node_affinity }
|
||||
|
||||
VDU2:
|
||||
type: VDU2.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
image-VDU2-VirtualStorage: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
subnet1: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, subnet ]}
|
||||
net2: { get_resource: internalVL1 }
|
||||
net3: { get_resource: internalVL2 }
|
||||
affinity: { get_resource: nfvi_node_affinity }
|
||||
|
||||
# delete the following lines when extmanagedVLs are specified in instantiatevnfrequest
|
||||
internalVL1:
|
||||
type: OS::Neutron::Net
|
||||
internalVL2:
|
||||
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
|
||||
|
||||
nfvi_node_affinity:
|
||||
type: OS::Nova::ServerGroup
|
||||
properties:
|
||||
name: nfvi_node_affinity
|
||||
policies: [ 'affinity' ]
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,357 @@
|
|||
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_sample3_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: simple
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate_start:
|
||||
implementation: sample-script
|
||||
instantiate_end:
|
||||
implementation: sample-script
|
||||
terminate_start:
|
||||
implementation: sample-script
|
||||
terminate_end:
|
||||
implementation: sample-script
|
||||
scale_start:
|
||||
implementation: sample-script
|
||||
scale_end:
|
||||
implementation: sample-script
|
||||
heal_start:
|
||||
implementation: sample-script
|
||||
heal_end:
|
||||
implementation: sample-script
|
||||
change_external_connectivity_start:
|
||||
implementation: sample-script
|
||||
change_external_connectivity_end:
|
||||
implementation: sample-script
|
||||
modify_information_start:
|
||||
implementation: sample-script
|
||||
modify_information_end:
|
||||
implementation: sample-script
|
||||
artifacts:
|
||||
sample-script:
|
||||
description: Sample script
|
||||
type: tosca.artifacts.Implementation.Python
|
||||
file: ../Scripts/sample_script.py
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
sw_image_data:
|
||||
name: cirros-0.5.2-x86_64-disk
|
||||
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:
|
||||
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
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
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: VDU2-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: VDU2-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
|
||||
|
||||
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
|
||||
- virtual_link: internalVL1
|
||||
|
||||
VDU1_CP3:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 2
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
- virtual_link: internalVL2
|
||||
|
||||
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
|
||||
- virtual_link: internalVL1
|
||||
|
||||
VDU2_CP3:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 2
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
- virtual_link: internalVL2
|
||||
|
||||
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
|
||||
|
||||
groups:
|
||||
affinityOrAntiAffinityGroup1:
|
||||
type: tosca.groups.nfv.PlacementGroup
|
||||
members: [ VDU1, VDU2 ]
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 1
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- internalVL1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
instantiation_level_2:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
targets: [ internalVL1 ]
|
||||
|
||||
- internalVL2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
instantiation_level_2:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
targets: [ internalVL2 ]
|
||||
|
||||
- policy_antiaffinity_group:
|
||||
type: tosca.policies.nfv.AntiAffinityRule
|
||||
targets: [ affinityOrAntiAffinityGroup1 ]
|
||||
properties:
|
||||
scope: nfvi_node
|
|
@ -0,0 +1,31 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- v2_sample3_types.yaml
|
||||
- v2_sample3_df_simple.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
selected_flavour:
|
||||
type: string
|
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: { get_input: selected_flavour }
|
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
provider: Company
|
||||
product_name: Sample VNF
|
||||
software_version: '1.0'
|
||||
descriptor_version: '1.0'
|
||||
vnfm_info:
|
||||
- Tacker
|
||||
requirements:
|
||||
#- virtual_link_external # mapped in lower-level templates
|
||||
#- virtual_link_internal # mapped in lower-level templates
|
|
@ -0,0 +1,55 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: VNF type definition
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
properties:
|
||||
descriptor_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
|
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
descriptor_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
provider:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Company' ] ]
|
||||
default: 'Company'
|
||||
product_name:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||
default: 'Sample VNF'
|
||||
software_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
constraints: [ valid_values: [ Tacker ] ]
|
||||
default: [ Tacker ]
|
||||
flavour_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ simple ] ]
|
||||
default: simple
|
||||
flavour_description:
|
||||
type: string
|
||||
default: "flavour"
|
||||
requirements:
|
||||
- virtual_link_external1:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_external2:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_internal:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright (C) 2022 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 FailScript(object):
|
||||
def __init__(self, vnfc_param):
|
||||
self.vnfc_param = vnfc_param
|
||||
|
||||
def run(self):
|
||||
operation = 'change_vnfpkg'
|
||||
if self.vnfc_param['is_rollback']:
|
||||
operation += '_rollback'
|
||||
if os.path.exists(f'/tmp/{operation}'):
|
||||
raise Exception(f'test {operation} error')
|
||||
|
||||
|
||||
def main():
|
||||
vnfc_param = pickle.load(sys.stdin.buffer)
|
||||
script = FailScript(vnfc_param)
|
||||
script.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,68 @@
|
|||
# Copyright (C) 2022 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 functools
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
|
||||
|
||||
class FailScript(object):
|
||||
"""Define error method for each operation
|
||||
|
||||
For example:
|
||||
|
||||
def instantiate_start(self):
|
||||
if os.path.exists('/tmp/instantiate_start')
|
||||
raise Exception('test instantiate_start error')
|
||||
"""
|
||||
|
||||
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 _fail(self, method):
|
||||
if os.path.exists(f'/tmp/{method}'):
|
||||
raise Exception(f'test {method} error')
|
||||
|
||||
def __getattr__(self, name):
|
||||
return functools.partial(self._fail, name)
|
||||
|
||||
|
||||
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 = FailScript(req, inst, grant_req, grant, csar_dir)
|
||||
getattr(script, operation)()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,4 @@
|
|||
TOSCA-Meta-File-Version: 1.0
|
||||
CSAR-Version: 1.1
|
||||
Created-by: Onboarding portal
|
||||
Entry-Definitions: Definitions/v2_sample3_top.vnfd.yaml
|
|
@ -0,0 +1,83 @@
|
|||
# Copyright (C) 2022 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 json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.tests.functional.sol_v2_common import paramgen
|
||||
from tacker.tests.functional.sol_v2_common import utils
|
||||
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
|
||||
# tacker/tests/etc...
|
||||
# /functional/sol_v2_common/samples/smapleX
|
||||
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(image_dir + image_file)
|
||||
|
||||
# tacker/sol_refactored/infra_drivers/openstack/userdata_standard.py
|
||||
# /tests/functional/sol_v2_common/samples/smapleX
|
||||
userdata_dir = "../../../../../sol_refactored/infra_drivers/openstack/"
|
||||
userdata_file = "userdata_standard.py"
|
||||
userdata_path = os.path.abspath(userdata_dir + userdata_file)
|
||||
|
||||
utils.make_zip(".", tmp_dir, vnfd_id, image_path=image_path,
|
||||
userdata_path=userdata_path)
|
||||
|
||||
shutil.copy(os.path.join(tmp_dir, zip_file_name), ".")
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
create_req = paramgen.sample3_create(vnfd_id)
|
||||
terminate_req = paramgen.sample3_terminate()
|
||||
|
||||
net_ids = utils.get_network_ids(['net0', 'net1', 'net_mgmt'])
|
||||
subnet_ids = utils.get_subnet_ids(['subnet0', 'subnet1'])
|
||||
|
||||
instantiate_req = paramgen.sample3_instantiate(
|
||||
net_ids, subnet_ids, "http://localhost/identity/v3")
|
||||
|
||||
scale_out_req = paramgen.sample3_scale_out()
|
||||
scale_in_req = paramgen.sample3_scale_in()
|
||||
heal_req = paramgen.sample3_heal()
|
||||
change_ext_conn_req = paramgen.sample3_change_ext_conn(net_ids)
|
||||
|
||||
with open("create_req", "w") as f:
|
||||
f.write(json.dumps(create_req, indent=2))
|
||||
|
||||
with open("terminate_req", "w") as f:
|
||||
f.write(json.dumps(terminate_req, indent=2))
|
||||
|
||||
with open("instantiate_req", "w") as f:
|
||||
f.write(json.dumps(instantiate_req, indent=2))
|
||||
|
||||
with open("scale_out_req", "w") as f:
|
||||
f.write(json.dumps(scale_out_req, indent=2))
|
||||
|
||||
with open("scale_in_req", "w") as f:
|
||||
f.write(json.dumps(scale_in_req, indent=2))
|
||||
|
||||
# NOTE: vnfcInstanceId should be filled by hand
|
||||
with open("heal_req", "w") as f:
|
||||
f.write(json.dumps(heal_req, indent=2))
|
||||
|
||||
with open("change_ext_conn_req", "w") as f:
|
||||
f.write(json.dumps(change_ext_conn_req, indent=2))
|
|
@ -0,0 +1,53 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU1 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image-VDU1:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
net2:
|
||||
type: string
|
||||
net3:
|
||||
type: string
|
||||
affinity:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU1
|
||||
image: { get_param: image-VDU1 }
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU1_CP1
|
||||
# replace the following line to Port ID when extmanagedVLs' Ports are
|
||||
# specified in instantiatevnfrequest
|
||||
- port:
|
||||
get_resource: VDU1_CP2
|
||||
- port:
|
||||
get_resource: VDU1_CP3
|
||||
scheduler_hints:
|
||||
group: { get_param: affinity }
|
||||
|
||||
# extVL without FixedIP or with numDynamicAddresses
|
||||
VDU1_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
|
||||
# CPs of internal VLs are deleted when extmanagedVLs and port are
|
||||
# specified in instantiatevnfrequest
|
||||
VDU1_CP2:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net2 }
|
||||
|
||||
VDU1_CP3:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net3 }
|
|
@ -0,0 +1,69 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'VDU2 HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image-VDU2-VirtualStorage:
|
||||
type: string
|
||||
net1:
|
||||
type: string
|
||||
net2:
|
||||
type: string
|
||||
net3:
|
||||
type: string
|
||||
subnet1:
|
||||
type: string
|
||||
affinity:
|
||||
type: string
|
||||
|
||||
resources:
|
||||
VDU2:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
flavor: { get_param: flavor }
|
||||
name: VDU2
|
||||
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
|
||||
networks:
|
||||
- port:
|
||||
get_resource: VDU2_CP1
|
||||
# replace the following line to Port ID when extmanagedVLs' Ports are
|
||||
# specified in instantiatevnfrequest
|
||||
- port:
|
||||
get_resource: VDU2_CP2
|
||||
- port:
|
||||
get_resource: VDU2_CP3
|
||||
scheduler_hints:
|
||||
group: {get_param: affinity }
|
||||
|
||||
VDU2-VirtualStorage:
|
||||
type: OS::Cinder::Volume
|
||||
properties:
|
||||
image: { get_param: image-VDU2-VirtualStorage }
|
||||
size: 1
|
||||
volume_type: { get_resource: multi }
|
||||
multi:
|
||||
type: OS::Cinder::VolumeType
|
||||
properties:
|
||||
name: VDU2-multi
|
||||
metadata: { multiattach: "<is> True" }
|
||||
|
||||
# extVL with numDynamicAddresses and subnet
|
||||
VDU2_CP1:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net1 }
|
||||
fixed_ips:
|
||||
- subnet: { get_param: subnet1 }
|
||||
|
||||
# CPs of internal VLs are deleted when extmanagedVLs and port are
|
||||
# specified in instantiatevnfrequest
|
||||
VDU2_CP2:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net2 }
|
||||
|
||||
VDU2_CP3:
|
||||
type: OS::Neutron::Port
|
||||
properties:
|
||||
network: { get_param: net3 }
|
|
@ -0,0 +1,57 @@
|
|||
heat_template_version: 2013-05-23
|
||||
description: 'Simple Base HOT for Sample VNF'
|
||||
|
||||
parameters:
|
||||
nfv:
|
||||
type: json
|
||||
|
||||
resources:
|
||||
VDU1:
|
||||
type: VDU1.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
|
||||
image-VDU1: { get_param: [ nfv, VDU, VDU1, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU1_CP1, network ] }
|
||||
net2: { get_resource: internalVL1 }
|
||||
net3: { get_resource: internalVL2 }
|
||||
affinity: { get_resource: nfvi_node_affinity }
|
||||
|
||||
VDU2:
|
||||
type: VDU2.yaml
|
||||
properties:
|
||||
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
|
||||
image-VDU2-VirtualStorage: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId ] }
|
||||
net1: { get_param: [ nfv, CP, VDU2_CP1, network ] }
|
||||
subnet1: { get_param: [nfv, CP, VDU2_CP1, fixed_ips, 0, subnet ]}
|
||||
net2: { get_resource: internalVL1 }
|
||||
net3: { get_resource: internalVL2 }
|
||||
affinity: { get_resource: nfvi_node_affinity }
|
||||
|
||||
# delete the following lines when extmanagedVLs are specified in instantiatevnfrequest
|
||||
internalVL1:
|
||||
type: OS::Neutron::Net
|
||||
internalVL2:
|
||||
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
|
||||
|
||||
nfvi_node_affinity:
|
||||
type: OS::Nova::ServerGroup
|
||||
properties:
|
||||
name: nfvi_node_affinity
|
||||
policies: [ 'affinity' ]
|
||||
|
||||
outputs: {}
|
|
@ -0,0 +1,357 @@
|
|||
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_sample4_types.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
descriptor_id:
|
||||
type: string
|
||||
descriptor_version:
|
||||
type: string
|
||||
provider:
|
||||
type: string
|
||||
product_name:
|
||||
type: string
|
||||
software_version:
|
||||
type: string
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
flavour_id:
|
||||
type: string
|
||||
flavour_description:
|
||||
type: string
|
||||
|
||||
substitution_mappings:
|
||||
node_type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: simple
|
||||
requirements:
|
||||
virtual_link_external1_1: [ VDU1_CP1, virtual_link ]
|
||||
virtual_link_external1_2: [ VDU2_CP1, virtual_link ]
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_description: A simple flavour
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
instantiate_start:
|
||||
implementation: sample-script
|
||||
instantiate_end:
|
||||
implementation: sample-script
|
||||
terminate_start:
|
||||
implementation: sample-script
|
||||
terminate_end:
|
||||
implementation: sample-script
|
||||
scale_start:
|
||||
implementation: sample-script
|
||||
scale_end:
|
||||
implementation: sample-script
|
||||
heal_start:
|
||||
implementation: sample-script
|
||||
heal_end:
|
||||
implementation: sample-script
|
||||
change_external_connectivity_start:
|
||||
implementation: sample-script
|
||||
change_external_connectivity_end:
|
||||
implementation: sample-script
|
||||
modify_information_start:
|
||||
implementation: sample-script
|
||||
modify_information_end:
|
||||
implementation: sample-script
|
||||
artifacts:
|
||||
sample-script:
|
||||
description: Sample script
|
||||
type: tosca.artifacts.Implementation.Python
|
||||
file: ../Scripts/sample_script.py
|
||||
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU1
|
||||
description: VDU1 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 3
|
||||
sw_image_data:
|
||||
name: VDU1-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:
|
||||
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
|
||||
artifacts:
|
||||
sw_image:
|
||||
type: tosca.artifacts.nfv.SwImage
|
||||
file: ../Files/images/cirros-0.5.2-x86_64-disk.img
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.Vdu.Compute
|
||||
properties:
|
||||
name: VDU2
|
||||
description: VDU2 compute node
|
||||
vdu_profile:
|
||||
min_number_of_instances: 1
|
||||
max_number_of_instances: 1
|
||||
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: VDU2-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
|
||||
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
|
||||
|
||||
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
|
||||
- virtual_link: internalVL1
|
||||
|
||||
VDU1_CP3:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 2
|
||||
requirements:
|
||||
- virtual_binding: VDU1
|
||||
- virtual_link: internalVL2
|
||||
|
||||
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
|
||||
- virtual_link: internalVL1
|
||||
|
||||
VDU2_CP3:
|
||||
type: tosca.nodes.nfv.VduCp
|
||||
properties:
|
||||
layer_protocols: [ ipv4 ]
|
||||
order: 2
|
||||
requirements:
|
||||
- virtual_binding: VDU2
|
||||
- virtual_link: internalVL2
|
||||
|
||||
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
|
||||
|
||||
groups:
|
||||
affinityOrAntiAffinityGroup1:
|
||||
type: tosca.groups.nfv.PlacementGroup
|
||||
members: [ VDU1, VDU2 ]
|
||||
|
||||
policies:
|
||||
- scaling_aspects:
|
||||
type: tosca.policies.nfv.ScalingAspects
|
||||
properties:
|
||||
aspects:
|
||||
VDU1_scale:
|
||||
name: VDU1_scale
|
||||
description: VDU1 scaling aspect
|
||||
max_scale_level: 2
|
||||
step_deltas:
|
||||
- delta_1
|
||||
|
||||
- VDU1_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_initial_delta:
|
||||
type: tosca.policies.nfv.VduInitialDelta
|
||||
properties:
|
||||
initial_delta:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- VDU1_scaling_aspect_deltas:
|
||||
type: tosca.policies.nfv.VduScalingAspectDeltas
|
||||
properties:
|
||||
aspect: VDU1_scale
|
||||
deltas:
|
||||
delta_1:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- instantiation_levels:
|
||||
type: tosca.policies.nfv.InstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
description: Smallest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 0
|
||||
instantiation_level_2:
|
||||
description: Largest size
|
||||
scale_info:
|
||||
VDU1_scale:
|
||||
scale_level: 1
|
||||
default_level: instantiation_level_1
|
||||
|
||||
- VDU1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 2
|
||||
targets: [ VDU1 ]
|
||||
|
||||
- VDU2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VduInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
number_of_instances: 1
|
||||
instantiation_level_2:
|
||||
number_of_instances: 1
|
||||
targets: [ VDU2 ]
|
||||
|
||||
- internalVL1_instantiation_levels:
|
||||
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
instantiation_level_2:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
targets: [ internalVL1 ]
|
||||
|
||||
- internalVL2_instantiation_levels:
|
||||
type: tosca.policies.nfv.VirtualLinkInstantiationLevels
|
||||
properties:
|
||||
levels:
|
||||
instantiation_level_1:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
instantiation_level_2:
|
||||
bitrate_requirements:
|
||||
root: 1048576
|
||||
leaf: 1048576
|
||||
targets: [ internalVL2 ]
|
||||
|
||||
- policy_antiaffinity_group:
|
||||
type: tosca.policies.nfv.AntiAffinityRule
|
||||
targets: [ affinityOrAntiAffinityGroup1 ]
|
||||
properties:
|
||||
scope: nfvi_node
|
|
@ -0,0 +1,31 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: Sample VNF
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
- v2_sample4_types.yaml
|
||||
- v2_sample4_df_simple.yaml
|
||||
|
||||
topology_template:
|
||||
inputs:
|
||||
selected_flavour:
|
||||
type: string
|
||||
description: VNF deployment flavour selected by the consumer. It is provided in the API
|
||||
|
||||
node_templates:
|
||||
VNF:
|
||||
type: company.provider.VNF
|
||||
properties:
|
||||
flavour_id: { get_input: selected_flavour }
|
||||
descriptor_id: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
provider: Company
|
||||
product_name: Sample VNF
|
||||
software_version: '1.0'
|
||||
descriptor_version: '1.0'
|
||||
vnfm_info:
|
||||
- Tacker
|
||||
requirements:
|
||||
#- virtual_link_external # mapped in lower-level templates
|
||||
#- virtual_link_internal # mapped in lower-level templates
|
|
@ -0,0 +1,55 @@
|
|||
tosca_definitions_version: tosca_simple_yaml_1_2
|
||||
|
||||
description: VNF type definition
|
||||
|
||||
imports:
|
||||
- etsi_nfv_sol001_common_types.yaml
|
||||
- etsi_nfv_sol001_vnfd_types.yaml
|
||||
|
||||
node_types:
|
||||
company.provider.VNF:
|
||||
derived_from: tosca.nodes.nfv.VNF
|
||||
properties:
|
||||
descriptor_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ b1bb0ce7-ebca-4fa7-95ed-4840d7000000 ] ]
|
||||
default: b1bb0ce7-ebca-4fa7-95ed-4840d7000000
|
||||
descriptor_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
provider:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Company' ] ]
|
||||
default: 'Company'
|
||||
product_name:
|
||||
type: string
|
||||
constraints: [ valid_values: [ 'Sample VNF' ] ]
|
||||
default: 'Sample VNF'
|
||||
software_version:
|
||||
type: string
|
||||
constraints: [ valid_values: [ '1.0' ] ]
|
||||
default: '1.0'
|
||||
vnfm_info:
|
||||
type: list
|
||||
entry_schema:
|
||||
type: string
|
||||
constraints: [ valid_values: [ Tacker ] ]
|
||||
default: [ Tacker ]
|
||||
flavour_id:
|
||||
type: string
|
||||
constraints: [ valid_values: [ simple ] ]
|
||||
default: simple
|
||||
flavour_description:
|
||||
type: string
|
||||
default: "flavour"
|
||||
requirements:
|
||||
- virtual_link_external1:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_external2:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
- virtual_link_internal:
|
||||
capability: tosca.capabilities.nfv.VirtualLinkable
|
||||
interfaces:
|
||||
Vnflcm:
|
||||
type: tosca.interfaces.nfv.Vnflcm
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright (C) 2022 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 FailScript(object):
|
||||
def __init__(self, vnfc_param):
|
||||
self.vnfc_param = vnfc_param
|
||||
|
||||
def run(self):
|
||||
operation = 'change_vnfpkg'
|
||||
if self.vnfc_param['is_rollback']:
|
||||
operation += '_rollback'
|
||||
if os.path.exists(f'/tmp/{operation}'):
|
||||
raise Exception(f'test {operation} error')
|
||||
|
||||
|
||||
def main():
|
||||
vnfc_param = pickle.load(sys.stdin.buffer)
|
||||
script = FailScript(vnfc_param)
|
||||
script.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,68 @@
|
|||
# Copyright (C) 2022 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 functools
|
||||
import os
|
||||
import pickle
|
||||
import sys
|
||||
|
||||
|
||||
class FailScript(object):
|
||||
"""Define error method for each operation
|
||||
|
||||
For example:
|
||||
|
||||
def instantiate_start(self):
|
||||
if os.path.exists('/tmp/instantiate_start')
|
||||
raise Exception('test instantiate_start error')
|
||||
"""
|
||||
|
||||
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 _fail(self, method):
|
||||
if os.path.exists(f'/tmp/{method}'):
|
||||
raise Exception(f'test {method} error')
|
||||
|
||||
def __getattr__(self, name):
|
||||
return functools.partial(self._fail, name)
|
||||
|
||||
|
||||
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 = FailScript(req, inst, grant_req, grant, csar_dir)
|
||||
getattr(script, operation)()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
os._exit(0)
|
||||
except Exception as ex:
|
||||
sys.stderr.write(str(ex))
|
||||
sys.stderr.flush()
|
||||
os._exit(1)
|
|
@ -0,0 +1,4 @@
|
|||
TOSCA-Meta-File-Version: 1.0
|
||||
CSAR-Version: 1.1
|
||||
Created-by: Onboarding portal
|
||||
Entry-Definitions: Definitions/v2_sample4_top.vnfd.yaml
|
|
@ -0,0 +1,52 @@
|
|||
# Copyright (C) 2022 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 json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.tests.functional.sol_v2_common import paramgen
|
||||
from tacker.tests.functional.sol_v2_common import utils
|
||||
|
||||
|
||||
zip_file_name = os.path.basename(os.path.abspath(".")) + '.zip'
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
vnfd_id = uuidutils.generate_uuid()
|
||||
|
||||
# tacker/tests/etc...
|
||||
# /functional/sol_v2_common/samples/sampleX
|
||||
image_dir = "../../../../etc/samples/etsi/nfv/common/Files/images/"
|
||||
image_file = "cirros-0.5.2-x86_64-disk.img"
|
||||
image_path = os.path.abspath(image_dir + image_file)
|
||||
|
||||
# tacker/sol_refactored/infra_drivers/openstack/userdata_standard.py
|
||||
# /tests/functional/sol_v2_common/samples/sampleX
|
||||
userdata_dir = "../../../../../sol_refactored/infra_drivers/openstack/"
|
||||
userdata_file = "userdata_standard.py"
|
||||
userdata_path = os.path.abspath(userdata_dir + userdata_file)
|
||||
|
||||
utils.make_zip(".", tmp_dir, vnfd_id, image_path=image_path,
|
||||
userdata_path=userdata_path)
|
||||
|
||||
shutil.copy(os.path.join(tmp_dir, zip_file_name), ".")
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
change_vnfpkg_req = paramgen.sample4_change_vnfpkg(vnfd_id)
|
||||
|
||||
with open("change_vnfpkg_req", "w") as f:
|
||||
f.write(json.dumps(change_vnfpkg_req, indent=2))
|
|
@ -23,7 +23,8 @@ import subprocess
|
|||
SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
|
||||
|
||||
|
||||
def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None):
|
||||
def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None,
|
||||
userdata_path=None):
|
||||
# NOTE: '.zip' will be added by shutil.make_archive
|
||||
zip_file_name = os.path.basename(os.path.abspath(sample_dir))
|
||||
zip_file_path = os.path.join(tmp_dir, zip_file_name)
|
||||
|
@ -53,6 +54,12 @@ def make_zip(sample_dir, tmp_dir, vnfd_id, image_path=None):
|
|||
os.makedirs(file_path)
|
||||
shutil.copy(image_path, file_path)
|
||||
|
||||
if userdata_path is not None:
|
||||
# mkdir UserData/ and copy userdata_path into it
|
||||
file_path = os.path.join(tmp_contents, "UserData")
|
||||
os.makedirs(file_path)
|
||||
shutil.copy(userdata_path, file_path)
|
||||
|
||||
shutil.make_archive(zip_file_path, "zip", tmp_contents)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue