From c3b2c2a9a22bac5cf17813c589444d3abebaa23b Mon Sep 17 00:00:00 2001 From: Wojciech Dec Date: Tue, 16 Aug 2016 19:27:01 +0200 Subject: Adding Mitaka networking-old module with the ODL topology based port binding resolution mechanism from https://review.openstack.org/333186 Change-Id: I10d400aac9bb639c146527f0f93e6925cb74d9de Signed-off-by: Wojciech Dec --- .../networking_odl/tests/unit/l3/__init__.py | 0 .../networking_odl/tests/unit/l3/test_l3_odl.py | 310 ++++++++++++ .../networking_odl/tests/unit/l3/test_l3_odl_v2.py | 526 +++++++++++++++++++++ 3 files changed, 836 insertions(+) create mode 100644 networking-odl/networking_odl/tests/unit/l3/__init__.py create mode 100644 networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py create mode 100644 networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py (limited to 'networking-odl/networking_odl/tests/unit/l3') diff --git a/networking-odl/networking_odl/tests/unit/l3/__init__.py b/networking-odl/networking_odl/tests/unit/l3/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py new file mode 100644 index 0000000..232864d --- /dev/null +++ b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py @@ -0,0 +1,310 @@ +# -*- coding: utf-8 -*- + +# 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. + +""" +test_l3_odl +---------------------------------- + +Tests for the L3 service plugin for networking-odl. +""" +import copy +import mock + +from neutron.extensions import l3 +from neutron.extensions import l3_ext_gw_mode +from neutron.tests.unit.api.v2 import test_base +from neutron.tests.unit.extensions import base as test_extensions_base +from webob import exc + +_get_path = test_base._get_path + + +class Testodll3(test_extensions_base.ExtensionTestCase): + + fmt = 'json' + + def setUp(self): + super(Testodll3, self).setUp() + # support ext-gw-mode + for key in l3.RESOURCE_ATTRIBUTE_MAP.keys(): + l3.RESOURCE_ATTRIBUTE_MAP[key].update( + l3_ext_gw_mode.EXTENDED_ATTRIBUTES_2_0.get(key, {})) + self._setUpExtension( + 'neutron.extensions.l3.RouterPluginBase', None, + l3.RESOURCE_ATTRIBUTE_MAP, l3.L3, '', + allow_pagination=True, allow_sorting=True, + supported_extension_aliases=['router', 'ext-gw-mode'], + use_quota=True) + + @staticmethod + def _get_mock_network_operation_context(): + current = {'status': 'ACTIVE', + 'subnets': [], + 'name': 'net1', + 'provider:physical_network': None, + 'admin_state_up': True, + 'tenant_id': 'test-tenant', + 'provider:network_type': 'local', + 'router:external': False, + 'shared': False, + 'id': 'd897e21a-dfd6-4331-a5dd-7524fa421c3e', + 'provider:segmentation_id': None} + context = mock.Mock(current=current) + return context + + @staticmethod + def _get_router_test(): + router_id = "234237d4-1e7f-11e5-9bd7-080027328c3a" + router = {'router': {'name': 'router1', 'admin_state_up': True, + 'tenant_id': router_id, + 'external_gateway_info': None}} + return router_id, router + + @staticmethod + def _get_floating_ip_test(): + floating_ip_id = "e4997650-6a83-4230-950a-8adab8e524b2" + floating_ip = { + "floatingip": {"fixed_ip_address": None, + "floating_ip_address": None, + "floating_network_id": None, + "id": floating_ip_id, + "router_id": "d23abc8d-2991-4a55-ba98-2aaea84cc72", + "port_id": None, + "status": None, + "tenant_id": "test-tenant" + } + } + return floating_ip_id, floating_ip + + @staticmethod + def _get_port_test(): + port_id = "3a44f4e5-1694-493a-a1fb-393881c673a4" + subnet_id = "a2f1f29d-571b-4533-907f-5803ab96ead1" + port = {'id': port_id, + 'network_id': "84b126bb-f45e-4b2e-8202-7e5ce9e21fe7", + 'fixed_ips': [{'ip_address': '19.4.4.4', + 'prefixlen': 24, + 'subnet_id': subnet_id}], + 'subnets': [{'id': subnet_id, + 'cidr': '19.4.4.0/24', + 'gateway_ip': '19.4.4.1'}]} + return port_id, port + + def test_create_router(self): + router_id, router = self._get_router_test() + + return_value = copy.deepcopy(router['router']) + return_value.update({'status': "ACTIVE", 'id': router_id}) + + instance = self.plugin.return_value + instance.create_router.return_value = return_value + instance.get_routers_count.return_value = 0 + + res = self.api.post(_get_path('routers', fmt=self.fmt), + self.serialize(router), + content_type='application/%s' % self.fmt) + + instance.create_router.assert_called_once_with(mock.ANY, router=router) + self.assertEqual(exc.HTTPCreated.code, res.status_int) + res = self.deserialize(res) + self.assertIn('router', res) + router = res['router'] + self.assertEqual(router_id, router['id']) + self.assertEqual("ACTIVE", router['status']) + self.assertEqual(True, router['admin_state_up']) + + def test_update_router(self): + router_id, router = self._get_router_test() + + router_request_info = {'external_gateway_info': { + "network_id": "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8", + "enable_snat": True} + } + return_value = copy.deepcopy(router['router']) + return_value.update(router_request_info) + return_value.update({'status': "ACTIVE", 'id': router_id}) + + instance = self.plugin.return_value + instance.update_router.return_value = return_value + + router_request = {'router': router_request_info} + res = self.api.put(_get_path('routers', id=router_id, fmt=self.fmt), + self.serialize(router_request)) + instance.update_router.assert_called_once_with(mock.ANY, router_id, + router=router_request) + + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertIn('router', res) + router = res['router'] + self.assertEqual(router_id, router['id']) + self.assertEqual("3c5bcddd-6af9-4e6b-9c3e-c153e521cab8", + router["external_gateway_info"]['network_id']) + self.assertEqual(True, router["external_gateway_info"]['enable_snat']) + + def test_delete_router(self): + router_id, router = self._get_router_test() + + instance = self.plugin.return_value + + res = self.api.delete(_get_path('routers', id=router_id, fmt=self.fmt)) + instance.delete_router.assert_called_once_with(mock.ANY, router_id) + + self.assertEqual(exc.HTTPNoContent.code, res.status_int) + + def test_create_floating_ip(self): + floating_ip_id, floating_ip = self._get_floating_ip_test() + port_id, port = self._get_port_test() + + floating_ip_request_info = {"floating_network_id": + "376da547-b977-4cfe-9cba-275c80debf57", + "tenant_id": "test-tenant", + "fixed_ip_address": "10.0.0.3", + "subnet_id": port['subnets'][0]['id'], + "port_id": port_id, + "floating_ip_address": "172.24.4.228" + } + + return_value = copy.deepcopy(floating_ip['floatingip']) + return_value.update(floating_ip_request_info) + return_value.update({'status': "ACTIVE"}) + + instance = self.plugin.return_value + instance.create_floatingip.return_value = return_value + instance.get_floatingips_count.return_value = 0 + instance.get_port = mock.Mock(return_value=port) + + floating_ip_request = {'floatingip': floating_ip_request_info} + + res = self.api.post(_get_path('floatingips', fmt=self.fmt), + self.serialize(floating_ip_request)) + + instance.create_floatingip.\ + assert_called_once_with(mock.ANY, + floatingip=floating_ip_request) + + self.assertEqual(exc.HTTPCreated.code, res.status_int) + res = self.deserialize(res) + self.assertIn('floatingip', res) + floatingip = res['floatingip'] + self.assertEqual(floating_ip_id, floatingip['id']) + self.assertEqual("ACTIVE", floatingip['status']) + + def test_update_floating_ip(self): + floating_ip_id, floating_ip = self._get_floating_ip_test() + + floating_ip_request_info = {"port_id": None} + + return_value = copy.deepcopy(floating_ip['floatingip']) + return_value.update(floating_ip_request_info) + return_value.update({"status": "ACTIVE", + "tenant_id": "test-tenant", + "floating_network_id": + "376da547-b977-4cfe-9cba-275c80debf57", + "fixed_ip_address": None, + "floating_ip_address": "172.24.4.228" + }) + + instance = self.plugin.return_value + instance.update_floatingip.return_value = return_value + port_id, port = self._get_port_test() + instance.get_port = mock.Mock(return_value=port) + + floating_ip_request = {'floatingip': floating_ip_request_info} + + res = self.api.put(_get_path('floatingips', id=floating_ip_id, + fmt=self.fmt), + self.serialize(floating_ip_request)) + + instance.update_floatingip.\ + assert_called_once_with(mock.ANY, + floating_ip_id, + floatingip=floating_ip_request) + + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertIn('floatingip', res) + floatingip = res['floatingip'] + self.assertEqual(floating_ip_id, floatingip['id']) + self.assertIsNone(floatingip['port_id']) + self.assertIsNone(floatingip['fixed_ip_address']) + + def test_delete_floating_ip(self): + floating_ip_id, floating_ip = self._get_floating_ip_test() + + instance = self.plugin.return_value + port_id, port = self._get_port_test() + instance.get_port = mock.Mock(return_value=port) + res = self.api.delete(_get_path('floatingips', id=floating_ip_id)) + instance.delete_floatingip.assert_called_once_with(mock.ANY, + floating_ip_id) + + self.assertEqual(exc.HTTPNoContent.code, res.status_int) + + def test_add_router_interface(self): + router_id, router = self._get_router_test() + interface_info = {"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"} + return_value = {"tenant_id": "6ba032e4730d42e2ad928f430f5da33e", + "port_id": "3a44f4e5-1694-493a-a1fb-393881c673a4", + "id": router_id + } + return_value.update(interface_info) + + instance = self.plugin.return_value + instance.add_router_interface.return_value = return_value + + res = self.api.put(_get_path('routers', id=router_id, + action="add_router_interface", + fmt=self.fmt), + self.serialize(interface_info) + ) + + instance.add_router_interface.assert_called_once_with(mock.ANY, + router_id, + interface_info) + + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertEqual(router_id, res['id']) + self.assertEqual("a2f1f29d-571b-4533-907f-5803ab96ead1", + res['subnet_id']) + + def test_remove_router_interface(self): + router_id, router = self._get_router_test() + interface_info = {"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1", + "port_id": "3a44f4e5-1694-493a-a1fb-393881c673a4" + } + return_value = {"tenant_id": "6ba032e4730d42e2ad928f430f5da33e", + "id": router_id + } + return_value.update(interface_info) + + instance = self.plugin.return_value + instance.remove_router_interface.return_value = return_value + res = self.api.put(_get_path('routers', id=router_id, + action="remove_router_interface", + fmt=self.fmt), + self.serialize(interface_info) + ) + + instance.remove_router_interface.\ + assert_called_once_with(mock.ANY, + router_id, + interface_info) + + self.assertEqual(exc.HTTPOk.code, res.status_int) + res = self.deserialize(res) + self.assertEqual(router_id, res['id']) + self.assertEqual("a2f1f29d-571b-4533-907f-5803ab96ead1", + res['subnet_id']) diff --git a/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py new file mode 100644 index 0000000..da3f644 --- /dev/null +++ b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py @@ -0,0 +1,526 @@ +# Copyright (c) 2016 OpenStack Foundation +# 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. + +from networking_odl.common import client +from networking_odl.common import constants as odl_const +from networking_odl.common import filters +from networking_odl.db import db +from networking_odl.journal import journal +from networking_odl.l3 import l3_odl_v2 +from networking_odl.ml2 import mech_driver_v2 + +import mock +from oslo_serialization import jsonutils +import requests + +from neutron import context +from neutron.db import api as neutron_db_api +from neutron.extensions import external_net as external_net +from neutron import manager +from neutron.plugins.ml2 import config as config +from neutron.plugins.ml2 import plugin +from neutron.tests import base +from neutron.tests.unit.db import test_db_base_plugin_v2 +from neutron.tests.unit import testlib_api + +EMPTY_DEP = [] +FLOATINGIP_ID = 'floatingip_uuid' +NETWORK_ID = 'network_uuid' +ROUTER_ID = 'router_uuid' +SUBNET_ID = 'subnet_uuid' +PORT_ID = 'port_uuid' + + +class OpenDayLightMechanismConfigTests(testlib_api.SqlTestCase): + + def _set_config(self, url='http://127.0.0.1:9999', username='someuser', + password='somepass'): + config.cfg.CONF.set_override('mechanism_drivers', + ['logger', 'opendaylight'], + 'ml2') + config.cfg.CONF.set_override('url', url, 'ml2_odl') + config.cfg.CONF.set_override('username', username, 'ml2_odl') + config.cfg.CONF.set_override('password', password, 'ml2_odl') + + def _test_missing_config(self, **kwargs): + self._set_config(**kwargs) + self.assertRaises(config.cfg.RequiredOptError, + plugin.Ml2Plugin) + + def test_valid_config(self): + self._set_config() + plugin.Ml2Plugin() + + def test_missing_url_raises_exception(self): + self._test_missing_config(url=None) + + def test_missing_username_raises_exception(self): + self._test_missing_config(username=None) + + def test_missing_password_raises_exception(self): + self._test_missing_config(password=None) + + +class DataMatcher(object): + + def __init__(self, operation, object_type, object_dict): + self._data = object_dict.copy() + self._object_type = object_type + filters.filter_for_odl(object_type, operation, self._data) + + def __eq__(self, s): + data = jsonutils.loads(s) + if self._object_type == odl_const.ODL_ROUTER_INTF: + return self._data == data + else: + return self._data == data[self._object_type] + + def __ne__(self, s): + return not self.__eq__(s) + + +class OpenDaylightL3TestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, + base.BaseTestCase): + def setUp(self): + config.cfg.CONF.set_override("core_plugin", + 'neutron.plugins.ml2.plugin.Ml2Plugin') + core_plugin = config.cfg.CONF.core_plugin + super(OpenDaylightL3TestCase, self).setUp(plugin=core_plugin) + config.cfg.CONF.set_override('mechanism_drivers', + ['logger', 'opendaylight'], 'ml2') + config.cfg.CONF.set_override('url', 'http://127.0.0.1:9999', 'ml2_odl') + config.cfg.CONF.set_override('username', 'someuser', 'ml2_odl') + config.cfg.CONF.set_override('password', 'somepass', 'ml2_odl') + mock.patch.object(journal.OpendaylightJournalThread, + 'start_odl_sync_thread').start() + self.db_session = neutron_db_api.get_session() + self.mech = mech_driver_v2.OpenDaylightMechanismDriver() + self.plugin = manager.NeutronManager.get_plugin() + self.plugin._network_is_external = mock.Mock(return_value=True) + self.driver = l3_odl_v2.OpenDaylightL3RouterPlugin() + self.thread = journal.OpendaylightJournalThread() + self.driver.get_floatingip = mock.Mock( + return_value={'router_id': ROUTER_ID, + 'floating_network_id': NETWORK_ID}) + self.addCleanup(self._db_cleanup) + + @staticmethod + def _get_mock_router_operation_info(network, subnet): + router_context = context.get_admin_context() + router = {odl_const.ODL_ROUTER: + {'name': 'router1', + 'admin_state_up': True, + 'tenant_id': network['network']['tenant_id'], + 'external_gateway_info': {'network_id': + network['network']['id']}}} + return router_context, router + + @staticmethod + def _get_mock_floatingip_operation_info(network, subnet): + floatingip_context = context.get_admin_context() + floatingip = {odl_const.ODL_FLOATINGIP: + {'floating_network_id': network['network']['id'], + 'tenant_id': network['network']['tenant_id']}} + return floatingip_context, floatingip + + @staticmethod + def _get_mock_router_interface_operation_info(network, subnet): + router_intf_context = context.get_admin_context() + router_intf_dict = {'subnet_id': subnet['subnet']['id'], + 'id': network['network']['id']} + return router_intf_context, router_intf_dict + + @classmethod + def _get_mock_operation_info(cls, object_type, *args): + getter = getattr(cls, '_get_mock_' + object_type + '_operation_info') + return getter(*args) + + def _db_cleanup(self): + rows = db.get_all_db_rows(self.db_session) + for row in rows: + db.delete_row(self.db_session, row=row) + + @classmethod + def _get_mock_request_response(cls, status_code): + response = mock.Mock(status_code=status_code) + response.raise_for_status = mock.Mock() if status_code < 400 else ( + mock.Mock(side_effect=requests.exceptions.HTTPError( + cls._status_code_msgs[status_code]))) + return response + + def _test_operation(self, status_code, expected_calls, *args, **kwargs): + request_response = self._get_mock_request_response(status_code) + with mock.patch('requests.request', + return_value=request_response) as mock_method: + with mock.patch.object(self.thread.event, 'wait', + return_value=False): + self.thread.run_sync_thread(exit_after_run=True) + + if expected_calls: + mock_method.assert_called_with( + headers={'Content-Type': 'application/json'}, + auth=(config.cfg.CONF.ml2_odl.username, + config.cfg.CONF.ml2_odl.password), + timeout=config.cfg.CONF.ml2_odl.timeout, *args, **kwargs) + self.assertEqual(expected_calls, mock_method.call_count) + + def _call_operation_object(self, operation, object_type, object_id, + network, subnet): + object_context, object_dict = self._get_mock_operation_info( + object_type, network, subnet) + method = getattr(self.driver, operation + '_' + object_type) + + if operation == odl_const.ODL_CREATE: + new_object_dict = method(object_context, object_dict) + elif operation == odl_const.ODL_UPDATE: + new_object_dict = method(object_context, object_id, object_dict) + elif operation in [odl_const.ODL_ADD, odl_const.ODL_REMOVE]: + router_dict = method(object_context, object_id, object_dict) + new_object_dict = self.driver._generate_router_dict( + object_id, object_dict, router_dict) + else: + new_object_dict = method(object_context, object_id) + + return object_context, new_object_dict + + def _test_operation_thread_processing(self, object_type, operation, + network, subnet, object_id, + expected_calls=1): + http_requests = {odl_const.ODL_CREATE: 'post', + odl_const.ODL_UPDATE: 'put', + odl_const.ODL_DELETE: 'delete', + odl_const.ODL_ADD: 'put', + odl_const.ODL_REMOVE: 'put'} + status_codes = {odl_const.ODL_CREATE: requests.codes.created, + odl_const.ODL_UPDATE: requests.codes.ok, + odl_const.ODL_DELETE: requests.codes.no_content, + odl_const.ODL_ADD: requests.codes.created, + odl_const.ODL_REMOVE: requests.codes.created} + + http_request = http_requests[operation] + status_code = status_codes[operation] + + # Create database entry. + object_context, new_object_dict = self._call_operation_object( + operation, object_type, object_id, network, subnet) + + # Setup expected results. + if operation in [odl_const.ODL_UPDATE, odl_const.ODL_DELETE]: + url = (config.cfg.CONF.ml2_odl.url + '/' + object_type + 's/' + + object_id) + elif operation in [odl_const.ODL_ADD, odl_const.ODL_REMOVE]: + url = (config.cfg.CONF.ml2_odl.url + '/' + odl_const.ODL_ROUTER + + 's/' + object_id + '/' + operation + '_router_interface') + else: + url = config.cfg.CONF.ml2_odl.url + '/' + object_type + 's' + + if operation in [odl_const.ODL_CREATE, odl_const.ODL_UPDATE, + odl_const.ODL_ADD, odl_const.ODL_REMOVE]: + kwargs = { + 'url': url, + 'data': DataMatcher(operation, object_type, new_object_dict)} + else: + kwargs = {'url': url, 'data': None} + + # Call threading routine to process database entry. Test results. + self._test_operation(status_code, expected_calls, http_request, + **kwargs) + + return new_object_dict + + def _test_thread_processing(self, object_type): + # Create network and subnet. + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as network: + with self.subnet(network=network, cidr='10.0.0.0/24'): + # Add and process create request. + new_object_dict = self._test_operation_thread_processing( + object_type, odl_const.ODL_CREATE, network, None, None) + object_id = new_object_dict['id'] + rows = db.get_all_db_rows_by_state(self.db_session, + odl_const.COMPLETED) + self.assertEqual(1, len(rows)) + + # Add and process 'update' request. Adds to database. + self._test_operation_thread_processing( + object_type, odl_const.ODL_UPDATE, network, None, + object_id) + rows = db.get_all_db_rows_by_state(self.db_session, + odl_const.COMPLETED) + self.assertEqual(2, len(rows)) + + # Add and process 'delete' request. Adds to database. + self._test_operation_thread_processing( + object_type, odl_const.ODL_DELETE, network, None, + object_id) + rows = db.get_all_db_rows_by_state(self.db_session, + odl_const.COMPLETED) + self.assertEqual(3, len(rows)) + + def _test_db_results(self, object_id, operation, object_type): + rows = db.get_all_db_rows(self.db_session) + + self.assertEqual(1, len(rows)) + self.assertEqual(operation, rows[0]['operation']) + self.assertEqual(object_type, rows[0]['object_type']) + self.assertEqual(object_id, rows[0]['object_uuid']) + + self._db_cleanup() + + def _test_object_db(self, object_type): + # Create network and subnet for testing. + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as network: + with self.subnet(network=network): + object_context, object_dict = self._get_mock_operation_info( + object_type, network, None) + + # Add and test 'create' database entry. + method = getattr(self.driver, + odl_const.ODL_CREATE + '_' + object_type) + new_object_dict = method(object_context, object_dict) + object_id = new_object_dict['id'] + self._test_db_results(object_id, odl_const.ODL_CREATE, + object_type) + + # Add and test 'update' database entry. + method = getattr(self.driver, + odl_const.ODL_UPDATE + '_' + object_type) + method(object_context, object_id, object_dict) + self._test_db_results(object_id, odl_const.ODL_UPDATE, + object_type) + + # Add and test 'delete' database entry. + method = getattr(self.driver, + odl_const.ODL_DELETE + '_' + object_type) + method(object_context, object_id) + self._test_db_results(object_id, odl_const.ODL_DELETE, + object_type) + + def _test_dependency_processing( + self, test_operation, test_object, test_id, test_context, + dep_operation, dep_object, dep_id, dep_context): + + # Mock sendjson to verify that it never gets called. + mock_sendjson = mock.patch.object(client.OpenDaylightRestClient, + 'sendjson').start() + + # Create dependency db row and mark as 'processing' so it won't + # be processed by the journal thread. + db.create_pending_row(self.db_session, dep_object, + dep_id, dep_operation, dep_context) + row = db.get_all_db_rows_by_state(self.db_session, odl_const.PENDING) + db.update_db_row_state(self.db_session, row[0], odl_const.PROCESSING) + + # Create test row with dependent ID. + db.create_pending_row(self.db_session, test_object, + test_id, test_operation, test_context) + + # Call journal thread. + with mock.patch.object(self.thread.event, 'wait', + return_value=False): + self.thread.run_sync_thread(exit_after_run=True) + + # Verify that dependency row is still set at 'processing'. + rows = db.get_all_db_rows_by_state(self.db_session, + odl_const.PROCESSING) + self.assertEqual(1, len(rows)) + + # Verify that the test row was processed and set back to 'pending' + # to be processed again. + rows = db.get_all_db_rows_by_state(self.db_session, odl_const.PENDING) + self.assertEqual(1, len(rows)) + + # Verify that _json_data was not called. + self.assertFalse(mock_sendjson.call_count) + + def test_router_db(self): + self._test_object_db(odl_const.ODL_ROUTER) + + def test_floatingip_db(self): + self._test_object_db(odl_const.ODL_FLOATINGIP) + + def test_router_intf_db(self): + # Create network, subnet and router for testing. + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as network: + with self.subnet(cidr='10.0.0.0/24') as subnet: + router_context, router_dict = ( + self._get_mock_router_operation_info(network, None)) + new_router_dict = self.driver.create_router(router_context, + router_dict) + router_id = new_router_dict['id'] + + object_type = odl_const.ODL_ROUTER_INTF + router_intf_context, router_intf_dict = \ + self._get_mock_router_interface_operation_info(network, + subnet) + + # Remove 'router' database entry to allow tests to pass. + self._db_cleanup() + + # Add and test router interface 'add' database entry. + # Note that router interface events do not generate unique + # UUIDs. + self.driver.add_router_interface(router_intf_context, + router_id, router_intf_dict) + self._test_db_results(odl_const.ODL_UUID_NOT_USED, + odl_const.ODL_ADD, object_type) + + # Add and test 'remove' database entry. + self.driver.remove_router_interface(router_intf_context, + router_id, + router_intf_dict) + self._test_db_results(odl_const.ODL_UUID_NOT_USED, + odl_const.ODL_REMOVE, object_type) + + def test_router_threading(self): + self._test_thread_processing(odl_const.ODL_ROUTER) + + def test_floatingip_threading(self): + self._test_thread_processing(odl_const.ODL_FLOATINGIP) + + def test_router_intf_threading(self): + # Create network, subnet and router for testing. + kwargs = {'arg_list': (external_net.EXTERNAL,), + external_net.EXTERNAL: True} + with self.network(**kwargs) as network: + with self.subnet(cidr='10.0.0.0/24') as subnet: + router_context, router_dict = ( + self._get_mock_router_operation_info(network, None)) + new_router_dict = self.driver.create_router(router_context, + router_dict) + router_id = new_router_dict['id'] + object_type = odl_const.ODL_ROUTER_INTF + + # Add and process router interface 'add' request. Adds to + # database. Expected calls = 2 because the create_router db + # entry is also processed. + self._test_operation_thread_processing( + object_type, odl_const.ODL_ADD, network, subnet, router_id, + expected_calls=2) + rows = db.get_all_db_rows_by_state(self.db_session, + odl_const.COMPLETED) + self.assertEqual(2, len(rows)) + + # Add and process 'remove' request. Adds to database. + self._test_operation_thread_processing( + object_type, odl_const.ODL_REMOVE, network, subnet, + router_id) + rows = db.get_all_db_rows_by_state(self.db_session, + odl_const.COMPLETED) + self.assertEqual(3, len(rows)) + + def test_delete_network_validate_ext_delete_router_dep(self): + router_context = [NETWORK_ID] + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_NETWORK, NETWORK_ID, None, + odl_const.ODL_DELETE, odl_const.ODL_ROUTER, ROUTER_ID, + router_context) + + def test_create_router_validate_ext_create_port_dep(self): + router_context = {'gw_port_id': PORT_ID} + self._test_dependency_processing( + odl_const.ODL_CREATE, odl_const.ODL_ROUTER, ROUTER_ID, + router_context, + odl_const.ODL_CREATE, odl_const.ODL_PORT, PORT_ID, None) + + def test_delete_router_validate_ext_delete_floatingip_dep(self): + floatingip_context = [ROUTER_ID] + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_ROUTER, ROUTER_ID, None, + odl_const.ODL_DELETE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + floatingip_context) + + def test_delete_router_validate_ext_remove_routerintf_dep(self): + router_intf_dict = {'id': ROUTER_ID} + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_ROUTER, ROUTER_ID, None, + odl_const.ODL_REMOVE, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, router_intf_dict) + + def test_delete_router_validate_self_create_dep(self): + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_ROUTER, ROUTER_ID, EMPTY_DEP, + odl_const.ODL_CREATE, odl_const.ODL_ROUTER, ROUTER_ID, None) + + def test_delete_router_validate_self_update_dep(self): + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_ROUTER, ROUTER_ID, EMPTY_DEP, + odl_const.ODL_UPDATE, odl_const.ODL_ROUTER, ROUTER_ID, None) + + def test_update_router_validate_self_create_dep(self): + router_context = {'gw_port_id': None} + self._test_dependency_processing( + odl_const.ODL_UPDATE, odl_const.ODL_ROUTER, ROUTER_ID, + router_context, + odl_const.ODL_CREATE, odl_const.ODL_ROUTER, ROUTER_ID, None) + + def test_create_floatingip_validate_ext_create_network_dep(self): + floatingip_context = {'floating_network_id': NETWORK_ID} + self._test_dependency_processing( + odl_const.ODL_CREATE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + floatingip_context, + odl_const.ODL_CREATE, odl_const.ODL_NETWORK, NETWORK_ID, None) + + def test_update_floatingip_validate_self_create_dep(self): + floatingip_context = {'floating_network_id': NETWORK_ID} + self._test_dependency_processing( + odl_const.ODL_UPDATE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + floatingip_context, + odl_const.ODL_CREATE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + EMPTY_DEP) + + def test_delete_floatingip_validate_self_create_dep(self): + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + EMPTY_DEP, + odl_const.ODL_CREATE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + None) + + def test_delete_floatingip_validate_self_update_dep(self): + self._test_dependency_processing( + odl_const.ODL_DELETE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + EMPTY_DEP, + odl_const.ODL_UPDATE, odl_const.ODL_FLOATINGIP, FLOATINGIP_ID, + None) + + def test_add_router_intf_validate_ext_create_router_dep(self): + router_intf_context = {'subnet_id': SUBNET_ID, + 'id': ROUTER_ID} + self._test_dependency_processing( + odl_const.ODL_ADD, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, router_intf_context, + odl_const.ODL_CREATE, odl_const.ODL_ROUTER, ROUTER_ID, None) + + def test_add_router_intf_validate_ext_create_subnet_dep(self): + router_intf_context = {'subnet_id': SUBNET_ID, + 'id': ROUTER_ID} + self._test_dependency_processing( + odl_const.ODL_ADD, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, router_intf_context, + odl_const.ODL_CREATE, odl_const.ODL_SUBNET, SUBNET_ID, None) + + def test_remove_router_intf_validate_self_remove_router_intf_dep(self): + router_intf_context = {'subnet_id': SUBNET_ID, + 'id': ROUTER_ID} + self._test_dependency_processing( + odl_const.ODL_REMOVE, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, router_intf_context, + odl_const.ODL_ADD, odl_const.ODL_ROUTER_INTF, + odl_const.ODL_UUID_NOT_USED, router_intf_context) -- cgit 1.2.3-korg