Support LCM notifications for VNF based on ETSI

Supported for Flow of sending notifications as defined in ETSI SOL003.

Notify LCM status to the cannback URL registered in subscription
Support for transmission processing from each LCM

Implements: blueprint support-etsi-nfv-specs
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/support-notification-api-based-on-etsi-nfv-sol.html

Change-Id: Idff7a98490ee87bb034d346cfda943bf03620b57
This commit is contained in:
Aldinson Esto 2020-08-19 19:49:17 +09:00 committed by Aldinson C. Esto
parent 5baeeefedf
commit f8f58e8cec
21 changed files with 2907 additions and 213 deletions

View File

@ -14,6 +14,149 @@ vnf_instance_id:
type: string
# variables in body
added_storage_resource_ids:
description: |
References to VirtualStorage resources that
have been added.
Each value refers to a
VirtualStorageResourceInfo item in the
VnfInstance that was added to the VNFC.
It shall be provided if at least one storage
resource was added to the VNFC.
in: body
required: false
type: array
affected_virtual_links:
description: |
Information about VL instances that were affected during
the lifecycle operation.
in: body
required: false
type: array
affected_virtual_links_change_type:
description: |
Signals the type of change.
Permitted values:
ADDED
REMOVED
MODIFIED
TEMPORARY
LINK_PORT_ADDED
LINK_PORT_REMOVED
For a temporary resource, an
AffectedVirtualLink structure exists as long as
the temporary resource exists.
in: body
required: true
type: string
affected_virtual_links_id:
description: |
Identifier of the virtual link instance, identifying
the applicable "vnfVirtualLinkResourceInfo"
entry in the "VnfInstance" data type.
in: body
required: true
type: string
affected_virtual_storages:
description: |
Information about virtualised storage instances that were
affected during the lifecycle operation.
in: body
required: false
type: array
affected_virtual_storages_change_type:
description: |
Signals the type of change.
Permitted values:
ADDED
REMOVED
MODIFIED
TEMPORARY
For a temporary resource, an
AffectedVirtualStorage structure exists as long
as the temporary resource exists.
in: body
required: true
type: string
affected_virtual_storages_id:
description: |
Identifier of the storage instance, identifying the
applicable "virtualStorageResourceInfo" entry
in the "VnfInstance" data type.
in: body
required: true
type: string
affected_virtual_storages_virtual_storage_desc_id:
description: |
Identifier of the related VirtualStorage
descriptor in the VNFD.
in: body
required: true
type: string
affected_vnfc_cp_ids:
description: |
Identifiers of CP(s) of the VNFC instance that
were affected by the change.
Shall be present for those affected CPs of the
VNFC instance that are associated to an
external CP of the VNF instance.
May be present for further affected CPs of the
VNFC instance.
in: body
required: false
type: array
affected_vnfcs:
description: |
Information about VNFC instances that were affected
during the lifecycle operation.
in: body
required: false
type: array
affected_vnfcs_change_type:
description: |
Signals the type of change
Permitted values:
ADDED
REMOVED
MODIFIED
TEMPORARY
For a temporary resource, an AffectedVnfc
structure exists as long as the temporary
resource exists.
in: body
required: true
type: string
affected_vnfcs_id:
description: |
Identifier of the Vnfc instance, identifying the
applicable "vnfcResourceInfo" entry in the
"VnfInstance" data type.
in: body
required: true
type: string
affected_vnfcs_vdu_id:
description: |
Identifier of the related VDU in the VNFD.
in: body
required: true
type: string
authentication:
description: |
Authentication parameters to configure the use of
@ -118,6 +261,83 @@ cause:
in: body
required: false
type: string
changed_info:
description: |
Information about the changed VNF instance information,
including VNF configurable properties, if applicable.
in: body
required: false
type: object
changed_info_metadata:
description: |
If present, this attribute signals modifications of the
"metadata" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vim_connection_info:
description: |
If present, this attribute signals modifications of certain
entries in the "vimConnectionInfo" attribute array in "VnfInstance".
in: body
required: false
type: array
changed_info_vnf_instance_description:
description: |
If present, this attribute signals modifications of the
"vnfInstanceDescription" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnf_instance_name:
description: |
If present, this attribute signals modifications of the
"vnfInstanceName" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnf_pkg_id:
description: |
If present, this attribute signals modifications of the
"vnfPkgId" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnf_product_name:
description: |
If present, this attribute signals modifications of the
"vnfProductName" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnf_provider:
description: |
If present, this attribute signals modifications of the
"vnfProvider" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnf_sotware_version:
description: |
If present, this attribute signals modifications of the
"vnfSoftwareVersion" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnfd_id:
description: |
If present, this attribute signals modifications of the
"vnfdId" attribute in "VnfInstance".
in: body
required: false
type: string
changed_info_vnfd_version:
description: |
If present, this attribute signals modifications of the
"vnfdVersion" attribute in "VnfInstance".
in: body
required: false
type: string
cp_config:
description: |
List of instance data that need to be configured on the CP instances
@ -147,6 +367,41 @@ cpd_id:
in: body
required: true
type: string
error:
description: |
If "operationState" is "FAILED_TEMP" or "FAILED" or
"operationState" is "PROCESSING" or "ROLLING_BACK"
and previous value of "operationState" was
"FAILED_TEMP", this attribute shall be present and
contain error information, unless it has been requested to
be excluded via an attribute selector.
in: body
required: false
type: object
error_detail:
description: |
A human-readable explanation specific to this occurrence
of the problem.
in: body
required: true
type: string
error_status:
description: |
The HTTP status code for this occurrence of the problem.
in: body
required: true
type: string
error_title:
description: |
A URI reference according to IETF RFC 3986 that
identifies the problem type. It is encouraged that the URI
provides human-readable documentation for the problem
(e.g. using HTML) when dereferenced. When this
member is not present, its value is assumed to be
"about:blank".
in: body
required: false
type: string
ext_cp_info:
description: |
Information about the external CPs exposed by the VNF instance.
@ -449,6 +704,16 @@ ip_over_ethernet_cp_info:
in: body
required: false
type: object
is_automatic_invocation:
description: |
Set to true if this VNF LCM operation occurrence has
been triggered by an automated procedure inside the
VNFM (i.e. ScaleVnf triggered by autoscale,
or HealVnf triggered by auto-heal).
Set to false otherwise.
in: body
required: true
type: boolean
is_dynamic:
description: |
Indicates whether this set of addresses was assigned dynamically (true)
@ -495,6 +760,86 @@ mac_address_cp_info:
in: body
required: false
type: string
notification_id:
description: |
Identifier of this notification. If a notification is sent
multiple times due to multiple subscriptions, the "id"
attribute of all these notifications shall have the same
value.
in: body
required: true
type: string
notification_operation:
description: |
The lifecycle management operation.
in: body
required: true
type: string
notification_operation_state:
description: |
The state of the VNF LCM operation occurrence.
in: body
required: true
type: string
notification_status:
description: |
Indicates whether this notification reports about the start
of a lifecycle operation or the result of a lifecycle
operation.
Permitted values:
START: Informs about the start of the VNF LCM
operation occurrence.
RESULT: Informs about the final or intermediate
result of the VNF LCM operation occurrence.
in: body
required: true
type: string
notification_subscription_id:
description: |
Identifier of the subscription that this notification relates to.
in: body
required: true
type: string
notification_time_tamp:
description: |
Date-time of the generation of the notification.
in: body
required: true
type: string
notification_type:
description: |
Discriminator for the different notification types.
in: body
required: true
type: string
notification_vnf_instance_id:
description: |
The identifier of the VNF instance affected.
in: body
required: true
type: string
notification_vnf_lcm_op_occ_id:
description: |
The identifier of the VNF lifecycle management
operation occurrence associated to the notification.
in: body
required: true
type: string
removed_storage_resource_ids:
description: |
References to VirtualStorage resources that
have been removed.
The value contains the identifier of a
VirtualStorageResourceInfo item that has been
removed from the VNFC, and might no longer
exist in the VnfInstance.
It shall be provided if at least one storage
resource was removed from the VNFC.
in: body
required: false
type: array
resource_handle:
description: |
Reference to the resource realizing this VL.
@ -594,6 +939,30 @@ vim_connection_info_access_info:
in: body
required: false
type: string
vim_connection_info_access_info_password:
description: |
The password to use for access.
in: body
required: true
type: string
vim_connection_info_access_info_region:
description: |
The OpenStack region to use for the VIM connection.
in: body
required: true
type: string
vim_connection_info_access_info_tenant:
description: |
The OpenStack tenant to use for the VIM connection.
in: body
required: true
type: string
vim_connection_info_access_info_username:
description: |
The username to use for access.
in: body
required: true
type: string
vim_connection_info_id:
description: |
The identifier of the VIM Connection. This identifier is managed by

View File

@ -0,0 +1,100 @@
{
"id": "fb864bd0-25f3-4180-b591-2910086c0013",
"notificationType": "VnfLcmOperationOccurrenceNotification",
"subscriptionId": "c25ae285-e245-4898-9df7-0527841efdc4",
"timeStamp": "2020-08-02T06:50:50.883373",
"notificationStatus": "RESULT",
"operationState": "COMPLETED",
"vnfInstanceId": "0b7b95a9-21d5-4ac4-80c8-9ae9f7323787",
"operation": "INSTANTIATE",
"isAutomaticInvocation": false,
"vnfLcmOpOccId": "1266b5b4-84d3-4c3a-acbf-aa515fada6b0",
"affectedVnfcs": [
{
"id": "36e24439-829c-4803-a413-385cd658d544",
"vduId": "VDU1",
"changeType": "ADDED",
"computeResource": {
"vimConnectionId": null,
"resourceId": "e0510ba9-3a53-4fcf-9dcc-58dea5c048b0",
"vimLevelResourceType": "OS::Nova::Server"
},
"metadata": {
"stackId": "f0faec36-721a-4245-8f50-4a19edec757a"
},
"affectedVnfcCpIds": [
"VDU1_CP0",
"VDU1_CP1"
],
"addedStorageResourceIds": []
},
{
"id": "51f930d7-f8b9-4a7f-8b02-18c47ec31340",
"vduId": "VDU2",
"changeType": "ADDED",
"computeResource": {
"vimConnectionId": null,
"resourceId": "",
"vimLevelResourceType": null
},
"metadata": {},
"affectedVnfcCpIds": [
"VDU2_CP0",
"VDU2_CP1"
],
"addedStorageResourceIds": []
},
{
"id": "52e4253a-7c9e-4161-bb74-0642d5072271",
"vduId": "VDU3",
"changeType": "ADDED",
"computeResource": {
"vimConnectionId": null,
"resourceId": "",
"vimLevelResourceType": null
},
"metadata": {},
"affectedVnfcCpIds": [
"VDU3_CP0",
"VDU3_CP1"
],
"addedStorageResourceIds": []
}
],
"affectedVirtualLinks": [
{
"id": "9836f7f2-5af4-4df5-a89f-933479448ef7",
"vnfVirtualLinkDescId": "internalNW1",
"changeType": "ADDED",
"networkResource": {
"vimConnectionId": null,
"resourceId": "400692e5-b2db-478e-acb1-b77a92635ec6",
"vimLevelResourceType": "OS::Neutron::Net"
},
"metadata": {}
},
{
"id": "8d6c4a5f-cbd1-4fd8-b240-867ed1d48385",
"vnfVirtualLinkDescId": "internalNW2",
"changeType": "ADDED",
"networkResource": {
"vimConnectionId": null,
"resourceId": "",
"vimLevelResourceType": null
},
"metadata": {}
}
],
"affectedVirtualStorages": [],
"_links": {
"vnfInstance": {
"href": "http://sample.com/vnflcm/v1/vnf_instances/0b7b95a9-21d5-4ac4-80c8-9ae9f7323787"
},
"subscription": {
"href": "http://sample.com/vnflcm/v1/subscriptions/c25ae285-e245-4898-9df7-0527841efdc4"
},
"vnfLcmOpOcc": {
"href": "http://sample.com/vnflcmv1/vnf_lcm_op_occs/1266b5b4-84d3-4c3a-acbf-aa515fada6b0"
}
}
}

View File

@ -758,3 +758,100 @@ Response Example
.. literalinclude:: samples/vnflcm/list-subscription-response.json
:language: javascript
Notification
=================
.. rest_method:: POST URI is provided by the client when creating the subscription.
The POST method delivers a notification from the API producer to an API consumer. The API consumer shall have
previously created an "Individual subscription" resource with a matching filter.
Response Codes
--------------
.. rest_status_code:: success status.yaml
- 204
.. rest_status_code:: error status.yaml
- 401
- 403
Request Parameters
------------------
.. rest_parameters:: parameters_vnflcm.yaml
- id: notification_id
- notificationType: notification_type
- subscriptionId: notification_subscription_id
- timeStamp: notification_time_tamp
- notificationStatus: notification_status
- operationState: notification_operation_state
- vnfInstanceId: notification_vnf_instance_id
- operation: notification_operation
- isAutomaticInvocation: is_automatic_invocation
- vnfLcmOpOccId: notification_vnf_lcm_op_occ_id
- affectedVnfcs: affected_vnfcs
- id: affected_vnfcs_id
- vduId: affected_vnfcs_vdu_id
- changeType: affected_vnfcs_change_type
- computeResource: vnfc_resource_info_compute_resource
- vimConnectionId: vim_connection_id
- resourceId: resource_handle_resource_id
- vimLevelResourceType: resource_handle_vim_level_resource_type
- affectedVnfcCpIds: affected_vnfc_cp_ids
- addedStorageResourceIds: added_storage_resource_ids
- removedStorageResourceIds: removed_storage_resource_ids
- affectedVirtualLinks: affected_virtual_links
- id: affected_virtual_links_id
- vnfVirtualLinkDescId: vnf_virtual_link_resource_info_vnf_virtual_link_desc_id
- virtualLinkDescId: vnf_virtual_link_resource_info_vnf_virtual_link_desc_id
- changeType: affected_virtual_links_change_type
- networkResource: vnf_virtual_link_resource_info_network_resource
- vimConnectionId: vim_connection_id
- resourceId: resource_handle_resource_id
- vimLevelResourceType: resource_handle_vim_level_resource_type
- affectedVirtualStorages: affected_virtual_storages
- id: affected_virtual_storages_id
- virtualStorageDescId: affected_virtual_storages_virtual_storage_desc_id
- changeType: affected_virtual_storages_change_type
- storageResource: virtual_storage_resource_info_storage_resource
- vimConnectionId: vim_connection_id
- resourceId: resource_handle_resource_id
- vimLevelResourceType: resource_handle_vim_level_resource_type
- changedInfo: changed_info
- vnfInstanceName: changed_info_vnf_instance_name
- vnfInstanceDescription: changed_info_vnf_instance_description
- metadata: changed_info_metadata
- vimConnectionInfo: changed_info_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
- region: vim_connection_info_access_info_region
- password: vim_connection_info_access_info_password
- tenant: vim_connection_info_access_info_tenant
- vnfPkgId: changed_info_vnf_pkg_id
- vnfdId: changed_info_vnfd_id
- vnfProvider: changed_info_vnf_provider
- vnfProductName: changed_info_vnf_product_name
- vnfSotwareVersion: changed_info_vnf_sotware_version
- vnfdVersion: changed_info_vnfd_version
- error: error
- title: error_title
- status: error_status
- detail: error_detail
- _links: vnf_instance_links
Response Example
----------------
.. literalinclude:: samples/vnflcm/notification-request.json
:language: javascript

View File

@ -158,10 +158,7 @@ class ViewBuilder(base.BaseViewBuilder):
paging):
# filter processing
lcmsubscription = []
# last_flg is True if nextpage_opaque_marker is set
last_flg = False
start_num = CONF.vnf_lcm.subscription_num * (paging - 1)
# Subscription_data counter for comparing
# subscription_data and start_num

View File

@ -15,18 +15,21 @@
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import encodeutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
import ast
import functools
import json
import re
import traceback
import six
from six.moves import http_client
from six.moves.urllib import parse
import webob
from urllib import parse
from tacker._i18n import _
from tacker.api.schemas import vnf_lcm
@ -37,10 +40,12 @@ from tacker.common import utils
from tacker.conductor.conductorrpc import vnf_lcm_rpc
import tacker.conf
from tacker.extensions import nfvo
from tacker.extensions import vnfm
from tacker import manager
from tacker import objects
from tacker.objects import fields
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
from tacker.plugins.common import constants
from tacker.policies import vnf_lcm as vnf_lcm_policies
from tacker.vnfm import vim_client
from tacker import wsgi
@ -63,7 +68,7 @@ def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
task_state = set(task_state)
def outer(f):
@six.wraps(f)
@functools.wraps(f)
def inner(self, context, vnf_instance, *args, **kw):
if instantiation_state is not None and \
vnf_instance.instantiation_state not in \
@ -85,6 +90,31 @@ def check_vnf_state(action, instantiation_state=None, task_state=(None,)):
return outer
def check_vnf_status(action, status=None):
"""Decorator to check vnf status are valid for particular action.
If the vnf is in the wrong state, it will raise conflict exception.
"""
if status is not None and not isinstance(status, set):
status = set(status)
def outer(f):
@functools.wraps(f)
def inner(self, context, vnf_instance, vnf, *args, **kw):
if status is not None and \
vnf['status'] not in \
status:
raise exceptions.VnfConflictState(
attr='status',
uuid=vnf['id'],
state=vnf['status'],
action=action)
return f(self, context, vnf_instance, vnf, *args, **kw)
return inner
return outer
class VnfLcmController(wsgi.Controller):
notification_type_list = ['VnfLcmOperationOccurrenceNotification',
@ -117,6 +147,9 @@ class VnfLcmController(wsgi.Controller):
def _get_vnf_instance_href(self, vnf_instance):
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
def _get_vnf_lcm_op_occs_href(self, vnf_lcm_op_occs_id):
return '/vnflcm/v1/vnf_lcm_op_occs/%s' % vnf_lcm_op_occs_id
def _get_vnf_instance(self, context, id):
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
@ -131,6 +164,24 @@ class VnfLcmController(wsgi.Controller):
return vnf_instance
def _get_vnf(self, context, id):
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
msg = _("Can not find requested vnf: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
try:
vnf = self._vnfm_plugin.get_vnf(context, id)
except vnfm.VNFNotFound:
msg = _("Can not find requested vnf: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
except Exception as exc:
msg = _("Encountered error while fetching vnf: %s") % id
LOG.debug("{}: {}".format(msg, six.text_type(exc)))
raise webob.exc.HTTPInternalServerError(explanation=six.
text_type(exc))
return vnf
def _validate_flavour_and_inst_level(self, context, req_body,
vnf_instance):
inst_levels = {}
@ -197,6 +248,65 @@ class VnfLcmController(wsgi.Controller):
% vim_id
raise webob.exc.HTTPBadRequest(explanation=msg)
def _notification_process(self, context, vnf_instance,
lcm_operation, request, is_auto=False):
vnf_lcm_op_occs_id = uuidutils.generate_uuid()
error_point = 0
if lcm_operation == fields.LcmOccsOperationType.HEAL:
request_dict = {
'vnfc_instance_id': request.vnfc_instance_id,
'cause': request.cause
}
operation_params = str(request_dict)
else:
# lcm is instantiation by default
operation_params = str(request.additional_params)
try:
# call create lcm op occs here
LOG.debug('Create LCM OP OCCS')
vnf_lcm_op_occs = objects.VnfLcmOpOcc(
context=context,
id=vnf_lcm_op_occs_id,
operation_state=fields.LcmOccsOperationState.STARTING,
start_time=timeutils.utcnow(),
state_entered_time=timeutils.utcnow(),
vnf_instance_id=vnf_instance.id,
is_cancel_pending=is_auto,
operation=lcm_operation,
is_automatic_invocation=is_auto,
operation_params=operation_params,
error_point=error_point)
vnf_lcm_op_occs.create()
except Exception:
msg = _("Failed to create LCM occurrence")
raise webob.exc.HTTPInternalServerError(explanation=msg)
vnf_lcm_url = self._get_vnf_lcm_op_occs_href(vnf_lcm_op_occs_id)
notification = {
'notificationType':
fields.LcmOccsNotificationType.VNF_OP_OCC_NOTIFICATION,
'notificationStatus': fields.LcmOccsNotificationStatus.START,
'operationState': fields.LcmOccsOperationState.STARTING,
'vnfInstanceId': vnf_instance.id,
'operation': lcm_operation,
'isAutomaticInvocation': is_auto,
'vnfLcmOpOccId': vnf_lcm_op_occs_id,
'_links': {
'vnfInstance': {
'href': self._get_vnf_instance_href(vnf_instance)},
'vnfLcmOpOcc': {
'href': vnf_lcm_url}}}
# call send notification
try:
self.rpc_api.send_notification(context, notification)
except Exception as ex:
LOG.error(
"Encoutered problem sending notification {}".format(
encodeutils.exception_to_unicode(ex)))
return vnf_lcm_op_occs_id
@wsgi.response(http_client.CREATED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
@validation.schema(vnf_lcm.create)
@ -247,6 +357,18 @@ class VnfLcmController(wsgi.Controller):
# add default vim to vim_connection_info
setattr(vnf_instance, 'vim_connection_info', [vim_con_info])
# create notification data
notification = {
'notificationType':
fields.LcmOccsNotificationType.VNF_ID_CREATION_NOTIFICATION,
'vnfInstanceId': vnf_instance.id,
'links': {
'vnfInstance': {
'href': self._get_vnf_instance_href(vnf_instance)}}}
# call send nootification
self.rpc_api.send_notification(context, notification)
vnf_instance.save()
result = self._view_builder.create(vnf_instance)
@ -291,10 +413,21 @@ class VnfLcmController(wsgi.Controller):
vnf_instance = self._get_vnf_instance(context, id)
self._delete(context, vnf_instance)
notification = {
"notificationType": "VnfIdentifierDeletionNotification",
"vnfInstanceId": vnf_instance.id,
"links": {
"vnfInstance":
"href:{apiRoot}/vnflcm/v1/vnf_instances/{vnfInstanceId}"}}
# send notification
self.rpc_api.send_notification(context, notification)
@check_vnf_state(action="instantiate",
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],
task_state=[None])
def _instantiate(self, context, vnf_instance, request_body):
@check_vnf_status(action="instantiate",
status=[constants.INACTIVE])
def _instantiate(self, context, vnf_instance, vnf, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
try:
@ -313,8 +446,13 @@ class VnfLcmController(wsgi.Controller):
vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING
vnf_instance.save()
self.rpc_api.instantiate(context, vnf_instance,
instantiate_vnf_request)
# lcm op process
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.INSTANTIATE,
instantiate_vnf_request)
self.rpc_api.instantiate(context, vnf_instance, vnf,
instantiate_vnf_request, vnf_lcm_op_occs_id)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND,
@ -324,14 +462,15 @@ class VnfLcmController(wsgi.Controller):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'instantiate')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
self._instantiate(context, vnf_instance, body)
self._instantiate(context, vnf_instance, vnf, body)
@check_vnf_state(action="terminate",
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
task_state=[None])
def _terminate(self, context, vnf_instance, request_body):
def _terminate(self, context, vnf_instance, request_body, vnf):
req_body = utils.convert_camelcase_to_snakecase(request_body)
terminate_vnf_req = \
objects.TerminateVnfRequest.obj_from_primitive(
@ -339,7 +478,15 @@ class VnfLcmController(wsgi.Controller):
vnf_instance.task_state = fields.VnfInstanceTaskState.TERMINATING
vnf_instance.save()
self.rpc_api.terminate(context, vnf_instance, terminate_vnf_req)
# lcm op process
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.TERMINATE,
terminate_vnf_req)
self.rpc_api.terminate(context, vnf_instance, vnf,
terminate_vnf_req, vnf_lcm_op_occs_id)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
@ -349,13 +496,16 @@ class VnfLcmController(wsgi.Controller):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'terminate')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
self._terminate(context, vnf_instance, body)
self._terminate(context, vnf_instance, body, vnf)
@check_vnf_state(action="heal",
instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
task_state=[None])
def _heal(self, context, vnf_instance, request_body):
@check_vnf_status(action="heal",
status=[constants.ACTIVE])
def _heal(self, context, vnf_instance, vnf_dict, request_body):
req_body = utils.convert_camelcase_to_snakecase(request_body)
heal_vnf_request = objects.HealVnfRequest(context=context, **req_body)
inst_vnf_info = vnf_instance.instantiated_vnf_info
@ -374,7 +524,14 @@ class VnfLcmController(wsgi.Controller):
vnf_instance.task_state = fields.VnfInstanceTaskState.HEALING
vnf_instance.save()
self.rpc_api.heal(context, vnf_instance, heal_vnf_request)
# call notification process
vnf_lcm_op_occs_id = \
self._notification_process(context, vnf_instance,
fields.LcmOccsOperationType.HEAL,
heal_vnf_request)
self.rpc_api.heal(context, vnf_instance, vnf_dict, heal_vnf_request,
vnf_lcm_op_occs_id)
@wsgi.response(http_client.ACCEPTED)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
@ -384,8 +541,9 @@ class VnfLcmController(wsgi.Controller):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'heal')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
self._heal(context, vnf_instance, body)
self._heal(context, vnf_instance, vnf, body)
@wsgi.response(http_client.CREATED)
@validation.schema(vnf_lcm.register_subscription)

View File

@ -232,6 +232,11 @@ class VnfInstanceConflictState(Conflict):
"%(action)s while the vnf instance is in this state.")
class VnfConflictState(Conflict):
message = _("Vnf %(uuid)s in %(attr)s %(state)s. Cannot "
"%(action)s while the vnf is in this state.")
class FlavourNotFound(NotFound):
message = _("No flavour with id '%(flavour_id)s'.")
@ -342,6 +347,10 @@ class LimitExceeded(TackerException):
super(LimitExceeded, self).__init__(*args, **kwargs)
class NotificationProcessingError(TackerException):
message = _("Notification Processing Failed: %(error)s")
class UserDataUpdateCreateFailed(TackerException):
msg_fmt = _("User data for VNF package %(id)s cannot be updated "
"or created after %(retries)d retries.")

View File

@ -12,29 +12,38 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import copy
import datetime
import functools
import inspect
import json
import os
import requests
import shutil
import sys
import time
import traceback
from glance_store import exceptions as store_exceptions
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_serialization import jsonutils
from oslo_service import periodic_task
from oslo_service import service
from oslo_utils import encodeutils
from oslo_utils import excutils
from oslo_utils import timeutils
from oslo_utils import uuidutils
from sqlalchemy import exc as sqlexc
from sqlalchemy.orm import exc as orm_exc
import yaml
from tacker.common import coordination
from tacker.common import csar_utils
from tacker.common import exceptions
from tacker.common import log
from tacker.common import safe_utils
from tacker.common import topics
from tacker.common import utils
@ -50,6 +59,7 @@ from tacker.objects.vnf_package import VnfPackagesList
from tacker.plugins.common import constants
from tacker import service as tacker_service
from tacker import version
from tacker.vnflcm import utils as vnflcm_utils
from tacker.vnflcm import vnflcm_driver
from tacker.vnfm import plugin
@ -115,7 +125,7 @@ def revert_upload_vnf_package(function):
or isinstance(exp, exceptions.VNFPackageURLInvalid)):
# Delete the csar file from the glance store.
glance_store.delete_csar(context, vnf_package.id,
vnf_package.location_glance_store)
vnf_package.location_glance_store)
csar_utils.delete_csar_data(vnf_package.id)
@ -407,6 +417,22 @@ class Conductor(manager.Manager):
return file_path_and_data
@coordination.synchronized('{vnf_package[id]}')
def _update_package_usage_state(self, context, vnf_package):
"""Update vnf package usage state to IN_USE/NOT_IN_USE
If vnf package is not used by any of the vnf instances, it's usage
state should be set to NOT_IN_USE otherwise it should be set to
IN_USE.
"""
result = vnf_package.is_package_in_use(context)
if result:
vnf_package.usage_state = fields.PackageUsageStateType.IN_USE
else:
vnf_package.usage_state = fields.PackageUsageStateType.NOT_IN_USE
vnf_package.save()
@periodic_task.periodic_task(spacing=CONF.vnf_package_delete_interval)
def _run_cleanup_vnf_packages(self, context):
"""Delete orphan extracted csar zip and files from extracted path
@ -438,87 +464,399 @@ class Conductor(manager.Manager):
{'zip': csar_path, 'folder': csar_zip_temp_path,
'uuid': vnf_pack.id})
@coordination.synchronized('{vnf_instance[id]}')
def instantiate(self, context, vnf_instance, instantiate_vnf):
# Check if vnf is already instantiated.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.INSTANTIATED:
LOG.error("Vnf instance %(id)s is already in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
self.vnflcm_driver.instantiate_vnf(context, vnf_instance,
instantiate_vnf)
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(context,
vnf_instance.vnfd_id)
vnf_package = objects.VnfPackage.get_by_id(context,
vnf_package_vnfd.package_uuid, expected_attrs=['vnfd'])
@log.log
def _get_vnf_notify(self, context, id):
try:
self._update_package_usage_state(context, vnf_package)
except Exception:
LOG.error("Failed to update usage_state of vnf package %s",
vnf_package.id)
vnf_notif = objects.VnfLcmOpOcc.get_by_id(context, id)
except exceptions.VnfInstanceNotFound:
error_msg = _("Can not find requested vnf instance: %s") % id
raise exceptions.NotificationProcessingError(error=error_msg)
except (sqlexc.SQLAlchemyError, Exception):
error_msg = _("Can not find requested vnf instance: %s") % id
raise exceptions.NotificationProcessingError(error=error_msg)
@coordination.synchronized('{vnf_package[id]}')
def _update_package_usage_state(self, context, vnf_package):
"""Update vnf package usage state to IN_USE/NOT_IN_USE
return vnf_notif
If vnf package is not used by any of the vnf instances, it's usage
state should be set to NOT_IN_USE otherwise it should be set to
IN_USE.
"""
result = vnf_package.is_package_in_use(context)
if result:
vnf_package.usage_state = fields.PackageUsageStateType.IN_USE
def _send_lcm_op_occ_notification(
self,
context,
vnf_lcm_op_occs_id,
old_vnf_instance,
vnf_instance,
request_obj,
**kwargs):
operation = kwargs.get('operation',
fields.LcmOccsOperationType.INSTANTIATE)
operation_state = kwargs.get('operation_state',
fields.LcmOccsOperationState.PROCESSING)
evacuate_end_list = kwargs.get('evacuate_end_list', None)
is_automatic_invocation = \
kwargs.get('is_automatic_invocation', False)
error = kwargs.get('error', None)
if old_vnf_instance:
vnf_instance_id = old_vnf_instance.id
else:
vnf_package.usage_state = fields.PackageUsageStateType.NOT_IN_USE
vnf_instance_id = vnf_instance.id
vnf_package.save()
@coordination.synchronized('{vnf_instance[id]}')
def terminate(self, context, vnf_instance, terminate_vnf_req):
# Check if vnf is in instantiated state.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.NOT_INSTANTIATED:
LOG.error("Terminate action cannot be performed on vnf %(id)s "
"which is in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
terminate_vnf_req)
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(context,
vnf_instance.vnfd_id)
vnf_package = objects.VnfPackage.get_by_id(context,
vnf_package_vnfd.package_uuid, expected_attrs=['vnfd'])
try:
self._update_package_usage_state(context, vnf_package)
LOG.debug("Update vnf lcm %s %s",
(vnf_lcm_op_occs_id,
operation_state))
vnf_notif = self._get_vnf_notify(context, vnf_lcm_op_occs_id)
vnf_notif.operation_state = operation_state
if operation_state == fields.LcmOccsOperationState.FAILED_TEMP:
error_details = objects.ProblemDetails(
context=context,
status=500,
details=error
)
vnf_notif.error = error_details
vnf_notif.save()
except Exception:
LOG.error("Failed to update usage_state of vnf package %s",
vnf_package.id)
error_msg = (
"Failed to update operation state of vnf instance %s" %
vnf_instance_id)
LOG.error(error_msg)
raise exceptions.NotificationProcessingError(error=error_msg)
# send notification
try:
notification_data = {
'notificationType':
fields.LcmOccsNotificationType.VNF_OP_OCC_NOTIFICATION,
'notificationStatus': fields.LcmOccsNotificationStatus.START,
'operationState': operation_state,
'vnfInstanceId': vnf_instance_id,
'operation': operation,
'isAutomaticInvocation': is_automatic_invocation,
'vnfLcmOpOccId': vnf_lcm_op_occs_id,
'_links': {
'vnfInstance': {
'href':
'/vnflcm/v1/vnf_instances/%s'
% vnf_instance_id},
'vnfLcmOpOcc': {
'href':
'/vnflcmv1/vnf_lcm_op_occs/%s'
% vnf_lcm_op_occs_id}}}
if(operation_state == fields.LcmOccsOperationState.COMPLETED or
operation_state == fields.LcmOccsOperationState.FAILED_TEMP):
affected_resources = vnflcm_utils._get_affected_resources(
old_vnf_instance=old_vnf_instance,
new_vnf_instance=vnf_instance,
extra_list=evacuate_end_list)
affected_resources_snake_case = \
utils.convert_camelcase_to_snakecase(affected_resources)
resource_change_obj = \
jsonutils.dumps(affected_resources_snake_case)
changed_resource = objects.ResourceChanges.obj_from_primitive(
resource_change_obj, context)
vnf_notif.resource_changes = changed_resource
vnf_notif.save()
notification_data['affectedVnfcs'] = \
affected_resources.get('affectedVnfcs', [])
notification_data['affectedVirtualLinks'] = \
affected_resources.get('affectedVirtualLinks', [])
notification_data['affectedVirtualStorages'] = \
affected_resources.get('affectedVirtualStorages', [])
notification_data['notificationStatus'] = \
fields.LcmOccsNotificationStatus.RESULT
if operation_state == fields.LcmOccsOperationState.FAILED_TEMP:
notification_data['error'] = error
# send notification
self.send_notification(context, notification_data)
except Exception as ex:
LOG.error(
"Failed to send notification {}. Details: {}".format(
vnf_lcm_op_occs_id, str(ex)))
def send_notification(self, context, notification):
try:
LOG.debug("send_notification start notification[%s]"
% notification)
if (notification.get('notificationType') ==
'VnfLcmOperationOccurrenceNotification'):
vnf_lcm_subscriptions = \
objects.LccnSubscriptionRequest.vnf_lcm_subscriptions_get(
context,
operation_type=notification.get('operation'),
notification_type=notification.get('notificationType')
)
else:
vnf_lcm_subscriptions = \
objects.LccnSubscriptionRequest.vnf_lcm_subscriptions_get(
context,
notification_type=notification.get('notificationType')
)
if not vnf_lcm_subscriptions:
LOG.warn(
"vnf_lcm_subscription not found id[%s]" %
notification.get('vnfInstanceId'))
return -1
notification['id'] = uuidutils.generate_uuid()
# Notification shipping
for line in vnf_lcm_subscriptions:
notification['subscriptionId'] = line.id.decode()
if (notification.get('notificationType') ==
'VnfLcmOperationOccurrenceNotification'):
notification['_links'] = {}
notification['_links']['subscription'] = {}
notification['_links']['subscription']['href'] = \
CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/subscriptions/" + line.id.decode()
else:
notification['links'] = {}
notification['links']['subscription'] = {}
notification['links']['subscription']['href'] = \
CONF.vnf_lcm.endpoint_url + \
"/vnflcm/v1/subscriptions/" + line.id.decode()
notification['timeStamp'] = datetime.datetime.utcnow(
).isoformat()
try:
for num in range(CONF.vnf_lcm.retry_num):
LOG.warn("send notify[%s]" % json.dumps(notification))
response = requests.post(
line.callback_uri.decode(),
data=json.dumps(notification))
if response.status_code == 204:
LOG.info(
"send success notify[%s]" %
json.dumps(notification))
break
else:
LOG.warning(
"Notification failed id[%s] status[%s] \
callback_uri[%s]" %
(notification['id'],
response.status_code,
line.callback_uri.decode()))
LOG.debug(
"retry_wait %s" %
CONF.vnf_lcm.retry_wait)
time.sleep(CONF.vnf_lcm.retry_wait)
if num == CONF.vnf_lcm.retry_num:
LOG.warn("Number of retries \
exceeded retry count")
continue
except Exception as e:
LOG.warn("send error[%s]" % str(e))
LOG.warn(traceback.format_exc())
continue
except Exception as e:
LOG.warn("Internal Sever Error[%s]" % str(e))
LOG.warn(traceback.format_exc())
return -2
return 0
@coordination.synchronized('{vnf_instance[id]}')
def heal(self, context, vnf_instance, heal_vnf_request):
# Check if vnf is in instantiated state.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.NOT_INSTANTIATED:
LOG.error("Heal action cannot be performed on vnf %(id)s "
"which is in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
def instantiate(
self,
context,
vnf_instance,
instantiate_vnf,
vnf_lcm_op_occs_id):
self.vnflcm_driver.heal_vnf(context, vnf_instance, heal_vnf_request)
try:
# 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=instantiate_vnf
)
# Check if vnf is already instantiated.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.INSTANTIATED:
LOG.error("Vnf instance %(id)s is already in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
self.vnflcm_driver.instantiate_vnf(context, vnf_instance,
instantiate_vnf)
vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(
context, vnf_instance.vnfd_id)
vnf_package = objects.VnfPackage.get_by_id(
context, vnf_package_vnfd.package_uuid,
expected_attrs=['vnfd'])
try:
self._update_package_usage_state(context, vnf_package)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error("Failed to update usage_state of vnf package %s",
vnf_package.id)
# 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=None,
vnf_instance=vnf_instance,
request_obj=instantiate_vnf,
operation_state=fields.LcmOccsOperationState.COMPLETED
)
except Exception as ex:
# Update vnf_lcm_op_occs table 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=None,
vnf_instance=vnf_instance,
request_obj=instantiate_vnf,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
error=str(ex)
)
@coordination.synchronized('{vnf_instance[id]}')
def terminate(self, context, vnf_lcm_op_occs_id,
vnf_instance, terminate_vnf_req, vnf_dict):
try:
# Check if vnf is in instantiated state.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.NOT_INSTANTIATED:
LOG.error("Terminate action cannot be performed on vnf %(id)s "
"which is in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
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=old_vnf_instance,
vnf_instance=None,
request_obj=terminate_vnf_req,
operation=fields.LcmOccsOperationType.TERMINATE
)
self.vnflcm_driver.terminate_vnf(context, vnf_instance,
terminate_vnf_req,
vnf_lcm_op_occs_id)
vnf_package_vnfd = \
objects.VnfPackageVnfd.get_by_id(context, vnf_instance.vnfd_id)
vnf_package = \
objects.VnfPackage.get_by_id(context,
vnf_package_vnfd.package_uuid,
expected_attrs=['vnfd'])
try:
self._update_package_usage_state(context, vnf_package)
except Exception:
with excutils.save_and_reraise_exception():
LOG.error("Failed to update usage_state of vnf package %s",
vnf_package.id)
# 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=None,
request_obj=terminate_vnf_req,
operation=fields.LcmOccsOperationType.TERMINATE,
operation_state=fields.LcmOccsOperationState.COMPLETED
)
except Exception as exc:
# Update vnf_lcm_op_occs table 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=None,
request_obj=terminate_vnf_req,
operation=fields.LcmOccsOperationType.TERMINATE,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
error=str(exc)
)
@coordination.synchronized('{vnf_instance[id]}')
def heal(self,
context,
vnf_instance,
vnf_dict,
heal_vnf_request,
vnf_lcm_op_occs_id):
try:
evacuate_end_list = []
# Check if vnf is in instantiated state.
vnf_instance = objects.VnfInstance.get_by_id(context,
vnf_instance.id)
if vnf_instance.instantiation_state == \
fields.VnfInstanceState.NOT_INSTANTIATED:
LOG.error("Heal action cannot be performed on vnf %(id)s "
"which is in %(state)s state.",
{"id": vnf_instance.id,
"state": vnf_instance.instantiation_state})
return
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=old_vnf_instance,
vnf_instance=vnf_instance,
request_obj=heal_vnf_request,
operation=fields.LcmOccsOperationType.HEAL
)
heal_result = \
self.vnflcm_driver.heal_vnf(context, vnf_instance, vnf_dict,
heal_vnf_request,
vnf_lcm_op_occs_id)
# update vnf_lcm_op_occs and send notification "COMPLETED"
if heal_result:
evacuate_end_list = heal_result.get('evacuate_end_list')
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=heal_vnf_request,
operation=fields.LcmOccsOperationType.HEAL,
operation_state=fields.LcmOccsOperationState.COMPLETED,
evacuate_end_list=evacuate_end_list
)
except Exception as ex:
# 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=heal_vnf_request,
operation=fields.LcmOccsOperationType.HEAL,
operation_state=fields.LcmOccsOperationState.FAILED_TEMP,
evacuate_end_list=evacuate_end_list,
error=str(ex)
)
def init(args, **kwargs):

View File

@ -14,7 +14,6 @@
# under the License.
import oslo_messaging
from tacker.common import rpc
from tacker.common import topics
from tacker.objects import base as objects_base
@ -28,7 +27,9 @@ class VNFLcmRPCAPI(object):
fanout=False,
version='1.0')
def instantiate(self, context, vnf_instance, instantiate_vnf, cast=True):
def instantiate(self, context, vnf_instance, vnf,
instantiate_vnf, vnf_lcm_op_occs_id,
cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
@ -37,9 +38,13 @@ class VNFLcmRPCAPI(object):
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'instantiate',
vnf_instance=vnf_instance,
instantiate_vnf=instantiate_vnf)
vnf_dict=vnf,
instantiate_vnf=instantiate_vnf,
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id)
def terminate(self, context, vnf_instance, terminate_vnf_req, cast=True):
def terminate(self, context, vnf_instance, vnf,
terminate_vnf_req, vnf_lcm_op_occs_id,
cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
@ -47,10 +52,13 @@ class VNFLcmRPCAPI(object):
cctxt = client.prepare()
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'terminate',
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id,
vnf_instance=vnf_instance,
terminate_vnf_req=terminate_vnf_req)
terminate_vnf_req=terminate_vnf_req,
vnf_dict=vnf)
def heal(self, context, vnf_instance, heal_vnf_request, cast=True):
def heal(self, context, vnf_instance, vnf_dict, heal_vnf_request,
vnf_lcm_op_occs_id, cast=True):
serializer = objects_base.TackerObjectSerializer()
client = rpc.get_client(self.target, version_cap=None,
@ -59,4 +67,16 @@ class VNFLcmRPCAPI(object):
rpc_method = cctxt.cast if cast else cctxt.call
return rpc_method(context, 'heal',
vnf_instance=vnf_instance,
heal_vnf_request=heal_vnf_request)
vnf_dict=vnf_dict,
heal_vnf_request=heal_vnf_request,
vnf_lcm_op_occs_id=vnf_lcm_op_occs_id)
def send_notification(self, context, notification, 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, 'send_notification',
notification=notification)

View File

@ -34,6 +34,7 @@ def register_all():
__import__('tacker.objects.vim_connection')
__import__('tacker.objects.instantiate_vnf_req')
__import__('tacker.objects.vnf_resources')
__import__('tacker.objects.vnf_lcm_op_occs')
__import__('tacker.objects.terminate_vnf_req')
__import__('tacker.objects.vnf_artifact')
__import__('tacker.objects.vnf_lcm_subscriptions')

View File

@ -184,3 +184,43 @@ class VnfcState(BaseTackerEnum):
STOPPED = 'STOPPED'
ALL = (STARTED, STOPPED)
class LcmOccsOperationState(BaseTackerEnum):
STARTING = 'STARTING'
PROCESSING = 'PROCESSING'
COMPLETED = 'COMPLETED'
FAILED_TEMP = 'FAILED_TEMP'
ALL = (STARTING, PROCESSING, COMPLETED, FAILED_TEMP)
class LcmOccsOperationType(BaseTackerEnum):
INSTANTIATE = 'INSTANTIATE'
TERMINATE = 'TERMINATE'
HEAL = 'HEAL'
ALL = (INSTANTIATE, TERMINATE, HEAL)
class LcmOccsNotificationStatus(BaseTackerEnum):
START = 'START'
RESULT = 'RESULT'
ALL = (START, RESULT)
class ResourceChangeType(BaseTackerEnum):
ADDED = 'ADDED'
REMOVED = 'REMOVED'
MODIFIED = 'MODIFIED'
TEMPORARY = 'TEMPORARY'
ALL = (ADDED, REMOVED, MODIFIED, TEMPORARY)
class LcmOccsNotificationType(BaseTackerEnum):
VNF_OP_OCC_NOTIFICATION = 'VnfLcmOperationOccurrenceNotification'
VNF_ID_CREATION_NOTIFICATION = 'VnfIdentifierCreationNotification'
ALL = (VNF_OP_OCC_NOTIFICATION)

View File

@ -30,7 +30,9 @@ class TerminateVnfRequest(base.TackerObject, base.TackerPersistentObject):
'termination_type': fields.VnfInstanceTerminationTypeField(
nullable=False),
'graceful_termination_timeout': fields.IntegerField(nullable=True,
default=0)
default=0),
'additional_params': fields.DictOfStringsField(nullable=True,
default={}),
}
@classmethod

View File

@ -0,0 +1,713 @@
# 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 datetime import datetime
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import timeutils
from sqlalchemy import exc
from sqlalchemy.orm import joinedload
from tacker.common import exceptions
from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models
from tacker import objects
from tacker.objects import base
from tacker.objects import fields
LOG = logging.getLogger(__name__)
@db_api.context_manager.writer
def _vnf_lcm_op_occ_create(context, values):
context.session.execute(
models.VnfLcmOpOccs.__table__.insert(None),
values)
@db_api.context_manager.writer
def _vnf_lcm_op_occ_update(context, values):
update = {'operation_state': values.operation_state,
'state_entered_time': values.state_entered_time,
'error_point': values.error_point,
'updated_at': datetime.utcnow()}
LOG.debug('values %s', values)
if 'resource_changes' in values:
if values.resource_changes:
update.update({'resource_changes': jsonutils.dumps(
values.resource_changes.to_dict())})
if 'error' in values:
if values.error:
update.update({'error': jsonutils.dumps(values.error.to_dict())})
if 'changed_info' in values:
if values.changed_info:
update.update({'changed_info': jsonutils.dumps(
values.changed_info.to_dict())})
api.model_query(context, models.VnfLcmOpOccs). \
filter_by(id=values.id). \
update(update, synchronize_session=False)
@db_api.context_manager.reader
def _vnf_lcm_op_occs_get_by_id(context, vnf_lcm_op_occ_id):
query = api.model_query(context, models.VnfLcmOpOccs,
read_deleted="no", project_only=True). \
filter_by(id=vnf_lcm_op_occ_id)
result = query.first()
if not result:
raise exceptions.NotFound(resource='table',
name='vnf_lcm_op_occs')
return result
@db_api.context_manager.reader
def _vnf_notify_get_by_id(context, vnf_instance_id, columns_to_join=None):
query = api.model_query(context, models.VnfLcmOpOccs,
read_deleted="no", project_only=True). \
filter_by(id=vnf_instance_id)
if columns_to_join:
for column in columns_to_join:
query = query.options(joinedload(column))
result = query.first()
if not result:
raise exceptions.VnfInstanceNotFound(id=vnf_instance_id)
return result
@db_api.context_manager.writer
def _vnf_notify_create(context, values):
vnf_lcm_op_occs = models.VnfLcmOpOccs()
vnf_lcm_op_occs.update(values)
vnf_lcm_op_occs.save(context.session)
return _vnf_notify_get_by_id(context, vnf_lcm_op_occs.id,
columns_to_join=None)
@db_api.context_manager.writer
def _vnf_notify_update(context, vnf_instance_id, values,
columns_to_join=None):
vnf_lcm_op_occs = _vnf_notify_get_by_id(context, vnf_instance_id,
columns_to_join=columns_to_join)
values = values.to_dict()
vnf_lcm_op_occs.update(values)
vnf_lcm_op_occs.save(session=context.session)
return vnf_lcm_op_occs
@db_api.context_manager.writer
def _destroy_vnf_notify(context, uuid):
now = timeutils.utcnow()
updated_values = {'deleted': True,
'deleted_at': now
}
api.model_query(context, models.VnfLcmOpOccs). \
filter_by(id=uuid). \
update(updated_values, synchronize_session=False)
# decorator to catch DBAccess exception
def _wrap_object_error(method):
def wrapper(*args, **kwargs):
try:
method(*args, **kwargs)
except exc.SQLAlchemyError:
raise exceptions.DBAccessError
return wrapper
@base.TackerObjectRegistry.register
class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.UUIDField(nullable=False),
'operation_state': fields.StringField(nullable=False),
'state_entered_time': fields.DateTimeField(nullable=False),
'start_time': fields.DateTimeField(nullable=False),
'vnf_instance_id': fields.StringField(nullable=False),
'operation': fields.StringField(nullable=False),
'is_automatic_invocation': fields.BooleanField(default=False),
'operation_params': fields.StringField(nullable=True),
'is_cancel_pending': fields.BooleanField(default=False),
'error': fields.ObjectField(
'ProblemDetails', nullable=True, default=None),
'resource_changes': fields.ObjectField(
'ResourceChanges', nullable=True, default=None),
'changed_info': fields.ObjectField(
'VnfInfoModifications', nullable=True, default=None),
'error_point': fields.IntegerField(nullable=True, default=0)
}
@base.remotable
def create(self):
updates = self.obj_clone()
_vnf_lcm_op_occ_create(self._context, updates)
@base.remotable
def save(self):
updates = self.obj_clone()
_vnf_lcm_op_occ_update(self._context, updates)
@staticmethod
def _from_db_object(context, vnf_lcm_op_occ_obj, db_vnf_lcm_op_occ):
special_fields = ['error',
'resource_changes', 'changed_info']
for key in vnf_lcm_op_occ_obj.fields:
if key in special_fields:
continue
setattr(vnf_lcm_op_occ_obj, key, db_vnf_lcm_op_occ.get(key))
if db_vnf_lcm_op_occ['error']:
error = ProblemDetails.obj_from_primitive(
db_vnf_lcm_op_occ['error'], context)
vnf_lcm_op_occ_obj.error = error
if db_vnf_lcm_op_occ['resource_changes']:
resource_changes = ResourceChanges.obj_from_primitive(
db_vnf_lcm_op_occ['resource_changes'], context)
vnf_lcm_op_occ_obj.resource_changes = resource_changes
if db_vnf_lcm_op_occ['changed_info']:
changed_info = VnfInfoModifications.obj_from_primitive(
db_vnf_lcm_op_occ['changed_info'], context)
vnf_lcm_op_occ_obj.changed_info = changed_info
vnf_lcm_op_occ_obj._context = context
vnf_lcm_op_occ_obj.obj_reset_changes()
return vnf_lcm_op_occ_obj
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
vnf_lcm_op_occ = super(
VnfLcmOpOcc, cls).obj_from_primitive(
primitive, context)
else:
if 'error' in primitive.keys():
obj_data = ProblemDetails._from_dict(
primitive.get('error'))
primitive.update({'error': obj_data})
if 'resource_changes' in primitive.keys():
obj_data = ResourceChanges._from_dict(
primitive.get('resource_changes'))
primitive.update({'resource_changes': obj_data})
if 'changed_info' in primitive.keys():
obj_data = VnfInfoModifications._from_dict(
primitive.get('changed_info'))
primitive.update({'changed_info': obj_data})
vnf_lcm_op_occ = VnfLcmOpOcc._from_dict(primitive)
return vnf_lcm_op_occ
@classmethod
def obj_from_db_obj(cls, context, db_obj):
return cls._from_db_object(context, cls(), db_obj)
@classmethod
def _from_dict(cls, data_dict):
operation_state = data_dict.get('operation_state')
state_entered_time = data_dict.get('state_entered_time')
start_time = data_dict.get('start_time')
vnf_instance_id = data_dict.get('vnf_instance_id')
operation = data_dict.get('operation')
is_automatic_invocation = data_dict.get('is_automatic_invocation')
operation_params = data_dict.get('operation_params')
is_cancel_pending = data_dict.get('is_cancel_pending')
error = data_dict.get('error')
resource_changes = data_dict.get('resource_changes')
changed_info = data_dict.get('changed_info')
error_point = data_dict.get('error_point')
obj = cls(operation_state=operation_state,
state_entered_time=state_entered_time,
start_time=start_time,
vnf_instance_id=vnf_instance_id,
operation=operation,
is_automatic_invocation=is_automatic_invocation,
operation_params=operation_params,
is_cancel_pending=is_cancel_pending,
error=error,
resource_changes=resource_changes,
changed_info=changed_info,
error_point=error_point
)
return obj
def to_dict(self):
data = {'id': self.id,
'operation_state': self.operation_state,
'state_entered_time': self.state_entered_time,
'start_time': self.start_time,
'vnf_instance_id': self.vnf_instance_id,
'operation': self.operation,
'is_automatic_invocation': self.is_automatic_invocation,
'operation_params': self.operation_params,
'is_cancel_pending': self.is_cancel_pending,
'error_point': self.error_point}
if self.error:
data.update({'error': self.error.to_dict()})
if self.resource_changes:
data.update({'resource_changes': self.resource_changes.to_dict()})
if self.changed_info:
data.update({'changed_info': self.changed_info.to_dict()})
return data
@base.remotable_classmethod
def get_by_id(cls, context, id):
db_vnf_lcm_op_occs = _vnf_lcm_op_occs_get_by_id(context, id)
return cls._from_db_object(context, cls(), db_vnf_lcm_op_occs)
@base.TackerObjectRegistry.register
class ResourceChanges(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'affected_vnfcs': fields.ListOfObjectsField(
'AffectedVnfc', nullable=True),
'affected_virtual_links': fields.ListOfObjectsField(
'AffectedVirtualLink', nullable=True),
'affected_virtual_storages': fields.ListOfObjectsField(
'AffectedVirtualStorage', nullable=True)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
resource_changes = super(
ResourceChanges, cls).obj_from_primitive(
primitive, context)
else:
rs_dict = jsonutils.loads(primitive)
if rs_dict.get('affected_vnfcs'):
obj_data = [AffectedVnfc._from_dict(
affected_vnfc) for affected_vnfc in rs_dict.get(
'affected_vnfcs', [])]
rs_dict.update({'affected_vnfcs': obj_data})
if rs_dict.get('affected_virtual_links'):
obj_data = [AffectedVirtualLink._from_dict(
affected_virtual_link)
for affected_virtual_link in rs_dict.get(
'affected_virtual_links', [])]
rs_dict.update({'affected_virtual_links': obj_data})
if rs_dict.get('affected_virtual_storages'):
obj_data = [AffectedVirtualStorage._from_dict(
affected_virtual_storage)
for affected_virtual_storage in rs_dict.get(
'affected_virtual_storages', [])]
rs_dict.update({'affected_virtual_storages': obj_data})
resource_changes = ResourceChanges._from_dict(rs_dict)
return resource_changes
@classmethod
def _from_dict(cls, data_dict):
affected_vnfcs = data_dict.get('affected_vnfcs')
affected_virtual_links = data_dict.get('affected_virtual_links')
affected_virtual_storages = data_dict.get('affected_virtual_storages')
obj = cls(affected_vnfcs=affected_vnfcs,
affected_virtual_links=affected_virtual_links,
affected_virtual_storages=affected_virtual_storages
)
return obj
def to_dict(self):
data = {}
if self.affected_vnfcs:
affected_vnfcs_list = []
for affected_vnfc in self.affected_vnfcs:
affected_vnfcs_list.append(affected_vnfc.to_dict())
data.update({'affected_vnfcs': affected_vnfcs_list})
if self.affected_virtual_links:
affected_virtual_links_list = []
for affected_virtual_link in self.affected_virtual_links:
affected_virtual_links_list.append(
affected_virtual_link.to_dict())
data.update(
{'affected_virtual_links': affected_virtual_links_list})
if self.affected_virtual_storages:
affected_virtual_storages_list = []
for affected_virtual_storage in self.affected_virtual_storages:
affected_virtual_storages_list.append(
affected_virtual_storage.to_dict())
data.update(
{'affected_virtual_storages': affected_virtual_storages_list})
return data
@base.TackerObjectRegistry.register
class ProblemDetails(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'title': fields.StringField(nullable=True, default=''),
'status': fields.IntegerField(nullable=False),
'detail': fields.StringField(nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
problem_detail = super(
ProblemDetails, cls).obj_from_primitive(
primitive, context)
else:
p_dict = jsonutils.loads(primitive)
problem_detail = ProblemDetails._from_dict(p_dict)
return problem_detail
@classmethod
def _from_dict(cls, data_dict):
title = data_dict.get('title')
status = data_dict.get('status')
detail = data_dict.get('detail')
obj = cls(title=title,
status=status,
detail=detail)
return obj
def to_dict(self):
return {'title': self.title,
'status': self.status,
'detail': self.detail}
@base.TackerObjectRegistry.register
class AffectedVnfc(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.StringField(nullable=False),
'vdu_id': fields.StringField(nullable=False),
'change_type': fields.StringField(nullable=False),
'compute_resource': fields.ObjectField(
'ResourceHandle', nullable=False),
'affected_vnfc_cp_ids':
fields.ListOfStringsField(nullable=True, default=[]),
'added_storage_resource_ids':
fields.ListOfStringsField(nullable=True, default=[]),
'removed_storage_resource_ids':
fields.ListOfStringsField(nullable=True, default=[])
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
affected_vnfc = super(
AffectedVnfc, cls).obj_from_primitive(
primitive, context)
else:
if 'compute_resource' in primitive.keys():
obj_data = ResourceHandle._from_dict(
primitive.get('compute_resource'))
primitive.update({'compute_resource': obj_data})
affected_vnfc = AffectedVnfc._from_dict(primitive)
return affected_vnfc
@classmethod
def _from_dict(cls, data_dict):
id = data_dict.get('id')
vdu_id = data_dict.get('vdu_id')
change_type = data_dict.get('change_type')
compute_resource = ResourceHandle._from_dict(
data_dict.get('compute_resource'))
affected_vnfc_cp_ids = data_dict.get('affected_vnfc_cp_ids')
added_storage_resource_ids = data_dict.get(
'added_storage_resource_ids')
removed_storage_resource_ids = data_dict.get(
'removed_storage_resource_ids')
obj = cls(id=id,
vdu_id=vdu_id,
change_type=change_type,
compute_resource=compute_resource,
affected_vnfc_cp_ids=affected_vnfc_cp_ids,
added_storage_resource_ids=added_storage_resource_ids,
removed_storage_resource_ids=removed_storage_resource_ids
)
return obj
def to_dict(self):
return {
'id': self.id,
'vdu_id': self.vdu_id,
'change_type': self.change_type,
'compute_resource': self.compute_resource.to_dict(),
'affected_vnfc_cp_ids': self.affected_vnfc_cp_ids,
'added_storage_resource_ids': self.added_storage_resource_ids,
'removed_storage_resource_ids': self.removed_storage_resource_ids}
@base.TackerObjectRegistry.register
class AffectedVirtualLink(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.StringField(nullable=False),
'vnf_virtual_link_desc_id': fields.StringField(nullable=False),
'change_type': fields.StringField(nullable=False),
'network_resource': fields.ObjectField(
'ResourceHandle', nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
affected_virtual_link = super(
AffectedVirtualLink, cls).obj_from_primitive(
primitive, context)
else:
if 'network_resource' in primitive.keys():
obj_data = ResourceHandle._from_dict(
primitive.get('network_resource'))
primitive.update({'network_resource': obj_data})
affected_virtual_link = AffectedVirtualLink._from_dict(primitive)
return affected_virtual_link
@classmethod
def _from_dict(cls, data_dict):
id = data_dict.get('id')
vnf_virtual_link_desc_id = data_dict.get('vnf_virtual_link_desc_id')
change_type = data_dict.get('change_type')
network_resource = ResourceHandle._from_dict(
data_dict.get('network_resource'))
obj = cls(id=id,
vnf_virtual_link_desc_id=vnf_virtual_link_desc_id,
change_type=change_type,
network_resource=network_resource
)
return obj
def to_dict(self):
return {'id': self.id,
'vnf_virtual_link_desc_id': self.vnf_virtual_link_desc_id,
'change_type': self.change_type,
'network_resource': self.network_resource.to_dict()}
@base.TackerObjectRegistry.register
class AffectedVirtualStorage(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'id': fields.StringField(nullable=False),
'virtual_storage_desc_id': fields.StringField(nullable=False),
'change_type': fields.StringField(nullable=False),
'storage_resource': fields.ObjectField(
'ResourceHandle', nullable=False)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
affected_virtual_storage = super(
AffectedVirtualStorage, cls).obj_from_primitive(
primitive, context)
else:
if 'storage_resource' in primitive.keys():
obj_data = ResourceHandle._from_dict(
primitive.get('storage_resource'))
primitive.update({'storage_resource': obj_data})
affected_virtual_storage = AffectedVirtualStorage._from_dict(
primitive)
return affected_virtual_storage
@classmethod
def _from_dict(cls, data_dict):
id = data_dict.get('id')
virtual_storage_desc_id = data_dict.get('virtual_storage_desc_id')
change_type = data_dict.get('change_type')
storage_resource = ResourceHandle._from_dict(
data_dict.get('storage_resource'))
obj = cls(id=id,
virtual_storage_desc_id=virtual_storage_desc_id,
change_type=change_type,
storage_resource=storage_resource
)
return obj
def to_dict(self):
return {'id': self.id,
'virtual_storage_desc_id': self.virtual_storage_desc_id,
'change_type': self.change_type,
'storage_resource': self.storage_resource.to_dict()}
@base.TackerObjectRegistry.register
class VnfInfoModifications(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vnf_instance_name': fields.StringField(nullable=True),
'vnf_instance_description': fields.StringField(nullable=True),
'vim_connection_info': fields.ListOfObjectsField(
'VimConnectionInfo', nullable=True, default=[]),
'vim_connection_info_delete_ids':
fields.ListOfStringsField(nullable=True, default=[]),
'vnf_pkg_id': fields.StringField(nullable=True, default=None),
'vnfd_id': fields.StringField(nullable=True),
'vnf_provider': fields.StringField(nullable=True),
'vnf_product_name': fields.StringField(nullable=True),
'vnf_software_version': fields.StringField(nullable=True),
'vnfd_version': fields.StringField(nullable=True)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
vnf_info_modifications = super(
VnfInfoModifications, 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})
vnf_info_modifications = VnfInfoModifications._from_dict(primitive)
return vnf_info_modifications
@classmethod
def _from_dict(cls, data_dict):
vnf_instance_name = data_dict.get('vnf_instance_name')
vnf_instance_description = data_dict.get('vnf_instance_description')
vim_connection_info = data_dict.get('vim_connection_info', [])
vim_connection_info_delete_ids = data_dict.get(
'vim_connection_info_delete_ids')
vnf_pkg_id = data_dict.get('vnf_pkg_id')
vnfd_id = data_dict.get('vnfd_id')
vnf_provider = data_dict.get('vnf_provider')
vnf_product_name = data_dict.get('vnf_product_name')
vnf_software_version = data_dict.get('vnf_software_version')
vnfd_version = data_dict.get('vnfd_version')
obj = cls(
vnf_instance_name=vnf_instance_name,
vnf_instance_description=vnf_instance_description,
vim_connection_info=vim_connection_info,
vim_connection_info_delete_ids=vim_connection_info_delete_ids,
vnf_pkg_id=vnf_pkg_id,
vnfd_id=vnfd_id,
vnf_provider=vnf_provider,
vnf_product_name=vnf_product_name,
vnf_software_version=vnf_software_version,
vnfd_version=vnfd_version)
return obj
def to_dict(self):
return {
'vnf_instance_name': self.vnf_instance_name,
'vnf_instance_description': self.vnf_instance_description,
'vim_connection_info': self.vim_connection_info,
'vim_connection_info_delete_ids':
self.vim_connection_info_delete_ids,
'vnf_pkg_id': self.vnf_pkg_id,
'vnfd_id': self.vnfd_id,
'vnf_provider': self.vnf_provider,
'vnf_product_name': self.vnf_product_name,
'vnf_software_version': self.vnf_software_version,
'vnfd_version': self.vnfd_version}
@base.TackerObjectRegistry.register
class ResourceHandle(base.TackerObject,
base.TackerPersistentObject):
# Version 1.0: Initial version
VERSION = '1.0'
fields = {
'vim_connection_id': fields.StringField(nullable=True,
default=None),
'resource_id': fields.StringField(nullable=False, default=""),
'vim_level_resource_type': fields.StringField(nullable=True,
default=None)
}
@classmethod
def obj_from_primitive(cls, primitive, context):
if 'tacker_object.name' in primitive:
resource_handle = super(
ResourceHandle, cls).obj_from_primitive(
primitive, context)
else:
resource_handle = ResourceHandle._from_dict(primitive)
return resource_handle
@classmethod
def _from_dict(cls, data_dict):
LOG.debug("data_dict %s", data_dict)
vim_connection_id = data_dict.get('vim_connection_id')
resource_id = data_dict.get('resource_id', "")
vim_level_resource_type = data_dict.get('vim_level_resource_type')
obj = cls(vim_connection_id=vim_connection_id,
resource_id=resource_id,
vim_level_resource_type=vim_level_resource_type)
return obj
def to_dict(self):
return {'vim_connection_id': self.vim_connection_id,
'resource_id': self.resource_id,
'vim_level_resource_type': self.vim_level_resource_type}

View File

@ -31,6 +31,7 @@ COMMON_PREFIXES = {
# Service operation status constants
ACTIVE = "ACTIVE"
ACK = "ACK"
INACTIVE = "INACTIVE"
PENDING_CREATE = "PENDING_CREATE"
PENDING_UPDATE = "PENDING_UPDATE"

View File

@ -13,14 +13,18 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import iso8601
import os
from oslo_config import cfg
import shutil
import tempfile
import uuid
import yaml
import zipfile
from oslo_config import cfg
from tacker.tests import utils
from tacker.tests import uuidsentinel
@ -86,6 +90,40 @@ def create_fake_csar_dir(vnf_package_id, temp_dir,
return fake_csar
def get_vnf_package_vnfd():
return {
"id": uuidsentinel.vnfd_id,
"vnf_provider": "test vnf provider",
"vnf_product_name": "Sample VNF",
"vnf_software_version": "1.0",
"vnfd_version": "1.0",
"name": 'Sample VNF Instance',
}
def get_lcm_op_occs_data():
return {
"tenant_id": uuidsentinel.tenant_id,
'operation_state': 'PROCESSING',
'state_entered_time':
datetime.datetime(1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'start_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'operation': 'MODIFY_INFO',
'is_automatic_invocation': 0,
'is_cancel_pending': 0,
}
def get_vnf_lcm_subscriptions():
subscription_id = uuidsentinel.subscription_id
return {
"id": subscription_id.encode(),
"callback_uri": b'http://localhost:9890/'
}
def get_expected_vnfd_data(zip_file=None):
if zip_file:
csar_temp_dir = tempfile.mkdtemp()

View File

@ -19,8 +19,10 @@ import sys
from unittest import mock
import fixtures
from glance_store import exceptions as store_exceptions
from oslo_config import cfg
import requests
from six.moves import urllib
import six.moves.urllib.error as urlerr
import yaml
@ -36,6 +38,7 @@ from tacker import objects
from tacker.objects import fields
from tacker.tests.unit.conductor import fakes
from tacker.tests.unit.db.base import SqlTestCase
from tacker.tests.unit.db import utils as db_utils
from tacker.tests.unit.objects import fakes as fake_obj
from tacker.tests.unit.vnflcm import fakes as vnflcm_fakes
from tacker.tests import utils
@ -62,6 +65,7 @@ class TestConductor(SqlTestCase):
self._mock_vnfm_plugin()
self.conductor = conductor_server.Conductor('host')
self.vnf_package = self._create_vnf_package()
self.instance_uuid = uuidsentinel.instance_id
self.temp_dir = self.useFixture(fixtures.TempDir()).path
def _mock_vnfm_plugin(self):
@ -84,6 +88,9 @@ class TestConductor(SqlTestCase):
vnfpkgm.create()
return vnfpkgm
def _create_vnf_package_vnfd(self):
return fakes.get_vnf_package_vnfd()
@mock.patch.object(conductor_server.Conductor, '_onboard_vnf_package')
@mock.patch.object(conductor_server, 'revert_upload_vnf_package')
@mock.patch.object(csar_utils, 'load_csar_data')
@ -204,10 +211,19 @@ class TestConductor(SqlTestCase):
return vnf_pack_vnfd_obj
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
def test_instantiate_vnf_instance(self, mock_package_in_use,
mock_get_lock):
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_instantiate_vnf_instance(self, mock_vnf_by_id,
mock_package_in_use,
mock_get_lock,
mock_save):
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)
@ -216,17 +232,27 @@ class TestConductor(SqlTestCase):
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
self.conductor.instantiate(self.context, vnf_instance,
instantiate_vnf_req)
instantiate_vnf_req,
vnf_lcm_op_occs_id)
self.vnflcm_driver.instantiate_vnf.assert_called_once_with(
self.context, mock.ANY, instantiate_vnf_req)
mock_package_in_use.assert_called_once()
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
@mock.patch('tacker.conductor.conductor_server.LOG')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_instantiate_vnf_instance_already_instantiated(self,
mock_log, mock_package_in_use, mock_get_lock):
mock_vnf_by_id, mock_log, mock_package_in_use, mock_get_lock,
mock_save):
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)
@ -236,8 +262,10 @@ class TestConductor(SqlTestCase):
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
self.conductor.instantiate(self.context, vnf_instance,
instantiate_vnf_req)
instantiate_vnf_req,
vnf_lcm_op_occs_id)
self.vnflcm_driver.instantiate_vnf.assert_not_called()
mock_package_in_use.assert_not_called()
expected_log = 'Vnf instance %(id)s is already in %(state)s state.'
@ -245,29 +273,58 @@ class TestConductor(SqlTestCase):
{'id': vnf_instance.id,
'state': fields.VnfInstanceState.INSTANTIATED})
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_instantiate_vnf_instance_with_vnf_package_in_use(self,
mock_vnf_package_in_use, mock_get_lock):
mock_vnf_by_id,
mock_vnf_lcm_subscriptions_get,
mock_vnf_package_in_use, mock_get_lock, mock_save):
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)
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
mock_vnf_lcm_subscriptions_get.return_value = \
m_vnf_lcm_subscriptions
mock_vnf_package_in_use.return_value = True
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
self.conductor.instantiate(self.context, vnf_instance,
instantiate_vnf_req)
instantiate_vnf_req,
vnf_lcm_op_occs_id)
self.vnflcm_driver.instantiate_vnf.assert_called_once_with(
self.context, mock.ANY, instantiate_vnf_req)
mock_vnf_package_in_use.assert_called_once()
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch('tacker.conductor.conductor_server.LOG')
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
@mock.patch('tacker.vnflcm.utils._get_affected_resources')
def test_instantiate_vnf_instance_failed_with_exception(
self, mock_log, mock_is_package_in_use, mock_get_lock):
self, mock_res, mock_vnf_by_id, mock_log,
mock_vnf_lcm_subscriptions_get,
mock_is_package_in_use, mock_get_lock, mock_save):
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)
@ -275,9 +332,16 @@ class TestConductor(SqlTestCase):
**vnf_instance_data)
vnf_instance.create()
instantiate_vnf_req = vnflcm_fakes.get_instantiate_vnf_request_obj()
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
mock_vnf_lcm_subscriptions_get.return_value = \
m_vnf_lcm_subscriptions
mock_is_package_in_use.side_effect = Exception
mock_res.return_value = {}
self.conductor.instantiate(self.context, vnf_instance,
instantiate_vnf_req)
instantiate_vnf_req,
vnf_lcm_op_occs_id)
self.vnflcm_driver.instantiate_vnf.assert_called_once_with(
self.context, mock.ANY, instantiate_vnf_req)
mock_is_package_in_use.assert_called_once()
@ -285,9 +349,13 @@ class TestConductor(SqlTestCase):
mock_log.error.assert_called_once_with(expected_log,
vnf_package_vnfd.package_uuid)
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_send_lcm_op_occ_notification')
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
def test_terminate_vnf_instance(self, mock_package_in_use, mock_get_lock):
def test_terminate_vnf_instance(self, mock_package_in_use,
mock_get_lock,
mock_send_notification):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -299,20 +367,27 @@ class TestConductor(SqlTestCase):
vnf_instance.create()
terminate_vnf_req = objects.TerminateVnfRequest(
termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
self.conductor.terminate(self.context, vnf_instance,
terminate_vnf_req)
termination_type=fields.VnfInstanceTerminationType.GRACEFUL,
additional_params={"key": "value"})
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf(instance_id=self.instance_uuid)
self.conductor.terminate(self.context, vnf_lcm_op_occs_id,
vnf_instance,
terminate_vnf_req, vnf_dict)
self.vnflcm_driver.terminate_vnf.assert_called_once_with(
self.context, mock.ANY, terminate_vnf_req)
self.context, mock.ANY, terminate_vnf_req,
vnf_lcm_op_occs_id)
mock_package_in_use.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_send_lcm_op_occ_notification')
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
@mock.patch('tacker.conductor.conductor_server.LOG')
def test_terminate_vnf_instance_already_not_instantiated(self,
mock_log, mock_package_in_use, mock_get_lock):
mock_log, mock_package_in_use, mock_get_lock,
mock_send_notification):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -324,10 +399,13 @@ class TestConductor(SqlTestCase):
vnf_instance.create()
terminate_vnf_req = objects.TerminateVnfRequest(
termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
self.conductor.terminate(self.context, vnf_instance,
terminate_vnf_req)
termination_type=fields.VnfInstanceTerminationType.GRACEFUL,
additional_params={"key": "value"})
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf(instance_id=self.instance_uuid)
self.conductor.terminate(self.context, vnf_lcm_op_occs_id,
vnf_instance,
terminate_vnf_req, vnf_dict)
self.vnflcm_driver.terminate_vnf.assert_not_called()
mock_package_in_use.assert_not_called()
@ -337,10 +415,13 @@ class TestConductor(SqlTestCase):
{'id': vnf_instance.id,
'state': fields.VnfInstanceState.NOT_INSTANTIATED})
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_send_lcm_op_occ_notification')
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
def test_terminate_vnf_instance_with_usage_state_not_in_use(self,
mock_vnf_package_is_package_in_use, mock_get_lock):
mock_vnf_package_is_package_in_use, mock_get_lock,
mock_send_notification):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -352,19 +433,26 @@ class TestConductor(SqlTestCase):
mock_vnf_package_is_package_in_use.return_value = False
terminate_vnf_req = objects.TerminateVnfRequest(
termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
self.conductor.terminate(self.context, vnf_instance,
terminate_vnf_req)
termination_type=fields.VnfInstanceTerminationType.GRACEFUL,
additional_params={"key": "value"})
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf(instance_id=self.instance_uuid)
self.conductor.terminate(self.context, vnf_lcm_op_occs_id,
vnf_instance,
terminate_vnf_req, vnf_dict)
self.vnflcm_driver.terminate_vnf.assert_called_once_with(
self.context, mock.ANY, terminate_vnf_req)
self.context, mock.ANY, terminate_vnf_req,
vnf_lcm_op_occs_id)
mock_vnf_package_is_package_in_use.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_send_lcm_op_occ_notification')
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
def test_terminate_vnf_instance_with_usage_state_already_in_use(self,
mock_vnf_package_is_package_in_use, mock_get_lock):
mock_vnf_package_is_package_in_use, mock_get_lock,
mock_send_notification):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -376,20 +464,27 @@ class TestConductor(SqlTestCase):
mock_vnf_package_is_package_in_use.return_value = True
terminate_vnf_req = objects.TerminateVnfRequest(
termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
self.conductor.terminate(self.context, vnf_instance,
terminate_vnf_req)
termination_type=fields.VnfInstanceTerminationType.GRACEFUL,
additional_params={"key": "value"})
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf(instance_id=self.instance_uuid)
self.conductor.terminate(self.context, vnf_lcm_op_occs_id,
vnf_instance,
terminate_vnf_req, vnf_dict)
self.vnflcm_driver.terminate_vnf.assert_called_once_with(
self.context, mock.ANY, terminate_vnf_req)
self.context, mock.ANY, terminate_vnf_req,
vnf_lcm_op_occs_id)
mock_vnf_package_is_package_in_use.assert_called_once()
@mock.patch('tacker.conductor.conductor_server.Conductor.'
'_send_lcm_op_occ_notification')
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch.object(objects.VnfPackage, 'is_package_in_use')
@mock.patch('tacker.conductor.conductor_server.LOG')
def test_terminate_vnf_instance_failed_to_update_usage_state(
self, mock_log, mock_is_package_in_use, mock_get_lock):
self, mock_log, mock_is_package_in_use, mock_get_lock,
mock_send_notification):
vnf_package_vnfd = self._create_and_upload_vnf_package()
vnf_instance_data = fake_obj.get_vnf_instance_data(
vnf_package_vnfd.vnfd_id)
@ -399,18 +494,30 @@ class TestConductor(SqlTestCase):
**vnf_instance_data)
vnf_instance.create()
terminate_vnf_req = objects.TerminateVnfRequest(
termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
termination_type=fields.VnfInstanceTerminationType.GRACEFUL,
additional_params={"key": "value"})
mock_is_package_in_use.side_effect = Exception
self.conductor.terminate(self.context, vnf_instance,
terminate_vnf_req)
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
vnf_dict = db_utils.get_dummy_vnf(instance_id=self.instance_uuid)
self.conductor.terminate(self.context, vnf_lcm_op_occs_id,
vnf_instance,
terminate_vnf_req, vnf_dict)
self.vnflcm_driver.terminate_vnf.assert_called_once_with(
self.context, mock.ANY, terminate_vnf_req)
self.context, mock.ANY, terminate_vnf_req,
vnf_lcm_op_occs_id)
expected_msg = "Failed to update usage_state of vnf package %s"
mock_log.error.assert_called_once_with(expected_msg,
vnf_package_vnfd.package_uuid)
@mock.patch.object(objects.VnfLcmOpOcc, "save")
@mock.patch.object(coordination.Coordinator, 'get_lock')
def test_heal_vnf_instance(self, mock_get_lock):
@mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")
def test_heal_vnf_instance(self, mock_vnf_by_id, mock_get_lock,
mock_save):
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)
@ -421,9 +528,10 @@ class TestConductor(SqlTestCase):
fields.VnfInstanceState.INSTANTIATED
vnf_instance.save()
heal_vnf_req = objects.HealVnfRequest(cause="healing request")
self.conductor.heal(self.context, vnf_instance, heal_vnf_req)
self.vnflcm_driver.heal_vnf.assert_called_once_with(
self.context, mock.ANY, heal_vnf_req)
vnf_dict = {"fake": "fake_dict"}
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
self.conductor.heal(self.context, vnf_instance, vnf_dict,
heal_vnf_req, vnf_lcm_op_occs_id)
@mock.patch.object(coordination.Coordinator, 'get_lock')
@mock.patch('tacker.conductor.conductor_server.LOG')
@ -440,7 +548,10 @@ class TestConductor(SqlTestCase):
vnf_instance.create()
heal_vnf_req = objects.HealVnfRequest(cause="healing request")
self.conductor.heal(self.context, vnf_instance, heal_vnf_req)
vnf_dict = {"fake": "fake_dict"}
vnf_lcm_op_occs_id = uuidsentinel.vnf_lcm_op_occs_id
self.conductor.heal(self.context, vnf_instance, vnf_dict,
heal_vnf_req, vnf_lcm_op_occs_id)
self.vnflcm_driver.heal_vnf.assert_not_called()
expected_log = ('Heal action cannot be performed on vnf %(id)s '
@ -508,3 +619,149 @@ class TestConductor(SqlTestCase):
user_name=user_name,
password=password)
self.assertEqual('CREATED', self.vnf_package.onboarding_state)
def test_send_notification_not_found_vnfd(self):
notification = {'vnfInstanceId': 'Test'}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, -2)
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
def test_send_notification_not_found_subscription(
self,
mock_vnf_lcm_subscriptions_get):
mock_vnf_lcm_subscriptions_get.return_value = None
notification = {
'vnfInstanceId': 'Test',
'notificationType': 'VnfLcmOperationOccurrenceNotification'}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, -1)
mock_vnf_lcm_subscriptions_get.assert_called()
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch('requests.post')
def test_send_notification_vnf_lcm_operation_occurrence(
self,
mock_post,
mock_vnf_lcm_subscriptions_get):
response = mock.Mock()
response.status_code = 204
mock_post.return_value = response
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
mock_vnf_lcm_subscriptions_get.return_value = \
m_vnf_lcm_subscriptions
notification = {
'vnfInstanceId': 'Test',
'notificationType': 'VnfLcmOperationOccurrenceNotification',
'operationTypes': 'SCALE',
'operationStates': 'RESULT',
'_links': {}}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, 0)
mock_vnf_lcm_subscriptions_get.assert_called()
mock_post.assert_called()
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch('requests.post')
def test_send_notification_vnf_identifier_creation(
self,
mock_post,
mock_vnf_lcm_subscriptions_get):
response = mock.Mock()
response.status_code = 204
mock_post.return_value = response
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
mock_vnf_lcm_subscriptions_get.return_value = \
m_vnf_lcm_subscriptions
notification = {
'vnfInstanceId': 'Test',
'notificationType': 'VnfIdentifierCreationNotification',
'links': {}}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, 0)
mock_vnf_lcm_subscriptions_get.assert_called()
mock_post.assert_called()
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch('requests.post')
def test_send_notification_retry_notification(
self,
mock_post,
mock_vnf_lcm_subscriptions_get):
response = mock.Mock()
response.status_code = 400
mock_post.return_value = response
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
mock_vnf_lcm_subscriptions_get.return_value = \
m_vnf_lcm_subscriptions
notification = {
'vnfInstanceId': 'Test',
'notificationType': 'VnfIdentifierCreationNotification',
'links': {}}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, 0)
mock_vnf_lcm_subscriptions_get.assert_called()
mock_post.assert_called()
self.assertEqual(mock_post.call_count, 3)
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
@mock.patch('requests.post')
def test_send_notification_send_error(self,
mock_post,
mock_vnf_lcm_subscriptions_get):
mock_post.side_effect = \
requests.exceptions.HTTPError("MockException")
m_vnf_lcm_subscriptions = \
[mock.MagicMock(**fakes.get_vnf_lcm_subscriptions())]
mock_vnf_lcm_subscriptions_get.return_value = \
m_vnf_lcm_subscriptions
notification = {
'vnfInstanceId': 'Test',
'notificationType': 'VnfIdentifierCreationNotification',
'links': {}}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, 0)
mock_vnf_lcm_subscriptions_get.assert_called()
mock_post.assert_called()
self.assertEqual(mock_post.call_count, 1)
@mock.patch.object(objects.LccnSubscriptionRequest,
'vnf_lcm_subscriptions_get')
def test_send_notification_internal_server_error(self,
mock_vnf_lcm_subscriptions_get):
mock_vnf_lcm_subscriptions_get.side_effect = Exception(
"MockException")
notification = {
'vnfInstanceId': 'Test',
'notificationType': 'VnfIdentifierCreationNotification',
'links': {}}
result = self.conductor.send_notification(self.context, notification)
self.assertEqual(result, -2)

View File

@ -178,6 +178,21 @@ def get_vnf_instance_data_with_id(vnfd_id):
}
def get_lcm_op_occs_data(vnf_instance_id):
return {
"tenant_id": uuidsentinel.tenant_id,
'operation_state': 'PROCESSING',
'state_entered_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'start_time': datetime.datetime(1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'vnf_instance_id': vnf_instance_id,
'operation': 'MODIFY_INFO',
'is_automatic_invocation': 0,
'is_cancel_pending': 0,
}
def fake_vnf_instance_model_dict(**updates):
vnf_instance = {
'deleted': False,
@ -401,3 +416,17 @@ def vnf_instance_model_object(vnf_instance):
vnf_instance_db_obj = models.VnfInstance()
vnf_instance_db_obj.update(instance_dict)
return vnf_instance_db_obj
def get_changed_info_data():
return {
"vnf_instance_name": "",
"vnf_instance_description": "",
"vnf_configurable_properties": {"test": "test_value"},
"vnfc_info_modifications_delete_ids": ["test1"],
"vnfd_id": "2c69a161-0000-4b0f-bcf8-391f8fc76600",
"vnf_provider": "NEC",
"vnf_product_name": "MME",
"vnf_software_version": "1.0",
"vnfd_version": "MME_1.0"
}

View File

@ -0,0 +1,92 @@
# 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 tacker import context
from tacker import objects
from tacker.tests.unit.db.base import SqlTestCase
from tacker.tests.unit.objects import fakes
from tacker.tests import uuidsentinel
class TestVnfLcmOpOcc(SqlTestCase):
def setUp(self):
super(TestVnfLcmOpOcc, self).setUp()
self.context = context.get_admin_context()
self.vnf_package = self._create_vnf_package()
self.vnf_package_vnfd = self._create_and_upload_vnf_package_vnfd()
self.vnf_instance = self._create_vnf_instance()
self.vnf_lcm_op_occs = self._create_vnf_lcm_op_occs()
def _create_vnf_package(self):
vnfpkgm = objects.VnfPackage(context=self.context,
**fakes.vnf_package_data)
vnfpkgm.create()
return vnfpkgm
def _create_and_upload_vnf_package_vnfd(self):
vnf_package = objects.VnfPackage(context=self.context,
**fakes.vnf_package_data)
vnf_package.create()
vnf_pack_vnfd = fakes.get_vnf_package_vnfd_data(
vnf_package.id, uuidsentinel.vnfd_id)
vnf_pack_vnfd_obj = objects.VnfPackageVnfd(
context=self.context, **vnf_pack_vnfd)
vnf_pack_vnfd_obj.create()
return vnf_pack_vnfd_obj
def _create_vnf_instance(self):
vnf_instance_data = fakes.get_vnf_instance_data(
self.vnf_package_vnfd.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
return vnf_instance
def _create_vnf_lcm_op_occs(self):
vnf_lcm_op_occs_data = fakes.get_lcm_op_occs_data(self.vnf_instance.id)
vnf_lcm_op_occs = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
context=self.context, **vnf_lcm_op_occs_data)
vnf_lcm_op_occs.create()
return vnf_lcm_op_occs
def test_create(self):
vnf_lcm_op_occs_data = fakes.get_lcm_op_occs_data(self.vnf_instance.id)
vnf_lcm_op_occs = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
context=self.context, **vnf_lcm_op_occs_data)
vnf_lcm_op_occs.create()
self.assertTrue(vnf_lcm_op_occs.vnf_instance_id)
def test_save(self):
vnf_lcm_op_occs_data = fakes.get_lcm_op_occs_data(self.vnf_instance.id)
vnf_lcm_op_occs = objects.vnf_lcm_op_occs.VnfLcmOpOcc(
context=self.context, **vnf_lcm_op_occs_data)
vnf_lcm_op_occs.create()
problem_obj = objects.vnf_lcm_op_occs.ProblemDetails()
problem_obj.status = '500'
problem_obj.detail = 'test_err'
changed_info = objects.vnf_lcm_op_occs.VnfInfoModifications(
context=self.context, **fakes.get_changed_info_data())
vnf_lcm_op_occs.operation_state = 'FAILED_TEMP'
vnf_lcm_op_occs.error = problem_obj
vnf_lcm_op_occs.id = uuidsentinel.vnf_lcm_op_occs_id
vnf_lcm_op_occs.changed_info = changed_info
vnf_lcm_op_occs.save()
self.assertEqual('FAILED_TEMP', vnf_lcm_op_occs.operation_state)
self.assertEqual(problem_obj, vnf_lcm_op_occs.error)

View File

@ -15,6 +15,7 @@
from unittest import mock
import ddt
from oslo_serialization import jsonutils
from six.moves import http_client
import urllib
@ -23,11 +24,15 @@ from webob import exc
from tacker.api.vnflcm.v1 import controller
from tacker.common import exceptions
from tacker.conductor.conductorrpc.vnf_lcm_rpc import VNFLcmRPCAPI
from tacker import context
import tacker.db.vnfm.vnfm_db
from tacker.extensions import nfvo
from tacker.manager import TackerManager
from tacker import objects
from tacker.objects import fields
from tacker.tests import constants
from tacker.tests.unit import base
from tacker.tests.unit.db import utils
from tacker.tests.unit import fake_request
import tacker.tests.unit.nfvo.test_nfvo_plugin as nfvo_plugin
from tacker.tests.unit.vnflcm import fakes
@ -72,6 +77,13 @@ class TestController(base.TestCase):
'placement_attr': {'region': 'TestRegionOne'},
'tenant': 'test'
}
self.context = context.get_admin_context()
with mock.patch.object(tacker.db.vnfm.vnfm_db.VNFMPluginDb, 'get_vnfs',
return_value=[]):
with mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()}):
self.controller = controller.VnfLcmController()
def tearDown(self):
self.mock_manager.stop()
@ -81,6 +93,17 @@ class TestController(base.TestCase):
def app(self):
return fakes.wsgi_app_v1()
def _get_dummy_vnf(self, vnf_id=None, status=None):
vnf_dict = utils.get_dummy_vnf()
if status:
vnf_dict['status'] = status
if vnf_id:
vnf_dict['id'] = vnf_id
return vnf_dict
@mock.patch.object(objects.VnfInstance, 'save')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_package.VnfPackage, 'get_by_id')
@ -125,7 +148,7 @@ class TestController(base.TestCase):
instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED,
**updates)
location_header = ('http://localhost/vnflcm/v1/vnf_instances/%s'
% resp.json['id'])
% resp.json['id'])
self.assertEqual(expected_vnf, resp.json)
self.assertEqual(location_header, resp.headers['location'])
@ -171,12 +194,10 @@ class TestController(base.TestCase):
self.assertEqual(http_client.CREATED, resp.status_code)
updates = {"vnfInstanceName": "SampleVnf",
"vnfInstanceDescription": "SampleVnf Description"}
expected_vnf = fakes.fake_vnf_instance_response(
instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED,
**updates)
"vnfInstanceDescription": "SampleVnf Description"}
expected_vnf = fakes.fake_vnf_instance_response(**updates)
location_header = ('http://localhost/vnflcm/v1/vnf_instances/%s'
% resp.json['id'])
% resp.json['id'])
self.assertEqual(expected_vnf, resp.json)
self.assertEqual(location_header, resp.headers['location'])
@ -265,12 +286,12 @@ class TestController(base.TestCase):
expected_message = ("Invalid input for field/attribute "
"{attribute}. Value: {value}. {value} is not "
"of type 'string'".
format(value=value, attribute=attribute))
format(value=value, attribute=attribute))
elif expected_type in ["name_allow_zero_min_length", "description"]:
expected_message = ("Invalid input for field/attribute "
"{attribute}. " "Value: {value}. {value} is "
"not of type 'string'".
format(value=value, attribute=attribute))
format(value=value, attribute=attribute))
elif expected_type == 'object':
expected_message = ("Invalid input for field/attribute "
"{attribute}. " "Value: {value}. {value} is "
@ -338,6 +359,12 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@ -347,7 +374,9 @@ class TestController(base.TestCase):
def test_instantiate_with_deployment_flavour(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
mock_vnf_instance_get_by_id, mock_get_vim):
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_insta_notfi_process,
mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -355,6 +384,10 @@ class TestController(base.TestCase):
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple"}
req = fake_request.HTTPRequest.blank(
@ -368,12 +401,18 @@ class TestController(base.TestCase):
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
def test_instantiate_with_non_existing_deployment_flavour(
self, mock_vnf_package_get_by_id, mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id):
self, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vnf,
mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -381,6 +420,10 @@ class TestController(base.TestCase):
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "invalid"}
req = fake_request.HTTPRequest.blank(
@ -393,8 +436,15 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("No flavour with id 'invalid'.",
resp.json['badRequest']['message'])
resp.json['badRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@ -404,7 +454,9 @@ class TestController(base.TestCase):
def test_instantiate_with_instantiation_level(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
mock_vnf_instance_get_by_id, mock_get_vim):
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_insta_notif_process,
mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -412,6 +464,10 @@ class TestController(base.TestCase):
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"instantiationLevelId": "instantiation_level_1"}
@ -425,7 +481,13 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
mock_get_vnf.assert_called_once()
mock_insta_notif_process.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@ -435,7 +497,8 @@ class TestController(base.TestCase):
def test_instantiate_with_no_inst_level_in_flavour(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
mock_vnf_instance_get_by_id, mock_get_vim):
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -444,6 +507,10 @@ class TestController(base.TestCase):
vnf_package = fakes.return_vnf_package_with_deployment_flavour()
vnf_package.vnf_deployment_flavours[0].instantiation_levels = None
mock_vnf_package_get_by_id.return_value = vnf_package
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
# No instantiation level in deployment flavour but it's passed in the
# request
@ -459,8 +526,14 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("No instantiation level with id "
"'instantiation_level_1'.", resp.json['badRequest']['message'])
"'instantiation_level_1'.",
resp.json['badRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@ -468,7 +541,8 @@ class TestController(base.TestCase):
def test_instantiate_with_non_existing_instantiation_level(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id):
mock_vnf_instance_get_by_id, mock_get_vnf,
mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -476,6 +550,10 @@ class TestController(base.TestCase):
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"instantiationLevelId": "non-existing"}
@ -489,8 +567,15 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("No instantiation level with id 'non-existing'.",
resp.json['badRequest']['message'])
resp.json['badRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.VnfLcmController.'
'_notification_process')
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@ -500,7 +585,9 @@ class TestController(base.TestCase):
def test_instantiate_with_vim_connection(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
mock_vnf_instance_get_by_id, mock_get_vim):
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_insta_notif_process,
mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -508,6 +595,10 @@ class TestController(base.TestCase):
fakes.return_vnf_package_vnfd()
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"vimConnectionInfo": [
@ -526,14 +617,22 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_instantiate.assert_called_once()
mock_get_vnf.assert_called_once()
mock_insta_notif_process.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
def test_instantiate_with_non_existing_vim(
self, mock_vnf_package_get_by_id, mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vim):
self, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -542,6 +641,10 @@ class TestController(base.TestCase):
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vim.side_effect = nfvo.VimNotFoundException
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"vimConnectionInfo": [
@ -559,15 +662,23 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("VimConnection id is not found: %s" %
uuidsentinel.vim_id, resp.json['badRequest']['message'])
uuidsentinel.vim_id,
resp.json['badRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
def test_instantiate_with_non_existing_region_vim(
self, mock_vnf_package_get_by_id, mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vim):
self, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -576,6 +687,10 @@ class TestController(base.TestCase):
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vim.side_effect = nfvo.VimRegionNotFoundException
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple",
"vimConnectionInfo": [
@ -594,15 +709,23 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("Region not found for the VimConnection: %s" %
uuidsentinel.vim_id, resp.json['badRequest']['message'])
uuidsentinel.vim_id,
resp.json['badRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(vim_client.VimClient, "get_vim")
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
def test_instantiate_with_default_vim_not_configured(
self, mock_vnf_package_get_by_id, mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vim):
self, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id,
mock_vnf_instance_get_by_id, mock_get_vim,
mock_get_vnf, mock_get_service_plugins):
mock_vnf_instance_get_by_id.return_value =\
fakes.return_vnf_instance_model()
@ -611,6 +734,10 @@ class TestController(base.TestCase):
mock_vnf_package_get_by_id.return_value = \
fakes.return_vnf_package_with_deployment_flavour()
mock_get_vim.side_effect = nfvo.VimDefaultNotDefined
mock_get_vnf.return_value = \
self._get_dummy_vnf(
vnf_id=mock_vnf_instance_get_by_id.return_value.id,
status='INACTIVE')
body = {"flavourId": "simple"}
req = fake_request.HTTPRequest.blank(
@ -623,10 +750,16 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("Default VIM is not defined.",
resp.json['badRequest']['message'])
resp.json['badRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
def test_instantiate_incorrect_instantiation_state(self, mock_vnf_by_id):
def test_instantiate_incorrect_instantiation_state(self, mock_vnf_by_id,
mock_get_vnf, mock_get_service_plugins):
vnf_instance = fakes.return_vnf_instance_model()
vnf_instance.instantiation_state = 'INSTANTIATED'
mock_vnf_by_id.return_value = vnf_instance
@ -642,8 +775,13 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
def test_instantiate_incorrect_task_state(self, mock_vnf_by_id):
def test_instantiate_incorrect_task_state(self, mock_vnf_by_id,
mock_get_vnf, mock_get_service_plugins):
vnf_instance = fakes.return_vnf_instance_model(
task_state=fields.VnfInstanceTaskState.INSTANTIATING)
mock_vnf_by_id.return_value = vnf_instance
@ -661,7 +799,7 @@ class TestController(base.TestCase):
expected_msg = ("Vnf instance %s in task_state INSTANTIATING. Cannot "
"instantiate while the vnf instance is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
@ddt.data({'attribute': 'flavourId', 'value': 123,
'expected_type': 'string'},
@ -713,7 +851,7 @@ class TestController(base.TestCase):
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("'flavourId' is a required property",
resp.json['badRequest']['message'])
resp.json['badRequest']['message'])
def test_instantiate_invalid_request_parameter(self):
body = {"flavourId": "simple"}
@ -749,12 +887,17 @@ class TestController(base.TestCase):
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual(
"Can not find requested vnf instance: %s" % constants.INVALID_UUID,
"Can not find requested vnf: %s" % constants.INVALID_UUID,
resp.json['itemNotFound']['message'])
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_instantiate_with_non_existing_vnf_instance(
self, mock_vnf_by_id):
self, mock_vnf_by_id, mock_get_vnf,
mock_get_service_plugins):
mock_vnf_by_id.side_effect = exceptions.VnfInstanceNotFound
body = {"flavourId": "simple"}
req = fake_request.HTTPRequest.blank(
@ -770,6 +913,7 @@ class TestController(base.TestCase):
self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
mock_get_vnf.assert_called_once()
@ddt.data('HEAD', 'PUT', 'DELETE', 'PATCH', 'GET')
def test_instantiate_invalid_http_method(self, method):
@ -813,17 +957,21 @@ class TestController(base.TestCase):
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
def test_show_with_invalid_uuid(self):
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
def test_show_with_invalid_uuid(self,
mock_get_service_plugins):
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s' % constants.INVALID_UUID)
resp = req.get_response(self.app)
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
constants.INVALID_UUID, resp.json['itemNotFound']['message'])
constants.INVALID_UUID,
resp.json['itemNotFound']['message'])
@ddt.data('PATCH', 'HEAD', 'PUT', 'POST')
def test_show_invalid_http_method(self, http_method):
@ -835,6 +983,12 @@ class TestController(base.TestCase):
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': 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(VNFLcmRPCAPI, "terminate")
@ -842,10 +996,15 @@ class TestController(base.TestCase):
{'terminationType': 'GRACEFUL'},
{'terminationType': 'GRACEFUL',
'gracefulTerminationTimeout': 10})
def test_terminate(self, body, mock_terminate, mock_save, mock_get_by_id):
def test_terminate(self, body, mock_terminate, mock_save,
mock_get_by_id, mock_get_vnf,
mock_notification_process,
mock_get_service_plugins):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_get_by_id.return_value = vnf_instance_obj
mock_get_vnf.return_value = \
self._get_dummy_vnf(vnf_id=vnf_instance_obj.id, status='ACTIVE')
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
@ -856,7 +1015,11 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.ACCEPTED, resp.status_code)
mock_terminate.assert_called_once()
mock_get_vnf.assert_called_once()
mock_notification_process.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@ddt.data(
{'attribute': 'terminationType', 'value': "TEST",
'expected_type': 'enum'},
@ -869,9 +1032,10 @@ class TestController(base.TestCase):
{'attribute': 'gracefulTerminationTimeout', 'value': "test",
'expected_type': 'integer'}
)
@ddt.unpack
def test_terminate_with_invalid_request_body(
self, attribute, value, expected_type):
self, values, mock_get_service_plugins):
attribute = values['attribute']
value = values['value']
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
body = {'terminationType': 'GRACEFUL',
@ -882,7 +1046,8 @@ class TestController(base.TestCase):
req.method = 'POST'
expected_message = ("Invalid input for field/attribute {attribute}. "
"Value: {value}.".format(value=value, attribute=attribute))
"Value: {value}.".
format(value=value, attribute=attribute))
exception = self.assertRaises(exceptions.ValidationError,
self.controller.terminate,
@ -901,7 +1066,7 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
self.assertEqual("'terminationType' is a required property",
resp.json['badRequest']['message'])
resp.json['badRequest']['message'])
@ddt.data('GET', 'HEAD', 'PUT', 'DELETE', 'PATCH')
def test_terminate_invalid_http_method(self, method):
@ -916,8 +1081,13 @@ class TestController(base.TestCase):
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': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
def test_terminate_non_existing_vnf_instance(self, mock_vnf_by_id):
def test_terminate_non_existing_vnf_instance(self, mock_vnf_by_id,
mock_get_vnf, mock_get_service_plugins):
body = {'terminationType': 'GRACEFUL',
'gracefulTerminationTimeout': 10}
mock_vnf_by_id.side_effect = exceptions.VnfInstanceNotFound
@ -931,11 +1101,17 @@ class TestController(base.TestCase):
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
def test_terminate_incorrect_instantiation_state(self, mock_vnf_by_id):
def test_terminate_incorrect_instantiation_state(self, mock_vnf_by_id,
mock_get_vnf, mock_get_service_plugins):
mock_vnf_by_id.return_value = fakes.return_vnf_instance()
body = {"terminationType": "FORCEFUL"}
req = fake_request.HTTPRequest.blank(
@ -951,10 +1127,16 @@ class TestController(base.TestCase):
"NOT_INSTANTIATED. Cannot terminate while the vnf "
"instance is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': FakeVNFMPlugin()})
@mock.patch('tacker.api.vnflcm.v1.controller.'
'VnfLcmController._get_vnf')
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_terminate_incorrect_task_state(self, mock_vnf_by_id):
def test_terminate_incorrect_task_state(self, mock_vnf_by_id,
mock_get_vnf, mock_get_service_plugins):
vnf_instance = fakes.return_vnf_instance(
instantiated_state=fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.TERMINATING)
@ -973,17 +1155,28 @@ class TestController(base.TestCase):
expected_msg = ("Vnf instance %s in task_state TERMINATING. Cannot "
"terminate while the vnf instance is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
mock_get_vnf.assert_called_once()
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': 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(VNFLcmRPCAPI, "heal")
@ddt.data({'cause': 'healing'}, {})
def test_heal(self, body, mock_rpc_heal, mock_save,
mock_vnf_by_id):
mock_vnf_by_id, mock_get_vnf,
mock_heal_notif_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')
req = fake_request.HTTPRequest.blank(
'/vnf_instances/%s/heal' % uuidsentinel.vnf_instance_id)
@ -1007,8 +1200,15 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': 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")
def test_heal_incorrect_instantiated_state(self, mock_vnf_by_id):
def test_heal_incorrect_instantiated_state(self, mock_vnf_by_id,
mock_get_vnf, mock_notif, mock_get_service_plugins):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.NOT_INSTANTIATED)
mock_vnf_by_id.return_value = vnf_instance_obj
@ -1023,13 +1223,20 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in instantiation_state "
"NOT_INSTANTIATED. Cannot heal while the vnf instance "
"is in this state.")
"NOT_INSTANTIATED. Cannot heal while the vnf instance "
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': 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")
def test_heal_incorrect_task_state(self, mock_vnf_by_id):
def test_heal_incorrect_task_state(self, mock_vnf_by_id, mock_get_vnf,
mock_notif, mock_get_service_plugins):
vnf_instance_obj = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED,
task_state=fields.VnfInstanceTaskState.HEALING)
@ -1045,16 +1252,25 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in task_state "
"HEALING. Cannot heal while the vnf instance "
"is in this state.")
"HEALING. Cannot heal while the vnf instance "
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
@mock.patch.object(TackerManager, 'get_service_plugins',
return_value={'VNFM': 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")
def test_heal_with_invalid_vnfc_id(self, mock_vnf_by_id):
def test_heal_with_invalid_vnfc_id(self, mock_vnf_by_id,
mock_get_vnf, mock_notif, 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 = {'vnfcInstanceId': [uuidsentinel.vnfc_instance_id]}
req = fake_request.HTTPRequest.blank(
@ -1067,7 +1283,8 @@ class TestController(base.TestCase):
self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
expected_msg = "Vnfc id %s not present in vnf instance %s"
self.assertEqual(expected_msg % (uuidsentinel.vnfc_instance_id,
uuidsentinel.vnf_instance_id), resp.json['badRequest']['message'])
uuidsentinel.vnf_instance_id),
resp.json['badRequest']['message'])
@ddt.data('HEAD', 'PUT', 'DELETE', 'PATCH', 'GET')
def test_heal_invalid_http_method(self, method):
@ -1171,8 +1388,8 @@ class TestController(base.TestCase):
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
uuidsentinel.vnf_instance_id,
resp.json['itemNotFound']['message'])
def test_delete_with_invalid_uuid(self):
req = fake_request.HTTPRequest.blank(
@ -1184,8 +1401,8 @@ class TestController(base.TestCase):
self.assertEqual(http_client.NOT_FOUND, resp.status_code)
self.assertEqual("Can not find requested vnf instance: %s" %
constants.INVALID_UUID,
resp.json['itemNotFound']['message'])
constants.INVALID_UUID,
resp.json['itemNotFound']['message'])
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_delete_with_incorrect_instantiation_state(self, mock_vnf_by_id):
@ -1202,10 +1419,10 @@ class TestController(base.TestCase):
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in instantiation_state "
"INSTANTIATED. Cannot delete while the vnf instance "
"is in this state.")
"INSTANTIATED. Cannot delete while the vnf instance "
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
@mock.patch.object(objects.VnfInstance, "get_by_id")
def test_delete_with_incorrect_task_state(self, mock_vnf_by_id):
@ -1223,10 +1440,10 @@ class TestController(base.TestCase):
self.assertEqual(http_client.CONFLICT, resp.status_code)
expected_msg = ("Vnf instance %s in task_state ERROR. "
"Cannot delete while the vnf instance "
"is in this state.")
"Cannot delete while the vnf instance "
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
resp.json['conflictingRequest']['message'])
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(

View File

@ -12,13 +12,13 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import fixtures
import os
import shutil
from unittest import mock
import fixtures
from oslo_config import cfg
from tacker.common import exceptions
from tacker.common import utils
from tacker import context
@ -32,6 +32,14 @@ from tacker.vnflcm import vnflcm_driver
from tacker.vnfm import vim_client
OPTS_INFRA_DRIVER = [
cfg.ListOpt(
'infra_driver', default=['noop', 'openstack', 'kubernetes'],
help=_('Hosting vnf drivers tacker plugin will use')),
]
cfg.CONF.register_opts(OPTS_INFRA_DRIVER, 'tacker')
class InfraDriverException(Exception):
pass
@ -256,7 +264,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfInstance, "save")
def test_instantiate_vnf_infra_fails_to_wait_after_instantiate(
self, mock_vnf_instance_save, mock_vnf_package_vnfd, mock_create):
self, mock_vnf_instance_save, mock_vnf_package_vnfd,
mock_create):
vnf_package_vnfd = fakes.return_vnf_package_vnfd()
vnf_package_id = vnf_package_vnfd.package_uuid
mock_vnf_package_vnfd.return_value = vnf_package_vnfd

View File

@ -76,6 +76,211 @@ def _get_vnfd_dict(context, vnfd_id, flavour_id):
return vnfd_dict
def _build_affected_resources(vnf_instance,
change_type=fields.ResourceChangeType.ADDED):
'''build affected resources from vnf_instance instantiated info '''
affected_resources = {}
instantiated_vnf_info = vnf_instance.instantiated_vnf_info
if hasattr(instantiated_vnf_info, 'instance_id'):
if instantiated_vnf_info.instance_id:
affected_resources['affectedVnfcs'] = []
affected_resources['affectedVirtualLinks'] = []
affected_resources['affectedVirtualStorages'] = []
# build AffectedVnfc
vnfc_resource_info = \
instantiated_vnf_info.vnfc_resource_info
for vnfc_resource in vnfc_resource_info:
data = {}
data['id'] = vnfc_resource.id
data['vduId'] = vnfc_resource.vdu_id
data['changeType'] = change_type
data['computeResource'] = \
vnfc_resource.compute_resource.to_dict()
data['metadata'] = vnfc_resource.metadata
affected_resources['affectedVnfcs'].append(data)
# build AffectedVirtualLink
vnf_virtual_link = \
instantiated_vnf_info.vnf_virtual_link_resource_info
for vnf_vl_info in vnf_virtual_link:
data = {}
data['id'] = vnf_vl_info.id
data['vnfVirtualLinkDescId'] = \
vnf_vl_info.vnf_virtual_link_desc_id
data['changeType'] = change_type
data['networkResource'] = \
vnf_vl_info.network_resource.to_dict()
data['metadata'] = {}
affected_resources['affectedVirtualLinks'].append(data)
# build affectedVirtualStorages
virtual_storage = \
instantiated_vnf_info.virtual_storage_resource_info
for vnf_storage_info in virtual_storage:
data = {}
data['id'] = vnf_storage_info.id
data['virtualStorageDescId'] = \
vnf_storage_info.virtual_storage_desc_id
data['changeType'] = change_type
data['storageResource'] = \
vnf_storage_info.storage_resource.to_dict()
data['metadata'] = {}
affected_resources['affectedVirtualStorages'].append(data)
return utils.convert_snakecase_to_camelcase(affected_resources)
def _get_affected_resources(old_vnf_instance=None,
new_vnf_instance=None, extra_list=None):
'''get_affected_resources
returns affected resources in new_vnf_instance not present
in old_vnf_instance.
if extra_list (list of physical resource ids) is present,
included to affected resources
'''
def _get_affected_cpids(affected_vnfc, vnf_instance):
affected_cpids = []
instantiated_vnf_info = vnf_instance.instantiated_vnf_info
for vnfc_resource in instantiated_vnf_info.vnfc_resource_info:
if vnfc_resource.id == affected_vnfc['id']:
for vnfc_cp in vnfc_resource.vnfc_cp_info:
if vnfc_cp.cpd_id:
affected_cpids.append(vnfc_cp.cpd_id)
if vnfc_cp.vnf_ext_cp_id:
affected_cpids.append(vnfc_cp.vnf_ext_cp_id)
return affected_cpids
def _get_added_storageids(affected_vnfc, vnf_instance):
affected_storage_ids = []
instantiated_vnf_info = vnf_instance.instantiated_vnf_info
for vnfc_resource in instantiated_vnf_info.vnfc_resource_info:
if vnfc_resource.id == affected_vnfc['id']:
for storage_resource_id in vnfc_resource.storage_resource_ids:
virtual_storage = \
instantiated_vnf_info.virtual_storage_resource_info
for virt_storage_res_info in virtual_storage:
if virt_storage_res_info.id == storage_resource_id:
affected_storage_ids.append(
virt_storage_res_info.virtual_storage_desc_id)
return affected_storage_ids
def diff_list(old_list, new_list):
diff = []
for item in new_list:
if item not in old_list:
diff.append(item)
return diff
affected_resources = {}
affected_resources['affectedVnfcs'] = []
affected_resources['affectedVirtualLinks'] = []
affected_resources['affectedVirtualStorages'] = []
if not old_vnf_instance:
affected_resources = _build_affected_resources(
new_vnf_instance, fields.ResourceChangeType.ADDED)
# add affected cpids and add added storageids
for affected_vnfc in affected_resources['affectedVnfcs']:
affected_vnfc['affectedVnfcCpIds'] = _get_affected_cpids(
affected_vnfc, new_vnf_instance)
affected_vnfc['addedStorageResourceIds'] = _get_added_storageids(
affected_vnfc, new_vnf_instance)
elif not new_vnf_instance:
affected_resources = _build_affected_resources(old_vnf_instance,
fields.ResourceChangeType.REMOVED)
# add affected cpids and add remove storageids
for affected_vnfc in affected_resources['affectedVnfcs']:
affected_vnfc['affectedVnfcCpIds'] = _get_affected_cpids(
affected_vnfc, old_vnf_instance)
affected_vnfc['removedStorageResourceIds'] = _get_added_storageids(
affected_vnfc, old_vnf_instance)
elif old_vnf_instance and new_vnf_instance:
old_affected_resources = _build_affected_resources(old_vnf_instance)
new_affected_resources = _build_affected_resources(new_vnf_instance,
fields.ResourceChangeType.MODIFIED)
# get resource_ids
old_vnfc_resource_ids = []
for vnfc_resource in old_affected_resources.get('affectedVnfcs', []):
old_vnfc_resource_ids.append(
vnfc_resource['computeResource']['resourceId'])
# remove extra_list items in old_vnfc_resource_ids
# so that this items will be considered new
if extra_list:
for item in extra_list:
if item in old_vnfc_resource_ids:
index = old_vnfc_resource_ids.index(item)
old_vnfc_resource_ids.pop(index)
new_vnfc_resource_ids = []
for vnfc_resource in new_affected_resources.get('affectedVnfcs', []):
resource_id = vnfc_resource['computeResource']['resourceId']
new_vnfc_resource_ids.append(resource_id)
old_vnf_vl_resource_ids = []
for vnf_vl_info in old_affected_resources.get(
'affectedVirtualLinks', []):
resource_id = vnf_vl_info['networkResource']['resourceId']
old_vnf_vl_resource_ids.append(resource_id)
new_vnf_vl_resource_ids = []
for vnf_vl_info in new_affected_resources.get(
'affectedVirtualLinks', []):
resource_id = vnf_vl_info['networkResource']['resourceId']
new_vnf_vl_resource_ids.append(resource_id)
old_vnf_storage_resource_ids = []
for vnf_storage_info in old_affected_resources.get(
'affectedVirtualStorages', []):
resource_id = vnf_storage_info['storageResource']['resourceId']
old_vnf_storage_resource_ids.append(resource_id)
new_vnf_storage_resource_ids = []
for vnf_storage_info in new_affected_resources.get(
'affectedVirtualStorages', []):
resource_id = vnf_storage_info['storageResource']['resourceId']
new_vnf_storage_resource_ids.append(resource_id)
# get difference between resource_ids
vnfc_resource_ids = diff_list(old_vnfc_resource_ids,
new_vnfc_resource_ids)
vnf_vl_resource_ids = diff_list(old_vnf_vl_resource_ids,
new_vnf_vl_resource_ids)
vnf_storage_resource_ids = diff_list(old_vnf_storage_resource_ids,
new_vnf_storage_resource_ids)
# return new affected resources
for affected_vls in new_affected_resources['affectedVirtualLinks']:
if (affected_vls['networkResource']
['resourceId'] in vnf_vl_resource_ids):
affected_resources['affectedVirtualLinks'].append(affected_vls)
affected_storages = new_affected_resources['affectedVirtualStorages']
for affected_storage in affected_storages:
if (affected_storage['storageResource']
['resourceId'] in vnf_storage_resource_ids):
affected_resources['affectedVirtualStorages'].append(
affected_storage)
for affected_vnfc in new_affected_resources['affectedVnfcs']:
if (affected_vnfc['computeResource']
['resourceId'] in vnfc_resource_ids):
# update affected affectedVnfcCpIds
affected_vnfc['affectedVnfcCpIds'] = _get_affected_cpids(
affected_vnfc, new_vnf_instance)
affected_resources['affectedVnfcs'].append(affected_vnfc)
return affected_resources
def _get_vnf_package_id(context, vnfd_id):
vnf_package = objects.VnfPackageVnfd.get_by_id(context, vnfd_id)
return vnf_package.package_uuid

View File

@ -28,6 +28,8 @@ import time
import eventlet.wsgi
# eventlet.patcher.monkey_patch(all=False, socket=True, thread=True)
from oslo_config import cfg
import tacker.conf
import oslo_i18n as i18n
from oslo_log import log as logging
from oslo_serialization import jsonutils
@ -75,7 +77,7 @@ socket_opts = [
"the server securely")),
]
CONF = cfg.CONF
CONF = tacker.conf.CONF
CONF.register_opts(socket_opts)