{% from "instance_zodb_base" import zodb_dict with context %} {% set wsgi = slapparameter_dict['wsgi'] -%} {% set webdav = slapparameter_dict['webdav'] -%} {% set use_ipv6 = slapparameter_dict.get('use-ipv6', False) -%} {% set next_port = itertools.count(slapparameter_dict['port-base']).next -%} {% set site_id = slapparameter_dict['site-id'] -%} {% set instance_index_list = range(slapparameter_dict['instance-count']) -%} {% set node_id_base = slapparameter_dict['name'] -%} {% set selenium_server_configuration_dict = slapparameter_dict.get('selenium-server-configuration-dict', None) -%} {% set node_id_index_format = '-%%0%ii' % (len(str(instance_index_list[-1])), ) -%} {% set part_list = [] -%} {% set publish_list = [] -%} {% set test_runner_address_list = [] -%} {% set test_runner_enabled = slapparameter_dict['test-runner-enabled'] -%} {% set test_runner_node_count = slapparameter_dict['test-runner-node-count'] -%} {% set test_runner_random_activity_priority = slapparameter_dict['test-runner-random-activity-priority'] -%} {% set longrequest_logger_base_path = buildout_directory ~ '/var/log/longrequest_logger_' -%} {% if webdav -%} {% set timerserver_interval = 0 -%} {% else -%} {% set timerserver_interval = slapparameter_dict['timerserver-interval'] -%} {%- endif %} {% macro section(name) %}{% do part_list.append(name) %}{{ name }}{% endmacro -%} {% set bin_directory = parameter_dict['buildout-bin-directory'] -%} {# XXX: This template only supports exactly one IPv4 and one IPv6 per partition. No more (undefined result), no less (IndexError). -#} {% set ipv4 = (ipv4_set | list)[0] -%} {% set publishable_hosts_dict = {} -%} {% set port_dict = {} -%} {% for alias, url in ( ('erp5-memcached-volatile', slapparameter_dict['memcached-url']), ('erp5-memcached-persistent', slapparameter_dict['kumofs-url']), ('erp5-cloudooo', slapparameter_dict['cloudooo-url']), ('erp5-smtp', slapparameter_dict['smtp-url']), ) -%} {% set parsed_url = urlparse.urlparse(url) -%} {% do port_dict.__setitem__(alias, parsed_url.port) -%} {% do publishable_hosts_dict.__setitem__(alias, parsed_url.hostname) -%} {%- endfor %} {% for i, url in enumerate(slapparameter_dict['mysql-url-list']) -%} {% do publishable_hosts_dict.__setitem__( 'erp5-catalog-' ~ i, urlparse.urlparse(url).hostname, ) -%} {%- endfor %} {% do publishable_hosts_dict.update(slapparameter_dict['hosts-dict']) -%} {% set hosts_dict = publishable_hosts_dict.copy() -%} {% do hosts_dict.update(slapparameter_dict['computer-hosts-dict'].get(computer, {})) -%} [jinja2-template-base] recipe = slapos.recipe.template:jinja2 mode = 644 [run-common] <= userhosts-wrapper-base environment-extra = environment += TMP=${directory:tmp} TMPDIR=${directory:tmp} HOME=${buildout:directory} PATH=${binary-link:target-directory}:{{ parameter_dict['coreutils'] }}/bin:{{ parameter_dict['perl_dbd_mariadb_path'] }} TZ={{ slapparameter_dict['timezone'] }} MATPLOTLIBRC={{ parameter_dict['matplotlibrc'] }} INSTANCE_HOME=${:instance-home} CAUCASE={{ slapparameter_dict['caucase-url'] }} FONTCONFIG_FILE=${fontconfig-conf:rendered} {% if slapparameter_dict.get('wendelin-core-zblk-fmt') %} WENDELIN_CORE_ZBLK_FMT={{ slapparameter_dict['wendelin-core-zblk-fmt'] }} {% endif %} WENDELIN_CORE_WCFS_AUTOSTART=no {% if slapparameter_dict['wcfs_enable'] %} WENDELIN_CORE_VIRTMEM=r:wcfs+w:uvmm {% else %} WENDELIN_CORE_VIRTMEM=rw:uvmm {% endif %} ${:environment-extra} [directory] recipe = slapos.cookbook:mkdirectory bin = ${buildout:directory}/bin etc = ${buildout:directory}/etc instance = ${:srv}/erp5shared instance-constraint = ${:instance}/Constraint instance-document = ${:instance}/Document instance-etc = ${:instance}/etc instance-etc-package-include = ${:instance}/etc/package-include instance-extensions = ${:instance}/Extensions instance-import = ${:instance}/import instance-lib = ${:instance}/lib instance-products = ${:instance}/Products instance-propertysheet = ${:instance}/PropertySheet instance-tests = ${:instance}/tests log = ${:var}/log run = ${:var}/run services = ${:etc}/run service-on-watch = ${:etc}/service srv = ${buildout:directory}/srv ca-dir = ${:srv}/ssl tmp = ${buildout:directory}/tmp var = ${buildout:directory}/var plugin = ${:etc}/plugin unit-test-path = ${:srv}/test-instance/unit_test fontconfig-cache = ${buildout:directory}/.fontconfig [fontconfig-conf] recipe = slapos.recipe.template:jinja2 template = {{ parameter_dict['template-fonts-conf'] }} rendered = ${directory:etc}/fonts.conf context = key cachedir directory:fontconfig-cache key fonts :fonts key includes :includes fonts = {% for font in parameter_dict['fonts'].splitlines() %} {{ font }} {% endfor %} includes = {% for include in parameter_dict['fontconfig-includes'].splitlines() %} {{ include }} {% endfor %} # Used for ERP5 resiliency or (more probably) # webrunner resiliency with erp5 inside. [{{ section("resiliency-exclude-file") }}] # Generate rdiff exclude file recipe = slapos.recipe.template:jinja2 mode = 644 template = {{ 'inline:{{ "${directory:log}/**\\n${directory:tmp}/**\\n" }}' }} rendered = ${directory:srv}/exporter.exclude [{{ section("resiliency-identity-signature-script")}}] # Generate identity script used by webrunner to check data integrity recipe = slapos.cookbook:wrapper command-line = {{ bin_directory }}/backup-identity-script-excluding-path --exclude-path "srv/backup/logrotate/**" wrapper-path = ${directory:srv}/.backup_identity_script mode = 770 [binary-link] recipe = slapos.cookbook:symbolic.link target-directory = ${directory:bin} link-binary = {{ dumps(parameter_dict['link-binary']) }} {% if use_ipv6 -%} {% set ipv6 = (ipv6_set | list)[0] -%} [ipv6toipv4-base] recipe = slapos.cookbook:ipv6toipv4 runner-path = ${directory:services}/${:base-name} 6tunnel-path = {{ parameter_dict['6tunnel'] }}/bin/6tunnel shell-path = {{ parameter_dict['dash'] }}/bin/dash ipv4 = {{ ipv4 }} ipv6 = {{ ipv6 }} {% endif -%} [hosts-parameter] # Used for both hosts and hostaliases sections. host-dict = {{ dumps(hosts_dict) }} hostalias-dict = {{ dumps(slapparameter_dict['hostalias-dict']) }} # Note: there is a subtle difference between hosts and hostaliases files: # - hosts files start with resolved, followed by alias(es) (only one alias per # line in this case) # - hostaliases start with alias, followed by resolved # ...so it's not possible to merge these templates (not a big deal anyway). [hostaliases] < = jinja2-template-base template = inline: {{ ' {% for alias, aliased in host_dict.items() -%} {{ alias }} {{ aliased }} {% endfor %} ' }} rendered = ${directory:etc}/hostaliases context = key host_dict hosts-parameter:hostalias-dict [hosts] < = jinja2-template-base template = inline: {{ ' {% for alias, aliased in host_dict.items() -%} {{ aliased }} {{ alias }} {% endfor %} ' }} rendered = ${directory:etc}/hosts context = key host_dict hosts-parameter:host-dict [userhosts-bin] recipe = slapos.recipe.template:jinja2 mode = 755 template = inline:#!{{ parameter_dict['dash'] }}/bin/dash export HOSTS_FILE=${hosts:rendered} export LD_PRELOAD={{ parameter_dict['userhosts'] }}:$LD_PRELOAD exec "$@" rendered = ${directory:bin}/userhosts [userhosts-wrapper-base] recipe = slapos.cookbook:wrapper environment = HOSTALIASES=${hostaliases:rendered} command-line = '${userhosts-bin:rendered}' ${:wrapped-command-line} {# Hack to deploy SSL certs via instance parameters -#} {% for zodb in zodb_dict.itervalues() -%} {% set storage_dict = zodb.setdefault('storage-dict', {}) -%} {% if zodb['type'] == 'neo' and storage_dict.get('ssl', 1) -%} {% for k, v in (('_ca', 'ca.crt'), ('_cert', 'neo.crt'), ('_key', 'neo.key')) -%} {% if k in storage_dict -%} [{{ section('neo-ssl-' + k[1:]) }}] < = jinja2-template-base rendered = ${directory:etc}/{{v}} template = inline:{{'{{'}}pem}} context = key pem :pem pem = {{dumps(storage_dict.pop(k))}} {% endif -%} {% endfor -%} {% endif -%} {% endfor -%} {# endhack -#} [runzope-base] <= run-common instance-home = ${directory:instance} {% if wsgi -%} wrapped-command-line = '{{ bin_directory }}/runwsgi' {% if webdav %}-w{% endif %} {{ ipv4 }}:${:port} {% if timerserver_interval %}--timerserver-interval={{ timerserver_interval }}{% endif %} '${:configuration-file}' {% else -%} wrapped-command-line = '{{ bin_directory }}/runzope' -C '${:configuration-file}' {%- endif %} {%- set private_dev_shm = slapparameter_dict['private-dev-shm'] %} {%- if private_dev_shm %} private-tmpfs = {{ private_dev_shm }} /dev/shm {%- endif %} [{{ section('zcml') }}] recipe = slapos.cookbook:copyfilelist target-directory = ${directory:instance-etc} file-list = {{ parameter_dict['site-zcml'] }} [{{ section('zope-inituser') }}] < = jinja2-template-base rendered = ${directory:instance}/inituser template = inline:{{ slapparameter_dict['inituser-login'] }}:{SHA}{{ hashlib.sha1(slapparameter_dict['inituser-password']).digest().encode('base64').rstrip() }} mode = 600 once = ${:rendered}_done [zope-conf-parameter-base] ip = {{ ipv4 }} site-id = {{ site_id }} {% if site_id -%} mysql-url = {{ slapparameter_dict['mysql-url-list'][0] }} inituser = {{ slapparameter_dict['inituser-login'] }} {% set mysql = urlparse.urlsplit(slapparameter_dict['mysql-url-list'][0]) -%} {% set mysql_db = mysql.path.split('/')[1] -%} sql-connection-string = {{ '%s@erp5-catalog-0:%s %s %s' % ( mysql_db, mysql.port, mysql.username, mysql.password) }} bt5 = {{ slapparameter_dict['bt5'] }} bt5-repository-url = {{ slapparameter_dict['bt5-repository-url'] }} id-store-interval = {{ dumps(slapparameter_dict['id-store-interval']) }} home = ${buildout:directory} # We only want to change the hostname to 'erp5-cloudooo' if we use the internal # cloudooo. We plan to remove the ability to have an internal one, so this # heuristic is enough. {% set cloudooo = urlparse.urlsplit(slapparameter_dict['cloudooo-url']) -%} cloudooo-url = {{ (cloudooo if cloudooo.port == None else cloudooo._replace(netloc='erp5-cloudooo:%s' % cloudooo.port)).geturl() }} {% endif -%} developer-list = {{ dumps(slapparameter_dict['developer-list']) }} publisher-timeout = {{ dumps(slapparameter_dict['publisher-timeout']) }} activity-timeout = {{ dumps(slapparameter_dict['activity-timeout']) }} instance = ${directory:instance} instance-products = ${directory:instance-products} deadlock-path = /manage_debug_threads deadlock-debugger-password = {{ dumps(slapparameter_dict['deadlock-debugger-password']) }} {% if slapparameter_dict.get('tidstorage-ip') -%} tidstorage-ip = {{ dumps(slapparameter_dict['tidstorage-ip']) }} tidstorage-port = {{ dumps(slapparameter_dict['tidstorage-port']) }} {% endif -%} {% set thread_amount = slapparameter_dict['thread-amount'] -%} {% set large_file_threshold = slapparameter_dict['large-file-threshold'] -%} thread-amount = {{ thread_amount }} webdav = {{ dumps(webdav) }} wsgi = {{ dumps(wsgi) }} timerserver-interval = {{ dumps(timerserver_interval) }} [zope-conf-base] < = jinja2-template-base template = {{ parameter_dict['zope-conf-template'] }} extensions = jinja2.ext.do jinja2.ext.loopcontrols import-list = rawfile root_common {{ root_common }} {% macro zope( index, port, longrequest_logger_timeout, longrequest_logger_interval ) -%} {% set name = 'zope-' ~ index -%} {% set conf_name = name ~ '-conf' -%} {% set conf_parameter_name = conf_name ~ '-param' -%} {% set zope_tunnel_section_name = name ~ '-ipv6toipv4' -%} {% set zope_tunnel_base_name = zope_tunnel_section_name -%} [{{ conf_parameter_name }}] < = zope-conf-parameter-base pid-file = ${directory:run}/{{ name }}.pid lock-file = ${directory:run}/{{ name }}.lock port = {{ port }} event-log = ${directory:log}/{{ name }}-event.log z2-log = ${directory:log}/{{ name }}-Z2.log node-id = {{ dumps(node_id_base ~ (node_id_index_format % index)) }} {% set log_list = [] -%} {% set import_set = set() -%} {% for db_name, zodb in zodb_dict.iteritems() -%} {% do zodb.setdefault('pool-size', thread_amount) -%} {% if zodb['type'] == 'neo' -%} {% do import_set.add('neo.client') -%} {% set log = name ~ '-neo-' ~ db_name ~ '.log' -%} {% do log_list.append('${directory:log}/' + log) -%} {% do zodb['storage-dict'].update(logfile='~/var/log/'+log) -%} {% endif -%} {% endfor -%} import-list = {{ dumps(list(import_set)) }} zodb-dict = {{ dumps(zodb_dict) }} large-file-threshold = {{ large_file_threshold }} {% if longrequest_logger_interval > 0 -%} longrequest-logger-file = {{ longrequest_logger_base_path ~ name ~ ".log" }} longrequest-logger-timeout = {{ longrequest_logger_timeout }} longrequest-logger-interval = {{ longrequest_logger_interval }} {% else -%} longrequest-logger-file = {% endif -%} [{{ conf_name }}] < = zope-conf-base rendered = ${directory:etc}/{{ name }}.conf context = section parameter_dict {{ conf_parameter_name }} import os os import re re [{{ section(name) }}] < = runzope-base wrapper-path = ${directory:service-on-watch}/{{ name }} configuration-file = {{ '${' ~ conf_name ~ ':rendered}' }} {%- if wsgi %} port = {{ port }} {%- endif %} hash-files = ${:configuration-file} hash-existing-files = ${buildout:directory}/software_release/buildout.cfg [{{ section("promise-" ~ name) }}] <= monitor-promise-base promise = check_socket_listening name = {{ name }}.py config-host = {{ ipv4 }} config-port = {{ port }} {% if use_ipv6 -%} [{{ zope_tunnel_section_name }}] < = ipv6toipv4-base base-name = {{ zope_tunnel_base_name }} ipv6-port = {{ port }} ipv4-port = {{ port }} {% do publish_list.append(("[" ~ ipv6 ~ "]:" ~ port, thread_amount, webdav)) -%} [{{ section("promise-tunnel-" ~ name) }}] <= monitor-promise-base promise = check_socket_listening name = {{ zope_tunnel_base_name }}.py config-host = {{ '${' ~ zope_tunnel_section_name ~ ':ipv6}' }} config-port = {{ '${' ~ zope_tunnel_section_name ~ ':ipv6-port}' }} {% else -%} {% do publish_list.append((ipv4 ~ ":" ~ port, thread_amount, webdav)) -%} {% endif -%} {% if longrequest_logger_interval > 0 -%} [{{ section('promise-check-' ~name ~ '-longrequest-error-log') }}] <= monitor-promise-base promise = check_error_on_zope_longrequest_log name = {{'check-' ~ name ~ '-longrequest-error-log.py'}} config-log-file = {{ '${' ~ conf_parameter_name ~ ':longrequest-logger-file}' }} config-error-threshold = {{ slapparameter_dict["zope-longrequest-logger-error-threshold"] }} config-maximum-delay = {{ slapparameter_dict["zope-longrequest-logger-maximum-delay"] }} {% endif -%} [{{ section('logrotate-entry-' ~ name) }}] < = logrotate-entry-base name = {{ name }} log = {{ '${' ~ conf_parameter_name ~ ':event-log}' }} {{ '${' ~ conf_parameter_name ~ ':z2-log}' }} {{ '${' ~ conf_parameter_name ~ ':longrequest-logger-file}' }} {{ ' '.join(log_list) }} post = test ! -s {{ '${' ~ conf_parameter_name ~ ':pid-file}' }} || {{ bin_directory }}/slapos-kill --pidfile {{ '${' ~ conf_parameter_name ~ ':pid-file}' }} -s USR2 {% endmacro -%} {% for i in instance_index_list -%} {{ zope( i, next_port(), slapparameter_dict['longrequest-logger-timeout'], slapparameter_dict['longrequest-logger-interval'], ) }} {% endfor -%} [{{ section("watch_activities") }}] <= userhosts-wrapper-base environment += MYSQL=${binary-link:target-directory}/mysql wrapped-command-line = {{ parameter_dict['erp5-location'] }}/product/CMFActivity/bin/${:_buildout_section_name_} "-h erp5-catalog-0 -P {{ mysql.port }} -u {{ mysql.username }} -p{{ mysql.password}} {{ mysql_db }}" 5 3600 wrapper-path = ${buildout:bin-directory}/${:_buildout_section_name_} [{{ section("wait_activities") }}] <= watch_activities {% if test_runner_enabled and test_runner_node_count -%} {% for _ in range(test_runner_node_count) %} {% do test_runner_address_list.append((ipv4, next_port())) %} {% endfor %} {% if selenium_server_configuration_dict -%} [test-zelenium-runner-parameter] configuration = {{ dumps(selenium_server_configuration_dict) }} user = {{ dumps(slapparameter_dict['inituser-login']) }} password = {{ dumps(slapparameter_dict['inituser-password']) }} bin-path = {{ bin_directory }}/{{ parameter_dict['egg-interpreter'] }} [{{ section('test-zelenium-runner') }}] <= jinja2-template-base template = {{ parameter_dict['run-zelenium-template'] }} rendered = ${directory:bin}/runTestSuite mode = 755 context = import json_module json key configuration test-zelenium-runner-parameter:configuration key user test-zelenium-runner-parameter:user key password test-zelenium-runner-parameter:password key bin_path test-zelenium-runner-parameter:bin-path {% else -%} [{{ section('run-unit-test-userhosts-wrapper') }}] <= userhosts-wrapper-base wrapped-command-line = ${runUnitTest:wrapper-path} wrapper-path = ${buildout:bin-directory}/runUnitTest [{{ section('run-test-suite-userhosts-wrapper') }}] <= userhosts-wrapper-base wrapped-command-line = ${runTestSuite:wrapper-path} wrapper-path = ${buildout:bin-directory}/runTestSuite {% set connection_string_list = [] -%} {% for url in slapparameter_dict['mysql-test-url-list'] -%} {% set parsed_url = urlparse.urlparse(url) -%} {% do connection_string_list.append( '%s@%s:%s %s %s' % ( parsed_url.path.lstrip('/'), parsed_url.hostname, parsed_url.port, parsed_url.username, parsed_url.password, ), ) -%} {% endfor -%} [run-test-common] < = run-common environment-extra = REAL_INSTANCE_HOME=${:instance-home} SQLBENCH_PATH={{ parameter_dict['sqlbench_path'] }} TEST_CA_PATH=${directory:ca-dir} ERP5_TEST_RUNNER_CONFIGURATION=${test-runner-configuration:rendered} {%- if wsgi %} erp5_wsgi=1 {%- endif %} instance-home = ${directory:unit-test-path} wrapper-path = ${directory:bin}/${:command-name}.real command-line = '{{ parameter_dict['bin-directory'] }}/${:command-name}' ${:command-line-extra} --conversion_server_url={{ slapparameter_dict['cloudooo-url'] }} --conversion_server_retry_count={{ slapparameter_dict.get('cloudooo-retry-count', 2) }} {#- BBB: We still have test suites that only accept the following 2 options. #} --conversion_server_hostname=erp5-cloudooo --conversion_server_port={{ port_dict['erp5-cloudooo'] }} --volatile_memcached_server_hostname=erp5-memcached-volatile --volatile_memcached_server_port={{ port_dict['erp5-memcached-volatile'] }} --persistent_memcached_server_hostname=erp5-memcached-persistent --persistent_memcached_server_port={{ port_dict['erp5-memcached-persistent'] }} [test-runner-configuration] recipe = slapos.recipe.template:jinja2 rendered = ${directory:etc}/${:_buildout_section_name_}.json template = inline: {{ json.dumps(slapparameter_dict['test-runner-configuration']) }} [{{ section('runUnitTest') }}] < = run-test-common command-name = runUnitTest command-line-extra = --erp5_sql_connection_string '{{ connection_string_list[0] }}' --extra_sql_connection_string_list '{{ ','.join(connection_string_list[1:]) }}' --zserver {{ test_runner_address_list[0][0] ~ ':' ~ test_runner_address_list[0][1] }} --zserver_frontend_url {{ slapparameter_dict['test-runner-apache-url-list'][0] }} {% if test_runner_random_activity_priority is not none %} --random_activity_priority={{ test_runner_random_activity_priority }} {%- endif %} [{{ section('runTestSuite') }}] < = run-test-common command-name = runTestSuite command-line-extra = --db_list '{{ ','.join(connection_string_list) }}' environment-extra += {#- turn a list of (ip, port) in a list of 'ip:port' #} {% set zserver_address_list = [] -%} {% for ip, port in test_runner_address_list %} {% do zserver_address_list.append(ip ~ ':' ~ port) %} {% endfor -%} zserver_address_list={{ ','.join(zserver_address_list) }} zserver_frontend_url_list={{ ','.join(slapparameter_dict['test-runner-apache-url-list']) }} [promise-test-runner-apache-url-executable] # promise to wait for apache partition to have returned the parameter recipe = slapos.cookbook:check_parameter value = {{ slapparameter_dict['test-runner-apache-url-list'] }} expected-not-value = not-ready path = ${directory:bin}/${:_buildout_section_name_} expected-value = [{{ section("promise-test-runner-apache-url") }}] <= monitor-promise-base promise = check_command_execute name = ${:_buildout_section_name_}.py config-command = ${promise-test-runner-apache-url-executable:path} {%- endif %} {%- endif %} [{{ section('promise-check-computer-memory') }}] <= monitor-promise-base promise = check_command_execute name = check-computer-memory.py config-command = "{{ parameter_dict["check-computer-memory-binary"] }}" -db ${monitor-instance-parameter:collector-db} --threshold "{{ slapparameter_dict["computer-memory-percent-threshold"] }}" --unit percent [publish] recipe = slapos.cookbook:publish.serialised zope-address-list = {{ dumps(publish_list) }} {# Note: hosts_dist is generated at zope level rather than at erp5 (root partition) level, as it is easier: we can access urls as python values trivially here. This has the downside of making each zope partition publish the (hopefuly) same dict toward erp5 partition, violating the DRY principle and making the intent hard to guess. -#} hosts-dict = {{ dumps(publishable_hosts_dict) }} monitor-base-url = ${monitor-publish-parameters:monitor-base-url} test-runner-address-list = {{ dumps(test_runner_address_list) }} software-release-url = ${slap-connection:software-release-url} [monitor-instance-parameter] monitor-httpd-ipv6 = {{ (ipv6_set | list)[0] }} monitor-httpd-port = {{ next_port() }} monitor-title = {{ slapparameter_dict['name'] }} password = {{ slapparameter_dict['monitor-passwd'] }} [buildout] extends = {{ template_monitor }} parts += {{ '\n '.join(part_list) }} publish