summaryrefslogtreecommitdiffstats
path: root/networking-odl/networking_odl/tests/unit/l3
diff options
context:
space:
mode:
Diffstat (limited to 'networking-odl/networking_odl/tests/unit/l3')
-rw-r--r--networking-odl/networking_odl/tests/unit/l3/__init__.py0
-rw-r--r--networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py310
-rw-r--r--networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py526
3 files changed, 836 insertions, 0 deletions
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
--- /dev/null
+++ b/networking-odl/networking_odl/tests/unit/l3/__init__.py
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)