# Copyright (C) 2020 NTT DATA
# 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 os
import yaml

from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import uuidutils
from toscaparser import tosca_template

from tacker.common import exceptions
from tacker.common import utils
from tacker.extensions import nfvo
from tacker import objects
from tacker.objects import fields
from tacker.tosca import utils as toscautils
from tacker.vnfm.lcm_user_data import utils as userdata_utils
from tacker.vnfm import vim_client

LOG = logging.getLogger(__name__)
CONF = cfg.CONF


# TODO(fengyi): change _get_vim to a public method
# and then delete this method.
def get_vim(context, vim_connection_info):
    return _get_vim(context, vim_connection_info)


def _get_vim(context, vim_connection_info):
    vim_client_obj = vim_client.VimClient()

    if vim_connection_info:
        vim_id = vim_connection_info[0].vim_id
        access_info = vim_connection_info[0].access_info
        if access_info:
            region_name = access_info.get('region')
        else:
            region_name = None
        extra = vim_connection_info[0].extra
    else:
        vim_id = None
        region_name = None
        extra = {}

    try:
        vim_res = vim_client_obj.get_vim(
            context, vim_id, region_name=region_name)
    except nfvo.VimNotFoundException:
        raise exceptions.VimConnectionNotFound(vim_id=vim_id)

    vim_res['vim_auth'].update({'region': region_name})
    if extra:
        for key, value in extra.items():
            vim_res['extra'][key] = value
    vim_info = {'id': vim_res['vim_id'], 'vim_id': vim_res['vim_id'],
                'vim_type': vim_res['vim_type'],
                'access_info': vim_res['vim_auth'],
                'tenant_id': vim_res['tenant'],
                'extra': vim_res.get('extra', {})}

    return vim_info


def _get_vnfd_dict(context, vnfd_id, flavour_id):
    vnfd_dict = _get_flavour_based_vnfd(
        _get_vnf_package_path(context, vnfd_id), flavour_id)

    # Remove requirements from substitution mapping
    vnfd_dict.get('topology_template').get(
        'substitution_mappings').pop('requirements')
    return vnfd_dict


def get_vnfd_dict(context, vnfd_id, flavour_id):
    return _get_vnfd_dict(context, vnfd_id, flavour_id)


def _get_vnflcm_interface(context, interface, vnf_instance, flavour_id):
    '''Gets the interface found in vnfd

        ...
            node_templates:
                VNF:
                    interfaces:
                        Vnflcm:
                            <interface>
    '''
    interface_value = None
    vnfd_dict = _get_vnfd_dict(context, vnf_instance.vnfd_id, flavour_id)

    if not isinstance(vnfd_dict, dict):
        raise exceptions.InvalidContentType(msg="VNFD not valid")

    if vnfd_dict.get('topology_template'):
        topology_template = vnfd_dict.get('topology_template')
        if topology_template.get('node_templates'):
            node_templates = topology_template.get('node_templates')
            if node_templates.get('VNF'):
                vnf = node_templates.get('VNF')
                if vnf.get('interfaces'):
                    interfaces = vnf.get('interfaces')
                    if interfaces.get('Vnflcm'):
                        vnflcm = interfaces.get('Vnflcm')
                        if vnflcm:
                            interface_value = vnflcm.get(interface)

    return interface_value


def _build_affected_resources(vnf_instance,
     change_type=fields.ResourceChangeType.ADDED):
    '''build affected resources from vnf_instance instantiated info '''

    affected_resources = {}
    instantiated_vnf_info = vnf_instance.instantiated_vnf_info
    if hasattr(instantiated_vnf_info, 'instance_id'):
        if instantiated_vnf_info.instance_id:

            affected_resources['affectedVnfcs'] = []
            affected_resources['affectedVirtualLinks'] = []
            affected_resources['affectedVirtualStorages'] = []

            # build AffectedVnfc
            vnfc_resource_info = \
                instantiated_vnf_info.vnfc_resource_info
            for vnfc_resource in vnfc_resource_info:
                data = {}
                data['id'] = vnfc_resource.id
                data['vduId'] = vnfc_resource.vdu_id
                data['changeType'] = change_type
                data['computeResource'] = \
                    vnfc_resource.compute_resource.to_dict()
                data['metadata'] = vnfc_resource.metadata
                affected_resources['affectedVnfcs'].append(data)

            # build AffectedVirtualLink
            vnf_virtual_link = \
                instantiated_vnf_info.vnf_virtual_link_resource_info
            for vnf_vl_info in vnf_virtual_link:
                data = {}
                data['id'] = vnf_vl_info.id
                data['vnfVirtualLinkDescId'] = \
                    vnf_vl_info.vnf_virtual_link_desc_id
                data['changeType'] = change_type
                data['networkResource'] = \
                    vnf_vl_info.network_resource.to_dict()
                data['metadata'] = {}
                affected_resources['affectedVirtualLinks'].append(data)

            # build affectedVirtualStorages
            virtual_storage = \
                instantiated_vnf_info.virtual_storage_resource_info
            for vnf_storage_info in virtual_storage:
                data = {}
                data['id'] = vnf_storage_info.id
                data['virtualStorageDescId'] = \
                    vnf_storage_info.virtual_storage_desc_id
                data['changeType'] = change_type
                data['storageResource'] = \
                    vnf_storage_info.storage_resource.to_dict()
                data['metadata'] = {}
                affected_resources['affectedVirtualStorages'].append(data)

    return utils.convert_snakecase_to_camelcase(affected_resources)


def _get_affected_resources(old_vnf_instance=None,
                new_vnf_instance=None, extra_list=None):
    '''get_affected_resources

    returns affected resources in new_vnf_instance not present
    in old_vnf_instance.
    if extra_list (list of physical resource ids) is present,
        included to affected resources
    '''
    def _get_affected_cpids(affected_vnfc, vnf_instance):
        affected_cpids = []
        instantiated_vnf_info = vnf_instance.instantiated_vnf_info
        for vnfc_resource in instantiated_vnf_info.vnfc_resource_info:
            if vnfc_resource.id == affected_vnfc['id']:
                for vnfc_cp in vnfc_resource.vnfc_cp_info:
                    if vnfc_cp.cpd_id:
                        affected_cpids.append(vnfc_cp.cpd_id)
                    if vnfc_cp.vnf_ext_cp_id:
                        affected_cpids.append(vnfc_cp.vnf_ext_cp_id)
        return affected_cpids

    def _get_added_storageids(affected_vnfc, vnf_instance):
        affected_storage_ids = []
        instantiated_vnf_info = vnf_instance.instantiated_vnf_info
        for vnfc_resource in instantiated_vnf_info.vnfc_resource_info:
            if vnfc_resource.id == affected_vnfc['id']:
                for storage_resource_id in vnfc_resource.storage_resource_ids:
                    virtual_storage = \
                        instantiated_vnf_info.virtual_storage_resource_info
                    for virt_storage_res_info in virtual_storage:
                        if virt_storage_res_info.id == storage_resource_id:
                            affected_storage_ids.append(
                                virt_storage_res_info.virtual_storage_desc_id)
        return affected_storage_ids

    def diff_list(old_list, new_list):
        diff = []
        for item in new_list:
            if item not in old_list:
                diff.append(item)
        return diff

    affected_resources = {}
    affected_resources['affectedVnfcs'] = []
    affected_resources['affectedVirtualLinks'] = []
    affected_resources['affectedVirtualStorages'] = []

    if not old_vnf_instance:
        affected_resources = _build_affected_resources(
            new_vnf_instance, fields.ResourceChangeType.ADDED)
        # add affected cpids and add added storageids
        for affected_vnfc in affected_resources['affectedVnfcs']:
            affected_vnfc['affectedVnfcCpIds'] = _get_affected_cpids(
                affected_vnfc, new_vnf_instance)
            affected_vnfc['addedStorageResourceIds'] = _get_added_storageids(
                affected_vnfc, new_vnf_instance)

    elif not new_vnf_instance:
        affected_resources = _build_affected_resources(old_vnf_instance,
                                            fields.ResourceChangeType.REMOVED)
        # add affected cpids and add remove storageids
        for affected_vnfc in affected_resources['affectedVnfcs']:
            affected_vnfc['affectedVnfcCpIds'] = _get_affected_cpids(
                affected_vnfc, old_vnf_instance)
            affected_vnfc['removedStorageResourceIds'] = _get_added_storageids(
                affected_vnfc, old_vnf_instance)
    elif old_vnf_instance and new_vnf_instance:
        old_affected_resources = _build_affected_resources(old_vnf_instance)
        new_affected_resources = _build_affected_resources(new_vnf_instance,
                                            fields.ResourceChangeType.MODIFIED)

        # get resource_ids
        old_vnfc_resource_ids = []
        for vnfc_resource in old_affected_resources.get('affectedVnfcs', []):
            old_vnfc_resource_ids.append(
                vnfc_resource['computeResource']['resourceId'])

        # remove extra_list items in old_vnfc_resource_ids
        # so that this items will be considered new
        if extra_list:
            for item in extra_list:
                if item in old_vnfc_resource_ids:
                    index = old_vnfc_resource_ids.index(item)
                    old_vnfc_resource_ids.pop(index)

        new_vnfc_resource_ids = []
        for vnfc_resource in new_affected_resources.get('affectedVnfcs', []):
            resource_id = vnfc_resource['computeResource']['resourceId']
            new_vnfc_resource_ids.append(resource_id)

        old_vnf_vl_resource_ids = []
        for vnf_vl_info in old_affected_resources.get(
                'affectedVirtualLinks', []):
            resource_id = vnf_vl_info['networkResource']['resourceId']
            old_vnf_vl_resource_ids.append(resource_id)

        new_vnf_vl_resource_ids = []
        for vnf_vl_info in new_affected_resources.get(
                'affectedVirtualLinks', []):
            resource_id = vnf_vl_info['networkResource']['resourceId']
            new_vnf_vl_resource_ids.append(resource_id)

        old_vnf_storage_resource_ids = []
        for vnf_storage_info in old_affected_resources.get(
                'affectedVirtualStorages', []):
            resource_id = vnf_storage_info['storageResource']['resourceId']
            old_vnf_storage_resource_ids.append(resource_id)

        new_vnf_storage_resource_ids = []
        for vnf_storage_info in new_affected_resources.get(
                'affectedVirtualStorages', []):
            resource_id = vnf_storage_info['storageResource']['resourceId']
            new_vnf_storage_resource_ids.append(resource_id)

        # get difference between resource_ids
        vnfc_resource_ids = diff_list(old_vnfc_resource_ids,
                                      new_vnfc_resource_ids)
        vnf_vl_resource_ids = diff_list(old_vnf_vl_resource_ids,
                                        new_vnf_vl_resource_ids)
        vnf_storage_resource_ids = diff_list(old_vnf_storage_resource_ids,
                                             new_vnf_storage_resource_ids)

        # return new affected resources
        for affected_vls in new_affected_resources.get(
                'affectedVirtualLinks', []):
            if (affected_vls['networkResource']
                    ['resourceId'] in vnf_vl_resource_ids):
                affected_resources['affectedVirtualLinks'].append(affected_vls)

        for affected_storage in new_affected_resources.get(
                'affectedVirtualStorages', []):
            if (affected_storage['storageResource']
                    ['resourceId'] in vnf_storage_resource_ids):
                affected_resources['affectedVirtualStorages'].append(
                    affected_storage)

        for affected_vnfc in new_affected_resources.get('affectedVnfcs', []):
            if (affected_vnfc['computeResource']
                    ['resourceId'] in vnfc_resource_ids):

                # update affected affectedVnfcCpIds
                affected_vnfc['affectedVnfcCpIds'] = _get_affected_cpids(
                    affected_vnfc, new_vnf_instance)

                affected_resources['affectedVnfcs'].append(affected_vnfc)

    return affected_resources


def _get_vnf_package_id(context, vnfd_id):
    vnf_package = objects.VnfPackageVnfd.get_by_id(context, vnfd_id)
    return vnf_package.package_uuid


def _create_grant_request(vnfd_dict, package_uuid):
    node_templates = vnfd_dict.get('topology_template',
                                   {}).get('node_templates', {})
    vnf_software_images = {}
    if not node_templates:
        return vnf_software_images

    def _build_vnf_software_image(sw_image_data, artifact_image_path):
        vnf_sw_image = objects.VnfSoftwareImage()
        vnf_sw_image.image_path = artifact_image_path
        vnf_sw_image.name = sw_image_data.get('name')
        vnf_sw_image.version = sw_image_data.get('version')
        if sw_image_data.get('checksum'):
            checksum = sw_image_data.get('checksum')
            if checksum.get('algorithm'):
                vnf_sw_image.algorithm = checksum.get('algorithm')
            if checksum.get('hash'):
                vnf_sw_image.hash = checksum.get('hash')

        vnf_sw_image.container_format = sw_image_data.get(
            'container_format')
        vnf_sw_image.disk_format = sw_image_data.get('disk_format')
        if sw_image_data.get('min_disk'):
            min_disk = utils.MemoryUnit.convert_unit_size_to_num(
                sw_image_data.get('min_disk'), 'GB')
            vnf_sw_image.min_disk = min_disk
        else:
            vnf_sw_image.min_disk = 0

        if sw_image_data.get('min_ram'):
            min_ram = utils.MemoryUnit.convert_unit_size_to_num(
                sw_image_data.get('min_ram'), 'MB')
            vnf_sw_image.min_ram = min_ram
        else:
            vnf_sw_image.min_ram = 0

        return vnf_sw_image

    def _get_image_path(artifact_image_path, package_uuid):
        vnf_package_path = CONF.vnf_package.vnf_package_csar_path
        artifact_image_path = os.path.join(
            vnf_package_path, package_uuid,
            artifact_image_path.split('../')[-1])
        return artifact_image_path

    for node, value in node_templates.items():
        if not value.get(
                'type') in ['tosca.nodes.nfv.Vdu.Compute',
                            'tosca.nodes.nfv.Vdu.VirtualBlockStorage']:
            continue

        sw_image_data = value.get('properties', {}).get('sw_image_data')
        artifacts = value.get('artifacts', {})
        for artifact, sw_image in artifacts.items():
            artifact_image_path = None
            if isinstance(sw_image, str):
                artifact_image_path = sw_image
            elif sw_image.get('type') == 'tosca.artifacts.nfv.SwImage':
                artifact_image_path = sw_image.get('file', {})
            if sw_image_data and artifact_image_path:
                is_url = utils.is_url(artifact_image_path)
                if not is_url:
                    artifact_image_path = _get_image_path(artifact_image_path,
                                                          package_uuid)

                vnf_software_image = _build_vnf_software_image(
                    sw_image_data, artifact_image_path)
                vnf_software_images[node] = vnf_software_image
            break

    return vnf_software_images


def _make_final_vnf_dict(vnfd_dict, id, name, param_values, vnf_dict=None):
    if vnf_dict:
        final_vnf_dict = vnf_dict
        final_vnf_dict['vnfd']['attributes'].\
            update({'vnfd': str(vnfd_dict)})
        final_vnf_dict['attributes'].\
            update({'param_values': str(param_values)})
        final_vnf_dict['attributes'].\
            update({'stack_name': name or ("vnflcm_" + id)})
        return final_vnf_dict
    else:
        return {'vnfd': {
            'attributes': {
                'vnfd': str(vnfd_dict)}},
            'id': id,
            'name': name,
            'attributes': {
                'param_values': str(param_values),
                'stack_name': name or ("vnflcm_" + id)}}


def _get_flavour_based_vnfd(csar_path, flavour_id):
    ext = (".yaml", ".yml")
    file_path_and_data = {}
    imp_list = []
    for item in os.listdir(csar_path):
        src_path = os.path.join(csar_path, item)
        if os.path.isdir(src_path):
            for file in os.listdir(src_path):
                if file.endswith(ext):
                    source_file_path = os.path.join(src_path, file)
                    with open(source_file_path) as file_obj:
                        data = yaml.safe_load(file_obj)
                    substitution_map = data.get(
                        'topology_template',
                        {}).get('substitution_mappings', {})
                    if substitution_map.get(
                            'properties', {}).get('flavour_id') == flavour_id:
                        if data.get('imports'):
                            for imp in data.get('imports'):
                                imp_path = os.path.join(src_path, imp)
                                imp_list.append(imp_path)
                            data.update({'imports': imp_list})

                        return data

        elif src_path.endswith(ext):
            with open(src_path) as file_obj:
                file_data = yaml.safe_load(file_obj)
            substitution_map = file_data.get(
                'topology_template', {}).get('substitution_mappings', {})
            if substitution_map.get(
                    'properties', {}).get('flavour_id') == flavour_id:
                if file_data.get('imports'):
                    for imp in file_data.get('imports'):
                        imp_list.append(os.path.join(src_path, imp))
                    file_data.update({'imports': imp_list})
                return file_data

    return file_path_and_data


def _get_param_data(vnfd_dict, instantiate_vnf_req):
    param_value = {}
    additional_param = instantiate_vnf_req.additional_params
    if additional_param is None:
        additional_param = {}
    substitution_map = vnfd_dict.get('topology_template',
                                     {}).get('substitution_mappings', {})
    input_attributes = vnfd_dict.get('topology_template', {}).get('inputs')
    if substitution_map is not None:
        subs_map_node_type = substitution_map.get('node_type')
        # Get properties in lower-level VNFD for top-level VNFD
        node_templates = vnfd_dict.get('topology_template',
                                    {}).get('node_templates', {})
        for node in node_templates.values():
            if node.get('type') == subs_map_node_type:
                node_property = node.get('properties', {})
                if node_property:
                    param_value.update(node_property)
        # Import `_type.yaml` file and get default properties.
        # If new value provided in additional_param, the property is updated.
        import_paths = vnfd_dict.get('imports', {})
        for imp_path in import_paths:
            with open(imp_path) as file_obj:
                import_data = yaml.safe_load(file_obj)
            imp_node_type = import_data.get('node_types')
            if imp_node_type:
                for key, value in imp_node_type.items():
                    if key == subs_map_node_type:
                        properties = value.get('properties')
                        if properties:
                            for key, prop in properties.items():
                                if additional_param.get(key):
                                    param_value.update({
                                        key: additional_param.get(key)})
                                # If the parameter is provided in lower-level
                                # VNFD, use it. Otherwise use the default.
                                elif not param_value.get(key):
                                    param_value.update(
                                        {key: prop.get('default')})

    for input_attr, value in input_attributes.items():
        if additional_param.get(input_attr):
            param_value.update({input_attr: additional_param.get(
                input_attr)})

    return param_value


def _get_vim_connection_info_from_vnf_req(vnf_instance, instantiate_vnf_req):
    vim_connection_obj_list = []

    if not instantiate_vnf_req.vim_connection_info:
        # add default vim
        if len(vnf_instance.vim_connection_info):
            vim_connection_obj_list.append(vnf_instance.vim_connection_info[0])

        return vim_connection_obj_list

    for vim_connection in instantiate_vnf_req.vim_connection_info:
        vim_conn = objects.VimConnectionInfo(id=vim_connection.id,
            vim_id=vim_connection.vim_id, vim_type=vim_connection.vim_type,
            access_info=vim_connection.access_info,
            interface_info=vim_connection.interface_info,
            extra=vim_connection.extra)

        vim_connection_obj_list.append(vim_conn)

    # add default vim
    if len(vnf_instance.vim_connection_info):
        if vim_conn.id and vnf_instance.vim_connection_info[0].id:
            is_default_vim_exist = [vim_conn for vim_conn
                in vim_connection_obj_list
                    if vim_conn.id == vnf_instance.vim_connection_info[0].id]
            if not len(is_default_vim_exist):
                vim_connection_obj_list.append(vnf_instance.
                    vim_connection_info[0])

    return vim_connection_obj_list


def _build_instantiated_vnf_info(vnfd_dict, instantiate_vnf_req,
                                 vnf_instance, vim_id, scale_status=None):
    inst_vnf_info = vnf_instance.instantiated_vnf_info
    inst_vnf_info.vnf_state = fields.VnfOperationalStateType.STARTED

    node_templates = vnfd_dict.get(
        'topology_template', {}).get('node_templates')

    vnfc_resource_info, virtual_storage_resource_info = (
        _get_vnfc_resource_info(
            vnfd_dict, instantiate_vnf_req, vim_id, scale_status=scale_status))

    inst_vnf_info.vnfc_resource_info = vnfc_resource_info

    tmp_insta_vnf_info = copy.deepcopy(inst_vnf_info)
    inst_vnf_info.ext_cp_info = _set_ext_cp_info(instantiate_vnf_req,
        inst_vnf_info=tmp_insta_vnf_info)
    inst_vnf_info.ext_virtual_link_info = _set_ext_virtual_link_info(
        instantiate_vnf_req, inst_vnf_info.ext_cp_info)

    inst_vnf_info.virtual_storage_resource_info = \
        virtual_storage_resource_info
    inst_vnf_info.vnf_virtual_link_resource_info = \
        _build_vnf_virtual_link_resource_info(
            node_templates, instantiate_vnf_req,
            inst_vnf_info.vnfc_resource_info, vim_id)

    inst_vnf_info.ext_managed_virtual_link_info = \
        _build_ext_managed_virtual_link_info(instantiate_vnf_req,
            inst_vnf_info)

    inst_vnf_info.additional_params = instantiate_vnf_req.additional_params

    vnf_instance.instantiated_vnf_info = inst_vnf_info


def _update_instantiated_vnf_info(change_ext_conn_req, vnf_instance):
    inst_vnf_info = vnf_instance.instantiated_vnf_info
    tmp_insta_vnf_info = copy.deepcopy(inst_vnf_info)

    inst_vnf_info.ext_cp_info = _update_ext_cp_info(change_ext_conn_req,
        inst_vnf_info=tmp_insta_vnf_info)
    inst_vnf_info.ext_virtual_link_info = _update_ext_virtual_link_info(
        change_ext_conn_req, inst_vnf_info=tmp_insta_vnf_info)

    inst_vnf_info.vnf_virtual_link_resource_info = \
        _update_vnf_virtual_link_resource_info(change_ext_conn_req,
            inst_vnf_info)

    vnf_instance.instantiated_vnf_info = inst_vnf_info


def _get_compute_nodes(vnfd_dict, instantiate_vnf_req, scale_status=None):
    """Read the node templates and prepare VDU data in below format

    {
        'VDU1': {
            'CP': [CP1, CP2],
            'VIRTUAL_STORAGE': [virtual_storage1]
        },
    }
    """

    node_templates = vnfd_dict.get(
        'topology_template', {}).get('node_templates')

    vdu_resources = {}
    for key, value in node_templates.items():
        if value.get('type') != 'tosca.nodes.nfv.Vdu.Compute':
            continue

        desired_capacity = _convert_desired_capacity(
            instantiate_vnf_req.instantiation_level_id, vnfd_dict, key,
            scale_status=scale_status)

        cp_list = _get_cp_for_vdu(key, node_templates)

        virtual_storages = []
        requirements = value.get('requirements', [])
        for requirement in requirements:
            if requirement.get('virtual_storage'):
                virtual_storages.append(
                    requirement.get('virtual_storage'))

        vdu_resources[key] = {"CP": cp_list,
                              "VIRTUAL_STORAGE": virtual_storages,
                              "COUNT": desired_capacity}

    return vdu_resources


def _get_virtual_link_nodes(node_templates):
    virtual_link_nodes = {}

    for key, value in node_templates.items():
        if value.get('type') == 'tosca.nodes.nfv.VnfVirtualLink':
            cp_list = _get_cp_for_vl(key, node_templates)
            virtual_link_nodes[key] = cp_list

    return virtual_link_nodes


def _get_cp_for_vdu(vdu, node_templates):
    cp_list = []
    for key, value in node_templates.items():
        if value.get('type') != 'tosca.nodes.nfv.VduCp':
            continue

        requirements = value.get('requirements', [])
        for requirement in requirements:
            if requirement.get('virtual_binding') and vdu == \
                    requirement.get('virtual_binding'):
                cp_list.append(key)

    return cp_list


def _get_cp_for_vl(vl, node_templates):
    cp_list = []
    for key, value in node_templates.items():
        if value.get('type') != 'tosca.nodes.nfv.VduCp':
            continue

        requirements = value.get('requirements', [])
        for requirement in requirements:
            if requirement.get('virtual_link') and vl == \
                    requirement.get('virtual_link'):
                cp_list.append(key)

    return cp_list


def _build_vnf_virtual_link_resource_info(node_templates, instantiate_vnf_req,
                                          vnfc_resource_info, vim_id):
    virtual_link_nodes_with_cp = _get_virtual_link_nodes(node_templates)

    # Read the external networks and extcps from InstantiateVnfRequest
    for ext_virt_link in instantiate_vnf_req.ext_virtual_links:
        virtual_link_nodes_with_cp[ext_virt_link.id] = [extcp.cpd_id for extcp
                in ext_virt_link.ext_cps]

    virtual_link_resource_info_list = []

    def _get_network_resource(vl_node):
        resource_handle = objects.ResourceHandle()
        found = False
        for ext_mg_vl in instantiate_vnf_req.ext_managed_virtual_links:
            if ext_mg_vl.vnf_virtual_link_desc_id == vl_node:
                resource_handle.resource_id = ext_mg_vl.resource_id
                resource_handle.vim_connection_id = \
                    ext_mg_vl.vim_connection_id
                # TODO(tpatil): This cannot be set here.
                resource_handle.vim_level_resource_type = \
                    'OS::Neutron::Net'
                found = True
                break

        if not found:
            # check if it exists in the ext_virtual_links
            for ext_virt_link in instantiate_vnf_req.ext_virtual_links:
                if ext_virt_link.id == vl_node:
                    resource_handle.resource_id = ext_virt_link.resource_id
                    resource_handle.vim_connection_id = \
                        ext_virt_link.vim_connection_id
                    # TODO(tpatil): This cannot be set here.
                    resource_handle.vim_level_resource_type = \
                        'OS::Neutron::Net'
                    found = True
                    break

        return resource_handle

    def _get_vnf_link_port_info(cp):
        vnf_link_port_info = objects.VnfLinkPortInfo()
        vnf_link_port_info.id = uuidutils.generate_uuid()

        resource_handle = objects.ResourceHandle()
        for ext_virt_link in instantiate_vnf_req.ext_virtual_links:
            for extcp in ext_virt_link.ext_cps:
                if extcp.cpd_id == cp:
                    for cpconfig in extcp.cp_config:
                        if cpconfig.link_port_id:
                            resource_handle.resource_id = \
                                cpconfig.link_port_id
                            resource_handle.vim_connection_id = \
                                ext_virt_link.vim_connection_id
                            # TODO(tpatil): This shouldn't be set here.
                            resource_handle.vim_level_resource_type = \
                                'OS::Neutron::Port'
                            break

        vnf_link_port_info.resource_handle = resource_handle

        return vnf_link_port_info

    for node, cp_list in virtual_link_nodes_with_cp.items():
        vnf_vl_resource_info = objects.VnfVirtualLinkResourceInfo()
        vnf_vl_resource_info.id = uuidutils.generate_uuid()
        vnf_vl_resource_info.vnf_virtual_link_desc_id = node
        vnf_vl_resource_info.network_resource = _get_network_resource(node)

        vnf_link_port_info_list = []
        for cp in cp_list:
            for vnfc_resource in vnfc_resource_info:
                for vnfc_cp in vnfc_resource.vnfc_cp_info:
                    if vnfc_cp.cpd_id == cp:
                        vnf_link_port_info = _get_vnf_link_port_info(cp)
                        vnf_link_port_info.cp_instance_id = vnfc_cp.id
                        # Identifier of the "vnfLinkPorts" structure in the
                        # "vnfVirtualLinkResourceInfo" structure.
                        vnfc_cp.vnf_link_port_id = vnf_link_port_info.id
                        vnf_link_port_info_list.append(vnf_link_port_info)

        vnf_vl_resource_info.vnf_link_ports = vnf_link_port_info_list

        virtual_link_resource_info_list.append(vnf_vl_resource_info)

    return virtual_link_resource_info_list


def _update_vnf_virtual_link_resource_info(change_ext_conn_req,
                                           inst_vnf_info):
    def _update(change_ext_conn_req, vnf_vl_resource_info):
        for ext_virtual_link in change_ext_conn_req.ext_virtual_links:
            if (ext_virtual_link.id ==
                    vnf_vl_resource_info.vnf_virtual_link_desc_id):
                res_handle = objects.ResourceHandle()
                res_handle.resource_id = ext_virtual_link.resource_id
                nw_res = vnf_vl_resource_info.network_resource
                res_handle.vim_connection_id = nw_res.vim_connection_id
                res_handle.vim_level_resource_type = \
                    nw_res.vim_level_resource_type
                new_vnf_vl_resource_info = \
                    objects.VnfVirtualLinkResourceInfo(
                        id=vnf_vl_resource_info.id,
                        vnf_virtual_link_desc_id=vnf_vl_resource_info.
                        vnf_virtual_link_desc_id,
                        network_resource=res_handle,
                        vnf_link_ports=vnf_vl_resource_info.vnf_link_ports)
                return new_vnf_vl_resource_info
        return None

    vnf_virtual_link_resource_list = []
    for vnf_vl_res_info in inst_vnf_info.vnf_virtual_link_resource_info:
        updated_vnf_vl_res_info = \
            _update(change_ext_conn_req, vnf_vl_res_info)
        if updated_vnf_vl_res_info:
            vnf_virtual_link_resource_list.append(updated_vnf_vl_res_info)
        else:
            vnf_virtual_link_resource_list.append(vnf_vl_res_info)

    return vnf_virtual_link_resource_list


def _build_vnf_cp_info(instantiate_vnf_req, cp_list):
    vnfc_cp_info_list = []

    if not cp_list:
        return vnfc_cp_info_list

    def _set_vnf_exp_cp_id_protocol_data(vnfc_cp_info):
        for ext_virt_link in instantiate_vnf_req.ext_virtual_links:
            for extcp in ext_virt_link.ext_cps:
                if extcp.cpd_id == cp:
                    vnfc_cp_info.cp_protocol_info = \
                        _set_cp_protocol_info(extcp)
                    for cpconfig in extcp.cp_config:
                        vnfc_cp_info.vnf_ext_cp_id = cpconfig.link_port_id
                        break

    for cp in cp_list:
        vnfc_cp_info = objects.VnfcCpInfo()
        vnfc_cp_info.id = uuidutils.generate_uuid()
        vnfc_cp_info.cpd_id = cp
        _set_vnf_exp_cp_id_protocol_data(vnfc_cp_info)
        vnfc_cp_info_list.append(vnfc_cp_info)

    return vnfc_cp_info_list


def _build_virtual_storage_info(virtual_storages):

    for storage_node in virtual_storages:
        virtual_storage = objects.VirtualStorageResourceInfo()
        virtual_storage.id = uuidutils.generate_uuid()
        virtual_storage.virtual_storage_desc_id = storage_node

        virtual_storage.storage_resource = objects.ResourceHandle()

        yield virtual_storage


def _get_vnfc_resource_info(vnfd_dict, instantiate_vnf_req, vim_id,
                            scale_status=None):
    vdu_resources = _get_compute_nodes(
        vnfd_dict, instantiate_vnf_req, scale_status=scale_status)
    vnfc_resource_info_list = []
    virtual_storage_resource_info_list = []

    def _build_vnfc_resource_info(vdu, vdu_resource):
        vnfc_resource_info = objects.VnfcResourceInfo()
        vnfc_resource_info.id = uuidutils.generate_uuid()
        vnfc_resource_info.vdu_id = vdu

        vnfc_resource_info.compute_resource = objects.ResourceHandle()

        vnfc_cp_info_list = _build_vnf_cp_info(instantiate_vnf_req,
                                               vdu_resource.get("CP"))
        vnfc_resource_info.vnfc_cp_info = vnfc_cp_info_list

        virtual_storages = vdu_resource.get("VIRTUAL_STORAGE")
        vdu_storages = []
        for storage in _build_virtual_storage_info(virtual_storages):
            vdu_storages.append(storage)
            virtual_storage_resource_info_list.append(storage)

        storage_resource_ids = [info.id for info in vdu_storages]
        vnfc_resource_info.storage_resource_ids = storage_resource_ids
        return vnfc_resource_info

    for vdu, vdu_resource in vdu_resources.items():
        count = vdu_resource.get('COUNT', 1)
        for num_instance in range(count):
            vnfc_resource_info = _build_vnfc_resource_info(vdu, vdu_resource)
            vnfc_resource_info_list.append(vnfc_resource_info)

    return vnfc_resource_info_list, virtual_storage_resource_info_list


def _set_ext_cp_info(instantiate_vnf_req, inst_vnf_info=None):
    ext_cp_info_list = []
    vnfc_info = []

    if inst_vnf_info.vnfc_resource_info:
        vnfc_info = inst_vnf_info.vnfc_resource_info

    if not instantiate_vnf_req.ext_virtual_links:
        return ext_cp_info_list

    for ext_virt_link in instantiate_vnf_req.ext_virtual_links:
        if not ext_virt_link.ext_cps:
            continue

        for ext_cp in ext_virt_link.ext_cps:
            ext_cp_info = objects.VnfExtCpInfo(
                id=uuidutils.generate_uuid(),
                cpd_id=ext_cp.cpd_id,
                cp_protocol_info=_set_cp_protocol_info(ext_cp),
                ext_link_port_id=_get_ext_link_port_id(ext_virt_link,
                        ext_cp.cpd_id),
                associated_vnfc_cp_id=_get_associated_vnfc_cp_id(vnfc_info,
                        ext_cp.cpd_id))

            ext_cp_info_list.append(ext_cp_info)

    return ext_cp_info_list


def _update_ext_cp_info(change_ext_conn_req, inst_vnf_info):

    def _update(change_ext_conn_req, ext_cp_info):
        for ext_virt_link in change_ext_conn_req.ext_virtual_links:
            if not ext_virt_link.ext_cps:
                continue
            for ext_cp in ext_virt_link.ext_cps:
                if ext_cp.cpd_id == ext_cp_info.cpd_id:
                    new_ext_cp_info = objects.VnfExtCpInfo(
                        id=ext_cp_info.id,
                        cpd_id=ext_cp.cpd_id,
                        cp_protocol_info=_set_cp_protocol_info(ext_cp),
                        associated_vnfc_cp_id=ext_cp_info.
                        associated_vnfc_cp_id)
                    return new_ext_cp_info
        return None

    ext_cp_info_list = []
    for ext_cp_info in inst_vnf_info.ext_cp_info:
        updated_ext_cp_info = _update(change_ext_conn_req, ext_cp_info)
        if updated_ext_cp_info:
            ext_cp_info_list.append(updated_ext_cp_info)
        else:
            ext_cp_info_list.append(ext_cp_info)

    return ext_cp_info_list


def _get_ext_link_port_id(ext_virtual_link, cpd_id):
    if not ext_virtual_link.ext_link_ports:
        return

    for ext_link in ext_virtual_link.ext_link_ports:
        if ext_link.id == cpd_id:
            return ext_link.id


def _get_associated_vnfc_cp_id(vnfc_info, cpd_id):
    if not isinstance(vnfc_info, list):
        return

    for vnfc in vnfc_info:
        if vnfc.vnfc_cp_info:
            for cp_info in vnfc.vnfc_cp_info:
                if cp_info.cpd_id == cpd_id:
                    return vnfc.id


def _build_ip_over_ethernet_address_info(cp_protocol_data):
    """Convert IpOverEthernetAddressData to IpOverEthernetAddressInfo"""

    if not cp_protocol_data.ip_over_ethernet:
        return

    ip_over_ethernet_add_info = objects.IpOverEthernetAddressInfo()
    ip_over_ethernet_add_info.mac_address = \
        cp_protocol_data.ip_over_ethernet.mac_address

    if not cp_protocol_data.ip_over_ethernet.ip_addresses:
        return ip_over_ethernet_add_info

    ip_address_list = []
    for ip_address in cp_protocol_data.ip_over_ethernet.ip_addresses:
        ip_address_info = objects.vnf_instantiated_info.IpAddress(
            type=ip_address.type,
            addresses=ip_address.fixed_addresses,
            is_dynamic=True if ip_address.num_dynamic_addresses else False,
            subnet_id=ip_address.subnet_id)

        ip_address_list.append(ip_address_info)

    ip_over_ethernet_add_info.ip_addresses = ip_address_list

    return ip_over_ethernet_add_info


def _build_cp_protocol_info(cp_protocol_data):
    ip_over_ethernet_add_info = _build_ip_over_ethernet_address_info(
        cp_protocol_data)
    cp_protocol_info = objects.CpProtocolInfo(
        layer_protocol=cp_protocol_data.layer_protocol,
        ip_over_ethernet=ip_over_ethernet_add_info)

    return cp_protocol_info


def _set_cp_protocol_info(ext_cp):
    """Convert CpProtocolData to CpProtocolInfo"""

    cp_protocol_info_list = []
    if not ext_cp.cp_config:
        return cp_protocol_info_list

    for cp_config in ext_cp.cp_config:
        for cp_protocol_data in cp_config.cp_protocol_data:
            cp_protocol_info = _build_cp_protocol_info(cp_protocol_data)
            cp_protocol_info_list.append(cp_protocol_info)

    return cp_protocol_info_list


def _set_ext_virtual_link_info(instantiate_vnf_req, ext_cp_info):
    ext_virtual_link_list = []

    if not instantiate_vnf_req.ext_virtual_links:
        return ext_virtual_link_list

    for ext_virtual_link in instantiate_vnf_req.ext_virtual_links:
        res_handle = objects.ResourceHandle()
        res_handle.resource_id = ext_virtual_link.resource_id
        res_handle.vim_connection_id = ext_virtual_link.vim_connection_id

        ext_virtual_link_info = objects.ExtVirtualLinkInfo(
            id=ext_virtual_link.id,
            resource_handle=res_handle,
            ext_link_ports=_set_ext_link_port(ext_virtual_link,
                ext_cp_info))

        ext_virtual_link_list.append(ext_virtual_link_info)

    return ext_virtual_link_list


def _update_ext_virtual_link_info(change_ext_conn_req, inst_vnf_info):

    def _update(change_ext_conn_req, ext_virtual_link_info):
        for ext_virtual_link in change_ext_conn_req.ext_virtual_links:
            if ext_virtual_link.id == ext_virtual_link_info.id:
                res_handle = objects.ResourceHandle()
                res_handle.resource_id = ext_virtual_link.resource_id
                new_ext_virtual_link_info = objects.ExtVirtualLinkInfo(
                    id=ext_virtual_link_info.id,
                    resource_handle=res_handle,
                    ext_link_ports=ext_virtual_link_info.ext_link_ports)
                res_handle.vim_connection_id = \
                    ext_virtual_link_info.resource_handle.vim_connection_id
                new_ext_virtual_link_info = objects.ExtVirtualLinkInfo(
                    id=ext_virtual_link_info.id,
                    resource_handle=res_handle,
                    ext_link_ports=ext_virtual_link_info.ext_link_ports)
                return new_ext_virtual_link_info
        return None

    ext_virtual_link_list = []
    for ext_virtual_link_info in inst_vnf_info.ext_virtual_link_info:
        updated_ext_virtual_link_info = \
            _update(change_ext_conn_req, ext_virtual_link_info)
        if updated_ext_virtual_link_info:
            ext_virtual_link_list.append(updated_ext_virtual_link_info)
        else:
            ext_virtual_link_list.append(ext_virtual_link_info)

    return ext_virtual_link_list


def _set_ext_link_port(ext_virtual_links, ext_cp_info):
    ext_link_port_list = []

    if not ext_virtual_links.ext_link_ports:
        return ext_link_port_list

    for ext_link_port in ext_virtual_links.ext_link_ports:
        resource_handle = ext_link_port.resource_handle.obj_clone()
        cp_instance_id = None
        if ext_virtual_links.ext_cps:
            for ext_cp in ext_cp_info:
                cp_instance_id = ext_cp.id

        ext_link_port_info = objects.ExtLinkPortInfo(id=ext_link_port.id,
            resource_handle=resource_handle, cp_instance_id=cp_instance_id)

        ext_link_port_list.append(ext_link_port_info)

    return ext_link_port_list


def _build_ext_managed_virtual_link_info(instantiate_vnf_req, inst_vnf_info):

    def _network_resource(ext_managed_vl):
        resource_handle = objects.ResourceHandle(
            resource_id=ext_managed_vl.resource_id)
        # TODO(tpatil): Remove hard coding of resource type as
        # OS::Neutron::Net resource type is specific to OpenStack infra
        # driver. It could be different for other infra drivers like
        # Kubernetes.
        resource_handle.vim_level_resource_type = 'OS::Neutron::Net'
        resource_handle.vim_connection_id = \
            ext_managed_vl.vim_connection_id

        return resource_handle

    ext_managed_virtual_link_info = []
    ext_managed_virt_link_from_req = \
        instantiate_vnf_req.ext_managed_virtual_links
    for ext_managed_vl in ext_managed_virt_link_from_req:
        ext_managed_virt_info = objects.ExtManagedVirtualLinkInfo()
        ext_managed_virt_info.id = ext_managed_vl.id
        ext_managed_virt_info.vnf_virtual_link_desc_id =\
            ext_managed_vl.vnf_virtual_link_desc_id

        ext_managed_virt_info.network_resource =\
            _network_resource(ext_managed_vl)

        # Populate the vnf_link_ports from vnf_virtual_link_resource_info
        # of instantiated_vnf_info.
        for vnf_vl_res_info in inst_vnf_info.vnf_virtual_link_resource_info:
            if ext_managed_vl.vnf_virtual_link_desc_id ==\
                    vnf_vl_res_info.vnf_virtual_link_desc_id:
                vnf_link_ports = []
                for vnf_lp in vnf_vl_res_info.vnf_link_ports:
                    vnf_link_ports.append(vnf_lp.obj_clone())
                ext_managed_virt_info.vnf_link_ports = vnf_link_ports

        ext_managed_virtual_link_info.append(ext_managed_virt_info)
    return ext_managed_virtual_link_info


def _convert_desired_capacity(
        inst_level_id, vnfd_dict, vdu, scale_status=None):
    aspect_delta_dict = {}
    aspect_vdu_dict = {}
    inst_level_dict = {}
    aspect_id_dict = {}
    vdu_delta_dict = {}
    aspect_max_level_dict = {}
    desired_capacity = 1

    tosca = tosca_template.ToscaTemplate(
        parsed_params={}, a_file=False, yaml_dict_tpl=vnfd_dict,
        local_defs=toscautils.tosca_tmpl_local_defs())
    tosca_policies = tosca.topology_template.policies
    default_inst_level_id = toscautils._extract_policy_info(
        tosca_policies, inst_level_dict,
        aspect_delta_dict, aspect_id_dict,
        aspect_vdu_dict, vdu_delta_dict,
        aspect_max_level_dict)

    if vdu_delta_dict.get(vdu) is None:
        return desired_capacity

    if inst_level_id:
        instantiation_level = inst_level_id
    elif default_inst_level_id:
        instantiation_level = default_inst_level_id
    else:
        return desired_capacity

    al_dict = inst_level_dict.get(instantiation_level)

    if not al_dict:
        return desired_capacity

    for aspect_id, level_num in al_dict.items():
        delta_id = aspect_id_dict.get(aspect_id)

        if delta_id is not None:
            delta_num = \
                aspect_delta_dict.get(aspect_id).get(delta_id)

        vdus = aspect_vdu_dict.get(aspect_id)
        initial_delta = None
        if vdu in vdus:
            initial_delta = vdu_delta_dict.get(vdu)

        if initial_delta is not None:
            scale_level = level_num
            if scale_status is not None:
                for scale in scale_status:
                    if scale['aspect_id'] == aspect_id:
                        scale_level = scale['scale_level']
            desired_capacity = initial_delta + delta_num * scale_level

    return desired_capacity


# TODO(fengyi): change _get_vnf_package_path to a public method
# and then delete this method.
def get_vnf_package_path(context, vnfd_id):
    return _get_vnf_package_path(context, vnfd_id)


def _get_vnf_package_path(context, vnfd_id):
    return os.path.join(CONF.vnf_package.vnf_package_csar_path,
                        _get_vnf_package_id(context, vnfd_id))


def get_base_nest_hot_dict(context, flavour_id, vnfd_id):
    base_hot_path = os.path.join(_get_vnf_package_path(context, vnfd_id),
                                 'BaseHOT', flavour_id)
    nested_hot_path = os.path.join(base_hot_path, 'nested')
    ext = (".yaml", ".yml")

    base_hot_dict = None
    nested_hot_dict = {}
    if os.path.exists(base_hot_path):
        for file in os.listdir(base_hot_path):
            if file.endswith(ext):
                with open(os.path.join(base_hot_path, file)) as file_obj:
                    base_hot_dict = yaml.safe_load(file_obj)
    if os.path.exists(nested_hot_path):
        for file in os.listdir(nested_hot_path):
            if file.endswith(ext):
                with open(os.path.join(nested_hot_path, file)) as file_obj:
                    nested_hot = yaml.safe_load(file_obj)
                    nested_hot_dict[file] = nested_hot
    LOG.debug("Loaded base hot: %s", base_hot_dict)
    LOG.debug("Loaded nested_hot_dict: %s", nested_hot_dict)
    return base_hot_dict, nested_hot_dict


def get_extract_policy_infos(tosca):
    aspect_delta_dict = {}
    aspect_vdu_dict = {}
    inst_level_dict = {}
    aspect_id_dict = {}
    vdu_delta_dict = {}
    aspect_max_level_dict = {}

    tosca_policies = tosca.topology_template.policies
    default_inst_level_id = toscautils._extract_policy_info(
        tosca_policies, inst_level_dict,
        aspect_delta_dict, aspect_id_dict,
        aspect_vdu_dict, vdu_delta_dict,
        aspect_max_level_dict)

    extract_policy_infos = dict()
    extract_policy_infos['inst_level_dict'] = inst_level_dict
    extract_policy_infos['aspect_delta_dict'] = aspect_delta_dict
    extract_policy_infos['aspect_id_dict'] = aspect_id_dict
    extract_policy_infos['aspect_vdu_dict'] = aspect_vdu_dict
    extract_policy_infos['vdu_delta_dict'] = vdu_delta_dict
    extract_policy_infos['aspect_max_level_dict'] = aspect_max_level_dict
    extract_policy_infos['default_inst_level_id'] = default_inst_level_id

    return extract_policy_infos


def get_scale_delta_num(extract_policy_infos, aspect_id):
    delta_num = 1

    if extract_policy_infos['aspect_id_dict'] is None:
        return delta_num
    delta_id = extract_policy_infos['aspect_id_dict'].get(aspect_id)
    if delta_id is None:
        return delta_num
    delta_num = \
        extract_policy_infos['aspect_delta_dict'].get(aspect_id).get(delta_id)

    return delta_num


def get_default_scale_status(context, vnf_instance, vnfd_dict):
    default_scale_status = None

    vnfd_dict = _get_vnfd_dict(context,
        vnf_instance.vnfd_id,
        vnf_instance.instantiated_vnf_info.flavour_id)
    tosca = tosca_template.ToscaTemplate(
        parsed_params={}, a_file=False, yaml_dict_tpl=vnfd_dict,
        local_defs=toscautils.tosca_tmpl_local_defs())
    extract_policy_infos = get_extract_policy_infos(tosca)

    if extract_policy_infos['inst_level_dict'] is None:
        return default_scale_status
    default_inst_level_id = extract_policy_infos['default_inst_level_id']
    default_al_dict = \
        extract_policy_infos['inst_level_dict'].get(default_inst_level_id)
    if default_al_dict is None:
        return default_scale_status
    default_scale_status = []
    for aspect_id, level_num in default_al_dict.items():
        default_scale_status.append(
            objects.ScaleInfo(
                aspect_id=aspect_id,
                scale_level=level_num))

    return default_scale_status


def get_target_vdu_def_dict(extract_policy_infos, aspect_id, tosca):
    vdu_def_dict = {}

    tosca_node_tpls = tosca.topology_template.nodetemplates

    if extract_policy_infos['aspect_vdu_dict'] is None:
        return vdu_def_dict
    vdus = extract_policy_infos['aspect_vdu_dict'].get(aspect_id)
    if vdus is None:
        return vdu_def_dict
    for nt in tosca_node_tpls:
        for node_name, node_value in nt.templates.items():
            if node_name in vdus:
                vdu_def_dict[node_name] = node_value

    return vdu_def_dict


def get_current_num_of_instances(extract_policy_infos,
                                 aspect_id, scale_level):
    delta_id = extract_policy_infos['aspect_id_dict'].get(aspect_id)
    if delta_id is None:
        raise exceptions.InvalidPoliciesDefinition(
            param='step_deltas id in ScalingAspects')

    # Get delta_num.
    delta_num = extract_policy_infos['aspect_delta_dict'].get(
        aspect_id, {}).get(delta_id)
    if delta_num is None:
        raise exceptions.InvalidPoliciesDefinition(
            param='delta num in VduScalingAspectDeltas')

    # Get initial_delta.
    vdus = extract_policy_infos['aspect_vdu_dict'].get(aspect_id)
    initial_delta = None
    if vdus:
        # NOTE: The current tacker implementation allows one target vdu
        #       per aspect_id, so get the first vdu_id here.
        initial_delta = extract_policy_infos['vdu_delta_dict'].get(vdus[0])

    if initial_delta is None:
        raise exceptions.InvalidPoliciesDefinition(
            param='initial_delta num in VduInitialDelta')
    # Calculate current number_of_instances.
    return initial_delta + delta_num * scale_level


def _get_changed_ext_connectivity(
        old_vnf_instance=None, new_vnf_instance=None):

    changed_ext_connectivities = []
    if not old_vnf_instance or not new_vnf_instance:
        return changed_ext_connectivities

    old_vnf_vl_res_info = \
        old_vnf_instance.instantiated_vnf_info.\
        vnf_virtual_link_resource_info
    new_vnf_vl_res_info = \
        new_vnf_instance.instantiated_vnf_info.\
        vnf_virtual_link_resource_info

    def _compare_vnf_link_ports(old_vnf_link_ports,
                                new_vnf_link_ports):
        differed_vnf_link_ports = []
        for old_vnf_link_port in old_vnf_link_ports:
            for new_vnf_link_port in new_vnf_link_ports:
                if old_vnf_link_port.id == new_vnf_link_port.id:
                    if (old_vnf_link_port.resource_handle.resource_id !=
                            new_vnf_link_port.resource_handle.resource_id):
                        differed_vnf_link_ports.append(new_vnf_link_port)
        return differed_vnf_link_ports

    for old_vl_res in old_vnf_vl_res_info:
        for new_vl_res in new_vnf_vl_res_info:
            if old_vl_res.id == new_vl_res.id:
                changed_ext_connectivity = objects.ExtVirtualLinkInfo(
                    id=new_vl_res.id,
                    resource_handle=new_vl_res.network_resource,
                    ext_link_ports=[])
                differed_vnf_link_ports = _compare_vnf_link_ports(
                    old_vl_res.vnf_link_ports,
                    new_vl_res.vnf_link_ports)
                for link_port in differed_vnf_link_ports:
                    changed_ext_link_port = objects.ExtLinkPortInfo(
                        id=link_port.id,
                        resource_handle=link_port.resource_handle,
                        cp_instance_id=link_port.cp_instance_id)
                    changed_ext_connectivity.ext_link_ports.\
                        append(changed_ext_link_port)
                if changed_ext_connectivity.ext_link_ports:
                    changed_ext_connectivities.append(
                        changed_ext_connectivity)

    LOG.debug('changed_ext_connectivities: {}'.format(
        changed_ext_connectivities))
    return changed_ext_connectivities


def get_stack_param(context, vnf_dict, heal_vnf_request, inst_vnf_info):
    stack_param = {}
    vnfc_resources = []

    # get vnfc resources
    if not heal_vnf_request.vnfc_instance_id:
        # include all vnfc resources
        vnfc_resources = [
            resource for resource in inst_vnf_info.vnfc_resource_info]
    else:
        for vnfc_resource in inst_vnf_info.vnfc_resource_info:
            if vnfc_resource.id in heal_vnf_request.vnfc_instance_id:
                vnfc_resources.append(vnfc_resource)

    def _update_stack_params(
            context, base_hot_dict, nested_hot_dict, vnfd_dict):
        param_base_hot_dict = copy.deepcopy(nested_hot_dict)
        param_base_hot_dict['heat_template'] = base_hot_dict

        initial_param_dict = (
            userdata_utils.create_initial_param_server_port_dict(
                param_base_hot_dict)
        )
        del initial_param_dict['nfv']['CP']

        vdu_flavor_dict = (
            userdata_utils.create_vdu_flavor_capability_name_dict(vnfd_dict)
        )
        vdu_image_dict = userdata_utils.create_sw_image_dict(vnfd_dict)

        final_param_dict = userdata_utils.create_final_param_dict(
            initial_param_dict, vdu_flavor_dict, vdu_image_dict, {})

        return final_param_dict['nfv']['VDU']

    # get HOT dict
    base_hot_dict, nested_hot_dict = get_base_nest_hot_dict(
        context, inst_vnf_info.flavour_id, vnf_dict['vnfd_id'])

    vnfd_dict = yaml.safe_load(
        vnf_dict['vnfd']['attributes']['vnfd_' + inst_vnf_info.flavour_id])

    if 'stack_param' in vnf_dict['attributes'].keys():
        stack_param = yaml.safe_load(
            vnf_dict['attributes']['stack_param'])

        updated_vdu_params = _update_stack_params(
            context, base_hot_dict, nested_hot_dict, vnfd_dict)

        for vnfc_resource in vnfc_resources:
            vdu_id = vnfc_resource.vdu_id
            if (updated_vdu_params.get(vdu_id) and
                    stack_param['nfv']['VDU'].get(vdu_id)):
                stack_param['nfv']['VDU'].update({
                    vdu_id: updated_vdu_params.get(vdu_id)
                })

    return stack_param
