UserData script (VNF LCM v2)¶
This document describes the requirements of userdata script how to make it for VNF LCM version 2.
Userdata script enables operators to flexibly customize VIM input parameter in LCM operations.
If you would like to know how to deploy VNF using userdata script, please check ETSI NFV-SOL VNF Deployment as VM with LCM Operation User Data or like to know how to make VNF packages including userdata script, please check VNF Package manual.
Requirements¶
Userdata script must be described according to the following rules.
Userdata class needs to be defined in userdata script file. Any file name and class name are acceptable.
Note
The names of the file and class have to correspond to following request parameters of LCM API, “lcm-operation-user-data”, “lcm-operation-user-data-class”.
userdata class must inherit “userdata_utils.AbstractUserData”, then functions have to be implemented.
Followings are requirements of methods supported by latest Tacker.
Input of all methods¶
All methods can use the following input data. The details of data types are defined in ETSI NFV SOL documents.
req: operationParams corresponding to API request
inst: VnfInstance
grant_req: GrantRequest
grant: Grants
tmp_csar_dir: the temporary path of csar expanded by Tacker
Output of methods¶
The required output is different for methods.
instantiate()¶
The method must return the following structure. Data are for stack create API in HEAT. The requirements of HEAT API are described in reference of Orchestration Service API v1 “POST /v1/{tenant_id}/stacks”
fields = {‘template’: value, ‘parameters’: value, ‘files’: value}
template: Dump of top HOT file
parameters: Input parameters for Heat API
files: Dump of all nested HOT files in the package
Following shows sample output.
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
scale()¶
The method must return the following structure if it is necessary to modify. Data are for update stack API in HEAT. The requirements of HEAT API are described in reference of Orchestration Service API v1 “PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}”
fields = {‘parameters’: {‘nfv’: {‘VDU’: new_vdus}}}
parameters: Input parameters for Heat API
Following shows sample output.
fields = {'parameters': {'nfv': {'VDU': new_vdus}}}
return fields
scale_rollback()¶
The method must return the following structure if it is necessary to modify. Data are for update stack API in HEAT. The requirements of HEAT API are described in reference of Orchestration Service API v1 “PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}”
fields = {‘parameters’: {‘nfv’: {‘VDU’: new_vdus}}}
parameters: Input parameters for Heat API
Following shows sample output.
fields = {'parameters': {'nfv': {'VDU': new_vdus}}}
return fields
change_ext_conn()¶
The method must return the following structure. Data are for update stack API in HEAT. The requirements of HEAT API are described in reference of Orchestration Service API v1 “PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}”
fields = {‘parameters’: {‘nfv’: {‘CP’: new_cps}}}
parameters: Input parameters for Heat API
Following shows sample output.
fields = {'parameters': {'nfv': {'CP': new_cps}}}
return fields
change_ext_conn_rollback()¶
The method must return the following structure. Data are for update stack API in HEAT. The requirements of HEAT API are described in reference of Orchestration Service API v1 “PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}”
fields = {‘parameters’: {‘nfv’: {‘CP’: new_cps}}}
parameters: Input parameters for Heat API
Following shows sample output.
fields = {'parameters': {'nfv': {'CP': new_cps}}}
return fields
heal()¶
The method must return the following structure. Data are for update stack API in HEAT. The requirements of HEAT API are described in reference of Orchestration Service API v1 “PATCH /v1/{tenant_id}/stacks/{stack_name}/{stack_id}”
fields = {‘parameters’: {‘nfv’: {}}}
parameters: Input parameters for Heat API
Following shows sample output.
fields = {'parameters': {'nfv': {}}}
return fields
Sample userdata script using AutoScalingGroup¶
If users do not specify the userdata in instantiate VNF request, the default process runs according to the following script.
The script can be used as a sample for making the original userdata script. It obtains HEAT input parameters such as computeFlavourId, vcImageId, locationConstraints, network, subnet, and fixed_ips from VNFD, parameters of Instantiate request and Grant.
# Copyright (C) 2021 Nippon Telegraph and Telephone Corporation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import 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 _get_new_cps_from_req(cps, req, grant):
# used by change_ext_conn and change_vnfpkg
new_cps = {}
for cp_name, cp_value in cps.items():
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, {})
new_cps[cp_name]['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, {})
new_cps[cp_name]['fixed_ips'] = fixed_ips
return new_cps
def _get_new_cps_from_inst(cps, inst):
# used by change_ext_conn and change_vnfpkg
new_cps = {}
for cp_name, cp_value in cps.items():
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, {})
new_cps[cp_name]['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, {})
new_cps[cp_name]['fixed_ips'] = fixed_ips
return new_cps
class DefaultUserData(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']
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
vdus = nfv_dict.get('VDU', {})
for vdu_name, vdu_value in vdus.items():
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))
if 'desired_capacity' in vdu_value:
vdu_value['desired_capacity'] = (
common_script_utils.get_param_capacity(
vdu_name, inst, grant_req))
cps = nfv_dict.get('CP', {})
for cp_name, cp_value in cps.items():
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):
# scale is interested in 'desired_capacity' only.
# This method returns only 'desired_capacity' part in the
# 'nfv' dict. 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']
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():
if 'desired_capacity' in vdu_value:
capacity = common_script_utils.get_param_capacity(
vdu_name, inst, grant_req)
new_vdus[vdu_name] = {'desired_capacity': capacity}
fields = {'parameters': {'nfv': {'VDU': new_vdus}}}
return fields
@staticmethod
def scale_rollback(req, inst, grant_req, grant, tmp_csar_dir):
# NOTE: This method is not called by a userdata script but
# is called by the openstack infra_driver directly now.
# It is thought that it is suitable that this method defines
# here since it is very likely to scale method above.
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']
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():
if 'desired_capacity' in vdu_value:
capacity = common_script_utils.get_current_capacity(
vdu_name, inst)
new_vdus[vdu_name] = {'desired_capacity': capacity}
fields = {'parameters': {'nfv': {'VDU': new_vdus}}}
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']
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_req(cps, req, grant)
fields = {'parameters': {'nfv': {'CP': new_cps}}}
return fields
@staticmethod
def change_ext_conn_rollback(req, inst, grant_req, grant, tmp_csar_dir):
# NOTE: This method is not called by a userdata script but
# is called by the openstack infra_driver directly now.
# It is thought that it is suitable that this method defines
# here since it is very likely to scale method above.
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']
nfv_dict = common_script_utils.init_nfv_dict(top_hot)
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_inst(cps, inst)
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']
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():
if 'computeFlavourId' in vdu_value:
flavor = common_script_utils.get_param_flavor(
vdu_name, flavour_id, vnfd, grant)
new_vdus.setdefault(vdu_name, {})
new_vdus[vdu_name]['computeFlavourId'] = flavor
if 'vcImageId' in vdu_value:
image = common_script_utils.get_param_image(
vdu_name, flavour_id, vnfd, grant)
new_vdus.setdefault(vdu_name, {})
new_vdus[vdu_name]['vcImageId'] = image
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_req(cps, req, grant)
fields = {
'parameters': {'nfv': {'VDU': new_vdus, 'CP': new_cps}}
}
return fields
@staticmethod
def change_vnfpkg_rollback(req, inst, grant_req, grant, tmp_csar_dir):
images = {}
flavors = {}
for vnfc in inst.get('instantiatedVnfInfo', {}).get(
'vnfcResourceInfo', []):
vdu_name = vnfc['vduId']
if vdu_name in flavors:
continue
for key, value in vnfc['metadata'].items():
if key == 'flavor':
flavors[vdu_name] = value
elif key.startswith('image-'):
image_vdu = key.replace('image-', '')
images[image_vdu] = value
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']
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():
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)
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_inst(cps, inst)
fields = {
'parameters': {'nfv': {'VDU': new_vdus, 'CP': new_cps}}
}
return fields
The following is sample Base HOT corresponding to above sample userdata script.
top Base HOT
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
parameters:
nfv:
type: json
resources:
VDU1_scale_group:
type: OS::Heat::AutoScalingGroup
properties:
min_size: 1
max_size: 3
desired_capacity: { get_param: [ nfv, VDU, VDU1, desired_capacity ] }
resource:
type: VDU1.yaml
properties:
flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] }
image-VDU1-VirtualStorage: { get_param: [ nfv, VDU, VDU1-VirtualStorage, vcImageId ] }
zone: { get_param: [ nfv, VDU, VDU1, locationConstraints] }
net1: { get_param: [ nfv, CP, VDU1_CP1, network] }
net2: { get_param: [ nfv, CP, VDU1_CP2, network ] }
subnet1: { get_param: [nfv, CP, VDU1_CP1, fixed_ips, 0, subnet ]}
subnet2: { get_param: [nfv, CP, VDU1_CP2, fixed_ips, 0, subnet ]}
net3: { get_resource: internalVL1 }
net4: { get_resource: internalVL2 }
net5: { get_resource: internalVL3 }
volume_type: { get_resource: VDU1-VolumeType }
# NOTE: Resource definition of OS::Heat::ScalingPolicy is omitted.
# It is not used by v2 scale implementation unlike v1.
VDU1-VolumeType:
type: OS::Cinder::VolumeType
properties:
name: VDU1-multi
metadata: { multiattach: "<is> True" }
VDU2:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
name: VDU2
availability_zone: { get_param: [ nfv, VDU, VDU2, locationConstraints ] }
block_device_mapping_v2: [{"volume_id": { get_resource: VDU2-VirtualStorage }}]
networks:
- port: { get_param: [ nfv, CP, VDU2_CP1-1, port ] }
- port: { get_param: [ nfv, CP, VDU2_CP1-2, port ] }
- port:
get_resource: VDU2_CP2
- port:
get_resource: VDU2_CP3
- port:
get_resource: VDU2_CP4
- port:
get_resource: VDU2_CP5
VDU2-VirtualStorage:
type: OS::Cinder::Volume
properties:
image: { get_param: [ nfv, VDU, VDU2-VirtualStorage, vcImageId] }
size: 1
volume_type: { get_resource: VDU2-VolumeType }
VDU2-VolumeType:
type: OS::Cinder::VolumeType
properties:
name: VDU2-multi
metadata: { multiattach: "<is> True" }
# extVL with FixedIP and Subnet
VDU2_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: [ nfv, CP, VDU2_CP2, network ] }
fixed_ips:
- ip_address: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, ip_address]}
subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 0, subnet]}
- subnet: { get_param: [nfv, CP, VDU2_CP2, fixed_ips, 1, subnet]}
VDU2_CP3:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL1 }
VDU2_CP4:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL2 }
VDU2_CP5:
type: OS::Neutron::Port
properties:
# replace the following line to VL's ID when extmanagedVLs are specified in instantiatevnfrequest
network: { get_resource: internalVL3 }
# delete the following lines when extmanagedVLs are specified in instantiatevnfrequest
internalVL1:
type: OS::Neutron::Net
internalVL2:
type: OS::Neutron::Net
internalVL3:
type: OS::Neutron::Net
internalVL1_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL1
cidr: 192.168.3.0/24
internalVL2_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL2
cidr: 192.168.4.0/24
internalVL3_subnet:
type: OS::Neutron::Subnet
properties:
ip_version: 4
network:
get_resource: internalVL3
cidr: 192.168.5.0/24
outputs: {}
nested Base HOT
heat_template_version: 2013-05-23
description: 'VDU1 HOT for Sample VNF'
parameters:
flavor:
type: string
image-VDU1-VirtualStorage:
type: string
zone:
type: string
net1:
type: string
net2:
type: string
net3:
type: string
net4:
type: string
net5:
type: string
subnet1:
type: string
subnet2:
type: string
volume_type:
type: string
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: flavor }
name: VDU1
block_device_mapping_v2: [{"volume_id": { get_resource: VDU1-VirtualStorage }}]
networks:
- port:
get_resource: VDU1_CP1
- port:
get_resource: VDU1_CP2
# replace the following line to Port ID when extmanagedVLs' Ports are specified in instantiatevnfrequest
- port:
get_resource: VDU1_CP3
- port:
get_resource: VDU1_CP4
- port:
get_resource: VDU1_CP5
availability_zone: { get_param: zone }
VDU1-VirtualStorage:
type: OS::Cinder::Volume
properties:
image: { get_param: image-VDU1-VirtualStorage }
size: 1
volume_type: { get_param: volume_type }
# extVL without FixedIP or with numDynamicAddresses
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: { get_param: net1 }
fixed_ips:
- subnet: { get_param: subnet1}
# extVL with numDynamicAddresses and subnet
VDU1_CP2:
type: OS::Neutron::Port
properties:
network: { get_param: net2 }
fixed_ips:
- subnet: { get_param: subnet2}
# delete the following line when extmanagedVLs' Ports are specified in instantiatevnfrequest
VDU1_CP3:
type: OS::Neutron::Port
properties:
network: { get_param: net3 }
VDU1_CP4:
type: OS::Neutron::Port
properties:
network: { get_param: net4 }
VDU1_CP5:
type: OS::Neutron::Port
properties:
network: { get_param: net5 }
Sample userdata script for not using AutoScalingGroup¶
Even if OS::Heat::AutoScalingGroup is not specified in HOT, Tacker can create the desired number of VNFC resources as individual resources on the basis of the VNFD. This configuration enables users to handle individual VNFC resources, e.g. users can change images or networks of specified VNFC.
The following shows the sample userdata script for handling VNFCs without AutoScalingGroup.
# 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
def _get_new_cps_from_req(cps, req, grant):
# used by change_ext_conn and change_vnfpkg
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
return new_cps
def _merge_additional_params(nfv_dict, 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'])
return nfv_dict
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
popped_vdu = {}
vdu_idxes = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
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 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(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)
vdus = nfv_dict.get('VDU', {})
for vdu_name_idx, vdu_value in vdus.items():
vdu_name = rm_idx(vdu_name_idx)
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'] = zones[vdu_name_idx]
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)
nfv_dict = _merge_additional_params(nfv_dict, req, grant)
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
popped_vdu = {}
vdu_idxes = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
vdu_idxes[vdu_name] = common_script_utils.get_current_capacity(
vdu_name, inst)
zones = {}
for res in grant_req['addResources']:
if res['type'] != 'COMPUTE':
continue
vdu_name = res['resourceTemplateId']
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(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)
vdus = nfv_dict.get('VDU', {})
for vdu_name_idx, vdu_value in vdus.items():
vdu_name = rm_idx(vdu_name_idx)
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'] = zones[vdu_name_idx]
exclude_params = [param for param, value in vdu_value.items()
if value is None]
for exclude_param in exclude_params:
del vdu_value[exclude_param]
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
exclude_params = [param for param, value in cp_value.items()
if value is None]
for exclude_param in exclude_params:
del cp_value[exclude_param]
common_script_utils.apply_ext_managed_vls_from_inst(top_hot, inst)
nfv_dict = _merge_additional_params(nfv_dict, req, grant)
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):
template = {'resources': {}}
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
fields = {
'template': yaml.safe_dump(template),
}
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': {}}
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
fields = {
'template': yaml.safe_dump(template),
}
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
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
popped_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(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)
cps = nfv_dict.get('CP', {})
new_cps = _get_new_cps_from_req(cps, req, grant)
nfv_dict = _merge_additional_params({'CP': new_cps}, req, grant)
fields = {'parameters': {'nfv': nfv_dict}}
return fields
@staticmethod
def change_ext_conn_rollback(req, inst, grant_req, grant, tmp_csar_dir):
fields = {
'parameters': {
'nfv': inst['instantiatedVnfInfo']['metadata']['nfv']
}
}
return fields
@staticmethod
def heal(req, inst, grant_req, grant, tmp_csar_dir):
vnfd = common_script_utils.get_vnfd(inst['vnfdId'], tmp_csar_dir)
flavour_id = inst['instantiatedVnfInfo']['flavourId']
vdus = inst['instantiatedVnfInfo']['metadata']['nfv'].get('VDU', {})
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_name = inst_vnfc['vduId']
vdu_idx = inst_vnfc['metadata']['vdu_idx']
image = common_script_utils.get_param_image(
vdu_name, flavour_id, vnfd, grant, fallback_vnfd=False)
if image is not None:
vdus[add_idx(vdu_name, vdu_idx)]['vcImageId'] = image
break
nfv_dict = _merge_additional_params({'VDU': vdus}, req, grant)
fields = {'parameters': {'nfv': nfv_dict}}
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
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
target_vnfc_res_ids = [
res['resource']['resourceId']
for res in grant_req['removeResources']
if res['type'] == 'COMPUTE'
]
cur_hot_reses = {}
new_hot_reses = {}
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']
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
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
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})
new_vdus = new_nfv_dict.get('VDU', {})
vdus = inst['instantiatedVnfInfo']['metadata']['nfv'].get('VDU', {})
for vdu_name_idx, vdu_value in new_vdus.items():
vdu_name = rm_idx(vdu_name_idx)
if 'computeFlavourId' in vdu_value:
vdus[vdu_name_idx]['computeFlavourId'] = (
common_script_utils.get_param_flavor(
vdu_name, flavour_id, vnfd, grant))
if 'vcImageId' in vdu_value:
vdus[vdu_name_idx]['vcImageId'] = (
common_script_utils.get_param_image(
vdu_name, flavour_id, vnfd, grant))
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 = inst['instantiatedVnfInfo']['metadata']['nfv'].get('CP', {})
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)
nfv_dict = _merge_additional_params({'VDU': vdus, 'CP': cps},
req, grant)
fields = {
'template': yaml.safe_dump(top_hot),
'parameters': {'nfv': nfv_dict},
'files': {},
'existing': False
}
for key, value in hot_dict.get('files', {}).items():
fields['files'][key] = yaml.safe_dump(value)
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
popped_vdu = {}
for vdu_name in vnfd.get_vdu_nodes(flavour_id).keys():
popped_vdu[vdu_name] = top_hot.get('resources', {}).pop(vdu_name)
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']
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
common_script_utils.apply_ext_managed_vls(top_hot, req, grant)
fields = {
'template': yaml.safe_dump(top_hot),
'parameters': {
'nfv': inst['instantiatedVnfInfo']['metadata']['nfv']},
'files': {},
'existing': False
}
for key, value in hot_dict.get('files', {}).items():
fields['files'][key] = yaml.safe_dump(value)
return fields
Following is the specification of the sample UserData script.
UserData script calculates the number of VNFCs on the basis of the number of
VnfInstance.instantiatedVnfInfo.vnfcResourceInfo
,Grant.addResources
, andGrant.removeResources
similar to the method of calculating desired_capacity. get_param_capacity, which is one of the utility functions for UserData class can be used to get the number of resources.UserData script describes the same number of resources as VNFC to adjusted HOT.
UserData scripts create the resource id of VNFC (e.g. VDU1-0, VDU-1-1).
Properties of resources are copied from BaseHOT.
UserData script makes the input-parameter corresponding to Adjusted HOT.
Note
There is a difference in scale-in operation with and without AutoScalingGroup. VNFCs are deleted in order from the latest in scale-in operation. In the case of using AutoScalingGroup, the latest resource is determined on the basis of the creation_time by OpenStack Nova. Since creation_time is updated by heal operation, the order of VNFCs is changed dynamically. On the other hand, in the case of not using AutoScalingGroup, the latest resource is determined by the resource-id (e.g. VDU1-0, VDU1-1). Thus the order of the VNFc is not changed by heal operation when not using AutoScalingGroup.
This userdata script creates the adjusted HOT from BaseHOT in the VNF package and it is used as HEAT template.
The following shows a sample BaseHOT and adjusted HOT.
BaseHOT¶
top HOT
heat_template_version: 2013-05-23 description: Test Base HOT parameters: nfv: type: json resources: VDU1: type: VDU1.yaml properties: name: { get_param: [ nfv, VDU, VDU1, computeName ] } flavor: { get_param: [ nfv, VDU, VDU1, computeFlavourId ] } image: { get_param: [ nfv, VDU, VDU1, vcImageId ] } zone: { get_param: [ nfv, VDU, VDU1, locationConstraints] } net: { get_param: [ nfv, CP, VDU1_CP1, network] }
nested HOT (VDU1.yaml specified in above top HOT)
heat_template_version: 2013-05-23 description: 'VDU1 HOT for Sample VNF' parameters: name: type: string flavor: type: string image: type: string zone: type: string net: type: string resources: VDU1: type: OS::Nova::Server properties: name: { get_param: name } flavor: { get_param: flavor } image: { get_param: image } networks: - port: get_resource: VDU1_CP1 availability_zone: { get_param: zone } VDU1_CP1: type: OS::Neutron::Port properties: network: { get_param: net }
Input-parameter
"nfv": { "VDU": { "VDU1": { "computeName": "VDU1", "computeFlavourId": "m1.tiny", "vcImageId": "6b8a14f0-1b40-418a-b650-ae4a0378daa5", "locationConstraints": "zone-x" } }, "CP": { "VDU1_CP1": { "network": "67c837dc-c247-4a3e-ac0f-5603bfef1ba3" } } }
Adjusted HOT¶
top HOT
heat_template_version: 2013-05-23 description: Test Base HOT parameters: nfv: type: json resources: VDU1-0: type: VDU1.yaml properties: name: { get_param: [ nfv, VDU, VDU1-0, computeName ] } flavor: { get_param: [ nfv, VDU, VDU1-0, computeFlavourId ] } image: { get_param: [ nfv, VDU, VDU1-0, vcImageId ] } zone: { get_param: [ nfv, VDU, VDU1-0, locationConstraints ] } net: { get_param: [ nfv, CP, VDU1_CP1-0, network ] } VDU1-1: type: VDU1.yaml properties: name: { get_param: [ nfv, VDU, VDU1-1, computeName ] } flavor: { get_param: [ nfv, VDU,VDU1-1, computeFlavourId ] } image: { get_param: [ nfv, VDU,VDU1-1, vcImageId ] } zone: { get_param: [ nfv, VDU,VDU1-1, locationConstraints ] } net: { get_param: [ nfv, CP, VDU1_CP1-1,network ] }
nested HOT
Only the top HOT is changed to the adjusted HOT. Nested HOT is unchanged from BaseHOT.
Input-parameter
"nfv": { "VDU": { "VDU1-0": { "computeName": "VDU1-0", "computeFlavourId": "m1.tiny", "vcImageId": "6b8a14f0-1b40-418a-b650-ae4a0378daa5", "locationConstraints": "zone-x" }, "VDU1-1": { "computeName": "VDU1-1", "computeFlavourId": "m1.large", "vcImageId": "0ef0597c-4aab-4235-8513-bf5d8304fe64", "locationConstraints": "zone-y" } }, "CP": { "VDU1_CP1-0": { "network": "67c837dc-c247-4a3e-ac0f-5603bfef1ba3" }, "VDU1_CP1-1": { "network": "4d8aa289-21eb-4997-86f2-49a884f78d0b" } } }
Utility functions for userdata class¶
Tacker provides the following utility functions for the userdata script. Following functions can be called in userdata class.
def get_vnfd(vnfd_id, csar_dir)¶
Get vnfd in yaml format.
vnf_id: vnfid , csar_dir: the path of csar
It returns an instance of Vnfd class.
def init_nfv_dict(hot_template)¶
Find the parameter specified by get_param in the HOT template and get the dict of the nfv structure for the HEAT input parameter.
hot_template: HOT in yaml format.
It returns the dict of nfv structure.
def get_param_flavor(vdu_name, req, vnfd, grant)¶
Get flavor of VDU. If Grant contains the flavor, it is returned. Otherwise, flavor is obtained from vnfd and returned.
vdu_name: the name of VDU , req: operationParams corresponding to API request , vnfd: vnfd , grant: Grants
It returns vimFlavourId
def get_param_image(vdu_name, req, vnfd, grant)¶
Get software image of VDU. If Grant contains the glance-imageId corresponding to the VDU, it is returned. Otherwise, name of software image is obtained from vnfd and returned.
vdu_name: the name of VDU , req: operationParams corresponding to API request , vnfd: vnfd , grant: Grants
It returns image ID or image name.
def get_param_zone(vdu_name, grant_req, grant)¶
Get zone id of VDU.
vdu_name: the name of VDU , req: operationParams corresponding to API request , vnfd: vnfd , grant: Grants
It returns zone id.
def get_current_capacity(vdu_name, inst)¶
Get desired capacity.
vdu_name: the name of VDU , inst: VnfInstance
It returns desired capacity.
def get_param_capacity(vdu_name, inst, grant_req)¶
Refer to addResources and removeResources in the grant request and get desired capacity.
vdu_name: the name of VDU , inst: VnfInstance , grant_req: GrantRequest
It returns desired capacity.
def _get_fixed_ips_from_extcp(extcp)¶
Get list of fixed address and subnet from extcp. extcp is instantiateVnfRequest > extVirtualLinks > extcps defined in ETSI NFV SOL003.
It returns the list of fixed address and subnet.
def get_param_network(cp_name, grant, req)¶
Get network resourceId of CP.
cp_name: the name of CP , grant: Grants , req: operationParams corresponding to API request
It returns network resourceId.
def get_param_fixed_ips(cp_name, grant, req)¶
Get fixed IP addresses of CP.
cp_name: the name of CP , grant: Grants , req: operationParams corresponding to API request
It returns fixed IP address of CP.
def get_param_network_from_inst(cp_name, inst)¶
Get network resourceId from VnfInstance.
cp_name: the name of CP , inst: VnfInstance
It returns network resourceId from VnfInstance.
def get_param_fixed_ips_from_inst(cp_name, inst)¶
Get fixed IP address of CP from VnfInstance.
cp_name: the name of CP , inst: VnfInstance
It returns fixed IP address of CP from VnfInstance.
def apply_ext_managed_vls(hot_dict, req, grant)¶
Modify HOT to apply externally provided extmanaged internal virtual link (extmanagedVL).
ExtmanagedVL is created by VNFM when instantiating VNF or externally created and specified by Grants or InstantiateVnfRequest. Since one HOT can correspond to only one of the cases, this function modifies HOT for the former case to for the latter case.
The Following shows the sample HOT description.
Input HOT
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
networks:
- port:
get_resource: VDU1_CP1
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: { get_resource: internalVL1 }
internalVL1:
type: OS::Neutron::Net
outputs: {}
Output HOT
heat_template_version: 2013-05-23
description: 'Simple Base HOT for Sample VNF'
resources:
VDU1:
type: OS::Nova::Server
properties:
flavor: { get_param: [ nfv, VDU, VDU2, computeFlavourId ] }
image: { get_param: [ nfv, VDU, VDU2, vcImageId] }
networks:
- port:
get_resource: VDU1_CP1
VDU1_CP1:
type: OS::Neutron::Port
properties:
network: network_id
outputs: {}
vnfd.get_base_hot(flavour_id)¶
Get HOT dict.
flavour_id: flavour_id of vnf instance.
It returns HOT dict with the following structure. dict = {‘template’:tophot, ‘Files’{file 1:, file2:…}}
vnf_instance_utils.json_merge_patch(target, patch)¶
Get the result of json_merge_patch (IETF RFC 7396).
target: merge target , patch: applied patch
It returns the result of json_merge_patch (IETF RFC 7396).