From ddd61ca084fb1c8105663f6f0cbbfceeccc166ea Mon Sep 17 00:00:00 2001 From: Matt Dietz Date: Wed, 31 Jul 2013 22:48:33 +0000 Subject: [PATCH] Adds coverage over drivers and db custom types Adds more coverage tests for some portions of the driver logic and for the db custom types. I decided to abstain from implementing some of the optimized driver coverage tests at this time because the only thing one can test there is that python and sqlalchemy work correctly. Otherwise, they're just convenience mthods. --- quark/__init__.py | 4 +- quark/drivers/base.py | 8 +-- quark/drivers/nvp_driver.py | 2 +- quark/drivers/optimized_nvp_driver.py | 14 ++-- quark/plugin_modules/networks.py | 2 +- quark/plugin_modules/ports.py | 2 +- quark/plugin_modules/security_groups.py | 2 +- quark/tests/test_base_driver.py | 68 +++++++++++++++++++ quark/tests/test_db_custom_types.py | 76 +++++++++++++++++++++ quark/tests/test_nvp_driver.py | 86 ++++++++++++++++++++++-- quark/tests/test_optimized_nvp_driver.py | 36 +++++++++- 11 files changed, 273 insertions(+), 27 deletions(-) create mode 100644 quark/tests/test_base_driver.py create mode 100644 quark/tests/test_db_custom_types.py diff --git a/quark/__init__.py b/quark/__init__.py index 9f30bff..21658bc 100644 --- a/quark/__init__.py +++ b/quark/__init__.py @@ -30,9 +30,7 @@ quark_opts = [ "after deallocation.")), cfg.StrOpt("strategy_driver", default='quark.network_strategy.JSONStrategy', - help=_("Tree of network assignment strategy")), - cfg.StrOpt('net_driver_cfg', default='/etc/neutron/quark.ini', - help=_("Path to the config for the net driver")) + help=_("Tree of network assignment strategy")) ] diff --git a/quark/drivers/base.py b/quark/drivers/base.py index da8917e..4f12b70 100644 --- a/quark/drivers/base.py +++ b/quark/drivers/base.py @@ -23,15 +23,15 @@ class BaseDriver(object): Usable as a replacement for the sample plugin. """ - def load_config(self, path): - LOG.info("load_config %s" % path) + def load_config(self): + LOG.info("load_config") def get_connection(self): LOG.info("get_connection") - def create_network(self, tenant_id, network_name, tags=None, + def create_network(self, context, network_name, tags=None, network_id=None, **kwargs): - LOG.info("create_network %s %s %s" % (tenant_id, network_name, + LOG.info("create_network %s %s %s" % (context, network_name, tags)) def delete_network(self, context, network_id): diff --git a/quark/drivers/nvp_driver.py b/quark/drivers/nvp_driver.py index 045b8c9..2ea6057 100644 --- a/quark/drivers/nvp_driver.py +++ b/quark/drivers/nvp_driver.py @@ -68,7 +68,7 @@ class NVPDriver(base.BaseDriver): 'max_rules_per_group': 0, 'max_rules_per_port': 0} - def load_config(self, path): + def load_config(self): #NOTE(mdietz): What does default_tz actually mean? # We don't have one default. default_tz = CONF.NVP.default_tz diff --git a/quark/drivers/optimized_nvp_driver.py b/quark/drivers/optimized_nvp_driver.py index 723d6af..bb4e8b2 100644 --- a/quark/drivers/optimized_nvp_driver.py +++ b/quark/drivers/optimized_nvp_driver.py @@ -60,9 +60,7 @@ class OptimizedNVPDriver(NVPDriver): update_port(context, port_id, status=status, security_groups=security_groups, allowed_pairs=allowed_pairs) - port = context.session.query(LSwitchPort).\ - filter(LSwitchPort.port_id == port_id).\ - first() + port = self._lport_select_by_id(context, port_id) port.update(nvp_port) def delete_port(self, context, port_id, lswitch_uuid=None): @@ -85,9 +83,7 @@ class OptimizedNVPDriver(NVPDriver): def delete_security_group(self, context, group_id): super(OptimizedNVPDriver, self).\ delete_security_group(context, group_id) - group = context.session.query(SecurityProfile).\ - filter(SecurityProfile.id == group_id).\ - first() + group = self._query_security_group(context, group_id) context.session.delete(group) def _lport_select_by_id(self, context, port_id): @@ -178,9 +174,9 @@ class OptimizedNVPDriver(NVPDriver): port = self._lport_select_by_id(context, port_id) return port.switch.nvp_id - def _get_security_group_id(self, context, group_id): + def _query_security_group(self, context, group_id): return context.session.query(SecurityProfile).\ - filter(SecurityProfile.id == group_id).first().nvp_id + filter(SecurityProfile.id == group_id).first() def _make_security_rule_dict(self, rule): res = {"port_range_min": rule.get("port_range_min"), @@ -201,7 +197,7 @@ class OptimizedNVPDriver(NVPDriver): for rule in group.rules: rulelist[rule.direction].append( self._make_security_rule_dict(rule)) - return {'uuid': self._get_security_group_id(context, group_id), + return {'uuid': self._query_security_group(context, group_id).nvp_id, 'logical_port_ingress_rules': rulelist['ingress'], 'logical_port_egress_rules': rulelist['egress']} diff --git a/quark/plugin_modules/networks.py b/quark/plugin_modules/networks.py index 896e2a5..13bea85 100644 --- a/quark/plugin_modules/networks.py +++ b/quark/plugin_modules/networks.py @@ -36,7 +36,7 @@ STRATEGY = network_strategy.STRATEGY ipam_driver = (importutils.import_class(CONF.QUARK.ipam_driver))() net_driver = (importutils.import_class(CONF.QUARK.net_driver))() -net_driver.load_config(CONF.QUARK.net_driver_cfg) +net_driver.load_config() def _adapt_provider_nets(context, network): diff --git a/quark/plugin_modules/ports.py b/quark/plugin_modules/ports.py index 3ede714..8edad47 100644 --- a/quark/plugin_modules/ports.py +++ b/quark/plugin_modules/ports.py @@ -31,7 +31,7 @@ LOG = logging.getLogger("neutron.quark") ipam_driver = (importutils.import_class(CONF.QUARK.ipam_driver))() net_driver = (importutils.import_class(CONF.QUARK.net_driver))() -net_driver.load_config(CONF.QUARK.net_driver_cfg) +net_driver.load_config() def create_port(context, port): diff --git a/quark/plugin_modules/security_groups.py b/quark/plugin_modules/security_groups.py index 26a3feb..29828fe 100644 --- a/quark/plugin_modules/security_groups.py +++ b/quark/plugin_modules/security_groups.py @@ -31,7 +31,7 @@ DEFAULT_SG_UUID = "00000000-0000-0000-0000-000000000000" net_driver = (importutils.import_class(CONF.QUARK.net_driver))() -net_driver.load_config(CONF.QUARK.net_driver_cfg) +net_driver.load_config() def _validate_security_group_rule(context, rule): diff --git a/quark/tests/test_base_driver.py b/quark/tests/test_base_driver.py new file mode 100644 index 0000000..56e84c9 --- /dev/null +++ b/quark/tests/test_base_driver.py @@ -0,0 +1,68 @@ +# Copyright (c) 2013 OpenStack Foundation +# +# 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 quark.drivers import base +from quark.tests import test_base + + +class TestBaseDriver(test_base.TestBase): + def setUp(self): + super(TestBaseDriver, self).setUp() + self.driver = base.BaseDriver() + + def test_load_config(self): + self.driver.load_config() + + def test_get_connection(self): + self.driver.get_connection() + + def test_create_network(self): + self.driver.create_network(context=self.context, network_name="public") + + def test_delete_network(self): + self.driver.delete_network(context=self.context, network_id=1) + + def test_create_port(self): + self.driver.create_port(context=self.context, network_id=1, port_id=2) + + def test_update_port(self): + self.driver.update_port(context=self.context, network_id=1, port_id=2) + + def test_delete_port(self): + self.driver.delete_port(context=self.context, port_id=2) + + def test_create_security_group(self): + self.driver.create_security_group(context=self.context, + group_name="mygroup") + + def test_delete_security_group(self): + self.driver.delete_security_group(context=self.context, + group_id=3) + + def test_update_security_group(self): + self.driver.update_security_group(context=self.context, + group_id=3) + + def test_create_security_group_rule(self): + rule = {'ethertype': 'IPv4', 'direction': 'ingress'} + self.driver.create_security_group_rule(context=self.context, + group_id=3, + rule=rule) + + def test_delete_security_group_rule(self): + rule = {'ethertype': 'IPv4', 'direction': 'ingress'} + self.driver.delete_security_group_rule(context=self.context, + group_id=3, + rule=rule) diff --git a/quark/tests/test_db_custom_types.py b/quark/tests/test_db_custom_types.py new file mode 100644 index 0000000..6f537eb --- /dev/null +++ b/quark/tests/test_db_custom_types.py @@ -0,0 +1,76 @@ +# Copyright 2013 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 quark.db import custom_types + +from quark.tests import test_base + +from sqlalchemy.dialects import mysql, sqlite + + +class TestDBCustomTypesINET(test_base.TestBase): + """Adding for coverage of the custom types.""" + + def setUp(self): + super(TestDBCustomTypesINET, self).setUp() + self.inet = custom_types.INET() + + def test_inet_load_dialect_impl(self): + dialect = self.inet.load_dialect_impl(mysql.dialect()) + self.assertEqual(type(dialect), type(custom_types.INET.impl())) + + def test_inet_load_dialect_impl_sqlite(self): + dialect = self.inet.load_dialect_impl(sqlite.dialect()) + self.assertEqual(type(dialect), sqlite.CHAR) + + def test_process_bind_param(self): + bind = self.inet.process_bind_param(None, None) + self.assertIsNone(bind) + + def test_process_bind_param_with_value(self): + bind = self.inet.process_bind_param("foo", sqlite.dialect()) + self.assertEqual(bind, "foo") + + def test_process_bind_param_with_value_not_sqlite(self): + bind = self.inet.process_bind_param("foo", mysql.dialect()) + self.assertEqual(bind, "foo") + + def test_process_result_value(self): + bind = self.inet.process_result_value(None, mysql.dialect()) + self.assertIsNone(bind) + + def test_process_result_value_with_value(self): + bind = self.inet.process_result_value(1.0, sqlite.dialect()) + self.assertEqual(bind, 1.0) + + def test_process_result_value_with_value_not_sqlite(self): + bind = self.inet.process_result_value(1.0, mysql.dialect()) + self.assertEqual(bind, 1.0) + + +class TestDBCustomTypesMACAddress(test_base.TestBase): + """Adding for coverage of the mac address custom types.""" + + def setUp(self): + super(TestDBCustomTypesMACAddress, self).setUp() + self.mac = custom_types.MACAddress() + + def test_mac_load_dialect_impl(self): + dialect = self.mac.load_dialect_impl(sqlite.dialect()) + self.assertEqual(type(dialect), sqlite.CHAR) + + def test_mac_load_dialect_impl_not_sqlite(self): + dialect = self.mac.load_dialect_impl(mysql.dialect()) + self.assertEqual(type(dialect), type(custom_types.MACAddress.impl())) diff --git a/quark/tests/test_nvp_driver.py b/quark/tests/test_nvp_driver.py index 57ca4d1..5409263 100644 --- a/quark/tests/test_nvp_driver.py +++ b/quark/tests/test_nvp_driver.py @@ -532,14 +532,11 @@ class TestSwitchCopying(TestNVPDriver): class TestNVPDriverDeletePort(TestNVPDriver): @contextlib.contextmanager - def _stubs(self, single_switch=True): + def _stubs(self, switch_count=1): with contextlib.nested( mock.patch("%s.get_connection" % self.d_pkg), ) as (get_connection,): - if not single_switch: - connection = self._create_connection(switch_count=2) - else: - connection = self._create_connection(switch_count=1) + connection = self._create_connection(switch_count=switch_count) get_connection.return_value = connection yield connection @@ -556,7 +553,12 @@ class TestNVPDriverDeletePort(TestNVPDriver): self.assertTrue(connection.lswitch_port().delete.called) def test_delete_port_many_switches(self): - with self._stubs(single_switch=False): + with self._stubs(switch_count=2): + with self.assertRaises(Exception): + self.driver.delete_port(self.context, self.port_id) + + def test_delete_port_no_switch_bad_data(self): + with self._stubs(switch_count=0): with self.assertRaises(Exception): self.driver.delete_port(self.context, self.port_id) @@ -729,6 +731,29 @@ class TestNVPDriverCreateSecurityGroupRule(TestNVPDriver): mock.call.update(), ], any_order=True) + def test_security_rule_create_with_ip_prefix_and_profile(self): + with self._stubs() as connection: + self.driver.create_security_group_rule( + self.context, 1, + {'ethertype': 'IPv4', 'direction': 'ingress', + 'remote_ip_prefix': "pre", "remote_group_id": "group", + "protocol": "udp"}) + connection.securityprofile.assert_any_calls(self.profile_id) + connection.securityprofile().assert_has_calls([ + mock.call.port_ingress_rules([{'ethertype': 'IPv4', + "ip_prefix": "pre", + "profile_uuid": "group", + "protocol": "udp"}]), + mock.call.update(), + ], any_order=True) + + def test_security_rule_create_invalid_direction(self): + with self._stubs(): + with self.assertRaises(AttributeError): + self.driver.create_security_group_rule( + self.context, 1, + {'ethertype': 'IPv4', 'direction': 'instantregret'}) + def test_security_rule_create_duplicate(self): with self._stubs() as connection: connection.securityprofile().read().update({ @@ -797,3 +822,52 @@ class TestNVPDriverDeleteSecurityGroupRule(TestNVPDriver): self.driver.delete_security_group_rule( self.context, 1, {'ethertype': 'IPv6', 'direction': 'egress'}) + + +class TestNVPDriverLoadConfig(TestNVPDriver): + def test_load_config(self): + controllers = "192.168.221.139:443:admin:admin:30:10:2:2" + cfg.CONF.set_override("controller_connection", [controllers], "NVP") + self.driver.load_config() + conn = self.driver.nvp_connections[0] + self.assertEqual(conn["username"], "admin") + self.assertEqual(conn["retries"], "2") + self.assertEqual(conn["redirects"], "2") + self.assertEqual(conn["http_timeout"], "10") + self.assertEqual(conn["req_timeout"], "30") + self.assertEqual(conn["default_tz"], None) + self.assertEqual(conn["password"], "admin") + self.assertEqual(conn["ip_address"], "192.168.221.139") + self.assertEqual(conn["port"], "443") + cfg.CONF.clear_override("controller_connection", "NVP") + + def test_load_config_no_connections(self): + self.driver.load_config() + self.assertEqual(len(self.driver.nvp_connections), 0) + + +class TestNVPGetConnection(TestNVPDriver): + @contextlib.contextmanager + def _stubs(self, has_conn): + controllers = "192.168.221.139:443:admin:admin:30:10:2:2" + cfg.CONF.set_override("controller_connection", [controllers], "NVP") + if has_conn: + self.driver.nvp_connections.append(dict(connection="foo")) + else: + self.driver.nvp_connections.append(dict(port="443", + ip_address="192.168.0.1", + username="admin", + password="admin")) + with mock.patch("aiclib.nvp.Connection") as (aiclib_conn): + yield aiclib_conn + cfg.CONF.clear_override("controller_connection", "NVP") + + def test_get_connection(self): + with self._stubs(has_conn=False) as aiclib_conn: + self.driver.get_connection() + self.assertTrue(aiclib_conn.called) + + def test_get_connection_connection_defined(self): + with self._stubs(has_conn=True) as aiclib_conn: + self.driver.get_connection() + self.assertFalse(aiclib_conn.called) diff --git a/quark/tests/test_optimized_nvp_driver.py b/quark/tests/test_optimized_nvp_driver.py index ec0b16b..cf2688a 100644 --- a/quark/tests/test_optimized_nvp_driver.py +++ b/quark/tests/test_optimized_nvp_driver.py @@ -269,4 +269,38 @@ class TestOptimizedNVPDriverCreatePort(TestOptimizedNVPDriver): class TestOptimizedNVPDriverUpdatePort(TestOptimizedNVPDriver): - pass + def test_update_port(self): + mod_path = "quark.drivers.%s" + op_path = "optimized_nvp_driver.OptimizedNVPDriver" + lport_path = "%s._lport_select_by_id" % op_path + with contextlib.nested( + mock.patch(mod_path % "nvp_driver.NVPDriver.update_port"), + mock.patch(mod_path % lport_path), + ) as (update_port, port_find): + ret_port = quark.drivers.optimized_nvp_driver.LSwitchPort() + port_find.return_value = ret_port + update_port.return_value = dict(switch_id=2) + self.driver.update_port(self.context, 1) + self.assertEqual(ret_port.switch_id, 2) + + +class TestCreateSecurityGroups(TestOptimizedNVPDriver): + def test_create_security_group(self): + with mock.patch("%s.get_connection" % self.d_pkg): + self.driver.create_security_group(self.context, "newgroup") + self.assertTrue(self.context.session.add.called) + + +class TestDeleteSecurityGroups(TestOptimizedNVPDriver): + def test_delete_security_group(self): + mod_path = "quark.drivers.nvp_driver.NVPDriver" + with contextlib.nested( + mock.patch("%s.get_connection" % self.d_pkg), + mock.patch("%s._query_security_group" % self.d_pkg), + mock.patch("%s.delete_security_group" % mod_path)): + + session_delete = self.context.session.delete + self.context.session.delete = mock.Mock(return_value=None) + self.driver.delete_security_group(self.context, 1) + self.assertTrue(self.context.session.delete.called) + self.context.session.delete = session_delete