From 3bc15512ae4fa4c21a02318df72a4a5fa7a7204e Mon Sep 17 00:00:00 2001 From: John Keates Date: Tue, 24 Feb 2015 22:19:02 +0100 Subject: [PATCH 1/8] Added jinja templated configuration support, a bit of documentation and a rough set of examples --- README.rst | 52 ++++++++++- haproxy/config.sls | 8 ++ .../files/haproxy-debian-package-default.cfg | 23 +++++ haproxy/files/haproxy-init-disable | 6 ++ haproxy/files/haproxy-init-enable | 6 ++ haproxy/init.sls | 23 ++--- haproxy/install.sls | 3 + haproxy/service.sls | 16 ++++ haproxy/templates/haproxy.jinja | 90 +++++++++++++++++++ pillar.example | 85 ++++++++++++++++++ 10 files changed, 296 insertions(+), 16 deletions(-) create mode 100644 haproxy/config.sls create mode 100644 haproxy/files/haproxy-debian-package-default.cfg create mode 100644 haproxy/files/haproxy-init-disable create mode 100644 haproxy/files/haproxy-init-enable create mode 100644 haproxy/install.sls create mode 100644 haproxy/service.sls create mode 100644 haproxy/templates/haproxy.jinja diff --git a/README.rst b/README.rst index c8338d1..8b6ccdc 100644 --- a/README.rst +++ b/README.rst @@ -4,4 +4,54 @@ haproxy haproxy ------- -Install and run haproxy +Install, configure and run haproxy based on: + +haproxy.install +haproxy.config +haproxy.service + +Use the supplied haproxy.cfg for a flat file approach, +or the jinja template and the pillar for a salt approach. + +haproxy.config +-------------- + +Currently, only a handful of options can be set using the pillar: + +- Global + - stats: enable stats, curently only via a unix socket which can be set to a path + - user: sets the user haproxy shall run as + - group: sets the group haproxy shall run as + - chroot: allows you to turn on chroot and set a directory + - daemon: allows you to turn daemon mode on and off + +- Default + - log: set the default log + - mode: sets the mode (i.e. http) + - retries: sets the number of retries + - options: an array of options that is simply looped with no special treatment + - timeouts: an array of timeouts that is simply looped with no special treatment + - errorfiles: an array of k:v errorfiles to point to the correct file matching an HTTP error code + +- Frontend + Frontend(s) is a list of the frontends you desire to have in your haproxy setup + Per frontend you can set: + - name: the name haproxy will use for the frontend + - bind: the bind string: this allows you to set the IP, Port and other paramters for the bind + - reqadd: an array of reqadd statements. Looped over and put in the configuration, no parsing + - default_backend: sets the default backend + - acls: a list of acls, not parsed, simply looped and put in to the configuration + - use_backends: a list of use_backend statements, looped over, not parsed + +- Backend + Backend(s) is a list of the backends you desire to have in your haproxy setup + Per backend you can set: + - name: set the backend name, used in the frontend references by haproxy + - balance: set the balance type, string + - redirect: if set, can be used to redirect; simply a string, not parsed + - servers: a list of servers this backend will contact, is looped over + - per server you can set: + - name: name of the server for haproxy + - host: the host to be contacted + - port: the port to contact the server on + - check: set to check to enable checking \ No newline at end of file diff --git a/haproxy/config.sls b/haproxy/config.sls new file mode 100644 index 0000000..63f51f2 --- /dev/null +++ b/haproxy/config.sls @@ -0,0 +1,8 @@ +haproxy.config: + file.managed: + - name: /etc/haproxy/haproxy.cfg + - source: salt://haproxy/templates/haproxy.jinja + - template: jinja + - user: root + - group: root + - mode: 644 diff --git a/haproxy/files/haproxy-debian-package-default.cfg b/haproxy/files/haproxy-debian-package-default.cfg new file mode 100644 index 0000000..e8feacd --- /dev/null +++ b/haproxy/files/haproxy-debian-package-default.cfg @@ -0,0 +1,23 @@ +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + user haproxy + group haproxy + daemon + +defaults + log global + mode http + option httplog + option dontlognull + contimeout 5000 + clitimeout 50000 + srvtimeout 50000 + errorfile 400 /etc/haproxy/errors/400.http + errorfile 403 /etc/haproxy/errors/403.http + errorfile 408 /etc/haproxy/errors/408.http + errorfile 500 /etc/haproxy/errors/500.http + errorfile 502 /etc/haproxy/errors/502.http + errorfile 503 /etc/haproxy/errors/503.http + errorfile 504 /etc/haproxy/errors/504.http \ No newline at end of file diff --git a/haproxy/files/haproxy-init-disable b/haproxy/files/haproxy-init-disable new file mode 100644 index 0000000..da2bf11 --- /dev/null +++ b/haproxy/files/haproxy-init-disable @@ -0,0 +1,6 @@ +# **** DO NOT EDIT THIS FILE **** +# +# This file is managed by Salt. +# Any changes will be overwritten. + +ENABLED=0 \ No newline at end of file diff --git a/haproxy/files/haproxy-init-enable b/haproxy/files/haproxy-init-enable new file mode 100644 index 0000000..e97f190 --- /dev/null +++ b/haproxy/files/haproxy-init-enable @@ -0,0 +1,6 @@ +# **** DO NOT EDIT THIS FILE **** +# +# This file is managed by Salt. +# Any changes will be overwritten. + +ENABLED=1 \ No newline at end of file diff --git a/haproxy/init.sls b/haproxy/init.sls index 19f0c0f..afdc9f8 100644 --- a/haproxy/init.sls +++ b/haproxy/init.sls @@ -1,15 +1,8 @@ -haproxy: - pkg.installed: [] - file.managed: - - name: /etc/haproxy/haproxy.cfg - - source: salt://haproxy/files/haproxy.cfg - - user: root - - group: root - - mode: 644 - - template: jinja - service.running: - - enable: True - - require: - - pkg: haproxy - - watch: - - file: haproxy +# haproxy +# +# Meta-state to fully setup haproxy on debian. (or any other distro that has haproxy in their repo) + +include: + - haproxy.install + - haproxy.service + - haproxy.config \ No newline at end of file diff --git a/haproxy/install.sls b/haproxy/install.sls new file mode 100644 index 0000000..7355f1b --- /dev/null +++ b/haproxy/install.sls @@ -0,0 +1,3 @@ +haproxy.install: + pkg.installed: + - name: haproxy \ No newline at end of file diff --git a/haproxy/service.sls b/haproxy/service.sls new file mode 100644 index 0000000..c1f3b62 --- /dev/null +++ b/haproxy/service.sls @@ -0,0 +1,16 @@ +haproxy.service: + service.running: + - name: haproxy + - enable: True + - require: + - pkg: haproxy + - watch: + - file: haproxy.config + file.managed: + - name: /etc/default/haproxy +#TODO: Add switch to turn the service on and off based on pillar configuration. + - source: salt://haproxy/files/haproxy-init-enable + - create: True + - user: "root" + - group: "root" + - mode: "0644" diff --git a/haproxy/templates/haproxy.jinja b/haproxy/templates/haproxy.jinja new file mode 100644 index 0000000..0c26ad5 --- /dev/null +++ b/haproxy/templates/haproxy.jinja @@ -0,0 +1,90 @@ +# HAProxy configuration +# +# **** DO NOT EDIT THIS FILE **** +# +# This file is managed by Salt. +# Any changes will be overwritten. + + +#--------------------------------------------------------------------- +# Global settings +#--------------------------------------------------------------------- +global + log /dev/log local0 + log /dev/log local1 notice + user {{ salt['pillar.get']('haproxy:global:user', 'haproxy') }} + group {{ salt['pillar.get']('haproxy:global:group', 'haproxy') }} +{%- if salt['pillar.get']('haproxy:global:chroot:enable', 'no') == True %} + chroot {{ salt['pillar.get']('haproxy:global:chroot:path', '/tmp') }} +{%- endif -%} +{% if salt['pillar.get']('haproxy:global:daemon', 'no') == True %} + daemon +{% endif %} +{%- if salt['pillar.get']('haproxy:global:stats:enable', 'no') == True %} + #Stats support is currently limited to socket mode + stats socket {{ salt['pillar.get']('haproxy:global:stats:socketpath', '/tmp/ha_stats.sock') }} + {% endif %} + +#--------------------------------------------------------------------- +# common defaults that all the 'listen' and 'backend' sections will +# use if not designated in their block +#--------------------------------------------------------------------- +defaults + log {{ salt['pillar.get']('haproxy:defaults:log') }} + mode {{ salt['pillar.get']('haproxy:defaults:mode') }} + retries {{ salt['pillar.get']('haproxy:defaults:retries') }} +{%- if 'options' in salt['pillar.get']('haproxy:defaults', {}) %} +{%- for option in salt['pillar.get']('haproxy:defaults:options') %} + option {{ option }}{% endfor %} +{% endif %} +{%- if 'timeouts' in salt['pillar.get']('haproxy:defaults', {}) %} +{%- for timeout in salt['pillar.get']('haproxy:defaults:timeouts') %} + timeout {{ timeout }}{% endfor %} +{% endif %} +{%- if 'errorfiles' in salt['pillar.get']('haproxy:defaults', {}) %} +{%- for errorfile in salt['pillar.get']('haproxy:defaults:errorfiles').iteritems() %} + errorfile {{ errorfile[0] }} {{ errorfile[1] }}{% endfor %} +{% endif %} + + + + +#--------------------------------------------------------------------- +# frontend instances +#--------------------------------------------------------------------- +{%- if 'frontends' in salt['pillar.get']('haproxy', {}) %} +{%- for frontend in salt['pillar.get']('haproxy:frontends', {}).iteritems() %} +frontend {{ frontend[1].name }} {{ frontend[1].bind }} +{%- if 'acls' in frontend[1] %} +{%- for acl in frontend[1].acls %} + acl {{ acl }} +{%- endfor %} +{%- endif %} +{%- if 'reqadd' in frontend[1] %} +{%- for reqadd in frontend[1].reqadd %} + reqadd {{ reqadd }} +{%- endfor %} +{%- endif %} + default_backend {{ frontend[1].default_backend }} +{%-if 'use_backends' in frontend[1] -%} +{%- for use_backend in frontend[1].use_backends %} + use_backend {{ use_backend }} +{% endfor %} +{%- endif %} +{% endfor %} +{%- endif %} + + +#--------------------------------------------------------------------- +# backend instances +#--------------------------------------------------------------------- +{%- if 'backends' in salt['pillar.get']('haproxy', {}) %} +{%- for backend in salt['pillar.get']('haproxy:backends', {}).iteritems() %} +backend {{ backend[1].name }} + balance {{ backend[1].balance }} + {%- if 'servers' in backend[1] %} + {%- for server in backend[1].servers.iteritems() %} + server {{ server[1].name }} {{ server[1].host }}:{{ server[1].port }} {{ server[1].check }}{% endfor %} + {% endif %} + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/pillar.example b/pillar.example index e69de29..78ed658 100644 --- a/pillar.example +++ b/pillar.example @@ -0,0 +1,85 @@ +# +# Example pillar configuration +# + +haproxy: + global: + stats: + enable: True + socketpath: /var/lib/haproxy/stats + + user: haproxy + group: haproxy + chroot: + enable: True + path: /var/lib/haproxy + + daemon: True + + defaults: + log: global + mode: http + retries: 3 + options: + - httplog + - dontlognull + - forwardfor + - http-server-close + timeouts: + - http-request 10s + - queue 1m + - connect 10s + - client 1m + - server 1m + - http-keep-alive 10s + - check 10s + + errorfiles: + 400: /etc/haproxy/errors/400.http + 403: /etc/haproxy/errors/403.http + 408: /etc/haproxy/errors/408.http + 500: /etc/haproxy/errors/500.http + 502: /etc/haproxy/errors/502.http + 503: /etc/haproxy/errors/503.http + 504: /etc/haproxy/errors/504.http + + frontends: + frontend1: + name: www-http + bind: "*:80" + reqadd: + - "X-Forwarded-Proto:\\ http" + default_backend: www-backend + + frontend2: + name: www-https + bind: "*:443 ssl crt /etc/ssl/private/certificate-chain-and-key-combined.pem" + reqadd: + - "X-Forwarded-Proto:\\ https" + default_backend: www-backend + acls: + - url_static path_beg -i /static /images /javascript /stylesheets + - url_static path_end -i .jpg .gif .png .css .js + use_backends: + - static if url_static + + backends: + backend1: + name: www-backend + balance: roundrobin + redirect: scheme https if !{ ssl_fc } + servers: + server1: + name: server1-its-name + host: 192.168.1.213 + check: check + backend2: + name: static + balance: roundrobin + redirect: scheme https if !{ ssl_fc } + servers: + server1: + name: some-server + host: 123.156.189.111 + port: 8080 + check: check \ No newline at end of file From 8701ac6765d9a4ee933315c8e30d9a03a0e91264 Mon Sep 17 00:00:00 2001 From: John Keates Date: Tue, 24 Feb 2015 22:23:43 +0100 Subject: [PATCH 2/8] Tiny readme fixup --- README.rst | 57 ------------------------------------------------------ 1 file changed, 57 deletions(-) delete mode 100644 README.rst diff --git a/README.rst b/README.rst deleted file mode 100644 index 8b6ccdc..0000000 --- a/README.rst +++ /dev/null @@ -1,57 +0,0 @@ -haproxy -======= - -haproxy -------- - -Install, configure and run haproxy based on: - -haproxy.install -haproxy.config -haproxy.service - -Use the supplied haproxy.cfg for a flat file approach, -or the jinja template and the pillar for a salt approach. - -haproxy.config --------------- - -Currently, only a handful of options can be set using the pillar: - -- Global - - stats: enable stats, curently only via a unix socket which can be set to a path - - user: sets the user haproxy shall run as - - group: sets the group haproxy shall run as - - chroot: allows you to turn on chroot and set a directory - - daemon: allows you to turn daemon mode on and off - -- Default - - log: set the default log - - mode: sets the mode (i.e. http) - - retries: sets the number of retries - - options: an array of options that is simply looped with no special treatment - - timeouts: an array of timeouts that is simply looped with no special treatment - - errorfiles: an array of k:v errorfiles to point to the correct file matching an HTTP error code - -- Frontend - Frontend(s) is a list of the frontends you desire to have in your haproxy setup - Per frontend you can set: - - name: the name haproxy will use for the frontend - - bind: the bind string: this allows you to set the IP, Port and other paramters for the bind - - reqadd: an array of reqadd statements. Looped over and put in the configuration, no parsing - - default_backend: sets the default backend - - acls: a list of acls, not parsed, simply looped and put in to the configuration - - use_backends: a list of use_backend statements, looped over, not parsed - -- Backend - Backend(s) is a list of the backends you desire to have in your haproxy setup - Per backend you can set: - - name: set the backend name, used in the frontend references by haproxy - - balance: set the balance type, string - - redirect: if set, can be used to redirect; simply a string, not parsed - - servers: a list of servers this backend will contact, is looped over - - per server you can set: - - name: name of the server for haproxy - - host: the host to be contacted - - port: the port to contact the server on - - check: set to check to enable checking \ No newline at end of file From d29bedeeb9698913340681d782866a9f8383ca81 Mon Sep 17 00:00:00 2001 From: John Keates Date: Tue, 24 Feb 2015 22:24:26 +0100 Subject: [PATCH 3/8] Tiny readme fixup fixup (yes, that's a fixup for a fixup) --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c379dc0 --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +haproxy +======= +--- + +haproxy +------- + +Install, configure and run haproxy based on: + +- haproxy.install +- haproxy.config +- haproxy.service + +Use the supplied haproxy.cfg for a flat file approach, +or the jinja template and the pillar for a salt approach. + +haproxy.config +-------------- + +Currently, only a handful of options can be set using the pillar: + +- Global + - stats: enable stats, curently only via a unix socket which can be set to a path + - user: sets the user haproxy shall run as + - group: sets the group haproxy shall run as + - chroot: allows you to turn on chroot and set a directory + - daemon: allows you to turn daemon mode on and off + +- Default + - log: set the default log + - mode: sets the mode (i.e. http) + - retries: sets the number of retries + - options: an array of options that is simply looped with no special treatment + - timeouts: an array of timeouts that is simply looped with no special treatment + - errorfiles: an array of k:v errorfiles to point to the correct file matching an HTTP error code + +- Frontend + Frontend(s) is a list of the frontends you desire to have in your haproxy setup + Per frontend you can set: + - name: the name haproxy will use for the frontend + - bind: the bind string: this allows you to set the IP, Port and other paramters for the bind + - reqadd: an array of reqadd statements. Looped over and put in the configuration, no parsing + - default_backend: sets the default backend + - acls: a list of acls, not parsed, simply looped and put in to the configuration + - use_backends: a list of use_backend statements, looped over, not parsed + +- Backend + Backend(s) is a list of the backends you desire to have in your haproxy setup + Per backend you can set: + - name: set the backend name, used in the frontend references by haproxy + - balance: set the balance type, string + - redirect: if set, can be used to redirect; simply a string, not parsed + - servers: a list of servers this backend will contact, is looped over + - per server you can set: + - name: name of the server for haproxy + - host: the host to be contacted + - port: the port to contact the server on + - check: set to check to enable checking \ No newline at end of file From 3bcb4fc930718b24ad96c4867c03dee2cccd67e4 Mon Sep 17 00:00:00 2001 From: John Keates Date: Wed, 25 Feb 2015 21:03:43 +0100 Subject: [PATCH 4/8] Change readme from MD to RST --- README.md | 58 ------------------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index c379dc0..0000000 --- a/README.md +++ /dev/null @@ -1,58 +0,0 @@ -haproxy -======= ---- - -haproxy -------- - -Install, configure and run haproxy based on: - -- haproxy.install -- haproxy.config -- haproxy.service - -Use the supplied haproxy.cfg for a flat file approach, -or the jinja template and the pillar for a salt approach. - -haproxy.config --------------- - -Currently, only a handful of options can be set using the pillar: - -- Global - - stats: enable stats, curently only via a unix socket which can be set to a path - - user: sets the user haproxy shall run as - - group: sets the group haproxy shall run as - - chroot: allows you to turn on chroot and set a directory - - daemon: allows you to turn daemon mode on and off - -- Default - - log: set the default log - - mode: sets the mode (i.e. http) - - retries: sets the number of retries - - options: an array of options that is simply looped with no special treatment - - timeouts: an array of timeouts that is simply looped with no special treatment - - errorfiles: an array of k:v errorfiles to point to the correct file matching an HTTP error code - -- Frontend - Frontend(s) is a list of the frontends you desire to have in your haproxy setup - Per frontend you can set: - - name: the name haproxy will use for the frontend - - bind: the bind string: this allows you to set the IP, Port and other paramters for the bind - - reqadd: an array of reqadd statements. Looped over and put in the configuration, no parsing - - default_backend: sets the default backend - - acls: a list of acls, not parsed, simply looped and put in to the configuration - - use_backends: a list of use_backend statements, looped over, not parsed - -- Backend - Backend(s) is a list of the backends you desire to have in your haproxy setup - Per backend you can set: - - name: set the backend name, used in the frontend references by haproxy - - balance: set the balance type, string - - redirect: if set, can be used to redirect; simply a string, not parsed - - servers: a list of servers this backend will contact, is looped over - - per server you can set: - - name: name of the server for haproxy - - host: the host to be contacted - - port: the port to contact the server on - - check: set to check to enable checking \ No newline at end of file From fafaa064f778eaa8d2d2ff9d137177c245794196 Mon Sep 17 00:00:00 2001 From: John Keates Date: Wed, 25 Feb 2015 21:04:17 +0100 Subject: [PATCH 5/8] Change readme from MD to RST --- README.rst | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..47d9b08 --- /dev/null +++ b/README.rst @@ -0,0 +1,59 @@ +haproxy +======= +--- + +haproxy +------- + +Install, configure and run haproxy based on: + +- haproxy.install +- haproxy.config +- haproxy.service + +Use the supplied haproxy.cfg for a flat file approach, +or the jinja template and the pillar for a salt approach. + +haproxy.config +-------------- + +Currently, only a handful of options can be set using the pillar: + +- Global + + + stats: enable stats, curently only via a unix socket which can be set to a path + + user: sets the user haproxy shall run as + + group: sets the group haproxy shall run as + + chroot: allows you to turn on chroot and set a directory + + daemon: allows you to turn daemon mode on and off + +- Default + + + log: set the default log + + mode: sets the mode (i.e. http) + + retries: sets the number of retries + + options: an array of options that is simply looped with no special treatment + + timeouts: an array of timeouts that is simply looped with no special treatment + + errorfiles: an array of k:v errorfiles to point to the correct file matching an HTTP error code + +- Frontend; Frontend(s) is a list of the frontends you desire to have in your haproxy setup + Per frontend you can set: + + + name: the name haproxy will use for the frontend + + bind: the bind string: this allows you to set the IP, Port and other paramters for the bind + + reqadd: an array of reqadd statements. Looped over and put in the configuration, no parsing + + default_backend: sets the default backend + + acls: a list of acls, not parsed, simply looped and put in to the configuration + + use_backends: a list of use_backend statements, looped over, not parsed + +- Backend; Backend(s) is a list of the backends you desire to have in your haproxy setup, per backend you can set: + + + name: set the backend name, used in the frontend references by haproxy + + balance: set the balance type, string + + redirect: if set, can be used to redirect; simply a string, not parsed + + servers: a list of servers this backend will contact, is looped over; per server you can set: + + + name: name of the server for haproxy + + host: the host to be contacted + + port: the port to contact the server on + + check: set to check to enable checking \ No newline at end of file From 2e242bf48733e92847473c0de75bc23b171c5340 Mon Sep 17 00:00:00 2001 From: John Keates Date: Wed, 25 Feb 2015 21:05:40 +0100 Subject: [PATCH 6/8] Update Readme.rst formatting --- README.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 47d9b08..fb74444 100644 --- a/README.rst +++ b/README.rst @@ -1,9 +1,7 @@ haproxy ======= ---- -haproxy -------- +**haproxy:** Install, configure and run haproxy based on: @@ -53,7 +51,7 @@ Currently, only a handful of options can be set using the pillar: + redirect: if set, can be used to redirect; simply a string, not parsed + servers: a list of servers this backend will contact, is looped over; per server you can set: - + name: name of the server for haproxy - + host: the host to be contacted - + port: the port to contact the server on - + check: set to check to enable checking \ No newline at end of file + + name: name of the server for haproxy + + host: the host to be contacted + + port: the port to contact the server on + + check: set to check to enable checking \ No newline at end of file From 5dd2cc2043a2cbba0d69e51ac05f3c02c5833188 Mon Sep 17 00:00:00 2001 From: John Keates Date: Thu, 26 Feb 2015 13:29:11 +0100 Subject: [PATCH 7/8] Changed from emphasis to head --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index fb74444..cc2eb48 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,8 @@ haproxy ======= -**haproxy:** +haproxy +------- Install, configure and run haproxy based on: From ec17df3eea662b9655cba5ca3871dd2a8b441779 Mon Sep 17 00:00:00 2001 From: John Keates Date: Thu, 26 Feb 2015 14:34:47 +0100 Subject: [PATCH 8/8] Change bind method so it doesn't always default --- haproxy/templates/haproxy.jinja | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/haproxy/templates/haproxy.jinja b/haproxy/templates/haproxy.jinja index 0c26ad5..c70203c 100644 --- a/haproxy/templates/haproxy.jinja +++ b/haproxy/templates/haproxy.jinja @@ -54,7 +54,8 @@ defaults #--------------------------------------------------------------------- {%- if 'frontends' in salt['pillar.get']('haproxy', {}) %} {%- for frontend in salt['pillar.get']('haproxy:frontends', {}).iteritems() %} -frontend {{ frontend[1].name }} {{ frontend[1].bind }} +frontend {{ frontend[1].name }} + bind {{ frontend[1].bind }} {%- if 'acls' in frontend[1] %} {%- for acl in frontend[1].acls %} acl {{ acl }}