Support deploying an Apache Storm cluster
Storm is required for running the Monasca thresholder component for generating alerts. Change-Id: I5e1ef74dc55a787293abbb3e629b5ab1ce5f4bbb Partially-Implements: blueprint monasca-roles
This commit is contained in:
parent
1ae10375f7
commit
516b0dd34e
@ -314,6 +314,15 @@ skydive_agents_port: "8090"
|
|||||||
solum_application_deployment_port: "9777"
|
solum_application_deployment_port: "9777"
|
||||||
solum_image_builder_port: "9778"
|
solum_image_builder_port: "9778"
|
||||||
|
|
||||||
|
storm_nimbus_thrift_port: 6627
|
||||||
|
storm_supervisor_thrift_port: 6628
|
||||||
|
# Storm will run up to (end - start) + 1 workers per worker host. Here
|
||||||
|
# we reserve ports for those workers, and implicitly define the maximum
|
||||||
|
# number of workers per host.
|
||||||
|
storm_worker_port_range:
|
||||||
|
start: 6700
|
||||||
|
end: 6703
|
||||||
|
|
||||||
swift_proxy_server_port: "8080"
|
swift_proxy_server_port: "8080"
|
||||||
swift_object_server_port: "6000"
|
swift_object_server_port: "6000"
|
||||||
swift_account_server_port: "6001"
|
swift_account_server_port: "6001"
|
||||||
@ -512,6 +521,7 @@ enable_searchlight: "no"
|
|||||||
enable_senlin: "no"
|
enable_senlin: "no"
|
||||||
enable_skydive: "no"
|
enable_skydive: "no"
|
||||||
enable_solum: "no"
|
enable_solum: "no"
|
||||||
|
enable_storm: "{{ enable_monasca | bool }}"
|
||||||
enable_swift: "no"
|
enable_swift: "no"
|
||||||
enable_tacker: "no"
|
enable_tacker: "no"
|
||||||
enable_telegraf: "no"
|
enable_telegraf: "no"
|
||||||
|
@ -103,6 +103,9 @@ control
|
|||||||
[monasca:children]
|
[monasca:children]
|
||||||
monitoring
|
monitoring
|
||||||
|
|
||||||
|
[storm:children]
|
||||||
|
monitoring
|
||||||
|
|
||||||
[mongodb:children]
|
[mongodb:children]
|
||||||
control
|
control
|
||||||
|
|
||||||
@ -458,6 +461,13 @@ monasca
|
|||||||
[monasca-log-metrics:children]
|
[monasca-log-metrics:children]
|
||||||
monasca
|
monasca
|
||||||
|
|
||||||
|
# Storm
|
||||||
|
[storm-worker:children]
|
||||||
|
storm
|
||||||
|
|
||||||
|
[storm-nimbus:children]
|
||||||
|
storm
|
||||||
|
|
||||||
# Ironic
|
# Ironic
|
||||||
[ironic-api:children]
|
[ironic-api:children]
|
||||||
ironic
|
ironic
|
||||||
|
@ -128,6 +128,9 @@ control
|
|||||||
[monasca:children]
|
[monasca:children]
|
||||||
monitoring
|
monitoring
|
||||||
|
|
||||||
|
[storm:children]
|
||||||
|
monitoring
|
||||||
|
|
||||||
[mongodb:children]
|
[mongodb:children]
|
||||||
control
|
control
|
||||||
|
|
||||||
@ -467,6 +470,13 @@ monasca
|
|||||||
[monasca-log-metrics:children]
|
[monasca-log-metrics:children]
|
||||||
monasca
|
monasca
|
||||||
|
|
||||||
|
# Storm
|
||||||
|
[storm-worker:children]
|
||||||
|
storm
|
||||||
|
|
||||||
|
[storm-nimbus:children]
|
||||||
|
storm
|
||||||
|
|
||||||
# Ironic
|
# Ironic
|
||||||
[ironic-api:children]
|
[ironic-api:children]
|
||||||
ironic
|
ironic
|
||||||
|
@ -223,6 +223,7 @@
|
|||||||
- { name: "senlin", enabled: "{{ enable_senlin }}" }
|
- { name: "senlin", enabled: "{{ enable_senlin }}" }
|
||||||
- { name: "skydive", enabled: "{{ enable_skydive }}" }
|
- { name: "skydive", enabled: "{{ enable_skydive }}" }
|
||||||
- { name: "solum", enabled: "{{ enable_solum }}" }
|
- { name: "solum", enabled: "{{ enable_solum }}" }
|
||||||
|
- { name: "storm", enabled: "{{ enable_storm }}" }
|
||||||
- { name: "swift", enabled: "{{ enable_swift }}" }
|
- { name: "swift", enabled: "{{ enable_swift }}" }
|
||||||
- { name: "tacker", enabled: "{{ enable_tacker }}" }
|
- { name: "tacker", enabled: "{{ enable_tacker }}" }
|
||||||
- { name: "tempest", enabled: "{{ enable_tempest }}" }
|
- { name: "tempest", enabled: "{{ enable_tempest }}" }
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
"/var/log/kolla/storm/*.log"
|
||||||
|
{
|
||||||
|
}
|
@ -49,6 +49,7 @@
|
|||||||
( 'senlin', enable_senlin ),
|
( 'senlin', enable_senlin ),
|
||||||
( 'skydive', enable_skydive ),
|
( 'skydive', enable_skydive ),
|
||||||
( 'solum', enable_solum ),
|
( 'solum', enable_solum ),
|
||||||
|
( 'storm', enable_storm ),
|
||||||
( 'swift', enable_swift ),
|
( 'swift', enable_swift ),
|
||||||
( 'tacker', enable_tacker ),
|
( 'tacker', enable_tacker ),
|
||||||
( 'tempest', enable_tempest ),
|
( 'tempest', enable_tempest ),
|
||||||
|
47
ansible/roles/storm/defaults/main.yml
Normal file
47
ansible/roles/storm/defaults/main.yml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
storm_services:
|
||||||
|
storm-worker:
|
||||||
|
container_name: storm_worker
|
||||||
|
group: storm-worker
|
||||||
|
enabled: true
|
||||||
|
image: "{{ storm_image_full }}"
|
||||||
|
environment:
|
||||||
|
STORM_LOG_DIR: /var/log/kolla/storm
|
||||||
|
STORM_LOG4J_PROP: "{{ storm_log_settings }}"
|
||||||
|
volumes:
|
||||||
|
- "{{ node_config_directory }}/storm-worker/:{{ container_config_directory }}/"
|
||||||
|
- "/etc/localtime:/etc/localtime:ro"
|
||||||
|
- "storm:/var/lib/storm/data"
|
||||||
|
- "kolla_logs:/var/log/kolla/"
|
||||||
|
dimensions: "{{ storm_worker_dimensions }}"
|
||||||
|
storm-nimbus:
|
||||||
|
container_name: storm_nimbus
|
||||||
|
group: storm-nimbus
|
||||||
|
enabled: true
|
||||||
|
image: "{{ storm_image_full }}"
|
||||||
|
environment:
|
||||||
|
STORM_LOG_DIR: /var/log/kolla/storm
|
||||||
|
STORM_LOG4J_PROP: "{{ storm_log_settings }}"
|
||||||
|
volumes:
|
||||||
|
- "{{ node_config_directory }}/storm-nimbus/:{{ container_config_directory }}/"
|
||||||
|
- "/etc/localtime:/etc/localtime:ro"
|
||||||
|
- "storm:/var/lib/storm/data"
|
||||||
|
- "kolla_logs:/var/log/kolla/"
|
||||||
|
dimensions: "{{ storm_nimbus_dimensions }}"
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Storm
|
||||||
|
####################
|
||||||
|
storm_log_settings: 'INFO,ROLLINGFILE'
|
||||||
|
storm_nimbus_servers: "{% for host in groups['storm-nimbus'] %}'{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}'{% if not loop.last %},{% endif %}{% endfor %}"
|
||||||
|
|
||||||
|
####################
|
||||||
|
# Docker
|
||||||
|
####################
|
||||||
|
storm_install_type: "{{ kolla_install_type }}"
|
||||||
|
storm_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ storm_install_type }}-storm"
|
||||||
|
storm_tag: "{{ openstack_release }}"
|
||||||
|
storm_image_full: "{{ storm_image }}:{{ storm_tag }}"
|
||||||
|
|
||||||
|
storm_worker_dimensions: "{{ default_container_dimensions }}"
|
||||||
|
storm_nimbus_dimensions: "{{ default_container_dimensions }}"
|
44
ansible/roles/storm/handlers/main.yml
Normal file
44
ansible/roles/storm/handlers/main.yml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
- name: Restart storm-worker container
|
||||||
|
vars:
|
||||||
|
service_name: "storm-worker"
|
||||||
|
service: "{{ storm_services[service_name] }}"
|
||||||
|
config_json: "{{ storm_config_jsons.results|selectattr('item.key', 'equalto', service_name)|first }}"
|
||||||
|
worker_container: "{{ check_storm_containers.results|selectattr('item.key', 'equalto', service_name)|first }}"
|
||||||
|
kolla_docker:
|
||||||
|
action: "recreate_or_restart_container"
|
||||||
|
common_options: "{{ docker_common_options }}"
|
||||||
|
name: "{{ service.container_name }}"
|
||||||
|
image: "{{ service.image }}"
|
||||||
|
environment: "{{ service.environment }}"
|
||||||
|
volumes: "{{ service.volumes }}"
|
||||||
|
dimensions: "{{ service.dimensions }}"
|
||||||
|
when:
|
||||||
|
- kolla_action != "config"
|
||||||
|
- inventory_hostname in groups[service.group]
|
||||||
|
- service.enabled | bool
|
||||||
|
- config_json.changed | bool
|
||||||
|
or storm_worker_conf.changed | bool
|
||||||
|
or worker_container.changed | bool
|
||||||
|
|
||||||
|
- name: Restart storm-nimbus container
|
||||||
|
vars:
|
||||||
|
service_name: "storm-nimbus"
|
||||||
|
service: "{{ storm_services[service_name] }}"
|
||||||
|
config_json: "{{ storm_config_jsons.results|selectattr('item.key', 'equalto', service_name)|first }}"
|
||||||
|
nimbus_container: "{{ check_storm_containers.results|selectattr('item.key', 'equalto', service_name)|first }}"
|
||||||
|
kolla_docker:
|
||||||
|
action: "recreate_or_restart_container"
|
||||||
|
common_options: "{{ docker_common_options }}"
|
||||||
|
name: "{{ service.container_name }}"
|
||||||
|
image: "{{ service.image }}"
|
||||||
|
environment: "{{ service.environment }}"
|
||||||
|
volumes: "{{ service.volumes }}"
|
||||||
|
dimensions: "{{ service.dimensions }}"
|
||||||
|
when:
|
||||||
|
- kolla_action != "config"
|
||||||
|
- inventory_hostname in groups[service.group]
|
||||||
|
- service.enabled | bool
|
||||||
|
- config_json.changed | bool
|
||||||
|
or storm_nimbus_conf.changed | bool
|
||||||
|
or nimbus_container.changed | bool
|
3
ansible/roles/storm/meta/main.yml
Normal file
3
ansible/roles/storm/meta/main.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
dependencies:
|
||||||
|
- { role: common }
|
1
ansible/roles/storm/tasks/check.yml
Normal file
1
ansible/roles/storm/tasks/check.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
---
|
84
ansible/roles/storm/tasks/config.yml
Normal file
84
ansible/roles/storm/tasks/config.yml
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
- name: Ensuring config directories exist
|
||||||
|
file:
|
||||||
|
path: "{{ node_config_directory }}/{{ item.key }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ config_owner_user }}"
|
||||||
|
group: "{{ config_owner_group }}"
|
||||||
|
mode: "0770"
|
||||||
|
recurse: yes
|
||||||
|
become: true
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[item.value.group]
|
||||||
|
- item.value.enabled | bool
|
||||||
|
with_dict: "{{ storm_services }}"
|
||||||
|
|
||||||
|
- name: Copying over config.json files for services
|
||||||
|
template:
|
||||||
|
src: "{{ item.key }}.json.j2"
|
||||||
|
dest: "{{ node_config_directory }}/{{ item.key }}/config.json"
|
||||||
|
mode: "0660"
|
||||||
|
become: true
|
||||||
|
register: storm_config_jsons
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[item.value.group]
|
||||||
|
- item.value.enabled | bool
|
||||||
|
with_dict: "{{ storm_services }}"
|
||||||
|
notify:
|
||||||
|
- "Restart {{ item.key }} container"
|
||||||
|
|
||||||
|
- name: Copying over storm worker config
|
||||||
|
vars:
|
||||||
|
service: "{{ storm_services['storm-worker'] }}"
|
||||||
|
template:
|
||||||
|
src: "{{ item }}"
|
||||||
|
dest: "{{ node_config_directory }}/storm-worker/storm.yml"
|
||||||
|
mode: "0660"
|
||||||
|
become: true
|
||||||
|
register: storm_worker_conf
|
||||||
|
with_first_found:
|
||||||
|
- "{{ node_custom_config }}/storm/{{ inventory_hostname }}/storm.yml"
|
||||||
|
- "{{ node_custom_config }}/storm.yml"
|
||||||
|
- "storm.yml.j2"
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[service['group']]
|
||||||
|
- service.enabled | bool
|
||||||
|
notify:
|
||||||
|
- Restart storm-worker container
|
||||||
|
|
||||||
|
- name: Copying over storm nimbus config
|
||||||
|
vars:
|
||||||
|
service: "{{ storm_services['storm-nimbus'] }}"
|
||||||
|
template:
|
||||||
|
src: "{{ item }}"
|
||||||
|
dest: "{{ node_config_directory }}/storm-nimbus/storm.yml"
|
||||||
|
mode: "0660"
|
||||||
|
become: true
|
||||||
|
register: storm_nimbus_conf
|
||||||
|
with_first_found:
|
||||||
|
- "{{ node_custom_config }}/storm/{{ inventory_hostname }}/storm.yml"
|
||||||
|
- "{{ node_custom_config }}/storm.yml"
|
||||||
|
- "storm.yml.j2"
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[service['group']]
|
||||||
|
- service.enabled | bool
|
||||||
|
notify:
|
||||||
|
- Restart storm-nimbus container
|
||||||
|
|
||||||
|
- name: Check storm containers
|
||||||
|
kolla_docker:
|
||||||
|
action: "compare_container"
|
||||||
|
common_options: "{{ docker_common_options }}"
|
||||||
|
name: "{{ item.value.container_name }}"
|
||||||
|
image: "{{ item.value.image }}"
|
||||||
|
volumes: "{{ item.value.volumes }}"
|
||||||
|
environment: "{{ item.value.environment }}"
|
||||||
|
dimensions: "{{ item.value.dimensions }}"
|
||||||
|
register: check_storm_containers
|
||||||
|
when:
|
||||||
|
- kolla_action != "config"
|
||||||
|
- inventory_hostname in groups[item.value.group]
|
||||||
|
- item.value.enabled | bool
|
||||||
|
with_dict: "{{ storm_services }}"
|
||||||
|
notify:
|
||||||
|
- "Restart {{ item.key }} container"
|
5
ansible/roles/storm/tasks/deploy.yml
Normal file
5
ansible/roles/storm/tasks/deploy.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
- include: config.yml
|
||||||
|
|
||||||
|
- name: Flush handlers
|
||||||
|
meta: flush_handlers
|
2
ansible/roles/storm/tasks/main.yml
Normal file
2
ansible/roles/storm/tasks/main.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
- include: "{{ kolla_action }}.yml"
|
41
ansible/roles/storm/tasks/precheck.yml
Normal file
41
ansible/roles/storm/tasks/precheck.yml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
- name: Get container facts
|
||||||
|
kolla_container_facts:
|
||||||
|
name:
|
||||||
|
- storm_worker
|
||||||
|
- storm_nimbus
|
||||||
|
register: container_facts
|
||||||
|
|
||||||
|
- name: Checking storm nimbus thrift port is available
|
||||||
|
wait_for:
|
||||||
|
host: "{{ api_interface_address }}"
|
||||||
|
port: "{{ storm_nimbus_thrift_port }}"
|
||||||
|
connect_timeout: 1
|
||||||
|
timeout: 1
|
||||||
|
state: stopped
|
||||||
|
when:
|
||||||
|
- container_facts['storm_nimbus'] is not defined
|
||||||
|
- inventory_hostname in groups['storm-nimbus']
|
||||||
|
|
||||||
|
- name: Checking storm supervisor thrift port is available
|
||||||
|
wait_for:
|
||||||
|
host: "{{ api_interface_address }}"
|
||||||
|
port: "{{ storm_supervisor_thrift_port }}"
|
||||||
|
connect_timeout: 1
|
||||||
|
timeout: 1
|
||||||
|
state: stopped
|
||||||
|
when:
|
||||||
|
- container_facts['storm_worker'] is not defined
|
||||||
|
- inventory_hostname in groups['storm-worker']
|
||||||
|
|
||||||
|
- name: Checking storm worker ports are available
|
||||||
|
wait_for:
|
||||||
|
host: "{{ api_interface_address }}"
|
||||||
|
port: "{{ item }}"
|
||||||
|
connect_timeout: 1
|
||||||
|
timeout: 1
|
||||||
|
state: stopped
|
||||||
|
with_sequence: "start={{ storm_worker_port_range.start|int }} end={{ storm_worker_port_range.end|int }}"
|
||||||
|
when:
|
||||||
|
- container_facts['storm_worker'] is not defined
|
||||||
|
- inventory_hostname in groups['storm-worker']
|
10
ansible/roles/storm/tasks/pull.yml
Normal file
10
ansible/roles/storm/tasks/pull.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
- name: Pulling storm images
|
||||||
|
kolla_docker:
|
||||||
|
action: "pull_image"
|
||||||
|
common_options: "{{ docker_common_options }}"
|
||||||
|
image: "{{ item.value.image }}"
|
||||||
|
when:
|
||||||
|
- inventory_hostname in groups[item.value.group]
|
||||||
|
- item.value.enabled | bool
|
||||||
|
with_dict: "{{ storm_services }}"
|
2
ansible/roles/storm/tasks/reconfigure.yml
Normal file
2
ansible/roles/storm/tasks/reconfigure.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
- include: deploy.yml
|
2
ansible/roles/storm/tasks/upgrade.yml
Normal file
2
ansible/roles/storm/tasks/upgrade.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
- include: deploy.yml
|
23
ansible/roles/storm/templates/storm-nimbus.json.j2
Normal file
23
ansible/roles/storm/templates/storm-nimbus.json.j2
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"command": "/opt/storm/bin/storm nimbus",
|
||||||
|
"config_files": [
|
||||||
|
{
|
||||||
|
"source": "{{ container_config_directory }}/storm.yml",
|
||||||
|
"dest": "/opt/storm/conf/storm.yaml",
|
||||||
|
"owner": "storm",
|
||||||
|
"perm": "0600"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"path": "/var/lib/storm",
|
||||||
|
"owner": "storm:storm",
|
||||||
|
"recurse": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/var/log/kolla/storm",
|
||||||
|
"owner": "storm:storm",
|
||||||
|
"recurse": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
ansible/roles/storm/templates/storm-worker.json.j2
Normal file
23
ansible/roles/storm/templates/storm-worker.json.j2
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"command": "/opt/storm/bin/storm supervisor",
|
||||||
|
"config_files": [
|
||||||
|
{
|
||||||
|
"source": "{{ container_config_directory }}/storm.yml",
|
||||||
|
"dest": "/opt/storm/conf/storm.yaml",
|
||||||
|
"owner": "storm",
|
||||||
|
"perm": "0600"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"path": "/var/lib/storm",
|
||||||
|
"owner": "storm:storm",
|
||||||
|
"recurse": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/var/log/kolla/storm",
|
||||||
|
"owner": "storm:storm",
|
||||||
|
"recurse": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
ansible/roles/storm/templates/storm.yml.j2
Normal file
14
ansible/roles/storm/templates/storm.yml.j2
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
storm.local.dir: "/var/lib/storm/data"
|
||||||
|
storm.log.dir: "/var/log/kolla/storm"
|
||||||
|
nimbus.seeds: [{{ storm_nimbus_servers }}]
|
||||||
|
storm.zookeeper.port: {{ zookeeper_client_port }}
|
||||||
|
storm.zookeeper.servers:
|
||||||
|
{% for host in groups['zookeeper'] %}
|
||||||
|
- "{{ hostvars[host]['ansible_' + hostvars[host]['api_interface']]['ipv4']['address'] }}"
|
||||||
|
{% endfor %}
|
||||||
|
supervisor.slots.ports:
|
||||||
|
{% for port in range(storm_worker_port_range.start|int, storm_worker_port_range.end|int + 1) %}
|
||||||
|
- {{ port }}
|
||||||
|
{% endfor %}
|
||||||
|
supervisor.thrift.port: {{ storm_supervisor_thrift_port }}
|
||||||
|
nimbus.thrift.port: {{ storm_nimbus_thrift_port }}
|
@ -279,6 +279,14 @@
|
|||||||
tags: kafka,
|
tags: kafka,
|
||||||
when: enable_kafka | bool }
|
when: enable_kafka | bool }
|
||||||
|
|
||||||
|
- name: Apply role storm
|
||||||
|
hosts: storm
|
||||||
|
serial: '{{ kolla_serial|default("0") }}'
|
||||||
|
roles:
|
||||||
|
- { role: storm,
|
||||||
|
tags: storm,
|
||||||
|
when: enable_storm | bool }
|
||||||
|
|
||||||
- name: Apply role karbor
|
- name: Apply role karbor
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
hosts: karbor
|
hosts: karbor
|
||||||
|
@ -266,6 +266,7 @@ kolla_internal_vip_address: "10.10.10.254"
|
|||||||
#enable_skydive: "no"
|
#enable_skydive: "no"
|
||||||
#enable_solum: "no"
|
#enable_solum: "no"
|
||||||
#enable_swift: "no"
|
#enable_swift: "no"
|
||||||
|
#enable_storm: "no"
|
||||||
#enable_telegraf: "no"
|
#enable_telegraf: "no"
|
||||||
#enable_tacker: "no"
|
#enable_tacker: "no"
|
||||||
#enable_tempest: "no"
|
#enable_tempest: "no"
|
||||||
|
Loading…
Reference in New Issue
Block a user