Commit 7386772a authored by Jérome Perrin's avatar Jérome Perrin

Update Release Candidate

parents d5e28481 93374140
......@@ -81,36 +81,16 @@ patches =
[golang14:platform.machine() == 'aarch64']
setarch = setarch arm
[golang1.13]
<= golang-common-pre-1.19
url = https://go.dev/dl/go1.13.15.src.tar.gz
md5sum = 4f4af14d88352a62761a9dcedf863ac0
# go1.13 needs go1.4 to bootstrap
environment-extra =
GOROOT_BOOTSTRAP=${golang14:location}
patches +=
https://lab.nexedi.com/kirr/go/commit/a724597faa7506c74f56208bed5432abc0935264.patch#2465a69e70bef37f34a5b2b7c93ebbb7
https://lab.nexedi.com/kirr/go/commit/d5cb71524482154b23c9e582ff0be0fc6d2856b2.patch#cc8c7524d12ebd2fc9334063901a9054
https://lab.nexedi.com/kirr/go/commit/6dfc2256e2cd1ad46f3a5e9da85af6a70be7fba3.patch#fa9de83838b198490abec0a6a924ad8d
https://lab.nexedi.com/kirr/go/commit/28fbdd01d6c89db1e81e12bd05910fdec4c3b9b3.patch#c75e2eb7aaaeffdd3c507ec1beacd3ca
[golang1.15]
<= golang-common-pre-1.19
url = https://go.dev/dl/go1.15.15.src.tar.gz
md5sum = 05fedd8289291eb2d91cd0c092b41aaa
# go1.1 needs go1.4 to bootstrap
environment-extra =
GOROOT_BOOTSTRAP=${golang14:location}
[golang1.16]
<= golang-common-pre-1.19
url = https://golang.org/dl/go1.16.15.src.tar.gz
md5sum = fda3664a020a79fabe2d0d5d9d10861f
patches +=
# Backport of https://github.com/golang/go/commit/d1d93129506c78cc8ee25644384286822d93c81a
${:_profile_base_location_}/crypto-tls-fix-Config.Time-in-tests-using-expired-ce-go-1-15.patch#26435b76ad4d6c07e8ed162b8f19b55a
# go1.16 needs go1.4 to bootstrap
# go1.15 needs go1.4 to bootstrap
environment-extra =
GOROOT_BOOTSTRAP=${golang14:location}
......@@ -119,30 +99,14 @@ environment-extra =
url = https://golang.org/dl/go1.17.13.src.tar.gz
md5sum = 4476707f05cf6915ec1173038dc357a9
# go1.17 needs go1.4 to bootstrap
environment-extra =
GOROOT_BOOTSTRAP=${golang14:location}
[golang1.18]
<= golang-common-pre-1.19
url = https://golang.org/dl/go1.18.10.src.tar.gz
md5sum = 002b4daec72ab396dc023a5ec445a68a
patches +=
# Backport of https://github.com/golang/go/commit/d1d93129506c78cc8ee25644384286822d93c81a
${:_profile_base_location_}/crypto-tls-fix-Config.Time-in-tests-using-expired-ce-go-1-15.patch#26435b76ad4d6c07e8ed162b8f19b55a
# go1.18 needs go1.4 or go1.17 to bootstrap.
# We use go1.4 to reduce the amount of components which
# need to be installed. See following note for rationale:
# https://lab.nexedi.com/nexedi/slapos/merge_requests/1305#note_174936
# go1.17 needs go1.4 to bootstrap
environment-extra =
GOROOT_BOOTSTRAP=${golang14:location}
[golang1.20]
<= golang-common-pre-1.21
url = https://go.dev/dl/go1.20.14.src.tar.gz
md5sum = 71103194b2ca39cc9cb943df9aa6154c
# go1.20 requires go1.17.13 to bootstrap (see https://go.dev/doc/go1.20#bootstrap)
environment-extra =
GOROOT_BOOTSTRAP=${golang1.17:location}
[golang1.21]
<= golang-common
url = https://go.dev/dl/go1.21.13.src.tar.gz
......@@ -157,6 +121,8 @@ patches +=
# https://github.com/golang/go/commit/9f03e8367d85d75675b2f2e90873e3293799d8aa
${:_profile_base_location_}/os-skip-Chown-tests-for-auxiliary-groups-that-fail-d.patch#81b7f75786d9024049c26d1663b79ba4
${:_profile_base_location_}/skip-unshare-mount-test.patch#325446d5135452e8685e95ab99c13a51
# Backport of https://github.com/golang/go/commit/d1d93129506c78cc8ee25644384286822d93c81a
${:_profile_base_location_}/crypto-tls-fix-Config.Time-in-tests-using-expired-ce-go-1-21.patch#8e30a06c854a9654e5b789c887453d64
# ---- infrastructure to build Go workspaces / projects ----
......
......@@ -15,7 +15,7 @@
# not need these here).
[instance-caucased]
filename = instance-caucased.cfg.jinja2
md5sum = 2e7c1d8c553b398dd68c875a9fa38cdb
md5sum = 361eea7fa660f550fd63afa53689142f
[instance]
filename = instance.cfg.jinja2
......
......@@ -13,6 +13,12 @@
"external-url": {
"$comment": "(deprecated)"
},
"external-netloc": {
"title": "External netloc",
"description": "When provided, this is used as netloc in the CRL distribution point extension in issued certificates. Setting up that domain and routing accesses to the IP and ports caucased binds to is outside the scope of this software-release. Otherwise, instead of the caucased bind IP and port are used (preventing caucased relocation).",
"type": "string",
"format": "uri"
},
"service-auto-approve-amount": {
"title": "Number of service certificate requests to automatically approve",
"description": "Once that number has been reached, a user must validate further requests. Renewals do not count toward this number. Cannot be changed once set.",
......@@ -25,9 +31,15 @@
"type": "integer",
"default": 1
},
"backup-period": {
"title": "Backup period",
"description": "Number of days between consecutive backups (0 to disable backup creation).",
"type": "number",
"default": 0
},
"key-length": {
"title": "Key length",
"description": "Size, in bits, of the SSL key generated to authenticate users.",
"description": "Size, in bits, of the SSL CAs (users and services) private keys generated by caucased.",
"default": 2048,
"type": "integer"
}
......
......@@ -3,7 +3,8 @@
"description": "Values returned by Caucase instantiation",
"properties": {
"url": {
"description": "Caucase URL",
"title": "Caucase URL",
"description": "This contains the plain IP and port caucasd binds to, indepencently from external-netloc parameter.",
"type": "string"
}
},
......
{% import "caucase" as caucase with context %}
{% set netloc = '[' ~ (ipv6_set | list)[0] ~ ']:' ~ slapparameter_dict.get('base-port', 8009) -%}
{% set bind = (ipv6_set | list)[0] -%}
{% set base_port = slapparameter_dict.get('base-port', 8009) -%}
{% set default_netloc = '[' ~ bind ~ ']:' ~ base_port -%}
{% set backup_period = slapparameter_dict.get('backup-period', 0) -%}
[directory]
recipe = slapos.cookbook:mkdirectory
etc = ${buildout:directory}/etc
service-on-watch = ${:etc}/service
srv = ${buildout:directory}/srv
srv-backup-caucased = ${:srv}/backup/caucased
tmp = ${buildout:directory}/tmp
{{ caucase.caucased(
......@@ -13,16 +17,20 @@ tmp = ${buildout:directory}/tmp
buildout_bin_directory=bin_directory,
caucased_path='${directory:service-on-watch}/caucased',
data_dir='${directory:srv}/caucased',
netloc=netloc,
netloc=slapparameter_dict.get('external-netloc', default_netloc),
tmp='%{directory:tmp}',
service_auto_approve_count=slapparameter_dict.get('service-auto-approve-amount', 0),
user_auto_approve_count=slapparameter_dict.get('user-auto-approve-amount', 1),
key_len=slapparameter_dict.get('key-length', 2048),
bind=bind,
base_port=base_port,
backup_dir='${directory:srv-backup-caucased}' if backup_period else None,
backup_period=backup_period,
) }}
[publish]
recipe = slapos.cookbook:publish.serialised
url = {{ dumps('http://' ~ netloc) }}
url = {{ dumps('http://' ~ default_netloc) }}
[buildout]
parts =
......
......@@ -19,15 +19,15 @@ md5sum = bf0a70140e4775efaacdd5fe03ac3b62
[template-kvm]
filename = instance-kvm.cfg.jinja2
md5sum = 20554e40f9fe666839ef86ff8216cebf
md5sum = 957f3a3c8e5f9a905026cc01a25b326e
[template-kvm-cluster]
filename = instance-kvm-cluster.cfg.jinja2.in
md5sum = 8ce14c5ae114dcfa6e9aff0511b218d4
md5sum = 09844c32845d8d83718841cdc41dc24a
[template-kvm-resilient]
filename = instance-kvm-resilient.cfg.jinja2
md5sum = 839fc16c112d3b87e2dbd2e382e326de
md5sum = 12d46a7d45f00e091d18e6d4552d0973
[template-kvm-import]
filename = instance-kvm-import.cfg.jinja2.in
......
......@@ -27,18 +27,18 @@
},
"frontend-additional-instance-guid": {
"title": "Additional Frontend Instance ID",
"description": "Unique identifier of the additional frontend instance, like \"SOFTINST-11031\", if empty won't be requested.",
"description": "Unique identifier of the additional frontend instance, like \"SOFTINST-11031\". Note that if the 3 options 'frontend-additional-instance-guid', 'frontend-additional-software-type' and 'frontend-additional-software-url' are empty, the additional frontend won't be requested.",
"type": "string"
},
"frontend-additional-software-type": {
"title": "Additional Frontend Software Type",
"description": "Type of the frontend instance, like \"default\".",
"description": "Type of the additional frontend instance, like \"default\". Note that if the 3 options 'frontend-additional-instance-guid', 'frontend-additional-software-type' and 'frontend-additional-software-url' are empty, the additional frontend won't be requested.",
"type": "string",
"default": "default"
},
"frontend-additional-software-url": {
"title": "Additional Frontend Software URL",
"description": "Software Release URL of the frontend instance, like \"http://example.com/path/to/software.cfg\".",
"description": "Software Release URL of the additional frontend instance, like \"http://example.com/path/to/software.cfg\". Note that if the 3 options 'frontend-additional-instance-guid', 'frontend-additional-software-type' and 'frontend-additional-software-url' are empty, the additional frontend won't be requested.",
"type": "string",
"format": "uri",
"default": "http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg"
......
......@@ -8,6 +8,7 @@
{% set slave_frontend_iguid = slave_frontend_dict.get('instance-guid', '') -%}
{% set WEBSOCKET_FRONTEND_DEFAULT_SR = 'http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg' %}
{% set WEBSOCKET_FRONTEND_DEFAULT_ST = 'default' %}
{% set additional_frontend = frontend_dict.get('frontend-additional-instance-guid') or frontend_dict.get('frontend-additional-software-type') or frontend_dict.get('frontend-additional-software-url')%}
{% set kvm_instance_dict = {} -%}
{% set kvm_hostname_list = [] -%}
{% set monitor_base_url_dict = {} -%}
......@@ -49,7 +50,7 @@ config-use-ipv6 = {{ dumps(slapparameter_dict.get('use-ipv6', False)) }}
recipe = slapos.cookbook:request.serialised
software-type = kvm
name = {{ instance_name }}
{% if kvm_parameter_dict.get('sticky-computer', '') -%}
{% if kvm_parameter_dict.get('sticky-compute-node', '') -%}
sla-computer_guid = ${slap-connection:computer-id}
{% else -%}
sla-computer_guid = {{ dumps(kvm_parameter_dict.get('computer-guid', '')) }}
......@@ -62,14 +63,16 @@ state = stopped
{% endif -%}
config-frontend-instance-name = {{ instance_name ~ ' VNC Real Frontend' }}
{{ setconfig('frontend-software-url', frontend_dict.get('frontend-software-url', WEBSOCKET_FRONTEND_DEFAULT_SR)) }}
{{ setconfig('frontend-software-type', frontend_dict.get('frontend-software-type', WEBSOCKET_FRONTEND_DEFAULT_ST)) }}
{{ setconfig('frontend-instance-guid', frontend_dict.get('frontend-instance-guid', '')) }}
{{ setconfig('frontend-software-url', frontend_dict.get('frontend-software-url')) }}
{{ setconfig('frontend-software-type', frontend_dict.get('frontend-software-type')) }}
{{ setconfig('frontend-instance-guid', frontend_dict.get('frontend-instance-guid')) }}
{% if additional_frontend -%}
config-frontend-additional-instance-name = {{ instance_name ~ ' VNC Real Frontend Additional' }}
{{ setconfig('frontend-additional-software-url', frontend_dict.get('frontend-additional-software-url', WEBSOCKET_FRONTEND_DEFAULT_SR)) }}
{{ setconfig('frontend-additional-software-type', frontend_dict.get('frontend-additional-software-type', WEBSOCKET_FRONTEND_DEFAULT_ST)) }}
{{ setconfig('frontend-additional-instance-guid', frontend_dict.get('frontend-additional-instance-guid', '')) }}
{{ setconfig('frontend-additional-software-url', frontend_dict.get('frontend-additional-software-url')) }}
{{ setconfig('frontend-additional-software-type', frontend_dict.get('frontend-additional-software-type')) }}
{{ setconfig('frontend-additional-instance-guid', frontend_dict.get('frontend-additional-instance-guid')) }}
{% endif -%}
config-name = {{ instance_name }}
{% if slapparameter_dict.get('authorized-keys', []) -%}
......@@ -157,7 +160,7 @@ sla-fw_restricted_access = {{ dumps(slapparameter_dict.get('fw-restricted-access
return =
url
{% if frontend_dict.get('frontend-additional-instance-guid') %}
{% if additional_frontend %}
url-additional
{% endif %}
backend-url
......@@ -185,7 +188,7 @@ return =
{% do monitor_base_url_dict.__setitem__(instance_name, '${' ~ section ~ ':connection-monitor-base-url}') -%}
{% do publish_dict.__setitem__(instance_name ~ '-backend-url', '${' ~ section ~ ':connection-backend-url}') -%}
{% do publish_dict.__setitem__(instance_name ~ '-url', '${' ~ section ~ ':connection-url}') -%}
{% if frontend_dict.get('frontend-additional-instance-guid') %}
{% if additional_frontend %}
{% do publish_dict.__setitem__(instance_name ~ '-url-additional', '${' ~ section ~ ':connection-url-additional}') -%}
{% endif %}
{% do kvm_instance_dict.__setitem__(instance_name, (use_nat, nat_rules_list)) -%}
......
......@@ -269,18 +269,18 @@
},
"frontend-additional-instance-guid": {
"title": "Additional Frontend Instance ID",
"description": "Unique identifier of the additional frontend instance, like \"SOFTINST-11031\", if empty won't be requested.",
"description": "Unique identifier of the additional frontend instance, like \"SOFTINST-11031\". Note that if the 3 options 'frontend-additional-instance-guid', 'frontend-additional-software-type' and 'frontend-additional-software-url' are empty, the additional frontend won't be requested.",
"type": "string"
},
"frontend-additional-software-type": {
"title": "Additional Frontend Software Type",
"description": "Type of the frontend instance, like \"frontend\".",
"description": "Type of the additional frontend instance, like \"frontend\". Note that if the 3 options 'frontend-additional-instance-guid', 'frontend-additional-software-type' and 'frontend-additional-software-url' are empty, the additional frontend won't be requested.",
"type": "string",
"default": "default"
},
"frontend-additional-software-url": {
"title": "Additional Frontend Software URL",
"description": "Software Release URL of the frontend instance, like \"http://example.com/path/to/software.cfg\".",
"description": "Software Release URL of the additional frontend instance, like \"http://example.com/path/to/software.cfg\". Note that if the 3 options 'frontend-additional-instance-guid', 'frontend-additional-software-type' and 'frontend-additional-software-url' are empty, the additional frontend won't be requested.",
"type": "string",
"format": "uri",
"default": "http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg"
......
......@@ -13,7 +13,7 @@
{% set monitor_dict = {'parameter': monitor_parameter, 'return': monitor_return} -%}
{% endif -%}
{% set monitor_interface_url = slapparameter_dict.pop('monitor-interface-url', 'https://monitor.app.officejs.com') -%}
{% set additional_frontend = (slapparameter_dict.get('frontend-additional-instance-guid', '').strip() != '') %}
{% set additional_frontend = slapparameter_dict.get('frontend-additional-instance-guid') or slapparameter_dict.get('frontend-additional-software-type') or slapparameter_dict.get('frontend-additional-software-url')%}
[buildout]
eggs-directory = {{ eggs_directory }}
......
......@@ -5,7 +5,7 @@
{% do slapparameter_dict.__setitem__(k, '') %}
{% endif %}
{% endfor %}
{% set additional_frontend = slapparameter_dict.get('frontend-additional-instance-guid') %}
{% set additional_frontend = slapparameter_dict.get('frontend-additional-instance-guid') or slapparameter_dict.get('frontend-additional-software-type') or slapparameter_dict.get('frontend-additional-software-url')%}
{% set enable_http = slapparameter_dict.get('enable-http-server', False) -%}
{% set use_tap = slapparameter_dict.get('use-tap', True) -%}
{% set use_nat = slapparameter_dict.get('use-nat', True) -%}
......@@ -773,7 +773,9 @@ config-content-to-receive = {{ dumps('RFB 003.008\n'.encode()) }}
software-url = ${slap-parameter:frontend-additional-software-url}
software-type = ${slap-parameter:frontend-additional-software-type}
name = ${slap-parameter:frontend-additional-instance-name}
{% if slapparameter_dict.get('frontend-additional-instance-guid') %}
sla-instance_guid = ${slap-parameter:frontend-additional-instance-guid}
{% endif %}
[frontend-additional-promise]
<= monitor-promise-base
......
......@@ -629,6 +629,13 @@ class TestAccessDefaultAdditional(MonitorAccessMixin, KVMTestCase):
)
self.assertIn('<title>noVNC</title>', result.text)
@skipUnlessKvm
class TestAccessDefaultAdditionalJson(TestAccessDefaultAdditional):
@classmethod
def getInstanceParameterDict(cls):
return {
'frontend-additional-software-url': 'http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg'
}
@skipUnlessKvm
class TestAccessDefaultAdditionalJson(
......
[instance-profile]
filename = instance.cfg.in
md5sum = 5bff4e422a2b22bec348bc5e4cb2f3b7
md5sum = 1698b0a6bfbc10777ea7e6514c6b4143
......@@ -8,6 +8,7 @@ parts =
directory
publish-connection-parameter
coupler-opc-ua
opc-ua-port-promise
eggs-directory = {{ buildout['eggs-directory'] }}
develop-eggs-directory = {{ buildout['develop-eggs-directory'] }}
......@@ -49,9 +50,16 @@ etc = ${:home}/etc
var = ${:home}/var
script = ${:etc}/run/
service = ${:etc}/service
promise = ${:etc}/promise
[publish-connection-parameter]
recipe = slapos.cookbook:publish
url-ipv6 = opc.tcp://[${instance-parameter:ipv6-random}]:${instance-parameter:configuration.opc_ua_port}
username = ${instance-parameter:configuration.username}
password = ${instance-parameter:configuration.password}
[opc-ua-port-promise]
recipe = slapos.cookbook:check_port_listening
path = ${directory:promise}/${:_buildout_section_name_}
hostname = ${instance-parameter:ipv6-random}
port = ${instance-parameter:configuration.opc_ua_port}
......@@ -15,7 +15,7 @@
[instance-theia]
_update_hash_filename_ = instance-theia.cfg.jinja.in
md5sum = b4e87cff99a8521e6d0b911e3ef35b30
md5sum = 6ea30357440478f3fe5400184da73315
[instance]
_update_hash_filename_ = instance.cfg.in
......
......@@ -56,19 +56,19 @@
},
"additional-frontend-guid": {
"title": "Additional Frontend Instance ID",
"description": "Unique identifier of the additional frontend instance, like \"SOFTINST-11031\", if empty won't be requested.",
"description": "Unique identifier of the additional frontend instance, like \"SOFTINST-11031\". Note that if the 3 options 'additional-frontend-guid', 'additional-frontend-sr-type' and 'additional-frontend-sr' are empty, the additional frontend won't be requested.",
"type": "string"
},
"additional-frontend-sr": {
"title": "Additional Frontend Software URL",
"description": "Software Release URL of the frontend instance, like \"http://example.com/path/to/software.cfg\".",
"description": "Software Release URL of the frontend instance, like \"http://example.com/path/to/software.cfg\". Note that if the 3 options 'additional-frontend-guid', 'additional-frontend-sr-type' and 'additional-frontend-sr' are empty, the additional frontend won't be requested.",
"type": "string",
"format": "uri",
"default": "http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg"
},
"additional-frontend-sr-type": {
"title": "Additional Frontend Software Type",
"description": "Type of the frontend instance, like \"frontend\".",
"description": "Type of the frontend instance, like \"frontend\". Note that if the 3 options 'additional-frontend-guid', 'additional-frontend-sr-type' and 'additional-frontend-sr' are empty, the additional frontend won't be requested.",
"type": "string",
"default": "default"
}
......
# compute additional_frontend before resolving the defaults
{%- set additional_frontend = parameter_dict.get('additional-frontend-guid') or parameter_dict.get('additional-frontend-sr') or parameter_dict.get('additional-frontend-sr-type') %}
{%- set parameter_dict = dict(default_parameter_dict, **parameter_dict) %}
{%- set additional_frontend = parameter_dict['additional-frontend-guid'] %}
{%- set embedded_instance_config = parameter_dict['initial-embedded-instance'] %}
[buildout]
......
......@@ -400,6 +400,12 @@ class TestTheiaFrontend(TheiaTestCase):
resp = requests.get(self.connection_parameters[key], verify=False)
self.assertEqual(requests.codes.unauthorized, resp.status_code)
class TestTheiaFrontendOtherAdditional(TestTheiaFrontend):
@classmethod
def getInstanceParameterDict(cls):
return {
'additional-frontend-sr': 'http://git.erp5.org/gitweb/slapos.git/blob_plain/HEAD:/software/apache-frontend/software.cfg'
}
class TestTheiaForwardFrontendRequestsEnabled(TheiaTestCase):
......
......@@ -35,7 +35,7 @@ url = ${:_profile_base_location_}/${:filename}
depends = ${caucase-jinja2-library-eggs:eggs}
[versions]
caucase = 0.9.14
caucase = 0.9.16
pem = 21.1.0
PyJWT = 2.4.0
......
......@@ -15,4 +15,4 @@
[caucase-jinja2-library]
filename = caucase.jinja2.library
md5sum = a16311ab9a8965ea9020929c7100209b
md5sum = 82c3d836b69aeb39fd4d232745c53da0
......@@ -9,6 +9,9 @@
user_auto_approve_count=1,
key_len=None,
backup_dir=None,
backup_period=None,
bind=None,
base_port=None,
promise=True
) -%}
[{{ prefix }}-directory]
......@@ -28,8 +31,10 @@ command-line = '{{ buildout_bin_directory }}/caucased'
--db '${ {{- prefix }}-directory:data-dir}/caucase.sqlite'
--server-key '${ {{- prefix }}-directory:data-dir}/server.key.pem'
--netloc '{{ netloc }}'
{% if bind != None %}--bind '{{ bind }}' {%- endif %}
{% if base_port != None %}--base-port '{{ base_port }}' {%- endif %}
{% if key_len %}--key-len '{{ key_len }}' {%- endif %}
{% if backup_dir %}--backup-directory {{ backup_dir }} {%- endif %}
{% if backup_dir %}--backup-directory '{{ backup_dir }}' {% if backup_period != None %}--backup-period '{{ float(backup_period) }}' {%- endif %}{%- endif %}
--service-auto-approve-count '{{ service_auto_approve_count }}'
--user-auto-approve-count '{{ user_auto_approve_count }}'
--lock-auto-approve-count
......
......@@ -366,7 +366,7 @@ slapos.rebootstrap = 4.7
slapos.recipe.build = 0.57
slapos.recipe.cmmi = 0.22
slapos.recipe.template = 5.1
slapos.toolbox = 0.142
slapos.toolbox = 0.146
smmap = 5.0.0
sniffio = 1.3.0
sortedcontainers = 2.4.0
......
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