Support attribute filtering for List VNF

Supported Attribute Filtering, filter defined in SOL013.

Implements: blueprint support-etsi-nfv-specs
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/victoria/enhancement_enhance-vnf-lcm-api-support.html
Change-Id: I98ce706a5081bbfa4947d782e2e28cd7667c5fdd
This commit is contained in:
Aldinson Esto 2020-08-27 11:15:36 +09:00 committed by Koichi Edagawa
parent 128cd17766
commit 60dd90d02b
8 changed files with 371 additions and 23 deletions

View File

@ -410,6 +410,8 @@ List VNF Instance
.. rest_method:: GET /vnflcm/v1/vnf_instances
The GET method queries information about multiple VNF instances.
In Victoria release, added attribute-based filtering expression (for VnfInstance)
that follows clause 5.2 of ETSI GS NFV SOL13.
Response Codes
--------------

View File

@ -13,11 +13,15 @@
# License for the specific language governing permissions and limitations
# under the License.
from tacker.api import views as base
from tacker.common import utils
from tacker.objects import fields
from tacker.objects import vnf_instance as _vnf_instance
class ViewBuilder(object):
class ViewBuilder(base.BaseViewBuilder):
FLATTEN_ATTRIBUTES = _vnf_instance.VnfInstance.FLATTEN_ATTRIBUTES
def _get_links(self, vnf_instance):
links = {
@ -54,7 +58,8 @@ class ViewBuilder(object):
return {"_links": links}
def _get_vnf_instance_info(self, vnf_instance):
def _get_vnf_instance_info(self,
vnf_instance, api_version=None):
vnf_instance_dict = vnf_instance.to_dict()
if 'vnf_metadata' in vnf_instance_dict:
metadata_val = vnf_instance_dict.pop('vnf_metadata')
@ -63,6 +68,9 @@ class ViewBuilder(object):
vnf_instance_dict = utils.convert_snakecase_to_camelcase(
vnf_instance_dict)
if api_version == "2.6.1":
del vnf_instance_dict["vnfPkgId"]
links = self._get_links(vnf_instance)
vnf_instance_dict.update(links)
@ -74,6 +82,6 @@ class ViewBuilder(object):
def show(self, vnf_instance):
return self._get_vnf_instance_info(vnf_instance)
def index(self, vnf_instances):
return [self._get_vnf_instance_info(vnf_instance)
def index(self, vnf_instances, api_version=None):
return [self._get_vnf_instance_info(vnf_instance, api_version)
for vnf_instance in vnf_instances]

View File

@ -207,8 +207,16 @@ class VnfLcmController(wsgi.Controller):
@wsgi.expected_errors((http_client.FORBIDDEN))
def index(self, request):
context = request.environ['tacker.context']
vnf_instances = objects.VnfInstanceList.get_all(context)
return self._view_builder.index(vnf_instances)
context.can(vnf_lcm_policies.VNFLCM % 'index')
filters = request.GET.get('filter')
filters = self._view_builder.validate_filter(filters)
vnf_instances = objects.VnfInstanceList.get_by_filters(
request.context, filters=filters)
api_version = request.headers['Version']
return self._view_builder.index(vnf_instances, api_version)
@check_vnf_state(action="delete",
instantiation_state=[fields.VnfInstanceState.NOT_INSTANTIATED],

View File

@ -18,15 +18,18 @@ from oslo_utils import timeutils
from oslo_utils import uuidutils
from oslo_versionedobjects import base as ovoo_base
from sqlalchemy.orm import joinedload
from sqlalchemy_filters import apply_filters
from tacker._i18n import _
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
from tacker import objects
from tacker.objects import base
from tacker.objects import fields
from tacker.objects import vnf_instantiated_info
LOG = logging.getLogger(__name__)
@ -100,6 +103,23 @@ def _vnf_instance_list(context, columns_to_join=None):
return query.all()
@db_api.context_manager.reader
def _vnf_instance_list_by_filter(context, columns_to_join=None,
filters=None):
query = api.model_query(context, models.VnfInstance,
read_deleted="no",
project_only=True)
if columns_to_join:
for column in columns_to_join:
query = query.options(joinedload(column))
if filters:
query = apply_filters(query, filters)
return query.all()
def _make_vnf_instance_list(context, vnf_instance_list, db_vnf_instance_list,
expected_attrs):
vnf_instance_cls = VnfInstance
@ -143,6 +163,34 @@ class VnfInstance(base.TackerObject, base.TackerPersistentObject,
'vnf_metadata': fields.DictOfStringsField(nullable=True, default={})
}
ALL_ATTRIBUTES = {
'id': ('id', "string", 'VnfInstance'),
'vnfInstanceName': ('vnf_instance_name', 'string', 'VnfInstance'),
'vnfInstanceDescription': (
'vnf_instance_description', 'string', 'VnfInstance'),
'instantiationState': ('instantiation_state', 'string', 'VnfInstance'),
'taskState': ('task_state', 'string', 'VnfInstance'),
'vnfdId': ('vnfd_id', 'string', 'VnfInstance'),
'vnfProvider': ('vnf_provider', 'string', 'VnfInstance'),
'vnfProductName': ('vnf_product_name', 'string', 'VnfInstance'),
'vnfSoftwareVersion': (
'vnf_software_version', 'string', 'VnfInstance'),
'vnfdVersion': ('vnfd_version', 'string', 'VnfInstance'),
'tenantId': ('tenant_id', 'string', 'VnfInstance'),
'vnfPkgId': ('vnf_pkg_id', 'string', 'VnfInstance'),
'vimConnectionInfo/*': ('vim_connection_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstance"}),
'metadata/*': ('vnf_metadata', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstance"}),
}
ALL_ATTRIBUTES.update(
vnf_instantiated_info.InstantiatedVnfInfo.ALL_ATTRIBUTES)
FLATTEN_ATTRIBUTES = utils.flatten_dict(ALL_ATTRIBUTES.copy())
def __init__(self, context=None, **kwargs):
super(VnfInstance, self).__init__(context, **kwargs)
self.obj_set_defaults()
@ -286,3 +334,14 @@ class VnfInstanceList(ovoo_base.ObjectListBase, base.TackerObject):
columns_to_join=expected_attrs)
return _make_vnf_instance_list(context, cls(), db_vnf_instances,
expected_attrs)
@base.remotable_classmethod
def get_by_filters(cls, context, filters=None,
expected_attrs=None):
expected_attrs = ["instantiated_vnf_info"]
db_vnf_instances = _vnf_instance_list_by_filter(
context, columns_to_join=expected_attrs,
filters=filters)
return _make_vnf_instance_list(context, cls(), db_vnf_instances,
expected_attrs)

View File

@ -16,6 +16,7 @@
from oslo_log import log as logging
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
@ -78,9 +79,48 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat,
default=None),
'additional_params': fields.DictOfStringsField(nullable=True,
default={})
}
ALL_ATTRIBUTES = {
'instantiatedInfo': {
'flavourId': ('id', 'string', 'VnfInstantiatedInfo'),
'vnfInstanceId':
('vnf_instance_id', 'string', 'VnfInstantiatedInfo'),
'vnfState': ('vnf_state', 'string', 'VnfInstantiatedInfo'),
'instanceId': ('instance_id', 'string', 'VnfInstantiatedInfo'),
'instantiationLevelId':
('instantiation_level_id', 'string', 'VnfInstantiatedInfo'),
'extCpInfo/*': ('ext_cp_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
'extVirtualLinkInfo/*': ('ext_virtual_link_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
'extManagedVirtualLinkInfo/*': (
'ext_managed_virtual_link_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
'vnfcResourceInfo/*': (
'vnfc_resource_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
'vnfVirtualLinkResourceInfo/*': (
'vnf_virtual_link_resource_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
'virtualStorageResourceInfo/*': (
'virtual_storage_resource_info', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
'additionalParams/*': (
'additional_params', 'key_value_pair',
{"key_column": "key", "value_column": "value",
"model": "VnfInstantiatedInfo"}),
}
}
FLATTEN_ATTRIBUTES = utils.flatten_dict(ALL_ATTRIBUTES.copy())
@staticmethod
def _from_db_object(context, inst_vnf_info, db_inst_vnf_info):

View File

@ -130,6 +130,19 @@ class TestVnfInstance(SqlTestCase):
self.assertTrue(result.objects, list)
self.assertTrue(result.objects)
def test_vnf_instance_list_get_by_filters(self):
vnf_instance_data = fakes.get_vnf_instance_data(
self.vnf_package.vnfd_id)
vnf_instance = objects.VnfInstance(context=self.context,
**vnf_instance_data)
vnf_instance.create()
filters = {'field': 'instantiation_state', 'model': 'VnfInstance',
'value': 'NOT_INSTANTIATED',
'op': '=='}
vnf_instance_list = objects.VnfInstanceList.get_by_filters(
self.context, filters=filters)
self.assertEqual(1, len(vnf_instance_list))
@mock.patch('tacker.objects.vnf_instance._destroy_vnf_instance')
def test_destroy(self, mock_vnf_destroy):
vnf_instance_data = fakes.get_vnf_instance_data(

View File

@ -172,7 +172,7 @@ def _fake_vnf_instance_not_instantiated_response(
def fake_vnf_instance_response(
instantiated_state=fields.VnfInstanceState.NOT_INSTANTIATED,
**updates):
api_version=None, **updates):
if instantiated_state == fields.VnfInstanceState.NOT_INSTANTIATED:
data = _fake_vnf_instance_not_instantiated_response(**updates)
else:
@ -191,6 +191,9 @@ def fake_vnf_instance_response(
data['instantiatedVnfInfo'] = _instantiated_vnf_info()
if api_version == '2.6.1':
del data['vnfPkgId']
return data

View File

@ -17,11 +17,12 @@ from unittest import mock
import ddt
from oslo_serialization import jsonutils
from six.moves import http_client
import urllib
from webob import exc
from tacker.api.vnflcm.v1 import controller
from tacker.common import exceptions
import tacker.conductor.conductorrpc.vnf_lcm_rpc as vnf_lcm_rpc
from tacker.conductor.conductorrpc.vnf_lcm_rpc import VNFLcmRPCAPI
from tacker.extensions import nfvo
from tacker import objects
from tacker.objects import fields
@ -327,7 +328,7 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
@mock.patch.object(VNFLcmRPCAPI, "instantiate")
def test_instantiate_with_deployment_flavour(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
@ -384,7 +385,7 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
@mock.patch.object(VNFLcmRPCAPI, "instantiate")
def test_instantiate_with_instantiation_level(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
@ -415,7 +416,7 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
@mock.patch.object(VNFLcmRPCAPI, "instantiate")
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,
@ -448,7 +449,7 @@ class TestController(base.TestCase):
@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")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
@mock.patch.object(VNFLcmRPCAPI, "instantiate")
def test_instantiate_with_non_existing_instantiation_level(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id,
@ -480,7 +481,7 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(objects.VnfPackageVnfd, 'get_by_id')
@mock.patch.object(objects.VnfPackage, "get_by_id")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "instantiate")
@mock.patch.object(VNFLcmRPCAPI, "instantiate")
def test_instantiate_with_vim_connection(
self, mock_instantiate, mock_vnf_package_get_by_id,
mock_vnf_package_vnfd_get_by_id, mock_save,
@ -821,7 +822,7 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "terminate")
@mock.patch.object(VNFLcmRPCAPI, "terminate")
@ddt.data({'terminationType': 'FORCEFUL'},
{'terminationType': 'GRACEFUL'},
{'terminationType': 'GRACEFUL',
@ -961,7 +962,7 @@ class TestController(base.TestCase):
@mock.patch.object(objects.VnfInstance, "get_by_id")
@mock.patch.object(objects.VnfInstance, "save")
@mock.patch.object(vnf_lcm_rpc.VNFLcmRPCAPI, "heal")
@mock.patch.object(VNFLcmRPCAPI, "heal")
@ddt.data({'cause': 'healing'}, {})
def test_heal(self, body, mock_rpc_heal, mock_save,
mock_vnf_by_id):
@ -1096,24 +1097,29 @@ class TestController(base.TestCase):
self.assertEqual(expected_message, exception.msg)
@mock.patch.object(objects.VnfInstanceList, "get_all")
def test_index(self, mock_vnf_list):
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(' ', '2.6.1')
def test_index(self, api_version, mock_vnf_list):
req = fake_request.HTTPRequest.blank('/vnf_instances')
req.headers['Version'] = api_version
vnf_instance_1 = fakes.return_vnf_instance()
vnf_instance_2 = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2]
resp = self.controller.index(req)
expected_result = [fakes.fake_vnf_instance_response(),
expected_result = [fakes.fake_vnf_instance_response(
api_version=api_version),
fakes.fake_vnf_instance_response(
fields.VnfInstanceState.INSTANTIATED)]
fields.VnfInstanceState.INSTANTIATED,
api_version=api_version)]
self.assertEqual(expected_result, resp)
@mock.patch.object(objects.VnfInstanceList, "get_all")
def test_index_empty_response(self, mock_vnf_list):
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(' ', '2.6.1')
def test_index_empty_response(self, api_version, mock_vnf_list):
req = fake_request.HTTPRequest.blank('/vnf_instances')
req.headers['Version'] = api_version
mock_vnf_list.return_value = []
resp = self.controller.index(req)
self.assertEqual([], resp)
@ -1212,3 +1218,212 @@ class TestController(base.TestCase):
"is in this state.")
self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
resp.json['conflictingRequest']['message'])
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': "(eq,vnfInstanceName,'dummy_name')"},
{'filter': "(in,vnfInstanceName,'dummy_name')"},
{'filter': "(cont,vnfInstanceName,'dummy_name')"},
{'filter': "(neq,vnfInstanceName,'dummy_name')"},
{'filter': "(nin,vnfInstanceName,'dummy_name')"},
{'filter': "(ncont,vnfInstanceName,'dummy_name')"},
{'filter': "(gt,vnfdVersion, 1)"},
{'filter': "(gte,vnfdVersion, 1)"},
{'filter': "(lt,vnfdVersion, 1)"},
{'filter': "(lte,vnfdVersion, 1)"},
)
def test_index_filter_operator(self, filter_params, mock_vnf_list):
"""Tests all supported operators in filter expression."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = api_version
vnf_instance_1 = fakes.return_vnf_instance()
vnf_instance_2 = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2]
res_dict = self.controller.index(req)
expected_result = [fakes.fake_vnf_instance_response(
api_version=api_version),
fakes.fake_vnf_instance_response(
fields.VnfInstanceState.INSTANTIATED,
api_version=api_version)]
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
def test_index_filter_combination(self, mock_vnf_list):
"""Test multiple filter parameters separated by semicolon."""
params = {
'filter': "(eq,vnfInstanceName,'dummy_name');"
"(eq,vnfInstanceDescription,'dummy_desc')"}
api_version = '2.6.1'
query = urllib.parse.urlencode(params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = '2.6.1'
vnf_instance_1 = fakes.return_vnf_instance()
vnf_instance_2 = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2]
res_dict = self.controller.index(req)
expected_result = [fakes.fake_vnf_instance_response(
api_version=api_version),
fakes.fake_vnf_instance_response(
fields.VnfInstanceState.INSTANTIATED,
api_version=api_version)]
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': "(eq,vnfInstanceName,dummy_value)"},
{'filter': "(eq,vnfInstanceDescription,dummy value)"},
{'filter': "(eq,instantiationState,'NOT_INSTANTIATED')"},
{'filter': "(eq,taskState,'ACTIVE')"},
{'filter': "(eq,vnfdId,'dummy_vnfd_id')"},
{'filter': "(eq,vnfProvider,'''dummy ''hi'' value''')"},
{'filter': "(eq,vnfProductName,'dummy_product_name')"},
{'filter': "(eq,vnfSoftwareVersion,'1.0')"},
{'filter': "(eq,vnfdVersion,'dummy_vnfd_version')"},
{'filter': "(eq,tenantId,'dummy_tenant_id')"},
{'filter': "(eq,vnfPkgId,'dummy_pkg_id')"},
{'filter': "(eq,vimConnectionInfo/accessInfo/region,'dummy_id')"},
{'filter': "(eq,instantiatedInfo/flavourId,'dummy_flavour')"},
{'filter': "(eq,instantiatedInfo/vnfInstanceId,'dummy_vnf_id')"},
{'filter': "(eq,instantiatedInfo/vnfState,'ACTIVE')"},
{'filter': "(eq,instantiatedInfo/instanceId,'dummy_vnf_id')"},
{'filter':
"(eq,instantiatedInfo/instantiationLevelId,'dummy_level_id')"},
{'filter': "(eq,instantiatedInfo/extCpInfo/id,'dummy_id')"},
{'filter': "(eq,instantiatedInfo/extVirtualLinkInfo/name,'dummy')"},
{'filter':
"(eq,instantiatedInfo/extManagedVirtualLinkInfo/id,'dummy_id')"},
{'filter': "(eq,instantiatedInfo/vnfcResourceInfo/vduId,'dummy_id')"},
{'filter':
"(eq,instantiatedInfo/vnfVirtualLinkResourceInfo/"
"vnfVirtualLinkDescId,'dummy_id')"},
{'filter':
"(eq,instantiatedInfo/virtualStorageResourceInfo/"
"virtualStorageDescId,'dummy_id')"},
{'filter': "(eq,instantiatedInfo/additionalParams/error,'dummy')"},
)
def test_index_filter_attributes(self, filter_params,
mock_vnf_list):
"""Test various attributes supported for filter parameter."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = '2.6.1'
vnf_instance_1 = fakes.return_vnf_instance()
vnf_instance_2 = fakes.return_vnf_instance(
fields.VnfInstanceState.INSTANTIATED)
mock_vnf_list.return_value = [vnf_instance_1, vnf_instance_2]
res_dict = self.controller.index(req)
expected_result = [fakes.fake_vnf_instance_response(
api_version=api_version),
fakes.fake_vnf_instance_response(
fields.VnfInstanceState.INSTANTIATED,
api_version=api_version)]
self.assertEqual(expected_result, res_dict)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': "(eq,vnfInstanceName,value"},
{'filter': "eq,vnfInstanceName,value)"},
{'filter': "(eq,vnfInstanceName,value);"},
{'filter': "(eq , vnfInstanceName ,value)"},
)
def test_index_filter_invalid_expression(self, filter_params,
mock_vnf_list):
"""Test invalid filter expression."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = api_version
self.assertRaises(exceptions.ValidationError,
self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': "(eq,vnfInstanceName,singl'quote)"},
{'filter': "(eq,vnfInstanceName,three''' quotes)"},
{'filter': "(eq,vnfInstanceName,round ) bracket)"},
{'filter': "(eq,vnfInstanceName,'dummy 'hi' value')"},
{'filter': "(eq,vnfInstanceName,'dummy's value')"},
{'filter': "(eq,vnfInstanceName,'three ''' quotes')"},
)
def test_index_filter_invalid_string_values(self, filter_params,
mock_vnf_list):
"""Test invalid string values as per ETSI NFV SOL013 5.2.2."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = api_version
self.assertRaises(exceptions.ValidationError,
self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': '(eq,vnfdId,value1,value2)'},
{'filter': '(fake,vnfdId,dummy_vnfd_id)'},
{'filter': '(,vnfdId,dummy_vnfd_id)'},
)
def test_index_filter_invalid_operator(self, filter_params,
mock_vnf_list):
"""Test invalid operator in filter expression."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = api_version
self.assertRaises(exceptions.ValidationError,
self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': '(eq,fakeattr,fakevalue)'},
{'filter': '(eq,,fakevalue)'},
)
def test_index_filter_invalid_attribute(self, filter_params,
mock_vnf_list):
"""Test invalid attribute in filter expression."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = api_version
self.assertRaises(exceptions.ValidationError,
self.controller.index, req)
@mock.patch.object(objects.VnfInstanceList, "get_by_filters")
@ddt.data(
{'filter': '(eq,data/size,fake_value)'},
{'filter': '(gt,data/createdAt,fake_value)'},
{'filter': '(eq,data/minDisk,fake_value)'},
{'filter': '(eq,data/minRam,fake_value)'},
)
def test_index_filter_invalid_value_type(self, filter_params,
mock_vnf_list):
"""Test values which doesn't match with attribute data type."""
api_version = '2.6.1'
query = urllib.parse.urlencode(filter_params)
req = fake_request.HTTPRequest.blank(
'/vnflcm/v1/vnf_instances?' + query)
req.headers['Version'] = api_version
self.assertRaises(exceptions.ValidationError,
self.controller.index, req)