mirror of
https://github.com/ansible-collections/community.mysql.git
synced 2025-07-24 13:50:26 -07:00
Fix deprecated options from MySQL 8.2 (#662)
* Fix show master status for MySQL 8.2+ * Fix mysqldump option form --master-data to --source-data * 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+. * Fix missing condition * Fix unit tests * Add a query resolver depending on implementation and version * Sanity * Fix SHOW REPLICA STATUS queries * Fix mariadb's SHOW REPLICA HOSTS query * Fix CHANGE MASTER for MySQL 8.0.23+ * Fix integration test for CHANGE MASTER * Fix integration test for CHANGE MASTER * Fix replication queries for MySQL 8.0.23+ and 8.4+ * Revert file edited by mistake * Enhance tests format
This commit is contained in:
parent
c503dc5b6b
commit
cd9f4fcf57
14 changed files with 503 additions and 94 deletions
|
@ -343,7 +343,15 @@ import traceback
|
|||
|
||||
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,
|
||||
|
|
|
@ -293,6 +293,9 @@ connector_version:
|
|||
from decimal import Decimal
|
||||
|
||||
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 (
|
||||
mysql_connect,
|
||||
mysql_common_argument_spec,
|
||||
|
@ -301,6 +304,7 @@ from ansible_collections.community.mysql.plugins.module_utils.mysql import (
|
|||
get_connector_name,
|
||||
get_connector_version,
|
||||
get_server_implementation,
|
||||
get_server_version,
|
||||
)
|
||||
|
||||
from ansible_collections.community.mysql.plugins.module_utils.user import (
|
||||
|
@ -335,11 +339,13 @@ 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.command_resolver = CommandResolver(self.server_implementation, self.server_version)
|
||||
self.info = {
|
||||
'version': {},
|
||||
'databases': {},
|
||||
|
@ -501,7 +507,8 @@ class MySQL_Info(object):
|
|||
|
||||
def __get_master_status(self):
|
||||
"""Get master status if the instance is a master."""
|
||||
res = self.__exec_sql('SHOW MASTER STATUS')
|
||||
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):
|
||||
|
@ -509,10 +516,8 @@ class MySQL_Info(object):
|
|||
|
||||
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']
|
||||
|
@ -533,7 +538,8 @@ class MySQL_Info(object):
|
|||
|
||||
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']
|
||||
|
@ -762,12 +768,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',
|
||||
|
|
|
@ -20,11 +20,12 @@ author:
|
|||
- 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),
|
||||
|
@ -298,8 +299,10 @@ queries:
|
|||
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
|
||||
)
|
||||
from ansible_collections.community.mysql.plugins.module_utils.mysql import (
|
||||
get_server_version,
|
||||
get_server_implementation,
|
||||
|
@ -313,18 +316,9 @@ from ansible.module_utils._text import to_native
|
|||
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
|
||||
|
@ -410,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)
|
||||
|
@ -420,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
|
||||
|
||||
|
@ -447,11 +441,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
|
||||
|
@ -566,8 +561,11 @@ 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
|
||||
|
@ -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:
|
||||
|
@ -610,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, 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:
|
||||
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":
|
||||
|
@ -671,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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue