Merge "Support for Change External VNF Connectivity"

This commit is contained in:
Zuul 2021-03-26 12:12:46 +00:00 committed by Gerrit Code Review
commit 4687cde8f6
44 changed files with 3623 additions and 35 deletions

View File

@ -121,6 +121,9 @@
$NEUTRON_DHCP_CONF:
DEFAULT:
enable_isolated_metadata: True
$CINDER_CONF:
lvmdriver-1:
image_volume_cache_enabled: False
devstack_plugins:
heat: https://opendev.org/openstack/heat
networking-sfc: https://opendev.org/openstack/networking-sfc

View File

@ -0,0 +1,69 @@
{
"extVirtualLinks": [
{
"id": "ext-vl-uuid-VL1",
"resourceId": "neutron-network-uuid_VL1",
"extCps": [
{
"cpdId": "CP1",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": "subnet-uuid"
}
]
}
}
]
}
]
},
{
"cpdId": "CP2",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
"10.0.0.1"
],
"subnetId": "subnet-uuid"
}
]
}
}
]
}
]
}
]
}
],
"vimConnectionInfo": [
{
"id": "vim-uuid",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"vimConnectionId": "dummy-vimid",
"interfaceInfo": {
"key1": "value1",
"key2": "value2"
},
"accessInfo": {
"key1": "value1",
"key2": "value2"
}
}
]
}

View File

@ -674,6 +674,81 @@ Request Example
.. literalinclude:: samples/vnflcm/modify-vnf-instance-request.json
:language: javascript
Change External VNF Connectivity
================================
.. rest_method:: POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/change_ext_conn
The POST method changes the external connectivity of a VNF instance.
This task resource represents the "Change external VNF connectivity" operation.
The client can use this resource to change the external connectivity of a VNF instance.
Response Codes
--------------
.. rest_status_code:: success status.yaml
- 202
.. rest_status_code:: error status.yaml
- 400
- 401
- 403
- 404
- 409
Request Parameters
------------------
.. rest_parameters:: parameters_vnflcm.yaml
- vnfInstanceId: vnf_instance_id
- extVirtualLinks: ext_virtual_links
- id: ext_virtual_links_id
- vimConnectionId: vim_connection_id
- resourceId: ext_virtual_links_resource_id
- extCps: ext_cps
- cpdId: cpd_id
- cpConfig: cp_config
- cpInstanceId: cp_instance_id
- linkPortId: link_port_id
- cpProtocolData: cp_protocol_data
- layerProtocol: layer_protocol
- ipOverEthernet: ip_over_ethernet
- macAddress: mac_address
- ipAddresses: ip_addresses
- type: ip_address_type
- fixedAddresses: fixed_addresses
- numDynamicAddresses: num_dynamic_addresses
- subnetId: subnet_id
- extLinkPorts: ext_link_ports
- id: ext_link_port_id
- resourceHandle: ext_link_port_resource_handle
- vimConnectionId: vim_connection_id
- resourceId: resource_handle_resource_id
- vimLevelResourceType: resource_handle_vim_level_resource_type
- vimConnectionInfo: vnf_instance_vim_connection_info
- 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
- username: vim_connection_info_access_info_username
- password: vim_connection_info_access_info_password
- region: vim_connection_info_access_info_region
- tenant: vim_connection_info_access_info_tenant
- additionalParams: vnf_instance_additional_params
Request Example
---------------
.. literalinclude:: samples/vnflcm/change-ext-conn-request.json
:language: javascript
Show VNF LCM operation occurrence
=================================

View File

@ -274,3 +274,14 @@ scale = {
'required': ['type', 'aspectId'],
'additionalProperties': True,
}
change_ext_conn = {
'type': 'object',
'properties': {
'extVirtualLinks': _extVirtualLinkData,
'vimConnectionInfo': _vimConnectionInfo,
'additionalParams': parameter_types.keyvalue_pairs,
},
'required': ['extVirtualLinks'],
'additionalProperties': True,
}

View File

@ -60,6 +60,10 @@ class ViewBuilder(base.BaseViewBuilder):
"heal": {
"href": '/vnflcm/v1/vnf_instances/%s/heal'
% vnf_instance.id
},
"changeExtConn": {
"href": '/vnflcm/v1/vnf_instances/%s/change_ext_conn'
% vnf_instance.id
}
}

View File

@ -53,6 +53,7 @@ from tacker.extensions import vnfm
from tacker import manager
from tacker import objects
from tacker.objects import fields
from tacker.objects.fields import ErrorPoint as EP
from tacker.objects import vnf_lcm_op_occs as vnf_lcm_op_occs_obj
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
from tacker.plugins.common import constants
@ -1577,6 +1578,49 @@ class VnfLcmController(wsgi.Controller):
vnf_lcm_subscription, cast=False)
return resp
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
http_client.NOT_FOUND, http_client.CONFLICT))
@validation.schema(vnf_lcm.change_ext_conn)
def change_ext_conn(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
if (vnf_instance.instantiation_state !=
fields.VnfInstanceState.INSTANTIATED):
return self._make_problem_detail(
'VNF is not instantiated',
409,
title='VNF IS NOT INSTANTIATED')
vnf['before_error_point'] = EP.INITIAL
self._change_ext_conn(context, vnf_instance, vnf, body)
def _change_ext_conn(self, context, vnf_instance, vnf, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
change_ext_conn_req = objects.ChangeExtConnRequest.obj_from_primitive(
req_body, context)
# call notification process
if vnf['before_error_point'] == EP.INITIAL:
vnf_lcm_op_occs_id = self._notification_process(
context,
vnf_instance,
fields.LcmOccsOperationType.CHANGE_EXT_CONN,
change_ext_conn_req,
request_body)
else:
vnf_lcm_op_occs_id = vnf['vnf_lcm_op_occs_id']
# Call Conductor server.
self.rpc_api.change_ext_conn(
context,
vnf_instance,
vnf,
change_ext_conn_req,
vnf_lcm_op_occs_id)
def create_resource():
return wsgi.Resource(VnfLcmController())

View File

@ -150,3 +150,8 @@ class VnflcmAPIRouter(wsgi.Router):
methods = {"GET": "list_lcm_op_occs"}
self._setup_route(mapper, "/vnf_lcm_op_occs",
methods, controller, default_resource)
# {apiRoot}/vnf_instances/{vnfInstanceId}/change_ext_conn resource
methods = {"POST": "change_ext_conn"}
self._setup_route(mapper, "/vnf_instances/{id}/change_ext_conn",
methods, controller, default_resource)

View File

@ -305,6 +305,16 @@ class VnfHealFailed(TackerException):
message = _("Heal Vnf failed for vnf %(id)s, error: %(error)s")
class VnfChangeExtConnFailed(TackerException):
message = _("Change external connectivity failed "
"for vnf %(id)s, error: %(error)s")
class VnfChangeExtConnWaitFailed(TackerException):
message = _("Change external connectivity wait failed "
"for vnf %(id)s, error: %(error)s")
class LockCreationFailed(TackerException):
message = _('Unable to create lock. Coordination backend not started.')

View File

@ -59,6 +59,7 @@ from tacker.glance_store import store as glance_store
from tacker import manager
from tacker import objects
from tacker.objects import fields
from tacker.objects.fields import ErrorPoint as EP
from tacker.objects.vnf_package import VnfPackagesList
from tacker.objects import vnfd as vnfd_db
from tacker.objects import vnfd_attribute as vnfd_attribute_db
@ -109,7 +110,8 @@ _ACTIVE_STATUS = ('ACTIVE',)
_PENDING_STATUS = ('PENDING_CREATE',
'PENDING_TERMINATE',
'PENDING_DELETE',
'PENDING_HEAL')
'PENDING_HEAL',
'PENDING_CHANGE_EXT_CONN')
_ERROR_STATUS = ('ERROR',)
_ALL_STATUSES = _ACTIVE_STATUS + _INACTIVE_STATUS + _PENDING_STATUS + \
_ERROR_STATUS
@ -781,6 +783,30 @@ class Conductor(manager.Manager):
format(error_msg))
raise exceptions.TackerException(message=error_msg)
@log.log
def _update_instantiated_vnf_info_change_ext_conn(
self, context, vnf_instance, change_ext_conn_req):
try:
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = \
objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
self.vnf_manager.invoke(
vim_connection_info.vim_type, 'post_change_ext_conn_vnf',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info)
vnflcm_utils._update_instantiated_vnf_info(
change_ext_conn_req, vnf_instance)
vnf_instance.instantiated_vnf_info.save()
except Exception as exp:
error_msg = \
"Failed to update instantiation information for vnf {}: {}".\
format(vnf_instance.id, encodeutils.exception_to_unicode(exp))
raise exceptions.TackerException(message=error_msg)
@log.log
def _add_additional_vnf_info(self, context, vnf_instance):
'''this method adds misc info to 'vnf' table'''
@ -834,6 +860,104 @@ class Conductor(manager.Manager):
{'zip': csar_path, 'folder': csar_zip_temp_path,
'uuid': vnf_pack.id})
def _get_vnf_link_ports_by_vl(self, vnf_info, ext_vl_id,
resource_id):
results = []
vnf_vl_resource_info = vnf_info.vnf_virtual_link_resource_info
for vnf_vl_res in vnf_vl_resource_info:
if ((vnf_vl_res.vnf_virtual_link_desc_id == ext_vl_id) and
(vnf_vl_res.network_resource.resource_id != resource_id)):
results.extend(vnf_vl_res.vnf_link_ports)
return results
def _get_vnf_link_ports_by_cp(self, vnf_info, cpd_id=None):
vnf_vl_resource_info = vnf_info.vnf_virtual_link_resource_info
vnfc_resource_info = vnf_info.vnfc_resource_info
def _get_vnf_link_port(vnf_link_port_id):
for vnf_vl_res in vnf_vl_resource_info:
for vnf_link_port in vnf_vl_res.vnf_link_ports:
if vnf_link_port.id == vnf_link_port_id:
return vnf_link_port
results = []
for vnfc_resource in vnfc_resource_info:
for vnfc_cp_info in vnfc_resource.vnfc_cp_info:
if cpd_id == vnfc_cp_info.cpd_id:
results.append(
_get_vnf_link_port(vnfc_cp_info.vnf_link_port_id))
return results
@grant_error_common
def _change_ext_conn_grant(
self,
context,
vnf_instance,
change_ext_conn_req,
vnf_lcm_op_occ_id):
if not self._get_grant_execute():
return
vnf_inf = vnf_instance.instantiated_vnf_info
def _create_linkport_rd(linkport, cpd_id):
rh = linkport.resource_handle
rd = objects.ResourceDefinition()
rd.resource = objects.ResourceHandle()
rd.id = linkport.id
rd.type = constants.TYPE_LINKPORT
rd.resource_template_id = cpd_id
rd.resource.vim_connection_id = rh.vim_connection_id
rd.resource.resource_id = rh.resource_id
rd.resource.vim_level_resource_type = rh.vim_level_resource_type
return rd
def _get_cpd_id(cp_instance_id):
vnfc_resource_info = vnf_inf.vnfc_resource_info
for vnfc_resource in vnfc_resource_info:
for vnfc_cp_info in vnfc_resource.vnfc_cp_info:
if cp_instance_id == vnfc_cp_info.id:
return vnfc_cp_info.cpd_id
update_resources = dict()
# If network resource of the VirtualLink changed, get all LinkPort
# resource related to VirtualLink
for ext_vl in change_ext_conn_req.ext_virtual_links:
nw_changed_resources = self._get_vnf_link_ports_by_vl(
vnf_inf, ext_vl.id, ext_vl.resource_id)
LOG.debug('nw_changed_resources {}'.format(nw_changed_resources))
if nw_changed_resources:
for resource in nw_changed_resources:
cpd_id = _get_cpd_id(resource.cp_instance_id)
update_resources[resource.resource_handle.resource_id] = \
_create_linkport_rd(resource, cpd_id)
continue
# If network resource of the VirtualLink does not change,
# Searching vnfc_resource_info table by the cpd_id, if found, get
# LinkPort resource corresponding the CP.
# It does not check that the CP status updated or not.
for ext_cp in ext_vl.ext_cps:
cp_changed_resources = \
self._get_vnf_link_ports_by_cp(vnf_inf, ext_cp.cpd_id)
LOG.debug('cp_changed_resources {}'.format(
cp_changed_resources))
for resource in cp_changed_resources:
update_resources[resource.resource_handle.resource_id] = \
_create_linkport_rd(resource, ext_cp.cpd_id)
update_resources_list = list(update_resources.values())
LOG.debug("Update Resources: %s", update_resources_list)
grant_request = self._make_grant_request(
context,
vnf_instance,
vnf_lcm_op_occ_id,
'CHANGE_EXT_CONN',
False,
update_resources=update_resources_list)
return self._grant(context, grant_request)
def _grant(self, context, grant_request):
LOG.info(
"grant start grant_request[%s]" %
@ -855,7 +979,11 @@ class Conductor(manager.Manager):
grant_obj.remove_resources):
msg = "grant remove resource error"
raise exceptions.ValidationError(detail=msg)
if len(
grant_request.update_resources) != len(
grant_obj.update_resources):
msg = "grant update resource error"
raise exceptions.ValidationError(detail=msg)
self._check_res_add_remove_rsc(context, grant_request, grant_obj)
return grant_obj
@ -881,6 +1009,16 @@ class Conductor(manager.Manager):
msg = "grant remove resource error"
raise exceptions.ValidationError(detail=msg)
for update_resource in grant_request.update_resources:
match_flg = False
for rsc in grant_obj.update_resources:
if update_resource.id == rsc.resource_definition_id:
match_flg = True
break
if not match_flg:
msg = "grant update resource error"
raise exceptions.ValidationError(detail=msg)
@grant_error_common
def _instantiate_grant(self,
context,
@ -1307,6 +1445,7 @@ class Conductor(manager.Manager):
is_automatic_invocation,
add_resources=[],
remove_resources=[],
update_resources=[],
placement_constraints=[]):
grant_request = objects.GrantRequest()
grant_request.vnf_instance_id = vnf_instance.id
@ -1330,6 +1469,8 @@ class Conductor(manager.Manager):
grant_request.add_resources = add_resources
if remove_resources:
grant_request.remove_resources = remove_resources
if update_resources:
grant_request.update_resources = update_resources
if placement_constraints:
grant_request.placement_constraints = placement_constraints
@ -1443,7 +1584,12 @@ class Conductor(manager.Manager):
jsonutils.dumps(affected_resources_snake_case)
changed_resource = objects.ResourceChanges.obj_from_primitive(
resource_change_obj, context)
changed_ext_connectivity = \
vnflcm_utils._get_changed_ext_connectivity(
old_vnf_instance=old_vnf_instance,
new_vnf_instance=vnf_instance)
vnf_notif.resource_changes = changed_resource
vnf_notif.changed_ext_connectivity = changed_ext_connectivity
vnf_notif.save()
notification_data['affectedVnfcs'] = \
affected_resources.get('affectedVnfcs', [])
@ -1453,6 +1599,9 @@ class Conductor(manager.Manager):
affected_resources.get('affectedVirtualStorages', [])
notification_data['notificationStatus'] = \
fields.LcmOccsNotificationStatus.RESULT
notification_data['changedExtConnectivity'] = \
utils.convert_snakecase_to_camelcase(
[i.to_dict() for i in changed_ext_connectivity])
if operation_state == \
fields.LcmOccsOperationState.FAILED_TEMP \
@ -2035,6 +2184,113 @@ class Conductor(manager.Manager):
self.vnflcm_driver.rollback_vnf(context, vnf_info,
vnf_instance, operation_params)
@coordination.synchronized('{vnf_instance[id]}')
def change_ext_conn(
self,
context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id):
"""Perform change external VNF connectivity operation.
This function will support changing external VNF connectivity
as defined in ETSI NFV SOL 002 and SOL 003, but now, you can
specify changing fixedAddresses or numDynamicAddresses in
ipAddresses attribute in extVirtualLinks.
Note:
1. Get grant from NFVO(if needed).
Request grant information is made from ExtVirtualLinkData
of ChangeExtConnRequest. If ExtVirtualLinkInfo is changed
from instantiated VNF, we inform VnfLinkPortInfo related
to that ExtVirtualLinkInfo. Also, we inform VnfLinkPortInfo
related to each individual VnfExtCpInfo.
2. Call vnflcm_driver to change networks.
Invoke vnflcm_driver to perform change external VNF
connectivity.
3. Update VNF information
Update InstantiatedVnfInfo as a post-processing.
Args:
context (Context): context for security/db session.
vnf_instance (VnfInstance): Information object for VNF instance.
vnf_dict (dict): Container for error point indication.
change_ext_conn_req (ChangeExtConnRequest):
Request object of change external connectivity.
vnf_lcm_op_occs_id (uuid): self-explanatory :)
"""
if vnf_dict['before_error_point'] == EP.INITIAL:
self._change_ext_conn_grant(
context,
vnf_instance,
change_ext_conn_req,
vnf_lcm_op_occs_id)
try:
old_vnf_instance = copy.deepcopy(vnf_instance)
# Update vnf_lcm_op_occs table and send notification "PROCESSING"
self._send_lcm_op_occ_notification(
context=context,
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id,
old_vnf_instance=None,
vnf_instance=vnf_instance,
request_obj=change_ext_conn_req,
operation=fields.LcmOccsOperationType.CHANGE_EXT_CONN
)
vnf_dict['current_error_point'] = EP.NOTIFY_PROCESSING
if vnf_dict['before_error_point'] <= EP.NOTIFY_PROCESSING:
# update vnf status to PENDING_CHANGE_EXT_CONN
self._change_vnf_status(context, vnf_instance.id,
_ACTIVE_STATUS, 'PENDING_CHANGE_EXT_CONN')
self.vnflcm_driver.change_ext_conn_vnf(
context,
vnf_instance,
vnf_dict,
change_ext_conn_req)
vnf_dict['current_error_point'] = EP.NOTIFY_COMPLETED
self._update_instantiated_vnf_info_change_ext_conn(
context, vnf_instance, change_ext_conn_req)
# update vnf status to ACTIVE
self._update_vnf_attributes(context, vnf_instance, vnf_dict,
_PENDING_STATUS, _ACTIVE_STATUS)
# Update vnf_lcm_op_occs table and send notification "COMPLETED"
self._send_lcm_op_occ_notification(
context=context,
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id,
old_vnf_instance=old_vnf_instance,
vnf_instance=vnf_instance,
request_obj=change_ext_conn_req,
operation=fields.LcmOccsOperationType.CHANGE_EXT_CONN,
operation_state=fields.LcmOccsOperationState.COMPLETED
)
except Exception as e:
# update vnf_status to 'ERROR' and create event with 'ERROR' status
self._change_vnf_status(context, vnf_instance.id,
_ALL_STATUSES, constants.ERROR, str(e))
LOG.error('Failed to execute operation. error={}'.format(e))
if vnf_dict['current_error_point'] in [EP.INTERNAL_PROCESSING,
EP.VNF_CONFIG_END]:
self._update_instantiated_vnf_info_change_ext_conn(
context, vnf_instance, change_ext_conn_req)
# update vnf_lcm_op_occs and send notification "FAILED_TEMP"
self._send_lcm_op_occ_notification(
context=context,
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id,
old_vnf_instance=old_vnf_instance,
vnf_instance=vnf_instance,
request_obj=change_ext_conn_req,
operation=fields.LcmOccsOperationType.CHANGE_EXT_CONN,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
error=str(e),
error_point=vnf_dict['current_error_point']
)
def init(args, **kwargs):
CONF(args=args, project='tacker',

View File

@ -136,3 +136,25 @@ class VNFLcmRPCAPI(object):
vnf_info=vnf_info,
vnf_instance=vnf_instance,
operation_params=operation_params)
def change_ext_conn(
self,
context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id,
cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
serializer=serializer)
cctxt = client.prepare()
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(
context,
'change_ext_conn',
vnf_instance=vnf_instance,
vnf_dict=vnf_dict,
change_ext_conn_req=change_ext_conn_req,
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id)

View File

@ -87,6 +87,10 @@ class VNFHealWaitFailed(exceptions.TackerException):
message = _('VNF Heal %(reason)s')
class VNFChangeExtConnWaitFailed(exceptions.TackerException):
message = _('VNF ChangeExtConn %(reason)s')
class VNFDeleteFailed(exceptions.TackerException):
message = _('%(reason)s')

View File

@ -44,3 +44,4 @@ def register_all():
__import__('tacker.objects.grant')
__import__('tacker.objects.grant_request')
__import__('tacker.objects.vnfd_attribute')
__import__('tacker.objects.change_ext_conn_req')

View File

@ -0,0 +1,68 @@
# 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.
from oslo_log import log as logging
from tacker import objects
from tacker.objects import base
from tacker.objects import fields
LOG = logging.getLogger(__name__)
@base.TackerObjectRegistry.register
class ChangeExtConnRequest(base.TackerObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vim_connection_info': fields.ListOfObjectsField(
'VimConnectionInfo', nullable=True, default=[]),
'ext_virtual_links': fields.ListOfObjectsField(
'ExtVirtualLinkData', nullable=False),
'additional_params': fields.DictOfNullableField(nullable=True,
default={})
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
obj_change_ext_conn_req = super(
ChangeExtConnRequest, cls).obj_from_primitive(
primitive, context)
else:
if 'vim_connection_info' in primitive.keys():
obj_data = [objects.VimConnectionInfo._from_dict(
vim_conn) for vim_conn in primitive.get(
'vim_connection_info', [])]
primitive.update({'vim_connection_info': obj_data})
if 'ext_virtual_links' in primitive.keys():
obj_data = [objects.ExtVirtualLinkData.obj_from_primitive(
ext_vir_link, context) for ext_vir_link in primitive.get(
'ext_virtual_links', [])]
primitive.update({'ext_virtual_links': obj_data})
obj_change_ext_conn_req = ChangeExtConnRequest._from_dict(
primitive)
return obj_change_ext_conn_req
@classmethod
def _from_dict(cls, data_dict):
vim_connection_info = data_dict.get('vim_connection_info', [])
ext_virtual_links = data_dict.get('ext_virtual_links', [])
additional_params = data_dict.get('additional_params', {})
return cls(
vim_connection_info=vim_connection_info,
ext_virtual_links=ext_virtual_links,
additional_params=additional_params)

View File

@ -233,8 +233,9 @@ class LcmOccsOperationType(BaseTackerEnum):
TERMINATE = 'TERMINATE'
HEAL = 'HEAL'
SCALE = 'SCALE'
CHANGE_EXT_CONN = 'CHANGE_EXT_CONN'
ALL = (INSTANTIATE, TERMINATE, HEAL, SCALE)
ALL = (INSTANTIATE, TERMINATE, HEAL, SCALE, CHANGE_EXT_CONN)
class LcmOccsNotificationStatus(BaseTackerEnum):

View File

@ -33,8 +33,12 @@ class Grant(base.TackerObject):
'GrantInfo', nullable=True, default=[]),
'remove_resources': fields.ListOfObjectsField(
'GrantInfo', nullable=True, default=[]),
'update_resources': fields.ListOfObjectsField(
'GrantInfo', nullable=True, default=[]),
'vim_assets': fields.ObjectField(
'VimAssets', nullable=True)
'VimAssets', nullable=True),
'ext_virtual_links': fields.ListOfObjectsField(
'ExtVirtualLinkData', nullable=True, default=[]),
}
@classmethod
@ -65,11 +69,20 @@ class Grant(base.TackerObject):
remove_rsc) for remove_rsc in primitive.get(
'remove_resources', [])]
primitive.update({'remove_resources': obj_data})
if 'update_resources' in primitive.keys():
obj_data = [GrantInfo._from_dict(
update_rsc) for update_rsc in primitive.get(
'update_resources', [])]
primitive.update({'update_resources': obj_data})
if 'vim_assets' in primitive.keys():
obj_data = VimAssets.obj_from_primitive(
primitive.get('vim_assets'), context)
primitive.update({'vim_assets': obj_data})
if 'ext_virtual_links' in primitive.keys():
obj_data = [objects.ExtVirtualLinkData.obj_from_primitive(
ext_vir_link, context) for ext_vir_link in primitive.get(
'ext_virtual_links', [])]
primitive.update({'ext_virtual_links': obj_data})
obj_grant = Grant._from_dict(primitive)
return obj_grant
@ -83,7 +96,9 @@ class Grant(base.TackerObject):
zones = data_dict.get('zones', [])
add_resources = data_dict.get('add_resources', [])
remove_resources = data_dict.get('remove_resources', [])
update_resources = data_dict.get('update_resources', [])
vim_assets = data_dict.get('vim_assets')
ext_virtual_links = data_dict.get('ext_virtual_links', [])
obj = cls(
id=id,
@ -93,7 +108,9 @@ class Grant(base.TackerObject):
zones=zones,
add_resources=add_resources,
remove_resources=remove_resources,
vim_assets=vim_assets)
update_resources=update_resources,
vim_assets=vim_assets,
ext_virtual_links=ext_virtual_links)
return obj

View File

@ -35,6 +35,8 @@ class GrantRequest(base.TackerObject):
'ResourceDefinition', nullable=True, default=[]),
'remove_resources': fields.ListOfObjectsField(
'ResourceDefinition', nullable=True, default=[]),
'update_resources': fields.ListOfObjectsField(
'ResourceDefinition', nullable=True, default=[]),
'placement_constraints': fields.ListOfObjectsField(
'PlacementConstraint', nullable=True, default=[]),
'_links': fields.ObjectField(
@ -57,11 +59,20 @@ class GrantRequest(base.TackerObject):
remove_rsc) for remove_rsc in primitive.get(
'remove_resources', [])]
primitive.update({'add_resources': obj_data})
if 'update_resources' in primitive.keys():
obj_data = [ResourceDefinition._from_dict(
update_rsc) for update_rsc in primitive.get(
'update_resources', [])]
primitive.update({'update_resources': obj_data})
if 'placement_constraints' in primitive.keys():
obj_data = [PlacementConstraint._from_dict(
place) for place in primitive.get(
'placement_constraints', [])]
primitive.update({'add_resources': obj_data})
if '_links' in primitive.keys():
obj_data = Links._from_dict(
primitive.get('_links', {}))
primitive.update({'_links': obj_data})
obj_grant_req = GrantRequest._from_dict(primitive)
return obj_grant_req
@ -76,6 +87,7 @@ class GrantRequest(base.TackerObject):
is_automatic_invocation = data_dict.get('is_automatic_invocation')
add_resources = data_dict.get('add_resources', [])
remove_resources = data_dict.get('remove_resources', [])
update_resources = data_dict.get('update_resources', [])
placement_constraints = data_dict.get('placement_constraints', [])
links = data_dict.get('_links')
@ -88,6 +100,7 @@ class GrantRequest(base.TackerObject):
is_automatic_invocation=is_automatic_invocation,
add_resources=add_resources,
remove_resources=remove_resources,
update_resources=update_resources,
placement_constraints=placement_constraints,
_links=links)
return obj
@ -112,6 +125,12 @@ class GrantRequest(base.TackerObject):
remove_resources_list.append(remove_resource.to_dict())
data.update({'remove_resources': remove_resources_list})
if self.update_resources:
update_resources_list = []
for update_resource in self.update_resources:
update_resources_list.append(update_resource.to_dict())
data.update({'update_resources': update_resources_list})
if self.placement_constraints:
placement_constraints_list = []
for placement_constraint in self.placement_constraints:

View File

@ -291,7 +291,8 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
changed_ext_conn = \
[objects.ExtVirtualLinkInfo.obj_from_primitive(
chg_ext_conn, context) for chg_ext_conn in
db_vnf_lcm_op_occ['changed_ext_connectivity']]
jsonutils.loads(
db_vnf_lcm_op_occ['changed_ext_connectivity'])]
vnf_lcm_op_occ_obj.changed_ext_connectivity = changed_ext_conn
vnf_lcm_op_occ_obj._context = context

View File

@ -40,6 +40,7 @@ PENDING_SCALE_IN = "PENDING_SCALE_IN"
PENDING_SCALE_OUT = "PENDING_SCALE_OUT"
PENDING_HEAL = "PENDING_HEAL"
PENDING_TERMINATE = "PENDING_TERMINATE"
PENDING_CHANGE_EXT_CONN = "PENDING_CHANGE_EXT_CONN"
DEAD = "DEAD"
ERROR = "ERROR"
NACK = "NACK"

View File

@ -176,6 +176,18 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=VNFLCM % 'change_ext_conn',
check_str=base.RULE_ADMIN_OR_OWNER,
description="Change external VNF connectivity.",
operations=[
{
'method': 'POST',
'path':
'/vnflcm/v1/vnf_instances/{vnfInstanceId}/change_ext_conn'
}
]
),
]

View File

@ -0,0 +1,4 @@
---
features:
- Add REST APIs for Change External VNF Connectivity.

View File

@ -74,3 +74,17 @@ class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver):
heal_vnf_request, grant,
grant_request, **kwargs):
pass
@log.log
def change_external_connectivity_start(
self, context, vnf_instance,
change_ext_conn_request, grant,
grant_request, **kwargs):
pass
@log.log
def change_external_connectivity_end(
self, context, vnf_instance,
change_ext_conn_request, grant,
grant_request, **kwargs):
pass

View File

@ -9,4 +9,4 @@ Content-type: application/x-iso9066-image
Name: Scripts/vnflcm_noop.py
Content-Type: text/x-python
Algorithm: SHA-256
Hash: 63cfaf9963680ff864981d4db809c2ec175d78054157c0bcd43ac7a85973af10
Hash: 950422e356c3eb6a7c92f424d975e2d3f295f480321bd91a97501045eddeab4a

View File

@ -262,6 +262,9 @@ class BaseVnfLcmTest(base.BaseTackerTest):
self.ext_link_ports = list()
# Create external subnet in net1
self.ext_subnets = list() # Store ids for cleaning.
# Create external networks to change.
self.changed_ext_networks = list()
self.changed_ext_subnets = list() # Store ids for cleaning.
networks = self.neutronclient().list_networks()
for nw in networks.get('networks'):
@ -281,13 +284,26 @@ class BaseVnfLcmTest(base.BaseTackerTest):
ext_mngd_net_id, _ = \
self._create_network("external_managed_internal_net")
self.ext_mngd_networks.append(ext_mngd_net_id)
changed_ext_net_id, changed_ext_net_name = \
self._create_network("changed_external_net")
self.changed_ext_networks.append(changed_ext_net_id)
# Chack how many networks are created.
networks = self.neutronclient().list_networks()
for nw in networks.get('networks'):
if nw['name'] not in [ext_net_name]:
if nw['name'] not in [ext_net_name, changed_ext_net_name]:
continue
self.ext_subnets.append(self._create_subnet(nw))
elif nw['name'] == ext_net_name:
self.ext_subnets.append(
self._create_subnet(nw,
cidr="22.22.1.0/24",
gateway="22.22.1.1"))
elif nw['name'] == changed_ext_net_name:
self.changed_ext_subnets.append(
self._create_subnet(nw,
cidr="22.22.2.0/24",
gateway="22.22.2.1"))
@classmethod
def _list_glance_image(cls, filter_name='cirros-0.4.0-x86_64-disk'):
@ -453,6 +469,16 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return resp, body
def _change_ext_conn_vnf_instance(self, vnf_instance_id, request_body):
url = os.path.join(
self.base_vnf_instances_url,
vnf_instance_id,
"change_ext_conn")
resp, body = self.http_client.do_request(url, "POST",
body=jsonutils.dumps(request_body))
return resp, body
def _rollback_op_occs(self, vnf_lcm_op_occs_id):
rollback_url = os.path.join(
self.base_vnf_lcm_op_occs_url,
@ -1140,18 +1166,18 @@ class BaseVnfLcmTest(base.BaseTackerTest):
self.fail("Failed, create network=<%s>, %s" %
(uniq_name, e))
def _create_subnet(self, network):
cidr_prefix = "22.22.{}".format(str(len(self.ext_subnets)))
def _create_subnet(self, network, cidr, gateway):
body = {'subnet': {'network_id': network['id'],
'name': "subnet-%s" % uuidutils.generate_uuid(),
'cidr': "{}.0/24".format(cidr_prefix),
'cidr': "{}".format(cidr),
'ip_version': 4,
'gateway_ip': "{}.1".format(cidr_prefix),
'gateway_ip': "{}".format(gateway),
"enable_dhcp": True}}
try:
subnet = self.neutronclient().create_subnet(body=body)["subnet"]
self.addCleanup(self._delete_subnet, subnet['id'])
print("Create subnet success, %s" % subnet['id'], flush=True)
return subnet['id']
except Exception as e:
self.fail("Failed, create subnet for net_id=<%s>, %s" %

View File

@ -40,7 +40,8 @@ class Subscription:
"SCALE",
"TERMINATE",
"HEAL",
"MODIFY_INFO"
"MODIFY_INFO",
"CHANGE_EXT_CONN"
]
},
"callbackUri": callback_uri
@ -373,3 +374,56 @@ class VnfInstances:
"samplekey": "samplevalue"
}
}
@staticmethod
def make_change_ext_conn_request_body(
tenant_id,
networks_id,
external_subnets_id):
# set external subnet_id on vim.
ext_cps_vdu2_cp2 = {
"cpdId": "VDU2_CP2",
"cpConfig": [{
"cpProtocolData": [{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["22.22.2.200"],
"subnetId": external_subnets_id[0]
}]
}
}]
}]
}
ext_virtual_link_cp2 = {
"id": uuidsentinel.evl2_id,
"resourceId": networks_id[0],
"extCps": [
ext_cps_vdu2_cp2
]
}
data = {
"extVirtualLinks": [
ext_virtual_link_cp2
],
"vimConnectionInfo": [{
"id": uuidsentinel.vim_connection_id,
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"vimConnectionId": uuidsentinel.vim_connection_id,
"interfaceInfo": {
"endpoint": "http://127.0.0.1/identity"
},
"accessInfo": {
"username": "nfv_user",
"region": "RegionOne",
"password": "devstack",
"tenant": tenant_id
}
}],
}
return data

View File

@ -1596,6 +1596,48 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
expected_usage_state,
vnf_package_info['usageState'])
def _get_fixed_ips(self, vnf_instance_id, request_body):
res_name = None
for extvirlink in request_body['extVirtualLinks']:
if 'extCps' not in extvirlink:
continue
for extcps in extvirlink['extCps']:
if 'cpdId' in extcps:
if res_name is None:
res_name = list()
res_name.append(extcps['cpdId'])
break
if res_name is None:
return []
stack = self._get_heat_stack(vnf_instance_id)
stack_id = stack.id
stack_resource = self._get_heat_resource_list(stack_id, nested_depth=2)
releations = dict()
for elmt in stack_resource:
if elmt.resource_type != 'OS::Neutron::Port':
continue
if elmt.resource_name not in res_name:
continue
releations[elmt.parent_resource] = elmt.resource_name
details = list()
for (parent_name, resource_name) in releations.items():
for elmt in stack_resource:
if parent_name != elmt.resource_name:
continue
detail_stack = self._get_heat_resource(
elmt.physical_resource_id, resource_name)
details.append(detail_stack)
ans_list = list()
for detail in details:
ans_list.append(detail.attributes['fixed_ips'])
return ans_list
def _assert_occ_show(self, resp, op_occs_info):
self.assertEqual(200, resp.status_code)
@ -1684,3 +1726,146 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
self.assertIsNotNone(_links.get('rollback').get('href'))
if _links.get('grant') is not None:
self.assertIsNotNone(_links.get('grant').get('href'))
def test_inst_chgextconn_term(self):
"""Test basic life cycle operations with sample VNFD.
In this test case, we do following steps.
- Create subscription.
- Show subscriptions.
- Get list of subscriptions.
- Create VNF package.
- Upload VNF package.
- Create VNF instance.
- Instantiate VNF.
- Get list of VNF instances.
- Get VNF informations.
- Change External VNF Connectivity.
- Get opOccs informations.
- Terminate VNF
- Delete VNF
- Delete subscription
"""
# Create subscription and register it.
request_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))
resp, response_body = self._register_subscription(request_body)
self.assertEqual(201, resp.status_code)
self.assert_http_header_location_for_subscription(resp.headers)
subscription_id = response_body.get('id')
self.addCleanup(
self._delete_subscription,
subscription_id)
# Subscription show
resp, body = self._wait_show_subscription(subscription_id)
self.assert_subscription_show(resp, body)
# Subscription list
resp, _ = self._list_subscription()
self.assertEqual(200, resp.status_code)
# Pre Setting: Create vnf package.
sample_name = 'functional5'
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
"../../../etc/samples/etsi/nfv",
sample_name))
tempname, _ = vnflcm_base._create_csar_with_unique_vnfd_id(
csar_package_path)
# upload vnf package
vnf_package_id, vnfd_id = vnflcm_base._create_and_upload_vnf_package(
self.tacker_client, user_defined_data={
"key": sample_name}, temp_csar_path=tempname)
# Post Setting: Reserve deleting vnf package.
self.addCleanup(vnflcm_base._delete_vnf_package, self.tacker_client,
vnf_package_id)
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(vnfd_id))
vnf_instance_id = vnf_instance['id']
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_create_vnf(resp, vnf_instance, vnf_package_id)
vnf_instance_name = vnf_instance['vnfInstanceName']
self.addCleanup(self._delete_vnf_instance, vnf_instance_id)
# Instantiate vnf instance
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
self.vim['tenant_id'], self.ext_networks, self.ext_mngd_networks,
self.ext_link_ports, self.ext_subnets)
resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_instantiate_vnf(resp, vnf_instance_id, vnf_package_id)
# List vnf instance
filter_expr = {
'filter': "(eq,id,{});(eq,vnfInstanceName,{})".format(
vnf_instance_id, vnf_instance_name)}
resp, vnf_instances = self._list_vnf_instance(params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(vnf_instances))
# Show vnf instance
resp, vnf_instance = self._show_vnf_instance(vnf_instance_id)
self.assertEqual(200, resp.status_code)
# Change external connectivity
request_body = \
fake_vnflcm.VnfInstances.make_change_ext_conn_request_body(
self.vim['tenant_id'], self.changed_ext_networks,
self.changed_ext_subnets)
before_fixed_ips = self._get_fixed_ips(vnf_instance_id, request_body)
resp, _ = \
self._change_ext_conn_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
after_fixed_ips = self._get_fixed_ips(vnf_instance_id, request_body)
self.assertNotEqual(before_fixed_ips, after_fixed_ips)
callback_url = os.path.join(
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
notify_mock_responses = vnflcm_base.FAKE_SERVER_MANAGER.get_history(
callback_url)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(
callback_url)
vnflcm_op_occ_id = notify_mock_responses[0].request_body.get(
'vnfLcmOpOccId')
self.assertIsNotNone(vnflcm_op_occ_id)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(callback_url)
# occ-show(chgextconn)
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
resource_name_list = [r.resource_name for r in resources_list]
glance_image_id_list = \
self._get_glance_image_list_from_stack_resource(
stack.id, resource_name_list)
terminate_req_body = fake_vnflcm.VnfInstances.make_term_request_body()
resp, _ = self._terminate_vnf_instance(
vnf_instance_id, terminate_req_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self.assert_terminate_vnf(resp, vnf_instance_id, stack.id,
resource_name_list, glance_image_id_list, vnf_package_id)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_delete_vnf(resp, vnf_instance_id, vnf_package_id)
# Subscription delete
resp, response_body = self._delete_subscription(subscription_id)
self.assertEqual(204, resp.status_code)
resp, _ = self._show_subscription(subscription_id)
self.assertEqual(404, resp.status_code)

View File

@ -76,6 +76,17 @@ class Grant:
return res_remove_resources
@staticmethod
def _make_update_resources(req_update_resources):
res_update_resources = []
for req_update_resource in req_update_resources:
res_update_resource = {
"resourceDefinitionId": req_update_resource['id']
}
res_update_resources.append(res_update_resource)
return res_update_resources
@staticmethod
def _make_vim_assets(image_id, flavour_id="1"):
# set m1.tiny="1" for flavour_id
@ -203,3 +214,17 @@ class Grant:
request_body['removeResources'])
return res
@staticmethod
def make_change_ext_conn_response_body(request_body, tenant_id, image_id):
request_body = Grant._convert_body_to_dict(request_body)
res = Grant._make_response_template(request_body)
res["vimConnections"] = Grant._make_vim_connection_info(tenant_id)
res["zones"] = Grant.ZONES
if 'updateResources' in request_body.keys():
res["updateResources"] = Grant._make_update_resources(
request_body['updateResources'])
res["vimAssets"] = Grant._make_vim_assets(image_id)
res["additionalParams"] = Grant.ADDITIONAL_PARAMS
return res

View File

@ -23,7 +23,7 @@ from tacker.tests.functional.sol_separated_nfvo.vnflcm import fake_vnfpkgm
class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest):
def _register_vnf_package_mock_response(self):
def _register_vnf_package_mock_response(self, package_dir="functional6"):
"""Prepare VNF package for test.
Register VNF package response to fake NFVO server and Cleanups.
@ -32,7 +32,7 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest):
Response: VNF Package information
"""
# Pre Setting: Create vnf package.
sample_name = "functional6"
sample_name = package_dir
csar_package_path = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
@ -92,6 +92,152 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest):
return vnf_package_info
def test_inst_chgextconn_term(self):
"""Test basic life cycle operations with sample VNFD with UserData.
In this test case, we do following steps.
- Create subscription.
- Create VNF instance.
- Instantiate VNF.
- List VNF instances.
- Show VNF instance.
- Change External VNF Connectivity.
- Get opOccs information.
- Terminate VNF.
- Delete VNF.
- Delete subscription.
- Show subscription.
"""
vnf_package_info = self._register_vnf_package_mock_response(
package_dir="functional5")
glance_image = self._list_glance_image()[0]
# Create subscription and register it.
request_body = fake_vnflcm.Subscription.make_create_request_body(
'http://localhost:{}{}'.format(
vnflcm_base.FAKE_SERVER_MANAGER.SERVER_PORT,
os.path.join(
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)))
resp, response_body = self._register_subscription(request_body)
self.assertEqual(201, resp.status_code)
self.assert_http_header_location_for_subscription(resp.headers)
subscription_id = response_body.get('id')
self.addCleanup(self._delete_subscription, subscription_id)
# Create vnf instance
resp, vnf_instance = self._create_vnf_instance_from_body(
fake_vnflcm.VnfInstances.make_create_request_body(
vnf_package_info['vnfdId']))
vnf_instance_id = vnf_instance.get('id')
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self._assert_create_vnf(resp, vnf_instance)
vnf_instance_name = vnf_instance['vnfInstanceName']
self.addCleanup(self._delete_vnf_instance, vnf_instance_id)
# Set Fake server response for Grant-Req(Instantiate)
vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST',
fake_grant.Grant.GRANT_REQ_PATH, status_code=201,
callback=lambda req_headers,
req_body: fake_grant.Grant.make_inst_response_body(req_body,
self.vim['tenant_id'], glance_image.id))
# Instantiate vnf instance
request_body = fake_vnflcm.VnfInstances.make_inst_request_body(
self.vim['tenant_id'], self.ext_networks, self.ext_mngd_networks,
self.ext_link_ports, self.ext_subnets)
resp, _ = self._instantiate_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self._assert_instantiate_vnf(resp, vnf_instance_id)
# List vnf instances
filter_expr = {
'filter': "(eq,id,{});(eq,vnfInstanceName,{})".format(
vnf_instance_id, vnf_instance_name)}
resp, vnf_instances = self._list_vnf_instance(params=filter_expr)
self.assertEqual(200, resp.status_code)
self.assertEqual(1, len(vnf_instances))
# Show vnf instance
resp, vnf_instance = self._show_vnf_instance(vnf_instance_id)
self.assertEqual(200, resp.status_code)
# Set Fake server response for Grant-Req(Chnage-ext-conn)
vnflcm_base.FAKE_SERVER_MANAGER.set_callback(
'POST',
fake_grant.Grant.GRANT_REQ_PATH,
status_code=201,
callback=lambda req_headers,
req_body: fake_grant.Grant.make_change_ext_conn_response_body(
req_body,
self.vim['tenant_id'],
glance_image.id))
# Change external connectivity
request_body = \
fake_vnflcm.VnfInstances.make_change_ext_conn_request_body(
self.vim['tenant_id'], self.changed_ext_networks,
self.changed_ext_subnets)
before_fixed_ips = self._get_fixed_ips(vnf_instance_id, request_body)
resp, _ = \
self._change_ext_conn_vnf_instance(vnf_instance_id, request_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
after_fixed_ips = self._get_fixed_ips(vnf_instance_id, request_body)
self.assertNotEqual(before_fixed_ips, after_fixed_ips)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(
fake_grant.Grant.GRANT_REQ_PATH)
# get vnflcm_op_occ_id
callback_url = os.path.join(
vnflcm_base.MOCK_NOTIFY_CALLBACK_URL,
self._testMethodName)
notify_mock_responses = vnflcm_base.FAKE_SERVER_MANAGER.get_history(
callback_url)
vnflcm_base.FAKE_SERVER_MANAGER.clear_history(
callback_url)
vnflcm_op_occ_id = notify_mock_responses[0].request_body.get(
'vnfLcmOpOccId')
self.assertIsNotNone(vnflcm_op_occ_id)
# occ-show(chgextconn)
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# Set Fake server response for Grant-Req(Terminate)
vnflcm_base.FAKE_SERVER_MANAGER.set_callback('POST',
fake_grant.Grant.GRANT_REQ_PATH, status_code=201,
callback=lambda req_headers,
req_body: fake_grant.Grant.make_term_response_body(req_body))
# Get stack informations to terminate.
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
resource_name_list = [r.resource_name for r in resources_list]
glance_image_id_list = self._get_glance_image_list_from_stack_resource(
stack.id, resource_name_list)
# Terminate VNF
terminate_req_body = fake_vnflcm.VnfInstances.make_term_request_body()
resp, _ = self._terminate_vnf_instance(vnf_instance_id,
terminate_req_body)
self._wait_lcm_done('COMPLETED', vnf_instance_id=vnf_instance_id)
self._assert_terminate_vnf(resp, vnf_instance_id, stack.id,
resource_name_list, glance_image_id_list)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
self.assert_delete_vnf(resp, vnf_instance_id)
# Delete Subscription
resp, response_body = self._delete_subscription(subscription_id)
self.assertEqual(204, resp.status_code)
# Check subscription was deleted
resp, show_body = self._show_subscription(subscription_id)
self.assertEqual(404, resp.status_code)
def test_inst_heal_term(self):
"""Test basic life cycle operations with sample VNFD with UserData.
@ -315,3 +461,64 @@ class VnfLcmWithNfvoSeparator(vnflcm_base.BaseVnfLcmTest):
self.assertEqual(
'{} {}'.format(expected_auth_type, expected_token_value),
actual_auth)
def _assert_occ_show(self, resp, op_occs_info):
self.assertEqual(200, resp.status_code)
# Only check required parameters.
self.assertIsNotNone(op_occs_info.get('id'))
self.assertIsNotNone(op_occs_info.get('operationState'))
self.assertIsNotNone(op_occs_info.get('stateEnteredTime'))
self.assertIsNotNone(op_occs_info.get('vnfInstanceId'))
self.assertIsNotNone(op_occs_info.get('operation'))
self.assertIsNotNone(op_occs_info.get('isAutomaticInvocation'))
self.assertIsNotNone(op_occs_info.get('isCancelPending'))
_links = op_occs_info.get('_links')
self.assertIsNotNone(_links.get('self'))
self.assertIsNotNone(_links.get('self').get('href'))
self.assertIsNotNone(_links.get('vnfInstance'))
self.assertIsNotNone(_links.get('vnfInstance').get('href'))
self.assertIsNotNone(_links.get('grant'))
self.assertIsNotNone(_links.get('grant').get('href'))
def _get_fixed_ips(self, vnf_instance_id, request_body):
res_name = None
for extvirlink in request_body['extVirtualLinks']:
if 'extCps' not in extvirlink:
continue
for extcps in extvirlink['extCps']:
if 'cpdId' in extcps:
if res_name is None:
res_name = list()
res_name.append(extcps['cpdId'])
break
self.assertTrue(res_name)
stack = self._get_heat_stack(vnf_instance_id)
stack_id = stack.id
stack_resource = self._get_heat_resource_list(stack_id, nested_depth=2)
releations = dict()
for elmt in stack_resource:
if elmt.resource_type != 'OS::Neutron::Port':
continue
if elmt.resource_name not in res_name:
continue
releations[elmt.parent_resource] = elmt.resource_name
details = list()
for (parent_name, resource_name) in releations.items():
for elmt in stack_resource:
if parent_name != elmt.resource_name:
continue
detail_stack = self._get_heat_resource(
elmt.physical_resource_id, resource_name)
details.append(detail_stack)
ans_list = list()
for detail in details:
ans_list.append(detail.attributes['fixed_ips'])
return ans_list

View File

@ -27,6 +27,7 @@ import zipfile
from oslo_config import cfg
from tacker.common import utils as common_utils
from tacker.db.db_sqlalchemy import models
from tacker.objects import scale_vnf_request
from tacker.tests import utils
@ -301,3 +302,250 @@ def scale_request(type, number_of_steps):
scale_request = scale_vnf_request.ScaleVnfRequest(**scale_request_data)
return scale_request
def get_instantiated_vnf_info():
vnf_info = objects.vnf_instance.VnfInstance()
def _get_instantiated_vnf_info_data():
return {
"flavour_id": "simple",
"vnf_instance_id": uuidsentinel.vnf_instance_id,
"vnf_virtual_link_resource_info":
_get_virtual_link_resource_info(),
"vnfc_resource_info": _get_vnfc_resource_info(),
}
def _get_virtual_link_resource_info():
return [{
"id": uuidsentinel.vnf_vl_resource_1,
"vnf_virtual_link_desc_id": uuidsentinel.ext_vl_1,
"network_resource": {
"vim_connection_id": None,
"resource_id": uuidsentinel.ext_vl_resource_1,
"vim_level_resource_type": "OS::Neutron::Net",
"deleted": False,
},
"vnf_link_ports": [
{
"id": uuidsentinel.vnf_link_port_1,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vnf_vl1_link_port_1,
"vim_level_resource_type": "OS::Neutron::Port",
"deleted": False,
},
"cp_instance_id": uuidsentinel.cp_instance_1,
"deleted": False,
},
{
"id": uuidsentinel.vnf_link_port_2,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vnf_vl1_link_port_2,
"vim_level_resource_type": "OS::Neutron::Port",
"deleted": False,
},
"cp_instance_id": uuidsentinel.cp_instance_2,
"deleted": False,
},
{
"id": uuidsentinel.vnf_link_port_5,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vnf_vl1_link_port_5,
"vim_level_resource_type": "OS::Neutron::Port",
"deleted": False,
},
"cp_instance_id": uuidsentinel.cp_instance_2,
"deleted": False,
},
],
},
{
"id": uuidsentinel.vnf_vl_resource_2,
"vnf_virtual_link_desc_id": uuidsentinel.ext_vl_2,
"network_resource": {
"vim_connection_id": None,
"resource_id": uuidsentinel.ext_vl_resource_2,
"vim_level_resource_type": "OS::Neutron::Net",
"deleted": False,
},
"vnf_link_ports": [
{
"id": uuidsentinel.vnf_link_port_3,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vnf_vl2_link_port_1,
"vim_level_resource_type": "OS::Neutron::Port",
"deleted": False,
},
"cp_instance_id": uuidsentinel.cp_instance_3,
"deleted": False,
},
{
"id": uuidsentinel.vnf_link_port_4,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vnf_vl2_link_port_2,
"vim_level_resource_type": "OS::Neutron::Port",
"deleted": False,
},
"cp_instance_id": uuidsentinel.cp_instance_4,
"deleted": False,
},
{
"id": uuidsentinel.vnf_link_port_5,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vnf_vl2_link_port_3,
"vim_level_resource_type": "OS::Neutron::Port",
"deleted": False,
},
"cp_instance_id": uuidsentinel.cp_instance_5,
"deleted": False,
}
]
}]
def _get_vnfc_resource_info():
return [{
"id": uuidsentinel.vnfc_resource_1,
"vdu_id": "VDU1",
"compute_resource": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.uuid,
"vim_level_resource_type": "OS::Nova::Server",
"deleted": False,
},
"storage_resource_ids": [],
"vnfc_cp_info": [{
"id": uuidsentinel.cp_instance_1,
"cpd_id": "CP1",
"vnf_ext_cp_id": None,
"cp_protocol_info": [],
"vnf_link_port_id": uuidsentinel.vnf_link_port_1,
}, {
"id": uuidsentinel.cp_instance_2,
"cpd_id": "CP2",
"vnf_ext_cp_id": None,
"cp_protocol_info": [],
"vnf_link_port_id": uuidsentinel.vnf_link_port_2,
}],
"metadata": {},
}, {
"id": uuidsentinel.vnfc_resource_2,
"vdu_id": "VDU2",
"compute_resource": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.uuid,
"vim_level_resource_type": "OS::Nova::Server",
"deleted": False,
},
"storage_resource_ids": [],
"vnfc_cp_info": [{
"id": uuidsentinel.cp_instance_3,
"cpd_id": "CP3",
"vnf_ext_cp_id": None,
"cp_protocol_info": [],
"vnf_link_port_id": uuidsentinel.vnf_link_port_3,
}, {
"id": uuidsentinel.cp_instance_4,
"cpd_id": "CP4",
"vnf_ext_cp_id": None,
"cp_protocol_info": [],
"vnf_link_port_id": uuidsentinel.vnf_link_port_4,
}, {
"id": uuidsentinel.cp_instance_5,
"cpd_id": "CP5",
"vnf_ext_cp_id": None,
"cp_protocol_info": [],
"vnf_link_port_id": uuidsentinel.vnf_link_port_5,
}],
"metadata": {},
}]
vnf_info.instantiated_vnf_info = \
objects.InstantiatedVnfInfo.obj_from_primitive(
_get_instantiated_vnf_info_data(), None
)
return vnf_info.instantiated_vnf_info
def get_change_ext_conn_request():
change_ext_conn_req_body = {
"extVirtualLinks": [{
"id": uuidsentinel.ext_vl_1,
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": uuidsentinel.ext_vl_resource_3,
"extCps": [{
"cpdId": 'CP1',
"cpConfig": [{
"cpInstanceId": uuidsentinel.uuid,
"cpProtocolData": [{
"layerProtocol": 'IP_OVER_ETHERNET',
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.0.0.1"],
"subnetId": uuidsentinel.uuid,
}]
}
}]
}]
}]}, {
"id": uuidsentinel.ext_vl_2,
"vimConnectionId": uuidsentinel.vim_connection_id,
"resourceId": uuidsentinel.ext_vl_resource_2,
"extCps": [{
"cpdId": 'CP3',
"cpConfig": [{
"cpInstanceId": uuidsentinel.uuid,
"cpProtocolData": [{
"layerProtocol": 'IP_OVER_ETHERNET',
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["10.0.0.2"],
"subnetId": uuidsentinel.uuid,
}]
}
}]
}]}, {
"cpdId": 'CP5',
"cpConfig": [{
"cpInstanceId": uuidsentinel.uuid,
"cpProtocolData": [{
"layerProtocol": 'IP_OVER_ETHERNET',
"ipOverEthernet": {
"ipAddresses": [{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": uuidsentinel.uuid,
}]
}
}]
}]
}]
}],
"vimConnectionInfo": [{
"id": uuidsentinel.vim_connection_id,
"vimId": uuidsentinel.uuid,
"vimType": 'openstack',
"interfaceInfo": {"key1": 'value1', "key2": 'value2'},
"accessInfo": {"key1": 'value1', "key2": 'value2'},
}],
}
return change_ext_conn_req_body
def get_change_ext_conn_request_obj():
"""Return ChangeExtConnRequest Object
obj_from_primitive() needs snake_case dictionary
"""
body = common_utils.convert_camelcase_to_snakecase(
get_change_ext_conn_request())
return objects.ChangeExtConnRequest.obj_from_primitive(
body, None)

View File

@ -3019,3 +3019,435 @@ class TestConductor(SqlTestCase, unit_base.FixturedTestCase):
vnf_data["software_version"] = vnf_data.pop("vnf_software_version")
vnf_data["descriptor_version"] = vnf_data.pop("vnfd_version")
return vnf_data
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_instantiated_vnf_info_change_ext_conn')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'.send_notification')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(conductor_server.Conductor, "_get_grant_execute")
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_change_ext_conn(
self,
mock_vnf_by_id,
mock_exec,
mock_get_lock,
mock_save,
mock_send_notification,
mock_change_vnf_status,
mock_update_vnf_attributes,
mock_update_instantiated_vnf_info_change_ext_conn):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
mock_vnf_by_id.return_value = objects.VnfLcmOpOcc(
context=self.context, **lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
vnf_instance.instantiated_vnf_info = fakes.get_instantiated_vnf_info()
vnf_dict = {"before_error_point": 0}
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
# Test condition settings.
mock_exec.return_value = False
self.conductor.change_ext_conn(
self.context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id)
mock_change_vnf_status.assert_called_with(self.context,
mock.ANY, (constants.ACTIVE,),
constants.PENDING_CHANGE_EXT_CONN)
mock_update_vnf_attributes.assert_called_with(self.context,
mock.ANY, mock.ANY, mock.ANY, (constants.ACTIVE,))
self.assertEqual(
mock_send_notification.call_args[0][1].get('operationState'),
'PROCESSING')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_instantiated_vnf_info_change_ext_conn')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'.send_notification')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
@mock.patch.object(conductor_server.Conductor, "_get_grant_execute")
@mock.patch.object(test_nfvo_client.GrantRequest, "grants")
def test_change_ext_conn_grant(
self,
mock_grants,
mock_exec,
mock_vnf_by_id,
mock_get_lock,
mock_save,
mock_send_notification,
mock_change_vnf_status,
mock_update_vnf_attributes,
mock_update_instantiated_vnf_info_change_ext_conn):
cfg.CONF.set_override(
'base_url',
'http://127.0.0.1:9990/grant/v1/grants',
group='connect_grant')
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
mock_vnf_by_id.return_value = objects.VnfLcmOpOcc(
context=self.context, **lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
vnf_instance.instantiated_vnf_info = fakes.get_instantiated_vnf_info()
vnf_dict = {"before_error_point": 0}
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
vnf_virtual_link = (
vnf_instance.instantiated_vnf_info.vnf_virtual_link_resource_info)
# Test condition settings.
mock_exec.return_value = True
res_grant = dict()
res_grant['id'] = uuidsentinel.grant_id
res_grant['vnfInstanceId'] = vnf_instance.id
res_grant['vnfLcmOpOccId'] = vnf_lcm_op_occs_id
res_grant['updateResources'] = [
{
'resourceDefinitionId':
vnf_virtual_link[0].vnf_link_ports[0].id,
},
{
'resourceDefinitionId':
vnf_virtual_link[0].vnf_link_ports[1].id,
},
{
'resourceDefinitionId':
vnf_virtual_link[1].vnf_link_ports[0].id,
},
{
'resourceDefinitionId':
vnf_virtual_link[1].vnf_link_ports[2].id,
},
]
mock_grants.return_value = MockResponse(json_data=res_grant)
self.conductor.change_ext_conn(
self.context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id)
mock_change_vnf_status.assert_called_with(self.context,
mock.ANY, (constants.ACTIVE,),
constants.PENDING_CHANGE_EXT_CONN)
mock_update_vnf_attributes.assert_called_with(self.context,
mock.ANY, mock.ANY, mock.ANY, (constants.ACTIVE,))
self.assertEqual(
mock_send_notification.call_args[0][1].get('operationState'),
'PROCESSING')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_instantiated_vnf_info_change_ext_conn')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'.send_notification')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(conductor_server.Conductor, "_get_grant_execute")
@mock.patch.object(test_nfvo_client.GrantRequest, "grants")
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_change_ext_conn_grant_exception_http_error(
self,
mock_vnf_by_id,
mock_grants,
mock_exec,
mock_get_lock,
mock_save,
mock_send_notification,
mock_change_vnf_status,
mock_update_vnf_attributes,
mock_update_instantiated_vnf_info_change_ext_conn):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
mock_vnf_by_id.return_value = objects.VnfLcmOpOcc(
context=self.context, **lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
vnf_instance.instantiated_vnf_info = fakes.get_instantiated_vnf_info()
vnf_dict = {"before_error_point": 0}
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
# Test condition settings.
mock_exec.return_value = True
mock_grants.side_effect = (
requests.exceptions.HTTPError("MockException"))
self.assertRaises(requests.exceptions.HTTPError,
self.conductor.change_ext_conn,
self.context, vnf_instance, vnf_dict,
change_ext_conn_req, vnf_lcm_op_occs_id)
mock_change_vnf_status.assert_not_called()
mock_update_instantiated_vnf_info_change_ext_conn.assert_not_called()
self.assertEqual(
mock_send_notification.call_args[0][1].get('operationState'),
'ROLLED_BACK')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_instantiated_vnf_info_change_ext_conn')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'.send_notification')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(conductor_server.Conductor, "_get_grant_execute")
@mock.patch.object(test_nfvo_client.GrantRequest, "grants")
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_change_ext_conn_grant_exception_validation_error(
self,
mock_vnf_by_id,
mock_grants,
mock_exec,
mock_get_lock,
mock_save,
mock_send_notification,
mock_change_vnf_status,
mock_update_vnf_attributes,
mock_update_instantiated_vnf_info_change_ext_conn):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
mock_vnf_by_id.return_value = objects.VnfLcmOpOcc(
context=self.context, **lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
vnf_instance.instantiated_vnf_info = fakes.get_instantiated_vnf_info()
vnf_dict = {"before_error_point": 0}
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
# Test condition settings.
mock_exec.return_value = True
res_upd_resource = []
resource = {
'resourceDefinitionId': uuidsentinel.rsc_dummy,
'vimConnectionId': '1ffcd358-bee3-4bfb-bc3a-920db09f5da5',
}
res_upd_resource.append(resource)
res_grant = dict()
res_grant['id'] = uuidsentinel.grant_id
res_grant['vnfInstanceId'] = vnf_instance.id
res_grant['vnfLcmOpOccId'] = vnf_lcm_op_occs_id
res_grant['updateResources'] = []
res_grant['updateResources'].extend(res_upd_resource)
mock_grants.return_value = MockResponse(json_data=res_grant)
self.assertRaises(exceptions.ValidationError,
self.conductor.change_ext_conn,
self.context, vnf_instance, vnf_dict,
change_ext_conn_req, vnf_lcm_op_occs_id)
mock_change_vnf_status.assert_not_called()
mock_update_vnf_attributes.assert_not_called()
mock_update_instantiated_vnf_info_change_ext_conn.assert_not_called()
self.assertEqual(
mock_send_notification.call_args[0][1].get('operationState'),
'ROLLED_BACK')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_instantiated_vnf_info_change_ext_conn')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.utils._convert_desired_capacity')
@mock.patch('tacker.conductor.conductor_server.LOG')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_change_ext_conn_failed_with_exception(
self,
mock_vnf_by_id,
mock_log,
mock_des,
mock_vnfd_dict,
mock_vnf_lcm_subscriptions_get,
mock_get_lock,
mock_save,
mock_update_vnf_info_change_ext_conn,
mock_update_vnf_attributes,
mock_change_vnf_status):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
mock_vnf_by_id.return_value = (
objects.VnfLcmOpOcc(context=self.context,
**lcm_op_occs_data))
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = {"before_error_point": 0,
"current_error_point": 6}
m_vnf_lcm_subscriptions = (
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())])
mock_vnf_lcm_subscriptions_get.return_value = (
m_vnf_lcm_subscriptions)
mock_update_vnf_info_change_ext_conn.side_effect = Exception
self.conductor.change_ext_conn(
self.context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id)
mock_change_vnf_status.assert_called_with(self.context,
mock.ANY, mock.ANY, constants.ERROR, mock.ANY)
self.vnflcm_driver.change_ext_conn_vnf.assert_called_once_with(
self.context, vnf_instance, vnf_dict, change_ext_conn_req)
mock_update_vnf_info_change_ext_conn.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor'
'.send_notification')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_instantiated_vnf_info_change_ext_conn')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_ext_conn_grant')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_change_ext_conn_retry_error_point_1(
self,
mock_vnf_by_id,
mock_vnfd_dict,
mock_get_lock,
mock_save,
mock_change_ext_conn_grant,
mock_update_vnf_info_change_ext_conn,
mock_update_vnf_attributes,
mock_change_vnf_status,
mock_send_notification):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
mock_vnf_by_id.return_value = objects.VnfLcmOpOcc(
context=self.context, **lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
vnf_instance.instantiated_vnf_info = fakes.get_instantiated_vnf_info()
vnf_dict = {"before_error_point": 1}
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
self.conductor.change_ext_conn(
self.context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id)
self.vnflcm_driver.change_ext_conn_vnf.assert_called_once_with(
self.context, vnf_instance, vnf_dict, change_ext_conn_req)
mock_change_vnf_status.assert_called_with(self.context,
mock.ANY, (constants.ACTIVE,),
constants.PENDING_CHANGE_EXT_CONN)
self.assertEqual(mock_change_ext_conn_grant.call_count, 0)
mock_update_vnf_attributes.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'send_notification')
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_change_vnf_status')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_vnf_attributes')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._update_instantiated_vnf_info_change_ext_conn')
@mock.patch('tacker.conductor.conductor_server.Conductor'
'._change_ext_conn_grant')
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_change_ext_conn_retry_error_point_7(
self,
mock_vnf_by_id,
mock_vnfd_dict,
mock_get_lock,
mock_save,
mock_change_ext_conn_grant,
mock_update_vnf_info_change_ext_conn,
mock_update_vnf_attributes,
mock_change_vnf_status,
mock_send_notification):
lcm_op_occs_data = fakes.get_lcm_op_occs_data()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
mock_vnf_by_id.return_value = objects.VnfLcmOpOcc(
context=self.context, **lcm_op_occs_data)
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
vnf_instance.instantiation_state = fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
vnf_instance.instantiated_vnf_info = fakes.get_instantiated_vnf_info()
vnf_dict = {"before_error_point": 7}
change_ext_conn_req = fakes.get_change_ext_conn_request_obj()
self.conductor.change_ext_conn(
self.context,
vnf_instance,
vnf_dict,
change_ext_conn_req,
vnf_lcm_op_occs_id)
self.vnflcm_driver.change_ext_conn_vnf.assert_called_once_with(
self.context, vnf_instance, vnf_dict, change_ext_conn_req)
mock_change_vnf_status.assert_not_called()
self.assertEqual(mock_change_ext_conn_grant.call_count, 0)
mock_update_vnf_attributes.assert_called_once()

View File

@ -0,0 +1,178 @@
# 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
from tacker import context
from tacker import objects
from tacker.tests.unit import base
from tacker.tests import uuidsentinel
class ChangeExtConnRequestTestCase(base.TestCase):
def setUp(self):
super(ChangeExtConnRequestTestCase, self).setUp()
self.context = context.get_admin_context()
def _get_change_ext_conn_request(self):
ext_vl_info = [{
"id": "external_network",
"vim_connection_id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"resource_id": "dc67ee99-e963-44e2-a152-f0fb492eae76",
"ext_cps": [{
"cpd_id": "CP1",
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"mac_address": "fa:16:3e:0d:6f:71"},
"layer_protocol": "IP_OVER_ETHERNET"}]}]}, {
"cpd_id": "CP2",
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"ipaddresses": [{
"type": "IPV4",
"fixed_addresses": [
"10.0.0.1"],
"subnet_id":
"55f0fb3c-6a70-11eb-9439-0242ac130002"}]},
"layer_protocol": "IP_OVER_ETHERNET"}]}]}, {
"cpd_id": "CP3",
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"ipaddresses": [{
"type": "IPV4",
"num_dynamic_addresses": 1,
"subnet_id":
"3a7a37fc-6a92-11eb-9439-0242ac130002"}]},
"layer_protocol": "IP_OVER_ETHERNET"}]}]}, {
"cpd_id": "CP4",
"cp_config": [{
"cp_protocol_data": [{
"layer_protocol": "IP_OVER_ETHERNET"}],
"link_port_id":
"413f4e46-21cf-41b1-be0f-de8d23f76cfe"}]}],
"ext_link_ports": [{
"id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe",
"resource_handle": {
"resource_id": "67f7e772-0d31-4087-bf4c-2576fadcbdb7",
"vim_connection_id":
"6b0ff598-60d6-49b4-a907-a1111de52d92",
"vim_level_resource_type": "LINKPORT"}}], }]
vim_connection_info = [{
"id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"vim_id": uuidsentinel.vim_id,
"vim_type": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"interface_info": {
"endpoint": "endpoint_value"},
"access_info": {
"username": "username_value",
"password": "password_value",
"region": "region_value",
"tenant": "tenant_value"}}]
change_ext_conn_data = {
'ext_virtual_links': ext_vl_info,
'vim_connection_info': vim_connection_info,
'additional_params': {'key1': 'value1'}}
return change_ext_conn_data
def test_obj_from_primitive(self):
change_ext_conn_data = self._get_change_ext_conn_request()
change_ext_conn_req = objects.ChangeExtConnRequest.obj_from_primitive(
copy.deepcopy(change_ext_conn_data), self.context)
self._check_change_ext_conn_req(change_ext_conn_req,
change_ext_conn_data)
def _check_change_ext_conn_req(self, obj, data):
def _check_vim_connection_info(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VimConnectionInfo)
def _check_external_virtual_links(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.ExtVirtualLinkData)
self.assertEqual(obj.id, data.get('id'))
self.assertEqual(obj.vim_connection_id,
data.get('vim_connection_id'))
self.assertEqual(obj.resource_id,
data.get('resource_id'))
_check_ext_cps(obj.ext_cps,
data.get('ext_cps', []))
_check_ext_link_ports(obj.ext_link_ports,
data.get('ext_link_ports', []))
def _check_ext_cps(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VnfExtCpData)
self.assertEqual(obj.cpd_id, data.get('cpd_id'))
_check_ext_cp_config(obj.cp_config,
data.get('cp_config', []))
def _check_ext_cp_config(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VnfExtCpConfig)
self.assertEqual(obj.cp_instance_id,
data.get('cp_instance_id'))
self.assertEqual(obj.link_port_id,
data.get('link_port_id'))
_check_cp_protocol_data(obj.cp_protocol_data,
data.get('cp_protocol_data', []))
def _check_cp_protocol_data(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.CpProtocolData)
self.assertEqual(obj.layer_protocol,
data.get('layer_protocol'))
if obj.ip_over_ethernet or data.get('ip_over_ethernet', None):
_check_ip_over_ethernet(obj.ip_over_ethernet,
data.get('ip_over_ethernet', None))
def _check_ip_over_ethernet(obj, data):
self.assertIsInstance(obj, objects.IpOverEthernetAddressData)
self.assertEqual(obj.mac_address, data.get('mac_address'))
_check_ip_addresses(obj.ip_addresses,
data.get('ip_addressest', []))
def _check_ip_addresses(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.IpAddressReq)
self.assertEqual(obj.type, data.get('type'))
self.assertEqual(obj.subnet_id, data.get('subnet_id'))
self.assertEqual(obj.num_dynamic_addresses,
data.get('num_dynamic_addresses'))
def _check_ext_link_ports(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.ExtLinkPortData)
self.assertEqual(obj.id, data.get('id'))
self.assertIsInstance(obj.resource_handle,
objects.ResourceHandle)
self.assertIsInstance(obj, objects.ChangeExtConnRequest)
_check_external_virtual_links(obj.ext_virtual_links,
data.get('ext_virtual_links'))
_check_vim_connection_info(obj.vim_connection_info,
data.get('vim_connection_info'))

View File

@ -0,0 +1,251 @@
# 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
from tacker import context
from tacker import objects
from tacker.tests.unit import base
from tacker.tests import uuidsentinel
class GrantTestCase(base.TestCase):
def setUp(self):
super(GrantTestCase, self).setUp()
self.context = context.get_admin_context()
def _get_grant_data(self):
vim_connection_info = [{
"id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"vim_id": uuidsentinel.vim_id,
"vim_type": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"interface_info": {
"endpoint": "endpoint_value"},
"access_info": {
"username": "username_value",
"password": "password_value",
"region": "region_value",
"tenant": "tenant_value"}}]
grant_info = [{
"resource_definition_id":
"ff150558-6f45-11eb-9439-0242ac130002"}, {
"resource_definition_id":
"030df048-6f46-11eb-9439-0242ac130002"}]
vim_compute_resource_flavour = [{
"vim_connection_id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"vnfd_virtual_compute_desc_id":
"a813a1ea-6f47-11eb-9439-0242ac130002",
"vim_flavour_id": "c924aa12-b69d-41ee-91dd-7a26d92e6147"}]
vim_software_image = [{
"vim_connection_id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"vnfd_software_image_id": "49597d31-0aad-4c46-865d-ed7e60b3edc7",
"vim_software_image_id": "93738ce0-3ddb-4c13-9900-62b757561edd"}]
ext_vl_info = [{
"id": "external_network",
"vim_connection_id": "6b0ff598-60d6-49b4-a907-a1111de52d92",
"resource_id": "dc67ee99-e963-44e2-a152-f0fb492eae76",
"ext_cps": [{
"cpd_id": "CP1",
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"mac_address": "fa:16:3e:0d:6f:71"},
"layer_protocol": "IP_OVER_ETHERNET"}]}]}, {
"cpd_id": "CP2",
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"ipaddresses": [{
"type": "IPV4",
"fixed_addresses": [
"10.0.0.1"],
"subnet_id":
"55f0fb3c-6a70-11eb-9439-0242ac130002"}]},
"layer_protocol": "IP_OVER_ETHERNET"}]}]}, {
"cpd_id": "CP3",
"cp_config": [{
"cp_protocol_data": [{
"ip_over_ethernet": {
"ipaddresses": [{
"type": "IPV4",
"num_dynamic_addresses": 1,
"subnet_id":
"3a7a37fc-6a92-11eb-9439-0242ac130002"}]},
"layer_protocol": "IP_OVER_ETHERNET"}]}]}, {
"cpd_id": "CP4",
"cp_config": [{
"cp_protocol_data": [{
"layer_protocol": "IP_OVER_ETHERNET"}],
"link_port_id":
"413f4e46-21cf-41b1-be0f-de8d23f76cfe"}]}],
"ext_link_ports": [{
"id": "413f4e46-21cf-41b1-be0f-de8d23f76cfe",
"resource_handle": {
"resource_id": "67f7e772-0d31-4087-bf4c-2576fadcbdb7",
"vim_connection_id":
"6b0ff598-60d6-49b4-a907-a1111de52d92",
"vim_level_resource_type": "LINKPORT"}}], }]
grant_data = {
"id": "3affab40-6f28-11eb-9439-0242ac130002",
"vnf_instance_id": "4d62c7c2-6f28-11eb-9439-0242ac130002",
"vnf_lcm_op_occ_id": "6e426236-6f28-11eb-9439-0242ac130002",
"vim_connections": vim_connection_info,
"update_resources": grant_info,
"vim_assets": {
"compute_resource_flavours": vim_compute_resource_flavour,
"software_images": vim_software_image,
},
"ext_virtual_links": ext_vl_info,
"additional_params": {"key1": "value1"},
"_links": {
"self": {"href": ""},
"vnf_lcm_op_occ": {"href": ""},
"vnf_instance": {"href": ""},
}
}
return grant_data
def test_obj_from_primitive(self):
grant_data = self._get_grant_data()
grant = objects.Grant.obj_from_primitive(
copy.deepcopy(grant_data), self.context)
self._check_grant(grant, grant_data)
def _check_grant(self, obj, data):
def _check_vim_connection_info(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VimConnectionInfo)
def _check_update_resources(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.GrantInfo)
def _check_vim_assets(obj, data):
self.assertIsInstance(obj, objects.VimAssets)
_check_compute_resource_flavours(
obj.compute_resource_flavours,
data.get('compute_resource_flavours', []))
_check_software_images(obj.software_images,
data.get('software_images', []))
def _check_compute_resource_flavours(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj,
objects.VimComputeResourceFlavour)
self.assertEqual(obj.vim_connection_id,
data.get('vim_connection_id'))
self.assertEqual(obj.vnfd_virtual_compute_desc_id,
data.get('vnfd_virtual_compute_desc_id'))
self.assertEqual(obj.vim_flavour_id,
data.get('vim_flavour_id'))
def _check_software_images(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VimSoftwareImage)
self.assertEqual(obj.vim_connection_id,
data.get('vim_connection_id'))
self.assertEqual(obj.vnfd_software_image_id,
data.get('vnfd_software_image_id'))
self.assertEqual(obj.vim_software_image_id,
data.get('vim_software_image_id'))
def _check_external_virtual_links(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.ExtVirtualLinkData)
self.assertEqual(obj.id, data.get('id'))
self.assertEqual(obj.vim_connection_id,
data.get('vim_connection_id'))
self.assertEqual(obj.resource_id,
data.get('resource_id'))
_check_ext_cps(obj.ext_cps,
data.get('ext_cps', []))
_check_ext_link_ports(obj.ext_link_ports,
data.get('ext_link_ports', []))
def _check_ext_cps(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VnfExtCpData)
self.assertEqual(obj.cpd_id, data.get('cpd_id'))
_check_ext_cp_config(obj.cp_config,
data.get('cp_config', []))
def _check_ext_cp_config(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.VnfExtCpConfig)
self.assertEqual(obj.cp_instance_id,
data.get('cp_instance_id'))
self.assertEqual(obj.link_port_id,
data.get('link_port_id'))
_check_cp_protocol_data(obj.cp_protocol_data,
data.get('cp_protocol_data', []))
def _check_cp_protocol_data(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.CpProtocolData)
self.assertEqual(obj.layer_protocol,
data.get('layer_protocol'))
if obj.ip_over_ethernet or data.get('ip_over_ethernet', None):
_check_ip_over_ethernet(obj.ip_over_ethernet,
data.get('ip_over_ethernet', None))
def _check_ip_over_ethernet(obj, data):
self.assertIsInstance(obj, objects.IpOverEthernetAddressData)
self.assertEqual(obj.mac_address, data.get('mac_address'))
_check_ip_addresses(obj.ip_addresses,
data.get('ip_addresses', []))
def _check_ip_addresses(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.IpAddressReq)
self.assertEqual(obj.type, data.get('type'))
self.assertEqual(obj.subnet_id, data.get('subnet_id'))
self.assertEqual(obj.num_dynamic_addresses,
data.get('num_dynamic_addresses'))
def _check_ext_link_ports(_obj, _data):
self.assertEqual(len(_obj), len(_data))
for obj, data in zip(_obj, _data):
self.assertIsInstance(obj, objects.ExtLinkPortData)
self.assertEqual(obj.id, data.get('id'))
self.assertIsInstance(obj.resource_handle,
objects.ResourceHandle)
self.assertIsInstance(obj, objects.Grant)
self.assertEqual(obj.id, data.get('id'))
self.assertEqual(obj.vnf_instance_id, data.get('vnf_instance_id'))
self.assertEqual(obj.vnf_lcm_op_occ_id, data.get('vnf_lcm_op_occ_id'))
_check_vim_connection_info(obj.vim_connections,
data.get('vim_connections'))
_check_update_resources(obj.update_resources,
data.get('update_resources'))
_check_vim_assets(obj.vim_assets,
data.get('vim_assets', None))
_check_external_virtual_links(obj.ext_virtual_links,
data.get('ext_virtual_links'))

View File

@ -20,9 +20,11 @@ import os
import webob
from tacker.api.vnflcm.v1.router import VnflcmAPIRouter
from tacker.common import utils
from tacker import context
from tacker.db.db_sqlalchemy import models
from tacker import objects
from tacker.objects.change_ext_conn_req import ChangeExtConnRequest
from tacker.objects import fields
from tacker.objects.instantiate_vnf_req import ExtManagedVirtualLinkData
from tacker.objects.instantiate_vnf_req import ExtVirtualLinkData
@ -229,7 +231,10 @@ def _instantiated_vnf_links(vnf_instance_id):
"terminate": {"href": "/vnflcm/v1/vnf_instances/%s/terminate" %
vnf_instance_id},
"heal": {"href": "/vnflcm/v1/vnf_instances/%s/heal" %
vnf_instance_id}}
vnf_instance_id},
"changeExtConn": {"href":
"/vnflcm/v1/vnf_instances/%s/change_ext_conn" %
vnf_instance_id}}
return links
@ -1598,3 +1603,62 @@ def return_vnf_lcm_opoccs_list():
obj = objects.VnfLcmOpOcc(**vnf_lcm_op_occs)
return [obj]
def get_change_ext_conn_request_body():
change_ext_conn_req_body = {
"extVirtualLinks": [{
"id": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vimConnectionId": '2b3beeff-d4a1-4dc7-a1f8-066f92cfcb75',
"resourceId": 'e08f5e67-55de-4c4a-815b-cf3f1e2bae04',
"extCps": [{
"cpdId": 'VDU2_CP2',
"cpConfig": [{
"cpInstanceId": '924d0ea7-786d-468b-bf45-65bfd483ee79',
"linkPortId": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"cpProtocolData": [{
"layerProtocol": 'IP_OVER_ETHERNET',
"ipOverEthernet": {
"macAddress":
'fa:16:3e:11:11:11',
"ipAddresses": [{
"type": "IPV4",
"fixedAddresses": ["22.22.1.20"],
"subnetId":
'497b7a75-6c10-4a74-85fa-83d498da2501'
}]
}
}]
}]
}],
"extLinkPorts": [{
"id": 'decd78d2-993c-4112-9a8f-1ad54cade4d7',
"resourceHandle": {
"resourceId": 'cb602960-05ee-4e03-8fe2-ea0b64e08332',
"vimConnectionId": '2b3beeff-d4a1-4dc7-a1f8-066f92cfcb75',
"vimLevelResourceType":
'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
}
}],
"vimConnectionInfo": [{
"id": '2b3beeff-d4a1-4dc7-a1f8-066f92cfcb75',
"vimId": 'f8c35bd0-4d67-4436-9f11-14b8a84c92aa',
"vimType": 'openstack',
"interfaceInfo": {"key1": 'value1', "key2": 'value2'},
"accessInfo": {"key1": 'value1', "key2": 'value2'}
}],
}]
}
return change_ext_conn_req_body
def get_change_ext_conn_request_obj():
"""Return ChangeExtConnRequest Object
obj_from_primitive() needs snake_case dictionary
"""
body = utils.convert_camelcase_to_snakecase(
get_change_ext_conn_request_body())
return ChangeExtConnRequest.obj_from_primitive(
body, context)

View File

@ -3554,3 +3554,152 @@ class TestController(base.TestCase):
mock_lcm_get_by_id.assert_called_once()
mock_vnf_get_by_id.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "change_ext_conn")
def test_change_ext_conn(self, mock_rpc, mock_save,
mock_vnf_by_id, mock_get_vnf,
mock_notification_process,
mock_get_service_plugins):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
body = fakes.get_change_ext_conn_request_body()
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/change_ext_conn' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_rpc.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_change_ext_conn_incorrect_instantiated_state(
self, mock_vnf_by_id, mock_get_vnf,
mock_get_service_plugins):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.NOT_INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
body = fakes.get_change_ext_conn_request_body()
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/change_ext_conn' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("VNF is not instantiated")
self.assertEqual(expected_msg, resp.json['detail'])
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@ddt.data('HEAD', 'PUT', 'DELETE', 'PATCH', 'GET')
def test_change_ext_conn_invalid_http_method(self, method,
mock_get_service_plugins):
body = {}
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/change_ext_conn' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = method
resp = req.get_response(self.app)
self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "change_ext_conn")
@ddt.data(
{
"type": "IPV4",
},
{
"type": "IPV4",
"fixedAddresses": ["22.22.1.20"],
"numDynamicAddresses": 1,
"subnetId": '497b7a75-6c10-4a74-85fa-83d498da2501',
},
{
"type": "IPV4",
"numDynamicAddresses": 0,
"subnetId": '497b7a75-6c10-4a74-85fa-83d498da2501',
},
)
def test_change_ext_conn_with_invalid_requests(
self,
ip_address,
mock_rpc,
mock_save,
mock_vnf_by_id,
mock_get_vnf,
mock_notification_process,
mock_get_service_plugins):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
body = fakes.get_change_ext_conn_request_body()
body['extVirtualLinks'][0]['extCps'][0]['cpConfig'][0][
'cpProtocolData'][0]['ipOverEthernet'][
'ipAddresses'] = [ip_address]
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/change_ext_conn' % uuidsentinel.vnf_instance_id)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(
resp.status_code, http_client.BAD_REQUEST)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM':
test_nfvo_plugin.FakeVNFMPlugin()})
def test_change_ext_conn_with_invalid_uuid(self, mock_get_service_plugins):
body = fakes.get_change_ext_conn_request_body()
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/change_ext_conn' % constants.INVALID_UUID)
req.body = jsonutils.dump_as_bytes(body)
req.headers['Content-Type'] = 'application/json'
req.method = 'POST'
resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual(
"Can not find requested vnf: %s" % constants.INVALID_UUID,
resp.json['itemNotFound']['message'])

View File

@ -136,6 +136,14 @@ class FakeDriverManager(mock.Mock):
raise InfraDriverException("post_heal_vnf failed")
if 'get_rollback_ids' in args:
return [], [], ""
if 'change_ext_conn_vnf' in args:
if self.fail_method_name and \
self.fail_method_name == 'change_ext_conn_vnf':
raise InfraDriverException("change_ext_conn_vnf failed")
elif 'change_ext_conn_vnf_wait' in args:
if self.fail_method_name and \
self.fail_method_name == 'change_ext_conn_vnf_wait':
raise InfraDriverException("change_ext_conn_vnf_wait failed")
class FakeVimClient(mock.Mock):
@ -2900,3 +2908,348 @@ class TestVnflcmDriver(db_base.SqlTestCase):
vnf_instance,
operation_params)
self.assertEqual(1, mock_lcm_save.call_count)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf(self, mock_vnf_interfaces, mock_vnfd_dict,
mock_log, mock_save, mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 0, 'grant': None}
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.change_ext_conn_vnf(self.context,
vnf_instance,
vnf_dict,
change_ext_conn_vnf_req)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for changing external "
"connectivity vnf '%s' is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_fail(self, mock_vnf_interfaces,
mock_vnfd_dict, mock_log, mock_save, mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 0, 'grant': None}
self._mock_vnf_manager(fail_method_name='change_ext_conn_vnf')
driver = vnflcm_driver.VnfLcmDriver()
self.assertRaises(exceptions.VnfChangeExtConnFailed,
driver.change_ext_conn_vnf, self.context, vnf_instance,
vnf_dict, change_ext_conn_vnf_req)
self.assertEqual(2, self._vnf_manager.invoke.call_count)
self.assertEqual(fields.VnfInstanceTaskState.ERROR,
vnf_instance.task_state)
expected_msg = ("Failed to change external connectivity "
"vnf %(id)s in infra driver. "
"Error: %(error)s")
mock_log.error.assert_called_with(expected_msg,
{'id': vnf_instance.id, 'error': 'change_ext_conn_vnf failed'})
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_wait_fail(self, mock_vnf_interfaces,
mock_vnfd_dict, mock_log, mock_save, mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 0, 'grant': None}
vnf_instance.instantiated_vnf_info.instance_id =\
uuidsentinel.instance_id
self._mock_vnf_manager(fail_method_name='change_ext_conn_vnf_wait')
driver = vnflcm_driver.VnfLcmDriver()
self.assertRaises(exceptions.VnfChangeExtConnWaitFailed,
driver.change_ext_conn_vnf, self.context, vnf_instance,
vnf_dict, change_ext_conn_vnf_req)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(
fields.VnfInstanceTaskState.ERROR,
vnf_instance.task_state)
expected_msg = ('Failed to update vnf %(id)s resources for '
'instance %(instance)s. Error: %(error)s')
mock_log.error.assert_called_with(expected_msg,
{'id': vnf_instance.id,
'instance': vnf_instance.instantiated_vnf_info.instance_id,
'error': 'change_ext_conn_vnf_wait failed'})
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_retry_error_point_2(
self,
mock_vnf_interfaces,
mock_vnfd_dict,
mock_log,
mock_save,
mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 2, 'grant': None}
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.change_ext_conn_vnf(self.context,
vnf_instance,
vnf_dict,
change_ext_conn_vnf_req)
self.assertEqual(4, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for changing external "
"connectivity vnf '%s' is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_retry_error_point_3(
self,
mock_vnf_interfaces,
mock_vnfd_dict,
mock_log,
mock_save,
mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 3, 'grant': None}
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.change_ext_conn_vnf(self.context,
vnf_instance,
vnf_dict,
change_ext_conn_vnf_req)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for changing external "
"connectivity vnf '%s' is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_retry_error_point_4(
self,
mock_vnf_interfaces,
mock_vnfd_dict,
mock_log,
mock_save,
mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 4, 'grant': None}
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.change_ext_conn_vnf(self.context,
vnf_instance,
vnf_dict,
change_ext_conn_vnf_req)
self.assertEqual(3, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for changing external "
"connectivity vnf '%s' is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_retry_error_point_5(
self,
mock_vnf_interfaces,
mock_vnfd_dict,
mock_log,
mock_save,
mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 5, 'grant': None}
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.change_ext_conn_vnf(self.context,
vnf_instance,
vnf_dict,
change_ext_conn_vnf_req)
self.assertEqual(1, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for changing external "
"connectivity vnf '%s' is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch.object(VnfLcmDriver,
'_init_mgmt_driver_hash')
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch('tacker.vnflcm.vnflcm_driver.LOG')
@mock.patch('tacker.vnflcm.utils._get_vnfd_dict')
@mock.patch('tacker.vnflcm.vnflcm_driver.VnfLcmDriver.'
'_load_vnf_interface')
def test_change_ext_conn_vnf_retry_error_point_6(
self,
mock_vnf_interfaces,
mock_vnfd_dict,
mock_log,
mock_save,
mock_init_hash,
mock_get_service_plugins):
mock_init_hash.return_value = {
"vnflcm_noop": "ffea638bfdbde3fb01f191bbe75b031859"
"b18d663b127100eb72b19eecd7ed51"
}
mock_vnf_interfaces.return_value = fakes.return_vnf_interfaces()
change_ext_conn_vnf_req = objects.ChangeExtConnRequest(
vim_connection_info=[],
ext_virtual_links=[],
additional_params={})
vnf_instance = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
vnf_dict = {'before_error_point': 6, 'grant': None}
self._mock_vnf_manager()
driver = vnflcm_driver.VnfLcmDriver()
driver.change_ext_conn_vnf(self.context,
vnf_instance,
vnf_dict,
change_ext_conn_vnf_req)
self.assertEqual(1, self._vnf_manager.invoke.call_count)
self.assertEqual(None, vnf_instance.task_state)
expected_msg = ("Request received for changing external "
"connectivity vnf '%s' is completed successfully")
mock_log.info.assert_called_with(expected_msg,
vnf_instance.id)

View File

@ -220,16 +220,84 @@ def get_vnfc_resource_info(vdu_id="VDU1", storage_resource_ids=None,
return vnfc_resource_info
def _get_ext_link_port(ext_vl_port_id, cp_instance_id,
set_resource_id=False):
if set_resource_id:
resource_id = uuidsentinel.ext_virtual_link_port_resource_id
else:
resource_id = ""
resource_handle = objects.ResourceHandle(
resource_id=resource_id,
vim_level_resource_type="OS::Neutron::Port")
ext_vl_port = objects.ExtLinkPortInfo(
id=ext_vl_port_id, cp_instance_id=cp_instance_id,
resource_handle=resource_handle)
return ext_vl_port
def get_ext_virtual_link_info(ext_virtual_link_id, desc_id="externalVL1",
set_resource_id=True):
network_resource = objects.ResourceHandle(
resource_id=uuidsentinel.virtual_link_resource_id,
vim_level_resource_type="OS::Neutron::Network")
ext_vl_link_port = _get_ext_link_port(
uuidsentinel.ext_vl_port_id,
cp_instance_id=uuidsentinel.cp_instance_id,
set_resource_id=set_resource_id)
ext_vl_info = objects.ExtVirtualLinkInfo(
id=uuidsentinel.ext_virtual_link_id,
resource_handle=network_resource,
ext_link_ports=[ext_vl_link_port])
return ext_vl_info
def get_ext_cp_info(ext_cp_id, cpd_id='VDU1_CP1', ip_addresses=[]):
ip_over_ethernet = objects.IpOverEthernetAddressInfo(
ip_addresses=ip_addresses)
cp_protocol_info = objects.CpProtocolInfo(
layer_protocol="IP_OVER_ETHERNET",
ip_over_ethernet=ip_over_ethernet)
ext_cp_info = objects.VnfExtCpInfo(
id=ext_cp_id,
cpd_id=cpd_id,
cp_protocol_info=[cp_protocol_info])
return ext_cp_info
def get_ip_address(ip_type='IPV4', subnet_id=None, is_dynamic=False,
addresses=[]):
ip_address = objects.IpAddress(type=ip_type,
subnet_id=subnet_id,
is_dynamic=is_dynamic,
addresses=addresses)
return ip_address
def get_vnf_instantiated_info(flavour_id='simple',
instantiation_level_id=None, vnfc_resource_info=None,
virtual_storage_resource_info=None,
vnf_virtual_link_resource_info=None,
ext_managed_virtual_link_info=None):
ext_managed_virtual_link_info=None,
ext_virtual_link_info=None,
ext_cp_info=None):
vnfc_resource_info = vnfc_resource_info or []
vnf_virtual_link_resource_info = vnf_virtual_link_resource_info or []
virtual_storage_resource_info = virtual_storage_resource_info or []
ext_managed_virtual_link_info = ext_managed_virtual_link_info or []
ext_virtual_link_info = ext_virtual_link_info or []
ext_cp_info = ext_cp_info or []
inst_vnf_info = objects.InstantiatedVnfInfo(flavour_id=flavour_id,
instantiation_level_id=instantiation_level_id,
@ -237,7 +305,9 @@ def get_vnf_instantiated_info(flavour_id='simple',
vnfc_resource_info=vnfc_resource_info,
vnf_virtual_link_resource_info=vnf_virtual_link_resource_info,
virtual_storage_resource_info=virtual_storage_resource_info,
ext_managed_virtual_link_info=ext_managed_virtual_link_info)
ext_managed_virtual_link_info=ext_managed_virtual_link_info,
ext_virtual_link_info=ext_virtual_link_info,
ext_cp_info=ext_cp_info)
return inst_vnf_info
@ -332,6 +402,162 @@ def get_grant_response_dict():
return grant_response_dict
def get_change_ext_conn_request():
def _get_ip_addresses(_type='IPV4', fixed_addrs=[], subnet_id=None):
if fixed_addrs and subnet_id:
return [{
"type": _type,
"fixed_addresses": fixed_addrs,
"subnet_id": subnet_id,
}]
elif fixed_addrs and not subnet_id:
return [{
"type": _type,
"fixed_addresses": fixed_addrs,
}]
elif not fixed_addrs and subnet_id:
return [{
"type": _type,
"num_dynamic_addresses": 1,
"subnet_id": subnet_id,
}]
elif not fixed_addrs and not subnet_id:
return [{
"type": _type,
"num_dynamic_addresses": 1,
}]
def _get_ext_cp_info(cpd_id, ip_address):
return {
"cpd_id": cpd_id,
"cp_config": [{
"cp_protocol_data": [{
"layer_protocol": "IP_OVER_ETHERNET",
"ip_over_ethernet": {
"ip_addresses": ip_address
}
}]
}]
}
def _get_request():
ext_vl_info = [{
"id": "external_network_1",
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": "nw-resource-id-1",
"ext_cps": [
_get_ext_cp_info('VDU1_CP1',
_get_ip_addresses(fixed_addrs=["20.0.0.1"])),
_get_ext_cp_info('VDU1_CP2',
_get_ip_addresses(
fixed_addrs=["30.0.0.2"],
subnet_id="changed-subnet-id-1")),
_get_ext_cp_info('VDU1_CP3',
_get_ip_addresses(fixed_addrs=["10.0.0.1"])),
]}, {
"id": "external_network_2",
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": "changed-nw-resource-id-2",
"ext_cps": [
_get_ext_cp_info('VDU2_CP1',
_get_ip_addresses(
subnet_id="changed-subnet-id-2")),
_get_ext_cp_info('VDU2_CP2', _get_ip_addresses())
]
}]
vim_connection_info = [{
"id": uuidsentinel.vim_connection_id,
"vim_id": uuidsentinel.vim_id,
"vim_type": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"interface_info": {
"endpoint": "endpoint_value"},
"access_info": {
"username": "username_value",
"password": "password_value",
"region": "region_value",
"tenant": "tenant_value"}}]
change_ext_conn_data = {
'ext_virtual_links': ext_vl_info,
'vim_connection_info': vim_connection_info,
'additional_params': {'key1': 'value1'}}
return change_ext_conn_data
change_ext_conn_data = _get_request()
change_ext_conn_req = objects.ChangeExtConnRequest.obj_from_primitive(
change_ext_conn_data, None)
return change_ext_conn_req
def get_original_stack_param():
stack_param = \
{'nfv': {
'VDU': {
'VDU1': {'flavor': 'm1.tiny', 'image': 'None'},
'VirtualStorage': {
'flavor': 'None',
'image': 'cirros-0.4.0-x86_64-disk'},
'VDU2': {
'flavor': 'm1.tiny',
'image': 'cirros-0.4.0-x86_64-disk'}},
'CP': {
'VDU1_CP1': {
'network': 'nw-resource-id-1',
'fixed_ips': [{'ip_address': '10.0.0.1'}]},
'VDU1_CP2': {
'network': 'nw-resource-id-1',
'fixed_ips': [{
'ip_address': '10.0.0.2',
'subnet': 'subnet-id-2'}]},
'VDU2_CP1': {
'network': 'nw-resource-id-2',
'fixed_ips': [{
'subnet': 'subnet-id-2'}]},
'VDU2_CP2': {'network': 'nw-resource-id-2'}}}}
return stack_param
def get_expect_stack_param():
stack_param = \
{'nfv': {
'VDU': {
'VDU1': {'flavor': 'm1.tiny', 'image': 'None'},
'VirtualStorage': {
'flavor': 'None',
'image': 'cirros-0.4.0-x86_64-disk'},
'VDU2': {
'flavor': 'm1.tiny',
'image': 'cirros-0.4.0-x86_64-disk'}},
'CP': {
'VDU1_CP1': {
'network': 'nw-resource-id-1',
'fixed_ips': [{'ip_address': '20.0.0.1'}]},
'VDU1_CP2': {
'network': 'nw-resource-id-1',
'fixed_ips': [{
'ip_address': '30.0.0.2',
'subnet': 'changed-subnet-id-1'}]},
'VDU2_CP1': {
'network': 'changed-nw-resource-id-2',
'fixed_ips': [{
'subnet': 'changed-subnet-id-2'}]},
'VDU2_CP2': {'network': 'changed-nw-resource-id-2'}}}}
return stack_param
def get_vnf_attribute_dict():
vnf_attribute_dict = dict()
vnf_attribute_dict.update(
{'stack_param': str(get_original_stack_param())})
return vnf_attribute_dict
def get_lcm_op_occs_object(operation="INSTANTIATE",
error_point=0):
vnf_lcm_op_occs = objects.VnfLcmOpOcc(

View File

@ -2178,3 +2178,126 @@ class TestOpenStack(base.FixturedTestCase):
None, self.context, vnf_dict, 'SP1', None, None)
self.assertEqual('30435eb8-1472-4cbc-abbe-00b395165ce7', grp_id)
@mock.patch('tacker.common.clients.OpenstackClients')
@mock.patch('tacker.vnflcm.utils.get_base_nest_hot_dict')
def test_change_ext_conn_vnf(self,
mock_get_base_hot_dict,
mock_mock_OpenstackClients_heat):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
nested_hot_dict = {'parameters': {'vnf': 'test'}}
mock_get_base_hot_dict.return_value = \
self._read_file(), nested_hot_dict
vnf_dict['vnfd'] = fd_utils.get_vnfd_dict()
vnf_dict['attributes'] = fd_utils.get_vnf_attribute_dict()
vim_connection_info = fd_utils.get_vim_connection_info_object()
change_ext_conn_request = fd_utils.get_change_ext_conn_request()
self.openstack.change_ext_conn_vnf(
self.context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_request)
self.assertEqual(
str(fd_utils.get_expect_stack_param()),
vnf_dict['attributes']['stack_param'])
def test_change_ext_conn_vnf_wait(self):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
# Mock various heat APIs that will be called by heatclient
# during the process of change_ext_conn_vnf_wait.
self._response_in_wait_until_stack_ready(["UPDATE_IN_PROGRESS",
"UPDATE_COMPLETE"])
stack = self.openstack.change_ext_conn_vnf_wait(
self.context, vnf_instance, vim_connection_info)
self.assertEqual('UPDATE_COMPLETE', stack.stack_status)
def test_change_ext_conn_vnf_wait_fail(self):
inst_vnf_info = fd_utils.get_vnf_instantiated_info()
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
# Mock various heat APIs that will be called by heatclient
# during the process of change_ext_conn_vnf_wait.
self._response_in_wait_until_stack_ready(["UPDATE_IN_PROGRESS"])
self.openstack.STACK_RETRIES = 1
result = self.assertRaises(vnfm.VNFChangeExtConnWaitFailed,
self.openstack.change_ext_conn_vnf_wait, self.context,
vnf_instance, vim_connection_info)
expected_msg = ("VNF ChangeExtConn action is not completed within 10 "
"seconds ""on stack %s") % inst_vnf_info.instance_id
self.assertIn(expected_msg, str(result))
def test_post_change_ext_conn_vnf(self):
v_s_resource_info = fd_utils.get_virtual_storage_resource_info(
desc_id="storage1", set_resource_id=False)
storage_resource_ids = [v_s_resource_info.id]
vnfc_resource_info = fd_utils.get_vnfc_resource_info(vdu_id="VDU_VNF",
storage_resource_ids=storage_resource_ids, set_resource_id=False)
v_l_resource_info = fd_utils.get_virtual_link_resource_info(
vnfc_resource_info.vnfc_cp_info[0].vnf_link_port_id,
vnfc_resource_info.vnfc_cp_info[0].id)
inst_vnf_info = fd_utils.get_vnf_instantiated_info(
virtual_storage_resource_info=[v_s_resource_info],
vnf_virtual_link_resource_info=[v_l_resource_info],
vnfc_resource_info=[vnfc_resource_info])
vnf_instance = fd_utils.get_vnf_instance_object(
instantiated_vnf_info=inst_vnf_info)
vim_connection_info = fd_utils.get_vim_connection_info_object()
resources = [{'resource_name': vnfc_resource_info.vdu_id,
'resource_type': vnfc_resource_info.compute_resource.
vim_level_resource_type,
'physical_resource_id': uuidsentinel.vdu_resource_id},
{'resource_name': v_s_resource_info.virtual_storage_desc_id,
'resource_type': v_s_resource_info.storage_resource.
vim_level_resource_type,
'physical_resource_id': uuidsentinel.storage_resource_id},
{'resource_name': vnfc_resource_info.vnfc_cp_info[0].cpd_id,
'resource_type': inst_vnf_info.vnf_virtual_link_resource_info[0].
vnf_link_ports[0].resource_handle.vim_level_resource_type,
'physical_resource_id': uuidsentinel.cp1_resource_id}]
self._responses_in_stack_list(inst_vnf_info.instance_id,
resources=resources)
self.openstack.post_change_ext_conn_vnf(
self.context, vnf_instance, vim_connection_info)
self.assertEqual(vnf_instance.instantiated_vnf_info.
vnfc_resource_info[0].metadata['stack_id'],
inst_vnf_info.instance_id)
# Check if vnfc resource "VDU_VNF" is set with resource_id
self.assertEqual(uuidsentinel.vdu_resource_id,
vnf_instance.instantiated_vnf_info.vnfc_resource_info[0].
compute_resource.resource_id)
# Check if virtual storage resource "storage1" is set with resource_id
self.assertEqual(uuidsentinel.storage_resource_id,
vnf_instance.instantiated_vnf_info.
virtual_storage_resource_info[0].storage_resource.resource_id)
# Check if virtual link port "CP1" is set with resource_id
self.assertEqual(uuidsentinel.cp1_resource_id,
vnf_instance.instantiated_vnf_info.
vnf_virtual_link_resource_info[0].vnf_link_ports[0].
resource_handle.resource_id)

View File

@ -562,6 +562,22 @@ def _build_instantiated_vnf_info(vnfd_dict, instantiate_vnf_req,
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):
"""Read the node templates and prepare VDU data in below format
@ -729,6 +745,40 @@ def _build_vnf_virtual_link_resource_info(node_templates, instantiate_vnf_req,
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 = []
@ -831,6 +881,34 @@ def _set_ext_cp_info(instantiate_vnf_req, inst_vnf_info=None):
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
@ -926,6 +1004,38 @@ def _set_ext_virtual_link_info(instantiate_vnf_req, ext_cp_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 = []
@ -1152,3 +1262,54 @@ def get_target_vdu_def_dict(extract_policy_infos, aspect_id, tosca):
vdu_def_dict[node_name] = node_value
return vdu_def_dict
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

View File

@ -1742,3 +1742,101 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
vnf_instance,
operation_params,
vim_connection_info)
def _change_ext_conn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req):
inst_vnf_info = vnf_instance.instantiated_vnf_info
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'change_ext_conn_vnf',
context=context, vnf_instance=vnf_instance, vnf_dict=vnf_dict,
vim_connection_info=vim_connection_info,
change_ext_conn_req=change_ext_conn_req)
except Exception as exp:
with excutils.save_and_reraise_exception() as exc_ctxt:
exc_ctxt.reraise = False
LOG.error("Failed to change external connectivity "
"vnf %(id)s in infra driver. "
"Error: %(error)s", {"id": vnf_instance.id, "error":
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfChangeExtConnFailed(id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
vnf_dict['current_error_point'] = fields.ErrorPoint.POST_VIM_CONTROL
try:
self._vnf_manager.invoke(
vim_connection_info.vim_type, 'change_ext_conn_vnf_wait',
context=context, vnf_instance=vnf_instance,
vim_connection_info=vim_connection_info)
except Exception as exp:
LOG.error("Failed to update vnf %(id)s resources for instance "
"%(instance)s. Error: %(error)s",
{'id': vnf_instance.id, 'instance':
inst_vnf_info.instance_id, 'error':
encodeutils.exception_to_unicode(exp)})
raise exceptions.VnfChangeExtConnWaitFailed(
id=vnf_instance.id,
error=encodeutils.exception_to_unicode(exp))
@log.log
@revert_to_error_task_state
def change_ext_conn_vnf(
self,
context,
vnf_instance,
vnf_dict,
change_ext_conn_req):
LOG.info("Request received for changing external connectivity "
"vnf '%s'", vnf_instance.id)
vnfd_dict = vnflcm_utils._get_vnfd_dict(
context, vnf_instance.vnfd_id,
vnf_instance.instantiated_vnf_info.flavour_id)
vnf_dict['current_error_point'] = EP.VNF_CONFIG_START
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_START:
# TODO(esto-aln): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'change_external_connectivity_start',
vnf_instance, vnfd_dict),
'change_external_connectivity_start', context=context,
vnf_instance=vnf_instance,
change_ext_conn_request=change_ext_conn_req,
grant=vnf_dict.get('grant'), grant_request=None)
vnf_dict['current_error_point'] = EP.PRE_VIM_CONTROL
if vnf_dict['before_error_point'] <= EP.POST_VIM_CONTROL:
vim_info = vnflcm_utils._get_vim(context,
vnf_instance.vim_connection_info)
vim_connection_info = \
objects.VimConnectionInfo.obj_from_primitive(
vim_info, context)
self._change_ext_conn_vnf(context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req)
# Since there is no processing corresponding to
# EP.INTERNAL_PROCESSING, it transitions to EP.VNF_CONFIG_END.
vnf_dict['current_error_point'] = EP.VNF_CONFIG_END
if vnf_dict['before_error_point'] <= EP.VNF_CONFIG_END:
# TODO(esto-aln): grant_request here is planned to pass
# as a parameter, however due to grant_request are not
# passed from conductor to vnflcm_driver, thus we put Null
# value to grant and grant_reqeust temporary.
# This part will be updated in next release.
self._mgmt_manager.invoke(
self._load_vnf_interface(
context, 'change_external_connectivity_end',
vnf_instance, vnfd_dict),
'change_external_connectivity_end', context=context,
vnf_instance=vnf_instance,
change_ext_conn_request=change_ext_conn_req,
grant=vnf_dict.get('grant'), grant_request=None)
LOG.info("Request received for changing external connectivity "
"vnf '%s' is completed successfully", vnf_instance.id)

View File

@ -131,3 +131,35 @@ class VnfAbstractDriver(extensions.PluginInterface, metaclass=abc.ABCMeta):
parameters passed in the heal request
"""
pass
@abc.abstractmethod
def change_ext_conn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req):
"""Change external VNF connectivity
:param context: A RequestContext
:param vnf_instance: tacker.objects.VnfInstance to be changed
:param vnf_dict:
:param vim_connection_info: Credentials to initialize Vim connection
:param change_ext_conn_req: tacker.objects.ChangeExtconnRequest object
containing parameters passed in the
change_ext_conn request
"""
pass
@abc.abstractmethod
def change_ext_conn_vnf_wait(self, context, vnf_instance,
vim_connection_info):
"""Check vnf external connnectivity is changed successfully"""
pass
@abc.abstractmethod
def post_change_ext_conn_vnf(self, context, vnf_instance,
vim_connection_info):
"""Update resource information for each external VL/LINKPORT resources
:param context: A RequestContext
:param vnf_instance: tacker.objects.VnfInstance to be changed
:param vim_connection_info: Credentials to initialize Vim connection
"""
pass

View File

@ -2030,6 +2030,18 @@ class Kubernetes(abstract_driver.VnfAbstractDriver,
finally:
self.clean_authenticate_vim(auth_cred, file_descriptor)
def change_ext_conn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req):
raise NotImplementedError()
def change_ext_conn_vnf_wait(self, context, vnf_instance,
vim_connection_info):
raise NotImplementedError()
def post_change_ext_conn_vnf(self, context, vnf_instance,
vim_connection_info):
raise NotImplementedError()
def get_scale_ids(self,
plugin,
context,

View File

@ -104,3 +104,15 @@ class VnfNoop(abstract_driver.VnfAbstractDriver):
def post_heal_vnf(self, context, vnf_instance, vim_connection_info,
heal_vnf_request):
pass
def change_ext_conn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req):
pass
def change_ext_conn_vnf_wait(self, context, vnf_instance,
vim_connection_info):
pass
def post_change_ext_conn_vnf(self, context, vnf_instance,
vim_connection_info):
pass

View File

@ -996,21 +996,6 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
vnfd_dict['instance_id'] = instance_id
return instance_id
@log.log
def post_vnf_instantiation(self, context, vnf_instance,
vim_connection_info, instantiate_vnf_req):
inst_vnf_info = vnf_instance.instantiated_vnf_info
access_info = vim_connection_info.access_info
heatclient = hc.HeatClient(access_info,
region_name=access_info.get('region'))
stack_resources = self._get_stack_resources(
inst_vnf_info.instance_id, heatclient)
self._update_vnfc_resources(vnf_instance, stack_resources,
vim_connection_info)
self._update_vnfc_info(vnf_instance)
def _update_resource_handle(self, vnf_instance, resource_handle,
stack_resources, resource_name,
vim_connection_info):
@ -2033,3 +2018,101 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
return_rs_list = before_rs_list
return return_list, return_rs_list, grp.physical_resource_id
@log.log
def change_ext_conn_vnf(self, context, vnf_instance, vnf_dict,
vim_connection_info, change_ext_conn_req):
base_hot_dict, nested_hot_dict = \
vnflcm_utils.get_base_nest_hot_dict(
context,
vnf_instance.instantiated_vnf_info.flavour_id,
vnf_instance.vnfd_id)
stack_param = yaml.safe_load(
vnf_dict['attributes']['stack_param'])
LOG.debug('before stack_param: {}'.format(stack_param))
cp_param = stack_param['nfv']['CP']
for ext_virtual_link in change_ext_conn_req.ext_virtual_links:
for ext_cp in ext_virtual_link.ext_cps:
if ext_cp.cpd_id not in cp_param.keys():
continue
cpd_id = ext_cp.cpd_id
network = cp_param[cpd_id].get('network', None)
if network and network != ext_virtual_link.resource_id:
cp_param[cpd_id].update(
dict(network=ext_virtual_link.resource_id))
try:
ip_addr = ext_cp.cp_config[0].cp_protocol_data[0].\
ip_over_ethernet.ip_addresses[0]
except IndexError:
# If the element under ext_cp does not exist,
# and ip_addresses cannot get, do nothing
continue
fixed_ips = dict()
updated_fixed_ips = []
if ip_addr.fixed_addresses:
for address in ip_addr.fixed_addresses:
fixed_ips = dict(ip_address=address)
if ip_addr.subnet_id:
fixed_ips.update(dict(subnet=ip_addr.subnet_id))
updated_fixed_ips.append(fixed_ips)
elif ip_addr.num_dynamic_addresses > 0:
if ip_addr.subnet_id:
fixed_ips.update(dict(subnet=ip_addr.subnet_id))
updated_fixed_ips.append(fixed_ips)
if updated_fixed_ips:
cp_param[cpd_id].update(
dict(fixed_ips=updated_fixed_ips))
LOG.debug('after stack_param: {}'.format(stack_param))
access_info = vim_connection_info.access_info
heatclient = hc.HeatClient(access_info,
region_name=access_info.get('region'))
# Update heat-stack with BaseHOT and parameters
self._update_stack_with_user_data(
heatclient, vnf_instance, base_hot_dict, nested_hot_dict,
stack_param, vnf_instance.instantiated_vnf_info.instance_id)
vnf_dict['attributes'].update({'stack_param': str(stack_param)})
@log.log
def change_ext_conn_vnf_wait(self, context, vnf_instance,
vim_connection_info):
access_info = vim_connection_info.access_info
region_name = access_info.get('region')
inst_vnf_info = vnf_instance.instantiated_vnf_info
stack = self._wait_until_stack_ready(inst_vnf_info.instance_id,
access_info, infra_cnst.STACK_UPDATE_IN_PROGRESS,
infra_cnst.STACK_UPDATE_COMPLETE,
vnfm.VNFChangeExtConnWaitFailed,
region_name=region_name)
return stack
def _update_vnfc_resources_and_info(self, context, vnf_instance,
vim_connection_info):
inst_vnf_info = vnf_instance.instantiated_vnf_info
access_info = vim_connection_info.access_info
heatclient = hc.HeatClient(access_info,
region_name=access_info.get('region'))
stack_resources = self._get_stack_resources(
inst_vnf_info.instance_id, heatclient)
self._update_vnfc_resources(vnf_instance, stack_resources,
vim_connection_info)
self._update_vnfc_info(vnf_instance)
@log.log
def post_vnf_instantiation(self, context, vnf_instance,
vim_connection_info, instantiate_vnf_req):
self._update_vnfc_resources_and_info(
context, vnf_instance, vim_connection_info)
@log.log
def post_change_ext_conn_vnf(self, context, vnf_instance,
vim_connection_info):
self._update_vnfc_resources_and_info(
context, vnf_instance, vim_connection_info)

View File

@ -83,3 +83,17 @@ class VnflcmMgmtAbstractDriver(metaclass=abc.ABCMeta):
heal_vnf_request, grant,
grant_request, **kwargs):
pass
@abc.abstractmethod
def change_external_connectivity_start(
self, context, vnf_instance,
change_ext_conn_request, grant,
grant_request, **kwargs):
pass
@abc.abstractmethod
def change_external_connectivity_end(
self, context, vnf_instance,
change_ext_conn_request, grant,
grant_request, **kwargs):
pass

View File

@ -74,3 +74,17 @@ class VnflcmMgmtNoop(vnflcm_abstract_driver.VnflcmMgmtAbstractDriver):
heal_vnf_request, grant,
grant_request, **kwargs):
pass
@log.log
def change_external_connectivity_start(
self, context, vnf_instance,
change_ext_conn_request, grant,
grant_request, **kwargs):
pass
@log.log
def change_external_connectivity_end(
self, context, vnf_instance,
change_ext_conn_request, grant,
grant_request, **kwargs):
pass