From cfbd9b801847275bb51a209a134f1b09ad628e05 Mon Sep 17 00:00:00 2001 From: Varun Lodaya Date: Thu, 3 Sep 2015 11:24:59 -0700 Subject: [PATCH] Allow custom configs with LBaaS This fix takes care of haproxy parsing and validation changes on vrouter agent. Removing extra white spaces Closes-Bug: #1475393 Change-Id: I822e27792f78168a178d555db5703fa1e73d0cc9 --- .../opencontrail-vrouter-netns/SConscript | 1 + .../haproxy_config.py | 64 +++++++++-- .../haproxy_validator.py | 105 ++++++++++++++++++ 3 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py diff --git a/src/vnsw/opencontrail-vrouter-netns/SConscript b/src/vnsw/opencontrail-vrouter-netns/SConscript index 9b7ac8713d9..a581bb4e99e 100644 --- a/src/vnsw/opencontrail-vrouter-netns/SConscript +++ b/src/vnsw/opencontrail-vrouter-netns/SConscript @@ -18,6 +18,7 @@ sources = [ 'opencontrail_vrouter_netns/vrouter_netns.py', 'opencontrail_vrouter_netns/haproxy_config.py', 'opencontrail_vrouter_netns/haproxy_process.py', + 'opencontrail_vrouter_netns/haproxy_validator.py', 'opencontrail_vrouter_netns/vrouter_docker.py', 'opencontrail_vrouter_netns/daemon_start.py', 'opencontrail_vrouter_netns/daemon_stop.py', diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py index 0063c65dc9a..f29c8e46a49 100644 --- a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_config.py @@ -1,6 +1,16 @@ import json import os +def validate_custom_attributes(config, section): + return {} + +try: + from haproxy_validator import validate_custom_attributes as validator + from haproxy_validator import custom_attributes_dict +except ImportError: + validator = validate_custom_attributes + custom_attributes_dict = {} + PROTO_TCP = 'TCP' PROTO_HTTP = 'HTTP' PROTO_HTTPS = 'HTTPS' @@ -45,6 +55,15 @@ def build_config(conf_file): return filename def _set_global_config(config, sock_path): + global_custom_attributes = validator(config, 'global') + maxconn = global_custom_attributes.pop('maxconn', None) \ + if 'maxconn' in global_custom_attributes else 65000 + ssl_ciphers = global_custom_attributes.pop('ssl_ciphers', None) \ + if 'ssl_ciphers' in global_custom_attributes else \ + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:' \ + 'ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:' \ + 'RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS' + conf = [ 'global', 'daemon', @@ -53,30 +72,45 @@ def _set_global_config(config, sock_path): 'log /dev/log local0', 'log /dev/log local1 notice', 'tune.ssl.default-dh-param 2048', - 'ssl-default-bind-ciphers ECDH+AESGCM:' \ - 'DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:' \ - 'DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:' \ - 'RSA+3DES:!aNULL:!MD5:!DSS', + 'ssl-default-bind-ciphers %s' % ssl_ciphers, 'ulimit-n 200000', - 'maxconn 65000' + 'maxconn %d' % maxconn ] conf.append('stats socket %s mode 0666 level user' % sock_path) + for key, value in global_custom_attributes.iteritems(): + cmd = custom_attributes_dict['global'][key]['cmd'] + conf.append(cmd % value) + return ("\n\t".join(conf)) def _set_defaults(config): + default_custom_attributes = validator(config, 'default') + client_timeout = default_custom_attributes.pop('client_timeout', None) \ + if 'client_timeout' in default_custom_attributes else 300000 + server_timeout = default_custom_attributes.pop('server_timeout', None) \ + if 'server_timeout' in default_custom_attributes else 300000 + connect_timeout = default_custom_attributes.pop('connect_timeout', None) \ + if 'connect_timeout' in default_custom_attributes else 5000 + conf = [ 'defaults', 'log global', 'retries 3', 'option redispatch', - 'timeout connect 5000', - 'timeout client 300000', - 'timeout server 300000', + 'timeout connect %d' % connect_timeout, + 'timeout client %d' % client_timeout, + 'timeout server %d' % server_timeout, ] + + for key, value in default_custom_attributes.iteritems(): + cmd = custom_attributes_dict['default'][key]['cmd'] + conf.append(cmd % value) + return ("\n\t".join(conf)) def _set_frontend(config): port = config['vip']['port'] + vip_custom_attributes = validator(config, 'vip') ssl = '' if config['vip']['protocol'] == PROTO_HTTPS: ssl = 'ssl crt %s no-sslv3' % config['ssl-crt'] @@ -92,9 +126,15 @@ def _set_frontend(config): if config['vip']['protocol'] == PROTO_HTTP or \ config['vip']['protocol'] == PROTO_HTTPS: conf.append('option forwardfor') + + for key, value in vip_custom_attributes.iteritems(): + cmd = custom_attributes_dict['vip'][key]['cmd'] + conf.append(cmd % value) + return ("\n\t".join(conf)) def _set_backend(config): + pool_custom_attributes = validator(config, 'pool') conf = [ 'backend %s' % config['pool']['id'], 'mode %s' % PROTO_MAP[config['pool']['protocol']], @@ -103,6 +143,10 @@ def _set_backend(config): if config['pool']['protocol'] == PROTO_HTTP: conf.append('option forwardfor') + for key, value in pool_custom_attributes.iteritems(): + cmd = custom_attributes_dict['pool'][key]['cmd'] + conf.append(cmd % value) + server_suffix, monitor_conf = _set_health_monitor(config) conf.extend(monitor_conf) session_conf = _set_session_persistence(config) @@ -117,6 +161,10 @@ def _set_backend(config): server += ' cookie %d' % config['members'].index(member) conf.append(server) + for key, value in pool_custom_attributes.iteritems(): + cmd = custom_attributes_dict['pool'][key]['cmd'] + conf.append(cmd % value) + return ("\n\t".join(conf)) def _set_health_monitor(config): diff --git a/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py new file mode 100644 index 00000000000..8b9d957dc8e --- /dev/null +++ b/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/haproxy_validator.py @@ -0,0 +1,105 @@ +custom_attributes_dict = { + 'global': { + 'max_conn': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxconn %d' + }, + 'max_conn_rate': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxconnrate %d' + }, + 'max_sess_rate': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxsessrate %d' + }, + 'max_ssl_conn': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxsslconn %d' + }, + 'max_ssl_rate': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'maxsslrate %d' + }, + 'ssl_ciphers': { + 'type': str, + 'limits': [1, 100], + 'cmd': 'ssl-default-bind-ciphers %s' + }, + 'tune_http_max_header': { + 'type': int, + 'limits': [1, 128], + 'cmd': 'tune.http.maxhdr %d' + }, + 'tune_ssl_max_record': { + 'type': int, + 'limits': [1, 16384], + 'cmd': 'tune.ssl.maxrecord %d' + } + }, + 'default': { + 'server_timeout': { + 'type': int, + 'limits': [1, 5000000], + 'cmd': 'timeout server %d' + }, + 'client_timeout': { + 'type': int, + 'limits': [1, 5000000], + 'cmd': 'timeout client %d' + }, + 'connect_timeout': { + 'type': int, + 'limits': [1, 5000000], + 'cmd': 'timeout connect %d' + } + }, + 'vip': { + 'http_server_close': { + 'type': bool, + 'limits': ['True', 'False'], + 'cmd': '%soption http-server-close' + }, + 'rate_limit_sessions': { + 'type': int, + 'limits': [1, 65535], + 'cmd': 'rate-limit sessions %d' + } + }, + 'pool': {}, +} + +def validate_custom_attributes(config, section): + section_dict = {} + if 'custom-attributes' in config and section in custom_attributes_dict: + custom_attributes = config['custom-attributes'] + for key, value in custom_attributes.iteritems(): + if key in custom_attributes_dict[section]: + #Sanitize the value + try: + type_attr = custom_attributes_dict[section][key]['type'] + limits = custom_attributes_dict[section][key]['limits'] + if type_attr == int: + value = type_attr(value) + if value in range(limits[0], limits[1]): + section_dict.update({key:value}) + elif type_attr == str: + if len(value) in range(limits[0], limits[1]): + section_dict.update({key:value}) + elif type_attr == bool: + if value in limits: + if value == 'True': + value = '' + elif value == 'False': + value = 'no ' + section_dict.update({key:value}) + except Exception as e: + print "Skipping key: %s, value: %s due to validation failure" \ + % (key, value) + continue + + return section_dict