{# Workaround empty parameter passing #}
{# In case of resilient '' is converted to 'None' string, but with slapproxy '' becomes None #}
{% for k, v in slapparameter_dict.items() %}
{%   if v == 'None' or v is none %}
{%     do slapparameter_dict.__setitem__(k, '') %}
{%   endif %}
{% endfor %}
{% set additional_frontend = slapparameter_dict.get('frontend-additional-instance-guid') %}
{% set enable_http = str(slapparameter_dict.get('enable-http-server', False)).lower() == 'true' -%}
{% set use_tap = str(slapparameter_dict.get('use-tap', True)).lower() == 'true' -%}
{% set use_nat = str(slapparameter_dict.get('use-nat', True)).lower() == 'true' -%}
{% set wipe_disk = str(slapparameter_dict.get('wipe-disk-ondestroy', False)).lower() == 'true' -%}
{% set nat_restrict = str(slapparameter_dict.get('nat-restrict-mode', False)).lower() == 'true' -%}
{% set name = slapparameter_dict.get('name', 'localhost') -%}
{% set disable_ansible_promise = str(slapparameter_dict.get('disable-ansible-promise', True)).lower() == 'true' -%}
{% set instance_type = slapparameter_dict.get('type', 'standalone') -%}
{% set nat_rule_list = slapparameter_dict.get('nat-rules', '22 80 443') -%}
{% set disk_device_path = slapparameter_dict.get('disk-device-path', None) -%}
{% set whitelist_domains = slapparameter_dict.get('whitelist-domains', '') -%}
{% set virtual_hard_drive_url_enabled = 'virtual-hard-drive-url' in slapparameter_dict %}
{% set virtual_hard_drive_url_gzipped = str(slapparameter_dict.get('virtual-hard-drive-gzipped', False)).lower() == 'true' %}
{% set boot_image_url_list_enabled = 'boot-image-url-list' in slapparameter_dict %}
{% set boot_image_url_select_enabled = 'boot-image-url-select' in slapparameter_dict %}
{% set bootstrap_script_url = slapparameter_dict.get('bootstrap-script-url') -%}
{% set cpu_max_count = dumps(slapparameter_dict.get('cpu-max-count', int(slapparameter_dict.get('cpu-count', 2)) + 1)) %}
{% set ram_max_size = dumps(slapparameter_dict.get('ram-max-size', int(slapparameter_dict.get('ram-size', 4096)) + 512)) %}
{% set extends_list = [] -%}
{% set part_list = [] -%}

{% if instance_type == 'cluster' -%}
{% set nat_rule_list = slapparameter_dict.get('nat-rules', '') %}
{% endif -%}
{% if not nat_rule_list or not nat_rule_list.strip() -%}
{%   set nat_rule_list = '' %}
{% endif -%}

{% do extends_list.append(template_monitor) -%}
{% do extends_list.append(logrotate_cfg) -%}

[slap-network-information]
local-ipv4 = {{ slap_configuration['ipv4-random'] }}
global-ipv6 = {{ slap_configuration['ipv6-random'] }}

[directory]
recipe = slapos.cookbook:mkdirectory
etc = ${buildout:directory}/etc
bin = ${buildout:directory}/bin
srv = ${buildout:directory}/srv
var = ${buildout:directory}/var
log = ${:var}/log
scripts = ${:etc}/run
services = ${:etc}/service
novnc-conf = ${:etc}/novnc
run = ${:var}/run
prerm = ${:etc}/prerm
ca-dir = ${:srv}/ssl
public = ${:srv}/public/
cron-entries = ${:etc}/cron.d
crontabs = ${:etc}/crontabs
cronstamps = ${:etc}/cronstamps
{%- if virtual_hard_drive_url_enabled %}
virtual-hard-drive-url-repository = ${:srv}/virtual-hard-drive-url-repository
virtual-hard-drive-url-var = ${:var}/virtual-hard-drive-url
virtual-hard-drive-url-expose = ${monitor-directory:private}/virtual-hard-drive-url
{%- endif %}
{%- if boot_image_url_list_enabled %}
boot-image-url-list-repository = ${:srv}/boot-image-url-list-repository
boot-image-url-list-var = ${:var}/boot-image-url-list
boot-image-url-list-expose = ${monitor-directory:private}/boot-image-url-list
{%- endif %}
{%- if boot_image_url_select_enabled %}
boot-image-url-select-repository = ${:srv}/boot-image-url-select-repository
boot-image-url-select-var = ${:var}/boot-image-url-select
boot-image-url-select-expose = ${monitor-directory:private}/boot-image-url-select
{%- endif %}

[create-mac]
recipe = slapos.cookbook:generate.mac
storage-path = ${directory:srv}/mac

[create-tap-mac]
recipe = slapos.cookbook:generate.mac
storage-path = ${directory:srv}/tap_mac

[gen-passwd]
recipe = slapos.cookbook:generate.password
storage-path = ${directory:srv}/.passwd
# VNC protocol supports passwords of 8 characters max
bytes = 8

{% if boot_image_url_select_enabled %}
## boot-image-url-select support BEGIN
[empty-file-state-base-select-promise]
<= monitor-promise-base
promise = check_file_state
name = ${:_buildout_section_name_}.py
config-state = empty
config-url = ${monitor-base:base-url}/private/boot-image-url-select/${:filename}

[boot-image-url-select-source-config]
recipe = slapos.recipe.template:jinja2
template = inline:
{%- raw %}
  {{ boot_image_url_select }}
{% endraw -%}
boot-image-url-select = {{ dumps(slapparameter_dict['boot-image-url-select']) }}
context =
  key boot_image_url_select :boot-image-url-select
rendered = ${directory:etc}/boot-image-url-select.json

[boot-image-url-select-processed-config]
# compares if the current configuration has been used by
# the boot-image-url-select-download, if not, exposes it as not empty file with
# information
recipe = slapos.recipe.build
install =
  import os
  import hashlib
  if not os.path.exists(location):
    os.mkdir(location)
  with open('${:state-file}', 'w') as state_handler:
    try:
      with open('${:config-file}', 'rb') as config_handler, open('${:processed-md5sum}') as processed_handler:
        config_md5sum = hashlib.md5(config_handler.read()).hexdigest()
        processed_md5sum = processed_handler.read()
        if config_md5sum == processed_md5sum:
          state_handler.write('')
        else:
          state_handler.write('config %s != processed %s' % (config_md5sum, processed_md5sum))
    except Exception as e:
      state_handler.write(str(e))

update = ${:install}
config-file = ${boot-image-url-select-source-config:rendered}
state-filename = boot-image-url-select-processed-config.state
state-file = ${directory:boot-image-url-select-expose}/${:state-filename}
processed-md5sum = ${directory:boot-image-url-select-var}/update-image-processed.md5sum

[boot-image-url-select-processed-config-promise]
# promise to check if the configuration provided by the user has been already
# processed by the boot-image-url-select-download script, which runs asynchronously
<= empty-file-state-base-select-promise
filename = ${boot-image-url-select-processed-config:state-filename}
config-filename = ${boot-image-url-select-processed-config:state-file}

[boot-image-url-select-json-config]
# generates json configuration from user configuration
recipe = plone.recipe.command
command = {{ python_executable }} {{ image_download_config_creator }} ${boot-image-url-select-source-config:rendered} ${:rendered} ${directory:boot-image-url-select-repository} ${:error-state-file}
update-command = ${:command}
rendered = ${directory:boot-image-url-select-var}/boot-image-url-select.json
error-state-filename = boot-image-url-select-json-config-error.txt
error-state-file = ${directory:boot-image-url-select-expose}/${:error-state-filename}

[boot-image-url-select-config-state-promise]
# promise to check if configuration has been parsed without errors
<= empty-file-state-base-select-promise
filename = ${boot-image-url-select-json-config:error-state-filename}
config-filename = ${boot-image-url-select-json-config:error-state-file}

[boot-image-url-select-download-wrapper]
# wrapper to execute boot-image-url-select-download on each run
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:scripts}/boot-image-url-select-updater
command-line = {{ python_executable }} {{ image_download_controller }} ${boot-image-url-select-json-config:rendered} {{ curl_executable_location }} ${:md5sum-state-file} ${:error-state-file} ${boot-image-url-select-processed-config:processed-md5sum}
md5sum-state-filename = boot-image-url-select-download-controller-md5sum-fail.json
md5sum-state-file = ${directory:boot-image-url-select-expose}/${:md5sum-state-filename}
error-state-filename = boot-image-url-select-download-controller-error.text
error-state-file = ${directory:boot-image-url-select-expose}/${:error-state-filename}
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[boot-image-url-select-download-md5sum-promise]
# promise to report errors with problems with calculating md5sum of the
# downloaded images
<= empty-file-state-base-select-promise
filename = ${boot-image-url-select-download-wrapper:md5sum-state-filename}
config-filename = ${boot-image-url-select-download-wrapper:md5sum-state-file}

[boot-image-url-select-download-state-promise]
# promise to report errors during download
<= empty-file-state-base-select-promise
filename = ${boot-image-url-select-download-wrapper:error-state-filename}
config-filename = ${boot-image-url-select-download-wrapper:error-state-file}
## boot-image-url-select support END
{% endif %} {# if boot_image_url_select_enabled #}

{% if boot_image_url_list_enabled %}
## boot-image-url-list support BEGIN
[empty-file-state-base-list-promise]
<= monitor-promise-base
promise = check_file_state
name = ${:_buildout_section_name_}.py
config-state = empty
config-url = ${monitor-base:base-url}/private/boot-image-url-list/${:filename}

[boot-image-url-list-source-config]
recipe = slapos.recipe.template:jinja2
template = inline:
{%- raw %}
  {{ boot_image_url_list }}
{% endraw -%}
boot-image-url-list = {{ dumps(slapparameter_dict['boot-image-url-list']) }}
context =
  key boot_image_url_list :boot-image-url-list
rendered = ${directory:etc}/boot-image-url-list.conf

[boot-image-url-list-processed-config]
# compares if the current configuration has been used by
# the boot-image-url-list-download, if not, exposes it as not empty file with
# information
recipe = slapos.recipe.build
install =
  import os
  import hashlib
  if not os.path.exists(location):
    os.mkdir(location)
  with open('${:state-file}', 'w') as state_handler:
    try:
      with open('${:config-file}', 'rb') as config_handler, open('${:processed-md5sum}') as processed_handler:
        config_md5sum = hashlib.md5(config_handler.read()).hexdigest()
        processed_md5sum = processed_handler.read()
        if config_md5sum == processed_md5sum:
          state_handler.write('')
        else:
          state_handler.write('config %s != processed %s' % (config_md5sum, processed_md5sum))
    except Exception as e:
      state_handler.write(str(e))

update = ${:install}
config-file = ${boot-image-url-list-source-config:rendered}
state-filename = boot-image-url-list-processed-config.state
state-file = ${directory:boot-image-url-list-expose}/${:state-filename}
processed-md5sum = ${directory:boot-image-url-list-var}/update-image-processed.md5sum

[boot-image-url-list-processed-config-promise]
# promise to check if the configuration provided by the user has been already
# processed by the boot-image-url-list-download script, which runs asynchronously
<= empty-file-state-base-list-promise
filename = ${boot-image-url-list-processed-config:state-filename}
config-filename = ${boot-image-url-list-processed-config:state-file}

[boot-image-url-list-json-config]
# generates json configuration from user configuration
recipe = plone.recipe.command
command = {{ python_executable }} {{ image_download_config_creator }} ${boot-image-url-list-source-config:rendered} ${:rendered} ${directory:boot-image-url-list-repository} ${:error-state-file}
update-command = ${:command}
rendered = ${directory:boot-image-url-list-var}/boot-image-url-list.json
error-state-filename = boot-image-url-list-json-config-error.txt
error-state-file = ${directory:boot-image-url-list-expose}/${:error-state-filename}

[boot-image-url-list-config-state-promise]
# promise to check if configuration has been parsed without errors
<= empty-file-state-base-list-promise
filename = ${boot-image-url-list-json-config:error-state-filename}
config-filename = ${boot-image-url-list-json-config:error-state-file}

[boot-image-url-list-download-wrapper]
# wrapper to execute boot-image-url-list-download on each run
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:scripts}/boot-image-url-list-updater
command-line = {{ python_executable }} {{ image_download_controller }} ${boot-image-url-list-json-config:rendered} {{ curl_executable_location }} ${:md5sum-state-file} ${:error-state-file} ${boot-image-url-list-processed-config:processed-md5sum}
md5sum-state-filename = boot-image-url-list-download-controller-md5sum-fail.json
md5sum-state-file = ${directory:boot-image-url-list-expose}/${:md5sum-state-filename}
error-state-filename = boot-image-url-list-download-controller-error.text
error-state-file = ${directory:boot-image-url-list-expose}/${:error-state-filename}
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[boot-image-url-list-download-md5sum-promise]
# promise to report errors with problems with calculating md5sum of the
# downloaded images
<= empty-file-state-base-list-promise
filename = ${boot-image-url-list-download-wrapper:md5sum-state-filename}
config-filename = ${boot-image-url-list-download-wrapper:md5sum-state-file}

[boot-image-url-list-download-state-promise]
# promise to report errors during download
<= empty-file-state-base-list-promise
filename = ${boot-image-url-list-download-wrapper:error-state-filename}
config-filename = ${boot-image-url-list-download-wrapper:error-state-file}
## boot-image-url-list support END
{% endif %} {# if boot_image_url_list_enabled #}

{% if virtual_hard_drive_url_enabled %}
## virtual-hard-drive-url support BEGIN
[empty-file-state-base-virtual-promise]
<= monitor-promise-base
promise = check_file_state
name = ${:_buildout_section_name_}.py
config-state = empty
config-url = ${monitor-base:base-url}/private/virtual-hard-drive-url/${:filename}

[virtual-hard-drive-url-source-config]
recipe = slapos.recipe.template:jinja2
template = inline:
{%- raw %}
  {{ virtual_hard_drive_url }}
{% endraw -%}
{# Enforce md5sum on virtual-hard-drive-url #}
virtual-hard-drive-url = {{ slapparameter_dict['virtual-hard-drive-url'] }}#{{ slapparameter_dict['virtual-hard-drive-md5sum'] }}
context =
  key virtual_hard_drive_url :virtual-hard-drive-url
rendered = ${directory:etc}/virtual-hard-drive-url.conf

[virtual-hard-drive-url-processed-config]
# compares if the current configuration has been used by
# the virtual-hard-drive-url-download, if not, exposes it as not empty file with
# information
recipe = slapos.recipe.build
install =
  import os
  import hashlib
  if not os.path.exists(location):
    os.mkdir(location)
  with open('${:state-file}', 'w') as state_handler:
    try:
      with open('${:config-file}', 'rb') as config_handler, open('${:processed-md5sum}') as processed_handler:
        config_md5sum = hashlib.md5(config_handler.read()).hexdigest()
        processed_md5sum = processed_handler.read()
        if config_md5sum == processed_md5sum:
          state_handler.write('')
        else:
          state_handler.write('config %s != processed %s' % (config_md5sum, processed_md5sum))
    except Exception as e:
      state_handler.write(str(e))

update = ${:install}
config-file = ${virtual-hard-drive-url-source-config:rendered}
state-filename = virtual-hard-drive-url-processed-config.state
state-file = ${directory:virtual-hard-drive-url-expose}/${:state-filename}
processed-md5sum = ${directory:virtual-hard-drive-url-var}/update-image-processed.md5sum

[virtual-hard-drive-url-processed-config-promise]
# promise to check if the configuration provided by the user has been already
# processed by the virtual-hard-drive-url-download script, which runs asynchronously
<= empty-file-state-base-virtual-promise
filename = ${virtual-hard-drive-url-processed-config:state-filename}
config-filename = ${virtual-hard-drive-url-processed-config:state-file}

[virtual-hard-drive-url-json-config]
# generates json configuration from user configuration
recipe = plone.recipe.command
command = {{ python_executable }} {{ image_download_config_creator }} ${virtual-hard-drive-url-source-config:rendered} ${:rendered} ${directory:virtual-hard-drive-url-repository} ${:error-state-file}
update-command = ${:command}
rendered = ${directory:virtual-hard-drive-url-var}/virtual-hard-drive-url.json
error-state-filename = virtual-hard-drive-url-json-config-error.txt
error-state-file = ${directory:virtual-hard-drive-url-expose}/${:error-state-filename}

[virtual-hard-drive-url-config-state-promise]
# promise to check if configuration has been parsed without errors
<= empty-file-state-base-virtual-promise
filename = ${virtual-hard-drive-url-json-config:error-state-filename}
config-filename = ${virtual-hard-drive-url-json-config:error-state-file}

[virtual-hard-drive-url-download-wrapper]
# wrapper to execute virtual-hard-drive-url-download on each run
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:scripts}/virtual-hard-drive-url-updater
command-line = {{ python_executable }} {{ image_download_controller }} ${virtual-hard-drive-url-json-config:rendered} {{ curl_executable_location }} ${:md5sum-state-file} ${:error-state-file} ${virtual-hard-drive-url-processed-config:processed-md5sum}
md5sum-state-filename = virtual-hard-drive-url-download-controller-md5sum-fail.json
md5sum-state-file = ${directory:virtual-hard-drive-url-expose}/${:md5sum-state-filename}
error-state-filename = virtual-hard-drive-url-download-controller-error.text
error-state-file = ${directory:virtual-hard-drive-url-expose}/${:error-state-filename}
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[virtual-hard-drive-url-download-md5sum-promise]
# promise to report errors with problems with calculating md5sum of the
# downloaded images
<= empty-file-state-base-virtual-promise
filename = ${virtual-hard-drive-url-download-wrapper:md5sum-state-filename}
config-filename = ${virtual-hard-drive-url-download-wrapper:md5sum-state-file}

[virtual-hard-drive-url-download-state-promise]
# promise to report errors during download
<= empty-file-state-base-virtual-promise
filename = ${virtual-hard-drive-url-download-wrapper:error-state-filename}
config-filename = ${virtual-hard-drive-url-download-wrapper:error-state-file}
## virtual-hard-drive-url support END
{% endif %} {# if virtual_hard_drive_url_enabled #}

[kvm-controller-parameter-dict]
python-path = {{ python_eggs_executable }}
vnc-passwd = ${gen-passwd:passwd}
socket-path = ${directory:var}/qmp_socket
pid-file = ${directory:run}/pid_file
kvm-status-path = ${directory:var}/qemu-vm-is-ready
cpu-count = ${slap-parameter:cpu-count}
cpu-model = ${slap-parameter:cpu-model}
ram-hotplug-slot-size = ${slap-parameter:ram-hotplug-slot-size}
ram-size = ${slap-parameter:ram-size}
enable-device-hotplug = ${slap-parameter:enable-device-hotplug}

[kvm-parameter-dict]
python-path = {{ python_executable }}
ipv4 = ${slap-network-information:local-ipv4}
ipv6 = ${slap-network-information:global-ipv6}
vnc-ip = ${:ipv4}
vnc-port = 5901

default-cdrom-iso = {{ debian_amd64_netinst_location }}
{% if virtual_hard_drive_url_enabled %}
virtual-hard-drive-url-json-config = ${virtual-hard-drive-url-json-config:rendered}
{% else %}
virtual-hard-drive-url-json-config =
{% endif %}
{% if boot_image_url_list_enabled %}
boot-image-url-list-json-config = ${boot-image-url-list-json-config:rendered}
{% else %}
boot-image-url-list-json-config =
{% endif %}
{% if boot_image_url_select_enabled %}
boot-image-url-select-json-config = ${boot-image-url-select-json-config:rendered}
{% else %}
boot-image-url-select-json-config =
{% endif %}
nbd-host = ${slap-parameter:nbd-host}
nbd-port = ${slap-parameter:nbd-port}
nbd2-host = ${slap-parameter:nbd2-host}
nbd2-port = ${slap-parameter:nbd2-port}

tap-interface = {{ slap_configuration.get('tap-name', '') }}
tap-ipv6-addr = {{ slap_configuration.get('tap-ipv6-addr', '') }}

disk-size = ${slap-parameter:disk-size}
disk-type = ${slap-parameter:disk-type}
disk-format = ${slap-parameter:disk-format}
disk-device-path = ${slap-parameter:disk-device-path}
disk-path = ${directory:srv}/virtual.${slap-parameter:disk-format}

network-adapter = ${slap-parameter:network-adapter}

pid-file-path = ${kvm-controller-parameter-dict:pid-file}
socket-path = ${kvm-controller-parameter-dict:socket-path}

{%- set enable_device_hotplug = slapparameter_dict.get('enable-device-hotplug', 'false').lower() == 'true' %}
smp-max-count = {{ cpu_max_count }}
ram-max-size = {{ ram_max_size }}
{%- if enable_device_hotplug %}
init-ram-size = 4096
init-smp-count = 2
{%- else %}
init-ram-size = ${kvm-controller-parameter-dict:ram-size}
init-smp-count = ${kvm-controller-parameter-dict:cpu-count}
{%- endif %}

mac-address = ${create-mac:mac-address}
tap-mac-address = ${create-tap-mac:mac-address}

use-tap = ${slap-parameter:use-tap}
use-nat = ${slap-parameter:use-nat}
nat-rules = {{ nat_rule_list }}
nat-restrict= {{ dumps(nat_restrict) }}
enable-vhost = ${slap-parameter:enable-vhost}

virtual-hard-drive-url = ${slap-parameter:virtual-hard-drive-url}
virtual-hard-drive-md5sum = ${slap-parameter:virtual-hard-drive-md5sum}
virtual-hard-drive-gzipped = ${slap-parameter:virtual-hard-drive-gzipped}
hard-drive-url-check-certificate = ${slap-parameter:hard-drive-url-check-certificate}

shell-path = {{ dash_executable_location }}
qemu-path =  {{ qemu_executable_location }}
qemu-img-path = {{ qemu_img_executable_location }}

etc-directory = ${directory:etc}
disk-storage-list =
{% for key, path in storage_dict.items() -%}
{{ '  ' ~ key ~ ' ' ~ path }}
{% endfor -%}
external-disk-number = ${slap-parameter:external-disk-number}
external-disk-size = ${slap-parameter:external-disk-size}
external-disk-format = ${slap-parameter:external-disk-format}

{% if enable_http -%}
httpd-port = ${slap-parameter:httpd-port}
{% else -%}
httpd-port = 0
{% endif -%}

# Main instance document server info
{% if slapparameter_dict.get('document-host', '') and slapparameter_dict.get('document-port', '') -%}
cluster-doc-host = ${tunnel-cluster-url:ipv4}
cluster-doc-port = ${tunnel-cluster-url:ipv4-port}
{% else -%}
cluster-doc-host =
cluster-doc-port = 0
{% endif -%}
netcat-binary = {{ netcat_bin }}

name = {{ slapparameter_dict.get('name', 'Single KVM') }}
disk-cache = ${slap-parameter:disk-cache}
disk-aio = ${slap-parameter:disk-aio}
auto-ballooning = ${slap-parameter:auto-ballooning}
machine-options = ${slap-parameter:machine-options}
cpu-model = ${slap-parameter:cpu-model}

log-file = ${directory:log}/qemu.log

[kvm-run]
recipe = slapos.recipe.template:jinja2
template = {{ template_kvm_run }}
rendered = ${directory:bin}/kvm_raw
mode = 700
context =
  section parameter_dict kvm-parameter-dict

[kvm-controller]
recipe = slapos.recipe.template:jinja2
template = {{ template_kvm_controller_run }}
rendered = ${directory:bin}/kvm_controller_raw
mode = 700
context =
  section parameter_dict kvm-controller-parameter-dict

[tunnel-6to4-base]
recipe = slapos.cookbook:wrapper
ipv4 = ${slap-network-information:local-ipv4}
ipv6 = ${slap-network-information:global-ipv6}
wrapper-path = ${directory:services}/6tunnel-${:ipv6-port}
command-line = {{ sixtunnel_executable_location }} -6 -4 -d -l ${:ipv6} ${:ipv6-port} ${:ipv4} ${:ipv4-port}
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

{% if use_nat and nat_rule_list -%}
{%   for port in nat_rule_list.split(' ') -%}
{%     if ':' in port -%}
{%       set proto, port = port.split(':') -%}
{%     else -%}
{%       set proto, port = 'tcp', port -%}
{%     endif -%}
{%     set external_port = 10000 + port|int() -%}
{%     set section_name = '6tunnel-' ~ proto ~ '-' ~ external_port -%}
[{{ section_name }}]
<= tunnel-6to4-base
ipv4-port = {{ external_port }}
ipv6-port = {{ external_port }}
{%     do part_list.append(section_name) -%}
{%   endfor -%}
{% endif -%}

{%- set depend_section_list = [] %}
{%- set hash_file_list = ['${kvm-run:rendered}'] %}
{%- macro generate_depend_section(section, key) %}
{%-  do depend_section_list.append('${' + section + ':command}' ) %}
{%-  do hash_file_list.append('${' + key + '}') %}
[{{ section }}]
recipe = plone.recipe.command
update-command = ${:command}
command = [ ! -f {{ '${' + key + '}' }} ] && touch {{ '${' +  key + '}' }}
{%- endmacro %}
{#- Create depending sections, as state files appear late, so it's better to have empty file which will impact the hash anyway #}
{%- if boot_image_url_list_enabled %}
{{    generate_depend_section('boot-image-url-list-depend', 'boot-image-url-list-download-wrapper:md5sum-state-file') }}
{%- endif %}
{%- if boot_image_url_select_enabled %}
{{    generate_depend_section('boot-image-url-select-depend', 'boot-image-url-select-download-wrapper:md5sum-state-file') }}
{%- endif %}
{%- if virtual_hard_drive_url_enabled %}
{{    generate_depend_section('virtual-hard-drive-url-depend', 'virtual-hard-drive-url-download-wrapper:md5sum-state-file') }}
{%- endif %}

[kvm-instance]
depends =
{%- for depend_section in depend_section_list %}
  {{ depend_section }}
{%- endfor %}
recipe = slapos.cookbook:wrapper
socket-path = ${kvm-controller-parameter-dict:socket-path}
wrapper-path = ${directory:services}/kvm
command-line = ${kvm-run:rendered}
kvm-controller = ${kvm-controller-wrapper:wrapper-path}
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
hash-files =
{%- for hash_file in hash_file_list %}
  {{ hash_file }}
{%- endfor %}

[kvm-controller-wrapper]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:scripts}/kvm_controller
command-line = ${kvm-controller:rendered}


[kvm-vnc-promise]
<= monitor-promise-base
promise = check_socket_listening
name = vnc_promise.py
config-host = ${kvm-parameter-dict:vnc-ip}
config-port = ${kvm-parameter-dict:vnc-port}

[kvm-disk-image-corruption-bin]
recipe = collective.recipe.template
input = inline:#!/bin/sh
  # Return code 0 is "OK"
  # Return code 3 is "found leaks, but image is OK"
  # http://git.qemu.org/?p=qemu.git;a=blob;f=qemu-img.c;h=4e9a7f5741c9cb863d978225829e68fefcae3947;hb=HEAD#l702
  if [ "${slap-parameter:disk-device-path}" != "" ]; then
    # disk device option is used, skip qemu img check
    exit 0
  fi
  ${kvm-parameter-dict:qemu-img-path} check -U ${kvm-parameter-dict:disk-path} > /dev/null 2>&1
  RETURN_CODE=$?
  if [ $RETURN_CODE -eq 0 ] || [ $RETURN_CODE -eq 3 ]; then
    exit 0
  else
    exit 1
  fi
output = ${directory:bin}/kvm-disk-image-corruption
mode = 700

[kvm-disk-image-corruption-promise]
# Check that disk image is not corrupted
<= monitor-promise-base
promise = check_command_execute
name = kvm-disk-image-corruption.py
config-command = ${kvm-disk-image-corruption-bin:output}

{% if wipe_disk -%}
{% do part_list.append('wipe-disk-wrapper') -%}
{% set wipe_file_list = '${kvm-parameter-dict:disk-path}' -%}
{% if storage_dict -%}
{% set wipe_file_list = '${kvm-parameter-dict:disk-path}' ~ ' ' ~ '/* '.join(storage_dict.values()) ~ '/*' -%}
{% endif -%}
[wipe-disk-wrapper]
recipe = slapos.cookbook:wrapper
wrapper-path = ${directory:prerm}/slapos_wipe_qemu_disk
command-line =
  {{ wipe_disk_wrapper }} -n {{ slapparameter_dict.get('wipe-disk-iterations', 1) }} -suz --check-pid-file ${kvm-parameter-dict:pid-file-path} --file {{ wipe_file_list }}
{% endif -%}

[kvm-started-bin]
recipe = slapos.recipe.template:jinja2
template = {{ qemu_start_promise_tpl }}
rendered = ${directory:bin}/qemu-virtual-machine-is-ready
mode = 700
context =
  raw dash {{ dash_executable_location }}
  raw qemu_ready_path ${kvm-controller-parameter-dict:kvm-status-path}
  raw qemu_service_log_file ${buildout:directory}/.${slap-connection:partition-id}_kvm.log

[kvm-started-promise]
<= monitor-promise-base
promise = check_command_execute
name = qemu-virtual-machine-is-ready.py
config-command = ${kvm-started-bin:rendered}

[novnc-instance]
recipe = slapos.cookbook:novnc
path = ${ca-novnc:executable}
ip = ${slap-network-information:global-ipv6}
port = 6080
vnc-ip = ${kvm-parameter-dict:vnc-ip}
vnc-port = ${kvm-parameter-dict:vnc-port}
novnc-location = {{ novnc_location }}
websockify-path = {{ websockify_executable_location }}
ssl-key-path = ${ca-novnc:key-file}
ssl-cert-path = ${ca-novnc:cert-file}

[websockify-sighandler]
recipe = slapos.cookbook:signalwrapper
wrapper-path = ${directory:bin}/websockify-sighandler
wrapped-path = ${novnc-instance:path}

[websockify-sighandler-service]
recipe = slapos.cookbook:wrapper
command-line = ${websockify-sighandler:wrapper-path}
wrapper-path = ${directory:services}/websockify
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
wait-for-files =
  ${ca-novnc:key-file}
  ${ca-novnc:cert-file}

[certificate-authority]
recipe = slapos.cookbook:certificate_authority
openssl-binary = {{ openssl_executable_location }}
ca-dir = ${directory:ca-dir}
requests-directory = ${cadirectory:requests}
wrapper = ${directory:bin}/certificate_authority
ca-private = ${cadirectory:private}
ca-certs = ${cadirectory:certs}
ca-newcerts = ${cadirectory:newcerts}
ca-crl = ${cadirectory:crl}

[certificate-authority-service]
recipe = slapos.cookbook:wrapper
command-line = ${certificate-authority:wrapper}
wrapper-path = ${directory:services}/certificate_authority
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[cadirectory]
recipe = slapos.cookbook:mkdirectory
requests = ${directory:ca-dir}/requests/
private = ${directory:ca-dir}/private/
certs = ${directory:ca-dir}/certs/
newcerts = ${directory:ca-dir}/newcerts/
crl = ${directory:ca-dir}/crl/

[ca-novnc]
<= certificate-authority
recipe = slapos.cookbook:certificate_authority.request
key-file = ${directory:novnc-conf}/novnc.key
cert-file = ${directory:novnc-conf}/novnc.crt
executable = ${directory:bin}/novnc
wrapper = ${directory:bin}/websockify

[novnc-promise]
<= monitor-promise-base
promise = check_socket_listening
name = novnc_promise.py
config-host = ${novnc-instance:ip}
config-port = ${novnc-instance:port}


#----------------
#--
#-- Deploy cron.

[cron]
recipe = slapos.cookbook:cron
dcrond-binary = {{ dcron_executable_location }}
cron-entries = ${directory:cron-entries}
crontabs = ${directory:crontabs}
cronstamps = ${directory:cronstamps}
catcher = ${cron-simplelogger:wrapper}
binary = ${directory:bin}/crond_raw

[cron-service]
recipe = slapos.cookbook:wrapper
command-line = ${cron:binary}
wrapper-path = ${directory:services}/crond
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[cron-simplelogger]
recipe = slapos.cookbook:simplelogger
wrapper = ${directory:bin}/cron_simplelogger
log = ${directory:log}/crond.log

#----------------
#--
#-- Deploy frontend.

[request-slave-frontend-base]
recipe = slapos.cookbook:requestoptional
server-url = ${slap-connection:server-url}
key-file = ${slap-connection:key-file}
cert-file = ${slap-connection:cert-file}
computer-id = ${slap-connection:computer-id}
partition-id = ${slap-connection:partition-id}
slave = true
config-https-only = True
config-type = websocket
config-url = https://[${novnc-instance:ip}]:${novnc-instance:port}
return = secure_access domain

[request-slave-frontend]
<= request-slave-frontend-base
software-url = ${slap-parameter:frontend-software-url}
software-type = ${slap-parameter:frontend-software-type}
name = ${slap-parameter:frontend-instance-name}
sla-instance_guid = ${slap-parameter:frontend-instance-guid}

[frontend-promise]
<= monitor-promise-base
promise = check_url_available
name = frontend_promise.py
config-url = ${request-slave-frontend:connection-secure_access}

{% if additional_frontend %}
[request-slave-frontend-additional]
<= request-slave-frontend-base
software-url = ${slap-parameter:frontend-additional-software-url}
software-type = ${slap-parameter:frontend-additional-software-type}
name = ${slap-parameter:frontend-additional-instance-name}
sla-instance_guid = ${slap-parameter:frontend-additional-instance-guid}

[frontend-additional-promise]
<= monitor-promise-base
promise = check_url_available
name = frontend_additional_promise.py
config-url = ${publish-connection-information:url-additional}
{% endif %}

{% if enable_http %}
[httpd]
recipe = slapos.cookbook:simplehttpserver
host = ${slap-network-information:local-ipv4}
port = ${slap-parameter:httpd-port}
base-path = ${directory:public}
wrapper = ${directory:bin}/http-server
log-file = ${directory:log}/httpd.log
use-hash-url = false

[httpd-service]
recipe = slapos.cookbook:wrapper
command-line = ${httpd:wrapper}
wrapper-path = ${directory:services}/http-server
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[httpd-promise]
<= monitor-promise-base
promise = check_socket_listening
name = httpd.py
config-host = ${httpd:host}
config-port = ${httpd:port}
{% endif %}

[monitor-instance-parameter]
monitor-httpd-port = 8026
monitor-title = {{ slapparameter_dict.get('name', 'KVM Standalone') }}
cors-domains = {{ slapparameter_dict.get('monitor-cors-domains', 'monitor.app.officejs.com') }}
{% if slapparameter_dict.get('monitor-username', '') -%}
username = {{ slapparameter_dict['monitor-username'] }}
{% endif -%}
{% if slapparameter_dict.get('monitor-password', '') -%}
password = {{ slapparameter_dict['monitor-password'] }}
{% endif -%}
interface-url = {{ slapparameter_dict.get('monitor-interface-url', 'https://monitor.app.officejs.com') }}

# this helper just gives a blank line to insert in multiline values
[helper]
blank-line =

[publish-connection-information]
<= monitor-publish
recipe = slapos.cookbook:publish
ipv6 = ${slap-network-information:global-ipv6}
backend-url = https://[${novnc-instance:ip}]:${novnc-instance:port}/vnc.html?auto=1&encrypt=1&password=${kvm-controller-parameter-dict:vnc-passwd}
url = ${request-slave-frontend:connection-secure_access}/vnc.html?auto=1&encrypt=1&password=${kvm-controller-parameter-dict:vnc-passwd}
{% if additional_frontend %}
url-additional = ${request-slave-frontend-additional:connection-secure_access}/vnc.html?auto=1&encrypt=1&password=${kvm-controller-parameter-dict:vnc-passwd}
{% endif %}
{% set disk_number = len(storage_dict) -%}
maximum-extra-disk-amount = {{ disk_number }}
{% set iface = 'ens3' -%}
{% if use_nat -%}
{%   set iface = 'ens4' -%}
{%   if nat_rule_list -%}
# Publish NAT port mapping status
{%     for port in nat_rule_list.split(' ') -%}
{%       if ':' in port -%}
{%         set proto, port = port.split(':') -%}
{%       else -%}
{%         set proto, port = 'tcp', port -%}
{%       endif -%}
{%       set external_port = 10000 + port|int() -%}
nat-rule-port-{{proto}}-{{port}} = ${slap-network-information:global-ipv6} : ${6tunnel-{{proto}}-{{external_port}}:ipv6-port}
{%       if slapparameter_dict.get('publish-nat-url', False) -%}
nat-rule-url-{{proto}}-{{port}} = [${slap-network-information:global-ipv6}]:${6tunnel-{{proto}}-{{external_port}}:ipv6-port}
{%       endif -%}
{%     endfor -%}
{%   endif -%}
{% endif -%}
{% if use_tap -%}
tap-ipv4 = {{ slap_configuration.get('tap-ipv4-addr', '') }}
tap-ipv6 = {{ slap_configuration.get('tap-ipv6-addr', '') }}
{% endif -%}

{% set kvm_http = 'http://${slap-network-information:local-ipv4}:' ~ slapparameter_dict.get('httpd-port', 8081) -%}
{% if enable_http %}
{%   if use_nat -%}
{%     set kvm_http = 'http://10.0.2.100' -%}
{%   endif %}
{%   if slapparameter_dict.get('authorized-key', '') and slapparameter_dict.get('type', '') == 'cluster' -%}
key_info = Get the publick key file in your VM with the command: wget {{ kvm_http }}/authorized_keys
{%   endif %}
{% endif %}

ipv6-network-info =
{% if use_tap and slap_configuration.get('tap-ipv6-addr') %}
  PERMANENT SOLUTION: in your VM, add the lines below in /etc/network/interfaces and then run: "ifup {{ iface }}"
  auto {{ iface }}
  iface {{ iface }} inet6 static
  	address {{ slap_configuration.get('tap-ipv6-gateway') }}
  	netmask {{ slap_configuration.get('tap-ipv6-network').split('/')[1] }}
  	gateway {{ slap_configuration.get('tap-ipv6-addr') }}
{% if enable_http %}
  ${helper:blank-line}
  TEMPORARY SOLUTION: run in your VM the command: "wget -O- {{ kvm_http }}/${network-config-ipv6:filename} | /bin/sh -"
  (the configuration will be gone after the next reboot)
{% endif %}
{% endif %}


{% if use_tap and slap_configuration.get('tap-ipv4-addr') -%}
{%   do part_list.append('network-config-ipv4') %}
[network-config-ipv4]
recipe = plone.recipe.command
filename = netconfig.sh
path = ${directory:public}/${:filename}
ipv4-add-address = ip -4 address add {{ slap_configuration.get('tap-ipv4-addr') }}/{{ slap_configuration.get('tap-ipv4-netmask') }} dev \$IFACE noprefixroute
ipv4-add-gateway-route = ip route add {{ slap_configuration.get('tap-ipv4-gateway') }} dev \$IFACE
{%   if nat_restrict -%}
ipv4-add-default-route = ip route add default via {{ slap_configuration.get('tap-ipv4-gateway') }} dev \$IFACE
{%   elif global_ipv4_prefix -%}
ipv4-add-default-route = ip route add {{ global_ipv4_prefix }} via {{ slap_configuration.get('tap-ipv4-gateway') }} dev \$IFACE src {{ slap_configuration.get('tap-ipv4-addr') }}
{%   else -%}
ipv4-add-default-route =
{%   endif -%}
ipv4-set-link-up = ip link set dev \$IFACE up
command =
  cat > ${:path} << EOF
  #!/bin/sh
  IFACE={{ iface }}
  #try to be compatible with OS with old names
  ip a | grep eth0: && [ \$IFACE = ens3 ] && IFACE=eth0
  ip a | grep eth1: && [ \$IFACE = ens4 ] && IFACE=eth1
  ${:ipv4-add-address}
  ${:ipv4-set-link-up}
  ${:ipv4-add-gateway-route}
  ${:ipv4-add-default-route}
  EOF
update-command = ${:command}
{% endif -%}

{% if use_tap and slap_configuration.get('tap-ipv6-addr') -%}
[network-config-ipv6]
recipe = plone.recipe.command
filename = ipv6_config.sh
path = ${directory:public}/${:filename}
ipv6-add-address = ip -6 address add {{ slap_configuration.get('tap-ipv6-gateway') }}/{{ slap_configuration.get('tap-ipv6-network').split('/')[1] }} dev \$IFACE
ipv6-add-default-route =
  ip -6 route del default ; ip -6 route add default dev \$IFACE via {{ slap_configuration.get('tap-ipv6-addr') }}
ipv6-set-link-up = ip link set dev \$IFACE up
command =
  cat > ${:path} << EOF
  #!/bin/sh
  IFACE={{ iface }}
  ${:ipv6-add-address}
  ${:ipv6-set-link-up}
  ${:ipv6-add-default-route}
  EOF
update-command = ${:command}
{% endif -%}

{% macro writefile(section_name, file_path, content, mode='') -%}
{% set data_list =  content.split('\n') -%}
[{{ section_name }}]
recipe = collective.recipe.template
input = inline:
  {{ data_list | join('\n  ') }}
output = {{ file_path }}
mode = {{ mode }}
{% endmacro -%}

# write vm-data into file public/data
{{ writefile('vm-data-content', '${directory:public}/data', slapparameter_dict.get('data-to-vm', ''), '700') }}

{% if slapparameter_dict.get('authorized-key', '') -%}
# write public key for vms to public/authorized_keys
{{   writefile('get-authorized-key', '${directory:public}/authorized_keys', slapparameter_dict.get('authorized-key', ''), '700') }}
{% endif -%}

{% if use_tap and nat_restrict -%}
# Ask to set default to tap interface in the vm
{{ writefile('set-default-interface', '${directory:public}/delDefaultIface', iface, '600') }}
{% do part_list.append('set-default-interface') -%}
{% endif -%}

[publish-host-config]
recipe = plone.recipe.command
name = {{ slapparameter_dict.get('name', 'localhost') }}
{% if use_tap and slap_configuration.get('tap-ipv4-addr') -%}
local-ipv4 = {{ slap_configuration['tap-ipv4-addr'] }}
gateway = {{ slap_configuration.get('tap-ipv4-gateway') }}
netmask = {{ slap_configuration.get('tap-ipv4-network') }}
network = {{ slap_configuration.get('tap-ipv4-netmask') }}
{% else -%}
local-ipv4 = 127.0.0.1
gateway =
netmask =
network =
{% endif -%}
path-host = ${directory:public}/hostname
path-ip = ${directory:public}/ipv4
path-gateway = ${directory:public}/gateway
path-network = ${directory:public}/network
path-netmask = ${directory:public}/netmask
command =
  rm -f ${:path-host}
  rm -f ${:path-ip}
  rm -f ${:path-gateway}
  rm -f ${:path-network}
  rm -f ${:path-netmask}
  echo "${:name}" > ${:path-host}
  echo "${:local-ipv4}" > ${:path-ip}
  echo "${:gateway}" > ${:path-gateway}
  echo "${:network}" > ${:path-network}
  echo "${:netmask}" > ${:path-netmask}
update-command = ${:command}

# To access documents of main instance (in case of kvm-cluster) through http
[cluster-url-path]
recipe = slapos.recipe.template:jinja2
template = {{ template_content }}
filename = cluster.hash
rendered = ${directory:public}/${:filename}
hash-url = https://10.0.2.101:443/{{ slapparameter_dict.get('document-path', '') }}
context =
    key content_list :hash-url
    raw sep #

# This 6to4 tunnel help to access document url in ipv4
[tunnel-cluster-url]
recipe = slapos.cookbook:ipv4toipv6
ipv6 = {{ slapparameter_dict.get('document-host', '') }}
ipv4 = ${slap-network-information:local-ipv4}
ipv6-port = {{ slapparameter_dict.get('document-port', '') }}
ipv4-port = 16936
shell-path = {{ dash_executable_location }}
6tunnel-path = {{ sixtunnel_executable_location }}
runner-path = ${directory:bin}/6tunnel-cluster

[tunnel-cluster-service]
recipe = slapos.cookbook:wrapper
command-line = ${tunnel-cluster-url:runner-path}
wrapper-path = ${directory:services}/6tunnel-cluster
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg

[ansible-vm-bin]
recipe = slapos.recipe.template:jinja2
template = {{ ansible_promise_tpl }}
rendered = ${directory:bin}/ansible_{{ name }}
extensions = jinja2.ext.do
context =
  raw logs ${directory:public}/ansible
  raw name {{ name }}

[ansible-vm-promise]
<= monitor-promise-base
promise = check_command_execute
name = ansible_{{ name }}.py
config-command = ${ansible-vm-bin:rendered}

{% if bootstrap_script_url -%}
[download-bootstrap-script]
recipe = slapos.recipe.build:download
offline = false
destination = ${directory:public}/vm-bootstrap
{% set bootstrap_script_url = bootstrap_script_url.split('#') -%}
url = {{ bootstrap_script_url[0] }}
md5sum = {{ bootstrap_script_url[1] }}
{% endif -%}

[logrotate-vm-bootstrap]
< = logrotate-entry-base
name = vm-bootstrap
log = ${directory:public}/ansible/vm-bootstrap.log

[slap-parameter]
# Default values if not specified
frontend-software-type = RootSoftwareInstance
frontend-software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
frontend-instance-guid =
frontend-instance-name = VNC Real Frontend
frontend-additional-software-type = RootSoftwareInstance
frontend-additional-software-url = http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg
frontend-additional-instance-guid =
frontend-additional-instance-name = VNC Real Frontend Additional
nbd-port = 1024
nbd-host =
nbd2-port = 1024
nbd2-host =
boot-image-url-list =

enable-device-hotplug = False
ram-size = 4096
ram-hotplug-slot-size = 512
disk-size = 40
disk-type = virtio
disk-format = qcow2
disk-device-path =

network-adapter = virtio-net-pci

cpu-count = 2
disk-cache =
disk-aio =
auto-ballooning = True
machine-options =
cpu-model = host

nat-rules = 22 80 443
use-nat = True
use-tap = True
nat-restrict-mode = False
enable-vhost = False

virtual-hard-drive-url =
virtual-hard-drive-md5sum =
virtual-hard-drive-gzipped = False
# if virtual-hard-drive-url use https, then specify if https certificate should be checked or not
hard-drive-url-check-certificate = True

external-disk-number = 0
external-disk-size = 20
external-disk-format = qcow2

# Help to get some configuration files into the vm from http
enable-http-server = False
httpd-port = 8081
# for auto config, the public key file will be available in the VM via url http://10.0.2.100/authorized_key if use-nat = True
authorized-key =

# send some content which will be accessible to the vm through static url: http://10.0.2.100/data
data-to-vm =

{% for k, v in slapparameter_dict.items() -%}
{% if k == 'authorized-key' and v -%}
{% set key_list =  v.split('\n') -%}
{{ k }} =
  {{ key_list | join('\n  ') }}
{% elif k in ['boot-image-url-list', 'boot-image-url-select', 'whitelist-domains'] %}
{# needs to decorate possibly multiline or maybe unsafe value #}
{{ k }} = {{ dumps(v) }}
{% else -%}
{{ k }} = {{ v }}
{% endif -%}
{% endfor -%}

#############################
#
# Instanciate kvm (Buildout Section)
#
#############################

{% if slapparameter_dict.get('document-host', '') %}
# Set Additionals parts
{%   do part_list.append('cluster-url-path') -%}
{% endif -%}
{% if enable_http %}
{%   do part_list.extend(['httpd', 'httpd-service', 'httpd-promise', 'publish-host-config']) -%}
{% if slapparameter_dict.get('data-to-vm', '') %}
{%   do part_list.append('vm-data-content') -%}
{% endif -%}
{% if not disable_ansible_promise %}
{%   do part_list.extend(['ansible-vm-promise', 'logrotate-vm-bootstrap']) -%}
{% endif -%}
{% if slapparameter_dict.get('authorized-key', '') and slapparameter_dict.get('type', '') == 'cluster' %}
{%   do part_list.append('get-authorized-key') -%}
{% endif -%}
{% if bootstrap_script_url -%}
{%   do part_list.append('download-bootstrap-script') -%}
{% endif -%}
{% if slapparameter_dict.get('document-port', '') -%}
{%   do part_list.append('tunnel-cluster-service') -%}
{% endif -%}

{% endif -%}


{% if disk_device_path %}
{%   do part_list.append('disk-device-permission') -%}
[disk-device-permission]
recipe = slapos.recipe.template:jinja2
template = inline:
{%- raw %}
  {%- set disk_list = [] %}
  {%- for disk in disk_device_path.split() %}
  {%-   do disk_list.append({'disk': disk}) %}
  {%- endfor -%}
  {{ json_module.dumps(disk_list) }}
{% endraw -%}
rendered = ${buildout:directory}/.slapos-disk-permission
extensions = jinja2.ext.do
context =
  import json_module json
  raw disk_device_path {{disk_device_path}}

{%   do part_list.append('wipe-disk-device-wrapper') -%}
[wipe-disk-device-wrapper]
recipe = slapos.recipe.template:jinja2
template = inline:
  #!/bin/sh
{%- for disk_device in disk_device_path.split() %}
  dd if=/dev/zero of={{ disk_device }} bs=4096 count=500k
{%- endfor %}
rendered = ${directory:prerm}/slapos_wipe_device_disk
mode = 0700
{% endif -%}

{%   do part_list.append('whitelist-firewall') -%}
[whitelist-firewall]
recipe = slapos.cookbook:wrapper
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
wrapper-path = ${directory:scripts}/${:_buildout_section_name_}
command-line =
  {{ dnsresolver_executable }}
  --style list
  --output ${:output}
  ${:source}

source =
  ${whitelist-domains-request:rendered}
  {{ whitelist_domains_default }}
  ${whitelist-domains-resolv.conf:output}
  ${whitelist-domains-download:output}
output = ${buildout:directory}/.slapos-whitelist-firewall

[whitelist-firewall-directory]
recipe = plone.recipe.command
location = ${buildout:parts-directory}/whitelist-firewall
command = mkdir -p ${:location}
update-command = ${:command}

[whitelist-domains-request]
recipe = slapos.recipe.template:jinja2
template = inline:
{%- raw %}
  {%- for domain in whitelist_domains.split() %}
  {{ domain }}
  {%- endfor %}
{% endraw -%}
rendered = ${whitelist-firewall-directory:location}/${:_buildout_section_name_}.txt
whitelist-domains = {{ dumps(whitelist_domains) }}
extensions = jinja2.ext.do
context =
  key whitelist_domains :whitelist-domains

[whitelist-domains-resolv.conf]
recipe = plone.recipe.command
output = ${whitelist-firewall-directory:location}/${:_buildout_section_name_}.txt
update-command = ${:command}
command =
  egrep ^nameserver /etc/resolv.conf  | cut -d ' ' -f 2 > ${:output}

[whitelist-domains-download]
recipe = slapos.cookbook:wrapper
hash-existing-files = ${buildout:directory}/software_release/buildout.cfg
wrapper-path = ${directory:scripts}/${:_buildout_section_name_}
output = ${whitelist-firewall-directory:location}/${:_buildout_section_name_}.txt
interval = 3600
command-line = {{ python_executable }} {{ whitelist_firewall_download_controller }} {{ curl_executable_location }} 3600 ${:output} ${:url}

url = https://stream.nxdcdn.com/rapidspace-whitelist-domains

[instance-kvm-parts]
parts =
  certificate-authority
  certificate-authority-service
  publish-connection-information
  kvm-instance
  kvm-controller-wrapper
  kvm-vnc-promise
  kvm-disk-image-corruption-promise
  websockify-sighandler
  websockify-sighandler-service
  novnc-promise
  kvm-started-promise
  cron
  cron-service
  cron-entry-logrotate
  frontend-promise
{% if virtual_hard_drive_url_enabled %}
  virtual-hard-drive-url-download-wrapper
  virtual-hard-drive-url-config-state-promise
  virtual-hard-drive-url-download-md5sum-promise
  virtual-hard-drive-url-download-state-promise
  virtual-hard-drive-url-processed-config-promise
{% endif %}
{% if boot_image_url_list_enabled %}
  boot-image-url-list-download-wrapper
  boot-image-url-list-config-state-promise
  boot-image-url-list-download-md5sum-promise
  boot-image-url-list-download-state-promise
  boot-image-url-list-processed-config-promise
{% endif %}
{% if boot_image_url_select_enabled %}
  boot-image-url-select-download-wrapper
  boot-image-url-select-config-state-promise
  boot-image-url-select-download-md5sum-promise
  boot-image-url-select-download-state-promise
  boot-image-url-select-processed-config-promise
{% endif %}
{% if additional_frontend %}
  frontend-additional-promise
{% endif %}
# monitor parts
  monitor-base
# Complete parts with sections
  {{ part_list | join('\n  ') }}

[buildout]
parts = ${instance-kvm-parts:parts}

extends =
#  Add extends list
 {{ extends_list | join('\n  ') }}
#  {{ template_httpd_cfg }}

eggs-directory = {{ eggs_directory }}
develop-eggs-directory = {{ develop_eggs_directory }}
offline = true