From c772a1dbc7ace58d099570d41a889adf851c8ba8 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Mon, 28 Dec 2015 16:05:13 -0800 Subject: Added networking-sfc from openstack project with merge date Dec 23 2015 Added patch 13 for subject "add missing db migration files" Change-Id: Id51a160335a14870c1dd816a44baf9b1958b9ac6 --- .../tests/unit/db/test_flowclassifier_db.py | 677 +++++++++++++++++++++ 1 file changed, 677 insertions(+) create mode 100644 networking_sfc/tests/unit/db/test_flowclassifier_db.py (limited to 'networking_sfc/tests/unit/db/test_flowclassifier_db.py') diff --git a/networking_sfc/tests/unit/db/test_flowclassifier_db.py b/networking_sfc/tests/unit/db/test_flowclassifier_db.py new file mode 100644 index 0000000..36c9af8 --- /dev/null +++ b/networking_sfc/tests/unit/db/test_flowclassifier_db.py @@ -0,0 +1,677 @@ +# Copyright 2015 Futurewei. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import contextlib +import mock +import six +import webob.exc + +from oslo_config import cfg +from oslo_utils import importutils +from oslo_utils import uuidutils + +from neutron.api import extensions as api_ext +from neutron.common import config +from neutron.common import constants as const +import neutron.extensions as nextensions + +from networking_sfc.db import flowclassifier_db as fdb +from networking_sfc import extensions +from networking_sfc.extensions import flowclassifier as fc_ext +from networking_sfc.tests import base + + +DB_FLOWCLASSIFIER_PLUGIN_CLASS = ( + "networking_sfc.db.flowclassifier_db.FlowClassifierDbPlugin" +) +extensions_path = ':'.join(extensions.__path__ + nextensions.__path__) + + +class FlowClassifierDbPluginTestCaseBase(base.BaseTestCase): + def _create_flow_classifier( + self, fmt, flow_classifier=None, expected_res_status=None, **kwargs + ): + ctx = kwargs.get('context', None) + tenant_id = kwargs.get('tenant_id', self._tenant_id) + data = {'flow_classifier': flow_classifier or {}} + if ctx is None: + data['flow_classifier'].update({'tenant_id': tenant_id}) + req = self.new_create_request( + 'flow_classifiers', data, fmt, context=ctx + ) + res = req.get_response(self.ext_api) + if expected_res_status: + self.assertEqual(res.status_int, expected_res_status) + return res + + @contextlib.contextmanager + def flow_classifier( + self, fmt=None, flow_classifier=None, do_delete=True, **kwargs + ): + if not fmt: + fmt = self.fmt + res = self._create_flow_classifier(fmt, flow_classifier, **kwargs) + if res.status_int >= 400: + raise webob.exc.HTTPClientError(code=res.status_int) + flow_classifier = self.deserialize(fmt or self.fmt, res) + yield flow_classifier + if do_delete: + self._delete('flow_classifiers', + flow_classifier['flow_classifier']['id']) + + def _get_expected_flow_classifier(self, flow_classifier): + expected_flow_classifier = { + 'name': flow_classifier.get('name') or '', + 'description': flow_classifier.get('description') or '', + 'source_port_range_min': flow_classifier.get( + 'source_port_range_min'), + 'source_port_range_max': flow_classifier.get( + 'source_port_range_max'), + 'destination_port_range_min': flow_classifier.get( + 'destination_port_range_min'), + 'destination_port_range_max': flow_classifier.get( + 'destination_port_range_max'), + 'ethertype': flow_classifier.get( + 'ethertype') or 'IPv4', + 'protocol': flow_classifier.get( + 'protocol'), + 'l7_parameters': flow_classifier.get( + 'l7_parameters') or {} + } + if ( + 'source_ip_prefix' in flow_classifier and + flow_classifier['source_ip_prefix'] + ): + expected_flow_classifier['source_ip_prefix'] = ( + flow_classifier['source_ip_prefix']) + if ( + 'destination_ip_prefix' in flow_classifier and + flow_classifier['destination_ip_prefix'] + ): + expected_flow_classifier['destination_ip_prefix'] = ( + flow_classifier['destination_ip_prefix']) + return expected_flow_classifier + + def _test_create_flow_classifier( + self, flow_classifier, expected_flow_classifier=None + ): + if expected_flow_classifier is None: + expected_flow_classifier = self._get_expected_flow_classifier( + flow_classifier) + with self.flow_classifier(flow_classifier=flow_classifier) as fc: + for k, v in six.iteritems(expected_flow_classifier): + self.assertIn(k, fc['flow_classifier']) + self.assertEqual(fc['flow_classifier'][k], v) + + +class FlowClassifierDbPluginTestCase( + base.NeutronDbPluginV2TestCase, + FlowClassifierDbPluginTestCaseBase +): + resource_prefix_map = dict( + (k, fc_ext.FLOW_CLASSIFIER_PREFIX) + for k in fc_ext.RESOURCE_ATTRIBUTE_MAP.keys() + ) + + def setUp(self, core_plugin=None, flowclassifier_plugin=None, + ext_mgr=None): + mock_log_p = mock.patch.object(fdb, 'LOG') + self.mock_log = mock_log_p.start() + cfg.CONF.register_opts(fc_ext.flow_classifier_quota_opts, 'QUOTAS') + if not flowclassifier_plugin: + flowclassifier_plugin = DB_FLOWCLASSIFIER_PLUGIN_CLASS + service_plugins = { + fc_ext.FLOW_CLASSIFIER_EXT: flowclassifier_plugin + } + fdb.FlowClassifierDbPlugin.supported_extension_aliases = [ + fc_ext.FLOW_CLASSIFIER_EXT] + fdb.FlowClassifierDbPlugin.path_prefix = ( + fc_ext.FLOW_CLASSIFIER_PREFIX + ) + super(FlowClassifierDbPluginTestCase, self).setUp( + ext_mgr=ext_mgr, + plugin=core_plugin, + service_plugins=service_plugins + ) + if not ext_mgr: + self.flowclassifier_plugin = importutils.import_object( + flowclassifier_plugin) + ext_mgr = api_ext.PluginAwareExtensionManager( + extensions_path, + {fc_ext.FLOW_CLASSIFIER_EXT: self.flowclassifier_plugin} + ) + app = config.load_paste_app('extensions_test_app') + self.ext_api = api_ext.ExtensionMiddleware(app, ext_mgr=ext_mgr) + + def test_create_flow_classifier(self): + self._test_create_flow_classifier({}) + + def test_quota_create_flow_classifier(self): + cfg.CONF.set_override('quota_flow_classifier', 3, group='QUOTAS') + self._create_flow_classifier(self.fmt, {}, expected_res_status=201) + self._create_flow_classifier(self.fmt, {}, expected_res_status=201) + self._create_flow_classifier(self.fmt, {}, expected_res_status=201) + self._create_flow_classifier(self.fmt, {}, expected_res_status=409) + + def test_create_flow_classifier_with_all_fields(self): + self._test_create_flow_classifier({ + 'name': 'test1', + 'ethertype': const.IPv4, + 'protocol': const.PROTO_NAME_TCP, + 'source_port_range_min': 100, + 'source_port_range_max': 200, + 'destination_port_range_min': 101, + 'destination_port_range_max': 201, + 'source_ip_prefix': '10.100.0.0/16', + 'destination_ip_prefix': '10.200.0.0/16', + 'logical_source_port': None, + 'logical_destination_port': None, + 'l7_parameters': {} + }) + + def test_create_flow_classifier_with_all_supported_ethertype(self): + self._test_create_flow_classifier({ + 'ethertype': None + }) + self._test_create_flow_classifier({ + 'ethertype': 'IPv4' + }) + self._test_create_flow_classifier({ + 'ethertype': 'IPv6' + }) + + def test_create_flow_classifier_with_invalid_ethertype(self): + self._create_flow_classifier( + self.fmt, { + 'ethertype': 'unsupported', + }, + expected_res_status=400 + ) + + def test_create_flow_classifier_with_all_supported_protocol(self): + self._test_create_flow_classifier({ + 'protocol': None + }) + self._test_create_flow_classifier({ + 'protocol': const.PROTO_NAME_TCP + }) + self._test_create_flow_classifier({ + 'protocol': const.PROTO_NAME_UDP + }) + self._test_create_flow_classifier({ + 'protocol': const.PROTO_NAME_ICMP + }) + + def test_create_flow_classifier_with_invalid_protocol(self): + self._create_flow_classifier( + self.fmt, { + 'protocol': 'unsupported', + }, + expected_res_status=400 + ) + + def test_create_flow_classifier_with_all_supported_port_protocol(self): + self._test_create_flow_classifier({ + 'source_port_range_min': None, + 'source_port_range_max': None, + 'destination_port_range_min': None, + 'destination_port_range_max': None + }) + self._test_create_flow_classifier({ + 'source_port_range_min': 100, + 'source_port_range_max': 200, + 'destination_port_range_min': 100, + 'destination_port_range_max': 200, + 'protocol': const.PROTO_NAME_TCP + }) + self._test_create_flow_classifier({ + 'source_port_range_min': 100, + 'source_port_range_max': 100, + 'destination_port_range_min': 100, + 'destination_port_range_max': 100, + 'protocol': const.PROTO_NAME_TCP + }) + self._test_create_flow_classifier({ + 'source_port_range_min': '100', + 'source_port_range_max': '200', + 'destination_port_range_min': '100', + 'destination_port_range_max': '200', + 'protocol': const.PROTO_NAME_UDP + }, { + 'source_port_range_min': 100, + 'source_port_range_max': 200, + 'destination_port_range_min': 100, + 'destination_port_range_max': 200, + 'protocol': const.PROTO_NAME_UDP + }) + + def test_create_flow_classifier_with_invalid__port_protocol(self): + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': 'abc', + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_max': 'abc', + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': 100, + 'source_port_range_max': 99, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': 65536, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_max': 65536, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': -1, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_max': -1, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': 'abc', + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_max': 'abc', + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': 100, + 'destination_port_range_max': 99, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': 65536, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_max': 65536, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': -1, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_max': -1, + 'protocol': const.PROTO_NAME_TCP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': 100 + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_max': 100 + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': 100, + 'source_port_range_max': 200 + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_port_range_min': 100, + 'source_port_range_max': 200, + 'protocol': const.PROTO_NAME_ICMP + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': 100 + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_max': 100 + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': 100, + 'destination_port_range_max': 200 + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_port_range_min': 100, + 'destination_port_range_max': 200, + 'protocol': const.PROTO_NAME_ICMP + }, + expected_res_status=400 + ) + + def test_create_flow_classifier_with_all_supported_ip_prefix(self): + self._test_create_flow_classifier({ + 'source_ip_prefix': None, + 'destination_ip_prefix': None + }) + self._test_create_flow_classifier({ + 'source_ip_prefix': '10.0.0.0/8', + 'destination_ip_prefix': '10.0.0.0/8' + }) + + def test_create_flow_classifier_with_invalid_ip_prefix(self): + self._create_flow_classifier( + self.fmt, { + 'source_ip_prefix': '10.0.0.0/34' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_ip_prefix': '10.0.0.0.0/8' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_ip_prefix': '256.0.0.0/8' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'source_ip_prefix': '10.0.0.0' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_ip_prefix': '10.0.0.0/34' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_ip_prefix': '10.0.0.0.0/8' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_ip_prefix': '256.0.0.0/8' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'destination_ip_prefix': '10.0.0.0' + }, + expected_res_status=400 + ) + + def test_create_flow_classifier_with_all_supported_l7_parameters(self): + self._test_create_flow_classifier({ + 'l7_parameters': None + }) + self._test_create_flow_classifier({ + 'l7_parameters': {} + }) + + def test_create_flow_classifier_with_invalid_l7_parameters(self): + self._create_flow_classifier( + self.fmt, { + 'l7_parameters': {'abc': 'def'} + }, + expected_res_status=400 + ) + + def test_create_flow_classifier_with_port_id(self): + self._test_create_flow_classifier({ + 'logical_source_port': None, + 'logical_destination_port': None, + }) + with self.port( + name='test1' + ) as port: + self._test_create_flow_classifier({ + 'logical_source_port': port['port']['id'], + 'logical_destination_port': port['port']['id'], + }) + + def test_create_flow_classifier_with_nouuid_port_id(self): + self._create_flow_classifier( + self.fmt, { + 'logical_source_port': 'abc' + }, + expected_res_status=400 + ) + self._create_flow_classifier( + self.fmt, { + 'logical_destination_port': 'abc' + }, + expected_res_status=400 + ) + + def test_create_flow_classifier_with_unknown_port_id(self): + self._create_flow_classifier( + self.fmt, { + 'logical_source_port': uuidutils.generate_uuid() + }, + expected_res_status=404 + ) + self._create_flow_classifier( + self.fmt, { + 'logical_destination_port': uuidutils.generate_uuid() + }, + expected_res_status=404 + ) + + def test_list_flow_classifiers(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1' + }) as fc1, self.flow_classifier(flow_classifier={ + 'name': 'test2', + }) as fc2: + fcs = [fc1, fc2] + self._test_list_resources( + 'flow_classifier', fcs + ) + + def test_list_flow_classifiers_with_params(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1' + }) as fc1, self.flow_classifier(flow_classifier={ + 'name': 'test2', + }) as fc2: + self._test_list_resources( + 'flow_classifier', [fc1], + query_params='name=test1' + ) + self._test_list_resources( + 'flow_classifier', [fc2], + query_params='name=test2' + ) + self._test_list_resources( + 'flow_classifier', [], + query_params='name=test3' + ) + + def test_list_flow_classifiers_with_unknown_params(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1' + }) as fc1, self.flow_classifier(flow_classifier={ + 'name': 'test2', + }) as fc2: + self._test_list_resources( + 'flow_classifier', [fc1, fc2], + query_params='hello=test3' + ) + + def test_show_flow_classifier(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1' + }) as fc: + req = self.new_show_request( + 'flow_classifiers', fc['flow_classifier']['id'] + ) + res = self.deserialize( + self.fmt, req.get_response(self.ext_api) + ) + for k, v in six.iteritems(fc['flow_classifier']): + self.assertEqual(res['flow_classifier'][k], v) + + def test_show_flow_classifier_noexist(self): + req = self.new_show_request( + 'flow_classifiers', '1' + ) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 404) + + def test_update_flow_classifier(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1', + 'description': 'desc1' + }) as fc: + updates = { + 'name': 'test2', + 'description': 'desc2', + } + req = self.new_update_request( + 'flow_classifiers', {'flow_classifier': updates}, + fc['flow_classifier']['id'] + ) + res = self.deserialize( + self.fmt, + req.get_response(self.ext_api) + ) + expected = fc['flow_classifier'] + expected.update(updates) + for k, v in six.iteritems(expected): + self.assertEqual(res['flow_classifier'][k], v) + req = self.new_show_request( + 'flow_classifiers', fc['flow_classifier']['id'] + ) + res = self.deserialize( + self.fmt, req.get_response(self.ext_api) + ) + for k, v in six.iteritems(expected): + self.assertEqual(res['flow_classifier'][k], v) + + def _test_update_with_field( + self, fc, updates, expected_status_code + ): + req = self.new_update_request( + 'flow_classifiers', {'flow_classifier': updates}, + fc['flow_classifier']['id'] + ) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, expected_status_code) + + def test_update_flow_classifer_unsupported_fields(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1', + 'description': 'desc1' + }) as fc: + self._test_update_with_field( + fc, {'ethertype': None}, 400) + self._test_update_with_field( + fc, {'protocol': None}, 400) + self._test_update_with_field( + fc, {'source_port_range_min': None}, 400) + self._test_update_with_field( + fc, {'source_port_range_max': None}, 400) + self._test_update_with_field( + fc, {'destination_port_range_min': None}, 400) + self._test_update_with_field( + fc, {'destination_port_range_max': None}, 400) + self._test_update_with_field( + fc, {'source_ip_prefix': None}, 400) + self._test_update_with_field( + fc, {'destination_ip_prefix': None}, 400) + self._test_update_with_field( + fc, {'l7_parameters': None}, 400) + + def test_delete_flow_classifier(self): + with self.flow_classifier(flow_classifier={ + 'name': 'test1' + }, do_delete=False) as fc: + req = self.new_delete_request( + 'flow_classifiers', fc['flow_classifier']['id'] + ) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 204) + req = self.new_show_request( + 'flow_classifiers', fc['flow_classifier']['id'] + ) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 404) + + def test_delete_flow_classifier_noexist(self): + req = self.new_delete_request( + 'flow_classifiers', '1' + ) + res = req.get_response(self.ext_api) + self.assertEqual(res.status_int, 404) -- cgit 1.2.3-korg