diff --git a/api-ref/source/v1/parameters_vnflcm.yaml b/api-ref/source/v1/parameters_vnflcm.yaml index 0d04708e9..020c23f5b 100644 --- a/api-ref/source/v1/parameters_vnflcm.yaml +++ b/api-ref/source/v1/parameters_vnflcm.yaml @@ -1,5 +1,4 @@ # variables in header - vnf_instance_id: description: | Identifier of the VNF instance. @@ -49,6 +48,16 @@ ext_cp_info: in: body required: true type: array +ext_cp_info_associated_vnfc_cp_id: + description: | + Identifier of the "vnfcCpInfo" structure in + "VnfcResourceInfo" structure that represents the + VNFC CP which is exposed by this external CP + instance. Shall be present in case this CP instance + maps to a VNFC CP. + in: body + required: false + type: string ext_cp_info_cp_protocol_info: description: | Network protocol information for this CP. @@ -77,6 +86,12 @@ ext_cp_info_id: in: body required: true type: string +ext_cp_info_metadata: + description: | + Metadata about this external CP. + in: body + required: false + type: string ext_cps: description: | External CPs of the VNF to be connected to this external VL. @@ -361,6 +376,30 @@ resource_handle_vim_level_resource_type: in: body required: false type: string +scale_status: + description: | + Scale status of the VNF, one entry per aspect. + Represents for every scaling aspect how "big" + the VNF has been scaled with reference to that aspect. + This attribute shall be present if the VNF + supports scaling. + in: body + required: false + type: array +scale_status_aspect_id: + description: | + Identifier of the scaling aspect. + in: body + required: true + type: string +scale_status_scale_level: + description: | + Indicates the scale level. The minimum value shall be 0 + and the maximum value shall be less than or equal to maxScaleLevel as + described in the VNFD. + in: body + required: true + type: string subnet_id: description: | Subnet defined by the identifier of the subnet resource in the VIM. @@ -386,6 +425,14 @@ termination_type: in: body required: true type: string +vim_connection_id: + description: | + Identifier of the VIM connection to manage the resource. + This attribute shall only be supported and present if VNF related + resource management in direct mode is applicable. + in: body + required: false + type: string vim_connection_info_access_info: description: | Authentication credentials for accessing the VIM, and other access-related @@ -409,6 +456,24 @@ vim_connection_info_id: in: body required: true type: string +vim_connection_info_interface_info: + description: | + Information about the interface or interfaces to the VIM, if + applicable, such as the URI of an interface endpoint to + communicate with the VIM. The applicable keys are + dependent on the content of vimType. + + Alternatively, such information may have been configured + into the VNFM and bound to the vimId. + in: body + required: false + type: string +vim_connection_info_interface_info_endpoint: + description: | + The url representing the interface endpoint. + in: body + required: true + type: string vim_connection_info_vim_id: description: | The identifier of the VIM instance. This identifier is managed by @@ -702,6 +767,35 @@ vnfc_cp_info_vnf_link_port_id: in: body required: false type: string +vnfc_info: + description: | + Information about the VNFC instances. + in: body + required: array + type: string +vnfc_info_id: + description: | + Identifier of the VNFC instance. + in: body + required: true + type: string +vnfc_info_vdu_id: + description: | + Reference to the applicable VDU information element in the VNFD. + in: body + required: true + type: string +vnfc_info_vnfc_state: + description: | + State of the VNFC instance. + Permitted values: + + STARTED: The VNFC instance is up and running. + + STOPPED: The VNFC instance has been shut down. + in: body + required: true + type: string vnfc_resource_info: description: | Information about the virtualised compute and storage resources used by diff --git a/api-ref/source/v1/vnflcm.inc b/api-ref/source/v1/vnflcm.inc index 01e176722..418d7196e 100644 --- a/api-ref/source/v1/vnflcm.inc +++ b/api-ref/source/v1/vnflcm.inc @@ -316,11 +316,16 @@ Response Parameters - id: vim_connection_info_id - vimId: vim_connection_info_vim_id - vimType: vim_connection_info_vim_type + - interfaceInfo: vim_connection_info_interface_info + - endpoint: vim_connection_info_interface_info_endpoint - accessInfo: vim_connection_info_access_info - instantiationState: vnf_instance_instantiation_state - instantiatedVnfInfo: instantiated_vnf_info - flavourId: flavour_id_response - vnfState: vnf_state + - scaleStatus: scale_status + - aspectId: scale_status_aspect_id + - scaleLevel: scale_status_scale_level - extCpInfo: ext_cp_info - id: ext_cp_info_id - cpdId: ext_cp_info_cpd_id @@ -334,14 +339,18 @@ Response Parameters - isDynamic: is_dynamic - subnetId: subnet_id - extLinkPortId: ext_cp_info_ext_link_port_id + - metadata: ext_cp_info_metadata + - associatedVnfcCpId: ext_cp_info_associated_vnfc_cp_id - extVirtualLinkInfo: ext_virtual_link_info - id: ext_virtual_link_info_id - resourceHandle: resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - extLinkPorts: ext_virtual_link_info_ext_link_ports - id: ext_virtual_link_info_ext_link_ports_id - resourceHandle: resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - cpInstanceId: ext_virtual_link_info_ext_link_ports_cp_instance_id @@ -349,11 +358,13 @@ Response Parameters - id: ext_managed_virtual_link_info_id - vnfVirtualLinkDescId: ext_managed_virtual_link_info_vnf_virtual_link_desc_id - networkResource: ext_managed_virtual_link_info_network_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - vnfLinkPorts: vnf_link_ports - id: vnf_link_port_id - resourceHandle: vnf_link_port_resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - cpInstanceId: vnf_link_port_cp_instance_id @@ -361,6 +372,7 @@ Response Parameters - id: vnfc_resource_info_id - vduId: vnfc_resource_info_vdu_id - computeResource: vnfc_resource_info_compute_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - storageResourceIds: vnfc_resource_info_storage_resource_ids @@ -382,11 +394,13 @@ Response Parameters - id: vnf_virtual_link_resource_info_id - vnfVirtualLinkDescId: vnf_virtual_link_resource_info_vnf_virtual_link_desc_id - networkResource: vnf_virtual_link_resource_info_network_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - vnfLinkPorts: vnf_link_ports - id: vnf_link_port_id - resourceHandle: vnf_link_port_resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - cpInstanceId: vnf_link_port_cp_instance_id @@ -394,8 +408,13 @@ Response Parameters - id: virtual_storage_resource_info_id - virtualStorageDescId: virtual_storage_resource_info_virtual_storage_desc_id - storageResource: virtual_storage_resource_info_storage_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type + - vnfcInfo: vnfc_info + - id: vnfc_info_id + - vduId: vnfc_info_vdu_id + - vnfcState: vnfc_info_vnfc_state - _links: vnf_instance_links Response Example @@ -444,11 +463,16 @@ Response Parameters - id: vim_connection_info_id - vimId: vim_connection_info_vim_id - vimType: vim_connection_info_vim_type + - interfaceInfo: vim_connection_info_interface_info + - endpoint: vim_connection_info_interface_info_endpoint - accessInfo: vim_connection_info_access_info - instantiationState: vnf_instance_instantiation_state - instantiatedVnfInfo: instantiated_vnf_info - flavourId: flavour_id_response - vnfState: vnf_state + - scaleStatus: scale_status + - aspectId: scale_status_aspect_id + - scaleLevel: scale_status_scale_level - extCpInfo: ext_cp_info - id: ext_cp_info_id - cpdId: ext_cp_info_cpd_id @@ -462,14 +486,18 @@ Response Parameters - isDynamic: is_dynamic - subnetId: subnet_id - extLinkPortId: ext_cp_info_ext_link_port_id + - metadata: ext_cp_info_metadata + - associatedVnfcCpId: ext_cp_info_associated_vnfc_cp_id - extVirtualLinkInfo: ext_virtual_link_info - id: ext_virtual_link_info_id - resourceHandle: resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - extLinkPorts: ext_virtual_link_info_ext_link_ports - id: ext_virtual_link_info_ext_link_ports_id - resourceHandle: resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - cpInstanceId: ext_virtual_link_info_ext_link_ports_cp_instance_id @@ -477,11 +505,13 @@ Response Parameters - id: ext_managed_virtual_link_info_id - vnfVirtualLinkDescId: ext_managed_virtual_link_info_vnf_virtual_link_desc_id - networkResource: ext_managed_virtual_link_info_network_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - vnfLinkPorts: vnf_link_ports - id: vnf_link_port_id - resourceHandle: vnf_link_port_resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - cpInstanceId: vnf_link_port_cp_instance_id @@ -489,6 +519,7 @@ Response Parameters - id: vnfc_resource_info_id - vduId: vnfc_resource_info_vdu_id - computeResource: vnfc_resource_info_compute_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - storageResourceIds: vnfc_resource_info_storage_resource_ids @@ -510,11 +541,13 @@ Response Parameters - id: vnf_virtual_link_resource_info_id - vnfVirtualLinkDescId: vnf_virtual_link_resource_info_vnf_virtual_link_desc_id - networkResource: vnf_virtual_link_resource_info_network_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - vnfLinkPorts: vnf_link_ports - id: vnf_link_port_id - resourceHandle: vnf_link_port_resource_handle + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type - cpInstanceId: vnf_link_port_cp_instance_id @@ -522,10 +555,15 @@ Response Parameters - id: virtual_storage_resource_info_id - virtualStorageDescId: virtual_storage_resource_info_virtual_storage_desc_id - storageResource: virtual_storage_resource_info_storage_resource + - vimConnectionId: vim_connection_id - resourceId: resource_handle_resource_id - vimLevelResourceType: resource_handle_vim_level_resource_type + - vnfcInfo: vnfc_info + - id: vnfc_info_id + - vduId: vnfc_info_vdu_id + - vnfcState: vnfc_info_vnfc_state - _links: vnf_instance_links - + Response Example ---------------- diff --git a/tacker/api/views/vnf_lcm.py b/tacker/api/views/vnf_lcm.py index dea88d510..27de678ed 100644 --- a/tacker/api/views/vnf_lcm.py +++ b/tacker/api/views/vnf_lcm.py @@ -58,9 +58,35 @@ class ViewBuilder(base.BaseViewBuilder): return {"_links": links} - def _get_vnf_instance_info(self, - vnf_instance, api_version=None): + def _get_vim_conn_info(self, vim_connection_info): + vim_connections = [] + + for vim in vim_connection_info: + access_info = { + 'username': '', + 'region': '', + 'password': '', + 'tenant': '' + } + vim_conn = vim + + for key_name in access_info.keys(): + if vim['access_info'].get(key_name): + access_info[key_name] = vim['access_info'].get(key_name) + + vim_conn['access_info'] = access_info + + vim_connections.append(vim_conn) + + return vim_connections + + def _get_vnf_instance_info(self, vnf_instance): vnf_instance_dict = vnf_instance.to_dict() + if vnf_instance_dict.get('vim_connection_info'): + vnf_instance_dict['vim_connection_info'] = \ + self._get_vim_conn_info(vnf_instance_dict.get( + 'vim_connection_info', [])) + if 'vnf_metadata' in vnf_instance_dict: metadata_val = vnf_instance_dict.pop('vnf_metadata') vnf_instance_dict['metadata'] = metadata_val @@ -68,9 +94,6 @@ class ViewBuilder(base.BaseViewBuilder): vnf_instance_dict = utils.convert_snakecase_to_camelcase( vnf_instance_dict) - if api_version == "2.6.1": - del vnf_instance_dict["vnfPkgId"] - links = self._get_links(vnf_instance) vnf_instance_dict.update(links) @@ -82,6 +105,6 @@ class ViewBuilder(base.BaseViewBuilder): def show(self, vnf_instance): return self._get_vnf_instance_info(vnf_instance) - def index(self, vnf_instances, api_version=None): - return [self._get_vnf_instance_info(vnf_instance, api_version) + def index(self, vnf_instances): + return [self._get_vnf_instance_info(vnf_instance) for vnf_instance in vnf_instances] diff --git a/tacker/api/vnflcm/v1/controller.py b/tacker/api/vnflcm/v1/controller.py index 3ff96d28a..b705a9367 100644 --- a/tacker/api/vnflcm/v1/controller.py +++ b/tacker/api/vnflcm/v1/controller.py @@ -174,6 +174,22 @@ class VnfLcmController(wsgi.Controller): except exceptions.VnfPackageVnfdNotFound as exc: raise webob.exc.HTTPBadRequest(explanation=six.text_type(exc)) + # get default vim information + vim_client_obj = vim_client.VimClient() + default_vim = vim_client_obj.get_vim(context) + + # set vim_connection_info + access_info = { + 'username': default_vim.get('vim_auth', {}).get('username'), + 'password': default_vim.get('vim_auth', {}).get('password'), + 'region': default_vim.get('placement_attr', {}).get('region'), + 'tenant': default_vim.get('tenant') + } + vim_con_info = objects.VimConnectionInfo(id=default_vim.get('vim_id'), + vim_id=default_vim.get('vim_id'), + vim_type=default_vim.get('vim_type'), + access_info=access_info) + vnf_instance = objects.VnfInstance( context=request.context, vnf_instance_name=req_body.get('vnf_instance_name'), @@ -190,6 +206,11 @@ class VnfLcmController(wsgi.Controller): vnf_metadata=req_body.get('metadata')) vnf_instance.create() + + # add default vim to vim_connection_info + setattr(vnf_instance, 'vim_connection_info', [vim_con_info]) + vnf_instance.save() + result = self._view_builder.create(vnf_instance) headers = {"location": self._get_vnf_instance_href(vnf_instance)} return wsgi.ResponseObject(result, headers=headers) @@ -215,8 +236,7 @@ class VnfLcmController(wsgi.Controller): vnf_instances = objects.VnfInstanceList.get_by_filters( request.context, filters=filters) - api_version = request.headers['Version'] - return self._view_builder.index(vnf_instances, api_version) + return self._view_builder.index(vnf_instances) @check_vnf_state(action="delete", instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED], diff --git a/tacker/db/db_sqlalchemy/models.py b/tacker/db/db_sqlalchemy/models.py index 331380734..d2db8a0e5 100644 --- a/tacker/db/db_sqlalchemy/models.py +++ b/tacker/db/db_sqlalchemy/models.py @@ -221,6 +221,7 @@ class VnfInstantiatedInfo(model_base.BASE, models.SoftDeleteMixin, vnfc_resource_info = sa.Column(sa.JSON(), nullable=True) vnf_virtual_link_resource_info = sa.Column(sa.JSON(), nullable=True) virtual_storage_resource_info = sa.Column(sa.JSON(), nullable=True) + vnfc_info = sa.Column(sa.JSON(), nullable=True) vnf_state = sa.Column(sa.String(255), nullable=False) instance_id = sa.Column(sa.Text(), nullable=True) instantiation_level_id = sa.Column(sa.String(255), nullable=True) diff --git a/tacker/db/migration/alembic_migrations/versions/8a7ca803e0d0_add_vnfc_info_to_instantiated_vnf_info.py b/tacker/db/migration/alembic_migrations/versions/8a7ca803e0d0_add_vnfc_info_to_instantiated_vnf_info.py new file mode 100644 index 000000000..3891220ca --- /dev/null +++ b/tacker/db/migration/alembic_migrations/versions/8a7ca803e0d0_add_vnfc_info_to_instantiated_vnf_info.py @@ -0,0 +1,39 @@ +# Copyright 2020 OpenStack Foundation +# +# 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. +# + +# flake8: noqa: E402 + +"""add_vnfc_info_to_instantiated_vnf_info + +Revision ID: 8a7ca803e0d0 +Revises: aaf461c8844c +Create Date: 2020-09-21 15:00:00.004343 + +""" + +# revision identifiers, used by Alembic. +revision = '8a7ca803e0d0' +down_revision = 'aaf461c8844c' + +from alembic import op +import sqlalchemy as sa + + +from tacker.db import migration + + +def upgrade(active_plugins=None, options=None): + op.add_column('vnf_instantiated_info', + sa.Column('vnfc_info', sa.JSON(), nullable=True)) diff --git a/tacker/db/migration/alembic_migrations/versions/HEAD b/tacker/db/migration/alembic_migrations/versions/HEAD index c1abb3260..aae979f6a 100644 --- a/tacker/db/migration/alembic_migrations/versions/HEAD +++ b/tacker/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -aaf461c8844c +8a7ca803e0d0 diff --git a/tacker/objects/fields.py b/tacker/objects/fields.py index a780d2595..cb223b7b5 100644 --- a/tacker/objects/fields.py +++ b/tacker/objects/fields.py @@ -177,3 +177,10 @@ class VnfInstanceTerminationType(BaseTackerEnum): class VnfInstanceTerminationTypeField(BaseEnumField): AUTO_TYPE = VnfInstanceTerminationType() + + +class VnfcState(BaseTackerEnum): + STARTED = 'STARTED' + STOPPED = 'STOPPED' + + ALL = (STARTED, STOPPED) diff --git a/tacker/objects/vnf_instantiated_info.py b/tacker/objects/vnf_instantiated_info.py index 38bfd53e7..70cd3a18f 100644 --- a/tacker/objects/vnf_instantiated_info.py +++ b/tacker/objects/vnf_instantiated_info.py @@ -72,6 +72,8 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, 'VnfVirtualLinkResourceInfo', nullable=True, default=[]), 'virtual_storage_resource_info': fields.ListOfObjectsField( 'VirtualStorageResourceInfo', nullable=True, default=[]), + 'vnfc_info': fields.ListOfObjectsField( + 'VnfcInfo', nullable=True, default=[]), 'vnf_state': fields.VnfOperationalStateTypeField(nullable=False, default=fields.VnfOperationalStateType.STOPPED), 'instance_id': fields.StringField(nullable=True, default=None), @@ -116,6 +118,10 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, 'additional_params', 'key_value_pair', {"key_column": "key", "value_column": "value", "model": "VnfInstantiatedInfo"}), + 'vnfcInfo/*': ( + 'vnfc_info', 'key_value_pair', + {"key_column": "key", "value_column": "value", + "model": "VnfInstantiatedInfo"}), } } @@ -128,7 +134,8 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, 'ext_managed_virtual_link_info', 'vnfc_resource_info', 'vnf_virtual_link_resource_info', - 'virtual_storage_resource_info'] + 'virtual_storage_resource_info', + 'vnfc_info'] for key in inst_vnf_info.fields: if key in special_fields: continue @@ -169,6 +176,13 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, vnf_vl_resource_info] inst_vnf_info.vnf_virtual_link_resource_info = vnf_vl_info_list + vnfc_info = db_inst_vnf_info[ + 'vnfc_info'] + vnfc_info_list = [VnfcInfo. + obj_from_primitive(vnfc, context) for vnfc in + vnfc_info] + inst_vnf_info.vnfc_info = vnfc_info_list + inst_vnf_info._context = context inst_vnf_info.obj_reset_changes() return inst_vnf_info @@ -241,6 +255,12 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, 'virtual_storage_resource_info', [])] primitive.update({'virtual_storage_resource_info': obj_data}) + if 'vnfc_info' in primitive.keys(): + obj_data = [VnfcInfo.obj_from_primitive( + vnfc_info, context) for vnfc_info in primitive.get( + 'vnfc_info', [])] + primitive.update({'vnfc_info': obj_data}) + instantiate_vnf_info = \ InstantiatedVnfInfo._from_dict(primitive) @@ -265,6 +285,7 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, vnf_state = data_dict.get('vnf_state') instantiation_level_id = data_dict.get('instantiation_level_id') additional_params = data_dict.get('additional_params', {}) + vnfc_info = data_dict.get('vnfc_info', []) obj = cls(flavour_id=flavour_id, ext_cp_info=ext_cp_info, ext_virtual_link_info=ext_virtual_link_info, @@ -272,6 +293,7 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, vnfc_resource_info=vnfc_resource_info, vnf_virtual_link_resource_info=vnf_virtual_link_resource_info, virtual_storage_resource_info=virtual_storage_resource_info, + vnfc_info=vnfc_info, vnf_state=vnf_state, instantiation_level_id=instantiation_level_id, additional_params=additional_params) @@ -328,6 +350,14 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, data.update({'virtual_storage_resource_info': virtual_storage_resource_info_list}) + if self.vnfc_info: + vnfc_info = [] + for vnfc in self.vnfc_info: + info = vnfc.to_dict() + vnfc_info.append(info) + + data.update({'vnfc_info': vnfc_info}) + data.update({'additional_params': self.additional_params}) @@ -343,6 +373,7 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat, self.virtual_storage_resource_info = [] self.instance_id = None self.vnf_state = fields.VnfOperationalStateType.STOPPED + self.vnfc_info = [] @base.TackerObjectRegistry.register @@ -358,6 +389,7 @@ class VnfExtCpInfo(base.TackerObject, base.TackerObjectDictCompat, 'cp_protocol_info': fields.ListOfObjectsField( 'CpProtocolInfo', nullable=False, default=[]), 'ext_link_port_id': fields.StringField(nullable=True, default=None), + 'associated_vnfc_cp_id': fields.StringField(nullable=False) } @classmethod @@ -383,16 +415,19 @@ class VnfExtCpInfo(base.TackerObject, base.TackerObjectDictCompat, cpd_id = data_dict.get('cpd_id') cp_protocol_info = data_dict.get('cp_protocol_info', []) ext_link_port_id = data_dict.get('ext_link_port_id') + associated_vnfc_cp_id = data_dict.get('associated_vnfc_cp_id') obj = cls(id=id, cpd_id=cpd_id, cp_protocol_info=cp_protocol_info, - ext_link_port_id=ext_link_port_id) + ext_link_port_id=ext_link_port_id, + associated_vnfc_cp_id=associated_vnfc_cp_id) return obj def to_dict(self): data = {'id': self.id, 'cpd_id': self.cpd_id, - 'ext_link_port_id': self.ext_link_port_id} + 'ext_link_port_id': self.ext_link_port_id, + 'associated_vnfc_cp_id': self.associated_vnfc_cp_id} cp_protocol_info_list = [] for cp_protocol_info in self.cp_protocol_info: @@ -987,12 +1022,53 @@ class VirtualStorageResourceInfo(base.TackerObject, 'storage_resource': self.storage_resource.to_dict()} +@base.TackerObjectRegistry.register +class VnfcInfo(base.TackerObject, base.TackerPersistentObject): + # Version 1.0: Initial version + VERSION = '1.0' + + fields = { + 'id': fields.StringField(nullable=False), + 'vdu_id': fields.StringField(nullable=False), + 'vnfc_state': fields.StringField(nullable=False) + } + + @classmethod + def obj_from_primitive(cls, primitive, context): + if 'tacker_object.name' in primitive: + obj_vnfc_info = super( + VnfcInfo, cls).obj_from_primitive( + primitive, context) + else: + obj_vnfc_info = VnfcInfo._from_dict( + primitive) + + return obj_vnfc_info + + @classmethod + def _from_dict(cls, data_dict): + id = data_dict.get('id') + vdu_id = data_dict.get('vdu_id') + vnfc_state = data_dict.get('vnfc_state') + + obj = cls(id=id, vdu_id=vdu_id, + vnfc_state=vnfc_state) + + return obj + + def to_dict(self): + return {'id': self.id, + 'vdu_id': self.vdu_id, + 'vnfc_state': self.vnfc_state} + + @base.TackerObjectRegistry.register class ResourceHandle(base.TackerObject, base.TackerPersistentObject): # Version 1.0: Initial version VERSION = '1.0' + # TODO(esto-aln):Add vimConnectionId in Type:ResourceHandle fields = { 'resource_id': fields.StringField(nullable=False, default=""), 'vim_level_resource_type': fields.StringField(nullable=True, diff --git a/tacker/tests/unit/objects/fakes.py b/tacker/tests/unit/objects/fakes.py index ef9791a64..e35fd5156 100644 --- a/tacker/tests/unit/objects/fakes.py +++ b/tacker/tests/unit/objects/fakes.py @@ -298,7 +298,8 @@ virtual_storage_resource_info = { vnf_ext_cp_info = { 'id': uuidsentinel.id, 'cpd_id': 'CP1', - 'cp_protocol_info': [cp_protocol_info] + 'cp_protocol_info': [cp_protocol_info], + 'associated_vnfc_cp_id': uuidsentinel.associated_vnfc_cp_id } diff --git a/tacker/tests/unit/objects/test_instantiate_vnf_req.py b/tacker/tests/unit/objects/test_instantiate_vnf_req.py index 2c4d65efa..ac7038f31 100644 --- a/tacker/tests/unit/objects/test_instantiate_vnf_req.py +++ b/tacker/tests/unit/objects/test_instantiate_vnf_req.py @@ -120,12 +120,14 @@ def get_instantiated_info_dict_for_ext_links_and_flavour_id(): "layer_protocol": "IP_OVER_ETHERNET"}], "cpd_id": "CP1", "id": "19f0aa71-9376-43dc-8e13-5e76e4bdc8bf", - "ext_link_port_id": None}, { + "ext_link_port_id": None, + "associated_vnfc_cp_id": "dc67ee99-e963-44e2-a152-f0fb492eae76"}, { "cp_protocol_info": [{ "layer_protocol": "IP_OVER_ETHERNET"}], "cpd_id": "CP2", "id": "f47a9e33-b31a-4290-828a-c7569c52bd0e", - "ext_link_port_id": None}] + "ext_link_port_id": None, + "associated_vnfc_cp_id": "7ec01a11-e584-404a-88bd-39a56b63e29c"}] } } diff --git a/tacker/tests/unit/vnflcm/fakes.py b/tacker/tests/unit/vnflcm/fakes.py index e7295d2de..de2d259a4 100644 --- a/tacker/tests/unit/vnflcm/fakes.py +++ b/tacker/tests/unit/vnflcm/fakes.py @@ -33,6 +33,43 @@ from tacker.tests import uuidsentinel from tacker import wsgi +def return_default_vim(): + default_vim = { + 'vim_auth': { + 'username': 'user123', + 'password': 'pass123' + }, + 'placement_attr': { + 'region': 'RegionOne' + }, + 'tenant': uuidsentinel.tenant_uuid, + 'vim_id': uuidsentinel.vim_uuid, + 'vim_type': 'openstack' + } + + return default_vim + + +def return_vim_connection_object(fields): + access_info = { + 'username': fields.get('vim_auth', {}). + get('username'), + 'password': fields.get('vim_auth', {}). + get('password'), + 'region': fields.get('placement_attr', {}). + get('region'), + 'tenant': fields.get('tenant') + } + + vim_con_info = objects.\ + VimConnectionInfo(id=fields.get('vim_id'), + vim_id=fields.get('vim_id'), + vim_type=fields.get('vim_type'), + access_info=access_info) + + return vim_con_info + + def fake_vnf_package_vnfd_model_dict(**updates): vnfd = { 'package_uuid': uuidsentinel.package_uuid, diff --git a/tacker/tests/unit/vnflcm/test_controller.py b/tacker/tests/unit/vnflcm/test_controller.py index 4efaf9ea0..caa66fdb7 100644 --- a/tacker/tests/unit/vnflcm/test_controller.py +++ b/tacker/tests/unit/vnflcm/test_controller.py @@ -65,6 +65,13 @@ class TestController(base.TestCase): return_value={'VNFM': nfvo_plugin.FakeVNFMPlugin()}) self.mock_manager = self.patcher.start() self.controller = controller.VnfLcmController() + self.vim_info = { + 'vim_id': uuidsentinel.vnfd_id, + 'vim_type': 'test', + 'vim_auth': {'username': 'test', 'password': 'test'}, + 'placement_attr': {'region': 'TestRegionOne'}, + 'tenant': 'test' + } def tearDown(self): self.mock_manager.stop() @@ -74,6 +81,7 @@ class TestController(base.TestCase): def app(self): return fakes.wsgi_app_v1() + @mock.patch.object(objects.VnfInstance, 'save') @mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(objects.vnf_package.VnfPackage, 'get_by_id') @mock.patch.object(objects.vnf_package.VnfPackage, 'save') @@ -82,8 +90,9 @@ class TestController(base.TestCase): def test_create_without_name_and_description( self, mock_get_by_id_package_vnfd, mock_vnf_instance_create, mock_package_save, - mock_get_by_id_package, mock_get_vim): - + mock_get_by_id_package, mock_get_vim, + mock_save): + mock_get_vim.return_value = self.vim_info mock_get_by_id_package_vnfd.return_value = \ fakes.return_vnf_package_vnfd() mock_get_by_id_package.return_value = \ @@ -121,6 +130,7 @@ class TestController(base.TestCase): self.assertEqual(expected_vnf, resp.json) self.assertEqual(location_header, resp.headers['location']) + @mock.patch.object(objects.VnfInstance, 'save') @mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(objects.vnf_package.VnfPackage, 'get_by_id') @mock.patch.object(objects.vnf_package.VnfPackage, 'save') @@ -129,7 +139,9 @@ class TestController(base.TestCase): def test_create_with_name_and_description( self, mock_get_by_id_package_vnfd, mock_vnf_instance_create, mock_package_save, - mock_get_by_id_package, mock_get_vim): + mock_get_by_id_package, mock_get_vim, + mock_save): + mock_get_vim.return_value = self.vim_info mock_get_by_id_package_vnfd.return_value = \ fakes.return_vnf_package_vnfd() mock_get_by_id_package.return_value = \ @@ -169,6 +181,7 @@ class TestController(base.TestCase): self.assertEqual(expected_vnf, resp.json) self.assertEqual(location_header, resp.headers['location']) + @mock.patch.object(objects.VnfInstance, 'save') @mock.patch.object(vim_client.VimClient, "get_vim") @mock.patch.object(objects.vnf_package.VnfPackage, 'get_by_id') @mock.patch.object(objects.vnf_package.VnfPackage, 'save') @@ -177,7 +190,9 @@ class TestController(base.TestCase): def test_create_without_name_and_description_with_v241( self, mock_get_by_id_package_vnfd, mock_vnf_instance_create, mock_package_save, - mock_get_by_id_package, mock_get_vim): + mock_get_by_id_package, mock_get_vim, + mock_save): + mock_get_vim.return_value = self.vim_info mock_get_by_id_package_vnfd.return_value = \ fakes.return_vnf_package_vnfd() mock_get_by_id_package.return_value = \ @@ -1098,28 +1113,22 @@ class TestController(base.TestCase): self.assertEqual(expected_message, exception.msg) @mock.patch.object(objects.VnfInstanceList, "get_by_filters") - @ddt.data(' ', '2.6.1') - def test_index(self, api_version, mock_vnf_list): + def test_index(self, mock_vnf_list): req = fake_request.HTTPRequest.blank('/vnf_instances') - req.headers['Version'] = api_version vnf_instance_1 = fakes.return_vnf_instance() vnf_instance_2 = fakes.return_vnf_instance( fields.VnfInstanceState.INSTANTIATED) mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2] resp = self.controller.index(req) - expected_result = [fakes.fake_vnf_instance_response( - api_version=api_version), + expected_result = [fakes.fake_vnf_instance_response(), fakes.fake_vnf_instance_response( - fields.VnfInstanceState.INSTANTIATED, - api_version=api_version)] + fields.VnfInstanceState.INSTANTIATED)] self.assertEqual(expected_result, resp) @mock.patch.object(objects.VnfInstanceList, "get_by_filters") - @ddt.data(' ', '2.6.1') - def test_index_empty_response(self, api_version, mock_vnf_list): + def test_index_empty_response(self, mock_vnf_list): req = fake_request.HTTPRequest.blank('/vnf_instances') - req.headers['Version'] = api_version mock_vnf_list.return_value = [] resp = self.controller.index(req) self.assertEqual([], resp) @@ -1234,12 +1243,10 @@ class TestController(base.TestCase): ) def test_index_filter_operator(self, filter_params, mock_vnf_list): """Tests all supported operators in filter expression.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = api_version vnf_instance_1 = fakes.return_vnf_instance() vnf_instance_2 = fakes.return_vnf_instance( @@ -1248,11 +1255,9 @@ class TestController(base.TestCase): mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2] res_dict = self.controller.index(req) - expected_result = [fakes.fake_vnf_instance_response( - api_version=api_version), + expected_result = [fakes.fake_vnf_instance_response(), fakes.fake_vnf_instance_response( - fields.VnfInstanceState.INSTANTIATED, - api_version=api_version)] + fields.VnfInstanceState.INSTANTIATED)] self.assertEqual(expected_result, res_dict) @mock.patch.object(objects.VnfInstanceList, "get_by_filters") @@ -1262,11 +1267,9 @@ class TestController(base.TestCase): 'filter': "(eq,vnfInstanceName,'dummy_name');" "(eq,vnfInstanceDescription,'dummy_desc')"} - api_version = '2.6.1' query = urllib.parse.urlencode(params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = '2.6.1' vnf_instance_1 = fakes.return_vnf_instance() vnf_instance_2 = fakes.return_vnf_instance( @@ -1275,11 +1278,9 @@ class TestController(base.TestCase): mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2] res_dict = self.controller.index(req) - expected_result = [fakes.fake_vnf_instance_response( - api_version=api_version), + expected_result = [fakes.fake_vnf_instance_response(), fakes.fake_vnf_instance_response( - fields.VnfInstanceState.INSTANTIATED, - api_version=api_version)] + fields.VnfInstanceState.INSTANTIATED)] self.assertEqual(expected_result, res_dict) @mock.patch.object(objects.VnfInstanceList, "get_by_filters") @@ -1318,11 +1319,9 @@ class TestController(base.TestCase): def test_index_filter_attributes(self, filter_params, mock_vnf_list): """Test various attributes supported for filter parameter.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = '2.6.1' vnf_instance_1 = fakes.return_vnf_instance() vnf_instance_2 = fakes.return_vnf_instance( @@ -1331,11 +1330,9 @@ class TestController(base.TestCase): mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2] res_dict = self.controller.index(req) - expected_result = [fakes.fake_vnf_instance_response( - api_version=api_version), + expected_result = [fakes.fake_vnf_instance_response(), fakes.fake_vnf_instance_response( - fields.VnfInstanceState.INSTANTIATED, - api_version=api_version)] + fields.VnfInstanceState.INSTANTIATED)] self.assertEqual(expected_result, res_dict) @mock.patch.object(objects.VnfInstanceList, "get_by_filters") @@ -1348,11 +1345,9 @@ class TestController(base.TestCase): def test_index_filter_invalid_expression(self, filter_params, mock_vnf_list): """Test invalid filter expression.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = api_version self.assertRaises(exceptions.ValidationError, self.controller.index, req) @@ -1369,11 +1364,9 @@ class TestController(base.TestCase): def test_index_filter_invalid_string_values(self, filter_params, mock_vnf_list): """Test invalid string values as per ETSI NFV SOL013 5.2.2.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = api_version self.assertRaises(exceptions.ValidationError, self.controller.index, req) @@ -1386,11 +1379,9 @@ class TestController(base.TestCase): def test_index_filter_invalid_operator(self, filter_params, mock_vnf_list): """Test invalid operator in filter expression.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = api_version self.assertRaises(exceptions.ValidationError, self.controller.index, req) @@ -1402,11 +1393,9 @@ class TestController(base.TestCase): def test_index_filter_invalid_attribute(self, filter_params, mock_vnf_list): """Test invalid attribute in filter expression.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = api_version self.assertRaises(exceptions.ValidationError, self.controller.index, req) @@ -1420,10 +1409,8 @@ class TestController(base.TestCase): def test_index_filter_invalid_value_type(self, filter_params, mock_vnf_list): """Test values which doesn't match with attribute data type.""" - api_version = '2.6.1' query = urllib.parse.urlencode(filter_params) req = fake_request.HTTPRequest.blank( '/vnflcm/v1/vnf_instances?' + query) - req.headers['Version'] = api_version self.assertRaises(exceptions.ValidationError, self.controller.index, req) diff --git a/tacker/tests/unit/vnfm/test_vim_client.py b/tacker/tests/unit/vnfm/test_vim_client.py index aad325923..3e97913e4 100644 --- a/tacker/tests/unit/vnfm/test_vim_client.py +++ b/tacker/tests/unit/vnfm/test_vim_client.py @@ -26,7 +26,8 @@ class TestVIMClient(base.TestCase): self.vim_info = {'id': 'aaaa', 'name': 'VIM0', 'type': 'test_vim', 'auth_cred': {'password': '****'}, 'auth_url': 'http://127.0.0.1/identity/v3', - 'placement_attr': {'regions': ['TestRegionOne']}} + 'placement_attr': {'regions': ['TestRegionOne']}, + 'tenant_id': 'test'} self.vimclient = vim_client.VimClient() self.service_plugins = mock.Mock() self.nfvo_plugin = mock.Mock() @@ -71,7 +72,8 @@ class TestVIMClient(base.TestCase): vim_id=self.vim_info['id'], region_name='TestRegionOne') vim_expect = {'vim_auth': {'password': '****'}, 'vim_id': 'aaaa', - 'vim_name': 'VIM0', 'vim_type': 'test_vim'} + 'vim_name': 'VIM0', 'vim_type': 'test_vim', + 'tenant': 'test'} self.assertEqual(vim_expect, vim_result) def test_get_vim_with_default_name(self): @@ -86,7 +88,8 @@ class TestVIMClient(base.TestCase): vim_id=self.vim_info['id'], region_name='TestRegionOne') vim_expect = {'vim_auth': {'password': '****'}, 'vim_id': 'aaaa', - 'vim_name': 'aaaa', 'vim_type': 'test_vim'} + 'vim_name': 'aaaa', 'vim_type': 'test_vim', + 'tenant': 'test'} self.assertEqual(vim_expect, vim_result) def test_find_vim_key_with_key_not_found_exception(self): diff --git a/tacker/vnflcm/utils.py b/tacker/vnflcm/utils.py index 31a3c88c4..220aa80a7 100644 --- a/tacker/vnflcm/utils.py +++ b/tacker/vnflcm/utils.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import copy import io import os import six @@ -256,6 +257,10 @@ 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: @@ -265,6 +270,16 @@ def _get_vim_connection_info_from_vnf_req(vnf_instance, instantiate_vnf_req): 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 @@ -272,9 +287,6 @@ def _build_instantiated_vnf_info(vnfd_dict, instantiate_vnf_req, vnf_instance, vim_id): inst_vnf_info = vnf_instance.instantiated_vnf_info inst_vnf_info.vnf_state = fields.VnfOperationalStateType.STARTED - inst_vnf_info.ext_cp_info = _set_ext_cp_info(instantiate_vnf_req) - inst_vnf_info.ext_virtual_link_info = _set_ext_virtual_link_info( - instantiate_vnf_req, inst_vnf_info.ext_cp_info) node_templates = vnfd_dict.get( 'topology_template', {}).get('node_templates') @@ -283,6 +295,13 @@ def _build_instantiated_vnf_info(vnfd_dict, instantiate_vnf_req, _get_vnfc_resource_info(vnfd_dict, instantiate_vnf_req, vim_id) 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 = \ @@ -530,8 +549,12 @@ def _get_vnfc_resource_info(vnfd_dict, instantiate_vnf_req, vim_id): return vnfc_resource_info_list, virtual_storage_resource_info_list -def _set_ext_cp_info(instantiate_vnf_req): +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 @@ -546,6 +569,8 @@ def _set_ext_cp_info(instantiate_vnf_req): 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) @@ -562,6 +587,17 @@ def _get_ext_link_port_id(ext_virtual_link, 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""" diff --git a/tacker/vnfm/infra_drivers/openstack/openstack.py b/tacker/vnfm/infra_drivers/openstack/openstack.py index 4640ccd8c..fd4946a92 100644 --- a/tacker/vnfm/infra_drivers/openstack/openstack.py +++ b/tacker/vnfm/infra_drivers/openstack/openstack.py @@ -27,6 +27,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslo_utils import excutils +from oslo_utils import uuidutils import yaml from tacker._i18n import _ @@ -36,6 +37,7 @@ from tacker.common import utils from tacker.extensions import vnflcm from tacker.extensions import vnfm from tacker import objects +from tacker.objects import fields from tacker.tosca.utils import represent_odict from tacker.vnfm.infra_drivers import abstract_driver from tacker.vnfm.infra_drivers.openstack import constants as infra_cnst @@ -796,6 +798,7 @@ class OpenStack(abstract_driver.VnfAbstractDriver, inst_vnf_info.instance_id, heatclient) self._update_vnfc_resources(vnf_instance, stack_resources) + self._update_vnfc_info(vnf_instance) def _update_resource_handle(self, vnf_instance, resource_handle, stack_resources, resource_name): @@ -926,6 +929,18 @@ class OpenStack(abstract_driver.VnfAbstractDriver, for vl_port in vnf_vl_resource_info.vnf_link_ports: _update_link_port(vl_port) + def _update_vnfc_info(self, vnf_instance): + inst_vnf_info = vnf_instance.instantiated_vnf_info + vnfc_info = [] + + for vnfc_res_info in inst_vnf_info.vnfc_resource_info: + vnfc = objects.VnfcInfo(id=uuidutils.generate_uuid(), + vdu_id=vnfc_res_info.vdu_id, + vnfc_state=fields.VnfcState.STARTED) + vnfc_info.append(vnfc) + + inst_vnf_info.vnfc_info = vnfc_info + def _update_vnfc_resources(self, vnf_instance, stack_resources): inst_vnf_info = vnf_instance.instantiated_vnf_info for vnfc_res_info in inst_vnf_info.vnfc_resource_info: diff --git a/tacker/vnfm/vim_client.py b/tacker/vnfm/vim_client.py index 4cb4ea475..aacf94a07 100644 --- a/tacker/vnfm/vim_client.py +++ b/tacker/vnfm/vim_client.py @@ -63,7 +63,8 @@ class VimClient(object): vim_auth = self._build_vim_auth(vim_info) vim_res = {'vim_auth': vim_auth, 'vim_id': vim_info['id'], 'vim_name': vim_info.get('name', vim_info['id']), - 'vim_type': vim_info['type']} + 'vim_type': vim_info['type'], + 'tenant': vim_info['tenant_id']} return vim_res @staticmethod