From 193dbf6d1c1fe400c8996b200baf0bb3ed06f7a8 Mon Sep 17 00:00:00 2001 From: Oleg Bondarev Date: Mon, 4 Mar 2013 18:25:11 +0400 Subject: [PATCH] Add UT for LBaaS HAProxy driver fixes bug 1137386 Change-Id: I791d09fc472fd8555f4030cfd1964e70ab9f2771 --- .../driver/haproxy/test_cfg.py | 157 ++++++++++++++++++ .../driver/haproxy/test_namespace_driver.py | 68 ++++++++ 2 files changed, 225 insertions(+) diff --git a/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_cfg.py b/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_cfg.py index 8bddcb9972..4b95a23106 100644 --- a/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_cfg.py +++ b/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_cfg.py @@ -17,12 +17,143 @@ # # @author: Oleg Bondarev (obondarev@mirantis.com) +import contextlib +import mock import testtools +from oslo.config import cfg as config + from quantum.plugins.services.agent_loadbalancer.drivers.haproxy import cfg class TestHaproxyCfg(testtools.TestCase): + def test_save_config(self): + with contextlib.nested( + mock.patch('quantum.plugins.services.agent_loadbalancer.' + 'drivers.haproxy.cfg._build_global'), + mock.patch('quantum.plugins.services.agent_loadbalancer.' + 'drivers.haproxy.cfg._build_defaults'), + mock.patch('quantum.plugins.services.agent_loadbalancer.' + 'drivers.haproxy.cfg._build_frontend'), + mock.patch('quantum.plugins.services.agent_loadbalancer.' + 'drivers.haproxy.cfg._build_backend'), + mock.patch('quantum.agent.linux.utils.replace_file') + ) as (b_g, b_d, b_f, b_b, replace): + test_config = ['globals', 'defaults', 'frontend', 'backend'] + b_g.return_value = [test_config[0]] + b_d.return_value = [test_config[1]] + b_f.return_value = [test_config[2]] + b_b.return_value = [test_config[3]] + + cfg.save_config('test_path', mock.Mock()) + replace.assert_called_once_with('test_path', + '\n'.join(test_config)) + + def test_build_global(self): + config.CONF.register_opt(config.StrOpt('user_group')) + config.CONF.set_override('user_group', 'test_group') + expected_opts = ['global', + '\tdaemon', + '\tuser nobody', + '\tgroup test_group', + '\tlog /dev/log local0', + '\tlog /dev/log local1 notice', + '\tstats socket test_path mode 0666 level user'] + opts = cfg._build_global(mock.Mock(), 'test_path') + self.assertEqual(expected_opts, list(opts)) + config.CONF.reset() + + def test_build_defaults(self): + expected_opts = ['defaults', + '\tlog global', + '\tretries 3', + '\toption redispatch', + '\ttimeout connect 5000', + '\ttimeout client 50000', + '\ttimeout server 50000'] + opts = cfg._build_defaults(mock.Mock()) + self.assertEqual(expected_opts, list(opts)) + config.CONF.reset() + + def test_build_frontend(self): + test_config = {'vip': {'id': 'vip_id', + 'protocol': 'HTTP', + 'port': {'fixed_ips': [ + {'ip_address': '10.0.0.2'}] + }, + 'protocol_port': 80, + 'connection_limit': 2000, + }, + 'pool': {'id': 'pool_id'}} + expected_opts = ['frontend vip_id', + '\toption tcplog', + '\tbind 10.0.0.2:80', + '\tmode http', + '\tdefault_backend pool_id', + '\tmaxconn 2000', + '\toption forwardfor'] + opts = cfg._build_frontend(test_config) + self.assertEqual(expected_opts, list(opts)) + + test_config['vip']['connection_limit'] = -1 + expected_opts.remove('\tmaxconn 2000') + opts = cfg._build_frontend(test_config) + self.assertEqual(expected_opts, list(opts)) + + def test_build_backend(self): + test_config = {'pool': {'id': 'pool_id', + 'protocol': 'HTTP', + 'lb_method': 'ROUND_ROBIN'}, + 'members': [{'status': 'ACTIVE', + 'admin_state_up': True, + 'id': 'member1_id', + 'address': '10.0.0.3', + 'protocol_port': 80, + 'weight': 1}], + 'healthmonitors': [{'status': 'ACTIVE', + 'admin_state_up': True, + 'delay': 3, + 'max_retries': 4, + 'timeout': 2, + 'type': 'TCP'}], + 'vip': {'session_persistence': {'type': 'HTTP_COOKIE'}}} + expected_opts = ['backend pool_id', + '\tmode http', + '\tbalance roundrobin', + '\toption forwardfor', + '\ttimeout check 2s', + '\tcookie SRV insert indirect nocache', + '\tserver member1_id 10.0.0.3:80 weight 1 ' + 'check inter 3s fall 4 cookie 0'] + opts = cfg._build_backend(test_config) + self.assertEqual(expected_opts, list(opts)) + + def test_get_server_health_option(self): + test_config = {'healthmonitors': [{'status': 'ERROR', + 'admin_state_up': False, + 'delay': 3, + 'max_retries': 4, + 'timeout': 2, + 'type': 'TCP', + 'http_method': 'GET', + 'url_path': '/', + 'expected_codes': '200'}]} + self.assertEqual(('', []), cfg._get_server_health_option(test_config)) + + test_config['healthmonitors'][0]['status'] = 'ACTIVE' + self.assertEqual(('', []), cfg._get_server_health_option(test_config)) + + test_config['healthmonitors'][0]['admin_state_up'] = True + expected = (' check inter 3s fall 4', ['timeout check 2s']) + self.assertEqual(expected, cfg._get_server_health_option(test_config)) + + test_config['healthmonitors'][0]['type'] = 'HTTPS' + expected = (' check inter 3s fall 4', + ['timeout check 2s', + 'option httpchk GET /', + 'http-check expect rstatus 200', + 'option ssl-hello-chk']) + self.assertEqual(expected, cfg._get_server_health_option(test_config)) def test_has_http_cookie_persistence(self): config = {'vip': {'session_persistence': {'type': 'HTTP_COOKIE'}}} @@ -53,3 +184,29 @@ class TestHaproxyCfg(testtools.TestCase): config = {'vip': {'session_persistence': {'type': 'UNSUPPORTED'}}} self.assertEqual(cfg._get_session_persistence(config), []) + + def test_expand_expected_codes(self): + exp_codes = '' + self.assertEqual(cfg._expand_expected_codes(exp_codes), set([])) + exp_codes = '200' + self.assertEqual(cfg._expand_expected_codes(exp_codes), set(['200'])) + exp_codes = '200, 201' + self.assertEqual(cfg._expand_expected_codes(exp_codes), + set(['200', '201'])) + exp_codes = '200, 201,202' + self.assertEqual(cfg._expand_expected_codes(exp_codes), + set(['200', '201', '202'])) + exp_codes = '200-202' + self.assertEqual(cfg._expand_expected_codes(exp_codes), + set(['200', '201', '202'])) + exp_codes = '200-202, 205' + self.assertEqual(cfg._expand_expected_codes(exp_codes), + set(['200', '201', '202', '205'])) + exp_codes = '200, 201-203' + self.assertEqual(cfg._expand_expected_codes(exp_codes), + set(['200', '201', '202', '203'])) + exp_codes = '200, 201-203, 205' + self.assertEqual(cfg._expand_expected_codes(exp_codes), + set(['200', '201', '202', '203', '205'])) + exp_codes = '201-200, 205' + self.assertEqual(cfg._expand_expected_codes(exp_codes), set(['205'])) diff --git a/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py b/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py index 56445e39db..ccb4c91d24 100644 --- a/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py +++ b/quantum/tests/unit/services/agent_loadbalancer/driver/haproxy/test_namespace_driver.py @@ -20,6 +20,7 @@ import contextlib import mock import testtools +from quantum.common import exceptions from quantum.plugins.services.agent_loadbalancer.drivers.haproxy import ( namespace_driver ) @@ -179,3 +180,70 @@ class TestHaproxyNSDriver(testtools.TestCase): socket.reset_mock() self.assertEqual({}, self.driver.get_stats('pool_id')) self.assertFalse(socket.called) + + def test_plug(self): + test_port = {'id': 'port_id', + 'network_id': 'net_id', + 'mac_address': 'mac_addr', + 'fixed_ips': [{'ip_address': '10.0.0.2', + 'subnet': {'cidr': 'cidr'}}]} + with contextlib.nested( + mock.patch('quantum.agent.linux.ip_lib.device_exists'), + mock.patch('netaddr.IPNetwork'), + ) as (dev_exists, ip_net): + self.vif_driver.get_device_name.return_value = 'test_interface' + dev_exists.return_value = False + ip_net.return_value = ip_net + ip_net.prefixlen = 24 + + self.driver._plug('test_ns', test_port) + self.vip_plug_callback.assert_called_once_with('plug', test_port) + self.assertTrue(dev_exists.called) + self.vif_driver.plug.assert_called_once_with('net_id', 'port_id', + 'test_interface', + 'mac_addr', + namespace='test_ns') + self.vif_driver.init_l3.assert_called_once_with('test_interface', + ['10.0.0.2/24'], + namespace= + 'test_ns') + + dev_exists.return_value = True + self.assertRaises(exceptions.PreexistingDeviceFailure, + self.driver._plug, 'test_ns', test_port, False) + + def test_unplug(self): + self.vif_driver.get_device_name.return_value = 'test_interface' + + self.driver._unplug('test_ns', 'port_id') + self.vip_plug_callback.assert_called_once_with('unplug', + {'id': 'port_id'}) + self.vif_driver.unplug('test_interface', namespace='test_ns') + + def test_kill_pids_in_file(self): + with contextlib.nested( + mock.patch('os.path.exists'), + mock.patch('__builtin__.open') + ) as (path_exists, mock_open): + file_mock = mock.MagicMock() + mock_open.return_value = file_mock + file_mock.__enter__.return_value = file_mock + file_mock.__iter__.return_value = iter(['123']) + ns_wrapper = mock.Mock() + + path_exists.return_value = False + namespace_driver.kill_pids_in_file(ns_wrapper, 'test_path') + path_exists.assert_called_once_with('test_path') + self.assertFalse(mock_open.called) + + path_exists.return_value = True + ns_wrapper.netns.execute.side_effect = RuntimeError + namespace_driver.kill_pids_in_file(ns_wrapper, 'test_path') + ns_wrapper.netns.execute.assert_called_once_with(['kill', '-9', + '123']) + + def test_get_state_file_path(self): + with mock.patch('os.makedirs') as mkdir: + path = self.driver._get_state_file_path('pool_id', 'conf') + self.assertEqual('/the/path/pool_id/conf', path) + mkdir.assert_called_once_with('/the/path/pool_id', 0755)