change_vnfpkg enhance to enable network change

This patch enhances about network update of change_vnfpkg
v2 API.

* support adding/removing external CPs.
* support extManagedVirtualLinks attribute of
  ChangeCurrentVnfPkgRequest and support to change external
  management networks.
* support to change internal networks.

Note that precondition of using these functions is to use
StandardUserData as UserData class.

Caution: extManagedVirtualLinks attribute of
ChangeCurrentVnfPkgRequest must be specified if an external
management network used even if it is not changed while
change_vnfpkg.

Functional test of changing (nova) flavor is added too.

Implements: blueprint enhance-change-package
Change-Id: Icdca7d81bd8081fddd7cc2e24ba0ca8b6b456a41
This commit is contained in:
Itsuro Oda 2022-10-27 23:44:44 +00:00
parent 01af921686
commit 3d2ae84e17
32 changed files with 3041 additions and 149 deletions

View File

@ -53,7 +53,13 @@ class HeatClient(object):
def update_stack(self, stack_name, fields, wait=True):
path = f"stacks/{stack_name}"
resp, body = self.client.do_request(path, "PATCH",
# It was assumed that PATCH is used and therefore 'fields'
# contains only update parts and 'existing' is not used.
# Now full replacing 'fields' is supported and it is indicated
# by 'existing' is False. if 'existing' is specified and
# it is False, PUT is used.
method = "PATCH" if fields.pop('existing', True) else "PUT"
resp, body = self.client.do_request(path, method,
expected_status=[202], body=fields)
if wait:
@ -107,7 +113,7 @@ class HeatClient(object):
LOG.info("%s %s done.", operation, stack_name.split('/')[0])
raise loopingcall.LoopingCallDone()
elif status in failed_status:
LOG.error("% %s failed.", operation, stack_name.split('/')[0])
LOG.error("%s %s failed.", operation, stack_name.split('/')[0])
sol_title = "%s failed" % operation
raise sol_ex.StackOperationFailed(sol_title=sol_title,
sol_detail=status_reason)

View File

@ -126,11 +126,22 @@ class Openstack(object):
stack_name = heat_utils.get_stack_name(inst)
heat_client.delete_stack(stack_name)
def _is_full_fields(self, fields):
# NOTE: fields made by UserData class contains only update parts
# and 'existing' is not specified (and is thought as True) by
# default. if 'existing' is specified and it is False, fields is
# a full content.
return not fields.get('existing', True)
def _update_fields(self, heat_client, stack_name, fields):
if self._is_full_fields(fields):
# used by change_vnfpkg(, rollback) only at the moment.
return 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
# NOTE: Using json.loads because 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'])
@ -334,6 +345,10 @@ class Openstack(object):
vdu_name = _rsc_with_idx(vnfc.vduId, vnfc.metadata.get('vdu_idx'))
return vdu_dict.get(vdu_name, {}).get('computeFlavourId')
def _get_zone_from_vdu_dict(self, vnfc, vdu_dict):
vdu_name = _rsc_with_idx(vnfc.vduId, vnfc.metadata.get('vdu_idx'))
return vdu_dict.get(vdu_name, {}).get('locationConstraints')
def _get_images_from_vdu_dict(self, vnfc, storage_infos, vdu_dict):
vdu_idx = vnfc.metadata.get('vdu_idx')
vdu_name = _rsc_with_idx(vnfc.vduId, vdu_idx)
@ -364,6 +379,20 @@ class Openstack(object):
for vnfc in inst.instantiatedVnfInfo.vnfcResourceInfo
if vnfc.computeResource.resourceId in vnfc_res_ids]
if self._is_full_fields(fields):
# NOTE: it is used by StandardUserData only at the moment,
# use it to check the fields is constructed by
# StandardUserData (and its inheritance) or not.
method = self._change_vnfpkg_rolling_update_user_data_standard
else:
# method for DefaultUserDate (and its inheritance)
method = self._change_vnfpkg_rolling_update_user_data_default
method(req, inst, grant_req, grant, vnfd, fields, heat_client,
vnfcs, is_rollback)
def _change_vnfpkg_rolling_update_user_data_default(self, req, inst,
grant_req, grant, vnfd, fields, heat_client, vnfcs, is_rollback):
templates = {}
def _get_template(parent_stack_id):
@ -400,8 +429,7 @@ class Openstack(object):
heat_client.update_stack(parent_stack_id, vdu_fields)
else:
# pickup 'vcImageId' and 'computeFlavourId' from vdu_dict
vdu_idx = vnfc.metadata.get('vdu_idx')
vdu_name = _rsc_with_idx(vnfc.vduId, vdu_idx)
vdu_name = vnfc.vduId
new_vdus = {}
flavor = self._get_flavor_from_vdu_dict(vnfc, vdu_dict)
if flavor:
@ -418,17 +446,14 @@ class Openstack(object):
# pickup 'CP' updates
cp_names = vnfd.get_vdu_cps(
inst.instantiatedVnfInfo.flavourId, vnfc.vduId)
new_cps = {}
for cp_name in cp_names:
cp_name = _rsc_with_idx(cp_name, vdu_idx)
if cp_name in cp_dict:
new_cps[cp_name] = cp_dict[cp_name]
new_cps = {cp_name: cp_dict[cp_name]
for cp_name in cp_names if cp_name in cp_dict}
update_fields = {
'parameters': {'nfv': {'VDU': new_vdus, 'CP': new_cps}}
}
update_fields = self._update_fields(heat_client, stack_name,
update_fields)
update_fields)
LOG.debug("stack fields: %s", update_fields)
heat_client.update_stack(stack_name, update_fields)
@ -436,6 +461,80 @@ class Openstack(object):
self._execute_coordinate_vnf_script(req, vnfd, vnfc, heat_client,
is_rollback)
def _change_vnfpkg_rolling_update_user_data_standard(self, req, inst,
grant_req, grant, vnfd, fields, heat_client, vnfcs, is_rollback):
stack_name = heat_utils.get_stack_name(inst)
# make base template
base_template = heat_client.get_template(stack_name)
new_template = yaml.safe_load(fields['template'])
diff_reses = {
res_name: res_value
for res_name, res_value in new_template['resources'].items()
if res_name not in base_template['resources'].keys()
}
base_template['resources'].update(diff_reses)
base_files = heat_client.get_files(stack_name)
base_files.update(fields['files'])
base_parameters = heat_client.get_parameters(stack_name)
# NOTE: Using json.loads because parameters['nfv'] is string
base_nfv_dict = json.loads(base_parameters.get('nfv', '{}'))
new_nfv_dict = fields['parameters']['nfv']
def _get_param_third_keys(res):
"""Get third parameter keys
example:
---
VDU1-1:
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 ] }
---
returns {'VDU1-1', 'VDU1_CP1-1'}
"""
keys = set()
for prop_value in res.get('properties', {}).values():
if not isinstance(prop_value, dict):
continue
for key, value in prop_value.items():
if (key == 'get_param' and isinstance(value, list) and
len(value) >= 4 and value[0] == 'nfv'):
keys.add(value[2])
return keys
for vnfc in vnfcs:
vdu_idx = vnfc.metadata.get('vdu_idx')
vdu_name = _rsc_with_idx(vnfc.vduId, vdu_idx)
# replace VDU_{idx} part
target_res = new_template['resources'][vdu_name]
base_template['resources'][vdu_name] = target_res
# update parameters
third_keys = _get_param_third_keys(target_res)
for item in ['VDU', 'CP']:
for key, value in new_nfv_dict.get(item, {}).items():
if key in third_keys:
base_nfv_dict[item][key] = value
update_fields = {
'template': base_template,
'files': base_files,
'parameters': {'nfv': base_nfv_dict}
}
LOG.debug("update %s: stack fields: %s", vdu_name, update_fields)
heat_client.update_stack(stack_name, update_fields)
# execute coordinate_vnf_script
self._execute_coordinate_vnf_script(req, vnfd, vnfc, heat_client,
is_rollback)
def _get_ssh_ip(self, stack_id, cp_name, heat_client):
# NOTE: It is assumed that if the user want to use floating_ip,
# he must specify the resource name of 'OS::Neutron::FloatingIP'
@ -997,6 +1096,10 @@ class Openstack(object):
storage_infos, nfv_dict['VDU'])
for vdu_name, image in images.items():
metadata[f'image-{vdu_name}'] = image
zone = self._get_zone_from_vdu_dict(vnfc_res_info,
nfv_dict['VDU'])
if zone is not None:
metadata['zone'] = zone
def _make_instantiated_vnf_info(self, req, inst, grant_req, grant, vnfd,
heat_client, is_rollback=False, stack_id=None):
@ -1005,6 +1108,7 @@ class Openstack(object):
'stack_id']
stack_name = heat_utils.get_stack_name(inst, stack_id)
heat_reses = heat_client.get_resources(stack_name)
# NOTE: Using json.loads because parameters['nfv'] is string
nfv_dict = json.loads(heat_client.get_parameters(stack_name)['nfv'])
op = grant_req.operation

View File

@ -137,24 +137,24 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_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)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
vdu_idxes[vdu_name] = 0
zones = {}
for res in grant_req['addResources']:
if res['type'] != 'COMPUTE':
continue
vdu_name = res['resourceTemplateId']
if vdu_name not in poped_vdu:
if vdu_name not in popped_vdu:
continue
vdu_idx = vdu_idxes[vdu_name]
vdu_idxes[vdu_name] += 1
zones[add_idx(vdu_name, vdu_idx)] = (
common_script_utils.get_param_zone_by_vnfc(
res['id'], grant))
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
res = add_idx_to_vdu_template(popped_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)
@ -231,10 +231,10 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_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)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
vdu_idxes[vdu_name] = common_script_utils.get_current_capacity(
vdu_name, inst)
@ -243,14 +243,14 @@ class StandardUserData(userdata_utils.AbstractUserData):
if res['type'] != 'COMPUTE':
continue
vdu_name = res['resourceTemplateId']
if vdu_name not in poped_vdu:
if vdu_name not in popped_vdu:
continue
vdu_idx = vdu_idxes[vdu_name]
vdu_idxes[vdu_name] += 1
zones[add_idx(vdu_name, vdu_idx)] = (
common_script_utils.get_param_zone_by_vnfc(
res['id'], grant))
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
res = add_idx_to_vdu_template(popped_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)
@ -393,9 +393,9 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
for inst_vnfc in inst['instantiatedVnfInfo'].get(
'vnfcResourceInfo', []):
@ -403,7 +403,7 @@ class StandardUserData(userdata_utils.AbstractUserData):
if vdu_idx is None:
continue
vdu_name = inst_vnfc['vduId']
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
res = add_idx_to_vdu_template(popped_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)
@ -424,9 +424,9 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
for inst_vnfc in inst['instantiatedVnfInfo'].get(
'vnfcResourceInfo', []):
@ -434,7 +434,7 @@ class StandardUserData(userdata_utils.AbstractUserData):
if vdu_idx is None:
continue
vdu_name = inst_vnfc['vduId']
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
res = add_idx_to_vdu_template(popped_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)
@ -455,9 +455,9 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
for res in grant_req['removeResources']:
if res['type'] != 'COMPUTE':
@ -469,7 +469,7 @@ class StandardUserData(userdata_utils.AbstractUserData):
vdu_idx = inst_vnfc['metadata']['vdu_idx']
break
vdu_name = res['resourceTemplateId']
res = add_idx_to_vdu_template(poped_vdu[vdu_name], vdu_idx)
res = add_idx_to_vdu_template(popped_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)
@ -499,28 +499,65 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
poped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
for res in grant_req['removeResources']:
if res['type'] != 'COMPUTE':
target_vnfc_res_ids = [
res['resource']['resourceId']
for res in grant_req['removeResources']
if res['type'] == 'COMPUTE'
]
cur_hot_reses = {}
new_hot_reses = {}
images = {}
flavors = {}
zones = {}
for inst_vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
vdu_idx = inst_vnfc['metadata'].get('vdu_idx')
if vdu_idx is None:
# should not be None. just check for consistency.
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
vdu_name = inst_vnfc['vduId']
vdu_name_idx = add_idx(vdu_name, vdu_idx)
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
res = add_idx_to_vdu_template(popped_vdu[vdu_name], vdu_idx)
top_hot['resources'][vdu_name_idx] = res
vdus = nfv_dict.get('VDU', {})
for vdu_name, vdu_value in vdus.items():
vdu_name = rm_idx(vdu_name)
if (inst_vnfc['computeResource']['resourceId'] in
target_vnfc_res_ids):
new_hot_reses[vdu_name_idx] = res
else:
cur_hot_reses[vdu_name_idx] = res
# NOTE: only zone is necessary for new_hot_reses
for key, value in inst_vnfc['metadata'].items():
if key == 'flavor':
flavors[vdu_name_idx] = value
elif key == 'zone':
zones[vdu_name_idx] = value
elif key.startswith('image-'):
image_vdu = key.replace('image-', '')
images[image_vdu] = value
cur_nfv_dict = common_script_utils.init_nfv_dict(
{'resources': cur_hot_reses})
new_nfv_dict = common_script_utils.init_nfv_dict(
{'resources': new_hot_reses})
cur_vdus = cur_nfv_dict.get('VDU', {})
new_vdus = new_nfv_dict.get('VDU', {})
for vdu_name_idx, vdu_value in cur_vdus.items():
if 'computeFlavourId' in vdu_value:
vdu_value['computeFlavourId'] = flavors.get(vdu_name_idx)
if 'vcImageId' in vdu_value:
vdu_value['vcImageId'] = images.get(vdu_name_idx)
if 'locationConstraints' in vdu_value:
vdu_value['locationConstraints'] = zones.get(vdu_name_idx)
for vdu_name_idx, vdu_value in new_vdus.items():
vdu_name = rm_idx(vdu_name_idx)
if 'computeFlavourId' in vdu_value:
vdu_value['computeFlavourId'] = (
common_script_utils.get_param_flavor(
@ -529,14 +566,33 @@ class StandardUserData(userdata_utils.AbstractUserData):
vdu_value['vcImageId'] = common_script_utils.get_param_image(
vdu_name, flavour_id, vnfd, grant)
if 'locationConstraints' in vdu_value:
vdu_value.pop('locationConstraints')
vdu_value['locationConstraints'] = zones.get(vdu_name_idx)
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_req(cps, req, grant)
vdus = cur_vdus
vdus.update(new_vdus)
cps = cur_nfv_dict.get('CP', {})
cps.update(new_nfv_dict.get('CP', {}))
# NOTE: req includes only different part. some CPs in new_nfv_dict
# may be necessary to get from inst.
cur_cps = _get_new_cps_from_inst(cps, inst)
req_cps = _get_new_cps_from_req(cps, req, grant)
for cp_name in cps.keys():
if cp_name in req_cps:
cps[cp_name] = req_cps[cp_name]
else:
cps[cp_name] = cur_cps[cp_name]
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
fields = {
'parameters': {'nfv': {'VDU': vdus, 'CP': new_cps}}
'template': yaml.safe_dump(top_hot),
'parameters': {'nfv': {'VDU': vdus, 'CP': cps}},
'files': {},
'existing': False
}
for key, value in hot_dict.get('files', {}).items():
fields['files'][key] = yaml.safe_dump(value)
return fields
@ -549,56 +605,57 @@ class StandardUserData(userdata_utils.AbstractUserData):
top_hot = hot_dict['template']
# first modify VDU resources
poped_vdu = {}
popped_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)
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
images = {}
flavors = {}
zones = {}
for inst_vnfc in inst['instantiatedVnfInfo']['vnfcResourceInfo']:
vdu_idx = inst_vnfc['metadata'].get('vdu_idx')
if vdu_idx is None:
# should not be None. just check for consistency.
continue
vdu_name = inst_vnfc['vduId']
if vdu_name in flavors:
continue
vdu_name_idx = add_idx(vdu_name, vdu_idx)
res = add_idx_to_vdu_template(popped_vdu[vdu_name], vdu_idx)
top_hot['resources'][vdu_name_idx] = res
for key, value in inst_vnfc['metadata'].items():
if key == 'flavor':
flavors[add_idx(vdu_name, vdu_idx)] = value
flavors[vdu_name_idx] = value
elif key == 'zone':
zones[vdu_name_idx] = value
elif key.startswith('image-'):
image_vdu = key.replace('image-', '')
images[image_vdu] = value
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
vdus = nfv_dict.get('VDU', {})
new_vdus = {}
for vdu_name, vdu_value in vdus.items():
for vdu_name_idx, vdu_value in vdus.items():
vdu_name = rm_idx(vdu_name)
if 'computeFlavourId' in vdu_value:
new_vdus.setdefault(vdu_name, {})
new_vdus[vdu_name]['computeFlavourId'] = flavors.get(vdu_name)
vdu_value['computeFlavourId'] = flavors.get(vdu_name_idx)
if 'vcImageId' in vdu_value:
new_vdus.setdefault(vdu_name, {})
new_vdus[vdu_name]['vcImageId'] = images.get(vdu_name)
vdu_value['vcImageId'] = images.get(vdu_name_idx)
if 'locationConstraints' in vdu_value:
vdu_value['locationConstraints'] = zones.get(vdu_name_idx)
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_inst(cps, inst)
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
fields = {
'parameters': {'nfv': {'VDU': new_vdus, 'CP': new_cps}}
'template': yaml.safe_dump(top_hot),
'parameters': {'nfv': {'VDU': vdus, 'CP': new_cps}},
'files': {},
'existing': False
}
for key, value in hot_dict.get('files', {}).items():
fields['files'][key] = yaml.safe_dump(value)
return fields

View File

@ -30,8 +30,6 @@ class ChangeCurrentVnfPkgRequest(base.TackerObject,
'vnfdId': fields.StringField(nullable=False),
'extVirtualLinks': fields.ListOfObjectsField(
'ExtVirtualLinkData', nullable=True),
# NOTE: 'extManagedVirtualLinks' is not supported.
# It can be specified but make no effect at all.
'extManagedVirtualLinks': fields.ListOfObjectsField(
'ExtManagedVirtualLinkData', nullable=True),
'vimConnectionInfo': fields.DictOfObjectsField(

View File

@ -53,11 +53,18 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
pkg_path_2, image_path=image_path, userdata_path=userdata_path)
# for change_vnfpkg network/flavor change test
pkg_path_3 = os.path.join(cur_dir,
"../sol_v2_common/samples/userdata_standard_change_vnfpkg_nw")
cls.vnf_pkg_3, cls.vnfd_id_3 = cls.create_vnf_package(
pkg_path_3, 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)
cls.delete_vnf_package(cls.vnf_pkg_3)
def setUp(self):
super().setUp()
@ -86,7 +93,12 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return vnfc['id']
def _get_vnfc_cps(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
return {cp_info['cpdId'] for cp_info in vnfc['vnfcCpInfo']}
def _get_vnfc_cp_net_id(self, inst, vdu, index, cp):
# this is for external CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
@ -99,6 +111,20 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# must be found
return ext_vl['resourceHandle']['resourceId']
def _get_vnfc_cp_net_name(self, inst, vdu, index, cp):
# this is for internal CPs
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
for cp_info in vnfc['vnfcCpInfo']:
if cp_info['cpdId'] == cp:
# must be found
link_port_id = cp_info['vnfLinkPortId']
break
for vl in inst['instantiatedVnfInfo']['vnfVirtualLinkResourceInfo']:
for port in vl['vnfLinkPorts']:
if port['id'] == link_port_id:
# must be found
return vl['vnfVirtualLinkDescId']
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():
@ -106,6 +132,11 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# must be found
return value
def _get_vnfc_flavor(self, inst, vdu, index):
vnfc = self._get_vnfc_by_vdu_index(inst, vdu, index)
# must exist
return vnfc['metadata']['flavor']
def _delete_instance(self, inst_id):
for _ in range(3):
resp, body = self.delete_vnf_instance(inst_id)
@ -559,3 +590,220 @@ class IndividualVnfcMgmtTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# Delete VNF instance
self._delete_instance(inst_id)
def test_change_vnfpkg_nw(self):
"""Test change_vnfpkg with additional functions
* Note:
This test focuses change_vnfpkg with the following changes.
- adding external CP
- change internal network
- change flavor
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. 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)
instantiate_req['instantiationLevelId'] = "instantiation_level_2"
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, 1}, self._get_vdu_indexes(inst_1, 'VDU1'))
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
# 2. Change_vnfpkg operation
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(self.vnfd_id_3,
net_ids, subnet_ids)
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_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check vnfdId is changed
self.assertEqual(self.vnfd_id_3, inst_2['vnfdId'])
# check images are changed
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU1', 0),
self._get_vnfc_image(inst_2, 'VDU1', 0))
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU1', 1),
self._get_vnfc_image(inst_2, 'VDU1', 1))
self.assertNotEqual(self._get_vnfc_image(inst_1, 'VDU2', 0),
self._get_vnfc_image(inst_2, 'VDU2', 0))
# check flavor is changed (VDU2 only)
self.assertNotEqual(self._get_vnfc_flavor(inst_1, 'VDU2', 0),
self._get_vnfc_flavor(inst_2, 'VDU2', 0))
# check external CPs, VDU1_CP4 and VDU2_CP4 are added
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_1, 'VDU1', 0))
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_1, 'VDU1', 1))
self.assertFalse('VDU2_CP4' in self._get_vnfc_cps(inst_1, 'VDU2', 0))
self.assertTrue('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertTrue('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 1))
self.assertTrue('VDU2_CP4' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 0, 'VDU1_CP4'))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU1', 1, 'VDU1_CP4'))
self.assertEqual(net_ids['net0'],
self._get_vnfc_cp_net_id(inst_2, 'VDU2', 0, 'VDU2_CP4'))
# check internal CPs, VDU1_CP3 and VDU2_CP3 are changed
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual("internalVL2",
self._get_vnfc_cp_net_name(inst_1, 'VDU2', 0, 'VDU2_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual("internalVL3",
self._get_vnfc_cp_net_name(inst_2, 'VDU2', 0, 'VDU2_CP3'))
# Terminate VNF instance
terminate_req = paramgen.sample5_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_change_vnfpkg_nw_rollback(self):
"""Test rollback of change_vnfpkg with additional functions
* Note:
This test focuses rollback of change_vnfpkg with the following
changes.
- adding external CP
- change internal network
- change flavor
* About LCM operations:
This test includes the following operations.
- Create VNF instance
- 1. Instantiate VNF instance
- Show VNF instance / check
- 2. 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']
# 1. Instantiate VNF instance
instantiate_req = paramgen.sample3_instantiate(
net_ids, subnet_ids, self.auth_url)
instantiate_req['instantiationLevelId'] = "instantiation_level_2"
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, 1}, self._get_vdu_indexes(inst_1, 'VDU1'))
self.assertEqual({0}, self._get_vdu_indexes(inst_1, 'VDU2'))
# 2. Change_vnfpkg operation
self._put_fail_file('change_vnfpkg')
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(self.vnfd_id_3,
net_ids, subnet_ids)
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_2 = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
# check vnfdId is not changed
self.assertEqual(self.vnfd_id_1, inst_2['vnfdId'])
# check images are not changed
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU1', 0),
self._get_vnfc_image(inst_2, 'VDU1', 0))
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU1', 1),
self._get_vnfc_image(inst_2, 'VDU1', 1))
self.assertEqual(self._get_vnfc_image(inst_1, 'VDU2', 0),
self._get_vnfc_image(inst_2, 'VDU2', 0))
# check flavor is not changed (VDU2 only)
self.assertEqual(self._get_vnfc_flavor(inst_1, 'VDU2', 0),
self._get_vnfc_flavor(inst_2, 'VDU2', 0))
# check external CPs, VDU1_CP4 and VDU2_CP4 are not added
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 0))
self.assertFalse('VDU1_CP4' in self._get_vnfc_cps(inst_2, 'VDU1', 1))
self.assertFalse('VDU2_CP4' in self._get_vnfc_cps(inst_2, 'VDU2', 0))
# check internal CPs, VDU1_CP3 and VDU2_CP3 are not changed
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 0, 'VDU1_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 0, 'VDU1_CP3'))
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU1', 1, 'VDU1_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU1', 1, 'VDU1_CP3'))
self.assertEqual(
self._get_vnfc_cp_net_name(inst_1, 'VDU2', 0, 'VDU2_CP3'),
self._get_vnfc_cp_net_name(inst_2, 'VDU2', 0, 'VDU2_CP3'))
# 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)

View File

@ -1015,7 +1015,7 @@ def sample3_instantiate(net_ids, subnet_ids, auth_url):
"extVirtualLinks": [ext_vl_1],
"extManagedVirtualLinks": [
{
"id": uuidutils.generate_uuid(),
"id": "ext_managed_vl_1",
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": net_ids['net_mgmt']
},
@ -1141,6 +1141,13 @@ def sample4_change_vnfpkg(vnfd_id, net_ids, subnet_ids):
]
}
],
"extManagedVirtualLinks": [
{
"id": "ext_managed_vl_1",
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": net_ids['net_mgmt']
},
],
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-new-vnf": "./Scripts/coordinate_vnf.py",
@ -1197,3 +1204,139 @@ def server_notification(alarm_id):
}
}
}
# sample5 is for change_vnfpkg test of StandardUserData
#
def sample5_change_vnfpkg(vnfd_id, net_ids, subnet_ids):
ext_vl_4 = {
"id": "ext_vl_id_net4",
"resourceId": net_ids['net0'],
"extCps": [
{
"cpdId": "VDU1_CP4",
"cpConfig": {
"VDU1_CP4_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1}]}}]}
}
},
{
"cpdId": "VDU2_CP4",
"cpConfig": {
"VDU2_CP4_1": {
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1}]}}]}
}
}
]
}
return {
"vnfdId": vnfd_id,
"extVirtualLinks": [ext_vl_4],
"extManagedVirtualLinks": [
{
"id": "ext_managed_vl_1",
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": net_ids['net_mgmt']
},
],
"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 sample5_change_vnfpkg_back(vnfd_id, net_ids, subnet_ids):
return {
"vnfdId": vnfd_id,
"extManagedVirtualLinks": [
{
"id": "ext_managed_vl_1",
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": net_ids['net_mgmt']
},
],
"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 sample5_terminate():
return {
"terminationType": "FORCEFUL"
}

View File

@ -45,7 +45,8 @@ resources:
multi:
type: OS::Cinder::VolumeType
properties:
name: VDU2-multi
# making unique name to avoid conflicting with other packages
name: { list_join: ['-', [get_param: OS::stack_name, 'VDU2-multi']] }
metadata: { multiattach: "<is> True" }
# extVL with numDynamicAddresses and subnet

View File

@ -1,5 +1,5 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
description: 'For Test of Individual Vnfc Management: sample3'
parameters:
nfv:

View File

@ -6,14 +6,14 @@ parameters:
type: string
image-VDU1:
type: string
zone:
type: string
net1:
type: string
net2:
type: string
net3:
type: string
affinity:
type: string
resources:
VDU1:
@ -25,14 +25,13 @@ resources:
networks:
- port:
get_resource: VDU1_CP1
# replace the following line to Port ID when extmanagedVLs' Ports are
# specified in instantiatevnfrequest
# 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 }
availability_zone: { get_param: zone }
# extVL without FixedIP or with numDynamicAddresses
VDU1_CP1:
@ -40,8 +39,8 @@ resources:
properties:
network: { get_param: net1 }
# CPs of internal VLs are deleted when extmanagedVLs and port are
# specified in instantiatevnfrequest
# CPs of internal VLs are deleted when extManagedVLs and port are
# specified in InstantiateVnfRequest
VDU1_CP2:
type: OS::Neutron::Port
properties:

View File

@ -27,8 +27,8 @@ resources:
networks:
- port:
get_resource: VDU2_CP1
# replace the following line to Port ID when extmanagedVLs' Ports are
# specified in instantiatevnfrequest
# replace the following line to Port ID when extManagedVLs' Ports are
# specified in InstantiateVnfRequest
- port:
get_resource: VDU2_CP2
- port:
@ -45,7 +45,8 @@ resources:
multi:
type: OS::Cinder::VolumeType
properties:
name: VDU2-multi
# making unique name to avoid conflicting with other packages
name: { list_join: ['-', [get_param: OS::stack_name, 'VDU2-multi']] }
metadata: { multiattach: "<is> True" }
# extVL with numDynamicAddresses and subnet
@ -56,8 +57,8 @@ resources:
fixed_ips:
- subnet: { get_param: subnet1 }
# CPs of internal VLs are deleted when extmanagedVLs and port are
# specified in instantiatevnfrequest
# CPs of internal VLs are deleted when extManagedVLs and port are
# specified in InstantiateVnfRequest
VDU2_CP2:
type: OS::Neutron::Port
properties:

View File

@ -1,5 +1,5 @@
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
description: 'For Test of Individual Vnfc Management: sample4'
parameters:
nfv:
@ -11,10 +11,10 @@ resources:
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image-VDU1: { get_param: [ nfv, VDU, VDU1, vcImageId ] }
zone: { get_param: [ nfv, VDU, VDU1, locationConstraints] }
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
@ -27,7 +27,7 @@ resources:
net3: { get_resource: internalVL2 }
affinity: { get_resource: nfvi_node_affinity }
# delete the following lines when extmanagedVLs are specified in instantiatevnfrequest
# delete the following lines when extManagedVLs are specified in InstantiateVnfRequest
internalVL1:
type: OS::Neutron::Net
internalVL2:

View File

@ -0,0 +1,61 @@
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
net4:
type: string
zone:
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
- port:
get_resource: VDU1_CP4
availability_zone: { get_param: zone }
# extVL without FixedIP or with numDynamicAddresses
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
VDU1_CP4:
type: OS::Neutron::Port
properties:
network: { get_param: net4 }
# 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 }

View File

@ -0,0 +1,79 @@
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
net4:
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
- port:
get_resource: VDU2_CP4
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:
# making unique name to avoid conflicting with other packages
name: { list_join: ['-', [get_param: OS::stack_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 }
VDU2_CP4:
type: OS::Neutron::Port
properties:
network: { get_param: net4 }
# 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 }

View File

@ -0,0 +1,59 @@
heat_template_version: 2013-05-23
description: 'For Test of Individual Vnfc Management: sample5'
parameters:
nfv:
type: json
resources:
VDU1:
type: VDU1-ver2.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: internalVL3 }
net4: { get_param: [ nfv, CP, VDU1_CP4, network ] }
zone: { get_param: [ nfv, VDU, VDU1, locationConstraints ] }
VDU2:
type: VDU2-ver2.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: internalVL3 }
net4: { get_param: [ nfv, CP, VDU2_CP4, network ] }
affinity: { get_resource: nfvi_node_affinity }
# delete the following lines when extManagedVLs are specified in InstantiateVnfRequest
internalVL1:
type: OS::Neutron::Net
internalVL3:
type: OS::Neutron::Net
internalVL1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL1
cidr: 192.168.3.0/24
internalVL3_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL3
cidr: 192.168.5.0/24
nfvi_node_affinity:
type: OS::Nova::ServerGroup
properties:
name: nfvi_node_affinity
policies: [ 'affinity' ]
outputs: {}

View File

@ -0,0 +1,375 @@
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_sample5_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 ]
virtual_link_external1_3: [ VDU1_CP4, virtual_link ]
virtual_link_external1_4: [ VDU2_CP4, 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.small
support_mandatory: true
target_performance_parameters:
entry_schema: test
virtual_memory:
virtual_mem_size: 2048 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: internalVL3
VDU1_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU1
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: internalVL3
VDU2_CP4:
type: tosca.nodes.nfv.VduCp
properties:
layer_protocols: [ ipv4 ]
order: 0
requirements:
- virtual_binding: VDU2
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
internalVL3:
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.5.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 ]
- internalVL3_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: [ internalVL3 ]
- policy_antiaffinity_group:
type: tosca.policies.nfv.AntiAffinityRule
targets: [ affinityOrAntiAffinityGroup1 ]
properties:
scope: nfvi_node

View File

@ -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_sample5_types.yaml
- v2_sample5_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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1,4 @@
TOSCA-Meta-File-Version: 1.0
CSAR-Version: 1.1
Created-by: Onboarding portal
Entry-Definitions: Definitions/v2_sample5_top.vnfd.yaml

View File

@ -0,0 +1,60 @@
# 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)
net_ids = utils.get_network_ids(['net0', 'net1', 'net_mgmt'])
subnet_ids = utils.get_subnet_ids(['subnet0', 'subnet1'])
change_vnfpkg_req = paramgen.sample5_change_vnfpkg(
vnfd_id, net_ids, subnet_ids)
change_vnfpkg_back_req = paramgen.sample5_change_vnfpkg_back(
"input-original-vnfd-id", net_ids, subnet_ids)
with open("change_vnfpkg_req", "w") as f:
f.write(json.dumps(change_vnfpkg_req, indent=2))
with open("change_vnfpkg_back_req", "w") as f:
f.write(json.dumps(change_vnfpkg_back_req, indent=2))

View File

@ -735,9 +735,7 @@ _change_vnfpkg_example = {
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-old-vnf": "./Scripts/coordinate_old_vnf.py",
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
"lcm-operation-coordinate-new-vnf": "./Scripts/coordinate_new_vnf.py",
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
"vdu_params": [
{
"vdu_id": "VDU1",
@ -769,9 +767,7 @@ _change_cnf_vnfpkg_example = {
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-old-vnf": "Scripts/coordinate_old_vnf.py",
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
"lcm-operation-coordinate-new-vnf": "Scripts/coordinate_new_vnf.py",
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
"lcm-kubernetes-def-files": [
"Files/new_kubernetes/new_deployment.yaml"
],

View File

@ -156,6 +156,70 @@ _instantiate_req_example = {
}
}
# example for test of StandardUserData
_instantiate_req_example_S = {
"flavourId": SAMPLE_FLAVOUR_ID,
"extVirtualLinks": [
{
"id": "id_ext_vl_1",
"resourceId": "res_id_ext_vl_1",
"extCps": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1
}
]
}
}
]
}
}
},
{
"cpdId": "VDU2_CP1",
"cpConfig": {
"VDU2_CP1_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": "res_id_subnet"
}
]
}
}
]
}
}
}
]
}
],
"extManagedVirtualLinks": [
{
"id": "id_ext_mgd_1",
"vnfVirtualLinkDescId": "internalVL1",
"resourceId": "res_id_internalVL1"
}
],
"vimConnectionInfo": {
"vim1": _vim_connection_info_example
}
}
# ChangeExtVnfConnectivityRequest example
_change_ext_conn_req_example = {
"extVirtualLinks": [
@ -233,54 +297,6 @@ _change_ext_conn_req_example = {
]
}
_change_vnfpkg_example = {
"vnfdId": uuidutils.generate_uuid(),
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-old-vnf": "Scripts/coordinate_old_vnf.py",
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
"lcm-operation-coordinate-new-vnf": "Scripts/coordinate_new_vnf.py",
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
"vdu_params": [{
"vdu_id": "VDU1",
"old_vnfc_param": {
"cp_name": "CP1",
"username": "ubuntu",
"password": "ubuntu"
},
"new_vnfc_param": {
"cp_name": "CP1",
"username": "ubuntu",
"password": "ubuntu"
},
}]
}
}
_change_vnfpkg_example_2 = {
"vnfdId": uuidutils.generate_uuid(),
"additionalParams": {
"upgrade_type": "RollingUpdate",
"lcm-operation-coordinate-old-vnf": "Scripts/coordinate_old_vnf.py",
"lcm-operation-coordinate-old-vnf-class": "CoordinateOldVnf",
"lcm-operation-coordinate-new-vnf": "Scripts/coordinate_new_vnf.py",
"lcm-operation-coordinate-new-vnf-class": "CoordinateNewVnf",
"vdu_params": [{
"vdu_id": "VDU2",
"old_vnfc_param": {
"cp_name": "CP1",
"username": "ubuntu",
"password": "ubuntu"
},
"new_vnfc_param": {
"cp_name": "CP1",
"username": "ubuntu",
"password": "ubuntu"
},
}]
}
}
# heat resources examples
# NOTE:
# - following attributes which are not related to tests are omitted.
@ -848,6 +864,328 @@ _heat_reses_example = (
_heat_reses_example_change_ext_conn = (
_heat_reses_example_base + _heat_reses_example_cps_after)
# heat resources example for test of StandardUserData
_stack_id_VDU1_0_S = (
"vnf-881718c6-830b-4232-9e83-4b0cd7678713-VDU1-0-ntteaet43n2s/"
"46c6328e-db32-4da0-8dee-c69079644982")
_href_VDU1_0_S = "".join((_url, _stack_id_VDU1_0_S))
_stack_id_VDU1_1_S = (
"vnf-881718c6-830b-4232-9e83-4b0cd7678713-VDU1-1-vswbicn4ezjc/"
"64e143e1-49dc-4114-ad7c-5bb74cbd1efe")
_href_VDU1_1_S = "".join((_url, _stack_id_VDU1_1_S))
_stack_id_VDU2_0_S = (
"vnf-881718c6-830b-4232-9e83-4b0cd7678713-VDU2-0-hht2ozgyxu7q/"
"f1398e13-26ca-4d87-93ef-883bac1f82f6")
_href_VDU2_0_S = "".join((_url, _stack_id_VDU2_0_S))
_heat_reses_example_S = [
{
"creation_time": "2022-11-24T23:31:01Z",
"resource_name": "VDU1-0",
"physical_resource_id": "46c6328e-db32-4da0-8dee-c69079644982",
"resource_type": "VDU1.yaml",
"links": [
{
"href": _href,
"rel": "stack"
}
],
"required_by": []
},
{
"creation_time": "2022-11-24T23:31:01Z",
"resource_name": "VDU1-1",
"physical_resource_id": "64e143e1-49dc-4114-ad7c-5bb74cbd1efe",
"resource_type": "VDU1.yaml",
"links": [
{
"href": _href,
"rel": "stack"
}
],
"required_by": []
},
{
"creation_time": "2022-11-24T23:31:02Z",
"resource_name": "internalVL2_subnet",
"physical_resource_id": "acbd442b-00cc-4dfa-9243-8943673ff8cb",
"resource_type": "OS::Neutron::Subnet",
"links": [
{
"href": _href,
"rel": "stack"
}
],
"required_by": []
},
{
"creation_time": "2022-11-24T23:31:02Z",
"resource_name": "VDU2-0",
"physical_resource_id": "f1398e13-26ca-4d87-93ef-883bac1f82f6",
"resource_type": "VDU2.yaml",
"links": [
{
"href": _href,
"rel": "stack"
}
],
"required_by": []
},
{
"creation_time": "2022-11-24T23:31:02Z",
"resource_name": "internalVL2",
"physical_resource_id": "res_id_internalVL2",
"resource_type": "OS::Neutron::Net",
"links": [
{
"href": _href,
"rel": "stack"
}
],
"required_by": [
"VDU1-0",
"internalVL2_subnet",
"VDU2-0",
"VDU1-1"
]
},
{
"creation_time": "2022-11-24T23:31:02Z",
"resource_name": "nfvi_node_affinity",
"physical_resource_id": "a91fc295-72fd-456e-a607-67bbfceabece",
"resource_type": "OS::Nova::ServerGroup",
"links": [
{
"href": _href,
"rel": "stack"
}
],
"required_by": [
"VDU2-0"
]
},
{
"creation_time": "2022-11-24T23:31:17Z",
"resource_name": "VDU1",
"physical_resource_id": "res_id_VDU1_0",
"resource_type": "OS::Nova::Server",
"links": [
{
"href": _href_VDU1_0_S,
"rel": "stack"
}
],
"required_by": [],
"parent_resource": "VDU1-0"
},
{
"creation_time": "2022-11-24T23:31:17Z",
"resource_name": "VDU1_CP3",
"physical_resource_id": "res_id_VDU1_CP3_0",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU1_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU1"
],
"parent_resource": "VDU1-0"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "VDU1_CP1",
"physical_resource_id": "res_id_VDU1_CP1_0",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU1_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU1"
],
"parent_resource": "VDU1-0"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "VDU1_CP2",
"physical_resource_id": "res_id_VDU1_CP2_0",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU1_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU1"
],
"parent_resource": "VDU1-0"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "VDU1",
"physical_resource_id": "res_id_VDU1_1",
"resource_type": "OS::Nova::Server",
"links": [
{
"href": _href_VDU1_1_S,
"rel": "stack"
}
],
"required_by": [],
"parent_resource": "VDU1-1"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "VDU1_CP3",
"physical_resource_id": "res_id_VDU1_CP3_1",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU1_1_S,
"rel": "stack"
}
],
"required_by": [
"VDU1"
],
"parent_resource": "VDU1-1"
},
{
"creation_time": "2022-11-24T23:31:19Z",
"resource_name": "VDU1_CP1",
"physical_resource_id": "res_id_VDU1_CP1_1",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU1_1_S,
"rel": "stack"
}
],
"required_by": [
"VDU1"
],
"parent_resource": "VDU1-1"
},
{
"creation_time": "2022-11-24T23:31:20Z",
"resource_name": "VDU1_CP2",
"physical_resource_id": "res_id_VDU1_CP2_1",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU1_1_S,
"rel": "stack"
}
],
"required_by": [
"VDU1"
],
"parent_resource": "VDU1-1"
},
{
"creation_time": "2022-11-24T23:31:17Z",
"resource_name": "VDU2",
"physical_resource_id": "res_id_VDU2_0",
"resource_type": "OS::Nova::Server",
"links": [
{
"href": _href_VDU2_0_S,
"rel": "stack"
}
],
"required_by": [],
"parent_resource": "VDU2-0"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "VDU2_CP3",
"physical_resource_id": "res_id_VDU2_CP3_0",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU2_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU2"
],
"parent_resource": "VDU2-0"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "VDU2-VirtualStorage",
"physical_resource_id": "res_id_VDU2-VirtualStorage_0",
"resource_type": "OS::Cinder::Volume",
"links": [
{
"href": _href_VDU2_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU2"
],
"parent_resource": "VDU2-0"
},
{
"creation_time": "2022-11-24T23:31:18Z",
"resource_name": "multi",
"physical_resource_id": "3374895e-2f83-4061-bad7-1085215efa68",
"resource_type": "OS::Cinder::VolumeType",
"links": [
{
"href": _href_VDU2_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU2-VirtualStorage"
],
"parent_resource": "VDU2-0"
},
{
"creation_time": "2022-11-24T23:31:19Z",
"resource_name": "VDU2_CP2",
"physical_resource_id": "res_id_VDU2_CP2_0",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU2_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU2"
],
"parent_resource": "VDU2-0"
},
{
"creation_time": "2022-11-24T23:31:20Z",
"resource_name": "VDU2_CP1",
"physical_resource_id": "res_id_VDU2_CP1_0",
"resource_type": "OS::Neutron::Port",
"links": [
{
"href": _href_VDU2_0_S,
"rel": "stack"
}
],
"required_by": [
"VDU2"
],
"parent_resource": "VDU2-0"
}
]
# heat get_parameters example
_nfv_dict = {
"VDU": {
@ -860,6 +1198,21 @@ _heat_get_parameters_example = {
'nfv': json.dumps(_nfv_dict)
}
# heat get_parameters example for test of StandardUserData
_nfv_dict_S = {
"VDU": {
"VDU1-0": {"vcImageId": "image-VDU1", "computeFlavourId": "m1.tiny",
"locationConstraints": "zone1"},
"VDU1-1": {"vcImageId": "image-VDU1", "computeFlavourId": "m1.tiny",
"locationConstraints": "zone1"},
"VDU2-0": {"computeFlavourId": "m1.small"},
"VDU2-VirtualStorage-0": {"vcImageId": "image-VDU2"}
}
}
_heat_get_parameters_example_S = {
'nfv': json.dumps(_nfv_dict_S)
}
# heat get_template example
_heat_get_template_example = {
"resources": {
@ -2266,6 +2619,382 @@ _expected_inst_info_change_ext_conn = {
}
}
# expected results for test of StandardUserData
_expected_inst_info_S = {
"flavourId": "simple",
"vnfState": "STARTED",
"extCpInfo": [
{
"id": "cp-res_id_VDU1_CP1_0",
"cpdId": "VDU1_CP1",
"cpConfigId": "VDU1_CP1_1",
"cpProtocolInfo": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"isDynamic": True
}
]
}
}
],
"extLinkPortId": "res_id_VDU1_CP1_0",
"associatedVnfcCpId": "VDU1_CP1-res_id_VDU1_0"
},
{
"id": "cp-res_id_VDU1_CP1_1",
"cpdId": "VDU1_CP1",
"cpConfigId": "VDU1_CP1_1",
"cpProtocolInfo": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"isDynamic": True
}
]
}
}
],
"extLinkPortId": "res_id_VDU1_CP1_1",
"associatedVnfcCpId": "VDU1_CP1-res_id_VDU1_1"
},
{
"id": "cp-res_id_VDU2_CP1_0",
"cpdId": "VDU2_CP1",
"cpConfigId": "VDU2_CP1_1",
"cpProtocolInfo": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"isDynamic": True,
"subnetId": "res_id_subnet"
}
]
}
}
],
"extLinkPortId": "res_id_VDU2_CP1_0",
"associatedVnfcCpId": "VDU2_CP1-res_id_VDU2_0"
}
],
"extVirtualLinkInfo": [
{
"id": "id_ext_vl_1",
"resourceHandle": {
"resourceId": "res_id_ext_vl_1"
},
"extLinkPorts": [
{
"id": "res_id_VDU1_CP1_0",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_CP1_0",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "cp-res_id_VDU1_CP1_0"
},
{
"id": "res_id_VDU1_CP1_1",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_CP1_1",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "cp-res_id_VDU1_CP1_1"
},
{
"id": "res_id_VDU2_CP1_0",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU2_CP1_0",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "cp-res_id_VDU2_CP1_0"
}
],
"currentVnfExtCpData": [
{
"cpdId": "VDU1_CP1",
"cpConfig": {
"VDU1_CP1_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1
}
]
}
}
]
}
}
},
{
"cpdId": "VDU2_CP1",
"cpConfig": {
"VDU2_CP1_1": {
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": "res_id_subnet"
}
]
}
}
]
}
}
}
]
}
],
"extManagedVirtualLinkInfo": [
{
"id": "id_ext_mgd_1",
"vnfVirtualLinkDescId": "internalVL1",
"networkResource": {
"resourceId": "res_id_internalVL1"
},
"vnfLinkPorts": [
{
"id": "res_id_VDU1_CP2_0",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_CP2_0",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "VDU1_CP2-res_id_VDU1_0",
"cpInstanceType": "VNFC_CP"
},
{
"id": "res_id_VDU1_CP2_1",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_CP2_1",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "VDU1_CP2-res_id_VDU1_1",
"cpInstanceType": "VNFC_CP"
},
{
"id": "res_id_VDU2_CP2_0",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU2_CP2_0",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "VDU2_CP2-res_id_VDU2_0",
"cpInstanceType": "VNFC_CP"
}
]
}
],
"vnfcResourceInfo": [
{
"id": "res_id_VDU1_1",
"vduId": "VDU1",
"computeResource": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_1",
"vimLevelResourceType": "OS::Nova::Server"
},
"vnfcCpInfo": [
{
"id": "VDU1_CP1-res_id_VDU1_1",
"cpdId": "VDU1_CP1",
"vnfExtCpId": "cp-res_id_VDU1_CP1_1"
},
{
"id": "VDU1_CP2-res_id_VDU1_1",
"cpdId": "VDU1_CP2",
"vnfLinkPortId": "res_id_VDU1_CP2_1"
},
{
"id": "VDU1_CP3-res_id_VDU1_1",
"cpdId": "VDU1_CP3",
"vnfLinkPortId": "res_id_VDU1_CP3_1"
}
],
"metadata": {
"creation_time": "2022-11-24T23:31:18Z",
"stack_id": _stack_id_VDU1_1_S,
"vdu_idx": 1,
"flavor": "m1.tiny",
"image-VDU1-1": "image-VDU1",
"zone": "zone1"
}
},
{
"id": "res_id_VDU1_0",
"vduId": "VDU1",
"computeResource": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_0",
"vimLevelResourceType": "OS::Nova::Server"
},
"vnfcCpInfo": [
{
"id": "VDU1_CP1-res_id_VDU1_0",
"cpdId": "VDU1_CP1",
"vnfExtCpId": "cp-res_id_VDU1_CP1_0"
},
{
"id": "VDU1_CP2-res_id_VDU1_0",
"cpdId": "VDU1_CP2",
"vnfLinkPortId": "res_id_VDU1_CP2_0"
},
{
"id": "VDU1_CP3-res_id_VDU1_0",
"cpdId": "VDU1_CP3",
"vnfLinkPortId": "res_id_VDU1_CP3_0"
}
],
"metadata": {
"creation_time": "2022-11-24T23:31:17Z",
"stack_id": _stack_id_VDU1_0_S,
"vdu_idx": 0,
"flavor": "m1.tiny",
"image-VDU1-0": "image-VDU1",
"zone": "zone1"
}
},
{
"id": "res_id_VDU2_0",
"vduId": "VDU2",
"computeResource": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU2_0",
"vimLevelResourceType": "OS::Nova::Server"
},
"storageResourceIds": [
"res_id_VDU2-VirtualStorage_0"
],
"vnfcCpInfo": [
{
"id": "VDU2_CP1-res_id_VDU2_0",
"cpdId": "VDU2_CP1",
"vnfExtCpId": "cp-res_id_VDU2_CP1_0"
},
{
"id": "VDU2_CP2-res_id_VDU2_0",
"cpdId": "VDU2_CP2",
"vnfLinkPortId": "res_id_VDU2_CP2_0"
},
{
"id": "VDU2_CP3-res_id_VDU2_0",
"cpdId": "VDU2_CP3",
"vnfLinkPortId": "res_id_VDU2_CP3_0"
}
],
"metadata": {
"creation_time": "2022-11-24T23:31:17Z",
"stack_id": _stack_id_VDU2_0_S,
"vdu_idx": 0,
"flavor": "m1.small",
"image-VDU2-VirtualStorage-0": "image-VDU2"
}
}
],
"vnfVirtualLinkResourceInfo": [
{
"id": "res_id_internalVL2",
"vnfVirtualLinkDescId": "internalVL2",
"networkResource": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_internalVL2",
"vimLevelResourceType": "OS::Neutron::Net"
},
"vnfLinkPorts": [
{
"id": "res_id_VDU1_CP3_0",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_CP3_0",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "VDU1_CP3-res_id_VDU1_0",
"cpInstanceType": "VNFC_CP"
},
{
"id": "res_id_VDU1_CP3_1",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU1_CP3_1",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "VDU1_CP3-res_id_VDU1_1",
"cpInstanceType": "VNFC_CP"
},
{
"id": "res_id_VDU2_CP3_0",
"resourceHandle": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU2_CP3_0",
"vimLevelResourceType": "OS::Neutron::Port"
},
"cpInstanceId": "VDU2_CP3-res_id_VDU2_0",
"cpInstanceType": "VNFC_CP"
}
]
}
],
"virtualStorageResourceInfo": [
{
"id": "res_id_VDU2-VirtualStorage_0",
"virtualStorageDescId": "VDU2-VirtualStorage",
"storageResource": {
"vimConnectionId": "vim_id_1",
"resourceId": "res_id_VDU2-VirtualStorage_0",
"vimLevelResourceType": "OS::Cinder::Volume"
},
"metadata": {
"stack_id": _stack_id_VDU2_0_S
}
}
],
"vnfcInfo": [
{
"id": "VDU1-res_id_VDU1_1",
"vduId": "VDU1",
"vnfcResourceInfoId": "res_id_VDU1_1",
"vnfcState": "STARTED"
},
{
"id": "VDU1-res_id_VDU1_0",
"vduId": "VDU1",
"vnfcResourceInfoId": "res_id_VDU1_0",
"vnfcState": "STARTED"
},
{
"id": "VDU2-res_id_VDU2_0",
"vduId": "VDU2",
"vnfcResourceInfoId": "res_id_VDU2_0",
"vnfcState": "STARTED"
}
],
"metadata": {
"stack_id": STACK_ID
}
}
mock_resource = {
'resources': [{
'updated_time': '2021-12-27T02:53:29Z',
@ -2760,6 +3489,9 @@ class TestOpenstack(base.BaseTestCase):
self.vnfd_1 = vnfd_utils.Vnfd(SAMPLE_VNFD_ID)
self.vnfd_1.init_from_csar_dir(os.path.join(sample_dir, "sample1"))
self.vnfd_S = vnfd_utils.Vnfd(SAMPLE_VNFD_ID)
self.vnfd_S.init_from_csar_dir(os.path.join(sample_dir,
"standard_sample"))
def _check_inst_info(self, expected, result):
# sort lists before compare with an expected result since
@ -2921,6 +3653,36 @@ class TestOpenstack(base.BaseTestCase):
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_change_ext_conn, result)
def test_make_instantiated_vnf_info_standard(self):
# test of _make_instantiated_vnf_info when StandardUserData is used.
# NOTE: main difference between DefaultUserData is making metadata
# of VnfcResourceInfo.
# prepare
req = objects.InstantiateVnfRequest.from_dict(
_instantiate_req_example_S)
inst = objects.VnfInstanceV2(
id=uuidutils.generate_uuid(),
vimConnectionInfo=req.vimConnectionInfo
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.INSTANTIATE
)
grant = objects.GrantV1()
# prepare heat responses
heat_client = mock.Mock()
heat_client.get_resources.return_value = _heat_reses_example_S
heat_client.get_parameters.return_value = (
_heat_get_parameters_example_S)
# execute make_instantiated_vnf_info
self.driver._make_instantiated_vnf_info(req, inst, grant_req, grant,
self.vnfd_S, heat_client, stack_id=STACK_ID)
# check
result = inst.to_dict()["instantiatedVnfInfo"]
self._check_inst_info(_expected_inst_info_S, result)
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_stack_id')
@mock.patch.object(openstack.heat_utils.HeatClient, 'get_status')
@mock.patch.object(openstack.heat_utils.HeatClient, 'create_stack')

View File

@ -0,0 +1,52 @@
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
zone:
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
availability_zone: { get_param: zone }
# 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 }

View File

@ -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 }

View File

@ -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 }
zone: { get_param: [ nfv, VDU, VDU1, locationConstraints ] }
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: {}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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