diff --git a/src/config/svc-monitor/SConscript b/src/config/svc-monitor/SConscript
index ca9af89cef5..e7ec12c5812 100644
--- a/src/config/svc-monitor/SConscript
+++ b/src/config/svc-monitor/SConscript
@@ -41,7 +41,6 @@ sources = [
'svc_monitor/services/loadbalancer/drivers/ha_proxy/__init__.py',
'svc_monitor/services/loadbalancer/drivers/ha_proxy/driver.py',
'svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_config.py',
- 'svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_template.txt',
'svc_monitor/snat_agent.py',
'svc_monitor/tests/__init__.py',
'svc_monitor/tests/test_common_utils.py',
diff --git a/src/config/svc-monitor/svc_monitor/config_db.py b/src/config/svc-monitor/svc_monitor/config_db.py
index b53d9478208..1078cfbd66a 100644
--- a/src/config/svc-monitor/svc_monitor/config_db.py
+++ b/src/config/svc-monitor/svc_monitor/config_db.py
@@ -490,6 +490,8 @@ def __init__(self, uuid, obj_dict=None):
self.virtual_machines = set()
self.logical_router = None
self.params = None
+ self.bindings = None
+ self.kvps = None
self.state = 'init'
self.launch_count = 0
self.back_off = -1
@@ -520,6 +522,9 @@ def update(self, obj=None):
self.proj_name = obj['fq_name'][-2]
self.check_vn_changes(obj)
self.params = obj.get('service_instance_properties', None)
+ self.bindings = obj.get('service_instance_bindings', None)
+ if self.bindings:
+ self.kvps = self.bindings.get('key_value_pair', None)
self.update_single_ref('service_template', obj)
self.update_single_ref('loadbalancer', obj)
self.update_single_ref('loadbalancer_pool', obj)
diff --git a/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py b/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py
index 46fda9f768e..40e8dd3affb 100644
--- a/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py
+++ b/src/config/svc-monitor/svc_monitor/loadbalancer_agent.py
@@ -208,6 +208,7 @@ def virtual_ip_add(self, vip):
v = self.virtual_ip_get_reqdict(vip)
driver = self._get_driver_for_pool(v['pool_id'])
try:
+ driver.set_config_v1(vip.loadbalancer_pool)
if not vip.last_sent:
driver.create_vip(v)
elif v != vip.last_sent:
@@ -230,6 +231,7 @@ def loadbalancer_add(self, loadbalancer):
lb = self.loadbalancer_get_reqdict(loadbalancer)
driver = self._get_driver_for_loadbalancer(lb['id'], 'opencontrail')
try:
+ driver.set_config_v2(loadbalancer.uuid)
if not loadbalancer.last_sent:
driver.create_loadbalancer(lb)
elif lb != loadbalancer.last_sent:
diff --git a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/f5/f5_driver.py b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/f5/f5_driver.py
index b2e56928b4c..e01146203ea 100644
--- a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/f5/f5_driver.py
+++ b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/f5/f5_driver.py
@@ -1327,3 +1327,9 @@ def update_health_monitor(self,
pool_id):
pass
# end update_health_monitor
+
+ def set_config_v1(self, pool_id):
+ pass
+
+ def set_config_v2(self, lb_id):
+ pass
diff --git a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/driver.py b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/driver.py
index ea204cd9514..0cf93a03c59 100644
--- a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/driver.py
+++ b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/driver.py
@@ -9,8 +9,10 @@
from vnc_api.vnc_api import ServiceTemplate, ServiceInstance, ServiceInstanceType
from vnc_api.vnc_api import ServiceScaleOutType, ServiceInstanceInterfaceType
from vnc_api.vnc_api import NoIdError, RefsExistError
+from vnc_api.vnc_api import KeyValuePair, KeyValuePairs
from svc_monitor.config_db import *
+import haproxy_config
LOADBALANCER_SERVICE_TEMPLATE = [
'default-domain',
@@ -383,3 +385,39 @@ def delete_pool_health_monitor(self, health_monitor, pool_id):
def update_health_monitor(self, id, health_monitor):
pass
+
+ def set_config_v1(self, pool_id):
+ pool = LoadbalancerPoolSM.get(pool_id)
+ if not pool:
+ return
+ conf = haproxy_config.get_config_v1(pool)
+ self.set_haproxy_config(pool.service_instance, pool.uuid, conf)
+
+ def set_config_v2(self, lb_id):
+ lb = LoadbalancerSM.get(lb_id)
+ if not lb:
+ return
+ conf = haproxy_config.get_config_v2(lb.uuid)
+ self.set_haproxy_config(pool.service_instance, lb.uuid, conf)
+
+ def set_haproxy_config(self, si_id, lb_uuid, conf):
+ si = ServiceInstanceSM.get(si_id)
+ if not si:
+ return
+
+ for kv in si.kvps:
+ if kv['key'] == 'haproxy_config':
+ if kv['value'] == conf:
+ return
+
+ si_obj = ServiceInstance()
+ si_obj.uuid = si.uuid
+ si_obj.fq_name = si.fq_name
+ kvp = KeyValuePair('haproxy_config', conf)
+ si_obj.add_service_instance_bindings(kvp)
+ kvp = KeyValuePair('lb_uuid', lb_uuid)
+ si_obj.add_service_instance_bindings(kvp)
+ try:
+ self._api.service_instance_update(si_obj)
+ except NoIdError:
+ return
diff --git a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_config.py b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_config.py
index 26ca8907c67..b7c6107223f 100644
--- a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_config.py
+++ b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_config.py
@@ -1,13 +1,13 @@
-from mako.template import Template
-from mako import exceptions
-from mako.runtime import Context
-from StringIO import StringIO
from svc_monitor.config_db import *
+PROTO_HTTP = 'HTTP'
+PROTO_HTTPS = 'HTTPS'
+
PROTO_MAP = {
'TCP': 'tcp',
'HTTP': 'http',
- 'HTTPS': 'http'
+ 'HTTPS': 'http',
+ 'TERMINATED_HTTPS': 'terminated_https'
}
LB_METHOD_MAP = {
@@ -16,134 +16,218 @@
'SOURCE_IP': 'source'
}
-class HaproxyConfig(object):
- def __init__(self):
- self.config = {}
- self.template = Template(filename='haproxy_template.txt', format_exceptions=True)
- self.conf_dir = '/var/lib/contrail/loadbalancer/'
+HEALTH_MONITOR_PING = 'PING'
+HEALTH_MONITOR_TCP = 'TCP'
+HEALTH_MONITOR_HTTP = 'HTTP'
+HEALTH_MONITOR_HTTPS = 'HTTPS'
- def build_config_v1(self, pool_dict):
- pool = LoadbalancerPoolSM.get(pool_dict['id'])
- if not pool:
- return
- self.set_globals(pool.uuid)
- self.set_defaults()
- self.set_virtual_ip(pool)
- buf = StringIO()
- ctx = Context(buf, **self.config)
- self.template.render_context(ctx)
- return buf.getvalue()
-
- def build_config_v2(self, lb):
- self.set_globals(lb.uuid)
- self.set_defaults()
- self.set_loadbalancer(lb)
- buf = StringIO()
- ctx = Context(buf, **self.config)
- self.template.render_context(ctx)
- return buf.getvalue()
-
- def set_globals(self, uuid):
- self.config['sock_path'] = self.conf_dir + uuid + '/haproxy.sock'
- self.config['max_conn'] = 65000
- self.config['ssl_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'
-
- def set_defaults(self):
- self.config['client_timeout'] = "300000"
- self.config['server_timeout'] = "300000"
- self.config['connect_timeout'] = "5000"
-
- def set_loadbalancer(self, lb):
- loadbalancer = {}
- loadbalancer['vip'] = lb.virtual_ip
- loadbalancer['listeners'] = []
- for listener_id in lb.listeners:
- listener = LoadbalancerListener.get(listener_id)
- if not listener:
- continue
- loadbalancer['listeners'].append(listener_id)
- self.add_listener(listener)
- self.config['loadbalancer'] = loadbalancer
-
- def add_listener(self, ll):
- listener = {}
- listener['port'] = ll.port
- listener['protocol'] = PROTO_MAP.get(ll.protocol)
- for pool_id in ll.pools:
- pool = LoadbalancerPool.get(pool_id)
- if not pool:
- continue
- listener['pool'] = pool_id
- self.add_pool(pool)
- self.config['listeners'][ll.uuid] = listener
-
- def set_virtual_ip(self, pool):
+PERSISTENCE_SOURCE_IP = 'SOURCE_IP'
+PERSISTENCE_HTTP_COOKIE = 'HTTP_COOKIE'
+PERSISTENCE_APP_COOKIE = 'APP_COOKIE'
+
+def get_config_v2(lb):
+ sock_path = '/var/lib/contrail/loadbalancer/'
+ sock_path += lb.uuid + '/haproxy.sock'
+ conf = set_globals(sock_path) + '\n\n'
+ conf += set_defaults() + '\n\n'
+ conf += set_v2_frontend_backend(lb)
+ return conf
+
+def get_config_v1(pool):
+ sock_path = '/var/lib/contrail/loadbalancer/'
+ sock_path += pool.uuid + '/haproxy.sock'
+ conf = set_globals(sock_path) + '\n\n'
+ conf += set_defaults() + '\n\n'
+ conf += set_v1_frontend_backend(pool)
+ return conf
+
+def set_globals(sock_path):
+ maxconn = 65000
+ ssl_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'
+
+ conf = [
+ 'global',
+ 'daemon',
+ 'user nobody',
+ 'group nogroup',
+ 'log /dev/log local0',
+ 'log /dev/log local1 notice',
+ 'tune.ssl.default-dh-param 2048',
+ 'ssl-default-bind-ciphers %s' % ssl_ciphers,
+ 'ulimit-n 200000',
+ 'maxconn %d' % maxconn
+ ]
+ conf.append('stats socket %s mode 0666 level user' % sock_path)
+ res = "\n\t".join(conf)
+ return res
+
+def set_defaults():
+ client_timeout = 300000
+ server_timeout = 300000
+ connect_timeout = 5000
+
+ conf = [
+ 'defaults',
+ 'log global',
+ 'retries 3',
+ 'option redispatch',
+ 'timeout connect %d' % connect_timeout,
+ 'timeout client %d' % client_timeout,
+ 'timeout server %d' % server_timeout,
+ ]
+
+ res = "\n\t".join(conf)
+ return res
+
+def set_v1_frontend_backend(pool):
+ conf = []
+ vip = VirtualIpSM.get(pool.virtual_ip)
+ if not vip and not vip.params['admin_state']:
+ return
+
+ ssl = ''
+ if vip.params['protocol'] == PROTO_HTTPS:
+ ssl = 'ssl crt %s no-sslv3' % crt_file
+
+ lconf = [
+ 'frontend %s' % vip.uuid,
+ 'option tcplog',
+ 'bind %s:%s %s' % (vip.params['address'],
+ vip.params['protocol_port'], ssl),
+ 'mode %s' % PROTO_MAP[vip.params['protocol']]
+ ]
+ if vip.params['protocol'] == PROTO_HTTP or \
+ vip.params['protocol'] == PROTO_HTTPS:
+ lconf.append('option forwardfor')
+
+ if pool and pool.params['admin_state']:
+ lconf.append('default_backend %s' % pool.uuid)
+ res = "\n\t".join(lconf) + '\n\n'
+ res += set_backend(pool)
+ conf.append(res)
+
+ return "\n".join(conf)
+
+def set_v2_frontend_backend(lb):
+ conf = []
+ for ll_id in lb.loadbalancer_listeners:
+ ll = LoadbalancerListenerSM.get(ll_id)
+ if not ll:
+ continue
+ if not ll.params['admin_state']:
+ continue
+
+ ssl = ''
+ if ll.params['protocol'] == PROTO_HTTPS:
+ ssl = 'ssl crt %s no-sslv3' % crt_file
+
+ lconf = [
+ 'frontend %s' % ll.uuid,
+ 'option tcplog',
+ 'bind %s:%s %s' % (lb.params['vip_address'],
+ ll.params['protocol_port'], ssl),
+ 'mode %s' % PROTO_MAP[ll.params['protocol']]
+ ]
+ if ll.params['protocol'] == PROTO_HTTP or \
+ ll.params['protocol'] == PROTO_HTTPS:
+ lconf.append('option forwardfor')
+
+ pool = LoadbalancerPoolSM.get(ll.loadbalancer_pool)
+ if pool and pool.params['admin_state']:
+ lconf.append('default_backend %s' % pool.uuid)
+ res = "\n\t".join(lconf) + '\n\n'
+ res += set_backend(pool)
+ conf.append(res)
+
+ return "\n".join(conf)
+
+def set_backend(pool):
+ conf = [
+ 'backend %s' % pool.uuid,
+ 'mode %s' % PROTO_MAP[pool.params['protocol']],
+ 'balance %s' % LB_METHOD_MAP[pool.params['loadbalancer_method']]
+ ]
+ if pool.params['protocol'] == PROTO_HTTP:
+ conf.append('option forwardfor')
+
+ server_suffix = ''
+ for hm_id in pool.loadbalancer_healthmonitors:
+ hm = HealthMonitorSM.get(hm_id)
+ if not hm:
+ continue
+ server_suffix, monitor_conf = set_health_monitor(hm)
+ conf.extend(monitor_conf)
+
+ session_conf = set_session_persistence(pool)
+ conf.extend(session_conf)
+
+ for member_id in pool.members:
+ member = LoadbalancerMemberSM.get(member_id)
+ if not member or not member.params['admin_state']:
+ continue
+ server = (('server %s %s:%s weight %s') % (member.uuid,
+ member.params['address'], member.params['protocol_port'],
+ member.params['weight'])) + server_suffix
+ conf.append(server)
+
+ return "\n\t".join(conf) + '\n'
+
+def set_health_monitor(hm):
+ if not hm.params['admin_state']:
+ return '', []
+
+ server_suffix = ' check inter %ss fall %s' % \
+ (hm.params['delay'], hm.params['max_retries'])
+ conf = [
+ 'timeout check %ss' % hm.params['timeout']
+ ]
+
+ if hm.params['monitor_type'] in (HEALTH_MONITOR_HTTP, HEALTH_MONITOR_HTTPS):
+ conf.append('option httpchk %s %s' %
+ (hm.params['http_method'], hm.params['url_path']))
+ conf.append(
+ 'http-check expect rstatus %s' %
+ '|'.join(_get_codes(hm.params['expected_codes']))
+ )
+
+ if hm.params['monitor_type'] == HEALTH_MONITOR_HTTPS:
+ conf.append('option ssl-hello-chk')
+
+ return server_suffix, conf
+
+def set_session_persistence(pool):
+ conf = []
+ if pool.virtual_ip:
vip = VirtualIpSM.get(pool.virtual_ip)
if not vip:
return
- loadbalancer = {}
- loadbalancer['vip'] = vip.params['address']
- loadbalancer['listeners'] = [vip.uuid]
- self.config['loadbalancer'] = loadbalancer
- self.config['persistence'] = vip.params['persistence_type']
- self.config['cookie'] = vip.params['persistence_cookie_name']
- self.config['ssl_cert'] = '/tmp/ssl_cert'
- self.config['listeners'] = {vip.uuid :
- {'port': vip.params['protocol_port'],
- 'protocol': PROTO_MAP.get(vip.params['protocol']),
- 'pool': pool.uuid}}
- self.config['pools'] = {}
- self.add_pool(pool)
-
- def add_pool(self, lp):
- pool = {}
- pool['protocol'] = PROTO_MAP.get(lp.params['protocol'])
- pool['method'] = LB_METHOD_MAP.get(lp.params['loadbalancer_method'])
- pool['members'] = []
- self.set_healthmonitor(lp)
- self.config['members'] = {}
- for server_id in lp.members:
- server = LoadbalancerMemberSM.get(server_id)
- if not server:
- continue
- pool['members'].append(server_id)
- self.add_server(server)
- self.config['pools'][lp.uuid] = pool
-
- def add_server(self, server):
- member = {}
- member['address'] = server.params['address']
- member['port'] = server.params['protocol_port']
- member['weight'] = server.params['weight']
- self.config['members'][server.uuid] = member
-
- def set_healthmonitor(self, pool):
- for hm_id in pool.loadbalancer_healthmonitors:
- hm = HealthMonitorSM.get(hm_id)
- if not hm:
- continue
- self.config['monitor_type'] = hm.params['monitor_type']
- self.config['timeout'] = hm.params['timeout']
- self.config['delay'] = hm.params['delay']
- self.config['max_retries'] = hm.params['max_retries']
- self.config['http_method'] = hm.params['http_method']
- self.config['url_path'] = hm.params['url_path']
- self.config['expected_codes'] = \
- '|'.join(self._get_codes(hm.params['expected_codes']))
- return
+ persistence = vip.params.get('persistence_type', None)
+ cookie = vip.params.get('persistence_cookie_name', None)
+ else:
+ persistence = pool.params.get('session_persistence', None)
+ cookie = pool.params.get('persistence_cookie_name', None)
+
+ if persistence == PERSISTENCE_SOURCE_IP:
+ conf.append('stick-table type ip size 10k')
+ conf.append('stick on src')
+ elif persistence == PERSISTENCE_HTTP_COOKIE:
+ conf.append('cookie SRV insert indirect nocache')
+ elif (persistence == PERSISTENCE_APP_COOKIE and cookie):
+ conf.append('appsession %s len 56 timeout 3h' % cookie)
+ return conf
- def _get_codes(self, codes):
- response = set()
- for code in codes.replace(',', ' ').split(' '):
- code = code.strip()
- if not code:
- continue
- elif '-' in code:
- low, hi = code.split('-')[:2]
- response.update(str(i) for i in xrange(int(low), int(hi) + 1))
- else:
- response.add(code)
- return response
+def _get_codes(codes):
+ response = set()
+ for code in codes.replace(',', ' ').split(' '):
+ code = code.strip()
+ if not code:
+ continue
+ elif '-' in code:
+ low, hi = code.split('-')[:2]
+ response.update(str(i) for i in xrange(int(low), int(hi) + 1))
+ else:
+ response.add(code)
+ return response
diff --git a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_template.txt b/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_template.txt
deleted file mode 100644
index 78d94ba4556..00000000000
--- a/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_template.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-global
- daemon
- user nobody
- group nogroup
- log /dev/log local0
- log /dev/log local1 notice
- tune.ssl.default-dh-param 2048
- ssl-default-bind-ciphers ${ssl_ciphers}
- ulimit-n 200000
- maxconn ${max_conn}
- stats socket ${sock_path} mode 0666 level user
-
-defaults
- log global
- retries 3
- option redispatch
- timeout connect ${connect_timeout}
- timeout client ${client_timeout}
- timeout server ${server_timeout}
-
-% for listener_id in loadbalancer['listeners']:
-<% listener = listeners[listener_id] %>\
-frontend ${listener_id}
- option tcplog
- bind ${loadbalancer['vip']}:${listener['port']} ${ssl_cert}
- mode ${listener['protocol']}
- default_backend ${listener['pool']}
- option forwardfor
-
-<% pool = pools[listener['pool']] %>\
-backend ${listener['pool']}
- mode ${pool['protocol']}
- balance ${pool['method']}
- % if pool['protocol'] == 'http':
- option forwardfor
- % endif
- % if timeout:
- timeout check ${timeout}s
- % endif
- % if monitor_type in ['HTTP', 'HTTPS']:
- option httpchk ${http_method} ${url_path}
- http-check expect rstatus ${expected_codes}
- % endif
- % if monitor_type in ['HTTPS']:
- option ssl-hello-chk
- % endif
- % if persistence == 'PERSISTENCE_SOURCE_IP':
- stick-table type ip size 10k
- stick on src
- % elif persistence == 'PERSISTENCE_HTTP_COOKIE':
- cookie SRV insert indirect nocache
- % elif (persistence == 'PERSISTENCE_APP_COOKIE' and cookie):
- appsession ${cookie} len 56 timeout 3h
- % endif
- % for server_id in pool['members']:
-<% server = members[server_id] %>\
- % if delay and max_retries:
- server ${server_id} ${server['address']}:${server['port']} weight ${server['weight']} check inter ${delay}s fall ${max_retries}
- % else:
- server ${server_id} ${server['address']}:${server['port']} weight ${server['weight']}
- % endif
-% endfor
-% endfor
diff --git a/src/config/svc-monitor/svc_monitor/tests/fake_lb_driver.py b/src/config/svc-monitor/svc_monitor/tests/fake_lb_driver.py
index 7df266a184f..3c8066b78c2 100644
--- a/src/config/svc-monitor/svc_monitor/tests/fake_lb_driver.py
+++ b/src/config/svc-monitor/svc_monitor/tests/fake_lb_driver.py
@@ -88,3 +88,9 @@ def update_health_monitor(self, id, health_monitor):
def stats(self, pool_id):
pass
+
+ def set_config_v1(self, pool_id):
+ pass
+
+ def set_config_v2(self, lb_id):
+ pass
diff --git a/src/schema/loadbalancer.xsd b/src/schema/loadbalancer.xsd
index 2f9a9c81dd6..e25795370f7 100644
--- a/src/schema/loadbalancer.xsd
+++ b/src/schema/loadbalancer.xsd
@@ -79,6 +79,7 @@
+