Skip to content
Snippets Groups Projects
Commit f2e09fd8 authored by Jenkins's avatar Jenkins Committed by Gerrit Code Review
Browse files

Merge "Implement Ansible role for Swift"

parents 323523b9 515cf2af
No related branches found
No related tags found
No related merge requests found
Showing
with 470 additions and 34 deletions
...@@ -91,6 +91,10 @@ cinder_api_port: "8776" ...@@ -91,6 +91,10 @@ cinder_api_port: "8776"
memcached_port: "11211" memcached_port: "11211"
swift_proxy_server_port: "8080"
swift_object_server_port: "6000"
swift_account_server_port: "6001"
swift_container_server_port: "6002"
#################### ####################
# Openstack options # Openstack options
...@@ -130,6 +134,7 @@ enable_rabbitmq: "yes" ...@@ -130,6 +134,7 @@ enable_rabbitmq: "yes"
# Additional optional OpenStack services are specified here # Additional optional OpenStack services are specified here
enable_cinder: "no" enable_cinder: "no"
enable_horizon: "yes" enable_horizon: "yes"
enable_swift: "no"
#################### ####################
# RabbitMQ options # RabbitMQ options
......
...@@ -45,6 +45,9 @@ control ...@@ -45,6 +45,9 @@ control
[horizon:children] [horizon:children]
control control
[swift:children]
control
# Additional control implemented here. These groups allow you to control which # Additional control implemented here. These groups allow you to control which
# services run on which hosts at a per-service level. # services run on which hosts at a per-service level.
...@@ -83,7 +86,7 @@ neutron ...@@ -83,7 +86,7 @@ neutron
[neutron-agents:children] [neutron-agents:children]
neutron neutron
#Cinder # Cinder
[cinder-api:children] [cinder-api:children]
cinder cinder
...@@ -95,3 +98,16 @@ cinder ...@@ -95,3 +98,16 @@ cinder
[cinder-volume:children] [cinder-volume:children]
storage storage
# Swift
[swift-proxy-server:children]
swift
[swift-account-server:children]
storage
[swift-container-server:children]
storage
[swift-object-server:children]
storage
...@@ -51,6 +51,9 @@ control ...@@ -51,6 +51,9 @@ control
[horizon:children] [horizon:children]
control control
[swift:children]
control
# Additional control implemented here. These groups allow you to control which # Additional control implemented here. These groups allow you to control which
# services run on which hosts at a per-service level. # services run on which hosts at a per-service level.
...@@ -89,7 +92,7 @@ neutron ...@@ -89,7 +92,7 @@ neutron
[neutron-agents:children] [neutron-agents:children]
neutron neutron
#Cinder # Cinder
[cinder-api:children] [cinder-api:children]
cinder cinder
...@@ -101,3 +104,16 @@ cinder ...@@ -101,3 +104,16 @@ cinder
[cinder-volume:children] [cinder-volume:children]
storage storage
# Swift
[swift-proxy-server:children]
storage
[swift-account-server:children]
storage
[swift-container-server:children]
storage
[swift-object-server:children]
storage
---
project_name: "swift"
####################
# Docker
####################
swift_proxy_server_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-proxy-server"
swift_proxy_server_tag: "{{ openstack_release }}"
swift_proxy_server_image_full: "{{ swift_proxy_server_image }}:{{ swift_proxy_server_tag }}"
swift_data_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-data"
swift_data_tag: "{{ openstack_release }}"
swift_data_image_full: "{{ swift_data_image }}:{{ swift_data_tag }}"
swift_account_server_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-account-server"
swift_account_server_tag: "{{ openstack_release }}"
swift_account_server_image_full: "{{ swift_account_server_image }}:{{ swift_account_server_tag }}"
swift_container_server_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-container-server"
swift_container_server_tag: "{{ openstack_release }}"
swift_container_server_image_full: "{{ swift_container_server_image }}:{{ swift_container_server_tag }}"
swift_object_auditor_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-object-auditor"
swift_object_auditor_tag: "{{ openstack_release }}"
swift_object_auditor_image_full: "{{ swift_object_auditor_image }}:{{ swift_object_auditor_tag }}"
swift_object_expirer_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-object-expirer"
swift_object_expirer_tag: "{{ openstack_release }}"
swift_object_expirer_image_full: "{{ swift_object_expirer_image }}:{{ swift_object_expirer_tag }}"
swift_object_replicator_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-object-replicator"
swift_object_replicator_tag: "{{ openstack_release }}"
swift_object_replicator_image_full: "{{ swift_object_replicator_image }}:{{ swift_object_replicator_tag }}"
swift_object_server_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-object-server"
swift_object_server_tag: "{{ openstack_release }}"
swift_object_server_image_full: "{{ swift_object_server_image }}:{{ swift_object_server_tag }}"
swift_object_updater_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ kolla_install_type }}-swift-object-updater"
swift_object_updater_tag: "{{ openstack_release }}"
swift_object_updater_image_full: "{{ swift_object_updater_image }}:{{ swift_object_updater_tag }}"
####################
# Openstack
####################
swift_public_address: "{{ kolla_external_address }}"
swift_admin_address: "{{ kolla_internal_address }}"
swift_internal_address: "{{ kolla_internal_address }}"
swift_logging_verbose: "{{ openstack_logging_verbose }}"
swift_logging_debug: "{{ openstack_logging_debug }}"
swift_keystone_user: "swift"
swift_admin_tenant_name: "admin"
swift_devices_mount_point: "/srv/node"
openstack_swift_auth: "{'auth_url':'{{ openstack_auth_v2.auth_url }}','username':'{{ openstack_auth_v2.username }}','password':'{{ openstack_auth_v2.password }}','project_name':'{{ openstack_auth_v2.project_name }}'}"
---
dependencies:
- { role: common }
- { role: memcached }
---
- name: Starting Swift data container
docker:
docker_api_version: "{{ docker_api_version }}"
net: host
pull: "{{ docker_pull_policy }}"
restart_policy: "{{ docker_restart_policy }}"
restart_policy_retry: "{{ docker_restart_policy_retry }}"
state: reloaded
registry: "{{ docker_registry }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
insecure_registry: "{{ docker_insecure_registry }}"
name: swift_data
image: "{{ swift_data_image_full }}"
volumes:
- "/srv/node/sdb1"
- "/srv/node/sdb2"
- "/srv/node/sdb3"
---
# TODO(pbourke): There needs to be one swift.conf generated per service for updates to work
# correctly. Figure out a way (with_items seems to not be allowed when using include)
- include: ../../config.yml
vars:
service_name: "swift"
config_source:
- "roles/swift/templates/swift.conf.j2"
- "/etc/kolla/config/global.conf"
- "/etc/kolla/config/swift/swift.conf"
config_template_dest:
- "{{ node_templates_directory }}/{{ service_name }}/swift.conf_minimal"
- "{{ node_templates_directory }}/{{ service_name }}/swift.conf_global"
- "{{ node_templates_directory }}/{{ service_name }}/swift.conf_augment"
config_dest: "{{ node_config_directory }}/{{ service_name }}/swift.conf"
- include: ../../config.yml
vars:
service_name: "swift-proxy-server"
config_source:
- "roles/swift/templates/proxy-server.conf.j2"
- "/etc/kolla/config/global.conf"
- "/etc/kolla/config/swift/proxy-server.conf"
config_template_dest:
- "{{ node_templates_directory }}/{{ service_name }}/proxy-server.conf_minimal"
- "{{ node_templates_directory }}/{{ service_name }}/proxy-server.conf_global"
- "{{ node_templates_directory }}/{{ service_name }}/proxy-server.conf_augment"
config_dest: "{{ node_config_directory }}/{{ service_name }}/proxy-server.conf"
when: inventory_hostname in groups['swift-proxy-server']
- include: ../../config.yml
vars:
service_name: "swift-account-server"
config_source:
- "roles/swift/templates/account-server.conf.j2"
- "/etc/kolla/config/global.conf"
- "/etc/kolla/config/swift/account-server.conf"
config_template_dest:
- "{{ node_templates_directory }}/{{ service_name }}/account-server.conf_minimal"
- "{{ node_templates_directory }}/{{ service_name }}/account-server.conf_global"
- "{{ node_templates_directory }}/{{ service_name }}/account-server.conf_augment"
config_dest: "{{ node_config_directory }}/{{ service_name }}/account-server.conf"
when: inventory_hostname in groups['swift-account-server']
- include: ../../config.yml
vars:
service_name: "swift-container-server"
config_source:
- "roles/swift/templates/container-server.conf.j2"
- "/etc/kolla/config/global.conf"
- "/etc/kolla/config/swift/container-server.conf"
config_template_dest:
- "{{ node_templates_directory }}/{{ service_name }}/container-server.conf_minimal"
- "{{ node_templates_directory }}/{{ service_name }}/container-server.conf_global"
- "{{ node_templates_directory }}/{{ service_name }}/container-server.conf_augment"
config_dest: "{{ node_config_directory }}/{{ service_name }}/container-server.conf"
when: inventory_hostname in groups['swift-container-server']
- include: ../../config.yml
vars:
service_name: "swift-object-server"
config_source:
- "roles/swift/templates/object-server.conf.j2"
- "/etc/kolla/config/global.conf"
- "/etc/kolla/config/swift/object-server.conf"
config_template_dest:
- "{{ node_templates_directory }}/{{ service_name }}/object-server.conf_minimal"
- "{{ node_templates_directory }}/{{ service_name }}/object-server.conf_global"
- "{{ node_templates_directory }}/{{ service_name }}/object-server.conf_augment"
config_dest: "{{ node_config_directory }}/{{ service_name }}/object-server.conf"
when: inventory_hostname in groups['swift-object-server']
- name: Copying over Swift ring files
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
backup: yes
with_items:
- { src: "/etc/kolla/config/swift/object.ring.gz",
dest: "{{ node_config_directory }}/swift/object.ring.gz" }
- { src: "/etc/kolla/config/swift/container.ring.gz",
dest: "{{ node_config_directory }}/swift/container.ring.gz" }
- { src: "/etc/kolla/config/swift/account.ring.gz",
dest: "{{ node_config_directory }}/swift/account.ring.gz" }
---
- include: register.yml
- include: config.yml
- include: bootstrap.yml
- include: start.yml
---
- name: Creating the Swift service and endpoint
command: docker exec -t kolla_ansible /usr/bin/ansible localhost
-m kolla_keystone_service
-a "service_name=swift
service_type=object-store
description='Openstack Object Storage'
endpoint_region={{ openstack_region_name }}
admin_url='http://{{ kolla_internal_address }}:{{ swift_proxy_server_port }}'
internal_url='http://{{ kolla_internal_address }}:{{ swift_proxy_server_port }}/v1/AUTH_%(tenant_id)s'
public_url='http://{{ kolla_external_address }}:{{ swift_proxy_server_port }}/v1/AUTH_%(tenant_id)s'
region_name={{ openstack_region_name }}
auth={{ '{{ openstack_swift_auth }}' }}"
-e "{'openstack_swift_auth':{{ openstack_swift_auth }}}"
register: swift_endpoint
changed_when: "{{ swift_endpoint.stdout.find('localhost | SUCCESS => ') != -1 and (swift_endpoint.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
until: swift_endpoint.stdout.split()[2] == 'SUCCESS'
retries: 10
delay: 5
run_once: True
- name: Creating the Swift project, user, and role
command: docker exec -t kolla_ansible /usr/bin/ansible localhost
-m kolla_keystone_user
-a "project=service
user={{ swift_keystone_user }}
password={{ swift_keystone_password }}
role={{ swift_admin_tenant_name }}
region_name={{ openstack_region_name }}
auth={{ '{{ openstack_swift_auth }}' }}"
-e "{'openstack_swift_auth':{{ openstack_swift_auth }}}"
register: swift_user
changed_when: "{{ swift_user.stdout.find('localhost | SUCCESS => ') != -1 and (swift_user.stdout.split('localhost | SUCCESS => ')[1]|from_json).changed }}"
until: swift_user.stdout.split()[2] == 'SUCCESS'
retries: 10
delay: 5
run_once: True
---
- name: Starting Swift Proxy Server container
docker:
docker_api_version: "{{ docker_api_version }}"
net: host
pull: "{{ docker_pull_policy }}"
restart_policy: "{{ docker_restart_policy }}"
restart_policy_retry: "{{ docker_restart_policy_retry }}"
state: reloaded
registry: "{{ docker_registry }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
insecure_registry: "{{ docker_insecure_registry }}"
name: swift_proxy_server
image: "{{ swift_proxy_server_image_full }}"
volumes:
- "{{ node_config_directory }}/swift/:/opt/kolla/swift/:ro"
- "{{ node_config_directory }}/swift-proxy-server/:/opt/kolla/swift-proxy-server/:ro"
volumes_from:
- swift_data
env:
KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
when: inventory_hostname in groups['swift-proxy-server']
- name: Starting Swift Account Server container
docker:
docker_api_version: "{{ docker_api_version }}"
net: host
pull: "{{ docker_pull_policy }}"
restart_policy: "{{ docker_restart_policy }}"
restart_policy_retry: "{{ docker_restart_policy_retry }}"
state: reloaded
registry: "{{ docker_registry }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
insecure_registry: "{{ docker_insecure_registry }}"
name: swift_account_server
image: "{{ swift_account_server_image_full }}"
volumes:
- "{{ node_config_directory }}/swift/:/opt/kolla/swift/:ro"
- "{{ node_config_directory }}/swift-account-server/:/opt/kolla/swift-account-server/:ro"
volumes_from:
- swift_data
env:
KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
when: inventory_hostname in groups['swift-account-server']
- name: Starting Swift Container Server container
docker:
docker_api_version: "{{ docker_api_version }}"
net: host
pull: "{{ docker_pull_policy }}"
restart_policy: "{{ docker_restart_policy }}"
restart_policy_retry: "{{ docker_restart_policy_retry }}"
state: reloaded
registry: "{{ docker_registry }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
insecure_registry: "{{ docker_insecure_registry }}"
name: swift_container_server
image: "{{ swift_container_server_image_full }}"
volumes:
- "{{ node_config_directory }}/swift/:/opt/kolla/swift/:ro"
- "{{ node_config_directory }}/swift-container-server/:/opt/kolla/swift-container-server/:ro"
volumes_from:
- swift_data
env:
KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
when: inventory_hostname in groups['swift-container-server']
- name: Starting Swift Object Server container
docker:
docker_api_version: "{{ docker_api_version }}"
net: host
pull: "{{ docker_pull_policy }}"
restart_policy: "{{ docker_restart_policy }}"
restart_policy_retry: "{{ docker_restart_policy_retry }}"
state: reloaded
registry: "{{ docker_registry }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
insecure_registry: "{{ docker_insecure_registry }}"
name: swift_object_server
image: "{{ swift_object_server_image_full }}"
volumes:
- "{{ node_config_directory }}/swift/:/opt/kolla/swift/:ro"
- "{{ node_config_directory }}/swift-object-server/:/opt/kolla/swift-object-server/:ro"
volumes_from:
- swift_data
env:
KOLLA_CONFIG_STRATEGY: "{{ config_strategy }}"
when: inventory_hostname in groups['swift-object-server']
[DEFAULT]
bind_ip = {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
bind_port = {{ swift_account_server_port }}
devices = {{ swift_devices_mount_point }}
mount_check = false
[pipeline:main]
pipeline = account-server
[app:account-server]
use = egg:swift#account
[DEFAULT]
bind_ip = {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
bind_port = {{ swift_container_server_port }}
devices = {{ swift_devices_mount_point }}
mount_check = false
[pipeline:main]
pipeline = container-server
[app:container-server]
use = egg:swift#container
[DEFAULT]
bind_ip = {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
bind_port = {{ swift_object_server_port }}
devices = {{ swift_devices_mount_point }}
mount_check = false
[pipeline:main]
pipeline = object-server
[app:object-server]
use = egg:swift#object
[DEFAULT]
bind_ip = {{ hostvars[inventory_hostname]['ansible_' + api_interface]['ipv4']['address'] }}
bind_port = {{ swift_proxy_server_port }}
[pipeline:main]
pipeline = catch_errors gatekeeper healthcheck cache container_sync bulk ratelimit authtoken keystoneauth slo dlo proxy-server
[app:proxy-server]
use = egg:swift#proxy
allow_account_management = true
account_autocreate = true
[filter:cache]
use = egg:swift#memcache
memcache_servers = {% for host in groups['swift-proxy-server'] %}{{ hostvars[host]['ansible_' + api_interface]['ipv4']['address'] }}:{{ memcached_port }}{% if not loop.last %},{% endif %}{% endfor %}
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:healthcheck]
use = egg:swift#healthcheck
[filter:proxy-logging]
use = egg:swift#proxy_logging
[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory
auth_uri = http://{{ kolla_internal_address }}:{{ keystone_public_port }}
auth_url = http://{{ kolla_internal_address }}:{{ keystone_admin_port }}
auth_plugin = password
project_domain_id = default
user_domain_id = default
project_name = service
username = {{ swift_keystone_user }}
password = {{ swift_keystone_password }}
[filter:keystoneauth]
use = egg:swift#keystoneauth
operator_roles = admin,user
[filter:container_sync]
use = egg:swift#container_sync
[filter:bulk]
use = egg:swift#bulk
[filter:ratelimit]
use = egg:swift#ratelimit
[filter:gatekeeper]
use = egg:swift#gatekeeper
[filter:slo]
use = egg:swift#slo
[filter:dlo]
use = egg:swift#dlo
[swift-hash]
swift_hash_path_suffix = {{ swift_hash_path_suffix }}
swift_hash_path_prefix = {{ swift_hash_path_prefix }}
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
roles: roles:
- { role: keystone, tags: keystone, when: enable_keystone | bool } - { role: keystone, tags: keystone, when: enable_keystone | bool }
- hosts: [swift-proxy-server, swift-account-server, swift-object-server, swift-container-server ]
roles:
- { role: swift, tags: swift, when: enable_swift | bool }
- hosts: [glance-api, glance-registry] - hosts: [glance-api, glance-registry]
roles: roles:
- { role: glance, tags: glance, when: enable_glance | bool } - { role: glance, tags: glance, when: enable_glance | bool }
......
#!/bin/bash #!/bin/bash
SOURCE="/opt/kolla/swift/swift.conf"
TARGET="/etc/swift/swift.conf"
SOURCE_ACCOUNT_SERVER="/opt/kolla/swift/account-server.conf"
TARGET_ACCOUNT_SERVER="/etc/swift/account-server.conf"
OWNER="swift"
if [[ -f "$SOURCE" ]]; then if [[ -f /opt/kolla/swift/swift.conf ]]; then
cp $SOURCE $TARGET cp /opt/kolla/swift/swift.conf /etc/swift/
chown ${OWNER}: $TARGET chown swift: /etc/swift/swift.conf
chmod 0640 $TARGET chmod 0640 /etc/swift/swift.conf
fi fi
if [[ -f "$SOURCE_ACCOUNT_SERVER" ]]; then if [[ -f "/opt/kolla/swift/account.ring.gz" ]]; then
cp $SOURCE_ACCOUNT_SERVER $TARGET_ACCOUNT_SERVER cp /opt/kolla/swift/account.ring.gz /etc/swift/account.ring.gz
chown ${OWNER}: $TARGET_ACCOUNT_SERVER chown swift: /etc/swift/account.ring.gz
chmod 0640 $TARGET_ACCOUNT_SERVER chmod 0640 /etc/swift/account.ring.gz
fi
if [[ -f /opt/kolla/swift-account-server/account-server.conf ]]; then
cp /opt/kolla/swift-account-server/account-server.conf /etc/swift/
chown swift: /etc/swift/account-server.conf
chmod 0640 /etc/swift/account-server.conf
fi fi
...@@ -8,6 +8,8 @@ ARGS="/etc/swift/account-server.conf --verbose" ...@@ -8,6 +8,8 @@ ARGS="/etc/swift/account-server.conf --verbose"
# Loading common functions. # Loading common functions.
source /opt/kolla/kolla-common.sh source /opt/kolla/kolla-common.sh
source /opt/kolla/config-swift.sh
# Execute config strategy # Execute config strategy
set_configs set_configs
......
#!/bin/bash #!/bin/bash
. /opt/kolla/kolla-common.sh chown -R swift: /srv/node
check_required_vars SWIFT_HASH_PATH_SUFFIX
cfg=/etc/swift/swift.conf
crudini --set $cfg swift-hash swift_hash_path_suffix "${SWIFT_HASH_PATH_SUFFIX}"
#!/bin/bash #!/bin/bash
SOURCE="/opt/kolla/swift/swift.conf"
TARGET="/etc/swift/swift.conf"
SOURCE_CONTAINER_SERVER="/opt/kolla/swift/container-server.conf"
TARGET_CONTAINER_SERVER="/etc/swift/container-server.conf"
OWNER="swift" OWNER="swift"
if [[ -f "$SOURCE" ]]; then if [[ -f "/opt/kolla/swift/swift.conf" ]]; then
cp $SOURCE $TARGET cp /opt/kolla/swift/swift.conf /etc/swift/swift.conf
chown ${OWNER}: $TARGET chown ${OWNER}: /etc/swift/swift.conf
chmod 0640 $TARGET chmod 0640 /etc/swift/swift.conf
fi
if [[ -f "/opt/kolla/swift/container.ring.gz" ]]; then
cp /opt/kolla/swift/container.ring.gz /etc/swift/container.ring.gz
chown ${OWNER}: /etc/swift/container.ring.gz
chmod 0640 /etc/swift/container.ring.gz
fi fi
if [[ -f "$SOURCE_CONTAINER_SERVER" ]]; then if [[ -f "/opt/kolla/swift-container-server/container-server.conf" ]]; then
cp $SOURCE_CONTAINER_SERVER $TARGET_CONTAINER_SERVER cp /opt/kolla/swift-container-server/container-server.conf /etc/swift/container-server.conf
chown ${OWNER}: $TARGET_CONTAINER_SERVER chown ${OWNER}: /etc/swift/container-server.conf
chmod 0640 $TARGET_CONTAINER_SERVER chmod 0640 /etc/swift/container-server.conf
fi fi
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment