Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix deprecated options from MySQL 8.2 #662

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions plugins/module_utils/command_resolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-

from __future__ import (absolute_import, division, print_function)
from ._version import LooseVersion
__metaclass__ = type


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 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",
("mariadb", "default"): "SHOW SLAVE HOSTS",
("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",
},
"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
}

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("Unsupported command: %s" % command)
25 changes: 21 additions & 4 deletions plugins/modules/mysql_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
23 changes: 15 additions & 8 deletions plugins/modules/mysql_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@
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,
Expand All @@ -301,6 +304,7 @@
get_connector_name,
get_connector_version,
get_server_implementation,
get_server_version,
)

from ansible_collections.community.mysql.plugins.module_utils.user import (
Expand Down Expand Up @@ -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': {},
Expand Down Expand Up @@ -501,18 +507,17 @@ 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')
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):
self.info['master_status'][vname] = self.__convert(val)

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']
Expand All @@ -533,7 +538,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']
Expand Down Expand Up @@ -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',
Expand Down
Loading
Loading