Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- mysql_replication - add ``startgroupreplication`` and ``stopgroupreplication`` options (https://github.com/ansible-collections/community.mysql/pull/647).
87 changes: 84 additions & 3 deletions plugins/modules/mysql_replication.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Balazs Pocze (@banyek)
- Andrew Klychkov (@Andersson007)
- Dennis Urtubia (@dennisurtubia)
- Sebastian Pfahl (@eryx12o45)
- Laurent Indermühle (@laurent-indermuehle)
options:
mode:
Expand All @@ -33,7 +34,9 @@
C(stopreplica) (STOP REPLICA),
C(resetprimary) (RESET MASTER) - supported since community.mysql 0.1.0,
C(resetreplica) (RESET REPLICA),
C(resetreplicaall) (RESET REPLICA ALL).
C(resetreplicaall) (RESET REPLICA ALL),
C(startgroupreplication) (START GROUP_REPLICATION) - supported since community.mysql 3.15.0,
C(stopgroupreplication) (STOP GROUP_REPLICATION) - supported since community.mysql 3.15.0.
type: str
choices:
- changeprimary
Expand All @@ -45,6 +48,8 @@
- resetprimary
- resetreplica
- resetreplicaall
- startgroupreplication
- stopgroupreplication
default: getreplica
primary_host:
description:
Expand Down Expand Up @@ -191,7 +196,16 @@
type: bool
default: false
version_added: '0.1.0'

group_replication_user:
description:
- User for group replication.
type: str
version_added: '3.15.0'
group_replication_password:
description:
- Password for group replication user.
type: str
version_added: '3.15.0'
notes:
- Compatible with MariaDB or MySQL.
- If an empty value for the parameter of string type is needed, use an empty string.
Expand Down Expand Up @@ -285,6 +299,17 @@
community.mysql.mysql_replication:
mode: changeprimary
fail_on_error: true

- name: Start mysql group replication
community.mysql.mysql_replication:
mode: startgroupreplication
group_replication_user: group_repl_user
group_replication_password: group_repl_passwd

- name: Stop mysql group replication
community.mysql.mysql_replication:
mode: stopgroupreplication

'''

RETURN = r'''
Expand Down Expand Up @@ -465,6 +490,38 @@ def changereplication(cursor, chm, channel=''):
cursor.execute(query)


def startgroupreplication(module, cursor, chm, fail_on_error=False, term='GROUP_REPLICATION'):
query = 'START %s %s' % (term, ','.join(chm))

try:
executed_queries.append(query)
cursor.execute(query)
started = True
except mysql_driver.Warning as e:
started = False
except Exception as e:
if fail_on_error:
module.fail_json(msg="START %s failed: %s" % (term, to_native(e)))
started = False
return started


def stopgroupreplication(module, cursor, fail_on_error=False, term='GROUP_REPLICATION'):
query = 'STOP %s' % term

try:
executed_queries.append(query)
cursor.execute(query)
stopped = True
except mysql_driver.Warning as e:
stopped = False
except Exception as e:
if fail_on_error:
module.fail_json(msg="STOP %s failed: %s" % (term, to_native(e)))
stopped = False
return stopped


def main():
argument_spec = mysql_common_argument_spec()
argument_spec.update(
Expand All @@ -477,7 +534,9 @@ def main():
'resetprimary',
'resetreplica',
'resetreplicaall',
'changereplication']),
'changereplication',
'startgroupreplication',
'stopgroupreplication']),
primary_auto_position=dict(type='bool', default=False, aliases=['master_auto_position']),
primary_host=dict(type='str', aliases=['master_host']),
primary_user=dict(type='str', aliases=['master_user']),
Expand All @@ -501,6 +560,8 @@ def main():
connection_name=dict(type='str'),
channel=dict(type='str'),
fail_on_error=dict(type='bool', default=False),
group_replication_user=dict(type='str'),
group_replication_password=dict(type='str', no_log=True),
)
module = AnsibleModule(
argument_spec=argument_spec,
Expand Down Expand Up @@ -540,6 +601,8 @@ def main():
connection_name = module.params["connection_name"]
channel = module.params['channel']
fail_on_error = module.params['fail_on_error']
group_replication_user = module.params['group_replication_user']
group_replication_password = module.params['group_replication_password']

if mysql_driver is None:
module.fail_json(msg=mysql_driver_fail_msg)
Expand Down Expand Up @@ -742,6 +805,24 @@ def main():
module.fail_json(msg='%s. Query == CHANGE REPLICATION SOURCE TO %s' % (to_native(e), chm))
result['changed'] = True
module.exit_json(queries=executed_queries, **result)
elif mode == "startgroupreplication":
chm = []
if group_replication_user is not None:
chm.append(" USER='%s'" % group_replication_user)
if group_replication_password is not None:
chm.append(" PASSWORD='%s'" % group_replication_password)
started = startgroupreplication(module, cursor, chm, fail_on_error)
if started:
module.exit_json(msg="Group replication started ", changed=True, queries=executed_queries)
else:
module.exit_json(msg="Group replication already started (Or cannot be started)", changed=False,
ueries=executed_queries)
elif mode == "stopgroupreplication":
stopped = stopgroupreplication(module, cursor, channel, fail_on_error)
if stopped:
module.exit_json(msg="Group replication stopped", changed=True, queries=executed_queries)
else:
module.exit_json(msg="Group replication already stopped", changed=False, queries=executed_queries)

warnings.simplefilter("ignore")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@ test_channel: test_channel-1

user_name_1: 'db_user1'
user_password_1: 'gadfFDSdtTU^Sdfuj'

enable_group_replication: true
group_replication_group_name: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
group_replication_port: 33061
group_replication_user: 'group_repl_user'
group_replication_pass: 'group_repl_pass'
group_replication_local_address: "{{ mysql_host }}:{{ group_replication_port }}"
group_replication_group_seeds: "{{ mysql_host }}:{{ group_replication_port }}"
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@
when:
- db_engine == 'mysql'
- db_version is version('8.0.23', '>=')

# Tests of group replication:
- import_tasks: mysql_replication_group.yml
when:
- (db_engine == 'mysql' and db_version is version('8.0.0', '>=')) or
(db_engine == 'mariadb' and db_version is version('10.1.0', '>='))
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---
# Copyright: (c) 2025, Sebastian Pfahl (@eryx12o45)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

- vars:
mysql_params: &mysql_params
login_user: '{{ mysql_user }}'
login_password: '{{ mysql_password }}'
login_host: '{{ mysql_host }}'

block:
# Setup group replication prerequisites
- name: Create group replication user
shell:
"echo \"CREATE USER IF NOT EXISTS \
'{{ group_replication_user }}'@'{{ mysql_host }}' \
IDENTIFIED {% if db_engine == 'mysql' %}WITH mysql_native_password {% endif %}BY '{{ group_replication_pass }}'; \
GRANT REPLICATION SLAVE ON *.* TO \
'{{ group_replication_user }}'@'{{ mysql_host }}'; \
GRANT CONNECTION_ADMIN ON *.* TO \
'{{ group_replication_user }}'@'{{ mysql_host }}'; \
GRANT BACKUP_ADMIN ON *.* TO \
'{{ group_replication_user }}'@'{{ mysql_host }}'; \
GRANT GROUP_REPLICATION_STREAM ON *.* TO \
'{{ group_replication_user }}'@'{{ mysql_host }}'; \
FLUSH PRIVILEGES;\" | {{ mysql_command }}"
when: db_engine == 'mysql' and db_version is version('8.0.0', '>=')

- name: Create group replication user for MariaDB
shell:
"echo \"CREATE USER IF NOT EXISTS \
'{{ group_replication_user }}'@'{{ mysql_host }}' \
IDENTIFIED BY '{{ group_replication_pass }}'; \
GRANT REPLICATION SLAVE ON *.* TO \
'{{ group_replication_user }}'@'{{ mysql_host }}';\" | {{ mysql_command }}"
when: db_engine == 'mariadb'

# Configure group replication settings
- name: Configure group replication settings for MySQL
shell:
"echo \"INSTALL PLUGIN group_replication SONAME 'group_replication.so'; \
SET GLOBAL group_replication_group_name='{{ group_replication_group_name }}'; \
SET GLOBAL group_replication_local_address='{{ mysql_host }}:{{ group_replication_port }}'; \
SET GLOBAL group_replication_group_seeds='{{ mysql_host }}:{{ group_replication_port }}'; \
SET GLOBAL group_replication_bootstrap_group=ON;\" | {{ mysql_command }}"
when: db_engine == 'mysql' and db_version is version('8.0.0', '>=')

- name: Configure group replication settings for MariaDB
shell:
"echo \"SET GLOBAL wsrep_provider='/usr/lib/galera/libgalera_smm.so'; \
SET GLOBAL wsrep_cluster_name='{{ group_replication_group_name }}'; \
SET GLOBAL wsrep_cluster_address='gcomm://{{ mysql_host }}:{{ group_replication_port }}'; \
SET GLOBAL wsrep_node_address='{{ mysql_host }}:{{ group_replication_port }}';\" | {{ mysql_command }}"
when: db_engine == 'mariadb' and db_version is version('10.1.0', '>=')
ignore_errors: true

# Test startgroupreplication mode
- name: Start group replication
mysql_replication:
<<: *mysql_params
mode: startgroupreplication
group_replication_user: '{{ group_replication_user }}'
group_replication_password: '{{ group_replication_pass }}'
register: result
ignore_errors: true

- name: Assert that startgroupreplication returns expected values
assert:
that:
- result is changed
- result.queries | length > 0
- "'START GROUP_REPLICATION' in result.queries[0]"
when: result is not failed
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
when: result is not failed
when: result is not failed

i see many ignore_erros + this condition. The thing is that we can't be sure our test work.
can we handle it somehow w/o using ignore_errors + run if not failed ?

Copy link
Collaborator

@Andersson007 Andersson007 Jul 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eryx12o45 ^ (besides the above, LGTM)


# Check group replication status
- name: Check group replication status
shell:
"echo \"SHOW STATUS LIKE 'group_replication_status';\" | {{ mysql_command }}"
register: gr_status
ignore_errors: true

# Test stopgroupreplication mode
- name: Stop group replication
mysql_replication:
<<: *mysql_params
mode: stopgroupreplication
register: result
ignore_errors: true

- name: Assert that stopgroupreplication returns expected values
assert:
that:
- result is changed
- result.queries | length > 0
- "'STOP GROUP_REPLICATION' in result.queries[0]"
when: result is not failed

# Cleanup group replication settings
- name: Reset group replication settings for MySQL
shell:
"echo \"SET GLOBAL group_replication_bootstrap_group=OFF;\" | {{ mysql_command }}"
when: db_engine == 'mysql' and db_version is version('8.0.0', '>=')
ignore_errors: true

- name: Drop group replication user
shell:
"echo \"DROP USER IF EXISTS '{{ group_replication_user }}'@'{{ mysql_host }}';\" | {{ mysql_command }}"
ignore_errors: true
Original file line number Diff line number Diff line change
Expand Up @@ -338,5 +338,5 @@
assert:
that:
-
"result.msg == 'value of mode must be one of: getprimary, getreplica, changeprimary, stopreplica, startreplica, resetprimary, resetreplica, resetreplicaall, changereplication, got: stopslave'"
"result.msg == 'value of mode must be one of: getprimary, getreplica, changeprimary, stopreplica, startreplica, resetprimary, resetreplica, resetreplicaall, changereplication, startgroupreplication, stopgroupreplication, got: stopslave'"
- result is failed
Loading