From e07e96bf6dff2c2431dc50d2bbbc526ca88c36bb Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Wed, 9 Dec 2020 10:27:36 +0300 Subject: [PATCH] mysql_replication: fix crashes caused by deprecated terminology --- plugins/modules/mysql_replication.py | 97 ++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index 9fb2a35..0eccae0 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -244,17 +244,67 @@ from ansible.module_utils._text import to_native executed_queries = [] +def uses_replica_terminology(cursor): + """Checks if REPLICA must be used instead of SLAVE""" + cursor.execute("SELECT VERSION()") + result = cursor.fetchone() + + if isinstance(result, dict): + if 'VERSION()' in result: + version_str = result['VERSION()'] + else: + version_str = result['version()'] + + else: + version_str = result[0] + + version = version_str.split('.') + + ver_major = int(version[0]) + ver_minor = int(version[1]) + if '-' in version[2]: + ver_patch = int(version[2].split('-')[0]) + else: + ver_patch = int(version[2]) + + if 'mariadb' in version_str.lower(): + # Since MariaDB 10.5.1 + if ver_major > 10: + return True + + elif ver_major == 10 and ver_minor > 5: + return True + + elif ver_major == 10 and ver_minor == 5 and ver_patch >= 1: + return True + + return False + + else: + # Since MySQL 8.0.22 + if ver_major > 8: + return True + + elif ver_major == 8 and ver_minor > 0: + return True + + elif ver_major == 8 and ver_minor == 0 and ver_patch >= 22: + return True + + return False + + def get_master_status(cursor): cursor.execute("SHOW MASTER STATUS") masterstatus = cursor.fetchone() return masterstatus -def get_slave_status(cursor, connection_name='', channel=''): +def get_slave_status(cursor, connection_name='', channel='', term='REPLICA'): if connection_name: - query = "SHOW SLAVE '%s' STATUS" % connection_name + query = "SHOW %s '%s' STATUS" % (term, connection_name) else: - query = "SHOW SLAVE STATUS" + query = "SHOW %s STATUS" % term if channel: query += " FOR CHANNEL '%s'" % channel @@ -264,11 +314,11 @@ def get_slave_status(cursor, connection_name='', channel=''): return slavestatus -def stop_slave(module, cursor, connection_name='', channel='', fail_on_error=False): +def stop_slave(module, cursor, connection_name='', channel='', fail_on_error=False, term='REPLICA'): if connection_name: - query = "STOP SLAVE '%s'" % connection_name + query = "STOP %s '%s'" % (term, connection_name) else: - query = 'STOP SLAVE' + query = 'STOP %s' % term if channel: query += " FOR CHANNEL '%s'" % channel @@ -286,11 +336,11 @@ def stop_slave(module, cursor, connection_name='', channel='', fail_on_error=Fal return stopped -def reset_slave(module, cursor, connection_name='', channel='', fail_on_error=False): +def reset_slave(module, cursor, connection_name='', channel='', fail_on_error=False, term='REPLICA'): if connection_name: - query = "RESET SLAVE '%s'" % connection_name + query = "RESET %s '%s'" % (term, connection_name) else: - query = 'RESET SLAVE' + query = 'RESET %s' % term if channel: query += " FOR CHANNEL '%s'" % channel @@ -308,11 +358,11 @@ def reset_slave(module, cursor, connection_name='', channel='', fail_on_error=Fa return reset -def reset_slave_all(module, cursor, connection_name='', channel='', fail_on_error=False): +def reset_slave_all(module, cursor, connection_name='', channel='', fail_on_error=False, term='REPLICA'): if connection_name: - query = "RESET SLAVE '%s' ALL" % connection_name + query = "RESET %s '%s' ALL" % (term, connection_name) else: - query = 'RESET SLAVE ALL' + query = 'RESET %s ALL' % term if channel: query += " FOR CHANNEL '%s'" % channel @@ -345,11 +395,11 @@ def reset_master(module, cursor, fail_on_error=False): return reset -def start_slave(module, cursor, connection_name='', channel='', fail_on_error=False): +def start_slave(module, cursor, connection_name='', channel='', fail_on_error=False, term='REPLICA'): if connection_name: - query = "START SLAVE '%s'" % connection_name + query = "START %s '%s'" % (term, connection_name) else: - query = 'START SLAVE' + query = 'START %s' % term if channel: query += " FOR CHANNEL '%s'" % channel @@ -465,6 +515,13 @@ def main(): else: module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e))) + # Since MySQL 8.0.22 and MariaDB 10.5.1, + # "REPLICA" must be used instead of "SLAVE" + if uses_replica_terminology(cursor): + replica_term = 'REPLICA' + else: + replica_term = 'SLAVE' + if mode in "getmaster": status = get_master_status(cursor) if not isinstance(status, dict): @@ -474,7 +531,7 @@ def main(): module.exit_json(queries=executed_queries, **status) elif mode in "getslave": - status = get_slave_status(cursor, connection_name, channel) + status = get_slave_status(cursor, connection_name, channel, replica_term) if not isinstance(status, dict): status = dict(Is_Slave=False, msg="Server is not configured as mysql slave") else: @@ -529,13 +586,13 @@ def main(): result['changed'] = True module.exit_json(queries=executed_queries, **result) elif mode in "startslave": - started = start_slave(module, cursor, connection_name, channel, fail_on_error) + started = start_slave(module, cursor, connection_name, channel, fail_on_error, replica_term) if started is True: module.exit_json(msg="Slave started ", changed=True, queries=executed_queries) else: module.exit_json(msg="Slave already started (Or cannot be started)", changed=False, queries=executed_queries) elif mode in "stopslave": - stopped = stop_slave(module, cursor, connection_name, channel, fail_on_error) + stopped = stop_slave(module, cursor, connection_name, channel, fail_on_error, replica_term) if stopped is True: module.exit_json(msg="Slave stopped", changed=True, queries=executed_queries) else: @@ -547,13 +604,13 @@ def main(): else: module.exit_json(msg="Master already reset", changed=False, queries=executed_queries) elif mode in "resetslave": - reset = reset_slave(module, cursor, connection_name, channel, fail_on_error) + reset = reset_slave(module, cursor, connection_name, channel, fail_on_error, replica_term) if reset is True: module.exit_json(msg="Slave reset", changed=True, queries=executed_queries) else: module.exit_json(msg="Slave already reset", changed=False, queries=executed_queries) elif mode in "resetslaveall": - reset = reset_slave_all(module, cursor, connection_name, channel, fail_on_error) + reset = reset_slave_all(module, cursor, connection_name, channel, fail_on_error, replica_term) if reset is True: module.exit_json(msg="Slave reset", changed=True, queries=executed_queries) else: