diff options
author | Phillip Toohill <phillip.toohill@rackspace.com> | 2014-07-14 15:32:17 -0500 |
---|---|---|
committer | Doug Wiegley <dougw@a10networks.com> | 2014-11-06 17:55:35 +0100 |
commit | 7147389bad4fb76d7794f7b406608e0631cec4d8 (patch) | |
tree | d0b3a5384580f62197fb47582f88e7e19daf2d41 | |
parent | cfa4a86ebe9c628a14f7c4dc34986e72b1504bf0 (diff) | |
download | neutron-feature/lbaasv2.tar.gz |
Implement Jinja templates for haproxy configfeature/lbaasv2
Added templates dir
Added haproxy v1.4 template
Added template tests
Added jinja_cfg for new haproxy jinja templating
Partially-implements: blueprint lbaas-api-and-objmodel-improvement
Partially-implements: blueprint lbaas-refactor-haproxy-namespace-driver-to-new-driver-interface
Change-Id: Iad3a7be0146e1148b29edee56233aa3196c982dc
Co-authored-by: Brandon Logan <brandon.logan@rackspace.com>
Co-authored-by: Phillip Toohill <phillip.toohill@rackspace.com>
Co-authored-by: Dustin Lundquist <dustin@null-ptr.net>
Co-authored-by: Doug Wiegley <dougw@a10networks.com>
9 files changed, 778 insertions, 0 deletions
diff --git a/etc/services.conf b/etc/services.conf index f8a6090055..262c120827 100644 --- a/etc/services.conf +++ b/etc/services.conf @@ -38,3 +38,6 @@ #async_requests = #lb_flavor = small #sync_interval = 60 + +[haproxy] +#jinja_config_template = /opt/stack/neutron/neutron/services/drivers/haproxy/templates/haproxy_v1.4.template diff --git a/neutron/services/loadbalancer/drivers/haproxy/jinja_cfg.py b/neutron/services/loadbalancer/drivers/haproxy/jinja_cfg.py new file mode 100644 index 0000000000..cf7d8ff5f2 --- /dev/null +++ b/neutron/services/loadbalancer/drivers/haproxy/jinja_cfg.py @@ -0,0 +1,198 @@ +# Copyright 2014 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. + +import os + +import jinja2 +import six + +from neutron.agent.linux import utils +from neutron.plugins.common import constants as plugin_constants +from neutron.services.loadbalancer import constants +from oslo.config import cfg + +PROTOCOL_MAP = { + constants.PROTOCOL_TCP: 'tcp', + constants.PROTOCOL_HTTP: 'http', + constants.PROTOCOL_HTTPS: 'tcp' +} + +BALANCE_MAP = { + constants.LB_METHOD_ROUND_ROBIN: 'roundrobin', + constants.LB_METHOD_LEAST_CONNECTIONS: 'leastconn', + constants.LB_METHOD_SOURCE_IP: 'source' +} + +STATS_MAP = { + constants.STATS_ACTIVE_CONNECTIONS: 'scur', + constants.STATS_MAX_CONNECTIONS: 'smax', + constants.STATS_CURRENT_SESSIONS: 'scur', + constants.STATS_MAX_SESSIONS: 'smax', + constants.STATS_TOTAL_CONNECTIONS: 'stot', + constants.STATS_TOTAL_SESSIONS: 'stot', + constants.STATS_IN_BYTES: 'bin', + constants.STATS_OUT_BYTES: 'bout', + constants.STATS_CONNECTION_ERRORS: 'econ', + constants.STATS_RESPONSE_ERRORS: 'eresp' +} + +ACTIVE_PENDING_STATUSES = plugin_constants.ACTIVE_PENDING_STATUSES + ( + plugin_constants.INACTIVE, plugin_constants.DEFERRED) + +TEMPLATES_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'templates/')) +JINJA_ENV = None + +jinja_opts = [ + cfg.StrOpt( + 'jinja_config_template', + default=os.path.join( + TEMPLATES_DIR, + 'haproxy_v1.4.template'), + help=_('Jinja template file for haproxy configuration')) +] + +cfg.CONF.register_opts(jinja_opts, 'haproxy') + + +def save_config(conf_path, loadbalancer, socket_path=None, + user_group='nogroup'): + """Convert a logical configuration to the HAProxy version.""" + config_str = render_loadbalancer_obj(loadbalancer, user_group, socket_path) + utils.replace_file(conf_path, config_str) + + +def _get_template(): + global JINJA_ENV + if not JINJA_ENV: + template_loader = jinja2.FileSystemLoader( + searchpath=os.path.dirname(cfg.CONF.haproxy.jinja_config_template)) + JINJA_ENV = jinja2.Environment( + loader=template_loader, trim_blocks=True, lstrip_blocks=True) + return JINJA_ENV.get_template(os.path.basename( + cfg.CONF.haproxy.jinja_config_template)) + + +def render_loadbalancer_obj(loadbalancer, user_group, socket_path): + loadbalancer = _transform_loadbalancer(loadbalancer) + return _get_template().render({'loadbalancer': loadbalancer, + 'user_group': user_group, + 'stats_sock': socket_path}, + constants=constants) + + +def _transform_loadbalancer(loadbalancer): + listeners = [_transform_listener(x) for x in loadbalancer.listeners] + return { + 'name': loadbalancer.name, + 'vip_address': loadbalancer.vip_address, + 'listeners': listeners + } + + +def _transform_listener(listener): + ret_value = { + 'id': listener.id, + 'protocol_port': listener.protocol_port, + 'protocol': PROTOCOL_MAP[listener.protocol] + } + if listener.connection_limit and listener.connection_limit > -1: + ret_value['connection_limit'] = listener.connection_limit + if listener.default_pool: + ret_value['default_pool'] = _transform_pool(listener.default_pool) + + return ret_value + + +def _transform_pool(pool): + ret_value = { + 'id': pool.id, + 'protocol': PROTOCOL_MAP[pool.protocol], + 'lb_algorithm': BALANCE_MAP.get(pool.lb_algorithm, 'roundrobin'), + 'members': [], + 'health_monitor': '', + 'session_persistence': '', + 'admin_state_up': pool.admin_state_up, + 'status': pool.status + } + members = [_transform_member(x) + for x in pool.members if _include_member(x)] + ret_value['members'] = members + if pool.healthmonitor: + ret_value['health_monitor'] = _transform_health_monitor( + pool.healthmonitor) + if pool.sessionpersistence: + ret_value['session_persistence'] = _transform_session_persistence( + pool.sessionpersistence) + return ret_value + + +def _transform_session_persistence(persistence): + return { + 'type': persistence.type, + 'cookie_name': persistence.cookie_name + } + + +def _transform_member(member): + return { + 'id': member.id, + 'address': member.address, + 'protocol_port': member.protocol_port, + 'weight': member.weight, + 'admin_state_up': member.admin_state_up, + 'subnet_id': member.subnet_id, + 'status': member.status + } + + +def _transform_health_monitor(monitor): + return { + 'id': monitor.id, + 'type': monitor.type, + 'delay': monitor.delay, + 'timeout': monitor.timeout, + 'max_retries': monitor.max_retries, + 'http_method': monitor.http_method, + 'url_path': monitor.url_path, + 'expected_codes': '|'.join( + _expand_expected_codes(monitor.expected_codes)), + 'admin_state_up': monitor.admin_state_up, + } + + +def _include_member(member): + return member.status in ACTIVE_PENDING_STATUSES and member.admin_state_up + + +def _expand_expected_codes(codes): + """Expand the expected code string in set of codes. + + 200-204 -> 200, 201, 202, 204 + 200, 203 -> 200, 203 + """ + + retval = set() + for code in codes.replace(',', ' ').split(' '): + code = code.strip() + + if not code: + continue + elif '-' in code: + low, hi = code.split('-')[:2] + retval.update( + str(i) for i in six.moves.xrange(int(low), int(hi) + 1)) + else: + retval.add(code) + return retval diff --git a/neutron/services/loadbalancer/drivers/haproxy/templates/__init__.py b/neutron/services/loadbalancer/drivers/haproxy/templates/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/neutron/services/loadbalancer/drivers/haproxy/templates/__init__.py diff --git a/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_base.template b/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_base.template new file mode 100644 index 0000000000..e405c959b5 --- /dev/null +++ b/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_base.template @@ -0,0 +1,33 @@ +{# # Copyright 2014 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. +# +#} +# Configuration for {{ loadbalancer_name }} +global + daemon + user nobody + group {{ usergroup }} + log /dev/log local0 + log /dev/log local1 notice + stats socket {{ sock_path }} mode 0666 level user + +defaults + log global + retries 3 + option redispatch + timeout connect 5000 + timeout client 50000 + timeout server 50000 + +{% block proxies %}{% endblock proxies %} diff --git a/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_v1.4.template b/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_v1.4.template new file mode 100644 index 0000000000..60da1da694 --- /dev/null +++ b/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_v1.4.template @@ -0,0 +1,29 @@ +{# # Copyright 2014 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. +# +#} +{% extends 'haproxy_v1.4_proxies.template' %} +{% set loadbalancer_name = loadbalancer.name %} +{% set usergroup = user_group %} +{% set sock_path = stats_sock %} + +{% block proxies %} +{% from 'haproxy_v1.4_proxies.template' import frontend_macro as frontend_macro, backend_macro%} +{% for listener in loadbalancer.listeners %} +{{ frontend_macro(constants, listener, loadbalancer.vip_address) }} +{% if listener.default_pool %} +{{ backend_macro(constants, listener, listener.default_pool) }} +{% endif %} +{% endfor %} +{% endblock proxies %}
\ No newline at end of file diff --git a/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_v1.4_proxies.template b/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_v1.4_proxies.template new file mode 100644 index 0000000000..7c69fa0f25 --- /dev/null +++ b/neutron/services/loadbalancer/drivers/haproxy/templates/haproxy_v1.4_proxies.template @@ -0,0 +1,74 @@ +{# # Copyright 2014 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. +# +#} +{% extends 'haproxy_base.template' %} + +{% macro frontend_macro(constants, listener, lb_vip_address) %} +frontend {{ listener.id }} + option tcplog +{% if listener.connection_limit is defined %} + maxconn {{ listener.connection_limit }} +{% endif %} +{% if listener.protocol == constants.PROTOCOL_HTTP.lower() %} + option forwardfor +{% endif %} + bind {{ lb_vip_address }}:{{ listener.protocol_port }} + mode {{ listener.protocol }} +{% if listener.default_pool %} + default_backend {{ listener.default_pool.id }} +{% endif %} +{% endmacro %} + +{% macro backend_macro(constants, listener, pool) %} +backend {{ pool.id }} + mode {{ pool.protocol }} + balance {{ pool.lb_algorithm }} +{% if pool.session_persistence %} +{% if pool.session_persistence.type == constants.SESSION_PERSISTENCE_SOURCE_IP %} + stick-table type ip size 10k + stick on src +{% elif pool.session_persistence.type == constants.SESSION_PERSISTENCE_HTTP_COOKIE %} + cookie SRV insert indirect nocache +{% elif pool.session_persistence.type == constants.SESSION_PERSISTENCE_APP_COOKIE and pool.session_persistence.cookie_name %} + appsession {{ pool.session_persistence.cookie_name }} len 56 timeout 3h +{% endif %} +{% endif %} +{% if pool.health_monitor %} + timeout check {{ pool.health_monitor.timeout }} +{% if pool.health_monitor.type == constants.HEALTH_MONITOR_HTTP or pool.health_monitor.type == constants.HEALTH_MONITOR_HTTPS %} + option httpchk {{ pool.health_monitor.http_method }} {{ pool.health_monitor.url_path }} + http-check expect rstatus {{ pool.health_monitor.expected_codes }} +{% endif %} +{% if pool.health_monitor.type == constants.HEALTH_MONITOR_HTTPS %} + option ssl-hello-chk +{% endif %} +{% endif %} +{% if listener.protocol == constants.PROTOCOL_HTTP.lower() %} + option forwardfor +{% endif %} +{% for member in pool.members %} +{% if pool.health_monitor %} +{% set hm_opt = " check inter %ds fall %d"|format(pool.health_monitor.delay, pool.health_monitor.max_retries) %} +{% else %} +{% set hm_opt = "" %} +{% endif %} +{%if pool.session_persistence.type == constants.SESSION_PERSISTENCE_HTTP_COOKIE %} +{% set persistence_opt = " cookie %s"|format(member.id) %} +{% else %} +{% set persistence_opt = "" %} +{% endif %} + {{ "server %s %s:%d weight %s%s%s"|e|format(member.id, member.address, member.protocol_port, member.weight, hm_opt, persistence_opt)|trim() }} +{% endfor %} +{% endmacro %}
\ No newline at end of file diff --git a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/sample_configs/__init__.py b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/sample_configs/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/sample_configs/__init__.py diff --git a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/sample_configs/sample_configs.py b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/sample_configs/sample_configs.py new file mode 100644 index 0000000000..1756d61cf9 --- /dev/null +++ b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/sample_configs/sample_configs.py @@ -0,0 +1,207 @@ +# Copyright 2014 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. +# + +import collections + +RET_PERSISTENCE = { + 'type': 'HTTP_COOKIE', + 'cookie_name': 'HTTP_COOKIE'} + +RET_MONITOR = { + 'id': 'sample_monitor_id_1', + 'type': 'HTTP', + 'delay': 30, + 'timeout': 31, + 'max_retries': 3, + 'http_method': 'GET', + 'url_path': '/index.html', + 'expected_codes': '405|404|500', + 'admin_state_up': 'true'} + +RET_MEMBER_1 = { + 'id': 'sample_member_id_1', + 'address': '10.0.0.99', + 'protocol_port': 82, + 'weight': 13, + 'subnet_id': '10.0.0.1/24', + 'admin_state_up': 'true', + 'status': 'ACTIVE'} + +RET_MEMBER_2 = { + 'id': 'sample_member_id_2', + 'address': '10.0.0.98', + 'protocol_port': 82, + 'weight': 13, + 'subnet_id': '10.0.0.1/24', + 'admin_state_up': 'true', + 'status': 'ACTIVE'} + +RET_POOL = { + 'id': 'sample_pool_id_1', + 'protocol': 'http', + 'lb_algorithm': 'roundrobin', + 'members': [RET_MEMBER_1, RET_MEMBER_2], + 'health_monitor': RET_MONITOR, + 'session_persistence': RET_PERSISTENCE, + 'admin_state_up': 'true', + 'status': 'ACTIVE'} + +RET_LISTENER = { + 'id': 'sample_listener_id_1', + 'protocol_port': 80, + 'protocol': 'http', + 'default_pool': RET_POOL, + 'connection_limit': 98} + +RET_LB = { + 'name': 'test-lb', + 'vip_address': '10.0.0.2', + 'listeners': [RET_LISTENER]} + + +def sample_loadbalancer_tuple(proto=None, monitor=True, persistence=True, + persistence_type=None): + proto = 'HTTP' if proto is None else proto + in_lb = collections.namedtuple( + 'loadbalancer', 'id, name, vip_address, protocol, vip_port, ' + 'listeners') + return in_lb( + id='sample_loadbalancer_id_1', + name='test-lb', + vip_address='10.0.0.2', + protocol=proto, + vip_port=sample_vip_port_tuple(), + listeners=[sample_listener_tuple(proto=proto, monitor=monitor, + persistence=persistence, + persistence_type=persistence_type)] + ) + + +def sample_vip_port_tuple(): + vip_port = collections.namedtuple('vip_port', 'fixed_ips') + ip_address = collections.namedtuple('ip_address', 'ip_address') + in_address = ip_address(ip_address='10.0.0.2') + return vip_port(fixed_ips=[in_address]) + + +def sample_listener_tuple(proto=None, monitor=True, persistence=True, + persistence_type=None): + proto = 'HTTP' if proto is None else proto + in_listener = collections.namedtuple( + 'listener', 'id, protocol_port, protocol, default_pool, ' + 'connection_limit') + return in_listener( + id='sample_listener_id_1', + protocol_port=80, + protocol=proto, + default_pool=sample_pool_tuple(proto=proto, monitor=monitor, + persistence=persistence, + persistence_type=persistence_type), + connection_limit=98 + ) + + +def sample_pool_tuple(proto=None, monitor=True, persistence=True, + persistence_type=None): + proto = 'HTTP' if proto is None else proto + in_pool = collections.namedtuple( + 'pool', 'id, protocol, lb_algorithm, members, healthmonitor,' + 'sessionpersistence, admin_state_up, status') + mon = sample_health_monitor_tuple(proto=proto) if monitor is True else None + persis = sample_session_persistence_tuple( + persistence_type=persistence_type) if persistence is True else None + return in_pool( + id='sample_pool_id_1', + protocol=proto, + lb_algorithm='ROUND_ROBIN', + members=[sample_member_tuple('sample_member_id_1', '10.0.0.99'), + sample_member_tuple('sample_member_id_2', '10.0.0.98')], + healthmonitor=mon, + sessionpersistence=persis, + admin_state_up='true', + status='ACTIVE') + + +def sample_member_tuple(id, ip): + in_member = collections.namedtuple('member', + 'id, address, protocol_port, ' + 'weight, subnet_id, ' + 'admin_state_up, status') + return in_member( + id=id, + address=ip, + protocol_port=82, + weight=13, + subnet_id='10.0.0.1/24', + admin_state_up='true', + status='ACTIVE') + + +def sample_session_persistence_tuple(persistence_type=None): + spersistence = collections.namedtuple('SessionPersistence', + 'type, cookie_name') + pt = 'HTTP_COOKIE' if persistence_type is None else persistence_type + return spersistence(type=pt, + cookie_name=pt) + + +def sample_health_monitor_tuple(proto=None): + proto = 'HTTP' if proto is None else proto + monitor = collections.namedtuple( + 'monitor', 'id, type, delay, timeout, max_retries, http_method, ' + 'url_path, expected_codes, admin_state_up') + + return monitor(id='sample_monitor_id_1', type=proto, delay=30, + timeout=31, max_retries=3, http_method='GET', + url_path='/index.html', expected_codes='500, 405, 404', + admin_state_up='true') + + +def sample_base_expected_config(frontend=None, backend=None): + if frontend is None: + frontend = ("frontend sample_listener_id_1\n" + " option tcplog\n" + " maxconn 98\n" + " option forwardfor\n" + " bind 10.0.0.2:80\n" + " mode http\n" + " default_backend sample_pool_id_1\n\n") + if backend is None: + backend = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " timeout check 31\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 405|404|500\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 " + "check inter 30s fall 3 cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 " + "check inter 30s fall 3 cookie sample_member_id_2\n") + return ("# Configuration for test-lb\n" + "global\n" + " daemon\n" + " user nobody\n" + " group nogroup\n" + " log /dev/log local0\n" + " log /dev/log local1 notice\n" + " stats socket /sock_path mode 0666 level user\n\n" + "defaults\n" + " log global\n" + " retries 3\n" + " option redispatch\n" + " timeout connect 5000\n" + " timeout client 50000\n" + " timeout server 50000\n\n" + frontend + backend) diff --git a/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_jinja_cfg.py b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_jinja_cfg.py new file mode 100644 index 0000000000..7bb87a55d8 --- /dev/null +++ b/neutron/tests/unit/services/loadbalancer/drivers/haproxy/test_jinja_cfg.py @@ -0,0 +1,234 @@ +# Copyright 2014 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. + +import contextlib +import mock + +from neutron.services.loadbalancer.drivers.haproxy import jinja_cfg +from neutron.tests import base +from neutron.tests.unit.services.loadbalancer.drivers.haproxy.sample_configs \ + import sample_configs + + +class TestHaproxyCfg(base.BaseTestCase): + def test_save_config(self): + with contextlib.nested( + mock.patch('neutron.services.loadbalancer.' + 'drivers.haproxy.jinja_cfg.render_loadbalancer_obj'), + mock.patch('neutron.agent.linux.utils.replace_file') + ) as (r_t, replace): + r_t.return_value = 'fake_rendered_template' + lb = mock.Mock() + jinja_cfg.save_config('test_conf_path', lb, 'test_sock_path') + r_t.assert_called_once_with(lb, 'nogroup', 'test_sock_path') + replace.assert_called_once_with('test_conf_path', + 'fake_rendered_template') + + def test_get_template_v14(self): + template = jinja_cfg._get_template() + self.assertEqual('haproxy_v1.4.template', template.name) + + def test_render_template_http(self): + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " timeout check 31\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 405|404|500\n" + " option forwardfor\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 check " + "inter 30s fall 3 cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 check " + "inter 30s fall 3 cookie sample_member_id_2\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple(), + 'nogroup', '/sock_path') + self.assertEqual( + sample_configs.sample_base_expected_config(backend=be), + rendered_obj) + + def test_render_template_https(self): + fe = ("frontend sample_listener_id_1\n" + " option tcplog\n" + " maxconn 98\n" + " bind 10.0.0.2:80\n" + " mode tcp\n" + " default_backend sample_pool_id_1\n\n") + be = ("backend sample_pool_id_1\n" + " mode tcp\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " timeout check 31\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 405|404|500\n" + " option ssl-hello-chk\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 check " + "inter 30s fall 3 cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 check " + "inter 30s fall 3 cookie sample_member_id_2\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple(proto='HTTPS'), + 'nogroup', '/sock_path') + self.assertEqual(sample_configs.sample_base_expected_config( + frontend=fe, backend=be), rendered_obj) + + def test_render_template_no_monitor_http(self): + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " option forwardfor\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 " + "cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 " + "cookie sample_member_id_2\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple(proto='HTTP', + monitor=False), + 'nogroup', '/sock_path') + self.assertEqual(sample_configs.sample_base_expected_config( + backend=be), rendered_obj) + + def test_render_template_no_monitor_https(self): + fe = ("frontend sample_listener_id_1\n" + " option tcplog\n" + " maxconn 98\n" + " bind 10.0.0.2:80\n" + " mode tcp\n" + " default_backend sample_pool_id_1\n\n") + be = ("backend sample_pool_id_1\n" + " mode tcp\n" + " balance roundrobin\n" + " cookie SRV insert indirect nocache\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 " + "cookie sample_member_id_1\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 " + "cookie sample_member_id_2\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple(proto='HTTPS', + monitor=False), + 'nogroup', '/sock_path') + self.assertEqual(sample_configs.sample_base_expected_config( + frontend=fe, backend=be), rendered_obj) + + def test_render_template_no_persistence_https(self): + fe = ("frontend sample_listener_id_1\n" + " option tcplog\n" + " maxconn 98\n" + " bind 10.0.0.2:80\n" + " mode tcp\n" + " default_backend sample_pool_id_1\n\n") + be = ("backend sample_pool_id_1\n" + " mode tcp\n" + " balance roundrobin\n" + " server sample_member_id_1 10.0.0.99:82 weight 13\n" + " server sample_member_id_2 10.0.0.98:82 weight 13\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple(proto='HTTPS', + monitor=False, + persistence=False), + 'nogroup', '/sock_path') + self.assertEqual(sample_configs.sample_base_expected_config( + frontend=fe, backend=be), rendered_obj) + + def test_render_template_no_persistence_http(self): + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " option forwardfor\n" + " server sample_member_id_1 10.0.0.99:82 weight 13\n" + " server sample_member_id_2 10.0.0.98:82 weight 13\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple(proto='HTTP', + monitor=False, + persistence=False), + 'nogroup', '/sock_path') + self.assertEqual(sample_configs.sample_base_expected_config( + backend=be), rendered_obj) + + def test_render_template_sourceip_persistence(self): + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " stick-table type ip size 10k\n" + " stick on src\n" + " timeout check 31\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 405|404|500\n" + " option forwardfor\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 check " + "inter 30s fall 3\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 check " + "inter 30s fall 3\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple( + persistence_type='SOURCE_IP'), + 'nogroup', '/sock_path') + self.assertEqual( + sample_configs.sample_base_expected_config(backend=be), + rendered_obj) + + def test_render_template_appsession_persistence(self): + be = ("backend sample_pool_id_1\n" + " mode http\n" + " balance roundrobin\n" + " appsession APP_COOKIE len 56 timeout 3h\n" + " timeout check 31\n" + " option httpchk GET /index.html\n" + " http-check expect rstatus 405|404|500\n" + " option forwardfor\n" + " server sample_member_id_1 10.0.0.99:82 weight 13 check " + "inter 30s fall 3\n" + " server sample_member_id_2 10.0.0.98:82 weight 13 check " + "inter 30s fall 3\n\n") + rendered_obj = jinja_cfg.render_loadbalancer_obj( + sample_configs.sample_loadbalancer_tuple( + persistence_type='APP_COOKIE'), + 'nogroup', '/sock_path') + self.assertEqual( + sample_configs.sample_base_expected_config(backend=be), + rendered_obj) + + def test_transform_session_persistence(self): + in_persistence = sample_configs.sample_session_persistence_tuple() + ret = jinja_cfg._transform_session_persistence(in_persistence) + self.assertEqual(sample_configs.RET_PERSISTENCE, ret) + + def test_transform_health_monitor(self): + in_persistence = sample_configs.sample_health_monitor_tuple() + ret = jinja_cfg._transform_health_monitor(in_persistence) + self.assertEqual(sample_configs.RET_MONITOR, ret) + + def test_transform_member(self): + in_member = sample_configs.sample_member_tuple('sample_member_id_1', + '10.0.0.99') + ret = jinja_cfg._transform_member(in_member) + self.assertEqual(sample_configs.RET_MEMBER_1, ret) + + def test_transform_pool(self): + in_pool = sample_configs.sample_pool_tuple() + ret = jinja_cfg._transform_pool(in_pool) + self.assertEqual(sample_configs.RET_POOL, ret) + + def test_transform_listener(self): + in_listener = sample_configs.sample_listener_tuple() + ret = jinja_cfg._transform_listener(in_listener) + self.assertEqual(sample_configs.RET_LISTENER, ret) + + def test_transform_loadbalancer(self): + in_lb = sample_configs.sample_loadbalancer_tuple() + ret = jinja_cfg._transform_loadbalancer(in_lb) + self.assertEqual(sample_configs.RET_LB, ret)
\ No newline at end of file |