From b03894af3bb333f3b5735dc25ca7af45fb81fa99 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Fri, 19 Jul 2024 15:03:08 +0200 Subject: [PATCH 01/15] Fix show master status for MySQL 8.2+ --- plugins/modules/mysql_info.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/modules/mysql_info.py b/plugins/modules/mysql_info.py index d8bc88c1..4d25f0a0 100644 --- a/plugins/modules/mysql_info.py +++ b/plugins/modules/mysql_info.py @@ -293,6 +293,7 @@ from decimal import Decimal from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion from ansible_collections.community.mysql.plugins.module_utils.mysql import ( mysql_connect, mysql_common_argument_spec, @@ -301,6 +302,7 @@ get_connector_name, get_connector_version, get_server_implementation, + get_server_version, ) from ansible_collections.community.mysql.plugins.module_utils.user import ( @@ -335,10 +337,11 @@ class MySQL_Info(object): 5. add info about the new subset with an example to RETURN block """ - def __init__(self, module, cursor, server_implementation, user_implementation): + def __init__(self, module, cursor, server_implementation, server_version, user_implementation): self.module = module self.cursor = cursor self.server_implementation = server_implementation + self.server_version = server_version self.user_implementation = user_implementation self.info = { 'version': {}, @@ -501,7 +504,16 @@ def __get_global_status(self): def __get_master_status(self): """Get master status if the instance is a master.""" - res = self.__exec_sql('SHOW MASTER STATUS') + si = self.server_implementation + sv = LooseVersion(self.server_version) + term = 'MASTER' + if si == "mysql" and sv >= LooseVersion("8.2.0"): + term = "BINARY LOG" + + if si == "mariadb" and sv >= LooseVersion("10.5.2"): + term = "BINLOG" + + res = self.__exec_sql('SHOW %s STATUS' % term) if res: for line in res: for vname, val in iteritems(line): @@ -762,12 +774,13 @@ def main(): module.fail_json(msg) server_implementation = get_server_implementation(cursor) + server_version = get_server_version(cursor) user_implementation = get_user_implementation(cursor) ############################### # Create object and do main job - mysql = MySQL_Info(module, cursor, server_implementation, user_implementation) + mysql = MySQL_Info(module, cursor, server_implementation, server_version, user_implementation) module.exit_json(changed=False, server_engine='MariaDB' if server_implementation == 'mariadb' else 'MySQL', From dff942f7441456005f7632160b4d8eb79f2c6be2 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Fri, 19 Jul 2024 15:00:35 +0200 Subject: [PATCH 02/15] Fix mysqldump option form --master-data to --source-data --- plugins/modules/mysql_db.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/modules/mysql_db.py b/plugins/modules/mysql_db.py index 8742f3c3..4a2c954a 100644 --- a/plugins/modules/mysql_db.py +++ b/plugins/modules/mysql_db.py @@ -343,7 +343,15 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.mysql.plugins.module_utils.database import mysql_quote_identifier -from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec +from ansible_collections.community.mysql.plugins.module_utils.mysql import ( + mysql_connect, + mysql_driver, + mysql_driver_fail_msg, + mysql_common_argument_spec, + get_server_implementation, + get_server_version, +) +from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion from ansible.module_utils.six.moves import shlex_quote from ansible.module_utils._text import to_native @@ -372,7 +380,8 @@ def db_delete(cursor, db): def db_dump(module, host, user, password, db_name, target, all_databases, port, - config_file, socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None, + config_file, server_implementation, server_version, socket=None, + ssl_cert=None, ssl_key=None, ssl_ca=None, single_transaction=None, quick=None, ignore_tables=None, hex_blob=None, encoding=None, force=False, master_data=0, skip_lock_tables=False, dump_extra_args=None, unsafe_password=False, restrict_config_file=False, @@ -431,7 +440,11 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port, if hex_blob: cmd += " --hex-blob" if master_data: - cmd += " --master-data=%s" % master_data + if (server_implementation == 'mysql' and + LooseVersion(server_version) >= LooseVersion("8.2.0")): + cmd += " --source-data=%s" % master_data + else: + cmd += " --master-data=%s" % master_data if dump_extra_args is not None: cmd += " " + dump_extra_args @@ -690,6 +703,9 @@ def main(): else: module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e))) + server_implementation = get_server_implementation(cursor) + server_version = get_server_version(cursor) + changed = False if not os.path.exists(config_file): config_file = None @@ -730,7 +746,8 @@ def main(): module.exit_json(changed=True, db=db_name, db_list=db) rc, stdout, stderr = db_dump(module, login_host, login_user, login_password, db, target, all_databases, - login_port, config_file, socket, ssl_cert, ssl_key, + login_port, config_file, server_implementation, server_version, + socket, ssl_cert, ssl_key, ssl_ca, single_transaction, quick, ignore_tables, hex_blob, encoding, force, master_data, skip_lock_tables, dump_extra_args, unsafe_login_password, restrict_config_file, From 2814584a4f8b7651940715d01cffd93f70a24ede Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Fri, 19 Jul 2024 14:56:06 +0200 Subject: [PATCH 03/15] Fix incompatibility between mysqldump 8.0 and MySQL 8.4 Installing the same version between the client and the server makes sense anyway. The incompatibility arise when you use mysqldump with --source-data. The the tool tries to perform a SHOW MASTER STATUS which is deprecated in MySQL 8.2+. --- .../targets/setup_controller/files/mysql.gpg | 49 +++++++++++++++++++ .../setup_controller/tasks/requirements.yml | 29 +++++++++++ .../test_mysql_db/tasks/state_dump_import.yml | 14 +++++- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/integration/targets/setup_controller/files/mysql.gpg diff --git a/tests/integration/targets/setup_controller/files/mysql.gpg b/tests/integration/targets/setup_controller/files/mysql.gpg new file mode 100644 index 00000000..117f1e78 --- /dev/null +++ b/tests/integration/targets/setup_controller/files/mysql.gpg @@ -0,0 +1,49 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: SKS 1.1.6 +Comment: Hostname: pgp.mit.edu + +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 +cAZUlaj3id3TxquAlud4lWDz +=h5nH +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/integration/targets/setup_controller/tasks/requirements.yml b/tests/integration/targets/setup_controller/tasks/requirements.yml index c9390983..af548ba7 100644 --- a/tests/integration/targets/setup_controller/tasks/requirements.yml +++ b/tests/integration/targets/setup_controller/tasks/requirements.yml @@ -2,6 +2,35 @@ # We use the ubuntu2204 image provided by ansible-test. +# The GPG key is imported in the files folder from: +# https://dev.mysql.com/doc/refman/8.4/en/checking-gpg-signature.html +# Downloading the key on each iteration of the tests is too slow. +- name: Install MySQL PGP public key + ansible.builtin.copy: + src: files/mysql.gpg + dest: /usr/share/keyrings/mysql.gpg + owner: root + group: root + mode: '0644' + when: + - db_engine == 'mysql' + - db_version is version('8.4', '>=') + +- name: Add Apt signing key to keyring + ansible.builtin.apt_key: + id: A8D3785C + file: /usr/share/keyrings/mysql.gpg + state: present + +- name: Add MySQL 8.4 repository + ansible.builtin.apt_repository: + repo: deb http://repo.mysql.com/apt/ubuntu/ jammy mysql-8.4-lts mysql-tools + state: present + filename: mysql + when: + - db_engine == 'mysql' + - db_version is version('8.4', '>=') + - name: "{{ role_name }} | Requirements | Install Linux packages" ansible.builtin.package: name: diff --git a/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml b/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml index e4ae7628..5c70fadc 100644 --- a/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml +++ b/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml @@ -111,11 +111,23 @@ check_implicit_admin: no register: result -- name: Dump and Import | Assert successful completion of dump operation +- name: Dump and Import | Assert successful completion of dump operation for MariaDB and MySQL < 8.2 assert: that: - result is changed - result.executed_commands[0] is search(".department --master-data=1 --skip-triggers") + when: + - > + db_engine == 'mariadb' or + db_engine == 'mysql' and db_version is version('8.2', '<') + +- name: Dump and Import | Assert successful completion of dump operation for MySQL >= 8.2 + assert: + that: + - result is changed + - result.executed_commands[0] is search(".department --source-data=1 --skip-triggers") + when: + - db_engine == 'mysql' and db_version is version('8.2', '>=') - name: Dump and Import | State dump/import - file name should exist (db_file_name) file: From 03c3e58f1b750d0baa4eec308d8a77788b954352 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Fri, 19 Jul 2024 15:47:57 +0200 Subject: [PATCH 04/15] Fix missing condition --- .../targets/setup_controller/tasks/requirements.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/targets/setup_controller/tasks/requirements.yml b/tests/integration/targets/setup_controller/tasks/requirements.yml index af548ba7..a576ce42 100644 --- a/tests/integration/targets/setup_controller/tasks/requirements.yml +++ b/tests/integration/targets/setup_controller/tasks/requirements.yml @@ -21,6 +21,9 @@ id: A8D3785C file: /usr/share/keyrings/mysql.gpg state: present + when: + - db_engine == 'mysql' + - db_version is version('8.4', '>=') - name: Add MySQL 8.4 repository ansible.builtin.apt_repository: From 65e9a4e9d64d25402cae289fb6caf4b53675a1ea Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Fri, 19 Jul 2024 15:48:06 +0200 Subject: [PATCH 05/15] Fix unit tests --- tests/unit/plugins/modules/test_mysql_info.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/plugins/modules/test_mysql_info.py b/tests/unit/plugins/modules/test_mysql_info.py index 0d086f4e..7b2de1c2 100644 --- a/tests/unit/plugins/modules/test_mysql_info.py +++ b/tests/unit/plugins/modules/test_mysql_info.py @@ -14,15 +14,15 @@ @pytest.mark.parametrize( - 'suffix,cursor_output,server_implementation,user_implementation', + 'suffix,cursor_output,server_implementation,server_version,user_implementation', [ - ('mysql', '5.5.1-mysql', 'mysql', 'mysql'), - ('log', '5.7.31-log', 'mysql', 'mysql'), - ('mariadb', '10.5.0-mariadb', 'mariadb', 'mariadb'), - ('', '8.0.22', 'mysql', 'mysql'), + ('mysql', '5.5.1-mysql', 'mysql', '5.5.1', 'mysql'), + ('log', '5.7.31-log', 'mysql', '5.7.31', 'mysql'), + ('mariadb', '10.5.0-mariadb', 'mariadb', '10.5.0', 'mariadb'), + ('', '8.0.22', 'mysql', '8.0.22', 'mysql'), ] ) -def test_get_info_suffix(suffix, cursor_output, server_implementation, user_implementation): +def test_get_info_suffix(suffix, cursor_output, server_implementation, server_version, user_implementation): def __cursor_return_value(input_parameter): if input_parameter == "SHOW GLOBAL VARIABLES": cursor.fetchall.return_value = [{"Variable_name": "version", "Value": cursor_output}] @@ -32,6 +32,6 @@ def __cursor_return_value(input_parameter): cursor = MagicMock() cursor.execute.side_effect = __cursor_return_value - info = MySQL_Info(MagicMock(), cursor, server_implementation, user_implementation) + info = MySQL_Info(MagicMock(), cursor, server_implementation, server_version, user_implementation) assert info.get_info([], [], False)['version']['suffix'] == suffix From 032c742ac56849e7f526351c87e19d7ce1b2a95a Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Mon, 22 Jul 2024 14:55:44 +0200 Subject: [PATCH 06/15] Add a query resolver depending on implementation and version --- plugins/module_utils/command_resolver.py | 87 +++++++++++++++++++ plugins/modules/mysql_info.py | 25 +++--- plugins/modules/mysql_replication.py | 26 +++--- .../module_utils/test_command_resolver.py | 38 ++++++++ 4 files changed, 147 insertions(+), 29 deletions(-) create mode 100644 plugins/module_utils/command_resolver.py create mode 100644 tests/unit/plugins/module_utils/test_command_resolver.py diff --git a/plugins/module_utils/command_resolver.py b/plugins/module_utils/command_resolver.py new file mode 100644 index 00000000..3c74f44b --- /dev/null +++ b/plugins/module_utils/command_resolver.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- + +from ._version import LooseVersion + +class CommandResolver: + def __init__(self, server_implementation, server_version): + self.server_implementation = server_implementation + self.server_version = LooseVersion(server_version) + + def resolve_command(self, command): + """ + Resolves the appropriate SQL command based on the server implementation and version. + + Parameters: + command (str): The base SQL command to be resolved (e.g., "SHOW SLAVE HOSTS"). + + Returns: + str: The resolved SQL command suitable for the given server implementation and version. + + Raises: + ValueError: If the command is not supported or recognized. + + Example: + Given a server implementation `mysql` and server version `8.0.23`, and a command `SHOW SLAVE HOSTS`, + the method will resolve the command based on the following table of versions: + + Table: + [ + ("mysql", "default", "SHOW SLAVES HOSTS default"), + ("mysql", "5.7.0", "SHOW SLAVES HOSTS"), + ("mysql", "8.0.22", "SHOW REPLICAS"), + ("mysql", "8.4.0", "SHOW REPLICAS 8.4"), + ("mariadb", "10.5.1", "SHOW REPLICAS HOSTS"), + ] + + Example usage: + >>> resolver = CommandResolver("mysql", "8.0.23") + >>> resolver.resolve_command("SHOW SLAVE HOSTS") + 'SHOW REPLICAS' + + In this example, the resolver will: + - Filter and sort applicable versions: [ + ("8.4.0", "SHOW REPLICAS 8.4"), + ("8.0.22", "HOW REPLICAS"), + ("5.7.0", "SHOW SLAVES HOSTS") + ] + + - Iterate through the sorted list and find the first version less than or equal to 8.0.23, + which is 8.0.22, and return the corresponding command. + """ + + # Convert the command to uppercase to ensure case-insensitive lookup + command = command.upper() + + commands = { + "SHOW MASTER STATUS": { + ("mysql", "default"): "SHOW MASTER STATUS", + ("mariadb", "default"): "SHOW MASTER STATUS", + ("mysql", "8.2.0"): "SHOW BINARY LOG STATUS", + ("mariadb", "10.5.2"): "SHOW BINLOG STATUS", + }, + "SHOW SLAVE STATUS": { + ("mysql", "default"): "SHOW SLAVE HOSTS STATUS", + ("mariadb", "default"): "SHOW SLAVE HOSTS STATUS", + ("mysql", "8.0.22"): "SHOW REPLICAS STATUS", + ("mariadb", "10.5.1"): "SHOW REPLICAS HOSTS STATUS", + }, + "SHOW SLAVE HOSTS": { + ("mysql", "default"): "SHOW SLAVE HOSTS", + ("mariadb", "default"): "SHOW SLAVE HOSTS", + ("mysql", "8.0.22"): "SHOW REPLICAS", + ("mariadb", "10.5.1"): "SHOW REPLICAS HOSTS", + }, + # Add more command mappings here + } + + if command in commands: + cmd_syntaxes = commands[command] + applicable_versions = [(v, cmd) for (impl, v), cmd in cmd_syntaxes.items() if impl == self.server_implementation and v != 'default'] + applicable_versions.sort(reverse=True, key=lambda x: LooseVersion(x[0])) + + for version, cmd in applicable_versions: + if self.server_version >= LooseVersion(version): + return cmd + + return cmd_syntaxes[(self.server_implementation, "default")] + raise ValueError(f"Unsupported command: {command}") \ No newline at end of file diff --git a/plugins/modules/mysql_info.py b/plugins/modules/mysql_info.py index 4d25f0a0..08e1be94 100644 --- a/plugins/modules/mysql_info.py +++ b/plugins/modules/mysql_info.py @@ -294,6 +294,9 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion +from ansible_collections.community.mysql.plugins.module_utils.command_resolver import ( + CommandResolver +) from ansible_collections.community.mysql.plugins.module_utils.mysql import ( mysql_connect, mysql_common_argument_spec, @@ -343,6 +346,7 @@ def __init__(self, module, cursor, server_implementation, server_version, user_i self.server_implementation = server_implementation self.server_version = server_version self.user_implementation = user_implementation + self.command_resolver = CommandResolver(self.server_implementation, self.server_version) self.info = { 'version': {}, 'databases': {}, @@ -504,16 +508,8 @@ def __get_global_status(self): def __get_master_status(self): """Get master status if the instance is a master.""" - si = self.server_implementation - sv = LooseVersion(self.server_version) - term = 'MASTER' - if si == "mysql" and sv >= LooseVersion("8.2.0"): - term = "BINARY LOG" - - if si == "mariadb" and sv >= LooseVersion("10.5.2"): - term = "BINLOG" - - res = self.__exec_sql('SHOW %s STATUS' % term) + query = self.command_resolver.resolve_command("SHOW MASTER STATUS") + res = self.__exec_sql(query) if res: for line in res: for vname, val in iteritems(line): @@ -521,10 +517,8 @@ def __get_master_status(self): def __get_slave_status(self): """Get slave status if the instance is a slave.""" - if self.server_implementation == "mariadb": - res = self.__exec_sql('SHOW ALL SLAVES STATUS') - else: - res = self.__exec_sql('SHOW SLAVE STATUS') + query = self.command_resolver.resolve_command("SHOW SLAVE STATUS") + res = self.__exec_sql(query) if res: for line in res: host = line['Master_Host'] @@ -545,7 +539,8 @@ def __get_slave_status(self): def __get_slaves(self): """Get slave hosts info if the instance is a master.""" - res = self.__exec_sql('SHOW SLAVE HOSTS') + query = self.command_resolver.resolve_command("SHOW SLAVE HOSTS") + res = self.__exec_sql(query) if res: for line in res: srv_id = line['Server_id'] diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index b0caf118..378473eb 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -300,6 +300,9 @@ from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.mysql.plugins.module_utils.command_resolver import ( + CommandResolver +) from ansible_collections.community.mysql.plugins.module_utils.mysql import ( get_server_version, get_server_implementation, @@ -313,18 +316,9 @@ executed_queries = [] -def get_primary_status(cursor): - term = "MASTER" - - version = get_server_version(cursor) - server_implementation = get_server_implementation(cursor) - if server_implementation == "mysql" and LooseVersion(version) >= LooseVersion("8.2.0"): - term = "BINARY LOG" - - if server_implementation == "mariadb" and LooseVersion(version) >= LooseVersion("10.5.2"): - term = "BINLOG" - - cursor.execute("SHOW %s STATUS" % term) +def get_primary_status(cursor, command_resolver): + query = command_resolver.resolve_command("SHOW MASTER STATUS") + cursor.execute(query) primarystatus = cursor.fetchone() return primarystatus @@ -566,12 +560,16 @@ def main(): else: module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e))) + server_version = get_server_version(cursor) + server_implementation = get_server_implementation(cursor) + command_resolver = CommandResolver(server_implementation, server_version) cursor.execute("SELECT VERSION()") - if 'mariadb' in cursor.fetchone()["VERSION()"].lower(): + if server_implementation == 'mariadb': from ansible_collections.community.mysql.plugins.module_utils.implementations.mariadb import replication as impl else: from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql import replication as impl + # Since MySQL 8.0.22 and MariaDB 10.5.1, # "REPLICA" must be used instead of "SLAVE" if impl.uses_replica_terminology(cursor): @@ -582,7 +580,7 @@ def main(): primary_use_gtid = 'slave_pos' if mode == 'getprimary': - status = get_primary_status(cursor) + status = get_primary_status(cursor, command_resolver) if status and "File" in status and "Position" in status: status['Is_Primary'] = True else: diff --git a/tests/unit/plugins/module_utils/test_command_resolver.py b/tests/unit/plugins/module_utils/test_command_resolver.py new file mode 100644 index 00000000..404ee679 --- /dev/null +++ b/tests/unit/plugins/module_utils/test_command_resolver.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible_collections.community.mysql.plugins.module_utils.command_resolver import ( + CommandResolver, +) + + +@pytest.mark.parametrize( + 'server_implementation,server_version,command,expected_output,expected_exception,expected_message', + [ + ('mysql', '1.0.0', 'SHOW NOTHING', '', ValueError, 'Unsupported command: SHOW NOTHING'), + ('mysql', '8.0.20', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Case insensitive + ('mysql', '8.0.20', 'show master status', 'SHOW MASTER STATUS', None, None), # Case insensitive + ('mysql', '8.0.20', 'SHOW master STATUS', 'SHOW MASTER STATUS', None, None), # Case insensitive + ('mysql', '8.2.0', 'SHOW MASTER STATUS', 'SHOW BINARY LOG STATUS', None, None), + ('mysql', '9.0.0', 'SHOW MASTER STATUS', 'SHOW BINARY LOG STATUS', None, None), + ('mariadb', '10.4.23', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Default + ('mariadb', '10.5.1', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Default + ('mariadb', '10.5.2', 'SHOW MASTER STATUS', 'SHOW BINLOG STATUS', None, None), + ('mariadb', '10.6.17', 'SHOW MASTER STATUS', 'SHOW BINLOG STATUS', None, None), + ] +) +def test_resolve_command(server_implementation, server_version, command, expected_output, expected_exception, expected_message): + """ + Tests that the CommandResolver method resolve_command return the correct query. + """ + resolver = CommandResolver(server_implementation, server_version) + if expected_exception: + with pytest.raises(expected_exception) as excinfo: + resolver.resolve_command(command) + assert str(excinfo.value) == expected_message + else: + assert resolver.resolve_command(command) == expected_output From efa0a87278f3a0f4f70cc19738c2a61f23462336 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Mon, 22 Jul 2024 15:03:14 +0200 Subject: [PATCH 07/15] Sanity --- plugins/module_utils/command_resolver.py | 7 +++++-- plugins/modules/mysql_info.py | 1 - plugins/modules/mysql_replication.py | 2 -- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/module_utils/command_resolver.py b/plugins/module_utils/command_resolver.py index 3c74f44b..234363b8 100644 --- a/plugins/module_utils/command_resolver.py +++ b/plugins/module_utils/command_resolver.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- +from __future__ import (absolute_import, division, print_function) from ._version import LooseVersion +__metaclass__ = type -class CommandResolver: + +class CommandResolver(): def __init__(self, server_implementation, server_version): self.server_implementation = server_implementation self.server_version = LooseVersion(server_version) @@ -84,4 +87,4 @@ def resolve_command(self, command): return cmd return cmd_syntaxes[(self.server_implementation, "default")] - raise ValueError(f"Unsupported command: {command}") \ No newline at end of file + raise ValueError("Unsupported command: %s" % command) diff --git a/plugins/modules/mysql_info.py b/plugins/modules/mysql_info.py index 08e1be94..2d1fe946 100644 --- a/plugins/modules/mysql_info.py +++ b/plugins/modules/mysql_info.py @@ -293,7 +293,6 @@ from decimal import Decimal from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion from ansible_collections.community.mysql.plugins.module_utils.command_resolver import ( CommandResolver ) diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index 378473eb..d23fc882 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -298,7 +298,6 @@ import os import warnings -from ansible_collections.community.mysql.plugins.module_utils.version import LooseVersion from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.mysql.plugins.module_utils.command_resolver import ( CommandResolver @@ -569,7 +568,6 @@ def main(): else: from ansible_collections.community.mysql.plugins.module_utils.implementations.mysql import replication as impl - # Since MySQL 8.0.22 and MariaDB 10.5.1, # "REPLICA" must be used instead of "SLAVE" if impl.uses_replica_terminology(cursor): From f32d93ff535acac622aa4a32491ce19c921ebe0c Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Mon, 22 Jul 2024 15:31:14 +0200 Subject: [PATCH 08/15] Fix SHOW REPLICA STATUS queries --- plugins/module_utils/command_resolver.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/module_utils/command_resolver.py b/plugins/module_utils/command_resolver.py index 234363b8..0d132671 100644 --- a/plugins/module_utils/command_resolver.py +++ b/plugins/module_utils/command_resolver.py @@ -63,10 +63,10 @@ def resolve_command(self, command): ("mariadb", "10.5.2"): "SHOW BINLOG STATUS", }, "SHOW SLAVE STATUS": { - ("mysql", "default"): "SHOW SLAVE HOSTS STATUS", - ("mariadb", "default"): "SHOW SLAVE HOSTS STATUS", - ("mysql", "8.0.22"): "SHOW REPLICAS STATUS", - ("mariadb", "10.5.1"): "SHOW REPLICAS HOSTS STATUS", + ("mysql", "default"): "SHOW SLAVE STATUS", + ("mariadb", "default"): "SHOW SLAVE STATUS", + ("mysql", "8.0.22"): "SHOW REPLICA STATUS", + ("mariadb", "10.5.1"): "SHOW REPLICA STATUS", }, "SHOW SLAVE HOSTS": { ("mysql", "default"): "SHOW SLAVE HOSTS", From 0cdee978fc4aa788a0a462371420a790fd7a4cf5 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Mon, 22 Jul 2024 16:07:00 +0200 Subject: [PATCH 09/15] Fix mariadb's SHOW REPLICA HOSTS query --- plugins/module_utils/command_resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/command_resolver.py b/plugins/module_utils/command_resolver.py index 0d132671..6464adc4 100644 --- a/plugins/module_utils/command_resolver.py +++ b/plugins/module_utils/command_resolver.py @@ -72,7 +72,7 @@ def resolve_command(self, command): ("mysql", "default"): "SHOW SLAVE HOSTS", ("mariadb", "default"): "SHOW SLAVE HOSTS", ("mysql", "8.0.22"): "SHOW REPLICAS", - ("mariadb", "10.5.1"): "SHOW REPLICAS HOSTS", + ("mariadb", "10.5.1"): "SHOW REPLICA HOSTS", }, # Add more command mappings here } From cb96b323ab7e4414f2998853b2dd18d2365e9af2 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Mon, 22 Jul 2024 16:17:45 +0200 Subject: [PATCH 10/15] Fix CHANGE MASTER for MySQL 8.0.23+ --- plugins/module_utils/command_resolver.py | 5 +++++ plugins/modules/mysql_replication.py | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/plugins/module_utils/command_resolver.py b/plugins/module_utils/command_resolver.py index 6464adc4..0c69f5f1 100644 --- a/plugins/module_utils/command_resolver.py +++ b/plugins/module_utils/command_resolver.py @@ -74,6 +74,11 @@ def resolve_command(self, command): ("mysql", "8.0.22"): "SHOW REPLICAS", ("mariadb", "10.5.1"): "SHOW REPLICA HOSTS", }, + "CHANGE MASTER": { + ("mysql", "default"): "CHANGE MASTER", + ("mariadb", "default"): "CHANGE MASTER", + ("mysql", "8.0.23"): "CHANGE REPLICATION SOURCE", + }, # Add more command mappings here } diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index d23fc882..5c369625 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -440,11 +440,12 @@ def start_replica(module, cursor, connection_name='', channel='', fail_on_error= return started -def changeprimary(cursor, chm, connection_name='', channel=''): +def changeprimary(cursor, command_resolver, chm, connection_name='', channel=''): + query_head = command_resolver.resolve_command("CHANGE MASTER") if connection_name: - query = "CHANGE MASTER '%s' TO %s" % (connection_name, ','.join(chm)) + query = "%s '%s' TO %s" % (query_head, connection_name, ','.join(chm)) else: - query = 'CHANGE MASTER TO %s' % ','.join(chm) + query = '%s TO %s' % (query_head, ','.join(chm)) if channel: query += " FOR CHANNEL '%s'" % channel @@ -647,7 +648,7 @@ def main(): if primary_use_gtid is not None: chm.append("MASTER_USE_GTID=%s" % primary_use_gtid) try: - changeprimary(cursor, chm, connection_name, channel) + changeprimary(cursor, command_resolver, chm, connection_name, channel) except mysql_driver.Warning as e: result['warning'] = to_native(e) except Exception as e: From 6243cce81e218dd429fc3033321ac5fff5069aa3 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Tue, 23 Jul 2024 15:19:45 +0200 Subject: [PATCH 11/15] Fix integration test for CHANGE MASTER --- .../tasks/mariadb_replication_initial.yml | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml index f65d0902..838c111a 100644 --- a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml +++ b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml @@ -46,10 +46,25 @@ master_log_pos: '{{ master_status.Position }}' register: result -- assert: +- name: Assert that changemaster is changed and query matchs for MariaDB and MySQL < 8.0.23 + ansible.builtin.assert: that: - - result is changed - - result.queries[0] is match("CHANGE MASTER ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") + - result is changed + - result.queries[0] is match("CHANGE MASTER ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") + when: + - > + db_engine == 'mariadb' or + db_engine == 'mysql' and db_version is version('8.0.23', '<') + +- name: Assert that changemaster is changed and query matchs for MariaDB and MySQL >= 8.0.23 + ansible.builtin.assert: + that: + - result is changed + - result.queries[0] is match("CHANGE REPLICATION SOURCE ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") + when: + - > + db_engine == 'mariadb' or + db_engine == 'mysql' and db_version is version('8.0.23', '>=') # Test startslave mode: - name: Start slave From 4d906e3306cc4c9a21d85126cf843cb736f856a2 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Tue, 23 Jul 2024 17:03:54 +0200 Subject: [PATCH 12/15] Fix integration test for CHANGE MASTER --- .../tasks/mariadb_replication_initial.yml | 8 +++---- .../tasks/mysql_replication_channel.yml | 21 ++++++++++++++++- .../tasks/mysql_replication_initial.yml | 23 +++++++++++++++++-- .../tasks/mysql_replication_primary_delay.yml | 19 ++++++++++++--- 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml index 838c111a..e54cd0dc 100644 --- a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml +++ b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml @@ -54,17 +54,15 @@ when: - > db_engine == 'mariadb' or - db_engine == 'mysql' and db_version is version('8.0.23', '<') + (db_engine == 'mysql' and db_version is version('8.0.23', '<')) -- name: Assert that changemaster is changed and query matchs for MariaDB and MySQL >= 8.0.23 +- name: Assert that changemaster is changed and query matchs for MySQL >= 8.0.23 ansible.builtin.assert: that: - result is changed - result.queries[0] is match("CHANGE REPLICATION SOURCE ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") when: - - > - db_engine == 'mariadb' or - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + - db_engine == 'mysql' and db_version is version('8.0.23', '>=') # Test startslave mode: - name: Start slave diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml index 7d37df05..0e20d943 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml @@ -32,10 +32,15 @@ channel: '{{ test_channel }}' register: result - - assert: + - name: Assert that run replication with channel is changed and query matches for MariaDB and MySQL < 8.0.23 + ansible.builtin.assert: that: - result is changed - result.queries == result_query + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.0.23', '<')) vars: result_query: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\ MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ @@ -43,6 +48,20 @@ '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ {{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"] + - name: Assert that run replication with channel is changed and query matches for MySQL >= 8.0.23 + ansible.builtin.assert: + that: + - result is changed + - result.queries == result_query + when: + - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + vars: + result_query: ["CHANGE REPLICATION SOURCE TO MASTER_HOST='{{ mysql_host }}',\ + MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ + MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE=\ + '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ + {{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"] + # Test startreplica mode: - name: Start replica with channel mysql_replication: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml index e08954b6..148cfef4 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml @@ -154,11 +154,15 @@ primary_ssl: no register: result - - name: Assert that changeprimmary is changed and return expected query - assert: + - name: Assert that changeprimmary is changed and return expected query for MariaDB and MySQL < 8.0.23 + ansible.builtin.assert: that: - result is changed - result.queries == expected_queries + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.0.23', '<')) vars: expected_queries: ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',\ MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ @@ -166,6 +170,21 @@ '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ {{ mysql_primary_status.Position }},MASTER_SSL=0,MASTER_SSL_CA=''"] + - name: Assert that changeprimmary is changed and return expected query for MySQL > 8.0.23 + ansible.builtin.assert: + that: + - result is changed + - result.queries == expected_queries + when: + - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + vars: + expected_queries: ["CHANGE REPLICATION SOURCE TO \ + MASTER_HOST='{{ mysql_host }}',\ + MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ + MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE=\ + '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ + {{ mysql_primary_status.Position }},MASTER_SSL=0,MASTER_SSL_CA=''"] + # Test startreplica mode: - name: Start replica mysql_replication: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml index 5e967e82..8dca243f 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml @@ -18,10 +18,23 @@ primary_delay: '{{ test_primary_delay }}' register: result - - assert: + - name: Assert that run replication is changed and query match expectation for MariaDB and MySQL < 8.0.23 + ansible.builtin.assert: + that: + - result is changed + - result.queries == ["CHANGE MASTER TO MASTER_DELAY=60"] + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.0.23', '<')) + + - name: Assert that run replication is changed and query match expectation for MySQL >= 8.0.23 + ansible.builtin.assert: that: - - result is changed - - result.queries == ["CHANGE MASTER TO MASTER_DELAY=60"] + - result is changed + - result.queries == ["CHANGE REPLICATION SOURCE TO MASTER_DELAY=60"] + when: + - db_engine == 'mysql' and db_version is version('8.0.23', '>=') # Auxiliary step: - name: Start replica From 8df9c69023e6cdc4392851b2303f51fc4b887e15 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Wed, 24 Jul 2024 17:30:45 +0200 Subject: [PATCH 13/15] Fix replication queries for MySQL 8.0.23+ and 8.4+ --- plugins/module_utils/command_resolver.py | 85 +++++++++++++++++++ plugins/modules/mysql_replication.py | 49 +++++------ .../test_mysql_replication/tasks/main.yml | 5 +- .../tasks/mysql_replication_channel.yml | 17 ++-- .../tasks/mysql_replication_initial.yml | 48 +++++------ .../tasks/mysql_replication_primary_delay.yml | 2 +- .../mysql_replication_resetprimary_mode.yml | 21 ++++- .../module_utils/test_command_resolver.py | 1 + 8 files changed, 168 insertions(+), 60 deletions(-) diff --git a/plugins/module_utils/command_resolver.py b/plugins/module_utils/command_resolver.py index 0c69f5f1..43748793 100644 --- a/plugins/module_utils/command_resolver.py +++ b/plugins/module_utils/command_resolver.py @@ -79,6 +79,91 @@ def resolve_command(self, command): ("mariadb", "default"): "CHANGE MASTER", ("mysql", "8.0.23"): "CHANGE REPLICATION SOURCE", }, + "MASTER_HOST": { + ("mysql", "default"): "MASTER_HOST", + ("mariadb", "default"): "MASTER_HOST", + ("mysql", "8.0.23"): "SOURCE_HOST", + }, + "MASTER_USER": { + ("mysql", "default"): "MASTER_USER", + ("mariadb", "default"): "MASTER_USER", + ("mysql", "8.0.23"): "SOURCE_USER", + }, + "MASTER_PASSWORD": { + ("mysql", "default"): "MASTER_PASSWORD", + ("mariadb", "default"): "MASTER_PASSWORD", + ("mysql", "8.0.23"): "SOURCE_PASSWORD", + }, + "MASTER_PORT": { + ("mysql", "default"): "MASTER_PORT", + ("mariadb", "default"): "MASTER_PORT", + ("mysql", "8.0.23"): "SOURCE_PORT", + }, + "MASTER_CONNECT_RETRY": { + ("mysql", "default"): "MASTER_CONNECT_RETRY", + ("mariadb", "default"): "MASTER_CONNECT_RETRY", + ("mysql", "8.0.23"): "SOURCE_CONNECT_RETRY", + }, + "MASTER_LOG_FILE": { + ("mysql", "default"): "MASTER_LOG_FILE", + ("mariadb", "default"): "MASTER_LOG_FILE", + ("mysql", "8.0.23"): "SOURCE_LOG_FILE", + }, + "MASTER_LOG_POS": { + ("mysql", "default"): "MASTER_LOG_POS", + ("mariadb", "default"): "MASTER_LOG_POS", + ("mysql", "8.0.23"): "SOURCE_LOG_POS", + }, + "MASTER_DELAY": { + ("mysql", "default"): "MASTER_DELAY", + ("mariadb", "default"): "MASTER_DELAY", + ("mysql", "8.0.23"): "SOURCE_DELAY", + }, + "MASTER_SSL": { + ("mysql", "default"): "MASTER_SSL", + ("mariadb", "default"): "MASTER_SSL", + ("mysql", "8.0.23"): "SOURCE_SSL", + }, + "MASTER_SSL_CA": { + ("mysql", "default"): "MASTER_SSL_CA", + ("mariadb", "default"): "MASTER_SSL_CA", + ("mysql", "8.0.23"): "SOURCE_SSL_CA", + }, + "MASTER_SSL_CAPATH": { + ("mysql", "default"): "MASTER_SSL_CAPATH", + ("mariadb", "default"): "MASTER_SSL_CAPATH", + ("mysql", "8.0.23"): "SOURCE_SSL_CAPATH", + }, + "MASTER_SSL_CERT": { + ("mysql", "default"): "MASTER_SSL_CERT", + ("mariadb", "default"): "MASTER_SSL_CERT", + ("mysql", "8.0.23"): "SOURCE_SSL_CERT", + }, + "MASTER_SSL_KEY": { + ("mysql", "default"): "MASTER_SSL_KEY", + ("mariadb", "default"): "MASTER_SSL_KEY", + ("mysql", "8.0.23"): "SOURCE_SSL_KEY", + }, + "MASTER_SSL_CIPHER": { + ("mysql", "default"): "MASTER_SSL_CIPHER", + ("mariadb", "default"): "MASTER_SSL_CIPHER", + ("mysql", "8.0.23"): "SOURCE_SSL_CIPHER", + }, + "MASTER_SSL_VERIFY_SERVER_CERT": { + ("mysql", "default"): "MASTER_SSL_VERIFY_SERVER_CERT", + ("mariadb", "default"): "MASTER_SSL_VERIFY_SERVER_CERT", + ("mysql", "8.0.23"): "SOURCE_SSL_VERIFY_SERVER_CERT", + }, + "MASTER_AUTO_POSITION": { + ("mysql", "default"): "MASTER_AUTO_POSITION", + ("mariadb", "default"): "MASTER_AUTO_POSITION", + ("mysql", "8.0.23"): "SOURCE_AUTO_POSITION", + }, + "RESET MASTER": { + ("mysql", "default"): "RESET MASTER", + ("mariadb", "default"): "RESET MASTER", + ("mysql", "8.4.0"): "RESET BINARY LOGS AND GTIDS", + }, # Add more command mappings here } diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index 5c369625..723fc358 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -20,11 +20,12 @@ - Balazs Pocze (@banyek) - Andrew Klychkov (@Andersson007) - Dennis Urtubia (@dennisurtubia) +- Laurent Indermühle (@laurent-indermuehle) options: mode: description: - Module operating mode. Could be - C(changeprimary) (CHANGE MASTER TO), + C(changeprimary) (CHANGE MASTER TO) - also works for MySQL 8.0.23 and later since community.mysql 3.10.0, C(changereplication) (CHANGE REPLICATION SOURCE TO) - only supported in MySQL 8.0.23 and later, C(getprimary) (SHOW MASTER STATUS), C(getreplica) (SHOW REPLICA STATUS), @@ -403,8 +404,8 @@ def reset_replica_all(module, cursor, connection_name='', channel='', fail_on_er return reset -def reset_primary(module, cursor, fail_on_error=False): - query = 'RESET MASTER' +def reset_primary(module, cursor, command_resolver, fail_on_error=False): + query = command_resolver.resolve_command('RESET MASTER') try: executed_queries.append(query) cursor.execute(query) @@ -413,7 +414,7 @@ def reset_primary(module, cursor, fail_on_error=False): reset = False except Exception as e: if fail_on_error: - module.fail_json(msg="RESET MASTER failed: %s" % to_native(e)) + module.fail_json(msg="%s failed: %s" % (command_resolver.resolve_command('RESET MASTER'), to_native(e))) reset = False return reset @@ -607,52 +608,52 @@ def main(): chm = [] result = {} if primary_host is not None: - chm.append("MASTER_HOST='%s'" % primary_host) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_HOST'), primary_host)) if primary_user is not None: - chm.append("MASTER_USER='%s'" % primary_user) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_USER'), primary_user)) if primary_password is not None: - chm.append("MASTER_PASSWORD='%s'" % primary_password) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_PASSWORD'), primary_password)) if primary_port is not None: - chm.append("MASTER_PORT=%s" % primary_port) + chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_PORT'), primary_port)) if primary_connect_retry is not None: - chm.append("MASTER_CONNECT_RETRY=%s" % primary_connect_retry) + chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_CONNECT_RETRY'), primary_connect_retry)) if primary_log_file is not None: - chm.append("MASTER_LOG_FILE='%s'" % primary_log_file) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_LOG_FILE'), primary_log_file)) if primary_log_pos is not None: - chm.append("MASTER_LOG_POS=%s" % primary_log_pos) + chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_LOG_POS'), primary_log_pos)) if primary_delay is not None: - chm.append("MASTER_DELAY=%s" % primary_delay) + chm.append("%s=%s" % (command_resolver.resolve_command('MASTER_DELAY'), primary_delay)) if relay_log_file is not None: chm.append("RELAY_LOG_FILE='%s'" % relay_log_file) if relay_log_pos is not None: chm.append("RELAY_LOG_POS=%s" % relay_log_pos) if primary_ssl is not None: if primary_ssl: - chm.append("MASTER_SSL=1") + chm.append("%s=1" % command_resolver.resolve_command('MASTER_SSL')) else: - chm.append("MASTER_SSL=0") + chm.append("%s=0" % command_resolver.resolve_command('MASTER_SSL')) if primary_ssl_ca is not None: - chm.append("MASTER_SSL_CA='%s'" % primary_ssl_ca) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CA'), primary_ssl_ca)) if primary_ssl_capath is not None: - chm.append("MASTER_SSL_CAPATH='%s'" % primary_ssl_capath) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CAPATH'), primary_ssl_capath)) if primary_ssl_cert is not None: - chm.append("MASTER_SSL_CERT='%s'" % primary_ssl_cert) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CERT'), primary_ssl_cert)) if primary_ssl_key is not None: - chm.append("MASTER_SSL_KEY='%s'" % primary_ssl_key) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_KEY'), primary_ssl_key)) if primary_ssl_cipher is not None: - chm.append("MASTER_SSL_CIPHER='%s'" % primary_ssl_cipher) + chm.append("%s='%s'" % (command_resolver.resolve_command('MASTER_SSL_CIPHER'), primary_ssl_cipher)) if primary_ssl_verify_server_cert: - chm.append("SOURCE_SSL_VERIFY_SERVER_CERT=1") + chm.append("%s=1" % command_resolver.resolve_command('MASTER_SSL_VERIFY_SERVER_CERT')) if primary_auto_position: - chm.append("MASTER_AUTO_POSITION=1") + chm.append("%s=1" % command_resolver.resolve_command('MASTER_AUTO_POSITION')) if primary_use_gtid is not None: - chm.append("MASTER_USE_GTID=%s" % primary_use_gtid) + chm.append("MASTER_USE_GTID=%s" % primary_use_gtid) # MariaDB only try: changeprimary(cursor, command_resolver, chm, connection_name, channel) except mysql_driver.Warning as e: result['warning'] = to_native(e) except Exception as e: - module.fail_json(msg='%s. Query == CHANGE MASTER TO %s' % (to_native(e), chm)) + module.fail_json(msg='%s. Query == %s TO %s' % (to_native(e), command_resolver.resolve_command('CHANGE MASTER'), chm)) result['changed'] = True module.exit_json(queries=executed_queries, **result) elif mode == "startreplica": @@ -668,7 +669,7 @@ def main(): else: module.exit_json(msg="Replica already stopped", changed=False, queries=executed_queries) elif mode == 'resetprimary': - reset = reset_primary(module, cursor, fail_on_error) + reset = reset_primary(module, cursor, command_resolver, fail_on_error) if reset is True: module.exit_json(msg="Primary reset", changed=True, queries=executed_queries) else: diff --git a/tests/integration/targets/test_mysql_replication/tasks/main.yml b/tests/integration/targets/test_mysql_replication/tasks/main.yml index 2baa5365..a65cabd7 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/main.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/main.yml @@ -1,3 +1,4 @@ +--- #################################################################### # WARNING: These are designed specifically for Ansible tests # # and should not be used as examples of how to write Ansible roles # @@ -18,8 +19,7 @@ # Tests of channel parameter: - import_tasks: mysql_replication_channel.yml when: - - db_engine == 'mysql' # FIXME: mariadb introduces FOR CHANNEL in 10.7 - - mysql8022_and_higher == true # FIXME: mysql 5.7 should work, but our tets fails, why? + - db_engine == 'mysql' # FIXME: mariadb introduces FOR CHANNEL in 10.7 # Tests of resetprimary mode: - import_tasks: mysql_replication_resetprimary_mode.yml @@ -30,3 +30,4 @@ - import_tasks: mysql_replication_changereplication_mode.yml when: - db_engine == 'mysql' + - db_version is version('8.0.23', '>=') diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml index 0e20d943..d89d88e9 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml @@ -56,10 +56,10 @@ when: - db_engine == 'mysql' and db_version is version('8.0.23', '>=') vars: - result_query: ["CHANGE REPLICATION SOURCE TO MASTER_HOST='{{ mysql_host }}',\ - MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ - MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE=\ - '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ + result_query: ["CHANGE REPLICATION SOURCE TO SOURCE_HOST='{{ mysql_host }}',\ + SOURCE_USER='{{ replication_user }}',SOURCE_PASSWORD='********',\ + SOURCE_PORT={{ mysql_primary_port }},SOURCE_LOG_FILE=\ + '{{ mysql_primary_status.File }}',SOURCE_LOG_POS=\ {{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"] # Test startreplica mode: @@ -102,7 +102,10 @@ mysql_host_value: '{{ mysql_host }}' mysql_primary_port_value: '{{ mysql_primary_port }}' test_channel_value: '{{ test_channel }}' - when: mysql8022_and_higher == false + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.0.22', '<')) - assert: that: @@ -118,7 +121,9 @@ mysql_host_value: '{{ mysql_host }}' mysql_primary_port_value: '{{ mysql_primary_port }}' test_channel_value: '{{ test_channel }}' - when: mysql8022_and_higher == true + when: + - db_engine == 'mysql' + - db_version is version('8.0.22', '>=') # Test stopreplica mode: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml index 148cfef4..30cd99fa 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml @@ -9,16 +9,6 @@ login_host: '{{ mysql_host }}' block: - - name: Set mysql8022_and_higher - set_fact: - mysql8022_and_higher: false - - - name: Set mysql8022_and_higher - set_fact: - mysql8022_and_higher: true - when: - - db_engine == 'mysql' - - db_version is version('8.0.22', '>=') # We use iF NOT EXISTS because the GITHUB Action: # "ansible-community/ansible-test-gh-action" uses "--retry-on-error". @@ -136,11 +126,10 @@ that: - result is not failed - # Test changeprimary mode: # primary_ssl_ca will be set as '' to check the module's behaviour for #23976, # must be converted to an empty string - - name: Run replication - mysql_replication: + - name: Test changeprimary mode with empty primary_ssl_ca + community.mysql.mysql_replication: <<: *mysql_params login_port: '{{ mysql_replica1_port }}' mode: changeprimary @@ -151,7 +140,7 @@ primary_log_file: '{{ mysql_primary_status.File }}' primary_log_pos: '{{ mysql_primary_status.Position }}' primary_ssl_ca: '' - primary_ssl: no + primary_ssl: false register: result - name: Assert that changeprimmary is changed and return expected query for MariaDB and MySQL < 8.0.23 @@ -176,14 +165,15 @@ - result is changed - result.queries == expected_queries when: - - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + - db_engine == 'mysql' + - db_version is version('8.0.23', '>=') vars: expected_queries: ["CHANGE REPLICATION SOURCE TO \ - MASTER_HOST='{{ mysql_host }}',\ - MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',\ - MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE=\ - '{{ mysql_primary_status.File }}',MASTER_LOG_POS=\ - {{ mysql_primary_status.Position }},MASTER_SSL=0,MASTER_SSL_CA=''"] + SOURCE_HOST='{{ mysql_host }}',\ + SOURCE_USER='{{ replication_user }}',SOURCE_PASSWORD='********',\ + SOURCE_PORT={{ mysql_primary_port }},SOURCE_LOG_FILE=\ + '{{ mysql_primary_status.File }}',SOURCE_LOG_POS=\ + {{ mysql_primary_status.Position }},SOURCE_SSL=0,SOURCE_SSL_CA=''"] # Test startreplica mode: - name: Start replica @@ -220,7 +210,10 @@ vars: mysql_host_value: "{{ mysql_host }}" mysql_primary_port_value: "{{ mysql_primary_port }}" - when: mysql8022_and_higher is falsy(convert_bool=True) + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.0.22', '<')) - name: Assert that getreplica returns expected values for MySQL newer than 8.0.22 assert: @@ -235,7 +228,9 @@ vars: mysql_host_value: "{{ mysql_host }}" mysql_primary_port_value: "{{ mysql_primary_port }}" - when: mysql8022_and_higher is truthy(convert_bool=True) + when: + - db_engine == 'mysql' + - db_version is version('8.0.22', '>=') # Create test table and add data to it: - name: Create test table @@ -262,13 +257,18 @@ assert: that: - replica_status.Exec_Master_Log_Pos != mysql_primary_status.Position - when: mysql8022_and_higher == false + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.0.22', '<')) - name: Assert that getreplica Log_Pos is different for MySQL newer than 8.0.22 assert: that: - replica_status.Exec_Source_Log_Pos != mysql_primary_status.Position - when: mysql8022_and_higher == true + when: + - db_engine == 'mysql' + - db_version is version('8.0.22', '>=') - name: Start replica that is already running mysql_replication: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml index 8dca243f..bb366966 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml @@ -32,7 +32,7 @@ ansible.builtin.assert: that: - result is changed - - result.queries == ["CHANGE REPLICATION SOURCE TO MASTER_DELAY=60"] + - result.queries == ["CHANGE REPLICATION SOURCE TO SOURCE_DELAY=60"] when: - db_engine == 'mysql' and db_version is version('8.0.23', '>=') diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml index 4bccc76b..89680491 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml @@ -1,3 +1,4 @@ +--- # Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -38,10 +39,24 @@ mode: resetprimary register: result - - assert: + - name: Assert that reset primary is changed and query matches for MariaDB and MySQL < 8.4 + ansible.builtin.assert: + that: + - result is changed + - result.queries == ["RESET MASTER"] + when: + - > + db_engine == 'mariadb' or + (db_engine == 'mysql' and db_version is version('8.4.0', '<')) + + - name: Assert that reset primary is changed and query matches for MySQL > 8.4 + ansible.builtin.assert: that: - - result is changed - - result.queries == ["RESET MASTER"] + - result is changed + - result.queries == ["RESET BINARY LOGS AND GTIDS"] + when: + - db_engine == 'mysql' + - db_version is version('8.4.0', '>=') # Get primary final status: - name: Get primary status diff --git a/tests/unit/plugins/module_utils/test_command_resolver.py b/tests/unit/plugins/module_utils/test_command_resolver.py index 404ee679..9653418a 100644 --- a/tests/unit/plugins/module_utils/test_command_resolver.py +++ b/tests/unit/plugins/module_utils/test_command_resolver.py @@ -23,6 +23,7 @@ ('mariadb', '10.5.1', 'SHOW MASTER STATUS', 'SHOW MASTER STATUS', None, None), # Default ('mariadb', '10.5.2', 'SHOW MASTER STATUS', 'SHOW BINLOG STATUS', None, None), ('mariadb', '10.6.17', 'SHOW MASTER STATUS', 'SHOW BINLOG STATUS', None, None), + ('mysql', '8.4.1', 'CHANGE MASTER', 'CHANGE REPLICATION SOURCE', None, None), ] ) def test_resolve_command(server_implementation, server_version, command, expected_output, expected_exception, expected_message): From d0840e512e2ba74c10c4cad3f89a85a2bdcf44b4 Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Wed, 24 Jul 2024 17:44:38 +0200 Subject: [PATCH 14/15] Revert file edited by mistake --- .../tasks/mariadb_replication_initial.yml | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml index e54cd0dc..f65d0902 100644 --- a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml +++ b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml @@ -46,23 +46,10 @@ master_log_pos: '{{ master_status.Position }}' register: result -- name: Assert that changemaster is changed and query matchs for MariaDB and MySQL < 8.0.23 - ansible.builtin.assert: - that: - - result is changed - - result.queries[0] is match("CHANGE MASTER ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") - when: - - > - db_engine == 'mariadb' or - (db_engine == 'mysql' and db_version is version('8.0.23', '<')) - -- name: Assert that changemaster is changed and query matchs for MySQL >= 8.0.23 - ansible.builtin.assert: +- assert: that: - - result is changed - - result.queries[0] is match("CHANGE REPLICATION SOURCE ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") - when: - - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + - result is changed + - result.queries[0] is match("CHANGE MASTER ('\S+' )?TO MASTER_HOST='[0-9.]+',MASTER_USER='\w+',MASTER_PASSWORD='[*]{8}',MASTER_PORT=\d+,MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=\d+") # Test startslave mode: - name: Start slave From 103faec98b244c07cda0b9a604128cc9089fd95c Mon Sep 17 00:00:00 2001 From: Laurent Indermuehle Date: Wed, 24 Jul 2024 17:44:55 +0200 Subject: [PATCH 15/15] Enhance tests format --- .../targets/test_mysql_db/tasks/state_dump_import.yml | 5 +++-- .../tasks/mysql_replication_channel.yml | 3 ++- .../tasks/mysql_replication_primary_delay.yml | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml b/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml index 5c70fadc..f8d2b4b4 100644 --- a/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml +++ b/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml @@ -119,7 +119,7 @@ when: - > db_engine == 'mariadb' or - db_engine == 'mysql' and db_version is version('8.2', '<') + (db_engine == 'mysql' and db_version is version('8.2', '<')) - name: Dump and Import | Assert successful completion of dump operation for MySQL >= 8.2 assert: @@ -127,7 +127,8 @@ - result is changed - result.executed_commands[0] is search(".department --source-data=1 --skip-triggers") when: - - db_engine == 'mysql' and db_version is version('8.2', '>=') + - db_engine == 'mysql' + - db_version is version('8.2', '>=') - name: Dump and Import | State dump/import - file name should exist (db_file_name) file: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml index d89d88e9..802865cd 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml @@ -54,7 +54,8 @@ - result is changed - result.queries == result_query when: - - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + - db_engine == 'mysql' + - db_version is version('8.0.23', '>=') vars: result_query: ["CHANGE REPLICATION SOURCE TO SOURCE_HOST='{{ mysql_host }}',\ SOURCE_USER='{{ replication_user }}',SOURCE_PASSWORD='********',\ diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml index bb366966..3ae43391 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml @@ -34,7 +34,8 @@ - result is changed - result.queries == ["CHANGE REPLICATION SOURCE TO SOURCE_DELAY=60"] when: - - db_engine == 'mysql' and db_version is version('8.0.23', '>=') + - db_engine == 'mysql' + - db_version is version('8.0.23', '>=') # Auxiliary step: - name: Start replica