Merge "Enhancement of get VNF LCM operation occurrence"

This commit is contained in:
Zuul 2021-03-26 07:42:59 +00:00 committed by Gerrit Code Review
commit b01e9f5d3a
20 changed files with 901 additions and 25 deletions

View File

@ -0,0 +1,82 @@
[
{
"id": "d85c6ae4-af16-42c0-96fc-82f7c014c468",
"operationState": "COMPLETED",
"stateEnteredTime": "2020-08-02T06:50:50.883373",
"startTime": "2020-08-02T06:41:34.883483",
"vnfInstanceId": "0b7b95a9-21d5-4ac4-80c8-9ae9f7323787",
"grantId": "3432cebe-db0a-11e8-9023-005056317abe",
"operation": "INSTANTIATE",
"isAutomaticInvocation": false,
"operationParams": "{
'flavourId': 'default',
'instantiationLevelId': 'vnf-min',
}",
"isCancelPending": false,
"resourceChanges": {
"affectedVnfcs": [
{
"id": "36e24439-829c-4803-a413-385cd658d544",
"vduId": "VDU",
"changeType": "ADDED",
"computeResource": {
"vimConnectionId": "f26f181d-7891-4720-b022-b074ec1733ef",
"resourceId": "e0510ba9-3a53-4fcf-9dcc-58dea5c048b0",
"vimLevelResourceType": "OS::Nova::Server",
},
"affectedVnfcCpIds": [
"VDU1_CP0",
"VDU1_CP1"
],
"addedStorageResourceIds": [
"81ae44f6-b65b-47aa-a578-e53b7a50a574"
]
}
],
"affectedVirtualLinks": [
{
"id": "9836f7f2-5af4-4df5-a89f-933479448ef7",
"vnfVirtualLinkDescId": "internalNW",
"changeType": "ADDED",
"networkResource": {
"vimConnectionId": "f26f181d-7891-4720-b022-b074ec1733ef",
"resourceId": "400692e5-b2db-478e-acb1-b77a92635ec6",
"vimLevelResourceType": "OS::Neutron::Net"
}
}
],
"affectedVirtualStorages": [
{
"id": "81ae44f6-b65b-47aa-a578-e53b7a50a574",
"virtualStorageDescId": "Storage",
"changeType": "ADDED",
"storageResource": {
"vimConnectionId": "f26f181d-7891-4720-b022-b074ec1733ef",
"resourceId": "842f527e-0092-4f11-aede-f981ba4fd884",
"vimLevelResourceType": "OS::Cinder::Volume"
}
}
]
},
"_links": {
"self": {
"href": "http://sample.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468"
},
"vnfInstance": {
"href": "http://sample.com/vnflcm/v1/vnf_instances/0b7b95a9-21d5-4ac4-80c8-9ae9f7323787"
},
"grant":{
"href":"http://sample.com/grant/v1/grants/3432cebe-db0a-11e8-9023-005056317abe"
},
"retry":{
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/retry"
},
"rollback":{
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/rollback"
},
"fail":{
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/fail"
}
}
}
]

View File

@ -4,6 +4,7 @@
"stateEnteredTime": "2020-08-02T06:50:50.883373",
"startTime": "2020-08-02T06:41:34.883483",
"vnfInstanceId": "0b7b95a9-21d5-4ac4-80c8-9ae9f7323787",
"grantId": "3432cebe-db0a-11e8-9023-005056317abe",
"operation": "INSTANTIATE",
"isAutomaticInvocation": false,
"operationParams": "{
@ -65,6 +66,15 @@
},
"grant": {
"href": "/grant/v1/grants/3432cebe-db0a-11e8-9023-005056317abe"
},
"retry":{
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/retry"
},
"rollback":{
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/rollback"
},
"fail":{
"href":"http://sample1.com/vnflcm/v1/vnf_lcm_op_occs/d85c6ae4-af16-42c0-96fc-82f7c014c468/fail"
}
}
}

View File

@ -712,6 +712,116 @@ Response Parameters
- stateEnteredTime: state_entered_time
- startTime: start_time
- vnfInstanceId: vnf_lcm_vnf_instance_id
- grantId: grant_id
- operation: operation
- isAutomaticInvocation: is_automatic_invocation
- operationParams: operation_params
- isCancelPending: is_cancel_pending
- error: error
- title: error_title
- status: error_status
- detail: error_detail
- resourceChanges: resource_changes
- 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
- 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
- changedExtConnectivity: changed_ext_connectivity
- id: changed_ext_connectivity_id
- resourceHandle: resource_handle
- vimConnectionId: vim_connection_id
- resourceId: resource_handle_resource_id
- vimLevelResourceType: resource_handle_vim_level_resource_type
- extLinkPorts: ext_link_ports
- id: ext_link_port_id
- resourceHandle: resource_handle
- vimConnectionId: vim_connection_id
- resourceId: resource_handle_resource_id
- vimLevelResourceType: resource_handle_vim_level_resource_type
- cpInstanceId: cp_instance_id
- _links: vnf_instance_links
Response Example
----------------
.. literalinclude:: samples/vnflcm/show-vnflcm-operation-occurrence-response.json
:language: javascript
List VNF LCM operation occurrence
=================================
.. rest_method:: GET /vnflcm/v1/vnf_lcm_op_occs
The API consumer can use this method to query status information about multiple VNF lifecycle management operation
occurrences.
Response Codes
--------------
.. rest_status_code:: success status.yaml
- 200
.. rest_status_code:: error status.yaml
- 400
- 403
Response Parameters
-------------------
.. rest_parameters:: parameters_vnflcm.yaml
- id: vnf_lcm_op_occ_id_response
- operationState: operation_state
- stateEnteredTime: state_entered_time
- startTime: start_time
- vnfInstanceId: vnf_lcm_vnf_instance_id
- grantId: grant_id
- operation: operation
- isAutomaticInvocation: is_automatic_invocation
- operationParams: operation_params
@ -774,7 +884,7 @@ Response Parameters
Response Example
----------------
.. literalinclude:: samples/vnflcm/show-vnflcm-operation-occurrence-response.json
.. literalinclude:: samples/vnflcm/list-vnflcm-operation-occurrence-response.json
:language: javascript
Roll back a VNF lifecycle operation

View File

@ -163,6 +163,18 @@ def _parse_filter(filter_rule):
try:
tokens = filter_rule.split(',')
filter_type = None
# TODO(esto-aln): This condition and the lines below will be removed
# if JSON will be supported via OR Mapping. Currently this condition
# allows support of JSON strings '"{...}"' for filtering
if (tokens[2].startswith("'\"{") and
tokens[len(tokens) - 1].endswith("}\"'")):
tokens[2] = ','.join(tokens[2:])
# retain first 3 indices and remove the rest
# to process as string
tokens = tokens[0:3]
if len(tokens) >= 3:
if tokens[0] in _filters.SUPPORTED_OP_ONE:
filter_type = 'simple_filter_expr_one'

View File

@ -89,6 +89,8 @@ class ViewBuilder(base.BaseViewBuilder):
return vim_connections
# TODO(esto-aln): This method will be transferred to
# tacker/api/views/vnf_lcm_op_occs.py in the future
def _get_lcm_op_occs_links(self, vnf_lcm_op_occs):
_links = {
"self": {
@ -101,6 +103,12 @@ class ViewBuilder(base.BaseViewBuilder):
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.vnf_instance_id}
},
"retry": {
"href":
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/retry'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
},
"rollback": {
"href":
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/rollback'
@ -111,6 +119,12 @@ class ViewBuilder(base.BaseViewBuilder):
"href": '%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/grant'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
},
"fail": {
"href":
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/fail'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
}
}
@ -129,11 +143,13 @@ class ViewBuilder(base.BaseViewBuilder):
vnf_instance_dict.update(links)
return vnf_instance_dict
# TODO(esto-aln): This method will be transferred to
# tacker/api/views/vnf_lcm_op_occs.py in the future
def _get_vnf_lcm_op_occs(self, vnf_lcm_op_occs):
vnf_lcm_op_occs_dict = vnf_lcm_op_occs.to_dict()
vnf_lcm_op_occs_dict.pop('error_point')
vnf_lcm_op_occs_dict = utils.convert_snakecase_to_camelcase(
vnf_lcm_op_occs_dict)
vnf_lcm_op_occs_dict.pop('errorPoint')
links = self._get_lcm_op_occs_links(vnf_lcm_op_occs)
@ -257,5 +273,7 @@ class ViewBuilder(base.BaseViewBuilder):
def subscription_show(self, vnf_lcm_subscriptions):
return self._get_vnf_lcm_subscription(vnf_lcm_subscriptions)
# TODO(esto-aln): This method will be transferred to
# tacker/api/views/vnf_lcm_op_occs.py in the future
def show_lcm_op_occs(self, vnf_lcm_op_occs):
return self._get_vnf_lcm_op_occs(vnf_lcm_op_occs)

View File

@ -0,0 +1,118 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from tacker.api import views as base
from tacker.common import utils
import tacker.conf
from tacker.objects import vnf_lcm_op_occs as _vnf_lcm_op_occs
CONF = tacker.conf.CONF
LOG = logging.getLogger(__name__)
class ViewBuilder(base.BaseViewBuilder):
FLATTEN_ATTRIBUTES = _vnf_lcm_op_occs.VnfLcmOpOcc.FLATTEN_ATTRIBUTES
COMPLEX_ATTRIBUTES = _vnf_lcm_op_occs.VnfLcmOpOcc.COMPLEX_ATTRIBUTES
FLATTEN_COMPLEX_ATTRIBUTES = [key for key in FLATTEN_ATTRIBUTES.keys()
if '/' in key]
def _get_lcm_op_occs_links(self, vnf_lcm_op_occs):
_links = {
"self": {
"href": '%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
},
"vnfInstance": {
"href": '%(endpoint)s/vnflcm/v1/vnf_instances/%(id)s'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.vnf_instance_id}
},
"retry": {
"href":
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/retry'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
},
"rollback": {
"href":
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/rollback'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
},
"grant": {
"href": '%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/grant'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
},
"fail": {
"href":
'%(endpoint)s/vnflcm/v1/vnf_lcm_op_occs/%(id)s/fail'
% {"endpoint": CONF.vnf_lcm.endpoint_url,
"id": vnf_lcm_op_occs.id}
}
}
return {"_links": _links}
def _get_vnf_lcm_op_occs_list(self, vnf_lcm_op_occs, include_fields=None):
vnf_lcm_op_occs_dict = vnf_lcm_op_occs.to_dict(
include_fields=include_fields)
vnf_lcm_op_occs_dict = utils.convert_snakecase_to_camelcase(
vnf_lcm_op_occs_dict)
vnf_lcm_op_occs_dict.pop('errorPoint', None)
links = self._get_lcm_op_occs_links(vnf_lcm_op_occs)
vnf_lcm_op_occs_dict.update(links)
return vnf_lcm_op_occs_dict
def index(self, request, vnf_lcm_op_occs, all_fields=True,
exclude_fields=None, fields=None, exclude_default=False):
# Find out which fields are to be returned in the response.
if all_fields:
include_fields = set(self.FLATTEN_ATTRIBUTES.keys())
if fields:
fields = set(fields.split(','))
attributes = set(self.COMPLEX_ATTRIBUTES).intersection(fields)
for attribute in attributes:
add_fields = set([key for key in self.FLATTEN_ATTRIBUTES.
keys() if key.startswith(attribute)])
fields = fields.union(add_fields)
include_fields = set(
_vnf_lcm_op_occs.VnfLcmOpOcc.SIMPLE_ATTRIBUTES).union(fields)
elif exclude_default:
include_fields = set(
_vnf_lcm_op_occs.VnfLcmOpOcc.SIMPLE_ATTRIBUTES)
elif exclude_fields:
exclude_fields = set(exclude_fields.split(','))
exclude_additional_attributes = set(
self.COMPLEX_ATTRIBUTES).intersection(exclude_fields)
for attribute in exclude_additional_attributes:
fields = set([key for key in self.FLATTEN_ATTRIBUTES.keys()
if key.startswith(attribute)])
exclude_fields = exclude_fields.union(fields)
include_fields = set(self.FLATTEN_ATTRIBUTES.keys()) - \
exclude_fields
return [
self._get_vnf_lcm_op_occs_list(
vnf_lcm_op_occ, include_fields=include_fields)
for vnf_lcm_op_occ in vnf_lcm_op_occs]

View File

@ -42,6 +42,7 @@ from tacker._i18n import _
from tacker.api.schemas import vnf_lcm
from tacker.api import validation
from tacker.api.views import vnf_lcm as vnf_lcm_view
from tacker.api.views import vnf_lcm_op_occs as vnf_op_occs_view
from tacker.api.vnflcm.v1 import sync_resource
from tacker.common import exceptions
from tacker.common import utils
@ -52,6 +53,7 @@ from tacker.extensions import vnfm
from tacker import manager
from tacker import objects
from tacker.objects import fields
from tacker.objects import vnf_lcm_op_occs as vnf_lcm_op_occs_obj
from tacker.objects import vnf_lcm_subscriptions as subscription_obj
from tacker.plugins.common import constants
from tacker.policies import vnf_lcm as vnf_lcm_policies
@ -185,6 +187,7 @@ class VnfLcmController(wsgi.Controller):
super(VnfLcmController, self).__init__()
self.rpc_api = vnf_lcm_rpc.VNFLcmRPCAPI()
self._vnfm_plugin = manager.TackerManager.get_service_plugins()['VNFM']
self._view_builder_op_occ = vnf_op_occs_view.ViewBuilder()
def _get_vnf_instance_href(self, vnf_instance):
return '/vnflcm/v1/vnf_instances/%s' % vnf_instance.id
@ -1503,6 +1506,40 @@ class VnfLcmController(wsgi.Controller):
return self._make_problem_detail(error_msg,
500, title='Internal Server Error')
@wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST))
def list_lcm_op_occs(self, request):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'list_lcm_op_occs')
all_fields = request.GET.get('all_fields')
exclude_default = request.GET.get('exclude_default')
fields = request.GET.get('fields')
exclude_fields = request.GET.get('exclude_fields')
filters = request.GET.get('filter')
if not (all_fields or fields or exclude_fields):
exclude_default = True
self._view_builder_op_occ.validate_attribute_fields(
all_fields=all_fields, fields=fields,
exclude_fields=exclude_fields,
exclude_default=exclude_default)
filters = self._view_builder_op_occ.validate_filter(filters)
try:
vnf_lcm_op_occs = \
vnf_lcm_op_occs_obj.VnfLcmOpOccList.get_by_filters(
request.context, read_deleted='no', filters=filters)
except Exception as e:
LOG.exception(traceback.format_exc())
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
return self._view_builder_op_occ.index(request, vnf_lcm_op_occs,
all_fields=all_fields, exclude_fields=exclude_fields,
fields=fields, exclude_default=exclude_default)
def _make_problem_detail(
self,
detail,

View File

@ -138,3 +138,15 @@ class VnflcmAPIRouter(wsgi.Router):
methods = {"GET": "subscription_show", "DELETE": "delete_subscription"}
self._setup_route(mapper, "/subscriptions/{subscriptionId}",
methods, controller, default_resource)
# {apiRoot}/vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry resource
methods = {"POST": "retry"}
self._setup_route(mapper,
"/vnf_lcm_op_occs/{id}/retry",
methods, controller, default_resource)
# Allowed methods on
# {apiRoot}/vnflcm/v1/vnf_lcm_op_occs resource
methods = {"GET": "list_lcm_op_occs"}
self._setup_route(mapper, "/vnf_lcm_op_occs",
methods, controller, default_resource)

View File

@ -305,6 +305,7 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
vnf_instance_id = sa.Column(sa.String(36),
sa.ForeignKey('vnf_instances.id'),
nullable=False)
grant_id = sa.Column(sa.String(36), nullable=True)
state_entered_time = sa.Column(sa.DateTime(), nullable=False)
start_time = sa.Column(sa.DateTime(), nullable=False)
operation_state = sa.Column(sa.String(length=255), nullable=False)
@ -315,6 +316,7 @@ class VnfLcmOpOccs(model_base.BASE, models.SoftDeleteMixin,
error = sa.Column(sa.JSON(), nullable=True)
resource_changes = sa.Column(sa.JSON(), nullable=True)
changed_info = sa.Column(sa.JSON(), nullable=True)
changed_ext_connectivity = sa.Column(sa.JSON(), nullable=True)
error_point = sa.Column(sa.Integer, nullable=False)

View File

@ -0,0 +1,38 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# flake8: noqa: E402
"""add_column_to_vnf_lcm_op_occs
Revision ID: 3adac34764da
Revises: 62d18199909e
Create Date: 2021-02-16 16:19:12.100380
"""
# revision identifiers, used by Alembic.
revision = '3adac34764da'
down_revision = '7186440a306b'
from alembic import op
import sqlalchemy as sa
from tacker.db import migration
def upgrade(active_plugins=None, options=None):
op.add_column('vnf_lcm_op_occs',
sa.Column('grant_id', sa.VARCHAR(length=36), nullable=True))
op.add_column('vnf_lcm_op_occs',
sa.Column('changed_ext_connectivity', sa.JSON(), nullable=True))

View File

@ -1 +1 @@
7186440a306b
3adac34764da

View File

@ -225,8 +225,7 @@ class LcmOccsOperationState(BaseTackerEnum):
FAILED_TEMP = 'FAILED_TEMP'
FAILED = 'FAILED'
ALL = (STARTING, PROCESSING, COMPLETED,
FAILED_TEMP, FAILED)
ALL = (STARTING, PROCESSING, COMPLETED, FAILED_TEMP, FAILED)
class LcmOccsOperationType(BaseTackerEnum):

View File

@ -15,10 +15,13 @@ from datetime import datetime
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import timeutils
from oslo_versionedobjects import base as ovoo_base
from sqlalchemy import exc
from sqlalchemy.orm import joinedload
from sqlalchemy_filters import apply_filters
from tacker.common import exceptions
from tacker.common import utils
from tacker.db import api as db_api
from tacker.db.db_sqlalchemy import api
from tacker.db.db_sqlalchemy import models
@ -55,11 +58,34 @@ def _vnf_lcm_op_occ_update(context, values):
if values.changed_info:
update.update({'changed_info': jsonutils.dumps(
values.changed_info.to_dict())})
if 'changed_ext_connectivity' in values:
if values.changed_ext_connectivity:
update.update({'changed_ext_connectivity': jsonutils.dumps(
[chg_ext_conn.to_dict() for chg_ext_conn in
values.changed_ext_connectivity])})
api.model_query(context, models.VnfLcmOpOccs). \
filter_by(id=values.id). \
update(update, synchronize_session=False)
def _make_vnf_lcm_op_occs_list(context, op_occ_list,
db_op_occ_list):
lcm_op_occ_class = VnfLcmOpOcc
op_occ_list.objects = []
for db_op_occ in db_op_occ_list:
if(db_op_occ['changed_info'] and
isinstance(db_op_occ['changed_info'], str)):
db_op_occ['changed_info'] = jsonutils.loads(
db_op_occ['changed_info'])
vnf_lcm_op_occ_obj = lcm_op_occ_class._from_db_object(
context, lcm_op_occ_class(context), db_op_occ)
op_occ_list.objects.append(vnf_lcm_op_occ_obj)
op_occ_list.obj_reset_changes()
return op_occ_list
@db_api.context_manager.reader
def _vnf_lcm_op_occs_get_by_id(context, vnf_lcm_op_occ_id):
@ -91,6 +117,19 @@ def _vnf_lcm_op_occs_get_by_vnf_instance_id(context, vnf_instance_id):
return result
@db_api.context_manager.reader
def _vnf_lcm_op_occs_get_by_filters(context, read_deleted=None,
filters=None):
query = api.model_query(context, models.VnfLcmOpOccs,
read_deleted=read_deleted, project_only=True)
if filters:
query = apply_filters(query, filters)
return query.all()
@db_api.context_manager.reader
def _vnf_notify_get_by_id(context, vnf_instance_id, columns_to_join=None):
@ -170,6 +209,7 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
'state_entered_time': fields.DateTimeField(nullable=False),
'start_time': fields.DateTimeField(nullable=False),
'vnf_instance_id': fields.StringField(nullable=False),
'grant_id': fields.StringField(nullable=True),
'operation': fields.StringField(nullable=False),
'is_automatic_invocation': fields.BooleanField(default=False),
'operation_params': fields.StringField(nullable=True),
@ -180,9 +220,40 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
'ResourceChanges', nullable=True, default=None),
'changed_info': fields.ObjectField(
'VnfInfoModifications', nullable=True, default=None),
'changed_ext_connectivity': fields.ListOfObjectsField(
'ExtVirtualLinkInfo', nullable=True, default=[]),
'error_point': fields.IntegerField(nullable=True, default=0)
}
ALL_ATTRIBUTES = {
'id': ('id', 'string', 'VnfLcmOpOccs'),
'operationState': ('operation_state', 'string', 'VnfLcmOpOccs'),
'stateEnteredTime':
('state_entered_time', 'datetime', 'VnfLcmOpOccs'),
'startTime': ('start_time', 'datetime', 'VnfLcmOpOccs'),
'vnfInstanceId': ('vnf_instance_id', 'string', 'VnfLcmOpOccs'),
'grantId': ('grant_id', 'string', 'VnfLcmOpOccs'),
'operation': ('operation', 'string', 'VnfLcmOpOccs'),
'isAutomaticInvocation':
('is_automatic_invocation', 'boolean', 'VnfLcmOpOccs'),
'isCancelPending': ('is_cancel_pending', 'string', 'VnfLcmOpOccs'),
'errorPoint': ('error_point', 'number', 'VnfLcmOpOccs'),
'operationParams': ('operation_params', 'string', 'VnfLcmOpOccs'),
'error': ('error', 'string', 'VnfLcmOpOccs'),
'resourceChanges': ('resource_changes', 'string', 'VnfLcmOpOccs'),
'changedInfo': ('changed_info', 'string', 'VnfLcmOpOccs')
}
FLATTEN_ATTRIBUTES = utils.flatten_dict(ALL_ATTRIBUTES.copy())
SIMPLE_ATTRIBUTES = ['id', 'operationState', 'stateEnteredTime',
'startTime', 'vnfInstanceId', 'grantId', 'operation',
'isAutomaticInvocation',
'isCancelPending', 'errorPoint']
COMPLEX_ATTRIBUTES = ['error', 'resourceChanges', 'changedInfo',
'operationParams', 'changedExtConnectivity']
@base.remotable
def create(self):
updates = self.obj_clone()
@ -197,7 +268,9 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
def _from_db_object(context, vnf_lcm_op_occ_obj, db_vnf_lcm_op_occ):
special_fields = ['error',
'resource_changes', 'changed_info']
'resource_changes',
'changed_info',
'changed_ext_connectivity']
for key in vnf_lcm_op_occ_obj.fields:
if key in special_fields:
continue
@ -214,6 +287,12 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
changed_info = VnfInfoModifications.obj_from_primitive(
db_vnf_lcm_op_occ['changed_info'], context)
vnf_lcm_op_occ_obj.changed_info = changed_info
if db_vnf_lcm_op_occ['changed_ext_connectivity']:
changed_ext_conn = \
[objects.ExtVirtualLinkInfo.obj_from_primitive(
chg_ext_conn, context) for chg_ext_conn in
db_vnf_lcm_op_occ['changed_ext_connectivity']]
vnf_lcm_op_occ_obj.changed_ext_connectivity = changed_ext_conn
vnf_lcm_op_occ_obj._context = context
vnf_lcm_op_occ_obj.obj_reset_changes()
@ -238,6 +317,11 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
obj_data = VnfInfoModifications._from_dict(
primitive.get('changed_info'))
primitive.update({'changed_info': obj_data})
if 'changed_ext_connectivity' in primitive.keys():
obj_data = [objects.ExtVirtualLinkInfo.obj_from_primitive(
chg_ext_conn, context) for chg_ext_conn in
primitive.get('changed_ext_connectivity')]
primitive.update({'changed_ext_connectivity': obj_data})
vnf_lcm_op_occ = VnfLcmOpOcc._from_dict(primitive)
return vnf_lcm_op_occ
@ -252,6 +336,7 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
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')
grant_id = data_dict.get('grant_id')
operation = data_dict.get('operation')
is_automatic_invocation = data_dict.get('is_automatic_invocation')
operation_params = data_dict.get('operation_params')
@ -259,12 +344,14 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
error = data_dict.get('error')
resource_changes = data_dict.get('resource_changes')
changed_info = data_dict.get('changed_info')
changed_ext_connectivity = data_dict.get('changed_ext_connectivity')
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,
grant_id=grant_id,
operation=operation,
is_automatic_invocation=is_automatic_invocation,
operation_params=operation_params,
@ -272,28 +359,76 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
error=error,
resource_changes=resource_changes,
changed_info=changed_info,
changed_ext_connectivity=changed_ext_connectivity,
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}
def _get_error(self, include_fields=None):
key = 'error'
if key in include_fields:
return {key: self.error.to_dict()}
def _get_resource_changes(self, include_fields=None):
key = 'resourceChanges'
if key in include_fields:
return {key: self.resource_changes.to_dict()}
def _get_changed_info(self, include_fields=None):
key = 'changedInfo'
if key in include_fields:
return {key: self.changed_info.to_dict()}
def _get_operation_params(self, include_fields=None):
key = 'operationParams'
if key in include_fields:
return {key: self.operation_params}
def _get_changed_ext_connectivity(self, include_fields=None):
key = 'changedExtConnectivity'
return {key: [chg_ext_conn.to_dict() for chg_ext_conn in
self.changed_ext_connectivity]}
def to_dict(self, include_fields=None):
data = dict()
if not include_fields:
include_fields = set(self.FLATTEN_ATTRIBUTES.keys())
# add simple fields
to_fields = set(self.SIMPLE_ATTRIBUTES).intersection(include_fields)
for field in to_fields:
data[field] = getattr(self, self.FLATTEN_ATTRIBUTES[field][0])
# add complex attributes
if self.error:
data.update({'error': self.error.to_dict()})
error = self._get_error(include_fields=include_fields)
if error:
data.update(error)
if self.resource_changes:
data.update({'resource_changes': self.resource_changes.to_dict()})
resource_changes = self._get_resource_changes(
include_fields=include_fields)
if resource_changes:
data.update(resource_changes)
if self.changed_info:
data.update({'changed_info': self.changed_info.to_dict()})
changed_info = self._get_changed_info(
include_fields=include_fields)
if changed_info:
data.update(changed_info)
if self.operation_params:
operation_params = self._get_operation_params(
include_fields=include_fields)
if operation_params:
data.update(operation_params)
if self.changed_ext_connectivity:
changed_ext_connectivity = self._get_changed_ext_connectivity(
include_fields=include_fields)
if changed_ext_connectivity:
data.update(changed_ext_connectivity)
return data
@ -309,6 +444,22 @@ class VnfLcmOpOcc(base.TackerObject, base.TackerObjectDictCompat,
return cls._from_db_object(context, cls(), db_vnf_lcm_op_occs)
@base.TackerObjectRegistry.register
class VnfLcmOpOccList(ovoo_base.ObjectListBase, base.TackerObject):
VERSION = '1.0'
fields = {
'objects': fields.ListOfObjectsField('VnfLcmOpOcc')
}
@base.remotable_classmethod
def get_by_filters(cls, context, read_deleted=None, filters=None):
db_vnf_lcm_op_occs = _vnf_lcm_op_occs_get_by_filters(
context, read_deleted=read_deleted, filters=filters)
return _make_vnf_lcm_op_occs_list(context, cls(), db_vnf_lcm_op_occs)
@base.TackerObjectRegistry.register
class ResourceChanges(base.TackerObject,
base.TackerPersistentObject):

View File

@ -99,6 +99,17 @@ rules = [
}
]
),
policy.DocumentedRuleDefault(
name=VNFLCM % 'list_lcm_op_occs',
check_str=base.RULE_ADMIN_OR_OWNER,
description="Query VNF LCM operation occurrence",
operations=[
{
'method': 'GET',
'path': '/vnflcm/v1/vnf_lcm_op_occs'
}
]
),
policy.DocumentedRuleDefault(
name=VNFLCM % 'index',
check_str=base.RULE_ADMIN_OR_OWNER,

View File

@ -487,6 +487,14 @@ class BaseVnfLcmTest(base.BaseTackerTest):
return resp, response_body
def _list_op_occs(self, filter_string=''):
show_url = os.path.join(
self.base_vnf_lcm_op_occs_url)
resp, response_body = self.http_client.do_request(
show_url + filter_string, "GET")
return resp, response_body
def _wait_terminate_vnf_instance(self, id, timeout=None):
start_time = int(time.time())

View File

@ -778,6 +778,8 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
- Get vnflcmOpOccId to retry.
- Retry instantiation operation.
- Get opOccs information.
- Get opOccs list.
- Delete VNF instance.
- Delete subscription.
"""
# Create subscription and register it.
@ -855,6 +857,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# occ-list
resp, op_occs_info = self._list_op_occs()
self._assert_occ_list(resp, op_occs_info)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
@ -877,7 +883,9 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
- Get vnfcmOpOccId to retry.
- Retry Scale-Out operation.
- Get opOccs information.
- Terminate VNF.
- Get opOccs list.
- Terminate VNF instance.
- Delete VNF instance.
- Delete subscription.
"""
# Create subscription and register it.
@ -964,6 +972,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# occ-list
resp, op_occs_info = self._list_op_occs()
self._assert_occ_list(resp, op_occs_info)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
@ -1000,6 +1012,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
- Get vnflcmOpOccId to rollback.
- Rollback instantiation operation.
- Get opOccs information.
- Get opOccs list
- Delete subscription.
"""
# Create subscription and register it.
@ -1072,6 +1085,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# occ-list
resp, op_occs_info = self._list_op_occs()
self._assert_occ_list(resp, op_occs_info)
# Delete VNF
resp, _ = self._delete_vnf_instance(vnf_instance_id)
self._wait_lcm_done(vnf_instance_id=vnf_instance_id)
@ -1094,6 +1111,7 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
- Get vnfcmOpOccId to rollback.
- Rollback Scale-Out operation.
- Get opOccs information.
- get opOccs List.
- Terminate VNF.
- Delete subscription.
"""
@ -1176,6 +1194,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# occ-list
resp, op_occs_info = self._list_op_occs()
self._assert_occ_list(resp, op_occs_info)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
@ -1283,6 +1305,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# occ-list
resp, op_occs_info = self._list_op_occs()
self._assert_occ_list(resp, op_occs_info)
# Delete Stack
stack = self._get_heat_stack(vnf_instance_id)
self._delete_heat_stack(stack.id)
@ -1391,6 +1417,10 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
resp, op_occs_info = self._show_op_occs(vnflcm_op_occ_id)
self._assert_occ_show(resp, op_occs_info)
# occ-list
resp, op_occs_info = self._list_op_occs()
self._assert_occ_list(resp, op_occs_info)
# Terminate VNF
stack = self._get_heat_stack(vnf_instance_id)
resources_list = self._get_heat_resource_list(stack.id)
@ -1562,3 +1592,24 @@ class VnfLcmWithUserDataTest(vnflcm_base.BaseVnfLcmTest):
self.assertIsNotNone(_links.get('vnfInstance').get('href'))
self.assertIsNotNone(_links.get('grant'))
self.assertIsNotNone(_links.get('grant').get('href'))
def _assert_occ_list(self, resp, op_occs_list):
self.assertEqual(200, resp.status_code)
# Only check required parameters.
for op_occs_info in op_occs_list:
self.assertIsNotNone(op_occs_info.get('id'))
self.assertIsNotNone(op_occs_info.get('operationState'))
self.assertIsNotNone(op_occs_info.get('stateEnteredTime'))
self.assertIsNotNone(op_occs_info.get('vnfInstanceId'))
self.assertIsNotNone(op_occs_info.get('operation'))
self.assertIsNotNone(op_occs_info.get('isAutomaticInvocation'))
self.assertIsNotNone(op_occs_info.get('isCancelPending'))
_links = op_occs_info.get('_links')
self.assertIsNotNone(_links.get('self'))
self.assertIsNotNone(_links.get('self').get('href'))
self.assertIsNotNone(_links.get('vnfInstance'))
self.assertIsNotNone(_links.get('vnfInstance').get('href'))
self.assertIsNotNone(_links.get('grant'))
self.assertIsNotNone(_links.get('grant').get('href'))

View File

@ -512,3 +512,23 @@ def get_vnf(vnfd_id, vim_id):
'placement_attr': "test_placement_attr",
'vim_id': vim_id
}
def get_changed_ext_conn_data():
return [{
"id": uuidsentinel.change_ext_conn_id,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.vl_resource_id,
"vim_level_resource_type": "OS::Neutron::Net",
},
"ext_link_ports": [{
"id": uuidsentinel.ext_link_ports_id,
"resource_handle": {
"vim_connection_id": uuidsentinel.vim_connection_id,
"resource_id": uuidsentinel.port_resource_id,
"vim_level_resource_type": "OS::Neutron::Port",
},
"cp_instance_id": uuidsentinel.cp_instance_id,
}]
}]

View File

@ -90,10 +90,13 @@ class TestVnfLcmOpOcc(SqlTestCase):
problem_obj.detail = 'test_err'
changed_info = objects.vnf_lcm_op_occs.VnfInfoModifications(
context=self.context, **fakes.get_changed_info_data())
changed_ext_conn = [objects.ExtVirtualLinkInfo.obj_from_primitive(
i, self.context) for i in fakes.get_changed_ext_conn_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.changed_ext_connectivity = changed_ext_conn
vnf_lcm_op_occs.save()
self.assertEqual('FAILED_TEMP', vnf_lcm_op_occs.operation_state)

View File

@ -1265,20 +1265,28 @@ VNFLCMOPOCC_RESPONSE = {
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_instances/'
'f26f181d-7891-4720-b022-b074ec1733ef'
},
"retry": {
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
'f26f181d-7891-4720-b022-b074ec1733ef/retry'
},
"rollback": {
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
'f26f181d-7891-4720-b022-b074ec1733ef/rollback'
},
"grant": {
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
'f26f181d-7891-4720-b022-b074ec1733ef/grant'
}},
'f26f181d-7891-4720-b022-b074ec1733ef/grant',
},
"fail": {
"href": CONF.vnf_lcm.endpoint_url + '/vnflcm/v1/vnf_lcm_op_occs/'
'f26f181d-7891-4720-b022-b074ec1733ef/fail'}},
'operationState': 'COMPLETED',
'stateEnteredTime': datetime.datetime(1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'startTime': datetime.datetime(1900, 1, 1, 1, 1, 1,
tzinfo=iso8601.UTC),
'vnfInstanceId': 'f26f181d-7891-4720-b022-b074ec1733ef',
'grantId': 'f26f181d-7891-4720-b022-b074ec1733ef',
'operation': 'MODIFY_INFO',
'isAutomaticInvocation': False,
'operationParams': '{"is_reverse": False, "is_auto": False}',
@ -1336,7 +1344,24 @@ VNFLCMOPOCC_RESPONSE = {
'vnfProductName': 'fake_vnf_product_name',
'vnfSoftwareVersion': 'fake_vnf_software_version',
'vnfdVersion': 'fake_vnfd_version'
}
},
"changedExtConnectivity": [{
"id": constants.UUID,
"resourceHandle": {
"vimConnectionId": constants.UUID,
"resourceId": constants.UUID,
"vimLevelResourceType": "OS::Neutron::Net",
},
"extLinkPorts": [{
"id": constants.UUID,
"resourceHandle": {
"vimConnectionId": constants.UUID,
"resourceId": constants.UUID,
"vimLevelResourceType": "OS::Neutron::Port",
},
"cpInstanceId": constants.UUID,
}]
}]
}
VNFLCMOPOCC_INDEX_RESPONSE = [VNFLCMOPOCC_RESPONSE]
@ -1436,6 +1461,28 @@ def fake_vnf_lcm_op_occs():
}
changed_info_obj = objects.VnfInfoModifications(**changed_info)
changed_ext_connectivity = [{
"id": constants.UUID,
"resource_handle": {
"vim_connection_id": constants.UUID,
"resource_id": constants.UUID,
"vim_level_resource_type": "OS::Neutron::Net",
},
"ext_link_ports": [{
"id": constants.UUID,
"resource_handle": {
"vim_connection_id": constants.UUID,
"resource_id": constants.UUID,
"vim_level_resource_type": "OS::Neutron::Port",
},
"cp_instance_id": constants.UUID,
}]
}]
changed_ext_connectivity_obj = \
[objects.ExtVirtualLinkInfo.obj_from_primitive(
chg_ext_conn, context) for chg_ext_conn in
changed_ext_connectivity]
dt = datetime.datetime(1900, 1, 1, 1, 1, 1, tzinfo=iso8601.UTC)
vnf_lcm_op_occs = {
'id': constants.UUID,
@ -1443,13 +1490,15 @@ def fake_vnf_lcm_op_occs():
'state_entered_time': dt,
'start_time': dt,
'vnf_instance_id': constants.UUID,
'grant_id': constants.UUID,
'operation': 'MODIFY_INFO',
'is_automatic_invocation': False,
'operation_params': '{"is_reverse": False, "is_auto": False}',
'is_cancel_pending': False,
'error': error_obj,
'resource_changes': resource_changes_obj,
'changed_info': changed_info_obj
'changed_info': changed_info_obj,
'changed_ext_connectivity': changed_ext_connectivity_obj,
}
return vnf_lcm_op_occs
@ -1484,3 +1533,10 @@ def vnf_data(status='ACTIVE'):
name='test',
status=status,
vim_id=uuidsentinel.vim_id)
def return_vnf_lcm_opoccs_list():
vnf_lcm_op_occs = fake_vnf_lcm_op_occs()
obj = objects.VnfLcmOpOcc(**vnf_lcm_op_occs)
return [obj]

View File

@ -3366,3 +3366,141 @@ class TestController(base.TestCase):
resp = req.get_response(self.app)
self.assertEqual(http_client.INTERNAL_SERVER_ERROR, resp.status_code)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
def test_op_occ_list(self, mock_op_occ_list):
req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs')
complex_attributes = [
'error',
'resourceChanges',
'operationParams',
'changedInfo']
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
expected_result = fakes.index_response(
remove_attrs=complex_attributes)
mock_op_occ_list.return_value = vnf_lcm_op_occ
res_dict = self.controller.list_lcm_op_occs(req)
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
@ddt.data(
{'filter': '(eq,id,f26f181d-7891-4720-b022-b074ec1733ef)'},
{'filter': '(neq,operationState,COMPLETED)'},
{'filter': '(gte,stateEnteredTime,2020-03-14 04:10:15+00:00)'},
{'filter': '(eq,isAutomaticInvocation,False)'},
{'filter': '(lt,errorPoint,4)'},
{'filter':
"""(eq,error,'"{"title": "ERROR",
"status": 500, "detail": "ERROR"}"')"""},
{'filter':
"""(in,changedInfo,'"{"vnfInstanceName": "test"}"')"""},
{'filter':
"""(neq,operationParams,'"{"terminationType": "FORCEFUL"}"')"""},
)
def test_op_occ_filter_attributes(self, filter_params,
mock_op_occ_list):
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
complex_attributes = [
'error',
'resourceChanges',
'operationParams',
'changedInfo']
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
expected_result = fakes.index_response(
remove_attrs=complex_attributes)
mock_op_occ_list.return_value = vnf_lcm_op_occ
res_dict = self.controller.list_lcm_op_occs(req)
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
def test_op_occ_filter_attributes_invalid_filter(self, mock_op_occ_list):
query = urllib.parse.urlencode({'filter': '(lt,non_existing,4)'})
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
mock_op_occ_list.return_value = vnf_lcm_op_occ
self.assertRaises(
exceptions.ValidationError, self.controller.list_lcm_op_occs, req)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
def test_op_occ_attribute_selector_all_fields(self, mock_op_occ_list):
params = {'all_fields': 'True'}
query = urllib.parse.urlencode(params)
req = fake_request.HTTPRequest.blank('/vnflcm/v1/vnf_lcm_op_occs?' +
query)
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
expected_result = fakes.index_response()
mock_op_occ_list.return_value = vnf_lcm_op_occ
res_dict = self.controller.list_lcm_op_occs(req)
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
@ddt.data(
{'fields': 'error'},
{'fields': 'resourceChanges'},
{'fields': 'operationParams'},
{'fields': 'changedInfo'}
)
def test_op_occ_attribute_selector_fields(self, filter_params,
mock_op_occ_list):
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
complex_attributes = [
'error',
'resourceChanges',
'operationParams',
'changedInfo']
remove_attributes = [
x for x in complex_attributes if x != filter_params['fields']]
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
expected_result = fakes.index_response(remove_attrs=remove_attributes)
mock_op_occ_list.return_value = vnf_lcm_op_occ
res_dict = self.controller.list_lcm_op_occs(req)
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
@ddt.data(
{'exclude_fields': 'error'},
{'exclude_fields': 'resourceChanges'},
{'exclude_fields': 'operationParams'},
{'exclude_fields': 'changedInfo'}
)
def test_op_occ_attribute_selector_exclude_fields(self, filter_params,
mock_op_occ_list):
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
remove_attributes = [filter_params['exclude_fields']]
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
expected_result = fakes.index_response(remove_attrs=remove_attributes)
mock_op_occ_list.return_value = vnf_lcm_op_occ
res_dict = self.controller.list_lcm_op_occs(req)
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfLcmOpOccList, "get_by_filters")
def test_op_occ_attribute_selector_fields_error(self, mock_op_occ_list):
query = urllib.parse.urlencode({'fields': 'non_existent_column'})
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_lcm_op_occs?' + query)
vnf_lcm_op_occ = fakes.return_vnf_lcm_opoccs_list()
mock_op_occ_list.return_value = vnf_lcm_op_occ
self.assertRaises(
exceptions.ValidationError, self.controller.list_lcm_op_occs, req)