Commit b80c72ff authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki

Merge branch 'erp5-component' into erp5

parents 51568042 882ee66a
No related merge requests found
......@@ -32,10 +32,10 @@ md5sum = 6f3417691c7a27090f36e7cf4d94b36e
recipe = slapos.recipe.cmmi
depends =
${gdbm:version}
version = 2.4.9
version = 2.4.10
revision = 1
url = http://mir2.ovh.net/ftp.apache.org/dist/httpd/httpd-${:version}.tar.bz2
md5sum = 2ef4e65353497606b24fa9bb3e5a3c40
md5sum = 44543dff14a4ebc1e9e2d86780507156
configure-command = cp -ar ${apr:location}/apr-${apr:version} srclib/apr/; cp -ar ${apr-util:location}/apr-util-${apr-util:version} srclib/apr-util; ./configure
configure-options = --prefix=${buildout:parts-directory}/${:_buildout_section_name_}
--disable-static
......
......@@ -2,7 +2,15 @@
parts =
binutils
extends =
../bison/buildout.cfg
../zlib/buildout.cfg
[binutils]
recipe = slapos.recipe.cmmi
url = http://ftp.gnu.org/gnu/binutils/binutils-2.21.1.tar.bz2
md5sum = bde820eac53fa3a8d8696667418557ad
environment =
PATH=${bison:location}/bin:%(PATH)s
CPPFLAGS=-I${zlib:location}/include
LDFLAGS=-L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
[buildout]
parts = tcl
[tcl-package]
recipe = hexagonit.recipe.download
[tcl]
recipe = slapos.recipe.cmmi
url = http://prdownloads.sourceforge.net/tcl/tcl8.5.13-src.tar.gz
md5sum = fa3a9bf9b2d6ed2431f1baa46f4058b8
strip-top-level-dir = true
[tcl]
recipe = plone.recipe.command
location = ${buildout:parts-directory}/${:_buildout_section_name_}
command = cd ${tcl-package:location}/unix &&
./configure --prefix=${:location} &&
make &&
make test &&
make install
\ No newline at end of file
configure-command = ./unix/configure
configure-options =
--prefix=${:location}
--with-encoding=utf-8
......@@ -7,7 +7,7 @@ extends =
../../component/make/buildout.cfg
../../component/openssl/buildout.cfg
../../component/tcl/buildout.cfg
../../component/libexpat/buildout.cfg
../../component/libxml2/buildout.cfg
../../component/pcre/buildout.cfg
../../component/libcap/buildout.cfg
../../component/flex/buildout.cfg
......@@ -25,16 +25,19 @@ md5sum = 18f7d56650cba260c8cce3bf4abfa56c
configure-options =
--prefix=${buildout:parts-directory}/${:_buildout_section_name_}
--with-openssl=${openssl:location}
--with-expat=${libexpat:location}
--with-xml=libxml2
--with-libxml2=${libxml2:location}
--with-pcre=${pcre:location}
--with-lua=${lua:location}
--with-ncurses=${ncurses:location}
--with-tcl=${tcl:location}/lib/
--with-lzma=${xz-utils:location}
--with-zlib=${zlib:location}
--disable-hwloc
--enable-experimental-plugins
environment =
PATH=${make:location}/bin:${libtool:location}/bin:${pkgconfig:location}/bin:%(PATH)s
LDFLAGS = -L${tcl:location}/lib -Wl,-rpath=${tcl:location}/lib -Wl,-rpath=${openssl:location}/lib
LDFLAGS =-L${libxml2:location}/lib -Wl,-rpath=${libxml2:location}/lib -L${tcl:location}/lib -Wl,-rpath=${tcl:location}/lib -Wl,-rpath=${openssl:location}/lib -L${zlib:location}/lib -Wl,-rpath=${zlib:location}/lib
make-target =
check
install
......@@ -67,20 +67,20 @@ mode = 0644
[template-apache-frontend]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/instance-apache-frontend.cfg
md5sum = 05216295ad8ab5ebf34644ac253f0db6
md5sum = 5388e77d520135b7491f1aeddac5f4e0
output = ${buildout:directory}/template-apache-frontend.cfg
mode = 0644
[template-apache-replicate]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/instance-apache-replicate.cfg.in
md5sum = 5de85259870dc4364954018041561882
md5sum = 8ec70e6276daaed240faa5059514929c
mode = 0644
[template-slave-list]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/templates/apache-custom-slave-list.cfg.in
md5sum = 5c49a66a0fe4b92ab21ef7148ea4e5ff
md5sum = c896e60c95ca387a75a163d817155d98
mode = 640
[template-slave-configuration]
......@@ -135,7 +135,7 @@ mode = 640
[template-default-slave-virtualhost]
recipe = slapos.recipe.build:download
url = ${:_profile_base_location_}/templates/default-virtualhost.conf.in
md5sum = ac845c0fa3835832307a0e7323cb339d
md5sum = 2241f952bc1072af77e87a6d5bb8b4f9
mode = 640
[template-log-access]
......
......@@ -168,7 +168,8 @@ extra-context =
key local_ipv4 instance-parameter:ipv4-random
key cache_port apache-configuration:cache-port
raw empty_template ${template-empty:target}
raw template_slave_configuration ${template-slave-configuration:target}
raw template_custom_slave_configuration ${template-slave-configuration:target}
raw template_default_slave_configuration ${template-default-slave-virtualhost:target}
raw template_rewrite_cached ${template-rewrite-cached:target}
raw software_type single-custom-personal
section logrotate_dict logrotate
......
......@@ -61,6 +61,17 @@ sla-{{ parameter }} = {{ slapparameter_dict.pop( sla_key + parameter ) }}
{% endif -%}
{% endfor -%}
{% set authorized_slave_string = slapparameter_dict.pop('-frontend-authorized-slave-string', '') -%}
{% set authorized_slave_list = [] %}
{% set rejected_slave_list = [] %}
{% for slave in slave_instance_list %}
{% if not (slave.has_key('apache_custom_http') and not slave.get('slave_reference') in authorized_slave_string) %}
{% do authorized_slave_list.append(slave) %}
{% else %}
{% do rejected_slave_list.append(slave.get('slave_reference')) %}
{% endif %}
{% endfor -%}
[replicate]
<= slap-connection
recipe = slapos.cookbook:requestoptional
......@@ -76,13 +87,16 @@ config = {{ ' '.join(slapparameter_dict.keys()) + ' ' + slave_list_name }}
{% for parameter, value in slapparameter_dict.iteritems() -%}
config-{{parameter}} = {{ value }}
{% endfor -%}
config-{{ slave_list_name }} = {{ json_module.dumps(slave_instance_list) }}
config-{{ slave_list_name }} = {{ json_module.dumps(authorized_slave_list) }}
connection-monitor_url =
[publish-information]
recipe = slapos.cookbook:publish
domain = {{ slapparameter_dict.get('domain') }}
slave-amount = {{ slave_instance_list | length }}
accepted-slave-amount = {{ authorized_slave_list | length }}
rejected-slave-amount = {{ rejected_slave_list | length }}
rejected-slave-list = {{ json_module.dumps(rejected_slave_list) }}
{% for frontend in frontend_section_list %}
{{ frontend }}-monitor-url = {{ '${' + frontend + ':connection-monitor_url}' }}
{% endfor -%}
......
{
"type": "object",
"$schema": "http://json-schema.org/draft-04/schema",
"title": "Input Parameters",
"properties": {
"url": {
"title": "Backend URL",
"description": "Url of the backend",
"type": "string"
},
"custom_domain": {
"title": "Custom Domain",
"description": "Custom Domain to use for the website",
"type": "string",
"pattern": "^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$"
},
"type": {
"title": "Backend Type",
"description": "Type of the backend",
"type": "string",
"default": "",
"enum": ["", "zope"]
},
"path": {
"title": "Backend Path",
"description": "Path to proxy to in the backend",
"type": "string",
"default": ""
},
"ssl_crt": {
"title": "SSL Certificate",
"description": "SSL Certificate",
"type": "string",
"default": ""
},
"ssl_key": {
"title": "SSL Key",
"description": "SSL Key",
"type": "string",
"default": ""
},
"ssl_ca_crt": {
"title": "SSL Certificate Authority's Certificate",
"description": "SSL Key",
"type": "string",
"default": ""
},
"https-only": {
"title": "HTTPS Only",
"description": "If set to true, http request are redirect to https",
"type": "string",
"default": "false",
"enum": ["false", "true"]
},
"enable_cache": {
"title": "Enable Cache",
"description": "If set to true, the cache is used",
"type": "string",
"default": "false",
"enum": ["false", "true"]
},
"apache_custom_http": {
"title": "HTTP configuration",
"description": "Raw http configuration in python template format. Your site will be rejected if you use it without notification and approval of the frontend adminastrator",
"default": "",
"type": "string"
},
"apache_custom_https": {
"title": "HTTPS configuration",
"description": "Raw https configuration in python template format. Your site will be rejected if you use it without notification and approval of the frontend adminastrator",
"default": "",
"type": "string"
}
}
}
......@@ -5,53 +5,52 @@ extends = common.cfg
Jinja2 = 2.7.3
MarkupSafe = 0.23
PyRSS2Gen = 1.1
Werkzeug = 0.9.4
apache-libcloud = 0.15.0
Werkzeug = 0.9.6
apache-libcloud = 0.15.1
async = 0.6.1
buildout-versions = 1.7
cffi = 0.8.2
cffi = 0.8.6
cmd2 = 0.6.7
cns.recipe.symlink = 0.2.3
collective.recipe.template = 1.11
cryptography = 0.4
cryptography = 0.5.2
gitdb = 0.5.4
hexagonit.recipe.cmmi = 2.0
inotifyx = 0.2.0-1
itsdangerous = 0.24
lxml = 3.3.5
meld3 = 1.0.0
mr.developer = 1.30
netaddr = 0.7.11
netaddr = 0.7.12
plone.recipe.command = 1.1
pyOpenSSL = 0.14
pycrypto = 2.6.1
pyparsing = 2.0.2
pytz = 2014.3
pytz = 2014.4
rdiff-backup = 1.0.5
six = 1.7.3
slapos.cookbook = 0.87
slapos.recipe.build = 0.12
slapos.recipe.cmmi = 0.2
slapos.recipe.template = 2.5
slapos.toolbox = 0.39.2
slapos.toolbox = 0.39.3
smmap = 0.8.2
stevedore = 0.15
# Required by:
# slapos.core==1.0.5
# slapos.toolbox==0.39.2
# slapos.core==1.1.2
# slapos.toolbox==0.39.3
Flask = 0.10.1
# Required by:
# slapos.toolbox==0.39.2
# slapos.toolbox==0.39.3
GitPython = 0.3.2.RC1
# Required by:
# slapos.toolbox==0.39.2
# slapos.toolbox==0.39.3
atomize = 0.2.0
# Required by:
# slapos.core==1.0.5
# slapos.core==1.1.2
cliff = 1.6.1
# Required by:
......@@ -59,7 +58,7 @@ cliff = 1.6.1
ecdsa = 0.11
# Required by:
# slapos.toolbox==0.39.2
# slapos.toolbox==0.39.3
feedparser = 5.1.3
# Required by:
......@@ -67,39 +66,40 @@ feedparser = 5.1.3
lock-file = 2.0
# Required by:
# slapos.core==1.0.5
# slapos.core==1.1.2
netifaces = 0.10.4
# Required by:
# slapos.toolbox==0.39.2
# slapos.toolbox==0.39.3
paramiko = 1.14.0
# Required by:
# slapos.toolbox==0.39.2
# slapos.core==1.1.2
# slapos.toolbox==0.39.3
psutil = 2.1.1
# Required by:
# cffi==0.8.2
# cffi==0.8.6
pycparser = 2.10
# Required by:
# slapos.core==1.0.5
# slapos.core==1.1.2
requests = 2.3.0
# Required by:
# slapos.toolbox==0.39.2
slapos.core = 1.0.5
# slapos.toolbox==0.39.3
slapos.core = 1.1.2
# Required by:
# slapos.core==1.0.5
# slapos.core==1.1.2
supervisor = 3.0
# Required by:
# slapos.toolbox==0.39.2
# slapos.toolbox==0.39.3
xml-marshaller = 0.9.7
# Required by:
# slapos.core==1.0.5
# slapos.core==1.1.2
zope.interface = 4.1.1
[networkcache]
......
......@@ -3,6 +3,7 @@
{% set cached_server_dict = {} -%}
{% set part_list = [] -%}
{% set cache_access = "http://%s:%s" % (local_ipv4, cache_port) -%}
{% set TRUE_VALUES = ['y', 'yes', '1', 'true'] -%}
{% set generic_instance_parameter_dict = {'cache_access': cache_access,} -%}
{% set slave_log_dict = {} -%}
{% if extra_slave_instance_list -%}
......@@ -29,8 +30,11 @@ context =
{% set slave_reference = slave_instance.get('slave_reference') -%}
{% set slave_section_title = 'dynamic-template-slave-instance-%s' % slave_reference -%}
{% set slave_parameter_dict = generic_instance_parameter_dict.copy() -%}
{% set slave_publish_dict = {} -%}
{% do part_list.append(slave_section_title) -%}
############################
#### Set Slave Log Directory and access
{% set slave_directory_section = slave_reference + "-directory" -%}
{% set slave_log_folder = logrotate_dict.get('backup') + '/' + slave_reference + "-logs" -%}
......@@ -41,6 +45,8 @@ log-folder = {{slave_log_folder}}
# Set Up log files
{% do slave_parameter_dict.__setitem__('access_log', '/'.join([apache_log_directory, '%s_access_log' % slave_reference])) -%}
{% do slave_parameter_dict.__setitem__('error_log', '/'.join([apache_log_directory, '%s_error_log' % slave_reference])) -%}
{% do slave_instance.__setitem__('access_log', slave_parameter_dict.get('access_log')) -%}
{% do slave_instance.__setitem__('error_log', slave_parameter_dict.get('error_log')) -%}
# Set slave logrotate entry
{% set slave_logrotate_section = slave_reference + "-logs" -%}
......@@ -85,17 +91,11 @@ command = {{frontend_configuration.get('apache-directory')}}/bin/htpasswd -cb ${
# Add slave log directory to the slave log access dict
{% do slave_log_dict.__setitem__(slave_reference, slave_log_folder) %}
# Set up apache configuration file for slave
[{{ slave_section_title }}]
< = jinja2-template-base
template = {{ template_slave_configuration }}
filename = {{ '%s.conf' % slave_reference }}
extra-context =
key apache_custom_https {{ 'slave-instance-%s-configuration:apache_custom_https' % slave_reference }}
key apache_custom_http {{ 'slave-instance-%s-configuration:apache_custom_http' % slave_reference }}
raw https_port {{ https_port }}
raw http_port {{ http_port }}
{{ '\n' }}
{% set slave_log_access_url = 'https://' + slave_reference + ':${'+ slave_password_section +':passwd}@[' + frontend_configuration.get('apache-ipv6') + ']:' + frontend_configuration.get('apache-https-port') + '/' + slave_reference.lower() + '/' %}
{% do slave_publish_dict.__setitem__('log-access', slave_log_access_url) %}
############################
#### Set Slave Certificates if needed
# Set ssl certificates for each slave
{% for cert_name in ('ssl_key', 'ssl_crt', 'ssl_ca_crt', 'ssl_csr')-%}
......@@ -104,6 +104,7 @@ extra-context =
{% set cert_file = '/'.join([custom_ssl_directory, cert_title.replace('-','.')]) -%}
{% do part_list.append(cert_title) -%}
{% do slave_parameter_dict.__setitem__(cert_name, cert_file) -%}
{% do slave_instance.__setitem__('path_to_' + cert_name, cert_file) -%}
# Store certificates on fs
[{{ cert_title }}]
< = jinja2-template-base
......@@ -117,31 +118,95 @@ value = {{ dumps(slave_instance.get(cert_name)) }}
{% endif -%}
{% endfor -%}
############################
#### Set Slave Configuration
{% if slave_instance.has_key('apache_custom_http') %}
#### Set Configuration for custom slaves
# Set up apache configuration file for slave
[{{ slave_section_title }}]
< = jinja2-template-base
template = {{ template_custom_slave_configuration }}
filename = {{ '%s.conf' % slave_reference }}
extra-context =
key apache_custom_https {{ 'slave-instance-%s-configuration:apache_custom_https' % slave_reference }}
key apache_custom_http {{ 'slave-instance-%s-configuration:apache_custom_http' % slave_reference }}
raw https_port {{ https_port }}
raw http_port {{ http_port }}
{{ '\n' }}
# Set apache configuration value for slave
[{{ ('slave-instance-%s-configuration' % slave_reference) }}]
{% set apache_custom_http = ((slave_instance.get('apache_custom_http', '')) % slave_parameter_dict) -%}
{% set apache_custom_https = ((slave_instance.get('apache_custom_https', '')) % slave_parameter_dict) -%}
{% set apache_custom_http = ((slave_instance.get('apache_custom_http', '')) % slave_parameter_dict) -%}
{% set apache_custom_https = ((slave_instance.get('apache_custom_https', '')) % slave_parameter_dict) -%}
apache_custom_http = {{ dumps(apache_custom_http) }}
apache_custom_https = {{ dumps(apache_custom_https) }}
{{ '\n' }}
# The slave use cache
{% if 'enable_cache' in slave_instance and 'url' in slave_instance and 'domain' in slave_instance -%}
{% do cached_server_dict.__setitem__(slave_instance.get('domain'), slave_instance.get('url')) -%}
{% if 'enable_cache' in slave_instance and 'url' in slave_instance and 'domain' in slave_instance -%}
{% do cached_server_dict.__setitem__(slave_instance.get('domain'), slave_instance.get('url')) -%}
{% endif -%}
# Publish information
{% do slave_publish_dict.update(**{'slave-reference':slave_instance.get('slave_reference'), 'public-ipv4':public_ipv4, 'log-access': slave_log_access_url}) %}
{% else %}
#### Set Configuration for default slaves
# Set slave domain if none was defined
{% if slave_instance.get('custom_domain', None) == None -%}
{% do slave_instance.__setitem__('custom_domain', "%s.%s" % (slave_instance.get('slave_reference').replace("-", "").lower(), slapparameter_dict.get('domain'))) -%}
{% endif -%}
# The slave use cache
# Next line is forbidden and people who copy it will be hanged short
{% set enable_cache = ('' ~ slave_instance.get('enable_cache', '')).lower() in TRUE_VALUES -%}
{% if enable_cache -%}
{% do cached_server_dict.__setitem__(slave_instance.get('custom_domain'), slave_instance.get('url')) -%}
{% do slave_instance.__setitem__('url', cache_access) -%}
{% endif -%}
{% do part_list.append(slave_section_title) -%}
[{{ ('slave-instance-%s-configuration' % slave_reference) }}]
{% for key, value in slave_instance.iteritems() -%}
{{ key }} = {{ dumps(value) }}
{% endfor %}
# Set up slave configuration file
[{{ slave_section_title }}]
< = jinja2-template-base
template = {{ template_default_slave_configuration }}
filename = {{ '%s.conf' % slave_reference }}
extensions = jinja2.ext.do
extra-context =
section slave_parameter {{ 'slave-instance-%s-configuration' % slave_reference }}
raw https_port {{ https_port }}
raw http_port {{ http_port }}
{{ '\n' }}
{% do slave_publish_dict.update(**{'slave-reference':slave_instance.get('slave_reference'), 'public-ipv4':public_ipv4, 'domain':slave_instance.get('custom_domain'), 'url':"http://%s" % slave_instance.get('custom_domain'), 'site_url':"http://%s" % slave_instance.get('custom_domain')}) %}
{% endif -%}
############################
#### Publish Slave Information
# Publish slave information
{% set slave_log_access_url = 'https://' + slave_reference + ':${'+ slave_password_section +':passwd}@[' + frontend_configuration.get('apache-ipv6') + ']:' + frontend_configuration.get('apache-https-port') + '/' + slave_reference.lower() + '/' %}
{% if not extra_slave_instance_list -%}
{% set publish_section_title = 'publish-%s-connection-information' % slave_instance.get('slave_reference') -%}
{% do part_list.append(publish_section_title) -%}
[{{ publish_section_title }}]
recipe = slapos.cookbook:publish
public-ipv4 = {{ public_ipv4 }}
log-access = {{ slave_log_access_url }}
-slave-reference = {{ slave_instance.get('slave_reference') }}
{% for key, value in slave_publish_dict.iteritems() %}
{{ key }} = {{ value }}
{% endfor %}
{% else -%}
{% do slave_instance_information_list.append({'slave-reference':slave_instance.get('slave_reference'), 'public-ipv4':public_ipv4, 'log-access': slave_log_access_url}) -%}
{% do slave_instance_information_list.append(slave_publish_dict) -%}
{% endif -%}
{% endfor -%}
......
{% set TRUE_VALUES = ['y', 'yes', '1', 'true'] -%}
<VirtualHost *:{{ https_port }}>
ServerName {{ slave_parameter.get('domain') }}
ServerAlias {{ slave_parameter.get('domain') }}
ServerName {{ slave_parameter.get('custom_domain') }}
ServerAlias {{ slave_parameter.get('custom_domain') }}
SSLEngine on
SSLProxyEngine on
......@@ -23,10 +23,10 @@
# One Slave two logs
ErrorLog "{{ error_log }}"
ErrorLog "{{ slave_parameter.get('error_log') }}"
LogLevel info
LogFormat "%h %l %{REMOTE_USER}i %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined
CustomLog "{{ access_log }}" combined
CustomLog "{{ slave_parameter.get('access_log') }}" combined
# Rewrite part
ProxyVia On
......@@ -38,15 +38,15 @@
# First, we check if we have a zope backend server
# If so, let's use Virtual Host Daemon rewrite
# We suppose that Apache listens to 443 (even indirectly thanks to things like iptables)
RewriteRule ^/(.*)$ {{ slave_parameter.get('url', '') }}/VirtualHostBase/https/{{ slave_parameter.get('domain', '') }}:443/{{ slave_parameter.get('path', '') }}/VirtualHostRoot/$1 [L,P]
RewriteRule ^/(.*)$ {{ slave_parameter.get('url', '') }}/VirtualHostBase/https/{{ slave_parameter.get('custom_domain', '') }}:443/{{ slave_parameter.get('path', '') }}/VirtualHostRoot/$1 [L,P]
{% else -%}
RewriteRule ^/(.*)$ {{ slave_parameter.get('url', '') }}/$1 [L,P]
{% endif -%}
</VirtualHost>
<VirtualHost *:{{ http_port }}>
ServerName {{ slave_parameter.get('domain') }}
ServerAlias {{ slave_parameter.get('domain') }}
ServerName {{ slave_parameter.get('custom_domain') }}
ServerAlias {{ slave_parameter.get('custom_domain') }}
SSLProxyEngine on
# Rewrite part
ProxyVia On
......@@ -55,10 +55,10 @@
RewriteEngine On
# One Slave two logs
ErrorLog "{{ error_log }}"
ErrorLog "{{ slave_parameter.get('error_log') }}"
LogLevel info
LogFormat "%h %l %{REMOTE_USER}i %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D" combined
CustomLog "{{ access_log }}" combined
CustomLog "{{ slave_parameter.get('access_log') }}" combined
# Remove "Secure" from cookies, as backend may be https
Header edit Set-Cookie "(?i)^(.+);secure$" "$1"
......@@ -75,7 +75,7 @@
# First, we check if we have a zope backend server
# If so, let's use Virtual Host Daemon rewrite
# We suppose that Apache listens to 80 (even indirectly thanks to things like iptables)
RewriteRule ^/(.*)$ {{ slave_parameter.get('url', '') }}/VirtualHostBase/http/{{ slave_parameter.get('domain', '') }}:80/{{ slave_parameter.get('path', '') }}/VirtualHostRoot/$1 [L,P]
RewriteRule ^/(.*)$ {{ slave_parameter.get('url', '') }}/VirtualHostBase/http/{{ slave_parameter.get('custom_domain', '') }}:80/{{ slave_parameter.get('path', '') }}/VirtualHostRoot/$1 [L,P]
{% else -%}
RewriteRule ^/(.*)$ {{ slave_parameter.get('url', '') }}/$1 [L,P]
{% endif -%}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment