mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Migrating MySQL to community.mysql (#633)
* Migrating MySQL to community.mysql * Added PR to changelog * Removed missed tests * Removed missed changelog fragments * Update meta/runtime.yml Co-authored-by: Ben Mildren <bmildren@digitalocean.com> Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
d0b07885f0
commit
f420e8f02e
109 changed files with 42 additions and 8303 deletions
25
.github/BOTMETA.yml
vendored
25
.github/BOTMETA.yml
vendored
|
@ -64,10 +64,6 @@ files:
|
|||
$doc_fragments/hwc.py:
|
||||
maintainers: $team_huawei
|
||||
labels: hwc
|
||||
$doc_fragments/mysql.py:
|
||||
maintainers: $team_mysql
|
||||
labels: database mysql
|
||||
keywords: mariadb
|
||||
$doc_fragments/postgres.py:
|
||||
maintainers: $team_postgresql
|
||||
labels: postgres postgresql
|
||||
|
@ -155,10 +151,6 @@ files:
|
|||
$module_utils/memset.py:
|
||||
maintainers: glitchcrab
|
||||
labels: cloud memset
|
||||
$module_utils/mysql.py:
|
||||
maintainers: $team_mysql
|
||||
labels: database mysql
|
||||
keywords: mariadb
|
||||
$module_utils/net_tools/nios/api.py:
|
||||
maintainers: $team_networking sganesh-infoblox
|
||||
labels: infoblox networking
|
||||
|
@ -498,22 +490,6 @@ files:
|
|||
authors: vedit
|
||||
maintainers: Jmainguy kenichi-ogawa-1988
|
||||
labels: mssql_db
|
||||
$modules/database/mysql/mysql_db.py:
|
||||
authors: ansible
|
||||
maintainers: $team_mysql
|
||||
$modules/database/mysql/:
|
||||
authors: Andersson007
|
||||
maintainers: Alexander198961 Xyon bmalynovytch bmildren kurtdavis michaelcoburn oneiroi tolland
|
||||
keywords: mariadb
|
||||
$modules/database/mysql/mysql_replication.py:
|
||||
authors: Andersson007 banyek
|
||||
$modules/database/mysql/mysql_user.py:
|
||||
authors: Jmainguy bmalynovytch
|
||||
maintainers: Alexander198961 Andersson007 Xyon bmildren kurtdavis michaelcoburn oneiroi tolland
|
||||
ignore: tomaszkiewicz
|
||||
$modules/database/mysql/mysql_variables.py:
|
||||
authors: banyek
|
||||
maintainers: $team_mysql
|
||||
$modules/database/postgresql/postgresql_db.py:
|
||||
authors: ansible
|
||||
maintainers: $team_postgresql
|
||||
|
@ -1341,7 +1317,6 @@ macros:
|
|||
team_linode: InTheCloudDan decentral1se displague rmcintosh
|
||||
team_macos: akasurde kyleabenson martinm82
|
||||
team_manageiq: abellotti cben gtanzillo yaacov zgalor
|
||||
team_mysql: Alexander198961 Andersson007 Xyon bmalynovytch bmildren kurtdavis michaelcoburn oneiroi tolland
|
||||
team_netapp: amit0701 carchi8py hulquest lmprice lonico ndswartz schmots1
|
||||
team_netscaler: chiradeep giorgos-nikolopoulos
|
||||
team_netvisor: Qalthos amitsi csharpe-pn pdam preetiparasar
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
bugfixes:
|
||||
- mysql_user - fix overriding password to the same (https://github.com/ansible-collections/community.general/issues/543).
|
|
@ -1,2 +0,0 @@
|
|||
bugfixes:
|
||||
- mysql_user - add missed privileges to support (https://github.com/ansible-collections/community.general/issues/617).
|
2
changelogs/fragments/mysql.yml
Normal file
2
changelogs/fragments/mysql.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
removed_features:
|
||||
- "mysql_* - all MySQL modules have been moved to the ``community.mysql`` collection. A redirection is active, which will be removed in version 2.0.0 (https://github.com/ansible-collections/community.general/pull/633)."
|
|
@ -286,6 +286,36 @@ plugin_routing:
|
|||
deprecation:
|
||||
removal_version: 3.0.0
|
||||
warning_text: see plugin documentation for details
|
||||
mysql_db:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql_db module has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql_db
|
||||
mysql_info:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql_info module has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql_info
|
||||
mysql_query:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql_query module has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql_query
|
||||
mysql_replication:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql_replication module has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql_replication
|
||||
mysql_user:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql_user module has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql_user
|
||||
mysql_variables:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql_variables module has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql_variables
|
||||
ovirt:
|
||||
deprecation:
|
||||
removal_version: 3.0.0
|
||||
|
@ -627,6 +657,11 @@ plugin_routing:
|
|||
removal_version: 2.0.0
|
||||
warning_text: The digital_ocean docs_fragment has been moved to the community.digitalocean collection.
|
||||
redirect: community.digitalocean.digital_ocean
|
||||
mysql:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql docs_fragment has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql
|
||||
proxysql:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
|
@ -638,3 +673,8 @@ plugin_routing:
|
|||
removal_version: 2.0.0
|
||||
warning_text: The digital_ocean module_utils has been moved to the community.digitalocean collection.
|
||||
redirect: community.digitalocean.digital_ocean
|
||||
mysql:
|
||||
deprecation:
|
||||
removal_version: 2.0.0
|
||||
warning_text: The mysql module_utils has been moved to the community.mysql collection.
|
||||
redirect: community.mysql.mysql
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2015, Jonathan Mainguy <jon@soh.re>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
class ModuleDocFragment(object):
|
||||
|
||||
# Standard mysql documentation fragment
|
||||
DOCUMENTATION = r'''
|
||||
options:
|
||||
login_user:
|
||||
description:
|
||||
- The username used to authenticate with.
|
||||
type: str
|
||||
login_password:
|
||||
description:
|
||||
- The password used to authenticate with.
|
||||
type: str
|
||||
login_host:
|
||||
description:
|
||||
- Host running the database.
|
||||
- In some cases for local connections the I(login_unix_socket=/path/to/mysqld/socket),
|
||||
that is usually C(/var/run/mysqld/mysqld.sock), needs to be used instead of I(login_host=localhost).
|
||||
type: str
|
||||
default: localhost
|
||||
login_port:
|
||||
description:
|
||||
- Port of the MySQL server. Requires I(login_host) be defined as other than localhost if login_port is used.
|
||||
type: int
|
||||
default: 3306
|
||||
login_unix_socket:
|
||||
description:
|
||||
- The path to a Unix domain socket for local connections.
|
||||
type: str
|
||||
connect_timeout:
|
||||
description:
|
||||
- The connection timeout when connecting to the MySQL server.
|
||||
type: int
|
||||
default: 30
|
||||
config_file:
|
||||
description:
|
||||
- Specify a config file from which user and password are to be read.
|
||||
type: path
|
||||
default: '~/.my.cnf'
|
||||
ca_cert:
|
||||
description:
|
||||
- The path to a Certificate Authority (CA) certificate. This option, if used, must specify the same certificate
|
||||
as used by the server.
|
||||
type: path
|
||||
aliases: [ ssl_ca ]
|
||||
client_cert:
|
||||
description:
|
||||
- The path to a client public key certificate.
|
||||
type: path
|
||||
aliases: [ ssl_cert ]
|
||||
client_key:
|
||||
description:
|
||||
- The path to the client private key.
|
||||
type: path
|
||||
aliases: [ ssl_key ]
|
||||
requirements:
|
||||
- PyMySQL (Python 2.7 and Python 3.X), or
|
||||
- MySQLdb (Python 2.x)
|
||||
notes:
|
||||
- Requires the PyMySQL (Python 2.7 and Python 3.X) or MySQL-python (Python 2.X) package on the remote host.
|
||||
The Python package may be installed with apt-get install python-pymysql (Ubuntu; see M(ansible.builtin.apt)) or
|
||||
yum install python2-PyMySQL (RHEL/CentOS/Fedora; see M(ansible.builtin.yum)). You can also use dnf install python2-PyMySQL
|
||||
for newer versions of Fedora; see M(ansible.builtin.dnf).
|
||||
- Both C(login_password) and C(login_user) are required when you are
|
||||
passing credentials. If none are present, the module will attempt to read
|
||||
the credentials from C(~/.my.cnf), and finally fall back to using the MySQL
|
||||
default login of 'root' with no password.
|
||||
- If there are problems with local connections, using I(login_unix_socket=/path/to/mysqld/socket)
|
||||
instead of I(login_host=localhost) might help. As an example, the default MariaDB installation of version 10.4
|
||||
and later uses the unix_socket authentication plugin by default that
|
||||
without using I(login_unix_socket=/var/run/mysqld/mysqld.sock) (the default path)
|
||||
causes the error ``Host '127.0.0.1' is not allowed to connect to this MariaDB server``.
|
||||
'''
|
|
@ -1,110 +0,0 @@
|
|||
# This code is part of Ansible, but is an independent component.
|
||||
# This particular file snippet, and this file snippet only, is BSD licensed.
|
||||
# Modules you write using this snippet, which is embedded dynamically by Ansible
|
||||
# still belong to the author of the module, and may assign their own license
|
||||
# to the complete work.
|
||||
#
|
||||
# Copyright (c), Jonathan Mainguy <jon@soh.re>, 2015
|
||||
# Most of this was originally added by Sven Schliesing @muffl0n in the mysql_user.py module
|
||||
#
|
||||
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
|
||||
from ansible.module_utils.six.moves import configparser
|
||||
|
||||
try:
|
||||
import pymysql as mysql_driver
|
||||
_mysql_cursor_param = 'cursor'
|
||||
except ImportError:
|
||||
try:
|
||||
import MySQLdb as mysql_driver
|
||||
import MySQLdb.cursors
|
||||
_mysql_cursor_param = 'cursorclass'
|
||||
except ImportError:
|
||||
mysql_driver = None
|
||||
|
||||
mysql_driver_fail_msg = 'The PyMySQL (Python 2.7 and Python 3.X) or MySQL-python (Python 2.X) module is required.'
|
||||
|
||||
|
||||
def parse_from_mysql_config_file(cnf):
|
||||
cp = configparser.ConfigParser()
|
||||
cp.read(cnf)
|
||||
return cp
|
||||
|
||||
|
||||
def mysql_connect(module, login_user=None, login_password=None, config_file='', ssl_cert=None,
|
||||
ssl_key=None, ssl_ca=None, db=None, cursor_class=None,
|
||||
connect_timeout=30, autocommit=False, config_overrides_defaults=False):
|
||||
config = {}
|
||||
|
||||
if config_file and os.path.exists(config_file):
|
||||
config['read_default_file'] = config_file
|
||||
cp = parse_from_mysql_config_file(config_file)
|
||||
# Override some commond defaults with values from config file if needed
|
||||
if cp and cp.has_section('client') and config_overrides_defaults:
|
||||
try:
|
||||
module.params['login_host'] = cp.get('client', 'host', fallback=module.params['login_host'])
|
||||
module.params['login_port'] = cp.getint('client', 'port', fallback=module.params['login_port'])
|
||||
except Exception as e:
|
||||
if "got an unexpected keyword argument 'fallback'" in e.message:
|
||||
module.fail_json('To use config_overrides_defaults, '
|
||||
'it needs Python 3.5+ as the default interpreter on a target host')
|
||||
|
||||
if ssl_ca is not None or ssl_key is not None or ssl_cert is not None:
|
||||
config['ssl'] = {}
|
||||
|
||||
if module.params['login_unix_socket']:
|
||||
config['unix_socket'] = module.params['login_unix_socket']
|
||||
else:
|
||||
config['host'] = module.params['login_host']
|
||||
config['port'] = module.params['login_port']
|
||||
|
||||
# If login_user or login_password are given, they should override the
|
||||
# config file
|
||||
if login_user is not None:
|
||||
config['user'] = login_user
|
||||
if login_password is not None:
|
||||
config['passwd'] = login_password
|
||||
if ssl_cert is not None:
|
||||
config['ssl']['cert'] = ssl_cert
|
||||
if ssl_key is not None:
|
||||
config['ssl']['key'] = ssl_key
|
||||
if ssl_ca is not None:
|
||||
config['ssl']['ca'] = ssl_ca
|
||||
if db is not None:
|
||||
config['db'] = db
|
||||
if connect_timeout is not None:
|
||||
config['connect_timeout'] = connect_timeout
|
||||
|
||||
if _mysql_cursor_param == 'cursor':
|
||||
# In case of PyMySQL driver:
|
||||
db_connection = mysql_driver.connect(autocommit=autocommit, **config)
|
||||
else:
|
||||
# In case of MySQLdb driver
|
||||
db_connection = mysql_driver.connect(**config)
|
||||
if autocommit:
|
||||
db_connection.autocommit(True)
|
||||
|
||||
if cursor_class == 'DictCursor':
|
||||
return db_connection.cursor(**{_mysql_cursor_param: mysql_driver.cursors.DictCursor}), db_connection
|
||||
else:
|
||||
return db_connection.cursor(), db_connection
|
||||
|
||||
|
||||
def mysql_common_argument_spec():
|
||||
return dict(
|
||||
login_user=dict(type='str', default=None),
|
||||
login_password=dict(type='str', no_log=True),
|
||||
login_host=dict(type='str', default='localhost'),
|
||||
login_port=dict(type='int', default=3306),
|
||||
login_unix_socket=dict(type='str'),
|
||||
config_file=dict(type='path', default='~/.my.cnf'),
|
||||
connect_timeout=dict(type='int', default=30),
|
||||
client_cert=dict(type='path', aliases=['ssl_cert']),
|
||||
client_key=dict(type='path', aliases=['ssl_key']),
|
||||
ca_cert=dict(type='path', aliases=['ssl_ca']),
|
||||
)
|
|
@ -1,727 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2012, Mark Theunissen <mark.theunissen@gmail.com>
|
||||
# Sponsored by Four Kitchens http://fourkitchens.com.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_db
|
||||
short_description: Add or remove MySQL databases from a remote host
|
||||
description:
|
||||
- Add or remove MySQL databases from a remote host.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the database to add or remove.
|
||||
- I(name=all) may only be provided if I(state) is C(dump) or C(import).
|
||||
- List of databases is provided with I(state=dump), I(state=present) and I(state=absent).
|
||||
- If I(name=all) it works like --all-databases option for mysqldump (Added in 2.0).
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
aliases: [db]
|
||||
state:
|
||||
description:
|
||||
- The database state
|
||||
type: str
|
||||
default: present
|
||||
choices: ['absent', 'dump', 'import', 'present']
|
||||
collation:
|
||||
description:
|
||||
- Collation mode (sorting). This only applies to new table/databases and
|
||||
does not update existing ones, this is a limitation of MySQL.
|
||||
type: str
|
||||
default: ''
|
||||
encoding:
|
||||
description:
|
||||
- Encoding mode to use, examples include C(utf8) or C(latin1_swedish_ci),
|
||||
at creation of database, dump or importation of sql script.
|
||||
type: str
|
||||
default: ''
|
||||
target:
|
||||
description:
|
||||
- Location, on the remote host, of the dump file to read from or write to.
|
||||
- Uncompressed SQL files (C(.sql)) as well as bzip2 (C(.bz2)), gzip (C(.gz)) and
|
||||
xz (Added in 2.0) compressed files are supported.
|
||||
type: path
|
||||
single_transaction:
|
||||
description:
|
||||
- Execute the dump in a single transaction.
|
||||
type: bool
|
||||
default: no
|
||||
quick:
|
||||
description:
|
||||
- Option used for dumping large tables.
|
||||
type: bool
|
||||
default: yes
|
||||
ignore_tables:
|
||||
description:
|
||||
- A list of table names that will be ignored in the dump
|
||||
of the form database_name.table_name.
|
||||
type: list
|
||||
elements: str
|
||||
required: no
|
||||
default: []
|
||||
hex_blob:
|
||||
description:
|
||||
- Dump binary columns using hexadecimal notation.
|
||||
required: no
|
||||
default: no
|
||||
type: bool
|
||||
version_added: '0.2.0'
|
||||
force:
|
||||
description:
|
||||
- Continue dump or import even if we get an SQL error.
|
||||
- Used only when I(state) is C(dump) or C(import).
|
||||
required: no
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
master_data:
|
||||
description:
|
||||
- Option to dump a master replication server to produce a dump file
|
||||
that can be used to set up another server as a slave of the master.
|
||||
- C(0) to not include master data.
|
||||
- C(1) to generate a 'CHANGE MASTER TO' statement
|
||||
required on the slave to start the replication process.
|
||||
- C(2) to generate a commented 'CHANGE MASTER TO'.
|
||||
- Can be used when I(state=dump).
|
||||
required: no
|
||||
type: int
|
||||
choices: [0, 1, 2]
|
||||
default: 0
|
||||
version_added: '0.2.0'
|
||||
skip_lock_tables:
|
||||
description:
|
||||
- Skip locking tables for read. Used when I(state=dump), ignored otherwise.
|
||||
required: no
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
dump_extra_args:
|
||||
description:
|
||||
- Provide additional arguments for mysqldump.
|
||||
Used when I(state=dump) only, ignored otherwise.
|
||||
required: no
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
use_shell:
|
||||
description:
|
||||
- Used to prevent C(Broken pipe) errors when the imported I(target) file is compressed.
|
||||
- If C(yes), the module will internally execute commands via a shell.
|
||||
- Used when I(state=import), ignored otherwise.
|
||||
required: no
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
unsafe_login_password:
|
||||
description:
|
||||
- If C(no), the module will safely use a shell-escaped version of the I(login_password) value.
|
||||
- It makes sense to use C(yes) only if there are special symbols in the value and errors C(Access denied) occur.
|
||||
- Used only when I(state) is C(import) or C(dump) and I(login_password) is passed, ignored otherwise.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
restrict_config_file:
|
||||
description:
|
||||
- Read only passed I(config_file).
|
||||
- When I(state) is C(dump) or C(import), by default the module passes I(config_file) parameter
|
||||
using C(--defaults-extra-file) command-line argument to C(mysql/mysqldump) utilities
|
||||
under the hood that read named option file in addition to usual option files.
|
||||
- If this behavior is undesirable, use C(yes) to read only named option file.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
check_implicit_admin:
|
||||
description:
|
||||
- Check if mysql allows login as root/nopassword before trying supplied credentials.
|
||||
- If success, passed I(login_user)/I(login_password) will be ignored.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
config_overrides_defaults:
|
||||
description:
|
||||
- If C(yes), connection parameters from I(config_file) will override the default
|
||||
values of I(login_host) and I(login_port) parameters.
|
||||
- Used when I(stat) is C(present) or C(absent), ignored otherwise.
|
||||
- It needs Python 3.5+ as the default interpreter on a target host.
|
||||
type: bool
|
||||
default: no
|
||||
version_added: '0.2.0'
|
||||
|
||||
seealso:
|
||||
- module: community.general.mysql_info
|
||||
- module: community.general.mysql_variables
|
||||
- module: community.general.mysql_user
|
||||
- module: community.general.mysql_replication
|
||||
- name: MySQL command-line client reference
|
||||
description: Complete reference of the MySQL command-line client documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/mysql.html
|
||||
- name: mysqldump reference
|
||||
description: Complete reference of the ``mysqldump`` client utility documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html
|
||||
- name: CREATE DATABASE reference
|
||||
description: Complete reference of the CREATE DATABASE command documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/create-database.html
|
||||
- name: DROP DATABASE reference
|
||||
description: Complete reference of the DROP DATABASE command documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/drop-database.html
|
||||
author: "Ansible Core Team"
|
||||
requirements:
|
||||
- mysql (command line binary)
|
||||
- mysqldump (command line binary)
|
||||
notes:
|
||||
- Requires the mysql and mysqldump binaries on the remote host.
|
||||
- This module is B(not idempotent) when I(state) is C(import),
|
||||
and will import the dump file each time if run more than once.
|
||||
extends_documentation_fragment:
|
||||
- community.general.mysql
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Create a new database with name 'bobdata'
|
||||
mysql_db:
|
||||
name: bobdata
|
||||
state: present
|
||||
|
||||
- name: Create new databases with names 'foo' and 'bar'
|
||||
mysql_db:
|
||||
name:
|
||||
- foo
|
||||
- bar
|
||||
state: present
|
||||
|
||||
# Copy database dump file to remote host and restore it to database 'my_db'
|
||||
- name: Copy database dump file
|
||||
copy:
|
||||
src: dump.sql.bz2
|
||||
dest: /tmp
|
||||
|
||||
- name: Restore database
|
||||
mysql_db:
|
||||
name: my_db
|
||||
state: import
|
||||
target: /tmp/dump.sql.bz2
|
||||
|
||||
- name: Restore database ignoring errors
|
||||
mysql_db:
|
||||
name: my_db
|
||||
state: import
|
||||
target: /tmp/dump.sql.bz2
|
||||
force: yes
|
||||
|
||||
- name: Dump multiple databases
|
||||
mysql_db:
|
||||
state: dump
|
||||
name: db_1,db_2
|
||||
target: /tmp/dump.sql
|
||||
|
||||
- name: Dump multiple databases
|
||||
mysql_db:
|
||||
state: dump
|
||||
name:
|
||||
- db_1
|
||||
- db_2
|
||||
target: /tmp/dump.sql
|
||||
|
||||
- name: Dump all databases to hostname.sql
|
||||
mysql_db:
|
||||
state: dump
|
||||
name: all
|
||||
target: /tmp/dump.sql
|
||||
|
||||
- name: Dump all databases to hostname.sql including master data
|
||||
mysql_db:
|
||||
state: dump
|
||||
name: all
|
||||
target: /tmp/dump.sql
|
||||
master_data: 1
|
||||
|
||||
# Import of sql script with encoding option
|
||||
- name: >
|
||||
Import dump.sql with specific latin1 encoding,
|
||||
similar to mysql -u <username> --default-character-set=latin1 -p <password> < dump.sql
|
||||
mysql_db:
|
||||
state: import
|
||||
name: all
|
||||
encoding: latin1
|
||||
target: /tmp/dump.sql
|
||||
|
||||
# Dump of database with encoding option
|
||||
- name: >
|
||||
Dump of Databse with specific latin1 encoding,
|
||||
similar to mysqldump -u <username> --default-character-set=latin1 -p <password> <database>
|
||||
mysql_db:
|
||||
state: dump
|
||||
name: db_1
|
||||
encoding: latin1
|
||||
target: /tmp/dump.sql
|
||||
|
||||
- name: Delete database with name 'bobdata'
|
||||
mysql_db:
|
||||
name: bobdata
|
||||
state: absent
|
||||
|
||||
- name: Make sure there is neither a database with name 'foo', nor one with name 'bar'
|
||||
mysql_db:
|
||||
name:
|
||||
- foo
|
||||
- bar
|
||||
state: absent
|
||||
|
||||
# Dump database with argument not directly supported by this module
|
||||
# using dump_extra_args parameter
|
||||
- name: Dump databases without including triggers
|
||||
mysql_db:
|
||||
state: dump
|
||||
name: foo
|
||||
target: /tmp/dump.sql
|
||||
dump_extra_args: --skip-triggers
|
||||
|
||||
- name: Try to create database as root/nopassword first. If not allowed, pass the credentials
|
||||
mysql_db:
|
||||
check_implicit_admin: yes
|
||||
login_user: bob
|
||||
login_password: 123456
|
||||
name: bobdata
|
||||
state: present
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
db:
|
||||
description: Database names in string format delimited by white space.
|
||||
returned: always
|
||||
type: str
|
||||
sample: "foo bar"
|
||||
db_list:
|
||||
description: List of database names.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ["foo", "bar"]
|
||||
executed_commands:
|
||||
description: List of commands which tried to run.
|
||||
returned: if executed
|
||||
type: list
|
||||
sample: ["CREATE DATABASE acme"]
|
||||
version_added: '0.2.0'
|
||||
'''
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.database import mysql_quote_identifier
|
||||
from ansible_collections.community.general.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six.moves import shlex_quote
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
executed_commands = []
|
||||
|
||||
# ===========================================
|
||||
# MySQL module specific support methods.
|
||||
#
|
||||
|
||||
|
||||
def db_exists(cursor, db):
|
||||
res = 0
|
||||
for each_db in db:
|
||||
res += cursor.execute("SHOW DATABASES LIKE %s", (each_db.replace("_", r"\_"),))
|
||||
return res == len(db)
|
||||
|
||||
|
||||
def db_delete(cursor, db):
|
||||
if not db:
|
||||
return False
|
||||
for each_db in db:
|
||||
query = "DROP DATABASE %s" % mysql_quote_identifier(each_db, 'database')
|
||||
executed_commands.append(query)
|
||||
cursor.execute(query)
|
||||
return True
|
||||
|
||||
|
||||
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,
|
||||
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,
|
||||
check_implicit_admin=False):
|
||||
cmd = module.get_bin_path('mysqldump', True)
|
||||
# If defined, mysqldump demands --defaults-extra-file be the first option
|
||||
if config_file:
|
||||
if restrict_config_file:
|
||||
cmd += " --defaults-file=%s" % shlex_quote(config_file)
|
||||
else:
|
||||
cmd += " --defaults-extra-file=%s" % shlex_quote(config_file)
|
||||
|
||||
if check_implicit_admin:
|
||||
cmd += " --user=root --password=''"
|
||||
else:
|
||||
if user is not None:
|
||||
cmd += " --user=%s" % shlex_quote(user)
|
||||
|
||||
if password is not None:
|
||||
if not unsafe_password:
|
||||
cmd += " --password=%s" % shlex_quote(password)
|
||||
else:
|
||||
cmd += " --password=%s" % password
|
||||
|
||||
if ssl_cert is not None:
|
||||
cmd += " --ssl-cert=%s" % shlex_quote(ssl_cert)
|
||||
if ssl_key is not None:
|
||||
cmd += " --ssl-key=%s" % shlex_quote(ssl_key)
|
||||
if ssl_ca is not None:
|
||||
cmd += " --ssl-ca=%s" % shlex_quote(ssl_ca)
|
||||
if force:
|
||||
cmd += " --force"
|
||||
if socket is not None:
|
||||
cmd += " --socket=%s" % shlex_quote(socket)
|
||||
else:
|
||||
cmd += " --host=%s --port=%i" % (shlex_quote(host), port)
|
||||
|
||||
if all_databases:
|
||||
cmd += " --all-databases"
|
||||
elif len(db_name) > 1:
|
||||
cmd += " --databases {0}".format(' '.join(db_name))
|
||||
else:
|
||||
cmd += " %s" % shlex_quote(' '.join(db_name))
|
||||
|
||||
if skip_lock_tables:
|
||||
cmd += " --skip-lock-tables"
|
||||
if (encoding is not None) and (encoding != ""):
|
||||
cmd += " --default-character-set=%s" % shlex_quote(encoding)
|
||||
if single_transaction:
|
||||
cmd += " --single-transaction=true"
|
||||
if quick:
|
||||
cmd += " --quick"
|
||||
if ignore_tables:
|
||||
for an_ignored_table in ignore_tables:
|
||||
cmd += " --ignore-table={0}".format(an_ignored_table)
|
||||
if hex_blob:
|
||||
cmd += " --hex-blob"
|
||||
if master_data:
|
||||
cmd += " --master-data=%s" % master_data
|
||||
if dump_extra_args is not None:
|
||||
cmd += " " + dump_extra_args
|
||||
|
||||
path = None
|
||||
if os.path.splitext(target)[-1] == '.gz':
|
||||
path = module.get_bin_path('gzip', True)
|
||||
elif os.path.splitext(target)[-1] == '.bz2':
|
||||
path = module.get_bin_path('bzip2', True)
|
||||
elif os.path.splitext(target)[-1] == '.xz':
|
||||
path = module.get_bin_path('xz', True)
|
||||
|
||||
if path:
|
||||
cmd = '%s | %s > %s' % (cmd, path, shlex_quote(target))
|
||||
else:
|
||||
cmd += " > %s" % shlex_quote(target)
|
||||
|
||||
executed_commands.append(cmd)
|
||||
rc, stdout, stderr = module.run_command(cmd, use_unsafe_shell=True)
|
||||
return rc, stdout, stderr
|
||||
|
||||
|
||||
def db_import(module, host, user, password, db_name, target, all_databases, port, config_file,
|
||||
socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None, encoding=None, force=False,
|
||||
use_shell=False, unsafe_password=False, restrict_config_file=False,
|
||||
check_implicit_admin=False):
|
||||
if not os.path.exists(target):
|
||||
return module.fail_json(msg="target %s does not exist on the host" % target)
|
||||
|
||||
cmd = [module.get_bin_path('mysql', True)]
|
||||
# --defaults-file must go first, or errors out
|
||||
if config_file:
|
||||
if restrict_config_file:
|
||||
cmd.append("--defaults-file=%s" % shlex_quote(config_file))
|
||||
else:
|
||||
cmd.append("--defaults-extra-file=%s" % shlex_quote(config_file))
|
||||
|
||||
if check_implicit_admin:
|
||||
cmd += " --user=root --password=''"
|
||||
else:
|
||||
if user:
|
||||
cmd.append("--user=%s" % shlex_quote(user))
|
||||
|
||||
if password:
|
||||
if not unsafe_password:
|
||||
cmd.append("--password=%s" % shlex_quote(password))
|
||||
else:
|
||||
cmd.append("--password=%s" % password)
|
||||
|
||||
if ssl_cert is not None:
|
||||
cmd.append("--ssl-cert=%s" % shlex_quote(ssl_cert))
|
||||
if ssl_key is not None:
|
||||
cmd.append("--ssl-key=%s" % shlex_quote(ssl_key))
|
||||
if ssl_ca is not None:
|
||||
cmd.append("--ssl-ca=%s" % shlex_quote(ssl_ca))
|
||||
if force:
|
||||
cmd.append("-f")
|
||||
if socket is not None:
|
||||
cmd.append("--socket=%s" % shlex_quote(socket))
|
||||
else:
|
||||
cmd.append("--host=%s" % shlex_quote(host))
|
||||
cmd.append("--port=%i" % port)
|
||||
if (encoding is not None) and (encoding != ""):
|
||||
cmd.append("--default-character-set=%s" % shlex_quote(encoding))
|
||||
if not all_databases:
|
||||
cmd.append("--one-database")
|
||||
cmd.append(shlex_quote(''.join(db_name)))
|
||||
|
||||
comp_prog_path = None
|
||||
if os.path.splitext(target)[-1] == '.gz':
|
||||
comp_prog_path = module.get_bin_path('gzip', required=True)
|
||||
elif os.path.splitext(target)[-1] == '.bz2':
|
||||
comp_prog_path = module.get_bin_path('bzip2', required=True)
|
||||
elif os.path.splitext(target)[-1] == '.xz':
|
||||
comp_prog_path = module.get_bin_path('xz', required=True)
|
||||
if comp_prog_path:
|
||||
# The line below is for returned data only:
|
||||
executed_commands.append('%s -dc %s | %s' % (comp_prog_path, target, cmd))
|
||||
|
||||
if not use_shell:
|
||||
p1 = subprocess.Popen([comp_prog_path, '-dc', target], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p2 = subprocess.Popen(cmd, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(stdout2, stderr2) = p2.communicate()
|
||||
p1.stdout.close()
|
||||
p1.wait()
|
||||
|
||||
if p1.returncode != 0:
|
||||
stderr1 = p1.stderr.read()
|
||||
return p1.returncode, '', stderr1
|
||||
else:
|
||||
return p2.returncode, stdout2, stderr2
|
||||
else:
|
||||
# Used to prevent 'Broken pipe' errors that
|
||||
# occasionaly occur when target files are compressed.
|
||||
# FYI: passing the `shell=True` argument to p2 = subprocess.Popen()
|
||||
# doesn't solve the problem.
|
||||
cmd = " ".join(cmd)
|
||||
cmd = "%s -dc %s | %s" % (comp_prog_path, shlex_quote(target), cmd)
|
||||
rc, stdout, stderr = module.run_command(cmd, use_unsafe_shell=True)
|
||||
return rc, stdout, stderr
|
||||
|
||||
else:
|
||||
cmd = ' '.join(cmd)
|
||||
cmd += " < %s" % shlex_quote(target)
|
||||
executed_commands.append(cmd)
|
||||
rc, stdout, stderr = module.run_command(cmd, use_unsafe_shell=True)
|
||||
return rc, stdout, stderr
|
||||
|
||||
|
||||
def db_create(cursor, db, encoding, collation):
|
||||
if not db:
|
||||
return False
|
||||
query_params = dict(enc=encoding, collate=collation)
|
||||
res = 0
|
||||
for each_db in db:
|
||||
query = ['CREATE DATABASE %s' % mysql_quote_identifier(each_db, 'database')]
|
||||
if encoding:
|
||||
query.append("CHARACTER SET %(enc)s")
|
||||
if collation:
|
||||
query.append("COLLATE %(collate)s")
|
||||
query = ' '.join(query)
|
||||
res += cursor.execute(query, query_params)
|
||||
try:
|
||||
executed_commands.append(cursor.mogrify(query, query_params))
|
||||
except AttributeError:
|
||||
executed_commands.append(cursor._executed)
|
||||
except Exception:
|
||||
executed_commands.append(query)
|
||||
return res > 0
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Module execution.
|
||||
#
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
login_user=dict(type='str'),
|
||||
login_password=dict(type='str', no_log=True),
|
||||
login_host=dict(type='str', default='localhost'),
|
||||
login_port=dict(type='int', default=3306),
|
||||
login_unix_socket=dict(type='str'),
|
||||
name=dict(type='list', required=True, aliases=['db']),
|
||||
encoding=dict(type='str', default=''),
|
||||
collation=dict(type='str', default=''),
|
||||
target=dict(type='path'),
|
||||
state=dict(type='str', default='present', choices=['absent', 'dump', 'import', 'present']),
|
||||
client_cert=dict(type='path', aliases=['ssl_cert']),
|
||||
client_key=dict(type='path', aliases=['ssl_key']),
|
||||
ca_cert=dict(type='path', aliases=['ssl_ca']),
|
||||
connect_timeout=dict(type='int', default=30),
|
||||
config_file=dict(type='path', default='~/.my.cnf'),
|
||||
single_transaction=dict(type='bool', default=False),
|
||||
quick=dict(type='bool', default=True),
|
||||
ignore_tables=dict(type='list', default=[]),
|
||||
hex_blob=dict(default=False, type='bool'),
|
||||
force=dict(type='bool', default=False),
|
||||
master_data=dict(type='int', default=0, choices=[0, 1, 2]),
|
||||
skip_lock_tables=dict(type='bool', default=False),
|
||||
dump_extra_args=dict(type='str'),
|
||||
use_shell=dict(type='bool', default=False),
|
||||
unsafe_login_password=dict(type='bool', default=False),
|
||||
restrict_config_file=dict(type='bool', default=False),
|
||||
check_implicit_admin=dict(type='bool', default=False),
|
||||
config_overrides_defaults=dict(type='bool', default=False),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
db = module.params["name"]
|
||||
if not db:
|
||||
module.exit_json(changed=False, db=db, db_list=[])
|
||||
db = [each_db.strip() for each_db in db]
|
||||
|
||||
encoding = module.params["encoding"]
|
||||
collation = module.params["collation"]
|
||||
state = module.params["state"]
|
||||
target = module.params["target"]
|
||||
socket = module.params["login_unix_socket"]
|
||||
login_port = module.params["login_port"]
|
||||
if login_port < 0 or login_port > 65535:
|
||||
module.fail_json(msg="login_port must be a valid unix port number (0-65535)")
|
||||
ssl_cert = module.params["client_cert"]
|
||||
ssl_key = module.params["client_key"]
|
||||
ssl_ca = module.params["ca_cert"]
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
config_file = module.params['config_file']
|
||||
login_password = module.params["login_password"]
|
||||
unsafe_login_password = module.params["unsafe_login_password"]
|
||||
login_user = module.params["login_user"]
|
||||
login_host = module.params["login_host"]
|
||||
ignore_tables = module.params["ignore_tables"]
|
||||
for a_table in ignore_tables:
|
||||
if a_table == "":
|
||||
module.fail_json(msg="Name of ignored table cannot be empty")
|
||||
single_transaction = module.params["single_transaction"]
|
||||
quick = module.params["quick"]
|
||||
hex_blob = module.params["hex_blob"]
|
||||
force = module.params["force"]
|
||||
master_data = module.params["master_data"]
|
||||
skip_lock_tables = module.params["skip_lock_tables"]
|
||||
dump_extra_args = module.params["dump_extra_args"]
|
||||
use_shell = module.params["use_shell"]
|
||||
restrict_config_file = module.params["restrict_config_file"]
|
||||
check_implicit_admin = module.params['check_implicit_admin']
|
||||
config_overrides_defaults = module.params['config_overrides_defaults']
|
||||
|
||||
if len(db) > 1 and state == 'import':
|
||||
module.fail_json(msg="Multiple databases are not supported with state=import")
|
||||
db_name = ' '.join(db)
|
||||
|
||||
all_databases = False
|
||||
if state in ['dump', 'import']:
|
||||
if target is None:
|
||||
module.fail_json(msg="with state=%s target is required" % state)
|
||||
if db == ['all']:
|
||||
all_databases = True
|
||||
else:
|
||||
if db == ['all']:
|
||||
module.fail_json(msg="name is not allowed to equal 'all' unless state equals import, or dump.")
|
||||
try:
|
||||
cursor = None
|
||||
if check_implicit_admin:
|
||||
try:
|
||||
cursor, db_conn = mysql_connect(module, 'root', '', config_file, ssl_cert, ssl_key, ssl_ca,
|
||||
connect_timeout=connect_timeout,
|
||||
config_overrides_defaults=config_overrides_defaults)
|
||||
except Exception as e:
|
||||
check_implicit_admin = False
|
||||
pass
|
||||
|
||||
if not cursor:
|
||||
cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca,
|
||||
connect_timeout=connect_timeout, config_overrides_defaults=config_overrides_defaults)
|
||||
except Exception as e:
|
||||
if os.path.exists(config_file):
|
||||
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "
|
||||
"Exception message: %s" % (config_file, to_native(e)))
|
||||
else:
|
||||
module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e)))
|
||||
|
||||
changed = False
|
||||
if not os.path.exists(config_file):
|
||||
config_file = None
|
||||
|
||||
existence_list = []
|
||||
non_existence_list = []
|
||||
|
||||
if not all_databases:
|
||||
for each_database in db:
|
||||
if db_exists(cursor, [each_database]):
|
||||
existence_list.append(each_database)
|
||||
else:
|
||||
non_existence_list.append(each_database)
|
||||
|
||||
if state == "absent":
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=bool(existence_list), db=db_name, db_list=db)
|
||||
try:
|
||||
changed = db_delete(cursor, existence_list)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="error deleting database: %s" % to_native(e))
|
||||
module.exit_json(changed=changed, db=db_name, db_list=db, executed_commands=executed_commands)
|
||||
elif state == "present":
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=bool(non_existence_list), db=db_name, db_list=db)
|
||||
changed = False
|
||||
if non_existence_list:
|
||||
try:
|
||||
changed = db_create(cursor, non_existence_list, encoding, collation)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="error creating database: %s" % to_native(e),
|
||||
exception=traceback.format_exc())
|
||||
module.exit_json(changed=changed, db=db_name, db_list=db, executed_commands=executed_commands)
|
||||
elif state == "dump":
|
||||
if non_existence_list and not all_databases:
|
||||
module.fail_json(msg="Cannot dump database(s) %r - not found" % (', '.join(non_existence_list)))
|
||||
if module.check_mode:
|
||||
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,
|
||||
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,
|
||||
check_implicit_admin)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="%s" % stderr)
|
||||
module.exit_json(changed=True, db=db_name, db_list=db, msg=stdout,
|
||||
executed_commands=executed_commands)
|
||||
elif state == "import":
|
||||
if module.check_mode:
|
||||
module.exit_json(changed=True, db=db_name, db_list=db)
|
||||
if non_existence_list and not all_databases:
|
||||
try:
|
||||
db_create(cursor, non_existence_list, encoding, collation)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="error creating database: %s" % to_native(e),
|
||||
exception=traceback.format_exc())
|
||||
rc, stdout, stderr = db_import(module, login_host, login_user,
|
||||
login_password, db, target,
|
||||
all_databases,
|
||||
login_port, config_file,
|
||||
socket, ssl_cert, ssl_key, ssl_ca,
|
||||
encoding, force, use_shell, unsafe_login_password,
|
||||
restrict_config_file, check_implicit_admin)
|
||||
if rc != 0:
|
||||
module.fail_json(msg="%s" % stderr)
|
||||
module.exit_json(changed=True, db=db_name, db_list=db, msg=stdout,
|
||||
executed_commands=executed_commands)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,543 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_info
|
||||
short_description: Gather information about MySQL servers
|
||||
description:
|
||||
- Gathers information about MySQL servers.
|
||||
|
||||
options:
|
||||
filter:
|
||||
description:
|
||||
- Limit the collected information by comma separated string or YAML list.
|
||||
- Allowable values are C(version), C(databases), C(settings), C(global_status),
|
||||
C(users), C(engines), C(master_status), C(slave_status), C(slave_hosts).
|
||||
- By default, collects all subsets.
|
||||
- You can use '!' before value (for example, C(!settings)) to exclude it from the information.
|
||||
- If you pass including and excluding values to the filter, for example, I(filter=!settings,version),
|
||||
the excluding values, C(!settings) in this case, will be ignored.
|
||||
type: list
|
||||
elements: str
|
||||
login_db:
|
||||
description:
|
||||
- Database name to connect to.
|
||||
- It makes sense if I(login_user) is allowed to connect to a specific database only.
|
||||
type: str
|
||||
exclude_fields:
|
||||
description:
|
||||
- List of fields which are not needed to collect.
|
||||
- "Supports elements: C(db_size). Unsupported elements will be ignored"
|
||||
type: list
|
||||
elements: str
|
||||
version_added: '0.2.0'
|
||||
return_empty_dbs:
|
||||
description:
|
||||
- Includes names of empty databases to returned dictionary.
|
||||
type: bool
|
||||
default: no
|
||||
|
||||
notes:
|
||||
- Calculating the size of a database might be slow, depending on the number and size of tables in it.
|
||||
To avoid this, use I(exclude_fields=db_size).
|
||||
|
||||
seealso:
|
||||
- module: community.general.mysql_variables
|
||||
- module: community.general.mysql_db
|
||||
- module: community.general.mysql_user
|
||||
- module: community.general.mysql_replication
|
||||
|
||||
author:
|
||||
- Andrew Klychkov (@Andersson007)
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.mysql
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
# Display info from mysql-hosts group (using creds from ~/.my.cnf to connect):
|
||||
# ansible mysql-hosts -m mysql_info
|
||||
|
||||
# Display only databases and users info:
|
||||
# ansible mysql-hosts -m mysql_info -a 'filter=databases,users'
|
||||
|
||||
# Display only slave status:
|
||||
# ansible standby -m mysql_info -a 'filter=slave_status'
|
||||
|
||||
# Display all info from databases group except settings:
|
||||
# ansible databases -m mysql_info -a 'filter=!settings'
|
||||
|
||||
- name: Collect all possible information using passwordless root access
|
||||
mysql_info:
|
||||
login_user: root
|
||||
|
||||
- name: Get MySQL version with non-default credentials
|
||||
mysql_info:
|
||||
login_user: mysuperuser
|
||||
login_password: mysuperpass
|
||||
filter: version
|
||||
|
||||
- name: Collect all info except settings and users by root
|
||||
mysql_info:
|
||||
login_user: root
|
||||
login_password: rootpass
|
||||
filter: "!settings,!users"
|
||||
|
||||
- name: Collect info about databases and version using ~/.my.cnf as a credential file
|
||||
become: yes
|
||||
mysql_info:
|
||||
filter:
|
||||
- databases
|
||||
- version
|
||||
|
||||
- name: Collect info about databases and version using ~alice/.my.cnf as a credential file
|
||||
become: yes
|
||||
mysql_info:
|
||||
config_file: /home/alice/.my.cnf
|
||||
filter:
|
||||
- databases
|
||||
- version
|
||||
|
||||
- name: Collect info about databases including empty and excluding their sizes
|
||||
become: yes
|
||||
mysql_info:
|
||||
config_file: /home/alice/.my.cnf
|
||||
filter:
|
||||
- databases
|
||||
exclude_fields: db_size
|
||||
return_empty_dbs: yes
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
version:
|
||||
description: Database server version.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample: { "version": { "major": 5, "minor": 5, "release": 60 } }
|
||||
contains:
|
||||
major:
|
||||
description: Major server version.
|
||||
returned: if not excluded by filter
|
||||
type: int
|
||||
sample: 5
|
||||
minor:
|
||||
description: Minor server version.
|
||||
returned: if not excluded by filter
|
||||
type: int
|
||||
sample: 5
|
||||
release:
|
||||
description: Release server version.
|
||||
returned: if not excluded by filter
|
||||
type: int
|
||||
sample: 60
|
||||
databases:
|
||||
description: Information about databases.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample:
|
||||
- { "mysql": { "size": 656594 }, "information_schema": { "size": 73728 } }
|
||||
contains:
|
||||
size:
|
||||
description: Database size in bytes.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample: { 'size': 656594 }
|
||||
settings:
|
||||
description: Global settings (variables) information.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample:
|
||||
- { "innodb_open_files": 300, innodb_page_size": 16384 }
|
||||
global_status:
|
||||
description: Global status information.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample:
|
||||
- { "Innodb_buffer_pool_read_requests": 123, "Innodb_buffer_pool_reads": 32 }
|
||||
users:
|
||||
description: Users information.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample:
|
||||
- { "localhost": { "root": { "Alter_priv": "Y", "Alter_routine_priv": "Y" } } }
|
||||
engines:
|
||||
description: Information about the server's storage engines.
|
||||
returned: if not excluded by filter
|
||||
type: dict
|
||||
sample:
|
||||
- { "CSV": { "Comment": "CSV storage engine", "Savepoints": "NO", "Support": "YES", "Transactions": "NO", "XA": "NO" } }
|
||||
master_status:
|
||||
description: Master status information.
|
||||
returned: if master
|
||||
type: dict
|
||||
sample:
|
||||
- { "Binlog_Do_DB": "", "Binlog_Ignore_DB": "mysql", "File": "mysql-bin.000001", "Position": 769 }
|
||||
slave_status:
|
||||
description: Slave status information.
|
||||
returned: if standby
|
||||
type: dict
|
||||
sample:
|
||||
- { "192.168.1.101": { "3306": { "replication_user": { "Connect_Retry": 60, "Exec_Master_Log_Pos": 769, "Last_Errno": 0 } } } }
|
||||
slave_hosts:
|
||||
description: Slave status information.
|
||||
returned: if master
|
||||
type: dict
|
||||
sample:
|
||||
- { "2": { "Host": "", "Master_id": 1, "Port": 3306 } }
|
||||
'''
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.mysql import (
|
||||
mysql_connect,
|
||||
mysql_common_argument_spec,
|
||||
mysql_driver,
|
||||
mysql_driver_fail_msg,
|
||||
)
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
# ===========================================
|
||||
# MySQL module specific support methods.
|
||||
#
|
||||
|
||||
class MySQL_Info(object):
|
||||
|
||||
"""Class for collection MySQL instance information.
|
||||
|
||||
Arguments:
|
||||
module (AnsibleModule): Object of AnsibleModule class.
|
||||
cursor (pymysql/mysql-python): Cursor class for interaction with
|
||||
the database.
|
||||
|
||||
Note:
|
||||
If you need to add a new subset:
|
||||
1. add a new key with the same name to self.info attr in self.__init__()
|
||||
2. add a new private method to get the information
|
||||
3. add invocation of the new method to self.__collect()
|
||||
4. add info about the new subset to the DOCUMENTATION block
|
||||
5. add info about the new subset with an example to RETURN block
|
||||
"""
|
||||
|
||||
def __init__(self, module, cursor):
|
||||
self.module = module
|
||||
self.cursor = cursor
|
||||
self.info = {
|
||||
'version': {},
|
||||
'databases': {},
|
||||
'settings': {},
|
||||
'global_status': {},
|
||||
'engines': {},
|
||||
'users': {},
|
||||
'master_status': {},
|
||||
'slave_hosts': {},
|
||||
'slave_status': {},
|
||||
}
|
||||
|
||||
def get_info(self, filter_, exclude_fields, return_empty_dbs):
|
||||
"""Get MySQL instance information based on filter_.
|
||||
|
||||
Arguments:
|
||||
filter_ (list): List of collected subsets (e.g., databases, users, etc.),
|
||||
when it is empty, return all available information.
|
||||
"""
|
||||
|
||||
inc_list = []
|
||||
exc_list = []
|
||||
|
||||
if filter_:
|
||||
partial_info = {}
|
||||
|
||||
for fi in filter_:
|
||||
if fi.lstrip('!') not in self.info:
|
||||
self.module.warn('filter element: %s is not allowable, ignored' % fi)
|
||||
continue
|
||||
|
||||
if fi[0] == '!':
|
||||
exc_list.append(fi.lstrip('!'))
|
||||
|
||||
else:
|
||||
inc_list.append(fi)
|
||||
|
||||
if inc_list:
|
||||
self.__collect(exclude_fields, return_empty_dbs, set(inc_list))
|
||||
|
||||
for i in self.info:
|
||||
if i in inc_list:
|
||||
partial_info[i] = self.info[i]
|
||||
|
||||
else:
|
||||
not_in_exc_list = list(set(self.info) - set(exc_list))
|
||||
self.__collect(exclude_fields, return_empty_dbs, set(not_in_exc_list))
|
||||
|
||||
for i in self.info:
|
||||
if i not in exc_list:
|
||||
partial_info[i] = self.info[i]
|
||||
|
||||
return partial_info
|
||||
|
||||
else:
|
||||
self.__collect(exclude_fields, return_empty_dbs, set(self.info))
|
||||
return self.info
|
||||
|
||||
def __collect(self, exclude_fields, return_empty_dbs, wanted):
|
||||
"""Collect all possible subsets."""
|
||||
if 'version' in wanted or 'settings' in wanted:
|
||||
self.__get_global_variables()
|
||||
|
||||
if 'databases' in wanted:
|
||||
self.__get_databases(exclude_fields, return_empty_dbs)
|
||||
|
||||
if 'global_status' in wanted:
|
||||
self.__get_global_status()
|
||||
|
||||
if 'engines' in wanted:
|
||||
self.__get_engines()
|
||||
|
||||
if 'users' in wanted:
|
||||
self.__get_users()
|
||||
|
||||
if 'master_status' in wanted:
|
||||
self.__get_master_status()
|
||||
|
||||
if 'slave_status' in wanted:
|
||||
self.__get_slave_status()
|
||||
|
||||
if 'slave_hosts' in wanted:
|
||||
self.__get_slaves()
|
||||
|
||||
def __get_engines(self):
|
||||
"""Get storage engines info."""
|
||||
res = self.__exec_sql('SHOW ENGINES')
|
||||
|
||||
if res:
|
||||
for line in res:
|
||||
engine = line['Engine']
|
||||
self.info['engines'][engine] = {}
|
||||
|
||||
for vname, val in iteritems(line):
|
||||
if vname != 'Engine':
|
||||
self.info['engines'][engine][vname] = val
|
||||
|
||||
def __convert(self, val):
|
||||
"""Convert unserializable data."""
|
||||
try:
|
||||
if isinstance(val, Decimal):
|
||||
val = float(val)
|
||||
else:
|
||||
val = int(val)
|
||||
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
return val
|
||||
|
||||
def __get_global_variables(self):
|
||||
"""Get global variables (instance settings)."""
|
||||
res = self.__exec_sql('SHOW GLOBAL VARIABLES')
|
||||
|
||||
if res:
|
||||
for var in res:
|
||||
self.info['settings'][var['Variable_name']] = self.__convert(var['Value'])
|
||||
|
||||
ver = self.info['settings']['version'].split('.')
|
||||
release = ver[2].split('-')[0]
|
||||
|
||||
self.info['version'] = dict(
|
||||
major=int(ver[0]),
|
||||
minor=int(ver[1]),
|
||||
release=int(release),
|
||||
)
|
||||
|
||||
def __get_global_status(self):
|
||||
"""Get global status."""
|
||||
res = self.__exec_sql('SHOW GLOBAL STATUS')
|
||||
|
||||
if res:
|
||||
for var in res:
|
||||
self.info['global_status'][var['Variable_name']] = self.__convert(var['Value'])
|
||||
|
||||
def __get_master_status(self):
|
||||
"""Get master status if the instance is a master."""
|
||||
res = self.__exec_sql('SHOW MASTER STATUS')
|
||||
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."""
|
||||
res = self.__exec_sql('SHOW SLAVE STATUS')
|
||||
if res:
|
||||
for line in res:
|
||||
host = line['Master_Host']
|
||||
if host not in self.info['slave_status']:
|
||||
self.info['slave_status'][host] = {}
|
||||
|
||||
port = line['Master_Port']
|
||||
if port not in self.info['slave_status'][host]:
|
||||
self.info['slave_status'][host][port] = {}
|
||||
|
||||
user = line['Master_User']
|
||||
if user not in self.info['slave_status'][host][port]:
|
||||
self.info['slave_status'][host][port][user] = {}
|
||||
|
||||
for vname, val in iteritems(line):
|
||||
if vname not in ('Master_Host', 'Master_Port', 'Master_User'):
|
||||
self.info['slave_status'][host][port][user][vname] = self.__convert(val)
|
||||
|
||||
def __get_slaves(self):
|
||||
"""Get slave hosts info if the instance is a master."""
|
||||
res = self.__exec_sql('SHOW SLAVE HOSTS')
|
||||
if res:
|
||||
for line in res:
|
||||
srv_id = line['Server_id']
|
||||
if srv_id not in self.info['slave_hosts']:
|
||||
self.info['slave_hosts'][srv_id] = {}
|
||||
|
||||
for vname, val in iteritems(line):
|
||||
if vname != 'Server_id':
|
||||
self.info['slave_hosts'][srv_id][vname] = self.__convert(val)
|
||||
|
||||
def __get_users(self):
|
||||
"""Get user info."""
|
||||
res = self.__exec_sql('SELECT * FROM mysql.user')
|
||||
if res:
|
||||
for line in res:
|
||||
host = line['Host']
|
||||
if host not in self.info['users']:
|
||||
self.info['users'][host] = {}
|
||||
|
||||
user = line['User']
|
||||
self.info['users'][host][user] = {}
|
||||
|
||||
for vname, val in iteritems(line):
|
||||
if vname not in ('Host', 'User'):
|
||||
self.info['users'][host][user][vname] = self.__convert(val)
|
||||
|
||||
def __get_databases(self, exclude_fields, return_empty_dbs):
|
||||
"""Get info about databases."""
|
||||
if not exclude_fields:
|
||||
query = ('SELECT table_schema AS "name", '
|
||||
'SUM(data_length + index_length) AS "size" '
|
||||
'FROM information_schema.TABLES GROUP BY table_schema')
|
||||
else:
|
||||
if 'db_size' in exclude_fields:
|
||||
query = ('SELECT table_schema AS "name" '
|
||||
'FROM information_schema.TABLES GROUP BY table_schema')
|
||||
|
||||
res = self.__exec_sql(query)
|
||||
|
||||
if res:
|
||||
for db in res:
|
||||
self.info['databases'][db['name']] = {}
|
||||
|
||||
if not exclude_fields or 'db_size' not in exclude_fields:
|
||||
self.info['databases'][db['name']]['size'] = int(db['size'])
|
||||
|
||||
# If empty dbs are not needed in the returned dict, exit from the method
|
||||
if not return_empty_dbs:
|
||||
return None
|
||||
|
||||
# Add info about empty databases (issue #65727):
|
||||
res = self.__exec_sql('SHOW DATABASES')
|
||||
if res:
|
||||
for db in res:
|
||||
if db['Database'] not in self.info['databases']:
|
||||
self.info['databases'][db['Database']] = {}
|
||||
|
||||
if not exclude_fields or 'db_size' not in exclude_fields:
|
||||
self.info['databases'][db['Database']]['size'] = 0
|
||||
|
||||
def __exec_sql(self, query, ddl=False):
|
||||
"""Execute SQL.
|
||||
|
||||
Arguments:
|
||||
ddl (bool): If True, return True or False.
|
||||
Used for queries that don't return any rows
|
||||
(mainly for DDL queries) (default False).
|
||||
"""
|
||||
try:
|
||||
self.cursor.execute(query)
|
||||
|
||||
if not ddl:
|
||||
res = self.cursor.fetchall()
|
||||
return res
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.module.fail_json(msg="Cannot execute SQL '%s': %s" % (query, to_native(e)))
|
||||
return False
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Module execution.
|
||||
#
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = mysql_common_argument_spec()
|
||||
argument_spec.update(
|
||||
login_db=dict(type='str'),
|
||||
filter=dict(type='list'),
|
||||
exclude_fields=dict(type='list'),
|
||||
return_empty_dbs=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
db = module.params['login_db']
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
login_user = module.params['login_user']
|
||||
login_password = module.params['login_password']
|
||||
ssl_cert = module.params['client_cert']
|
||||
ssl_key = module.params['client_key']
|
||||
ssl_ca = module.params['ca_cert']
|
||||
config_file = module.params['config_file']
|
||||
filter_ = module.params['filter']
|
||||
exclude_fields = module.params['exclude_fields']
|
||||
return_empty_dbs = module.params['return_empty_dbs']
|
||||
|
||||
if filter_:
|
||||
filter_ = [f.strip() for f in filter_]
|
||||
|
||||
if exclude_fields:
|
||||
exclude_fields = set([f.strip() for f in exclude_fields])
|
||||
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
try:
|
||||
cursor, db_conn = mysql_connect(module, login_user, login_password,
|
||||
config_file, ssl_cert, ssl_key, ssl_ca, db,
|
||||
connect_timeout=connect_timeout, cursor_class='DictCursor')
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "
|
||||
"Exception message: %s" % (config_file, to_native(e)))
|
||||
|
||||
###############################
|
||||
# Create object and do main job
|
||||
|
||||
mysql = MySQL_Info(module, cursor)
|
||||
|
||||
module.exit_json(changed=False, **mysql.get_info(filter_, exclude_fields, return_empty_dbs))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,233 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_query
|
||||
short_description: Run MySQL queries
|
||||
description:
|
||||
- Runs arbitrary MySQL queries.
|
||||
- Pay attention, the module does not support check mode!
|
||||
All queries will be executed in autocommit mode.
|
||||
version_added: '0.2.0'
|
||||
options:
|
||||
query:
|
||||
description:
|
||||
- SQL query to run. Multiple queries can be passed using YAML list syntax.
|
||||
type: list
|
||||
elements: str
|
||||
required: yes
|
||||
positional_args:
|
||||
description:
|
||||
- List of values to be passed as positional arguments to the query.
|
||||
- Mutually exclusive with I(named_args).
|
||||
type: list
|
||||
named_args:
|
||||
description:
|
||||
- Dictionary of key-value arguments to pass to the query.
|
||||
- Mutually exclusive with I(positional_args).
|
||||
type: dict
|
||||
login_db:
|
||||
description:
|
||||
- Name of database to connect to and run queries against.
|
||||
type: str
|
||||
single_transaction:
|
||||
description:
|
||||
- Where passed queries run in a single transaction (C(yes)) or commit them one-by-one (C(no)).
|
||||
type: bool
|
||||
default: no
|
||||
notes:
|
||||
- To pass a query containing commas, use YAML list notation with hyphen (see EXAMPLES block).
|
||||
author:
|
||||
- Andrew Klychkov (@Andersson007)
|
||||
extends_documentation_fragment:
|
||||
- community.general.mysql
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Simple select query to acme db
|
||||
mysql_query:
|
||||
login_db: acme
|
||||
query: SELECT * FROM orders
|
||||
|
||||
- name: Select query to db acme with positional arguments
|
||||
mysql_query:
|
||||
login_db: acme
|
||||
query: SELECT * FROM acme WHERE id = %s AND story = %s
|
||||
positional_args:
|
||||
- 1
|
||||
- test
|
||||
|
||||
- name: Select query to test_db with named_args
|
||||
mysql_query:
|
||||
login_db: test_db
|
||||
query: SELECT * FROM test WHERE id = %(id_val)s AND story = %(story_val)s
|
||||
named_args:
|
||||
id_val: 1
|
||||
story_val: test
|
||||
|
||||
- name: Run several insert queries against db test_db in single transaction
|
||||
mysql_query:
|
||||
login_db: test_db
|
||||
query:
|
||||
- INSERT INTO articles (id, story) VALUES (2, 'my_long_story')
|
||||
- INSERT INTO prices (id, price) VALUES (123, '100.00')
|
||||
single_transaction: yes
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
executed_queries:
|
||||
description: List of executed queries.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['SELECT * FROM bar', 'UPDATE bar SET id = 1 WHERE id = 2']
|
||||
query_result:
|
||||
description:
|
||||
- List of lists (sublist for each query) containing dictionaries
|
||||
in column:value form representing returned rows.
|
||||
returned: changed
|
||||
type: list
|
||||
sample: [[{"Column": "Value1"},{"Column": "Value2"}], [{"ID": 1}, {"ID": 2}]]
|
||||
rowcount:
|
||||
description: Number of affected rows for each subquery.
|
||||
returned: changed
|
||||
type: list
|
||||
sample: [5, 1]
|
||||
'''
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.mysql import (
|
||||
mysql_connect,
|
||||
mysql_common_argument_spec,
|
||||
mysql_driver,
|
||||
mysql_driver_fail_msg,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
DML_QUERY_KEYWORDS = ('INSERT', 'UPDATE', 'DELETE')
|
||||
# TRUNCATE is not DDL query but it also returns 0 rows affected:
|
||||
DDL_QUERY_KEYWORDS = ('CREATE', 'DROP', 'ALTER', 'RENAME', 'TRUNCATE')
|
||||
|
||||
|
||||
# ===========================================
|
||||
# Module execution.
|
||||
#
|
||||
|
||||
def main():
|
||||
argument_spec = mysql_common_argument_spec()
|
||||
argument_spec.update(
|
||||
query=dict(type='list', elements='str', required=True),
|
||||
login_db=dict(type='str'),
|
||||
positional_args=dict(type='list'),
|
||||
named_args=dict(type='dict'),
|
||||
single_transaction=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=(
|
||||
('positional_args', 'named_args'),
|
||||
),
|
||||
)
|
||||
|
||||
db = module.params['login_db']
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
login_user = module.params['login_user']
|
||||
login_password = module.params['login_password']
|
||||
ssl_cert = module.params['client_cert']
|
||||
ssl_key = module.params['client_key']
|
||||
ssl_ca = module.params['ca_cert']
|
||||
config_file = module.params['config_file']
|
||||
query = module.params["query"]
|
||||
if module.params["single_transaction"]:
|
||||
autocommit = False
|
||||
else:
|
||||
autocommit = True
|
||||
# Prepare args:
|
||||
if module.params.get("positional_args"):
|
||||
arguments = module.params["positional_args"]
|
||||
elif module.params.get("named_args"):
|
||||
arguments = module.params["named_args"]
|
||||
else:
|
||||
arguments = None
|
||||
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
# Connect to DB:
|
||||
try:
|
||||
cursor, db_connection = mysql_connect(module, login_user, login_password,
|
||||
config_file, ssl_cert, ssl_key, ssl_ca, db,
|
||||
connect_timeout=connect_timeout,
|
||||
cursor_class='DictCursor', autocommit=autocommit)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to connect to database, check login_user and "
|
||||
"login_password are correct or %s has the credentials. "
|
||||
"Exception message: %s" % (config_file, to_native(e)))
|
||||
# Set defaults:
|
||||
changed = False
|
||||
|
||||
max_keyword_len = len(max(DML_QUERY_KEYWORDS + DDL_QUERY_KEYWORDS, key=len))
|
||||
|
||||
# Execute query:
|
||||
query_result = []
|
||||
executed_queries = []
|
||||
rowcount = []
|
||||
for q in query:
|
||||
try:
|
||||
cursor.execute(q, arguments)
|
||||
|
||||
except Exception as e:
|
||||
if not autocommit:
|
||||
db_connection.rollback()
|
||||
|
||||
cursor.close()
|
||||
module.fail_json(msg="Cannot execute SQL '%s' args [%s]: %s" % (q, arguments, to_native(e)))
|
||||
|
||||
try:
|
||||
query_result.append([dict(row) for row in cursor.fetchall()])
|
||||
|
||||
except Exception as e:
|
||||
if not autocommit:
|
||||
db_connection.rollback()
|
||||
|
||||
module.fail_json(msg="Cannot fetch rows from cursor: %s" % to_native(e))
|
||||
|
||||
# Check DML or DDL keywords in query and set changed accordingly:
|
||||
q = q.lstrip()[0:max_keyword_len].upper()
|
||||
for keyword in DML_QUERY_KEYWORDS:
|
||||
if keyword in q and cursor.rowcount > 0:
|
||||
changed = True
|
||||
|
||||
for keyword in DDL_QUERY_KEYWORDS:
|
||||
if keyword in q:
|
||||
changed = True
|
||||
|
||||
executed_queries.append(cursor._last_executed)
|
||||
rowcount.append(cursor.rowcount)
|
||||
|
||||
# When the module run with the single_transaction == True:
|
||||
if not autocommit:
|
||||
db_connection.commit()
|
||||
|
||||
# Create dict with returned values:
|
||||
kw = {
|
||||
'changed': changed,
|
||||
'executed_queries': executed_queries,
|
||||
'query_result': query_result,
|
||||
'rowcount': rowcount,
|
||||
}
|
||||
|
||||
# Exit:
|
||||
module.exit_json(**kw)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,573 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2013, Balazs Pocze <banyek@gawker.com>
|
||||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# Certain parts are taken from Mark Theunissen's mysqldb module
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_replication
|
||||
short_description: Manage MySQL replication
|
||||
description:
|
||||
- Manages MySQL server replication, slave, master status, get and change master host.
|
||||
author:
|
||||
- Balazs Pocze (@banyek)
|
||||
- Andrew Klychkov (@Andersson007)
|
||||
options:
|
||||
mode:
|
||||
description:
|
||||
- Module operating mode. Could be
|
||||
C(changemaster) (CHANGE MASTER TO),
|
||||
C(getmaster) (SHOW MASTER STATUS),
|
||||
C(getslave) (SHOW SLAVE STATUS),
|
||||
C(startslave) (START SLAVE),
|
||||
C(stopslave) (STOP SLAVE),
|
||||
C(resetmaster) (RESET MASTER) - supported since community.general 0.2.0,
|
||||
C(resetslave) (RESET SLAVE),
|
||||
C(resetslaveall) (RESET SLAVE ALL).
|
||||
type: str
|
||||
choices:
|
||||
- changemaster
|
||||
- getmaster
|
||||
- getslave
|
||||
- startslave
|
||||
- stopslave
|
||||
- resetmaster
|
||||
- resetslave
|
||||
- resetslaveall
|
||||
default: getslave
|
||||
master_host:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_user:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_password:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_port:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: int
|
||||
master_connect_retry:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: int
|
||||
master_log_file:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_log_pos:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: int
|
||||
relay_log_file:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
relay_log_pos:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: int
|
||||
master_ssl:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: bool
|
||||
master_ssl_ca:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_capath:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_cert:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_key:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_cipher:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_auto_position:
|
||||
description:
|
||||
- Whether the host uses GTID based replication or not.
|
||||
type: bool
|
||||
master_use_gtid:
|
||||
description:
|
||||
- Configures the slave to use the MariaDB Global Transaction ID.
|
||||
- C(disabled) equals MASTER_USE_GTID=no command.
|
||||
- To find information about available values see
|
||||
U(https://mariadb.com/kb/en/library/change-master-to/#master_use_gtid).
|
||||
- Available since MariaDB 10.0.2.
|
||||
choices: [current_pos, slave_pos, disabled]
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
master_delay:
|
||||
description:
|
||||
- Time lag behind the master's state (in seconds).
|
||||
- Available from MySQL 5.6.
|
||||
- For more information see U(https://dev.mysql.com/doc/refman/8.0/en/replication-delayed.html).
|
||||
type: int
|
||||
version_added: '0.2.0'
|
||||
connection_name:
|
||||
description:
|
||||
- Name of the master connection.
|
||||
- Supported from MariaDB 10.0.1.
|
||||
- Mutually exclusive with I(channel).
|
||||
- For more information see U(https://mariadb.com/kb/en/library/multi-source-replication/).
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
channel:
|
||||
description:
|
||||
- Name of replication channel.
|
||||
- Multi-source replication is supported from MySQL 5.7.
|
||||
- Mutually exclusive with I(connection_name).
|
||||
- For more information see U(https://dev.mysql.com/doc/refman/8.0/en/replication-multi-source.html).
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
fail_on_error:
|
||||
description:
|
||||
- Fails on error when calling mysql.
|
||||
type: bool
|
||||
default: False
|
||||
version_added: '0.2.0'
|
||||
|
||||
notes:
|
||||
- If an empty value for the parameter of string type is needed, use an empty string.
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.mysql
|
||||
|
||||
|
||||
seealso:
|
||||
- module: community.general.mysql_info
|
||||
- name: MySQL replication reference
|
||||
description: Complete reference of the MySQL replication documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/replication.html
|
||||
- name: MariaDB replication reference
|
||||
description: Complete reference of the MariaDB replication documentation.
|
||||
link: https://mariadb.com/kb/en/library/setting-up-replication/
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Stop mysql slave thread
|
||||
mysql_replication:
|
||||
mode: stopslave
|
||||
|
||||
- name: Get master binlog file name and binlog position
|
||||
mysql_replication:
|
||||
mode: getmaster
|
||||
|
||||
- name: Change master to master server 192.0.2.1 and use binary log 'mysql-bin.000009' with position 4578
|
||||
mysql_replication:
|
||||
mode: changemaster
|
||||
master_host: 192.0.2.1
|
||||
master_log_file: mysql-bin.000009
|
||||
master_log_pos: 4578
|
||||
|
||||
- name: Check slave status using port 3308
|
||||
mysql_replication:
|
||||
mode: getslave
|
||||
login_host: ansible.example.com
|
||||
login_port: 3308
|
||||
|
||||
- name: On MariaDB change master to use GTID current_pos
|
||||
mysql_replication:
|
||||
mode: changemaster
|
||||
master_use_gtid: current_pos
|
||||
|
||||
- name: Change master to use replication delay 3600 seconds
|
||||
mysql_replication:
|
||||
mode: changemaster
|
||||
master_host: 192.0.2.1
|
||||
master_delay: 3600
|
||||
|
||||
- name: Start MariaDB standby with connection name master-1
|
||||
mysql_replication:
|
||||
mode: startslave
|
||||
connection_name: master-1
|
||||
|
||||
- name: Stop replication in channel master-1
|
||||
mysql_replication:
|
||||
mode: stopslave
|
||||
channel: master-1
|
||||
|
||||
- name: >
|
||||
Run RESET MASTER command which will delete all existing binary log files
|
||||
and reset the binary log index file on the master
|
||||
mysql_replication:
|
||||
mode: resetmaster
|
||||
|
||||
- name: Run start slave and fail the task on errors
|
||||
mysql_replication:
|
||||
mode: startslave
|
||||
connection_name: master-1
|
||||
fail_on_error: yes
|
||||
|
||||
- name: Change master and fail on error (like when slave thread is running)
|
||||
mysql_replication:
|
||||
mode: changemaster
|
||||
fail_on_error: yes
|
||||
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
queries:
|
||||
description: List of executed queries which modified DB's state.
|
||||
returned: always
|
||||
type: list
|
||||
sample: ["CHANGE MASTER TO MASTER_HOST='master2.example.com',MASTER_PORT=3306"]
|
||||
version_added: '0.2.0'
|
||||
'''
|
||||
|
||||
import os
|
||||
import warnings
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
executed_queries = []
|
||||
|
||||
|
||||
def get_master_status(cursor):
|
||||
cursor.execute("SHOW MASTER STATUS")
|
||||
masterstatus = cursor.fetchone()
|
||||
return masterstatus
|
||||
|
||||
|
||||
def get_slave_status(cursor, connection_name='', channel=''):
|
||||
if connection_name:
|
||||
query = "SHOW SLAVE '%s' STATUS" % connection_name
|
||||
else:
|
||||
query = "SHOW SLAVE STATUS"
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
cursor.execute(query)
|
||||
slavestatus = cursor.fetchone()
|
||||
return slavestatus
|
||||
|
||||
|
||||
def stop_slave(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "STOP SLAVE '%s'" % connection_name
|
||||
else:
|
||||
query = 'STOP SLAVE'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
stopped = True
|
||||
except mysql_driver.Warning as e:
|
||||
stopped = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="STOP SLAVE failed: %s" % to_native(e))
|
||||
stopped = False
|
||||
return stopped
|
||||
|
||||
|
||||
def reset_slave(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "RESET SLAVE '%s'" % connection_name
|
||||
else:
|
||||
query = 'RESET SLAVE'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
reset = True
|
||||
except mysql_driver.Warning as e:
|
||||
reset = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="RESET SLAVE failed: %s" % to_native(e))
|
||||
reset = False
|
||||
return reset
|
||||
|
||||
|
||||
def reset_slave_all(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "RESET SLAVE '%s' ALL" % connection_name
|
||||
else:
|
||||
query = 'RESET SLAVE ALL'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
reset = True
|
||||
except mysql_driver.Warning as e:
|
||||
reset = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="RESET SLAVE ALL failed: %s" % to_native(e))
|
||||
reset = False
|
||||
return reset
|
||||
|
||||
|
||||
def reset_master(module, cursor, fail_on_error=False):
|
||||
query = 'RESET MASTER'
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
reset = True
|
||||
except mysql_driver.Warning as e:
|
||||
reset = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="RESET MASTER failed: %s" % to_native(e))
|
||||
reset = False
|
||||
return reset
|
||||
|
||||
|
||||
def start_slave(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "START SLAVE '%s'" % connection_name
|
||||
else:
|
||||
query = 'START SLAVE'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
started = True
|
||||
except mysql_driver.Warning as e:
|
||||
started = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="START SLAVE failed: %s" % to_native(e))
|
||||
started = False
|
||||
return started
|
||||
|
||||
|
||||
def changemaster(cursor, chm, connection_name='', channel=''):
|
||||
if connection_name:
|
||||
query = "CHANGE MASTER '%s' TO %s" % (connection_name, ','.join(chm))
|
||||
else:
|
||||
query = 'CHANGE MASTER TO %s' % ','.join(chm)
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
login_user=dict(type='str'),
|
||||
login_password=dict(type='str', no_log=True),
|
||||
login_host=dict(type='str', default='localhost'),
|
||||
login_port=dict(type='int', default=3306),
|
||||
login_unix_socket=dict(type='str'),
|
||||
mode=dict(type='str', default='getslave', choices=[
|
||||
'getmaster', 'getslave', 'changemaster', 'stopslave',
|
||||
'startslave', 'resetmaster', 'resetslave', 'resetslaveall']),
|
||||
master_auto_position=dict(type='bool', default=False),
|
||||
master_host=dict(type='str'),
|
||||
master_user=dict(type='str'),
|
||||
master_password=dict(type='str', no_log=True),
|
||||
master_port=dict(type='int'),
|
||||
master_connect_retry=dict(type='int'),
|
||||
master_log_file=dict(type='str'),
|
||||
master_log_pos=dict(type='int'),
|
||||
relay_log_file=dict(type='str'),
|
||||
relay_log_pos=dict(type='int'),
|
||||
master_ssl=dict(type='bool', default=False),
|
||||
master_ssl_ca=dict(type='str'),
|
||||
master_ssl_capath=dict(type='str'),
|
||||
master_ssl_cert=dict(type='str'),
|
||||
master_ssl_key=dict(type='str'),
|
||||
master_ssl_cipher=dict(type='str'),
|
||||
connect_timeout=dict(type='int', default=30),
|
||||
config_file=dict(type='path', default='~/.my.cnf'),
|
||||
client_cert=dict(type='path', aliases=['ssl_cert']),
|
||||
client_key=dict(type='path', aliases=['ssl_key']),
|
||||
ca_cert=dict(type='path', aliases=['ssl_ca']),
|
||||
master_use_gtid=dict(type='str', choices=['current_pos', 'slave_pos', 'disabled']),
|
||||
master_delay=dict(type='int'),
|
||||
connection_name=dict(type='str'),
|
||||
channel=dict(type='str'),
|
||||
fail_on_error=dict(type='bool', default=False),
|
||||
),
|
||||
mutually_exclusive=[
|
||||
['connection_name', 'channel']
|
||||
],
|
||||
)
|
||||
mode = module.params["mode"]
|
||||
master_host = module.params["master_host"]
|
||||
master_user = module.params["master_user"]
|
||||
master_password = module.params["master_password"]
|
||||
master_port = module.params["master_port"]
|
||||
master_connect_retry = module.params["master_connect_retry"]
|
||||
master_log_file = module.params["master_log_file"]
|
||||
master_log_pos = module.params["master_log_pos"]
|
||||
relay_log_file = module.params["relay_log_file"]
|
||||
relay_log_pos = module.params["relay_log_pos"]
|
||||
master_ssl = module.params["master_ssl"]
|
||||
master_ssl_ca = module.params["master_ssl_ca"]
|
||||
master_ssl_capath = module.params["master_ssl_capath"]
|
||||
master_ssl_cert = module.params["master_ssl_cert"]
|
||||
master_ssl_key = module.params["master_ssl_key"]
|
||||
master_ssl_cipher = module.params["master_ssl_cipher"]
|
||||
master_auto_position = module.params["master_auto_position"]
|
||||
ssl_cert = module.params["client_cert"]
|
||||
ssl_key = module.params["client_key"]
|
||||
ssl_ca = module.params["ca_cert"]
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
config_file = module.params['config_file']
|
||||
master_delay = module.params['master_delay']
|
||||
if module.params.get("master_use_gtid") == 'disabled':
|
||||
master_use_gtid = 'no'
|
||||
else:
|
||||
master_use_gtid = module.params["master_use_gtid"]
|
||||
connection_name = module.params["connection_name"]
|
||||
channel = module.params['channel']
|
||||
fail_on_error = module.params['fail_on_error']
|
||||
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
else:
|
||||
warnings.filterwarnings('error', category=mysql_driver.Warning)
|
||||
|
||||
login_password = module.params["login_password"]
|
||||
login_user = module.params["login_user"]
|
||||
|
||||
try:
|
||||
cursor, db_conn = mysql_connect(module, login_user, login_password, config_file,
|
||||
ssl_cert, ssl_key, ssl_ca, None, cursor_class='DictCursor',
|
||||
connect_timeout=connect_timeout)
|
||||
except Exception as e:
|
||||
if os.path.exists(config_file):
|
||||
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "
|
||||
"Exception message: %s" % (config_file, to_native(e)))
|
||||
else:
|
||||
module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e)))
|
||||
|
||||
if mode in "getmaster":
|
||||
status = get_master_status(cursor)
|
||||
if not isinstance(status, dict):
|
||||
status = dict(Is_Master=False, msg="Server is not configured as mysql master")
|
||||
else:
|
||||
status['Is_Master'] = True
|
||||
module.exit_json(queries=executed_queries, **status)
|
||||
|
||||
elif mode in "getslave":
|
||||
status = get_slave_status(cursor, connection_name, channel)
|
||||
if not isinstance(status, dict):
|
||||
status = dict(Is_Slave=False, msg="Server is not configured as mysql slave")
|
||||
else:
|
||||
status['Is_Slave'] = True
|
||||
module.exit_json(queries=executed_queries, **status)
|
||||
|
||||
elif mode in "changemaster":
|
||||
chm = []
|
||||
result = {}
|
||||
if master_host is not None:
|
||||
chm.append("MASTER_HOST='%s'" % master_host)
|
||||
if master_user is not None:
|
||||
chm.append("MASTER_USER='%s'" % master_user)
|
||||
if master_password is not None:
|
||||
chm.append("MASTER_PASSWORD='%s'" % master_password)
|
||||
if master_port is not None:
|
||||
chm.append("MASTER_PORT=%s" % master_port)
|
||||
if master_connect_retry is not None:
|
||||
chm.append("MASTER_CONNECT_RETRY=%s" % master_connect_retry)
|
||||
if master_log_file is not None:
|
||||
chm.append("MASTER_LOG_FILE='%s'" % master_log_file)
|
||||
if master_log_pos is not None:
|
||||
chm.append("MASTER_LOG_POS=%s" % master_log_pos)
|
||||
if master_delay is not None:
|
||||
chm.append("MASTER_DELAY=%s" % master_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 master_ssl:
|
||||
chm.append("MASTER_SSL=1")
|
||||
if master_ssl_ca is not None:
|
||||
chm.append("MASTER_SSL_CA='%s'" % master_ssl_ca)
|
||||
if master_ssl_capath is not None:
|
||||
chm.append("MASTER_SSL_CAPATH='%s'" % master_ssl_capath)
|
||||
if master_ssl_cert is not None:
|
||||
chm.append("MASTER_SSL_CERT='%s'" % master_ssl_cert)
|
||||
if master_ssl_key is not None:
|
||||
chm.append("MASTER_SSL_KEY='%s'" % master_ssl_key)
|
||||
if master_ssl_cipher is not None:
|
||||
chm.append("MASTER_SSL_CIPHER='%s'" % master_ssl_cipher)
|
||||
if master_auto_position:
|
||||
chm.append("MASTER_AUTO_POSITION=1")
|
||||
if master_use_gtid is not None:
|
||||
chm.append("MASTER_USE_GTID=%s" % master_use_gtid)
|
||||
try:
|
||||
changemaster(cursor, 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))
|
||||
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)
|
||||
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)
|
||||
if stopped is True:
|
||||
module.exit_json(msg="Slave stopped", changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.exit_json(msg="Slave already stopped", changed=False, queries=executed_queries)
|
||||
elif mode in "resetmaster":
|
||||
reset = reset_master(module, cursor, fail_on_error)
|
||||
if reset is True:
|
||||
module.exit_json(msg="Master reset", changed=True, queries=executed_queries)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
||||
warnings.simplefilter("ignore")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,991 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2012, Mark Theunissen <mark.theunissen@gmail.com>
|
||||
# Sponsored by Four Kitchens http://fourkitchens.com.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_user
|
||||
short_description: Adds or removes a user from a MySQL database
|
||||
description:
|
||||
- Adds or removes a user from a MySQL database.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the user (role) to add or remove.
|
||||
type: str
|
||||
required: true
|
||||
password:
|
||||
description:
|
||||
- Set the user's password..
|
||||
type: str
|
||||
encrypted:
|
||||
description:
|
||||
- Indicate that the 'password' field is a `mysql_native_password` hash.
|
||||
type: bool
|
||||
default: no
|
||||
host:
|
||||
description:
|
||||
- The 'host' part of the MySQL username.
|
||||
type: str
|
||||
default: localhost
|
||||
host_all:
|
||||
description:
|
||||
- Override the host option, making ansible apply changes to all hostnames for a given user.
|
||||
- This option cannot be used when creating users.
|
||||
type: bool
|
||||
default: no
|
||||
priv:
|
||||
description:
|
||||
- "MySQL privileges string in the format: C(db.table:priv1,priv2)."
|
||||
- "Multiple privileges can be specified by separating each one using
|
||||
a forward slash: C(db.table:priv/db.table:priv)."
|
||||
- The format is based on MySQL C(GRANT) statement.
|
||||
- Database and table names can be quoted, MySQL-style.
|
||||
- If column privileges are used, the C(priv1,priv2) part must be
|
||||
exactly as returned by a C(SHOW GRANT) statement. If not followed,
|
||||
the module will always report changes. It includes grouping columns
|
||||
by permission (C(SELECT(col1,col2)) instead of C(SELECT(col1),SELECT(col2))).
|
||||
- Can be passed as a dictionary (see the examples).
|
||||
type: raw
|
||||
append_privs:
|
||||
description:
|
||||
- Append the privileges defined by priv to the existing ones for this
|
||||
user instead of overwriting existing ones.
|
||||
type: bool
|
||||
default: no
|
||||
sql_log_bin:
|
||||
description:
|
||||
- Whether binary logging should be enabled or disabled for the connection.
|
||||
type: bool
|
||||
default: yes
|
||||
state:
|
||||
description:
|
||||
- Whether the user should exist.
|
||||
- When C(absent), removes the user.
|
||||
type: str
|
||||
choices: [ absent, present ]
|
||||
default: present
|
||||
check_implicit_admin:
|
||||
description:
|
||||
- Check if mysql allows login as root/nopassword before trying supplied credentials.
|
||||
- If success, passed I(login_user)/I(login_password) will be ignored.
|
||||
type: bool
|
||||
default: no
|
||||
update_password:
|
||||
description:
|
||||
- C(always) will update passwords if they differ.
|
||||
- C(on_create) will only set the password for newly created users.
|
||||
type: str
|
||||
choices: [ always, on_create ]
|
||||
default: always
|
||||
plugin:
|
||||
description:
|
||||
- User's plugin to authenticate (``CREATE USER user IDENTIFIED WITH plugin``).
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
plugin_hash_string:
|
||||
description:
|
||||
- User's plugin hash string (``CREATE USER user IDENTIFIED WITH plugin AS plugin_hash_string``).
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
plugin_auth_string:
|
||||
description:
|
||||
- User's plugin auth_string (``CREATE USER user IDENTIFIED WITH plugin BY plugin_auth_string``).
|
||||
type: str
|
||||
version_added: '0.2.0'
|
||||
resource_limits:
|
||||
description:
|
||||
- Limit the user for certain server resources. Provided since MySQL 5.6 / MariaDB 10.2.
|
||||
- "Available options are C(MAX_QUERIES_PER_HOUR: num), C(MAX_UPDATES_PER_HOUR: num),
|
||||
C(MAX_CONNECTIONS_PER_HOUR: num), C(MAX_USER_CONNECTIONS: num)."
|
||||
- Used when I(state=present), ignored otherwise.
|
||||
type: dict
|
||||
version_added: '0.2.0'
|
||||
|
||||
notes:
|
||||
- "MySQL server installs with default login_user of 'root' and no password. To secure this user
|
||||
as part of an idempotent playbook, you must create at least two tasks: the first must change the root user's password,
|
||||
without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing
|
||||
the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from
|
||||
the file."
|
||||
- Currently, there is only support for the `mysql_native_password` encrypted password hash module.
|
||||
|
||||
seealso:
|
||||
- module: community.general.mysql_info
|
||||
- name: MySQL access control and account management reference
|
||||
description: Complete reference of the MySQL access control and account management documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/access-control.html
|
||||
- name: MySQL provided privileges reference
|
||||
description: Complete reference of the MySQL provided privileges documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html
|
||||
|
||||
author:
|
||||
- Jonathan Mainguy (@Jmainguy)
|
||||
- Benjamin Malynovytch (@bmalynovytch)
|
||||
- Lukasz Tomaszkiewicz (@tomaszkiewicz)
|
||||
extends_documentation_fragment:
|
||||
- community.general.mysql
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Removes anonymous user account for localhost
|
||||
mysql_user:
|
||||
name: ''
|
||||
host: localhost
|
||||
state: absent
|
||||
|
||||
- name: Removes all anonymous user accounts
|
||||
mysql_user:
|
||||
name: ''
|
||||
host_all: yes
|
||||
state: absent
|
||||
|
||||
- name: Create database user with name 'bob' and password '12345' with all database privileges
|
||||
mysql_user:
|
||||
name: bob
|
||||
password: 12345
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
|
||||
- name: Create database user using hashed password with all database privileges
|
||||
mysql_user:
|
||||
name: bob
|
||||
password: '*EE0D72C1085C46C5278932678FBE2C6A782821B4'
|
||||
encrypted: yes
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
|
||||
- name: Create database user with password and all database privileges and 'WITH GRANT OPTION'
|
||||
mysql_user:
|
||||
name: bob
|
||||
password: 12345
|
||||
priv: '*.*:ALL,GRANT'
|
||||
state: present
|
||||
|
||||
- name: Create user with password, all database privileges and 'WITH GRANT OPTION' in db1 and db2
|
||||
mysql_user:
|
||||
state: present
|
||||
name: bob
|
||||
password: 12345dd
|
||||
priv:
|
||||
'db1.*': 'ALL,GRANT'
|
||||
'db2.*': 'ALL,GRANT'
|
||||
|
||||
# Note that REQUIRESSL is a special privilege that should only apply to *.* by itself.
|
||||
- name: Modify user to require SSL connections.
|
||||
mysql_user:
|
||||
name: bob
|
||||
append_privs: yes
|
||||
priv: '*.*:REQUIRESSL'
|
||||
state: present
|
||||
|
||||
- name: Ensure no user named 'sally'@'localhost' exists, also passing in the auth credentials.
|
||||
mysql_user:
|
||||
login_user: root
|
||||
login_password: 123456
|
||||
name: sally
|
||||
state: absent
|
||||
|
||||
# check_implicit_admin example
|
||||
- name: >
|
||||
Ensure no user named 'sally'@'localhost' exists, also passing in the auth credentials.
|
||||
If mysql allows root/nopassword login, try it without the credentials first.
|
||||
If it's not allowed, pass the credentials.
|
||||
mysql_user:
|
||||
check_implicit_admin: yes
|
||||
login_user: root
|
||||
login_password: 123456
|
||||
name: sally
|
||||
state: absent
|
||||
|
||||
- name: Ensure no user named 'sally' exists at all
|
||||
mysql_user:
|
||||
name: sally
|
||||
host_all: yes
|
||||
state: absent
|
||||
|
||||
- name: Specify grants composed of more than one word
|
||||
mysql_user:
|
||||
name: replication
|
||||
password: 12345
|
||||
priv: "*.*:REPLICATION CLIENT"
|
||||
state: present
|
||||
|
||||
- name: Revoke all privileges for user 'bob' and password '12345'
|
||||
mysql_user:
|
||||
name: bob
|
||||
password: 12345
|
||||
priv: "*.*:USAGE"
|
||||
state: present
|
||||
|
||||
# Example privileges string format
|
||||
# mydb.*:INSERT,UPDATE/anotherdb.*:SELECT/yetanotherdb.*:ALL
|
||||
|
||||
- name: Example using login_unix_socket to connect to server
|
||||
mysql_user:
|
||||
name: root
|
||||
password: abc123
|
||||
login_unix_socket: /var/run/mysqld/mysqld.sock
|
||||
|
||||
- name: Example of skipping binary logging while adding user 'bob'
|
||||
mysql_user:
|
||||
name: bob
|
||||
password: 12345
|
||||
priv: "*.*:USAGE"
|
||||
state: present
|
||||
sql_log_bin: no
|
||||
|
||||
- name: Create user 'bob' authenticated with plugin 'AWSAuthenticationPlugin'
|
||||
mysql_user:
|
||||
name: bob
|
||||
plugin: AWSAuthenticationPlugin
|
||||
plugin_hash_string: RDS
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
|
||||
- name: Limit bob's resources to 10 queries per hour and 5 connections per hour
|
||||
mysql_user:
|
||||
name: bob
|
||||
resource_limits:
|
||||
MAX_QUERIES_PER_HOUR: 10
|
||||
MAX_CONNECTIONS_PER_HOUR: 5
|
||||
|
||||
# Example .my.cnf file for setting the root password
|
||||
# [client]
|
||||
# user=root
|
||||
# password=n<_665{vS43y
|
||||
'''
|
||||
|
||||
import re
|
||||
import string
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.database import SQLParseError
|
||||
from ansible_collections.community.general.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
VALID_PRIVS = frozenset(('CREATE', 'DROP', 'GRANT', 'GRANT OPTION',
|
||||
'LOCK TABLES', 'REFERENCES', 'EVENT', 'ALTER',
|
||||
'DELETE', 'INDEX', 'INSERT', 'SELECT', 'UPDATE',
|
||||
'CREATE TEMPORARY TABLES', 'TRIGGER', 'CREATE VIEW',
|
||||
'SHOW VIEW', 'ALTER ROUTINE', 'CREATE ROUTINE',
|
||||
'EXECUTE', 'FILE', 'CREATE TABLESPACE', 'CREATE USER',
|
||||
'PROCESS', 'PROXY', 'RELOAD', 'REPLICATION CLIENT',
|
||||
'REPLICATION SLAVE', 'SHOW DATABASES', 'SHUTDOWN',
|
||||
'SUPER', 'ALL', 'ALL PRIVILEGES', 'USAGE', 'REQUIRESSL',
|
||||
'CREATE ROLE', 'DROP ROLE', 'APPLICATION_PASSWORD_ADMIN',
|
||||
'AUDIT_ADMIN', 'BACKUP_ADMIN', 'BINLOG_ADMIN',
|
||||
'BINLOG_ENCRYPTION_ADMIN', 'CLONE_ADMIN', 'CONNECTION_ADMIN',
|
||||
'ENCRYPTION_KEY_ADMIN', 'FIREWALL_ADMIN', 'FIREWALL_USER',
|
||||
'GROUP_REPLICATION_ADMIN', 'INNODB_REDO_LOG_ARCHIVE',
|
||||
'NDB_STORED_USER', 'PERSIST_RO_VARIABLES_ADMIN',
|
||||
'REPLICATION_APPLIER', 'REPLICATION_SLAVE_ADMIN',
|
||||
'RESOURCE_GROUP_ADMIN', 'RESOURCE_GROUP_USER',
|
||||
'ROLE_ADMIN', 'SESSION_VARIABLES_ADMIN', 'SET_USER_ID',
|
||||
'SYSTEM_USER', 'SYSTEM_VARIABLES_ADMIN', 'SYSTEM_USER',
|
||||
'TABLE_ENCRYPTION_ADMIN', 'VERSION_TOKEN_ADMIN',
|
||||
'XA_RECOVER_ADMIN', 'LOAD FROM S3', 'SELECT INTO S3',
|
||||
'INVOKE LAMBDA',
|
||||
'ALTER ROUTINE',
|
||||
'BINLOG ADMIN',
|
||||
'BINLOG MONITOR',
|
||||
'BINLOG REPLAY',
|
||||
'CONNECTION ADMIN',
|
||||
'READ_ONLY ADMIN',
|
||||
'REPLICATION MASTER ADMIN',
|
||||
'REPLICATION SLAVE',
|
||||
'REPLICATION SLAVE ADMIN',
|
||||
'SET USER',))
|
||||
|
||||
|
||||
class InvalidPrivsError(Exception):
|
||||
pass
|
||||
|
||||
# ===========================================
|
||||
# MySQL module specific support methods.
|
||||
#
|
||||
|
||||
|
||||
# User Authentication Management changed in MySQL 5.7 and MariaDB 10.2.0
|
||||
def use_old_user_mgmt(cursor):
|
||||
cursor.execute("SELECT VERSION()")
|
||||
result = cursor.fetchone()
|
||||
version_str = result[0]
|
||||
version = version_str.split('.')
|
||||
|
||||
if 'mariadb' in version_str.lower():
|
||||
# Prior to MariaDB 10.2
|
||||
if int(version[0]) * 1000 + int(version[1]) < 10002:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
# Prior to MySQL 5.7
|
||||
if int(version[0]) * 1000 + int(version[1]) < 5007:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_mode(cursor):
|
||||
cursor.execute('SELECT @@GLOBAL.sql_mode')
|
||||
result = cursor.fetchone()
|
||||
mode_str = result[0]
|
||||
if 'ANSI' in mode_str:
|
||||
mode = 'ANSI'
|
||||
else:
|
||||
mode = 'NOTANSI'
|
||||
return mode
|
||||
|
||||
|
||||
def user_exists(cursor, user, host, host_all):
|
||||
if host_all:
|
||||
cursor.execute("SELECT count(*) FROM mysql.user WHERE user = %s", (user,))
|
||||
else:
|
||||
cursor.execute("SELECT count(*) FROM mysql.user WHERE user = %s AND host = %s", (user, host))
|
||||
|
||||
count = cursor.fetchone()
|
||||
return count[0] > 0
|
||||
|
||||
|
||||
def user_add(cursor, user, host, host_all, password, encrypted,
|
||||
plugin, plugin_hash_string, plugin_auth_string, new_priv, check_mode):
|
||||
# we cannot create users without a proper hostname
|
||||
if host_all:
|
||||
return False
|
||||
|
||||
if check_mode:
|
||||
return True
|
||||
|
||||
# Determine what user management method server uses
|
||||
old_user_mgmt = use_old_user_mgmt(cursor)
|
||||
|
||||
if password and encrypted:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password))
|
||||
elif password and not encrypted:
|
||||
if old_user_mgmt:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED BY %s", (user, host, password))
|
||||
else:
|
||||
cursor.execute("SELECT CONCAT('*', UCASE(SHA1(UNHEX(SHA1(%s)))))", (password,))
|
||||
encrypted_password = cursor.fetchone()[0]
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, encrypted_password))
|
||||
|
||||
elif plugin and plugin_hash_string:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string))
|
||||
elif plugin and plugin_auth_string:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string))
|
||||
elif plugin:
|
||||
cursor.execute("CREATE USER %s@%s IDENTIFIED WITH %s", (user, host, plugin))
|
||||
else:
|
||||
cursor.execute("CREATE USER %s@%s", (user, host))
|
||||
if new_priv is not None:
|
||||
for db_table, priv in iteritems(new_priv):
|
||||
privileges_grant(cursor, user, host, db_table, priv)
|
||||
return True
|
||||
|
||||
|
||||
def is_hash(password):
|
||||
ishash = False
|
||||
if len(password) == 41 and password[0] == '*':
|
||||
if frozenset(password[1:]).issubset(string.hexdigits):
|
||||
ishash = True
|
||||
return ishash
|
||||
|
||||
|
||||
def user_mod(cursor, user, host, host_all, password, encrypted,
|
||||
plugin, plugin_hash_string, plugin_auth_string, new_priv, append_privs, module):
|
||||
changed = False
|
||||
msg = "User unchanged"
|
||||
grant_option = False
|
||||
|
||||
if host_all:
|
||||
hostnames = user_get_hostnames(cursor, [user])
|
||||
else:
|
||||
hostnames = [host]
|
||||
|
||||
for host in hostnames:
|
||||
# Handle clear text and hashed passwords.
|
||||
if bool(password):
|
||||
# Determine what user management method server uses
|
||||
old_user_mgmt = use_old_user_mgmt(cursor)
|
||||
|
||||
# Get a list of valid columns in mysql.user table to check if Password and/or authentication_string exist
|
||||
cursor.execute("""
|
||||
SELECT COLUMN_NAME FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'mysql' AND TABLE_NAME = 'user' AND COLUMN_NAME IN ('Password', 'authentication_string')
|
||||
ORDER BY COLUMN_NAME DESC LIMIT 1
|
||||
""")
|
||||
colA = cursor.fetchone()
|
||||
|
||||
cursor.execute("""
|
||||
SELECT COLUMN_NAME FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = 'mysql' AND TABLE_NAME = 'user' AND COLUMN_NAME IN ('Password', 'authentication_string')
|
||||
ORDER BY COLUMN_NAME ASC LIMIT 1
|
||||
""")
|
||||
colB = cursor.fetchone()
|
||||
|
||||
# Select hash from either Password or authentication_string, depending which one exists and/or is filled
|
||||
cursor.execute("""
|
||||
SELECT COALESCE(
|
||||
CASE WHEN %s = '' THEN NULL ELSE %s END,
|
||||
CASE WHEN %s = '' THEN NULL ELSE %s END
|
||||
)
|
||||
FROM mysql.user WHERE user = %%s AND host = %%s
|
||||
""" % (colA[0], colA[0], colB[0], colB[0]), (user, host))
|
||||
current_pass_hash = cursor.fetchone()[0]
|
||||
if isinstance(current_pass_hash, bytes):
|
||||
current_pass_hash = current_pass_hash.decode('ascii')
|
||||
|
||||
if encrypted:
|
||||
encrypted_password = password
|
||||
if not is_hash(encrypted_password):
|
||||
module.fail_json(msg="encrypted was specified however it does not appear to be a valid hash expecting: *SHA1(SHA1(your_password))")
|
||||
else:
|
||||
if old_user_mgmt:
|
||||
cursor.execute("SELECT PASSWORD(%s)", (password,))
|
||||
else:
|
||||
cursor.execute("SELECT CONCAT('*', UCASE(SHA1(UNHEX(SHA1(%s)))))", (password,))
|
||||
encrypted_password = cursor.fetchone()[0]
|
||||
|
||||
if current_pass_hash != encrypted_password:
|
||||
msg = "Password updated"
|
||||
if module.check_mode:
|
||||
return (True, msg)
|
||||
if old_user_mgmt:
|
||||
cursor.execute("SET PASSWORD FOR %s@%s = %s", (user, host, encrypted_password))
|
||||
msg = "Password updated (old style)"
|
||||
else:
|
||||
try:
|
||||
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, encrypted_password))
|
||||
msg = "Password updated (new style)"
|
||||
except (mysql_driver.Error) as e:
|
||||
# https://stackoverflow.com/questions/51600000/authentication-string-of-root-user-on-mysql
|
||||
# Replacing empty root password with new authentication mechanisms fails with error 1396
|
||||
if e.args[0] == 1396:
|
||||
cursor.execute(
|
||||
"UPDATE mysql.user SET plugin = %s, authentication_string = %s, Password = '' WHERE User = %s AND Host = %s",
|
||||
('mysql_native_password', encrypted_password, user, host)
|
||||
)
|
||||
cursor.execute("FLUSH PRIVILEGES")
|
||||
msg = "Password forced update"
|
||||
else:
|
||||
raise e
|
||||
changed = True
|
||||
|
||||
# Handle plugin authentication
|
||||
if plugin:
|
||||
cursor.execute("SELECT plugin, authentication_string FROM mysql.user "
|
||||
"WHERE user = %s AND host = %s", (user, host))
|
||||
current_plugin = cursor.fetchone()
|
||||
|
||||
update = False
|
||||
|
||||
if current_plugin[0] != plugin:
|
||||
update = True
|
||||
|
||||
if plugin_hash_string and current_plugin[1] != plugin_hash_string:
|
||||
update = True
|
||||
|
||||
if plugin_auth_string and current_plugin[1] != plugin_auth_string:
|
||||
# this case can cause more updates than expected,
|
||||
# as plugin can hash auth_string in any way it wants
|
||||
# and there's no way to figure it out for
|
||||
# a check, so I prefer to update more often than never
|
||||
update = True
|
||||
|
||||
if update:
|
||||
if plugin_hash_string:
|
||||
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string))
|
||||
elif plugin_auth_string:
|
||||
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string))
|
||||
else:
|
||||
cursor.execute("ALTER USER %s@%s IDENTIFIED WITH %s", (user, host, plugin))
|
||||
changed = True
|
||||
|
||||
# Handle privileges
|
||||
if new_priv is not None:
|
||||
curr_priv = privileges_get(cursor, user, host)
|
||||
|
||||
# If the user has privileges on a db.table that doesn't appear at all in
|
||||
# the new specification, then revoke all privileges on it.
|
||||
for db_table, priv in iteritems(curr_priv):
|
||||
# If the user has the GRANT OPTION on a db.table, revoke it first.
|
||||
if "GRANT" in priv:
|
||||
grant_option = True
|
||||
if db_table not in new_priv:
|
||||
if user != "root" and "PROXY" not in priv and not append_privs:
|
||||
msg = "Privileges updated"
|
||||
if module.check_mode:
|
||||
return (True, msg)
|
||||
privileges_revoke(cursor, user, host, db_table, priv, grant_option)
|
||||
changed = True
|
||||
|
||||
# If the user doesn't currently have any privileges on a db.table, then
|
||||
# we can perform a straight grant operation.
|
||||
for db_table, priv in iteritems(new_priv):
|
||||
if db_table not in curr_priv:
|
||||
msg = "New privileges granted"
|
||||
if module.check_mode:
|
||||
return (True, msg)
|
||||
privileges_grant(cursor, user, host, db_table, priv)
|
||||
changed = True
|
||||
|
||||
# If the db.table specification exists in both the user's current privileges
|
||||
# and in the new privileges, then we need to see if there's a difference.
|
||||
db_table_intersect = set(new_priv.keys()) & set(curr_priv.keys())
|
||||
for db_table in db_table_intersect:
|
||||
priv_diff = set(new_priv[db_table]) ^ set(curr_priv[db_table])
|
||||
if len(priv_diff) > 0:
|
||||
msg = "Privileges updated"
|
||||
if module.check_mode:
|
||||
return (True, msg)
|
||||
if not append_privs:
|
||||
privileges_revoke(cursor, user, host, db_table, curr_priv[db_table], grant_option)
|
||||
privileges_grant(cursor, user, host, db_table, new_priv[db_table])
|
||||
changed = True
|
||||
|
||||
return (changed, msg)
|
||||
|
||||
|
||||
def user_delete(cursor, user, host, host_all, check_mode):
|
||||
if check_mode:
|
||||
return True
|
||||
|
||||
if host_all:
|
||||
hostnames = user_get_hostnames(cursor, user)
|
||||
|
||||
for hostname in hostnames:
|
||||
cursor.execute("DROP USER %s@%s", (user, hostname))
|
||||
else:
|
||||
cursor.execute("DROP USER %s@%s", (user, host))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def user_get_hostnames(cursor, user):
|
||||
cursor.execute("SELECT Host FROM mysql.user WHERE user = %s", (user,))
|
||||
hostnames_raw = cursor.fetchall()
|
||||
hostnames = []
|
||||
|
||||
for hostname_raw in hostnames_raw:
|
||||
hostnames.append(hostname_raw[0])
|
||||
|
||||
return hostnames
|
||||
|
||||
|
||||
def privileges_get(cursor, user, host):
|
||||
""" MySQL doesn't have a better method of getting privileges aside from the
|
||||
SHOW GRANTS query syntax, which requires us to then parse the returned string.
|
||||
Here's an example of the string that is returned from MySQL:
|
||||
|
||||
GRANT USAGE ON *.* TO 'user'@'localhost' IDENTIFIED BY 'pass';
|
||||
|
||||
This function makes the query and returns a dictionary containing the results.
|
||||
The dictionary format is the same as that returned by privileges_unpack() below.
|
||||
"""
|
||||
output = {}
|
||||
cursor.execute("SHOW GRANTS FOR %s@%s", (user, host))
|
||||
grants = cursor.fetchall()
|
||||
|
||||
def pick(x):
|
||||
if x == 'ALL PRIVILEGES':
|
||||
return 'ALL'
|
||||
else:
|
||||
return x
|
||||
|
||||
for grant in grants:
|
||||
res = re.match("""GRANT (.+) ON (.+) TO (['`"]).*\\3@(['`"]).*\\4( IDENTIFIED BY PASSWORD (['`"]).+\\6)? ?(.*)""", grant[0])
|
||||
if res is None:
|
||||
raise InvalidPrivsError('unable to parse the MySQL grant string: %s' % grant[0])
|
||||
privileges = res.group(1).split(",")
|
||||
privileges = [pick(x.strip()) for x in privileges]
|
||||
if "WITH GRANT OPTION" in res.group(7):
|
||||
privileges.append('GRANT')
|
||||
if "REQUIRE SSL" in res.group(7):
|
||||
privileges.append('REQUIRESSL')
|
||||
db = res.group(2)
|
||||
output.setdefault(db, []).extend(privileges)
|
||||
return output
|
||||
|
||||
|
||||
def privileges_unpack(priv, mode):
|
||||
""" Take a privileges string, typically passed as a parameter, and unserialize
|
||||
it into a dictionary, the same format as privileges_get() above. We have this
|
||||
custom format to avoid using YAML/JSON strings inside YAML playbooks. Example
|
||||
of a privileges string:
|
||||
|
||||
mydb.*:INSERT,UPDATE/anotherdb.*:SELECT/yetanother.*:ALL
|
||||
|
||||
The privilege USAGE stands for no privileges, so we add that in on *.* if it's
|
||||
not specified in the string, as MySQL will always provide this by default.
|
||||
"""
|
||||
if mode == 'ANSI':
|
||||
quote = '"'
|
||||
else:
|
||||
quote = '`'
|
||||
output = {}
|
||||
privs = []
|
||||
for item in priv.strip().split('/'):
|
||||
pieces = item.strip().rsplit(':', 1)
|
||||
dbpriv = pieces[0].rsplit(".", 1)
|
||||
|
||||
# Check for FUNCTION or PROCEDURE object types
|
||||
parts = dbpriv[0].split(" ", 1)
|
||||
object_type = ''
|
||||
if len(parts) > 1 and (parts[0] == 'FUNCTION' or parts[0] == 'PROCEDURE'):
|
||||
object_type = parts[0] + ' '
|
||||
dbpriv[0] = parts[1]
|
||||
|
||||
# Do not escape if privilege is for database or table, i.e.
|
||||
# neither quote *. nor .*
|
||||
for i, side in enumerate(dbpriv):
|
||||
if side.strip('`') != '*':
|
||||
dbpriv[i] = '%s%s%s' % (quote, side.strip('`'), quote)
|
||||
pieces[0] = object_type + '.'.join(dbpriv)
|
||||
|
||||
if '(' in pieces[1]:
|
||||
output[pieces[0]] = re.split(r',\s*(?=[^)]*(?:\(|$))', pieces[1].upper())
|
||||
for i in output[pieces[0]]:
|
||||
privs.append(re.sub(r'\s*\(.*\)', '', i))
|
||||
else:
|
||||
output[pieces[0]] = pieces[1].upper().split(',')
|
||||
privs = output[pieces[0]]
|
||||
new_privs = frozenset(privs)
|
||||
if not new_privs.issubset(VALID_PRIVS):
|
||||
raise InvalidPrivsError('Invalid privileges specified: %s' % new_privs.difference(VALID_PRIVS))
|
||||
|
||||
if '*.*' not in output:
|
||||
output['*.*'] = ['USAGE']
|
||||
|
||||
# if we are only specifying something like REQUIRESSL and/or GRANT (=WITH GRANT OPTION) in *.*
|
||||
# we still need to add USAGE as a privilege to avoid syntax errors
|
||||
if 'REQUIRESSL' in priv and not set(output['*.*']).difference(set(['GRANT', 'REQUIRESSL'])):
|
||||
output['*.*'].append('USAGE')
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def privileges_revoke(cursor, user, host, db_table, priv, grant_option):
|
||||
# Escape '%' since mysql db.execute() uses a format string
|
||||
db_table = db_table.replace('%', '%%')
|
||||
if grant_option:
|
||||
query = ["REVOKE GRANT OPTION ON %s" % db_table]
|
||||
query.append("FROM %s@%s")
|
||||
query = ' '.join(query)
|
||||
cursor.execute(query, (user, host))
|
||||
priv_string = ",".join([p for p in priv if p not in ('GRANT', 'REQUIRESSL')])
|
||||
query = ["REVOKE %s ON %s" % (priv_string, db_table)]
|
||||
query.append("FROM %s@%s")
|
||||
query = ' '.join(query)
|
||||
cursor.execute(query, (user, host))
|
||||
|
||||
|
||||
def privileges_grant(cursor, user, host, db_table, priv):
|
||||
# Escape '%' since mysql db.execute uses a format string and the
|
||||
# specification of db and table often use a % (SQL wildcard)
|
||||
db_table = db_table.replace('%', '%%')
|
||||
priv_string = ",".join([p for p in priv if p not in ('GRANT', 'REQUIRESSL')])
|
||||
query = ["GRANT %s ON %s" % (priv_string, db_table)]
|
||||
query.append("TO %s@%s")
|
||||
if 'REQUIRESSL' in priv:
|
||||
query.append("REQUIRE SSL")
|
||||
if 'GRANT' in priv:
|
||||
query.append("WITH GRANT OPTION")
|
||||
query = ' '.join(query)
|
||||
cursor.execute(query, (user, host))
|
||||
|
||||
|
||||
def convert_priv_dict_to_str(priv):
|
||||
"""Converts privs dictionary to string of certain format.
|
||||
|
||||
Args:
|
||||
priv (dict): Dict of privileges that needs to be converted to string.
|
||||
|
||||
Returns:
|
||||
priv (str): String representation of input argument.
|
||||
"""
|
||||
priv_list = ['%s:%s' % (key, val) for key, val in iteritems(priv)]
|
||||
|
||||
return '/'.join(priv_list)
|
||||
|
||||
|
||||
# Alter user is supported since MySQL 5.6 and MariaDB 10.2.0
|
||||
def server_supports_alter_user(cursor):
|
||||
"""Check if the server supports ALTER USER statement or doesn't.
|
||||
|
||||
Args:
|
||||
cursor (cursor): DB driver cursor object.
|
||||
|
||||
Returns: True if supports, False otherwise.
|
||||
"""
|
||||
cursor.execute("SELECT VERSION()")
|
||||
version_str = cursor.fetchone()[0]
|
||||
version = version_str.split('.')
|
||||
|
||||
if 'mariadb' in version_str.lower():
|
||||
# MariaDB 10.2 and later
|
||||
if int(version[0]) * 1000 + int(version[1]) >= 10002:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
# MySQL 5.6 and later
|
||||
if int(version[0]) * 1000 + int(version[1]) >= 5006:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_resource_limits(cursor, user, host):
|
||||
"""Get user resource limits.
|
||||
|
||||
Args:
|
||||
cursor (cursor): DB driver cursor object.
|
||||
user (str): User name.
|
||||
host (str): User host name.
|
||||
|
||||
Returns: Dictionary containing current resource limits.
|
||||
"""
|
||||
|
||||
query = ('SELECT max_questions AS MAX_QUERIES_PER_HOUR, '
|
||||
'max_updates AS MAX_UPDATES_PER_HOUR, '
|
||||
'max_connections AS MAX_CONNECTIONS_PER_HOUR, '
|
||||
'max_user_connections AS MAX_USER_CONNECTIONS '
|
||||
'FROM mysql.user WHERE User = %s AND Host = %s')
|
||||
cursor.execute(query, (user, host))
|
||||
res = cursor.fetchone()
|
||||
|
||||
if not res:
|
||||
return None
|
||||
|
||||
current_limits = {
|
||||
'MAX_QUERIES_PER_HOUR': res[0],
|
||||
'MAX_UPDATES_PER_HOUR': res[1],
|
||||
'MAX_CONNECTIONS_PER_HOUR': res[2],
|
||||
'MAX_USER_CONNECTIONS': res[3],
|
||||
}
|
||||
return current_limits
|
||||
|
||||
|
||||
def match_resource_limits(module, current, desired):
|
||||
"""Check and match limits.
|
||||
|
||||
Args:
|
||||
module (AnsibleModule): Ansible module object.
|
||||
current (dict): Dictionary with current limits.
|
||||
desired (dict): Dictionary with desired limits.
|
||||
|
||||
Returns: Dictionary containing parameters that need to change.
|
||||
"""
|
||||
|
||||
if not current:
|
||||
# It means the user does not exists, so we need
|
||||
# to set all limits after its creation
|
||||
return desired
|
||||
|
||||
needs_to_change = {}
|
||||
|
||||
for key, val in iteritems(desired):
|
||||
if key not in current:
|
||||
# Supported keys are listed in the documentation
|
||||
# and must be determined in the get_resource_limits function
|
||||
# (follow 'AS' keyword)
|
||||
module.fail_json(msg="resource_limits: key '%s' is unsupported." % key)
|
||||
|
||||
try:
|
||||
val = int(val)
|
||||
except Exception:
|
||||
module.fail_json(msg="Can't convert value '%s' to integer." % val)
|
||||
|
||||
if val != current.get(key):
|
||||
needs_to_change[key] = val
|
||||
|
||||
return needs_to_change
|
||||
|
||||
|
||||
def limit_resources(module, cursor, user, host, resource_limits, check_mode):
|
||||
"""Limit user resources.
|
||||
|
||||
Args:
|
||||
module (AnsibleModule): Ansible module object.
|
||||
cursor (cursor): DB driver cursor object.
|
||||
user (str): User name.
|
||||
host (str): User host name.
|
||||
resource_limit (dict): Dictionary with desired limits.
|
||||
check_mode (bool): Run the function in check mode or not.
|
||||
|
||||
Returns: True, if changed, False otherwise.
|
||||
"""
|
||||
if not server_supports_alter_user(cursor):
|
||||
module.fail_json(msg="The server version does not match the requirements "
|
||||
"for resource_limits parameter. See module's documentation.")
|
||||
|
||||
current_limits = get_resource_limits(cursor, user, host)
|
||||
|
||||
needs_to_change = match_resource_limits(module, current_limits, resource_limits)
|
||||
|
||||
if not needs_to_change:
|
||||
return False
|
||||
|
||||
if needs_to_change and check_mode:
|
||||
return True
|
||||
|
||||
# If not check_mode
|
||||
tmp = []
|
||||
for key, val in iteritems(needs_to_change):
|
||||
tmp.append('%s %s' % (key, val))
|
||||
|
||||
query = "ALTER USER %s@%s"
|
||||
query += ' WITH %s' % ' '.join(tmp)
|
||||
cursor.execute(query, (user, host))
|
||||
return True
|
||||
|
||||
# ===========================================
|
||||
# Module execution.
|
||||
#
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
login_user=dict(type='str'),
|
||||
login_password=dict(type='str', no_log=True),
|
||||
login_host=dict(type='str', default='localhost'),
|
||||
login_port=dict(type='int', default=3306),
|
||||
login_unix_socket=dict(type='str'),
|
||||
user=dict(type='str', required=True, aliases=['name']),
|
||||
password=dict(type='str', no_log=True),
|
||||
encrypted=dict(type='bool', default=False),
|
||||
host=dict(type='str', default='localhost'),
|
||||
host_all=dict(type="bool", default=False),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||
priv=dict(type='raw'),
|
||||
append_privs=dict(type='bool', default=False),
|
||||
check_implicit_admin=dict(type='bool', default=False),
|
||||
update_password=dict(type='str', default='always', choices=['always', 'on_create'], no_log=False),
|
||||
connect_timeout=dict(type='int', default=30),
|
||||
config_file=dict(type='path', default='~/.my.cnf'),
|
||||
sql_log_bin=dict(type='bool', default=True),
|
||||
client_cert=dict(type='path', aliases=['ssl_cert']),
|
||||
client_key=dict(type='path', aliases=['ssl_key']),
|
||||
ca_cert=dict(type='path', aliases=['ssl_ca']),
|
||||
plugin=dict(default=None, type='str'),
|
||||
plugin_hash_string=dict(default=None, type='str'),
|
||||
plugin_auth_string=dict(default=None, type='str'),
|
||||
resource_limits=dict(type='dict'),
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
login_user = module.params["login_user"]
|
||||
login_password = module.params["login_password"]
|
||||
user = module.params["user"]
|
||||
password = module.params["password"]
|
||||
encrypted = module.boolean(module.params["encrypted"])
|
||||
host = module.params["host"].lower()
|
||||
host_all = module.params["host_all"]
|
||||
state = module.params["state"]
|
||||
priv = module.params["priv"]
|
||||
check_implicit_admin = module.params['check_implicit_admin']
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
config_file = module.params['config_file']
|
||||
append_privs = module.boolean(module.params["append_privs"])
|
||||
update_password = module.params['update_password']
|
||||
ssl_cert = module.params["client_cert"]
|
||||
ssl_key = module.params["client_key"]
|
||||
ssl_ca = module.params["ca_cert"]
|
||||
db = ''
|
||||
sql_log_bin = module.params["sql_log_bin"]
|
||||
plugin = module.params["plugin"]
|
||||
plugin_hash_string = module.params["plugin_hash_string"]
|
||||
plugin_auth_string = module.params["plugin_auth_string"]
|
||||
resource_limits = module.params["resource_limits"]
|
||||
if priv and not (isinstance(priv, str) or isinstance(priv, dict)):
|
||||
module.fail_json(msg="priv parameter must be str or dict but %s was passed" % type(priv))
|
||||
|
||||
if priv and isinstance(priv, dict):
|
||||
priv = convert_priv_dict_to_str(priv)
|
||||
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
|
||||
cursor = None
|
||||
try:
|
||||
if check_implicit_admin:
|
||||
try:
|
||||
cursor, db_conn = mysql_connect(module, 'root', '', config_file, ssl_cert, ssl_key, ssl_ca, db,
|
||||
connect_timeout=connect_timeout)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not cursor:
|
||||
cursor, db_conn = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, db,
|
||||
connect_timeout=connect_timeout)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or %s has the credentials. "
|
||||
"Exception message: %s" % (config_file, to_native(e)))
|
||||
|
||||
if not sql_log_bin:
|
||||
cursor.execute("SET SQL_LOG_BIN=0;")
|
||||
|
||||
if priv is not None:
|
||||
try:
|
||||
mode = get_mode(cursor)
|
||||
except Exception as e:
|
||||
module.fail_json(msg=to_native(e))
|
||||
try:
|
||||
priv = privileges_unpack(priv, mode)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="invalid privileges string: %s" % to_native(e))
|
||||
|
||||
if state == "present":
|
||||
if user_exists(cursor, user, host, host_all):
|
||||
try:
|
||||
if update_password == 'always':
|
||||
changed, msg = user_mod(cursor, user, host, host_all, password, encrypted,
|
||||
plugin, plugin_hash_string, plugin_auth_string,
|
||||
priv, append_privs, module)
|
||||
else:
|
||||
changed, msg = user_mod(cursor, user, host, host_all, None, encrypted,
|
||||
plugin, plugin_hash_string, plugin_auth_string,
|
||||
priv, append_privs, module)
|
||||
|
||||
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
|
||||
module.fail_json(msg=to_native(e))
|
||||
else:
|
||||
if host_all:
|
||||
module.fail_json(msg="host_all parameter cannot be used when adding a user")
|
||||
try:
|
||||
changed = user_add(cursor, user, host, host_all, password, encrypted,
|
||||
plugin, plugin_hash_string, plugin_auth_string,
|
||||
priv, module.check_mode)
|
||||
if changed:
|
||||
msg = "User added"
|
||||
|
||||
except (SQLParseError, InvalidPrivsError, mysql_driver.Error) as e:
|
||||
module.fail_json(msg=to_native(e))
|
||||
|
||||
if resource_limits:
|
||||
changed = limit_resources(module, cursor, user, host, resource_limits, module.check_mode) or changed
|
||||
|
||||
elif state == "absent":
|
||||
if user_exists(cursor, user, host, host_all):
|
||||
changed = user_delete(cursor, user, host, host_all, module.check_mode)
|
||||
msg = "User deleted"
|
||||
else:
|
||||
changed = False
|
||||
msg = "User doesn't exist"
|
||||
module.exit_json(changed=changed, user=user, msg=msg)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,272 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2013, Balazs Pocze <banyek@gawker.com>
|
||||
# Certain parts are taken from Mark Theunissen's mysqldb module
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_variables
|
||||
|
||||
short_description: Manage MySQL global variables
|
||||
description:
|
||||
- Query / Set MySQL variables.
|
||||
author:
|
||||
- Balazs Pocze (@banyek)
|
||||
options:
|
||||
variable:
|
||||
description:
|
||||
- Variable name to operate
|
||||
type: str
|
||||
required: yes
|
||||
value:
|
||||
description:
|
||||
- If set, then sets variable value to this
|
||||
type: str
|
||||
mode:
|
||||
description:
|
||||
- C(global) assigns C(value) to a global system variable which will be changed at runtime
|
||||
but won't persist across server restarts.
|
||||
- C(persist) assigns C(value) to a global system variable and persists it to
|
||||
the mysqld-auto.cnf option file in the data directory
|
||||
(the variable will survive service restarts).
|
||||
- C(persist_only) persists C(value) to the mysqld-auto.cnf option file in the data directory
|
||||
but without setting the global variable runtime value
|
||||
(the value will be changed after the next service restart).
|
||||
- Supported by MySQL 8.0 or later.
|
||||
- For more information see U(https://dev.mysql.com/doc/refman/8.0/en/set-variable.html).
|
||||
type: str
|
||||
choices: ['global', 'persist', 'persist_only']
|
||||
default: global
|
||||
version_added: '0.2.0'
|
||||
|
||||
seealso:
|
||||
- module: community.general.mysql_info
|
||||
- name: MySQL SET command reference
|
||||
description: Complete reference of the MySQL SET command documentation.
|
||||
link: https://dev.mysql.com/doc/refman/8.0/en/set-statement.html
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.general.mysql
|
||||
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
- name: Check for sync_binlog setting
|
||||
mysql_variables:
|
||||
variable: sync_binlog
|
||||
|
||||
- name: Set read_only variable to 1 persistently
|
||||
mysql_variables:
|
||||
variable: read_only
|
||||
value: 1
|
||||
mode: persist
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
queries:
|
||||
description: List of executed queries which modified DB's state.
|
||||
returned: if executed
|
||||
type: list
|
||||
sample: ["SET GLOBAL `read_only` = 1"]
|
||||
version_added: '0.2.0'
|
||||
'''
|
||||
|
||||
import os
|
||||
import warnings
|
||||
from re import match
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.community.general.plugins.module_utils.database import SQLParseError, mysql_quote_identifier
|
||||
from ansible_collections.community.general.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
executed_queries = []
|
||||
|
||||
|
||||
def check_mysqld_auto(module, cursor, mysqlvar):
|
||||
"""Check variable's value in mysqld-auto.cnf."""
|
||||
query = ("SELECT VARIABLE_VALUE "
|
||||
"FROM performance_schema.persisted_variables "
|
||||
"WHERE VARIABLE_NAME = %s")
|
||||
try:
|
||||
cursor.execute(query, (mysqlvar,))
|
||||
res = cursor.fetchone()
|
||||
except Exception as e:
|
||||
if "Table 'performance_schema.persisted_variables' doesn't exist" in str(e):
|
||||
module.fail_json(msg='Server version must be 8.0 or greater.')
|
||||
|
||||
if res:
|
||||
return res[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def typedvalue(value):
|
||||
"""
|
||||
Convert value to number whenever possible, return same value
|
||||
otherwise.
|
||||
|
||||
>>> typedvalue('3')
|
||||
3
|
||||
>>> typedvalue('3.0')
|
||||
3.0
|
||||
>>> typedvalue('foobar')
|
||||
'foobar'
|
||||
|
||||
"""
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def getvariable(cursor, mysqlvar):
|
||||
cursor.execute("SHOW VARIABLES WHERE Variable_name = %s", (mysqlvar,))
|
||||
mysqlvar_val = cursor.fetchall()
|
||||
if len(mysqlvar_val) == 1:
|
||||
return mysqlvar_val[0][1]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def setvariable(cursor, mysqlvar, value, mode='global'):
|
||||
""" Set a global mysql variable to a given value
|
||||
|
||||
The DB driver will handle quoting of the given value based on its
|
||||
type, thus numeric strings like '3.0' or '8' are illegal, they
|
||||
should be passed as numeric literals.
|
||||
|
||||
"""
|
||||
if mode == 'persist':
|
||||
query = "SET PERSIST %s = " % mysql_quote_identifier(mysqlvar, 'vars')
|
||||
elif mode == 'global':
|
||||
query = "SET GLOBAL %s = " % mysql_quote_identifier(mysqlvar, 'vars')
|
||||
elif mode == 'persist_only':
|
||||
query = "SET PERSIST_ONLY %s = " % mysql_quote_identifier(mysqlvar, 'vars')
|
||||
|
||||
try:
|
||||
cursor.execute(query + "%s", (value,))
|
||||
executed_queries.append(query + "%s" % value)
|
||||
cursor.fetchall()
|
||||
result = True
|
||||
except Exception as e:
|
||||
result = to_native(e)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
login_user=dict(type='str'),
|
||||
login_password=dict(type='str', no_log=True),
|
||||
login_host=dict(type='str', default='localhost'),
|
||||
login_port=dict(type='int', default=3306),
|
||||
login_unix_socket=dict(type='str'),
|
||||
variable=dict(type='str'),
|
||||
value=dict(type='str'),
|
||||
client_cert=dict(type='path', aliases=['ssl_cert']),
|
||||
client_key=dict(type='path', aliases=['ssl_key']),
|
||||
ca_cert=dict(type='path', aliases=['ssl_ca']),
|
||||
connect_timeout=dict(type='int', default=30),
|
||||
config_file=dict(type='path', default='~/.my.cnf'),
|
||||
mode=dict(type='str', choices=['global', 'persist', 'persist_only'], default='global'),
|
||||
),
|
||||
)
|
||||
user = module.params["login_user"]
|
||||
password = module.params["login_password"]
|
||||
connect_timeout = module.params['connect_timeout']
|
||||
ssl_cert = module.params["client_cert"]
|
||||
ssl_key = module.params["client_key"]
|
||||
ssl_ca = module.params["ca_cert"]
|
||||
config_file = module.params['config_file']
|
||||
db = 'mysql'
|
||||
|
||||
mysqlvar = module.params["variable"]
|
||||
value = module.params["value"]
|
||||
mode = module.params["mode"]
|
||||
|
||||
if mysqlvar is None:
|
||||
module.fail_json(msg="Cannot run without variable to operate with")
|
||||
if match('^[0-9a-z_.]+$', mysqlvar) is None:
|
||||
module.fail_json(msg="invalid variable name \"%s\"" % mysqlvar)
|
||||
if mysql_driver is None:
|
||||
module.fail_json(msg=mysql_driver_fail_msg)
|
||||
else:
|
||||
warnings.filterwarnings('error', category=mysql_driver.Warning)
|
||||
|
||||
try:
|
||||
cursor, db_conn = mysql_connect(module, user, password, config_file, ssl_cert, ssl_key, ssl_ca, db,
|
||||
connect_timeout=connect_timeout)
|
||||
except Exception as e:
|
||||
if os.path.exists(config_file):
|
||||
module.fail_json(msg=("unable to connect to database, check login_user and "
|
||||
"login_password are correct or %s has the credentials. "
|
||||
"Exception message: %s" % (config_file, to_native(e))))
|
||||
else:
|
||||
module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, to_native(e)))
|
||||
|
||||
mysqlvar_val = None
|
||||
var_in_mysqld_auto_cnf = None
|
||||
|
||||
mysqlvar_val = getvariable(cursor, mysqlvar)
|
||||
if mysqlvar_val is None:
|
||||
module.fail_json(msg="Variable not available \"%s\"" % mysqlvar, changed=False)
|
||||
|
||||
if value is None:
|
||||
module.exit_json(msg=mysqlvar_val)
|
||||
|
||||
if mode in ('persist', 'persist_only'):
|
||||
var_in_mysqld_auto_cnf = check_mysqld_auto(module, cursor, mysqlvar)
|
||||
|
||||
if mode == 'persist_only':
|
||||
if var_in_mysqld_auto_cnf is None:
|
||||
mysqlvar_val = False
|
||||
else:
|
||||
mysqlvar_val = var_in_mysqld_auto_cnf
|
||||
|
||||
# Type values before using them
|
||||
value_wanted = typedvalue(value)
|
||||
value_actual = typedvalue(mysqlvar_val)
|
||||
value_in_auto_cnf = None
|
||||
if var_in_mysqld_auto_cnf is not None:
|
||||
value_in_auto_cnf = typedvalue(var_in_mysqld_auto_cnf)
|
||||
|
||||
if value_wanted == value_actual and mode in ('global', 'persist'):
|
||||
if mode == 'persist' and value_wanted == value_in_auto_cnf:
|
||||
module.exit_json(msg="Variable is already set to requested value globally"
|
||||
"and stored into mysqld-auto.cnf file.", changed=False)
|
||||
|
||||
elif mode == 'global':
|
||||
module.exit_json(msg="Variable is already set to requested value.", changed=False)
|
||||
|
||||
if mode == 'persist_only' and value_wanted == value_in_auto_cnf:
|
||||
module.exit_json(msg="Variable is already stored into mysqld-auto.cnf "
|
||||
"with requested value.", changed=False)
|
||||
|
||||
try:
|
||||
result = setvariable(cursor, mysqlvar, value_wanted, mode)
|
||||
except SQLParseError as e:
|
||||
result = to_native(e)
|
||||
|
||||
if result is True:
|
||||
module.exit_json(msg="Variable change succeeded prev_value=%s" % value_actual,
|
||||
changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.fail_json(msg=result, changed=False)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1 +0,0 @@
|
|||
./database/mysql/mysql_db.py
|
|
@ -1 +0,0 @@
|
|||
./database/mysql/mysql_info.py
|
|
@ -1 +0,0 @@
|
|||
./database/mysql/mysql_query.py
|
|
@ -1 +0,0 @@
|
|||
./database/mysql/mysql_replication.py
|
|
@ -1 +0,0 @@
|
|||
./database/mysql/mysql_user.py
|
|
@ -1 +0,0 @@
|
|||
./database/mysql/mysql_variables.py
|
|
@ -1,7 +0,0 @@
|
|||
destructive
|
||||
unsupported # these tests conflict with mysql_replication tests and do not run on changes to the mysql_replication module
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
||||
skip/rhel
|
||||
needs/root
|
|
@ -1,7 +0,0 @@
|
|||
master_port: 3306
|
||||
standby_port: 3307
|
||||
test_db: test_db
|
||||
replication_user: replication_user
|
||||
replication_pass: replication_pass
|
||||
dump_path: /tmp/dump.sql
|
||||
conn_name: master-1
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
dependencies:
|
||||
- setup_mariadb
|
|
@ -1,21 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Initial CI tests of mysql_replication module
|
||||
- import_tasks: mariadb_replication_initial.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '>=')
|
||||
|
||||
# Tests of master_use_gtid parameter
|
||||
# https://github.com/ansible/ansible/pull/62648
|
||||
- import_tasks: mariadb_master_use_gtid.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '>=')
|
||||
|
||||
# Tests of connection_name parameter
|
||||
- import_tasks: mariadb_replication_connection_name.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '>=')
|
|
@ -1,173 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Tests for master_use_gtid parameter.
|
||||
# https://github.com/ansible/ansible/pull/62648
|
||||
|
||||
#############################
|
||||
# master_use_gtid: "disabled"
|
||||
#############################
|
||||
|
||||
# Auxiliary step:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: getmaster
|
||||
register: primary_status
|
||||
|
||||
# Set master_use_gtid disabled:
|
||||
- name: Run replication
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ primary_db.port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: mysql-bin.000001
|
||||
master_log_pos: '{{ primary_status.Position }}'
|
||||
master_use_gtid: disabled
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
# Start standby for further tests:
|
||||
- name: Start standby
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: startslave
|
||||
|
||||
- name: Get standby status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Using_Gtid == 'No'
|
||||
|
||||
# Stop standby for further tests:
|
||||
- name: Stop standby
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: stopslave
|
||||
|
||||
################################
|
||||
# master_use_gtid: "current_pos"
|
||||
################################
|
||||
|
||||
# Auxiliary step:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: getmaster
|
||||
register: primary_status
|
||||
|
||||
# Set master_use_gtid current_pos:
|
||||
- name: Run replication
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ primary_db.port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: mysql-bin.000001
|
||||
master_log_pos: '{{ primary_status.Position }}'
|
||||
master_use_gtid: current_pos
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
# Start standby for further tests:
|
||||
- name: Start standby
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: startslave
|
||||
|
||||
- name: Get standby status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Using_Gtid == 'Current_Pos'
|
||||
|
||||
# Stop standby for further tests:
|
||||
- name: Stop standby
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: stopslave
|
||||
|
||||
##############################
|
||||
# master_use_gtid: "slave_pos"
|
||||
##############################
|
||||
|
||||
# Auxiliary step:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: getmaster
|
||||
register: primary_status
|
||||
|
||||
# Set master_use_gtid slave_pos:
|
||||
- name: Run replication
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ primary_db.port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: mysql-bin.000001
|
||||
master_log_pos: '{{ primary_status.Position }}'
|
||||
master_use_gtid: slave_pos
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
# Start standby for further tests:
|
||||
- name: Start standby
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: startslave
|
||||
|
||||
- name: Get standby status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Using_Gtid == 'Slave_Pos'
|
||||
|
||||
# Stop standby for further tests:
|
||||
- name: Stop standby
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: stopslave
|
|
@ -1,118 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Needs for further tests:
|
||||
- name: Stop slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: stopslave
|
||||
|
||||
- name: Reset slave all
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: resetslaveall
|
||||
|
||||
# Get master log pos:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: getmaster
|
||||
register: primary_status
|
||||
|
||||
# Test changemaster mode:
|
||||
- name: Run replication with connection_name
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ primary_db.port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: mysql-bin.000001
|
||||
master_log_pos: '{{ primary_status.Position }}'
|
||||
connection_name: '{{ conn_name }}'
|
||||
register: result
|
||||
|
||||
- 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+")
|
||||
|
||||
# Test startslave mode:
|
||||
- name: Start slave with connection_name
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: startslave
|
||||
connection_name: "{{ conn_name }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["START SLAVE \'{{ conn_name }}\'"]
|
||||
|
||||
# Test getslave mode:
|
||||
- name: Get standby statu with connection_name
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: getslave
|
||||
connection_name: "{{ conn_name }}"
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Is_Slave == true
|
||||
- slave_status.Master_Host == '127.0.0.1'
|
||||
- slave_status.Exec_Master_Log_Pos == primary_status.Position
|
||||
- slave_status.Master_Port == {{ primary_db.port }}
|
||||
- slave_status.Last_IO_Errno == 0
|
||||
- slave_status.Last_IO_Error == ''
|
||||
- slave_status is not changed
|
||||
|
||||
# Test stopslave mode:
|
||||
- name: Stop slave with connection_name
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: stopslave
|
||||
connection_name: "{{ conn_name }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["STOP SLAVE \'{{ conn_name }}\'"]
|
||||
|
||||
# Test reset
|
||||
- name: Reset slave with connection_name
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: resetslave
|
||||
connection_name: "{{ conn_name }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["RESET SLAVE \'{{ conn_name }}\'"]
|
||||
|
||||
# Test reset all
|
||||
- name: Reset slave all with connection_name
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: resetslaveall
|
||||
connection_name: "{{ conn_name }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["RESET SLAVE \'{{ conn_name }}\' ALL"]
|
|
@ -1,96 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Preparation:
|
||||
- name: Create user for replication
|
||||
shell: "echo \"GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'localhost' IDENTIFIED BY '{{ replication_pass }}'; FLUSH PRIVILEGES;\" | mysql -P {{ primary_db.port }} -h 127.0.0.1"
|
||||
|
||||
- name: Create test database
|
||||
mysql_db:
|
||||
login_host: 127.0.0.1
|
||||
login_port: '{{ primary_db.port }}'
|
||||
state: present
|
||||
name: '{{ test_db }}'
|
||||
|
||||
- name: Dump all databases from the master
|
||||
shell: 'mysqldump -P {{ primary_db.port }} -h 127.0.01 --all-databases --master-data=2 > {{ dump_path }}'
|
||||
|
||||
- name: Restore the dump to the replica
|
||||
shell: 'mysql -P {{ replica_db.port }} -h 127.0.0.1 < {{ dump_path }}'
|
||||
|
||||
# Test getmaster mode:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ primary_db.port }}"
|
||||
mode: getmaster
|
||||
register: master_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- master_status.Is_Master == true
|
||||
- master_status.Position != 0
|
||||
- master_status is not changed
|
||||
|
||||
# Test changemaster mode:
|
||||
- name: Run replication
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ primary_db.port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: mysql-bin.000001
|
||||
master_log_pos: '{{ master_status.Position }}'
|
||||
register: result
|
||||
|
||||
- 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+")
|
||||
|
||||
# Test startslave mode:
|
||||
- name: Start slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: startslave
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["START SLAVE"]
|
||||
|
||||
# Test getslave mode:
|
||||
- name: Get replica status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Is_Slave == true
|
||||
- slave_status.Master_Host == '127.0.0.1'
|
||||
- slave_status.Exec_Master_Log_Pos == master_status.Position
|
||||
- slave_status.Master_Port == {{ primary_db.port }}
|
||||
- slave_status.Last_IO_Errno == 0
|
||||
- slave_status.Last_IO_Error == ''
|
||||
- slave_status is not changed
|
||||
|
||||
# Test stopslave mode:
|
||||
- name: Stop slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ replica_db.port }}"
|
||||
mode: stopslave
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["STOP SLAVE"]
|
|
@ -1,5 +0,0 @@
|
|||
destructive
|
||||
shippable/posix/group5
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
|
@ -1,10 +0,0 @@
|
|||
---
|
||||
# defaults file for test_mysql_db
|
||||
db_name: 'data'
|
||||
db_name2: 'data2'
|
||||
db_user1: 'datauser1'
|
||||
db_user2: 'datauser2'
|
||||
|
||||
tmp_dir: '/tmp'
|
||||
db_latin1_name: 'db_latin1'
|
||||
file4: 'latin1_file'
|
|
@ -1,2 +0,0 @@
|
|||
dependencies:
|
||||
- setup_mysql_db
|
|
@ -1,71 +0,0 @@
|
|||
- set_fact:
|
||||
db_to_create=testdb1
|
||||
config_file="/root/.my1.cnf"
|
||||
fake_port=9999
|
||||
fake_host="blahblah.local"
|
||||
|
||||
- name: Create custom config file
|
||||
shell: 'echo "[client]" > {{ config_file }}'
|
||||
|
||||
- name: Add fake port to config file
|
||||
shell: 'echo "port = {{ fake_port }}" >> {{ config_file }}'
|
||||
|
||||
- name: Create database using fake port to connect to, must fail
|
||||
mysql_db:
|
||||
name: '{{ db_to_create }}'
|
||||
state: present
|
||||
check_implicit_admin: yes
|
||||
config_file: '{{ config_file }}'
|
||||
config_overrides_defaults: yes
|
||||
ignore_errors: yes
|
||||
register: result
|
||||
|
||||
- name: Must fail because login_port default has beed overriden by wrong value from config file
|
||||
assert:
|
||||
that:
|
||||
- result is failed
|
||||
- result.msg is search("unable to connect to database")
|
||||
|
||||
- name: Create database using default port
|
||||
mysql_db:
|
||||
name: '{{ db_to_create }}'
|
||||
state: present
|
||||
check_implicit_admin: yes
|
||||
config_file: '{{ config_file }}'
|
||||
config_overrides_defaults: no
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Must not fail because of the default of login_port is correct
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Reinit custom config file
|
||||
shell: 'echo "[client]" > {{ config_file }}'
|
||||
|
||||
- name: Add fake host to config file
|
||||
shell: 'echo "host = {{ fake_host }}" >> {{ config_file }}'
|
||||
|
||||
- name: Remove database using fake login_host
|
||||
mysql_db:
|
||||
name: '{{ db_to_create }}'
|
||||
state: absent
|
||||
config_file: '{{ config_file }}'
|
||||
config_overrides_defaults: yes
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Must fail because login_host default has beed overriden by wrong value from config file
|
||||
assert:
|
||||
that:
|
||||
- result is failed
|
||||
- result.msg is search("Can't connect to MySQL server on '{{ fake_host }}'")
|
||||
|
||||
# Clean up
|
||||
- name: Remove test db
|
||||
mysql_db:
|
||||
name: '{{ db_to_create }}'
|
||||
state: absent
|
||||
check_implicit_admin: yes
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
|
@ -1,78 +0,0 @@
|
|||
---
|
||||
|
||||
- set_fact:
|
||||
latin1_file1="{{tmp_dir}}/{{file}}"
|
||||
|
||||
- name: Deleting Latin1 encoded Database
|
||||
mysql_db:
|
||||
name: '{{ db_latin1_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: create Latin1 encoded database
|
||||
mysql_db:
|
||||
name: '{{ db_latin1_name }}'
|
||||
state: present
|
||||
encoding: latin1
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: create a table in Latin1 database
|
||||
command: mysql {{ db_latin1_name }} -e 'create table testlatin1(id int, name varchar(100));'
|
||||
|
||||
|
||||
# Inserting a string in latin1 into table, , this string be tested later,
|
||||
# so report any change of content in the test too
|
||||
- name: inserting data into Latin1 database
|
||||
command: mysql {{ db_latin1_name }} -e "insert into testlatin1 value(47,'Amédée Bôlüt');"
|
||||
|
||||
- name: selecting table
|
||||
command: mysql {{ db_latin1_name }} -e "select * from testlatin1;"
|
||||
register: output
|
||||
|
||||
- name: Dumping a table in Latin1 database
|
||||
mysql_db:
|
||||
name: "{{ db_latin1_name }}"
|
||||
encoding: latin1
|
||||
target: "{{ latin1_file1 }}"
|
||||
state: dump
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: dump_result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: state dump - file name should exist
|
||||
file: name={{ latin1_file1 }} state=file
|
||||
|
||||
- name: od the file and check of latin1 encoded string is present
|
||||
shell: grep -a 47 {{ latin1_file1 }} | od -c |grep "A m 351 d 351 e B 364\|A m 303 251 d 303 251 e B 303"
|
||||
|
||||
- name: Dropping {{ db_latin1_name }} database
|
||||
mysql_db:
|
||||
name: '{{ db_latin1_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: Importing the latin1 mysql script
|
||||
mysql_db:
|
||||
state: import
|
||||
encoding: latin1
|
||||
name: '{{ db_latin1_name }}'
|
||||
target: "{{ latin1_file1 }}"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: check encoding of table
|
||||
shell: mysql {{ db_latin1_name }} -e "SHOW FULL COLUMNS FROM testlatin1;"
|
||||
register: output
|
||||
failed_when: '"latin1_swedish_ci" not in output.stdout'
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: '{{ db_latin1_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
|
@ -1,263 +0,0 @@
|
|||
# test code for the mysql_db module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
|
||||
- name: remove database if it exists
|
||||
command: >
|
||||
mysql -sse "drop database {{db_name}};"
|
||||
ignore_errors: True
|
||||
|
||||
- name: make sure the test database is not there
|
||||
command: mysql {{db_name}}
|
||||
register: mysql_db_check
|
||||
failed_when: "'1049' not in mysql_db_check.stderr"
|
||||
|
||||
- name: test state=present for a database name (expect changed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message that database exist
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.db == '{{ db_name }}'
|
||||
- result.executed_commands == ["CREATE DATABASE `{{ db_name }}`"]
|
||||
|
||||
- name: run command to test state=present for a database name (expect db_name in stdout)
|
||||
command: mysql "-e show databases like '{{ db_name }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database exist
|
||||
assert: { that: "'{{ db_name }}' in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- name: test state=absent for a database name (expect changed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message that database does not exist
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.db == '{{ db_name }}'
|
||||
- result.executed_commands == ["DROP DATABASE `{{ db_name }}`"]
|
||||
|
||||
- name: run command to test state=absent for a database name (expect db_name not in stdout)
|
||||
command: mysql "-e show databases like '{{ db_name }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database does not exist
|
||||
assert: { that: "'{{ db_name }}' not in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- name: test mysql_db encoding param not valid - issue 8075
|
||||
mysql_db:
|
||||
name: datanotvalid
|
||||
state: present
|
||||
encoding: notvalid
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: assert test mysql_db encoding param not valid - issue 8075 (failed=true)
|
||||
assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "'Traceback' not in result.msg"
|
||||
- "'Unknown character set' in result.msg"
|
||||
|
||||
# ============================================================
|
||||
- name: test mysql_db using a valid encoding utf8 (expect changed=true)
|
||||
mysql_db:
|
||||
name: 'en{{ db_name }}'
|
||||
state: present
|
||||
encoding: utf8
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message created a database
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_commands == ["CREATE DATABASE `en{{ db_name }}` CHARACTER SET 'utf8'"]
|
||||
|
||||
- name: test database was created
|
||||
command: mysql "-e SHOW CREATE DATABASE en{{ db_name }};"
|
||||
register: result
|
||||
|
||||
- name: assert created database is of encoding utf8
|
||||
assert: { that: "'utf8' in result.stdout" }
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: 'en{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
# ============================================================
|
||||
- name: test mysql_db using valid encoding binary (expect changed=true)
|
||||
mysql_db:
|
||||
name: 'en{{ db_name }}'
|
||||
state: present
|
||||
encoding: binary
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message that database was created
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_commands == ["CREATE DATABASE `en{{ db_name }}` CHARACTER SET 'binary'"]
|
||||
|
||||
- name: run command to test database was created
|
||||
command: mysql "-e SHOW CREATE DATABASE en{{ db_name }};"
|
||||
register: result
|
||||
|
||||
- name: assert created database is of encoding binary
|
||||
assert: { that: "'binary' in result.stdout" }
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: 'en{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
# ============================================================
|
||||
- name: create user1 to access database dbuser1
|
||||
mysql_user:
|
||||
name: user1
|
||||
password: 'Hfd6fds^dfA8Ga'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: create database dbuser1 using user1
|
||||
mysql_db:
|
||||
name: '{{ db_user1 }}'
|
||||
state: present
|
||||
login_user: user1
|
||||
login_password: 'Hfd6fds^dfA8Ga'
|
||||
register: result
|
||||
|
||||
- name: assert output message that database was created
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: run command to test database was created using user1
|
||||
command: mysql "-e show databases like '{{ db_user1 }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database exist
|
||||
assert: { that: "'{{ db_user1 }}' in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- name: create user2 to access database with privilege select only
|
||||
mysql_user:
|
||||
name: user2
|
||||
password: 'kjsfd&F7safjad'
|
||||
priv: '*.*:SELECT'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: create database dbuser2 using user2 with no privilege to create (expect failed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_user2 }}'
|
||||
state: present
|
||||
login_user: user2
|
||||
login_password: 'kjsfd&F7safjad'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: assert output message that database was not created using dbuser2
|
||||
assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "'Access denied' in result.msg"
|
||||
|
||||
- name: run command to test that database was not created
|
||||
command: mysql "-e show databases like '{{ db_user2 }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database does not exist
|
||||
assert: { that: "'{{ db_user2 }}' not in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- name: delete database using user2 with no privilege to delete (expect failed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_user1 }}'
|
||||
state: absent
|
||||
login_user: user2
|
||||
login_password: 'kjsfd&F7safjad'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: assert output message that database was not deleted using dbuser2
|
||||
assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
- "'Access denied' in result.msg"
|
||||
|
||||
- name: run command to test database was not deleted
|
||||
command: mysql "-e show databases like '{{ db_user1 }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database still exist
|
||||
assert: { that: "'{{ db_user1 }}' in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- name: delete database using user1 with all privilege to delete a database (expect changed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_user1 }}'
|
||||
state: absent
|
||||
login_user: user1
|
||||
login_password: 'Hfd6fds^dfA8Ga'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- name: assert output message that database was deleted using user1
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_commands == ["DROP DATABASE `{{ db_user1 }}`"]
|
||||
|
||||
- name: run command to test database was deleted using user1
|
||||
command: mysql "-e show databases like '{{ db_name }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database does not exist
|
||||
assert: { that: "'{{ db_user1 }}' not in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- include: state_dump_import.yml format_type=sql file=dbdata.sql format_msg_type=ASCII file2=dump2.sql file3=dump3.sql file4=dump4.sql
|
||||
|
||||
- include: state_dump_import.yml format_type=gz file=dbdata.gz format_msg_type=gzip file2=dump2.gz file3=dump3.gz file4=dump4.gz
|
||||
|
||||
- include: state_dump_import.yml format_type=bz2 file=dbdata.bz2 format_msg_type=bzip2 file2=dump2.bz2 file3=dump3.bz2 file4=dump4.bz2
|
||||
|
||||
- include: multi_db_create_delete.yml
|
||||
|
||||
- include: encoding_dump_import.yml file=latin1.sql format_msg_type=ASCII
|
||||
|
||||
- include: config_overrides_defaults.yml
|
||||
when: ansible_python.version_info[0] >= 3
|
|
@ -1,557 +0,0 @@
|
|||
# Copyright (c) 2019, Pratik Gadiya <pratikgadiya1@gmail.com>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- set_fact:
|
||||
db1_name="database1"
|
||||
db2_name="database2"
|
||||
db3_name="database3"
|
||||
db4_name="database4"
|
||||
db5_name="database5"
|
||||
dump1_file="/tmp/dump1_file.sql"
|
||||
dump2_file="/tmp/all.sql"
|
||||
|
||||
# ============================== CREATE TEST ===============================
|
||||
#
|
||||
# ==========================================================================
|
||||
# Initial check - To confirm that database does not exist before executing check mode tasks
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases does not exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' not in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Create multiple databases that does not exists (check mode)
|
||||
- name: Create multiple databases that does not exists (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert successful completion of create database using check_mode since databases does not exist prior
|
||||
assert:
|
||||
that:
|
||||
- check_mode_result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases does not exist (since created via check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' not in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Create multiple databases
|
||||
- name: Create multiple databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert successful completion of create database
|
||||
assert:
|
||||
that:
|
||||
- result.changed == true
|
||||
- result.db_list == ['{{ db1_name }}', '{{ db2_name }}', '{{ db3_name }}']
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist after creation
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# =========================================================================
|
||||
# Recreate already existing databases (check mode)
|
||||
- name: Recreate already existing databases (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert that recreation of existing databases does not make change (since recreated using check mode)
|
||||
assert:
|
||||
that:
|
||||
- check_mode_result.changed == false
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist (since performed recreation of existing databases via check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Recreate same databases
|
||||
- name: Recreate multiple databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert that recreation of existing databases does not make change
|
||||
assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases does priorly exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Delete one of the databases (db2 here)
|
||||
- name: Delete db2 database
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db2_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert successful completion of deleting database
|
||||
assert:
|
||||
that:
|
||||
- result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that only db2 database does not exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# =========================================================================
|
||||
# Recreate multiple databases in which few databases does not exists (check mode)
|
||||
- name: Recreate multiple databases in which few databases does not exists (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert successful completion of recreation of partially existing database using check mode
|
||||
assert:
|
||||
that:
|
||||
- check_mode_result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that recreated non existing databases does not exist (since created via check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Create multiple databases
|
||||
- name: Create multiple databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert successful completion of create database
|
||||
assert:
|
||||
that:
|
||||
- result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# ============================== DUMP TEST =================================
|
||||
#
|
||||
# ==========================================================================
|
||||
# Check that dump file does not exist
|
||||
- name: Dump file does not exist
|
||||
file: name={{ dump1_file }} state=absent
|
||||
|
||||
# ==========================================================================
|
||||
# Dump existing databases (check mode)
|
||||
- name: Dump existing databases (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: dump
|
||||
target: '{{ dump1_file }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_dump_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert successful completion of dump operation using check mode
|
||||
assert:
|
||||
that:
|
||||
- check_mode_dump_result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist (check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
- name: state dump - file name should not exist (since dumped via check mode)
|
||||
file: name={{ dump1_file }} state=absent
|
||||
|
||||
# ==========================================================================
|
||||
# Dump existing and non-existing databases (check mode)
|
||||
- name: Dump existing and non-existing databases (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- "{{ db1_name }}"
|
||||
- "{{ db4_name }}"
|
||||
- "{{ db3_name }}"
|
||||
state: dump
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
target: "{{ dump1_file }}"
|
||||
register: check_mode_dump_result
|
||||
ignore_errors: True
|
||||
check_mode: yes
|
||||
|
||||
- name: assert that dump operation of existing and non existing databases does not make change (using check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'Cannot dump database' in check_mode_dump_result['msg']"
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist (since check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
- "'{{ db4_name }}' not in mysql_result.stdout"
|
||||
|
||||
- name: state dump - file name should not exist (since prior dump operation performed via check mode)
|
||||
file: name={{ dump1_file }} state=absent
|
||||
|
||||
# ==========================================================================
|
||||
# Dump non-existing databases (check mode)
|
||||
- name: Dump non-existing databases (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- "{{ db4_name }}"
|
||||
- "{{ db5_name }}"
|
||||
state: dump
|
||||
target: "{{ dump1_file }}"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_dump_result
|
||||
ignore_errors: True
|
||||
check_mode: yes
|
||||
|
||||
- name: assert successful completion of dump operation using check mode
|
||||
assert:
|
||||
that:
|
||||
- "'Cannot dump database' in check_mode_dump_result['msg']"
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist (since delete via check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
- "'{{ db4_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db5_name }}' not in mysql_result.stdout"
|
||||
|
||||
- name: state dump - file name should not exist (since prior dump operation performed via check mode)
|
||||
file: name={{ dump1_file }} state=absent
|
||||
|
||||
# ==========================================================================
|
||||
# Dump existing databases
|
||||
- name: Dump existing databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: dump
|
||||
target: '{{ dump1_file }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: dump_result
|
||||
|
||||
- name: assert successful completion of dump operation
|
||||
assert:
|
||||
that:
|
||||
- dump_result.changed == true
|
||||
- dump_result.db_list == ['{{ db1_name }}', '{{ db2_name }}', '{{ db3_name }}']
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
- name: state dump - file name should exist
|
||||
file: name={{ dump1_file }} state=file
|
||||
|
||||
- name: Check if db1 database create command is present in the dumped file
|
||||
shell: "grep -i 'CREATE DATABASE.*`{{ db1_name }}`' {{ dump1_file }}"
|
||||
|
||||
- name: Check if db2 database create command is present in the dumped file
|
||||
shell: "grep -i 'CREATE DATABASE.*`{{ db2_name }}`' {{ dump1_file }}"
|
||||
|
||||
- name: Check if db3 database create command is present in the dumped file
|
||||
shell: "grep -i 'CREATE DATABASE.*`{{ db3_name }}`' {{ dump1_file }}"
|
||||
|
||||
# ==========================================================================
|
||||
# Dump all databases
|
||||
|
||||
- name: state dump - dump2 file name should not exist
|
||||
file: name={{ dump2_file }} state=absent
|
||||
|
||||
- name: Dump existing databases
|
||||
mysql_db:
|
||||
name: all
|
||||
state: dump
|
||||
target: '{{ dump2_file }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: dump_result
|
||||
|
||||
- name: assert successful completion of dump operation
|
||||
assert:
|
||||
that:
|
||||
- dump_result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
- "'{{ db4_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db5_name }}' not in mysql_result.stdout"
|
||||
|
||||
- name: state dump - file name should exist
|
||||
file: name={{ dump2_file }} state=file
|
||||
|
||||
# ============================ DELETE TEST =================================
|
||||
#
|
||||
# ==========================================================================
|
||||
# Delete multiple databases which already exists (check mode)
|
||||
- name: Delete multiple databases which already exists (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert successful completion of delete databases which already exists using check mode
|
||||
assert:
|
||||
that:
|
||||
- check_mode_result.changed == true
|
||||
|
||||
- name: run command to test state=absent for a database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases exist even after deleting (since deleted via check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db2_name }}' in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Delete multiple databases
|
||||
- name: Delete multiple databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert successful completion of deleting database
|
||||
assert:
|
||||
that:
|
||||
- result.changed == true
|
||||
- result.db_list == ['{{ db2_name }}', '{{ db3_name }}']
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases does not exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' not in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Delete non existing databases (check mode)
|
||||
- name: Delete non existing databases (check mode)
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db4_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_mode_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert that deletion of non existing databases does not make change (using check mode)
|
||||
assert:
|
||||
that:
|
||||
- check_mode_result.changed == false
|
||||
|
||||
- name: run command to test state=absent for a database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases does not exist since were deleted priorly (check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db4_name }}' not in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Delete already deleted databases
|
||||
- name: Delete already deleted databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db4_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert that deletion of non existing databases does not make change
|
||||
assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that databases does not exists
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db4_name }}' not in mysql_result.stdout"
|
||||
|
||||
# ==========================================================================
|
||||
# Delete all databases
|
||||
- name: Delete all databases
|
||||
mysql_db:
|
||||
name:
|
||||
- '{{ db1_name }}'
|
||||
- '{{ db2_name }}'
|
||||
- '{{ db3_name }}'
|
||||
- '{{ db4_name }}'
|
||||
- '{{ db5_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert successful completion of deleting database
|
||||
assert:
|
||||
that:
|
||||
- result.changed == true
|
||||
|
||||
- name: run command to list databases like specified database name
|
||||
command: mysql "-e show databases like 'database%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that specific databases does not exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db1_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db2_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db3_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db4_name }}' not in mysql_result.stdout"
|
||||
- "'{{ db5_name }}' not in mysql_result.stdout"
|
||||
|
||||
- name: state dump - dump 1 file name should be removed
|
||||
file: name={{ dump1_file }} state=absent
|
||||
|
||||
- name: state dump - dump 2 file name should be removed
|
||||
file: name={{ dump2_file }} state=absent
|
|
@ -1,375 +0,0 @@
|
|||
# test code for state dump and import for mysql_db module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- set_fact:
|
||||
db_file_name="{{tmp_dir}}/{{file}}"
|
||||
wrong_sql_file="{{tmp_dir}}/wrong.sql"
|
||||
dump_file1="{{tmp_dir}}/{{file2}}"
|
||||
dump_file2="{{tmp_dir}}/{{file3}}"
|
||||
db_user="test"
|
||||
db_user_unsafe_password="pass!word"
|
||||
config_file="/root/.my.cnf"
|
||||
|
||||
- name: create custom config file
|
||||
shell: 'echo "[client]" > {{ config_file }}'
|
||||
|
||||
- name: create user for test unsafe_login_password parameter
|
||||
mysql_user:
|
||||
name: '{{ db_user }}'
|
||||
password: '{{ db_user_unsafe_password }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: state dump/import - create database
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
check_implicit_admin: yes
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: create database
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: present
|
||||
check_implicit_admin: no
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: state dump/import - create table department
|
||||
command: mysql {{ db_name }} '-e create table department(id int, name varchar(100));'
|
||||
|
||||
- name: state dump/import - create table employee
|
||||
command: mysql {{ db_name }} '-e create table employee(id int, name varchar(100));'
|
||||
|
||||
- name: state dump/import - insert data into table employee
|
||||
command: mysql {{ db_name }} "-e insert into employee value(47,'Joe Smith');"
|
||||
|
||||
- name: state dump/import - insert data into table department
|
||||
command: mysql {{ db_name }} "-e insert into department value(2,'Engineering');"
|
||||
|
||||
- name: state dump/import - file name should not exist
|
||||
file: name={{ db_file_name }} state=absent
|
||||
|
||||
- name: database dump file1 should not exist
|
||||
file: name={{ dump_file1 }} state=absent
|
||||
|
||||
- name: database dump file2 should not exist
|
||||
file: name={{ dump_file2 }} state=absent
|
||||
|
||||
- name: state dump without department table.
|
||||
mysql_db:
|
||||
login_user: '{{ db_user }}'
|
||||
login_password: '{{ db_user_unsafe_password }}'
|
||||
unsafe_login_password: yes
|
||||
name: "{{ db_name }}"
|
||||
state: dump
|
||||
target: "{{ db_file_name }}"
|
||||
ignore_tables:
|
||||
- "{{ db_name }}.department"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
force: yes
|
||||
master_data: 1
|
||||
skip_lock_tables: yes
|
||||
dump_extra_args: --skip-triggers
|
||||
config_file: '{{ config_file }}'
|
||||
restrict_config_file: yes
|
||||
check_implicit_admin: no
|
||||
register: result
|
||||
|
||||
- name: assert successful completion of dump operation
|
||||
assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_commands[0] is search("mysqldump --defaults-file={{ config_file }} --user={{ db_user }} --password=\*\*\*\*\*\*\*\* --force --socket={{ mysql_socket }} {{ db_name }} --skip-lock-tables --quick --ignore-table={{ db_name }}.department --master-data=1 --skip-triggers")
|
||||
|
||||
- name: state dump/import - file name should exist
|
||||
file: name={{ db_file_name }} state=file
|
||||
|
||||
- name: state dump with multiple databases in comma separated form.
|
||||
mysql_db:
|
||||
name: "{{ db_name }},{{ db_name2 }}"
|
||||
state: dump
|
||||
target: "{{ dump_file1 }}"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
check_implicit_admin: yes
|
||||
register: dump_result1
|
||||
|
||||
- name: assert successful completion of dump operation (with multiple databases in comma separated form)
|
||||
assert:
|
||||
that:
|
||||
- dump_result1 is changed
|
||||
- dump_result1.executed_commands[0] is search(" --user=root --password=''")
|
||||
|
||||
- name: state dump - dump file1 should exist
|
||||
file: name={{ dump_file1 }} state=file
|
||||
|
||||
- name: state dump with multiple databases in list form via check_mode
|
||||
mysql_db:
|
||||
name:
|
||||
- "{{ db_name }}"
|
||||
- "{{ db_name2 }}"
|
||||
state: dump
|
||||
target: "{{ dump_file2 }}"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: dump_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert successful completion of dump operation (with multiple databases in list form) via check mode
|
||||
assert:
|
||||
that:
|
||||
- "dump_result.changed == true"
|
||||
|
||||
- name: database dump file2 should not exist
|
||||
stat:
|
||||
path: "{{ dump_file2 }}"
|
||||
register: stat_result
|
||||
|
||||
- name: assert that check_mode does not create dump file for databases
|
||||
assert:
|
||||
that:
|
||||
- stat_result.stat.exists is defined and not stat_result.stat.exists
|
||||
|
||||
- name: state dump with multiple databases in list form.
|
||||
mysql_db:
|
||||
name:
|
||||
- "{{ db_name }}"
|
||||
- "{{ db_name2 }}"
|
||||
state: dump
|
||||
target: "{{ dump_file2 }}"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: dump_result2
|
||||
|
||||
- name: assert successful completion of dump operation (with multiple databases in list form)
|
||||
assert:
|
||||
that:
|
||||
- "dump_result2.changed == true"
|
||||
|
||||
- name: state dump - dump file2 should exist
|
||||
file: name={{ dump_file2 }} state=file
|
||||
|
||||
- name: state dump/import - remove database
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: test state=import to restore the database of type {{ format_type }} (expect changed=true)
|
||||
mysql_db:
|
||||
login_user: '{{ db_user }}'
|
||||
login_password: '{{ db_user_unsafe_password }}'
|
||||
unsafe_login_password: yes
|
||||
name: '{{ db_name }}'
|
||||
state: import
|
||||
target: '{{ db_file_name }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
use_shell: yes
|
||||
register: result
|
||||
|
||||
- name: show the tables
|
||||
command: mysql {{ db_name }} "-e show tables;"
|
||||
register: result
|
||||
|
||||
- name: assert that the department table is absent.
|
||||
assert:
|
||||
that:
|
||||
- "'department' not in result.stdout"
|
||||
|
||||
- name: test state=import to restore a database from multiple database dumped file1
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: import
|
||||
target: '{{ dump_file1 }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
use_shell: no
|
||||
register: import_result
|
||||
|
||||
- name: assert output message restored a database from dump file1
|
||||
assert: { that: "import_result.changed == true" }
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: run command to list databases
|
||||
command: mysql "-e show databases like 'data%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that db_name2 database does not exist
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db_name2 }}' not in mysql_result.stdout"
|
||||
|
||||
- name: test state=import to restore a database from dumped file2 (check mode)
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: import
|
||||
target: '{{ dump_file2 }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: check_import_result
|
||||
check_mode: yes
|
||||
|
||||
- name: assert output message restored a database from dump file2 (check mode)
|
||||
assert: { that: "check_import_result.changed == true" }
|
||||
|
||||
- name: run command to list databases
|
||||
command: mysql "-e show databases like 'data%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that db_name2 database does not exist (check mode)
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db_name2 }}' not in mysql_result.stdout"
|
||||
|
||||
- name: test state=import to restore a database from multiple database dumped file2
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: import
|
||||
target: '{{ dump_file2 }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: import_result2
|
||||
|
||||
- name: assert output message restored a database from dump file2
|
||||
assert:
|
||||
that:
|
||||
- import_result2.changed == true
|
||||
- import_result2.db_list == ['{{ db_name2 }}']
|
||||
|
||||
- name: run command to list databases
|
||||
command: mysql "-e show databases like 'data%';"
|
||||
register: mysql_result
|
||||
|
||||
- name: assert that db_name2 database does exist after import
|
||||
assert:
|
||||
that:
|
||||
- "'{{ db_name2 }}' in mysql_result.stdout"
|
||||
|
||||
- name: test state=dump to backup the database of type {{ format_type }} (expect changed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: dump
|
||||
target: '{{ db_file_name }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message backup the database
|
||||
assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "result.db =='{{ db_name }}'"
|
||||
|
||||
- name: assert database was backed up successfully
|
||||
command: file {{ db_file_name }}
|
||||
register: result
|
||||
|
||||
- name: assert file format type
|
||||
assert: { that: "'{{format_msg_type}}' in result.stdout" }
|
||||
|
||||
- name: update database table employee
|
||||
command: mysql {{ db_name }} "-e update employee set name='John Doe' where id=47;"
|
||||
|
||||
- name: test state=import to restore the database of type {{ format_type }} (expect changed=true)
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: import
|
||||
target: '{{ db_file_name }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message restore the database
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: select data from table employee
|
||||
command: mysql {{ db_name }} "-e select * from employee;"
|
||||
register: result
|
||||
|
||||
- name: assert data in database is from the restore database
|
||||
assert:
|
||||
that:
|
||||
- "'47' in result.stdout"
|
||||
- "'Joe Smith' in result.stdout"
|
||||
|
||||
##########################
|
||||
# Test ``force`` parameter
|
||||
##########################
|
||||
|
||||
- name: create wrong sql file
|
||||
shell: echo 'CREATE TABLE hello (id int); CREATE ELBAT ehlo (int id);' >> '{{ wrong_sql_file }}'
|
||||
|
||||
- name: try to import without force parameter, must fail
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: import
|
||||
target: '{{ wrong_sql_file }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
force: no
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.failed == true
|
||||
|
||||
- name: try to import with force parameter
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: import
|
||||
target: '{{ wrong_sql_file }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
force: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
##########
|
||||
# Clean up
|
||||
##########
|
||||
|
||||
- name: remove database name
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: '{{ db_name2 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: remove file name
|
||||
file: name={{ db_file_name }} state=absent
|
||||
|
||||
- name: remove file name
|
||||
file: name={{ wrong_sql_file }} state=absent
|
||||
|
||||
- name: remove dump file1
|
||||
file: name={{ dump_file1 }} state=absent
|
||||
|
||||
- name: remove dump file2
|
||||
file: name={{ dump_file2 }} state=absent
|
|
@ -1,6 +0,0 @@
|
|||
destructive
|
||||
shippable/posix/group1
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
||||
skip/rhel
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
# defaults file for test_mysql_info
|
||||
db_name: data
|
||||
user_name: alice
|
||||
user_pass: alice
|
|
@ -1,3 +0,0 @@
|
|||
dependencies:
|
||||
- setup_mysql_db
|
||||
- setup_remote_tmp_dir
|
|
@ -1,190 +0,0 @@
|
|||
# Test code for mysql_info module
|
||||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
###################
|
||||
# Prepare for tests
|
||||
#
|
||||
|
||||
# Create role for tests
|
||||
- name: mysql_info - create mysql user {{ user_name }}
|
||||
mysql_user:
|
||||
name: '{{ user_name }}'
|
||||
password: '{{ user_pass }}'
|
||||
state: present
|
||||
priv: '*.*:ALL'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
# Create default MySQL config file with credentials
|
||||
- name: mysql_info - create default config file
|
||||
template:
|
||||
src: my.cnf.j2
|
||||
dest: /root/.my.cnf
|
||||
mode: '0400'
|
||||
|
||||
# Create non-default MySQL config file with credentials
|
||||
- name: mysql_info - create non-default config file
|
||||
template:
|
||||
src: my.cnf.j2
|
||||
dest: /root/non-default_my.cnf
|
||||
mode: '0400'
|
||||
|
||||
###############
|
||||
# Do tests
|
||||
|
||||
# Access by default cred file
|
||||
- name: mysql_info - collect default cred file
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.version != {}
|
||||
- result.settings != {}
|
||||
- result.global_status != {}
|
||||
- result.databases != {}
|
||||
- result.engines != {}
|
||||
- result.users != {}
|
||||
|
||||
# Access by non-default cred file
|
||||
- name: mysql_info - check non-default cred file
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
config_file: /root/non-default_my.cnf
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.version != {}
|
||||
|
||||
# Remove cred files
|
||||
- name: mysql_info - remove cred files
|
||||
file:
|
||||
path: '{{ item }}'
|
||||
state: absent
|
||||
with_items:
|
||||
- /root/.my.cnf
|
||||
- /root/non-default_my.cnf
|
||||
|
||||
# Access with password
|
||||
- name: mysql_info - check access with password
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.version != {}
|
||||
|
||||
# Test excluding
|
||||
- name: Collect all info except settings and users
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
filter: '!settings,!users'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.version != {}
|
||||
- result.global_status != {}
|
||||
- result.databases != {}
|
||||
- result.engines != {}
|
||||
- result.settings is not defined
|
||||
- result.users is not defined
|
||||
|
||||
# Test including
|
||||
- name: Collect info only about version and databases
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
filter:
|
||||
- version
|
||||
- databases
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.version != {}
|
||||
- result.databases != {}
|
||||
- result.engines is not defined
|
||||
- result.settings is not defined
|
||||
- result.global_status is not defined
|
||||
- result.users is not defined
|
||||
|
||||
# Test exclude_fields: db_size
|
||||
# 'unsupported' element is passed to check that an unsupported value
|
||||
# won't break anything (will be ignored regarding to the module's documentation).
|
||||
- name: Collect info about databases excluding their sizes
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
filter:
|
||||
- databases
|
||||
exclude_fields:
|
||||
- db_size
|
||||
- unsupported
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.databases != {}
|
||||
- result.databases.mysql == {}
|
||||
|
||||
########################################################
|
||||
# Issue #65727, empty databases must be in returned dict
|
||||
#
|
||||
- name: Create empty database acme
|
||||
mysql_db:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
name: acme
|
||||
|
||||
- name: Collect info about databases
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
filter:
|
||||
- databases
|
||||
return_empty_dbs: true
|
||||
register: result
|
||||
|
||||
# Check acme is in returned dict
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.databases.acme.size == 0
|
||||
- result.databases.mysql != {}
|
||||
|
||||
- name: Collect info about databases excluding their sizes
|
||||
mysql_info:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
filter:
|
||||
- databases
|
||||
exclude_fields:
|
||||
- db_size
|
||||
return_empty_dbs: true
|
||||
register: result
|
||||
|
||||
# Check acme is in returned dict
|
||||
- assert:
|
||||
that:
|
||||
- result.changed == false
|
||||
- result.databases.acme == {}
|
||||
- result.databases.mysql == {}
|
||||
|
||||
- name: Remove acme database
|
||||
mysql_db:
|
||||
login_user: '{{ user_name }}'
|
||||
login_password: '{{ user_pass }}'
|
||||
name: acme
|
||||
state: absent
|
|
@ -1,3 +0,0 @@
|
|||
[client]
|
||||
user={{ user_name }}
|
||||
password={{ user_pass }}
|
|
@ -1,6 +0,0 @@
|
|||
destructive
|
||||
shippable/posix/group3
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
||||
skip/rhel
|
|
@ -1,6 +0,0 @@
|
|||
root_user: root
|
||||
db_name: data
|
||||
test_db: testdb
|
||||
test_table1: test1
|
||||
test_table2: test2
|
||||
test_script_path: /tmp/test.sql
|
|
@ -1,2 +0,0 @@
|
|||
dependencies:
|
||||
- setup_mysql8
|
|
@ -1,5 +0,0 @@
|
|||
# mysql_query module initial CI tests
|
||||
- import_tasks: mysql_query_initial.yml
|
||||
when:
|
||||
- ansible_distribution == 'CentOS'
|
||||
- ansible_distribution_major_version >= '7'
|
|
@ -1,249 +0,0 @@
|
|||
# Test code for mysql_query module
|
||||
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
- vars:
|
||||
mysql_parameters: &mysql_params
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: '{{ root_user }}'
|
||||
login_password: '{{ root_pass }}'
|
||||
|
||||
block:
|
||||
|
||||
- name: Create db {{ test_db }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
query: 'CREATE DATABASE {{ test_db }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['CREATE DATABASE {{ test_db }}']
|
||||
|
||||
- name: Create {{ test_table1 }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'CREATE TABLE {{ test_table1 }} (id int)'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['CREATE TABLE {{ test_table1 }} (id int)']
|
||||
|
||||
- name: Insert test data
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query:
|
||||
- 'INSERT INTO {{ test_table1 }} VALUES (1), (2)'
|
||||
- 'INSERT INTO {{ test_table1 }} VALUES (3)'
|
||||
single_transaction: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.rowcount == [2, 1]
|
||||
- result.executed_queries == ['INSERT INTO {{ test_table1 }} VALUES (1), (2)', 'INSERT INTO {{ test_table1 }} VALUES (3)']
|
||||
|
||||
- name: Check data in {{ test_table1 }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table1 }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ['SELECT * FROM {{ test_table1 }}']
|
||||
- result.rowcount == [3]
|
||||
- result.query_result[0][0].id == 1
|
||||
- result.query_result[0][1].id == 2
|
||||
- result.query_result[0][2].id == 3
|
||||
|
||||
- name: Check data in {{ test_table1 }} using positional args
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table1 }} WHERE id = %s'
|
||||
positional_args:
|
||||
- 1
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ["SELECT * FROM {{ test_table1 }} WHERE id = 1"]
|
||||
- result.rowcount == [1]
|
||||
- result.query_result[0][0].id == 1
|
||||
|
||||
- name: Check data in {{ test_table1 }} using named args
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table1 }} WHERE id = %(some_id)s'
|
||||
named_args:
|
||||
some_id: 1
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ["SELECT * FROM {{ test_table1 }} WHERE id = 1"]
|
||||
- result.rowcount == [1]
|
||||
- result.query_result[0][0].id == 1
|
||||
|
||||
- name: Update data in {{ test_table1 }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'UPDATE {{ test_table1 }} SET id = %(new_id)s WHERE id = %(current_id)s'
|
||||
named_args:
|
||||
current_id: 1
|
||||
new_id: 0
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['UPDATE {{ test_table1 }} SET id = 0 WHERE id = 1']
|
||||
- result.rowcount == [1]
|
||||
|
||||
- name: Check the prev update - row with value 1 does not exist anymore
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table1 }} WHERE id = %(some_id)s'
|
||||
named_args:
|
||||
some_id: 1
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ['SELECT * FROM {{ test_table1 }} WHERE id = 1']
|
||||
- result.rowcount == [0]
|
||||
|
||||
- name: Check the prev update - row with value - exist
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table1 }} WHERE id = %(some_id)s'
|
||||
named_args:
|
||||
some_id: 0
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ['SELECT * FROM {{ test_table1 }} WHERE id = 0']
|
||||
- result.rowcount == [1]
|
||||
|
||||
- name: Update data in {{ test_table1 }} again
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'UPDATE {{ test_table1 }} SET id = %(new_id)s WHERE id = %(current_id)s'
|
||||
named_args:
|
||||
current_id: 1
|
||||
new_id: 0
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ['UPDATE {{ test_table1 }} SET id = 0 WHERE id = 1']
|
||||
- result.rowcount == [0]
|
||||
|
||||
- name: Delete data from {{ test_table1 }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query:
|
||||
- 'DELETE FROM {{ test_table1 }} WHERE id = 0'
|
||||
- 'SELECT * FROM {{ test_table1 }} WHERE id = 0'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['DELETE FROM {{ test_table1 }} WHERE id = 0', 'SELECT * FROM {{ test_table1 }} WHERE id = 0']
|
||||
- result.rowcount == [1, 0]
|
||||
|
||||
- name: Delete data from {{ test_table1 }} again
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'DELETE FROM {{ test_table1 }} WHERE id = 0'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
- result.executed_queries == ['DELETE FROM {{ test_table1 }} WHERE id = 0']
|
||||
- result.rowcount == [0]
|
||||
|
||||
- name: Truncate {{ test_table1 }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query:
|
||||
- 'TRUNCATE {{ test_table1 }}'
|
||||
- 'SELECT * FROM {{ test_table1 }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['TRUNCATE {{ test_table1 }}', 'SELECT * FROM {{ test_table1 }}']
|
||||
- result.rowcount == [0, 0]
|
||||
|
||||
- name: Rename {{ test_table1 }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'RENAME TABLE {{ test_table1 }} TO {{ test_table2 }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['RENAME TABLE {{ test_table1 }} TO {{ test_table2 }}']
|
||||
- result.rowcount == [0]
|
||||
|
||||
- name: Check the prev rename
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table1 }}'
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.failed == true
|
||||
|
||||
- name: Check the prev rename
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
login_db: '{{ test_db }}'
|
||||
query: 'SELECT * FROM {{ test_table2 }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.rowcount == [0]
|
||||
|
||||
- name: Drop db {{ test_db }}
|
||||
mysql_query:
|
||||
<<: *mysql_params
|
||||
query: 'DROP DATABASE {{ test_db }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.executed_queries == ['DROP DATABASE {{ test_db }}']
|
|
@ -1,8 +0,0 @@
|
|||
destructive
|
||||
shippable/posix/group4
|
||||
# Make sure that this test runs in a different group than mariadb_replication!
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
||||
skip/rhel
|
||||
needs/root
|
|
@ -1,9 +0,0 @@
|
|||
master_port: 3306
|
||||
standby_port: 3307
|
||||
test_db: test_db
|
||||
test_table: test_table
|
||||
test_master_delay: 60
|
||||
replication_user: replication_user
|
||||
replication_pass: replication_pass
|
||||
dump_path: /tmp/dump.sql
|
||||
test_channel: test_channel-1
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
dependencies:
|
||||
- setup_mysql_replication
|
|
@ -1,26 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Initial CI tests of mysql_replication module:
|
||||
- import_tasks: mysql_replication_initial.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '==')
|
||||
|
||||
# Tests of master_delay parameter:
|
||||
- import_tasks: mysql_replication_master_delay.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '==')
|
||||
|
||||
# Tests of channel parameter:
|
||||
- import_tasks: mysql_replication_channel.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '==')
|
||||
|
||||
# Tests of resetmaster mode:
|
||||
- import_tasks: mysql_replication_resetmaster_mode.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version('7', '==')
|
|
@ -1,119 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Needs for further tests:
|
||||
- name: Stop slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: stopslave
|
||||
|
||||
- name: Reset slave all
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: resetslaveall
|
||||
|
||||
# Get master log file and log pos:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ master_port }}"
|
||||
mode: getmaster
|
||||
register: master_status
|
||||
|
||||
# Test changemaster mode:
|
||||
- name: Run replication with channel
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ master_port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: "{{ master_status.File }}"
|
||||
master_log_pos: "{{ master_status.Position }}"
|
||||
channel: "{{ test_channel }}"
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["CHANGE MASTER TO MASTER_HOST='127.0.0.1',MASTER_USER='replication_user',MASTER_PASSWORD='********',MASTER_PORT=3306,MASTER_LOG_FILE='{{ master_status.File }}',MASTER_LOG_POS={{ master_status.Position }} FOR CHANNEL '{{ test_channel }}'"]
|
||||
|
||||
# Test startslave mode:
|
||||
- name: Start slave with channel
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
channel: '{{ test_channel }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["START SLAVE FOR CHANNEL '{{ test_channel }}'"]
|
||||
|
||||
# Test getslave mode:
|
||||
- name: Get standby status with channel
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: getslave
|
||||
channel: '{{ test_channel }}'
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Is_Slave == true
|
||||
- slave_status.Master_Host == '127.0.0.1'
|
||||
- slave_status.Exec_Master_Log_Pos == master_status.Position
|
||||
- slave_status.Master_Port == {{ master_port }}
|
||||
- slave_status.Last_IO_Errno == 0
|
||||
- slave_status.Last_IO_Error == ''
|
||||
- slave_status.Channel_Name == '{{ test_channel }}'
|
||||
- slave_status is not changed
|
||||
|
||||
# Test stopslave mode:
|
||||
- name: Stop slave with channel
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: stopslave
|
||||
channel: '{{ test_channel }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["STOP SLAVE FOR CHANNEL '{{ test_channel }}'"]
|
||||
|
||||
# Test reset
|
||||
- name: Reset slave with channel
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: resetslave
|
||||
channel: '{{ test_channel }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["RESET SLAVE FOR CHANNEL '{{ test_channel }}'"]
|
||||
|
||||
# Test reset all
|
||||
- name: Reset slave all with channel
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: resetslaveall
|
||||
channel: '{{ test_channel }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["RESET SLAVE ALL FOR CHANNEL '{{ test_channel }}'"]
|
|
@ -1,191 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Preparation:
|
||||
- name: Create user for replication
|
||||
shell: "echo \"GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'localhost' IDENTIFIED BY '{{ replication_pass }}'; FLUSH PRIVILEGES;\" | mysql -P {{ master_port }} -h 127.0.0.1"
|
||||
|
||||
- name: Create test database
|
||||
mysql_db:
|
||||
login_host: 127.0.0.1
|
||||
login_port: '{{ master_port }}'
|
||||
state: present
|
||||
name: '{{ test_db }}'
|
||||
|
||||
- name: Dump all databases from the master
|
||||
shell: 'mysqldump -P {{ master_port }} -h 127.0.0.1 --all-databases --master-data=2 > {{ dump_path }}'
|
||||
|
||||
- name: Restore the dump to the standby
|
||||
shell: 'mysql -P {{ standby_port }} -h 127.0.0.1 < {{ dump_path }}'
|
||||
|
||||
# Test getmaster mode:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ master_port }}"
|
||||
mode: getmaster
|
||||
register: master_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- master_status.Is_Master == true
|
||||
- master_status.Position != 0
|
||||
- master_status is not changed
|
||||
|
||||
# Test startslave fails without changemaster first. This needs fail_on_error
|
||||
- name: Start slave and fail because master is not specified; failing on error as requested
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
fail_on_error: yes
|
||||
register: result
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is failed
|
||||
|
||||
# Test startslave doesn't fail if fail_on_error: no
|
||||
- name: Start slave and fail without propagating it to ansible as we were asked not to
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
fail_on_error: no
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not failed
|
||||
|
||||
# Test startslave doesn't fail if there is no fail_on_error.
|
||||
# This is suboptimal because nothing happens, but it's the old behavior.
|
||||
- name: Start slave and fail without propagating it to ansible as previous versions did not fail on error
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not failed
|
||||
|
||||
# Test changemaster mode:
|
||||
# master_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:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: changemaster
|
||||
master_host: 127.0.0.1
|
||||
master_port: "{{ master_port }}"
|
||||
master_user: "{{ replication_user }}"
|
||||
master_password: "{{ replication_pass }}"
|
||||
master_log_file: "{{ master_status.File }}"
|
||||
master_log_pos: "{{ master_status.Position }}"
|
||||
master_ssl_ca: ''
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["CHANGE MASTER TO MASTER_HOST='127.0.0.1',MASTER_USER='replication_user',MASTER_PASSWORD='********',MASTER_PORT=3306,MASTER_LOG_FILE='{{ master_status.File }}',MASTER_LOG_POS={{ master_status.Position }},MASTER_SSL_CA=''"]
|
||||
|
||||
# Test startslave mode:
|
||||
- name: Start slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["START SLAVE"]
|
||||
|
||||
# Test getslave mode:
|
||||
- name: Get standby status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Is_Slave == true
|
||||
- slave_status.Master_Host == '127.0.0.1'
|
||||
- slave_status.Exec_Master_Log_Pos == master_status.Position
|
||||
- slave_status.Master_Port == {{ master_port }}
|
||||
- slave_status.Last_IO_Errno == 0
|
||||
- slave_status.Last_IO_Error == ''
|
||||
- slave_status is not changed
|
||||
|
||||
# Create test table and add data to it:
|
||||
- name: Create test table
|
||||
shell: "echo \"CREATE TABLE {{ test_table }} (id int);\" | mysql -P {{ master_port }} -h 127.0.0.1 {{ test_db }}"
|
||||
|
||||
- name: Insert data
|
||||
shell: >
|
||||
echo "INSERT INTO {{ test_table }} (id) VALUES (1), (2), (3); FLUSH LOGS;" |
|
||||
mysql -P {{ master_port }} -h 127.0.0.1 {{ test_db }}
|
||||
|
||||
- name: Small pause to be sure the bin log, which was flushed previously, reached the slave
|
||||
pause:
|
||||
seconds: 2
|
||||
|
||||
# Test master log pos has been changed:
|
||||
- name: Get standby status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
# master_status.Position is not actual and it has been changed by the prev step,
|
||||
# so slave_status.Exec_Master_Log_Pos must be different:
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.Exec_Master_Log_Pos != master_status.Position
|
||||
|
||||
- name: Start slave that is already running
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
fail_on_error: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
# Test stopslave mode:
|
||||
- name: Stop slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: stopslave
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["STOP SLAVE"]
|
||||
|
||||
# Test stopslave mode:
|
||||
- name: Stop slave that is no longer running
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: stopslave
|
||||
fail_on_error: true
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
|
@ -1,44 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Test master_delay mode:
|
||||
- name: Run replication
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: changemaster
|
||||
master_delay: '{{ test_master_delay }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["CHANGE MASTER TO MASTER_DELAY=60"]
|
||||
|
||||
# Auxiliary step:
|
||||
- name: Start slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: startslave
|
||||
register: result
|
||||
|
||||
# Check master_delay:
|
||||
- name: Get standby status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: getslave
|
||||
register: slave_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- slave_status.SQL_Delay == {{ test_master_delay }}
|
||||
- slave_status is not changed
|
||||
|
||||
# Stop standby for further tests:
|
||||
- name: Stop slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: stopslave
|
|
@ -1,48 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Needs for further tests:
|
||||
- name: Stop slave
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: stopslave
|
||||
|
||||
- name: Reset slave all
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ standby_port }}"
|
||||
mode: resetslaveall
|
||||
|
||||
# Get master initial status:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ master_port }}"
|
||||
mode: getmaster
|
||||
register: master_initial_status
|
||||
|
||||
# Test resetmaster mode:
|
||||
- name: Reset master
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ master_port }}"
|
||||
mode: resetmaster
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["RESET MASTER"]
|
||||
|
||||
# Get master final status:
|
||||
- name: Get master status
|
||||
mysql_replication:
|
||||
login_host: 127.0.0.1
|
||||
login_port: "{{ master_port }}"
|
||||
mode: getmaster
|
||||
register: master_final_status
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- master_initial_status.File != master_final_status.File
|
|
@ -1,5 +0,0 @@
|
|||
destructive
|
||||
shippable/posix/group1
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
# defaults file for test_mysql_user
|
||||
db_name: 'data'
|
||||
user_name_1: 'db_user1'
|
||||
user_name_2: 'db_user2'
|
||||
user_name_3: 'db_user3'
|
||||
|
||||
user_password_1: 'gadfFDSdtTU^Sdfuj'
|
||||
user_password_2: 'jkFKUdfhdso78yi&td'
|
||||
user_password_3: 'jkFKUdfhdso78yi&tk'
|
||||
|
||||
root_password: 'zevuR6oPh7'
|
||||
|
||||
db_names:
|
||||
- clientdb
|
||||
- employeedb
|
||||
- providerdb
|
||||
|
||||
tmp_dir: '/tmp'
|
|
@ -1,8 +0,0 @@
|
|||
USE foo;
|
||||
DELIMITER ;;
|
||||
CREATE FUNCTION `function` () RETURNS tinyint(4)
|
||||
BEGIN
|
||||
DECLARE NAME_FOUND tinyint DEFAULT 0;
|
||||
RETURN NAME_FOUND;
|
||||
END;;
|
||||
DELIMITER ;
|
|
@ -1,5 +0,0 @@
|
|||
USE bar;
|
||||
DELIMITER ;;
|
||||
CREATE PROCEDURE `procedure` ()
|
||||
SELECT * FROM bar;;
|
||||
DELIMITER ;
|
|
@ -1,3 +0,0 @@
|
|||
dependencies:
|
||||
- setup_mysql_db
|
||||
- setup_remote_tmp_dir
|
|
@ -1,25 +0,0 @@
|
|||
# test code to assert no mysql user
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- name: run command to query for mysql user
|
||||
command: mysql "-e SELECT User FROM mysql.user where user='{{ user_name }}';"
|
||||
register: result
|
||||
|
||||
- name: assert mysql user is not present
|
||||
assert: { that: "'{{ user_name }}' not in result.stdout" }
|
|
@ -1,34 +0,0 @@
|
|||
# test code to assert mysql user
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- name: run command to query for mysql user
|
||||
command: mysql "-e SELECT User FROM mysql.user where user='{{ user_name }}';"
|
||||
register: result
|
||||
|
||||
- name: assert mysql user is present
|
||||
assert: { that: "'{{ user_name }}' in result.stdout" }
|
||||
|
||||
- name: run command to show privileges for user (expect privileges in stdout)
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name }}'@'localhost';"
|
||||
register: result
|
||||
when: priv is defined
|
||||
|
||||
- name: assert user has giving privileges
|
||||
assert: { that: "'GRANT {{priv}} ON *.*' in result.stdout" }
|
||||
when: priv is defined
|
|
@ -1,29 +0,0 @@
|
|||
# test code to create mysql user
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- name: create mysql user {{user_name}}
|
||||
mysql_user:
|
||||
name: '{{user_name}}'
|
||||
password: '{{user_password}}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message mysql user was created
|
||||
assert: { that: "result.changed == true" }
|
|
@ -1,78 +0,0 @@
|
|||
---
|
||||
|
||||
- name: Issue test setup - drop database
|
||||
mysql_db:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
loop:
|
||||
- foo
|
||||
- bar
|
||||
|
||||
- name: Issue test setup - create database
|
||||
mysql_db:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
loop:
|
||||
- foo
|
||||
- bar
|
||||
|
||||
- name: Copy SQL scripts to remote
|
||||
copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ remote_tmp_dir }}/{{ item | basename }}"
|
||||
with_items:
|
||||
- create-function.sql
|
||||
- create-procedure.sql
|
||||
|
||||
- name: Create function for test
|
||||
shell: "mysql < {{ remote_tmp_dir }}/create-function.sql"
|
||||
|
||||
- name: Create procedure for test
|
||||
shell: "mysql < {{ remote_tmp_dir }}/create-procedure.sql"
|
||||
|
||||
- name: Create user with FUNCTION and PROCEDURE privileges
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
state: present
|
||||
priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert Create user with FUNCTION and PROCEDURE privileges
|
||||
assert:
|
||||
that:
|
||||
- result is success
|
||||
- result is changed
|
||||
|
||||
- name: Create user with FUNCTION and PROCEDURE privileges - Idempotent check
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
state: present
|
||||
priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert Create user with FUNCTION and PROCEDURE privileges
|
||||
assert:
|
||||
that:
|
||||
- result is success
|
||||
- result is not changed
|
||||
|
||||
- name: Remove user
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: Issue test teardown - cleanup databases
|
||||
mysql_db:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
loop:
|
||||
- foo
|
||||
- bar
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
|
||||
- name: Set root password
|
||||
mysql_user:
|
||||
name: root
|
||||
password: '{{ root_password }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_password }}'
|
||||
check_implicit_admin: yes
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert root password is changed
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: Set root password again
|
||||
mysql_user:
|
||||
name: root
|
||||
password: '{{ root_password }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_password }}'
|
||||
check_implicit_admin: yes
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert root password is not changed
|
||||
assert: { that: "result.changed == false" }
|
|
@ -1,222 +0,0 @@
|
|||
# test code for the mysql_user module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 dof the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
# create mysql user and verify user is added to mysql database
|
||||
#
|
||||
- include: create_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- include: resource_limits.yml
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_1}}
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- include: assert_no_user.yml user_name={{user_name_1}}
|
||||
|
||||
# ============================================================
|
||||
# Create mysql user that already exist on mysql database
|
||||
#
|
||||
- include: create_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- name: create mysql user that already exist (expect changed=false)
|
||||
mysql_user:
|
||||
name: '{{user_name_1}}'
|
||||
password: '{{user_password_1}}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message mysql user was not created
|
||||
assert: { that: "result.changed == false" }
|
||||
|
||||
# ============================================================
|
||||
# remove mysql user and verify user is removed from mysql database
|
||||
#
|
||||
- name: remove mysql user state=absent (expect changed=true)
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message mysql user was removed
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- include: assert_no_user.yml user_name={{user_name_1}}
|
||||
|
||||
# ============================================================
|
||||
# remove mysql user that does not exist on mysql database
|
||||
#
|
||||
- name: remove mysql user that does not exist state=absent (expect changed=false)
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message mysql user that does not exist
|
||||
assert: { that: "result.changed == false" }
|
||||
|
||||
- include: assert_no_user.yml user_name={{user_name_1}}
|
||||
|
||||
# ============================================================
|
||||
# Create user with no privileges and verify default privileges are assign
|
||||
#
|
||||
- name: create user with select privilege state=present (expect changed=true)
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_1}} priv=USAGE
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- include: assert_no_user.yml user_name={{user_name_1}}
|
||||
|
||||
# ============================================================
|
||||
# Create user with select privileges and verify select privileges are assign
|
||||
#
|
||||
- name: create user with select privilege state=present (expect changed=true)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
state: present
|
||||
priv: '*.*:SELECT'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv=SELECT
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_2 }}
|
||||
|
||||
- include: assert_no_user.yml user_name={{user_name_2}}
|
||||
|
||||
# ============================================================
|
||||
# Assert user has access to multiple databases
|
||||
#
|
||||
- name: give users access to multiple databases
|
||||
mysql_user:
|
||||
name: '{{ item[0] }}'
|
||||
priv: '{{ item[1] }}.*:ALL'
|
||||
append_privs: yes
|
||||
password: '{{ user_password_1 }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
with_nested:
|
||||
- [ '{{ user_name_1 }}', '{{ user_name_2 }}']
|
||||
- "{{db_names}}"
|
||||
|
||||
- name: show grants access for user1 on multiple database
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_1 }}'@'localhost';"
|
||||
register: result
|
||||
|
||||
- name: assert grant access for user1 on multiple database
|
||||
assert: { that: "'{{ item }}' in result.stdout" }
|
||||
with_items: "{{db_names}}"
|
||||
|
||||
- name: show grants access for user2 on multiple database
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';"
|
||||
register: result
|
||||
|
||||
- name: assert grant access for user2 on multiple database
|
||||
assert: { that: "'{{ item }}' in result.stdout" }
|
||||
with_items: "{{db_names}}"
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }}
|
||||
|
||||
- name: give user access to database via wildcard
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
priv: '%db.*:SELECT'
|
||||
append_privs: yes
|
||||
password: '{{ user_password_1 }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: show grants access for user1 on multiple database
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_1 }}'@'localhost';"
|
||||
register: result
|
||||
|
||||
- name: assert grant access for user1 on multiple database
|
||||
assert:
|
||||
that:
|
||||
- "'%db' in result.stdout"
|
||||
- "'SELECT' in result.stdout"
|
||||
|
||||
- name: change user access to database via wildcard
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
priv: '%db.*:INSERT'
|
||||
append_privs: yes
|
||||
password: '{{ user_password_1 }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: show grants access for user1 on multiple database
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_1 }}'@'localhost';"
|
||||
register: result
|
||||
|
||||
- name: assert grant access for user1 on multiple database
|
||||
assert:
|
||||
that:
|
||||
- "'%db' in result.stdout"
|
||||
- "'INSERT' in result.stdout"
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
# ============================================================
|
||||
# Update user password for a user.
|
||||
# Assert the user password is updated and old password can no longer be used.
|
||||
#
|
||||
#- include: user_password_update_test.yml
|
||||
|
||||
# ============================================================
|
||||
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege=SELECT current_append_privs=no
|
||||
|
||||
# ============================================================
|
||||
# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege=DROP current_append_privs=yes
|
||||
|
||||
# ============================================================
|
||||
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege='UPDATE,ALTER' current_append_privs=no
|
||||
|
||||
# ============================================================
|
||||
# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege='INSERT,DELETE' current_append_privs=yes
|
||||
|
||||
# Tests for the priv parameter with dict value (https://github.com/ansible/ansible/issues/57533)
|
||||
- include: test_priv_dict.yml
|
||||
|
||||
- import_tasks: issue-29511.yaml
|
||||
tags:
|
||||
- issue-29511
|
||||
|
||||
- import_tasks: issue-64560.yaml
|
||||
tags:
|
||||
- issue-64560
|
|
@ -1,59 +0,0 @@
|
|||
# test code to remove mysql user
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- name: remove mysql user {{user_name}}
|
||||
mysql_user:
|
||||
name: '{{user_name}}'
|
||||
password: '{{user_password}}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message mysql user was removed
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
# ============================================================
|
||||
- name: create blank mysql user to be removed later
|
||||
mysql_user:
|
||||
name: ""
|
||||
state: present
|
||||
password: 'KJFDY&D*Sfuydsgf'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: remove blank mysql user with hosts=all (expect changed)
|
||||
mysql_user:
|
||||
user: ""
|
||||
host_all: true
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert changed is true for removing all blank users
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: remove blank mysql user with hosts=all (expect ok)
|
||||
mysql_user:
|
||||
user: ""
|
||||
host_all: true
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert changed is true for removing all blank users
|
||||
assert: { that: "result.changed == false" }
|
|
@ -1,112 +0,0 @@
|
|||
# test code for resource_limits parameter
|
||||
|
||||
- block:
|
||||
|
||||
- name: Drop mysql user {{ user_name_1 }} if exists
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: Create mysql user {{ user_name_1 }} with resource limits in check_mode
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
resource_limits:
|
||||
MAX_QUERIES_PER_HOUR: 10
|
||||
MAX_CONNECTIONS_PER_HOUR: 5
|
||||
check_mode: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Create mysql user {{ user_name_1 }} with resource limits in actual mode
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
resource_limits:
|
||||
MAX_QUERIES_PER_HOUR: 10
|
||||
MAX_CONNECTIONS_PER_HOUR: 5
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Check
|
||||
mysql_query:
|
||||
query: >
|
||||
SELECT User FROM mysql.user WHERE User = '{{ user_name_1 }}' AND Host = 'localhost'
|
||||
AND max_questions = 10 AND max_connections = 5
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.rowcount[0] == 1
|
||||
|
||||
- name: Try to set the same limits again in check mode
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
resource_limits:
|
||||
MAX_QUERIES_PER_HOUR: 10
|
||||
MAX_CONNECTIONS_PER_HOUR: 5
|
||||
check_mode: yes
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Try to set the same limits again in actual mode
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
resource_limits:
|
||||
MAX_QUERIES_PER_HOUR: 10
|
||||
MAX_CONNECTIONS_PER_HOUR: 5
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- name: Change limits
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
resource_limits:
|
||||
MAX_QUERIES_PER_HOUR: 5
|
||||
MAX_CONNECTIONS_PER_HOUR: 5
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
|
||||
- name: Check
|
||||
mysql_query:
|
||||
query: >
|
||||
SELECT User FROM mysql.user WHERE User = '{{ user_name_1 }}' AND Host = 'localhost'
|
||||
AND max_questions = 5 AND max_connections = 5
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.rowcount[0] == 1
|
||||
|
||||
when: (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18') or (ansible_distribution == 'CentOS' and ansible_distribution_major_version >= '8')
|
|
@ -1,46 +0,0 @@
|
|||
# Tests for priv parameter value passed as a dict
|
||||
- name: Create test databases
|
||||
mysql_db:
|
||||
name: '{{ item }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
loop:
|
||||
- data1
|
||||
- data2
|
||||
|
||||
- name: Create user with privileges
|
||||
mysql_user:
|
||||
name: '{{ user_name_3 }}'
|
||||
password: '{{ user_password_3 }}'
|
||||
priv:
|
||||
"data1.*": "SELECT"
|
||||
"data2.*": "SELECT"
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: Run command to show privileges for user (expect privileges in stdout)
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_3 }}'@'localhost';"
|
||||
register: result
|
||||
|
||||
- name: Assert user has giving privileges
|
||||
assert:
|
||||
that:
|
||||
- "'GRANT SELECT ON `data1`.*' in result.stdout"
|
||||
- "'GRANT SELECT ON `data2`.*' in result.stdout"
|
||||
|
||||
##########
|
||||
# Clean up
|
||||
- name: Drop test databases
|
||||
mysql_db:
|
||||
name: '{{ item }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
loop:
|
||||
- data1
|
||||
- data2
|
||||
|
||||
- name: Drop test user
|
||||
mysql_user:
|
||||
name: '{{ user_name_3 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
|
@ -1,155 +0,0 @@
|
|||
# test code for privileges for mysql_user module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- name: create user with basic select privileges
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:SELECT'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
when: current_append_privs == "yes"
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv='SELECT'
|
||||
when: current_append_privs == "yes"
|
||||
|
||||
- name: create user with current privileges (expect changed=true)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:{{current_privilege}}'
|
||||
append_privs: '{{current_append_privs}}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message for current privileges
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: run command to show privileges for user (expect privileges in stdout)
|
||||
command: mysql "-e SHOW GRANTS FOR '{{user_name_2}}'@'localhost';"
|
||||
register: result
|
||||
|
||||
- name: assert user has correct privileges
|
||||
assert: { that: "'GRANT {{current_privilege | replace(',', ', ')}} ON *.*' in result.stdout" }
|
||||
when: current_append_privs == "no"
|
||||
|
||||
- name: assert user has correct privileges
|
||||
assert: { that: "'GRANT SELECT, {{current_privilege | replace(',', ', ')}} ON *.*' in result.stdout" }
|
||||
when: current_append_privs == "yes"
|
||||
|
||||
- name: create database using user current privileges
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
login_user: '{{ user_name_2 }}'
|
||||
login_password: '{{ user_password_2 }}'
|
||||
ignore_errors: true
|
||||
|
||||
- name: run command to test that database was not created
|
||||
command: mysql "-e show databases like '{{ db_name }}';"
|
||||
register: result
|
||||
|
||||
- name: assert database was not created
|
||||
assert: { that: "'{{ db_name }}' not in result.stdout" }
|
||||
|
||||
# ============================================================
|
||||
- name: Add privs to a specific table (expect changed)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: 'jmainguy.jmainguy:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert that priv changed
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: Add privs to a specific table (expect ok)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: 'jmainguy.jmainguy:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert that priv did not change
|
||||
assert: { that: "result.changed == false" }
|
||||
|
||||
# ============================================================
|
||||
- name: update user with all privileges
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'
|
||||
|
||||
- name: create database using user
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
login_user: '{{ user_name_2 }}'
|
||||
login_password: '{{ user_password_2 }}'
|
||||
|
||||
- name: run command to test database was created using user new privileges
|
||||
command: mysql "-e SHOW CREATE DATABASE {{ db_name }};"
|
||||
|
||||
- name: drop database using user
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: absent
|
||||
login_user: '{{ user_name_2 }}'
|
||||
login_password: '{{ user_password_2 }}'
|
||||
|
||||
# ============================================================
|
||||
- name: update user with a long privileges list (mysql has a special multiline grant output)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:CREATE USER,FILE,PROCESS,RELOAD,REPLICATION CLIENT,REPLICATION SLAVE,SHOW DATABASES,SHUTDOWN,SUPER,CREATE,DROP,EVENT,LOCK TABLES,INSERT,UPDATE,DELETE,SELECT,SHOW VIEW,GRANT'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert that priv changed
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: Test idempotency (expect ok)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:CREATE USER,FILE,PROCESS,RELOAD,REPLICATION CLIENT,REPLICATION SLAVE,SHOW DATABASES,SHUTDOWN,SUPER,CREATE,DROP,EVENT,LOCK TABLES,INSERT,UPDATE,DELETE,SELECT,SHOW VIEW,GRANT'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: Assert that priv did not change
|
||||
assert: { that: "result.changed == false" }
|
||||
|
||||
- name: remove username
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
|
@ -1,155 +0,0 @@
|
|||
# test code update password for the mysql_user module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 dof the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
# Update user password for a user.
|
||||
# Assert the user password is updated and old password can no longer be used.
|
||||
#
|
||||
- name: create user1 state=present with a password
|
||||
mysql_user:
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: create user2 state=present with a password
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: store user2 grants with old password (mysql 5.7.6 and newer)
|
||||
command: mysql "-e SHOW CREATE USER '{{ user_name_2 }}'@'localhost';"
|
||||
register: user_password_old_create
|
||||
ignore_errors: yes
|
||||
|
||||
- name: store user2 grants with old password (mysql 5.7.5 and older)
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';"
|
||||
register: user_password_old
|
||||
when: user_password_old_create is failed
|
||||
|
||||
- name: update user2 state=present with same password (expect changed=false)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- name: assert output user2 was not updated
|
||||
assert: { that: "result.changed == false" }
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'
|
||||
|
||||
- name: update user2 state=present with a new password (expect changed=true)
|
||||
mysql_user:
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: result
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'
|
||||
|
||||
- name: store user2 grants with old password (mysql 5.7.6 and newer)
|
||||
command: mysql "-e SHOW CREATE USER '{{ user_name_2 }}'@'localhost';"
|
||||
register: user_password_new_create
|
||||
ignore_errors: yes
|
||||
|
||||
- name: store user2 grants with new password
|
||||
command: mysql "-e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost';"
|
||||
register: user_password_new
|
||||
when: user_password_new_create is failed
|
||||
|
||||
- name: assert output message password was update for user2 (mysql 5.7.6 and newer)
|
||||
assert: { that: "user_password_old_create.stdout != user_password_new_create.stdout" }
|
||||
when: user_password_new_create is not failed
|
||||
|
||||
- name: assert output message password was update for user2 (mysql 5.7.5 and older)
|
||||
assert: { that: "user_password_old.stdout != user_password_new.stdout" }
|
||||
when: user_password_new_create is failed
|
||||
|
||||
- name: create database using user2 and old password
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
login_user: '{{ user_name_2 }}'
|
||||
login_password: '{{ user_password_2 }}'
|
||||
ignore_errors: true
|
||||
register: result
|
||||
|
||||
- debug: var=result.msg
|
||||
- name: assert output message that database not create with old password
|
||||
assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
|
||||
- name: create database using user2 and new password
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
login_user: '{{ user_name_2 }}'
|
||||
login_password: '{{ user_password_1 }}'
|
||||
register: result
|
||||
|
||||
- name: assert output message that database is created with new password
|
||||
assert: { that: "result.changed == true" }
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
name: '{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }}
|
||||
|
||||
- name: Create user with Fdt8fd^34ds using hash. (expect changed=true)
|
||||
mysql_user:
|
||||
name: jmainguy
|
||||
password: '*0cb5b86f23fdc24db19a29b8854eb860cbc47793'
|
||||
encrypted: yes
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: encrypt_result
|
||||
|
||||
- name: Check that the module made a change
|
||||
assert:
|
||||
that:
|
||||
- "encrypt_result.changed == True"
|
||||
|
||||
- name: See if the password needs to be updated. (expect changed=false)
|
||||
mysql_user:
|
||||
name: jmainguy
|
||||
password: 'Fdt8fd^34ds'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
register: plain_result
|
||||
|
||||
- name: Check that the module did not change the password
|
||||
assert:
|
||||
that:
|
||||
- "plain_result.changed == False"
|
||||
|
||||
- name: Remove user (cleanup)
|
||||
mysql_user:
|
||||
name: jmainguy
|
||||
state: absent
|
|
@ -1,6 +0,0 @@
|
|||
destructive
|
||||
shippable/posix/group3
|
||||
skip/aix
|
||||
skip/osx
|
||||
skip/freebsd
|
||||
skip/rhel
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
# defaults file for test_mysql_variables
|
||||
user: 'user1'
|
||||
password: 'khH&DYigjg1#'
|
|
@ -1,2 +0,0 @@
|
|||
dependencies:
|
||||
- setup_mysql8
|
|
@ -1,25 +0,0 @@
|
|||
# test code to assert message in mysql_variables module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
# Assert message failure and confirm failed=true
|
||||
#
|
||||
- name: assert message failure (expect failed=true)
|
||||
assert:
|
||||
that:
|
||||
- "output.failed == true"
|
|
@ -1,34 +0,0 @@
|
|||
# test code to assert variables in mysql_variables module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
# Assert mysql variable name and value from mysql database
|
||||
#
|
||||
- name: assert output message changed value
|
||||
assert: { that: "output.changed == {{changed}}" }
|
||||
|
||||
- name: run mysql command to show variable
|
||||
command: "mysql -uroot -p{{ root_pass }} \"-e show variables like '{{var_name}}';\""
|
||||
register: result
|
||||
|
||||
- name: assert output mysql variable name and value
|
||||
assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "'{{var_name}}' in result.stdout"
|
||||
- "'{{var_value}}' in result.stdout"
|
|
@ -1,38 +0,0 @@
|
|||
# test code to assert variables in mysql_variables module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
# Assert output variable name/value match mysql variable name/value
|
||||
#
|
||||
- name: assert output message changed value
|
||||
assert: { that: "output.changed == {{changed}}" }
|
||||
|
||||
- set_fact:
|
||||
key_name: "{{var_name}}"
|
||||
key_value: "{{output.msg[0][0]}}"
|
||||
|
||||
- name: run mysql command to show variable
|
||||
command: "mysql -uroot -p{{ root_pass }} \"-e show variables like '{{var_name}}';\""
|
||||
register: result
|
||||
|
||||
- name: assert output variable info match mysql variable info
|
||||
assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
- "key_name in result.stdout"
|
||||
- "key_value in result.stdout"
|
|
@ -1,2 +0,0 @@
|
|||
- import_tasks: mysql_variables.yml
|
||||
when: ansible_distribution == 'CentOS' and ansible_distribution_major_version >= '7'
|
|
@ -1,412 +0,0 @@
|
|||
# test code for the mysql_variables module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable successfully queries a variable
|
||||
#
|
||||
- set_fact: set_name='version'
|
||||
|
||||
- name: read mysql variables (expect changed=false)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
|
||||
- include: assert_var_output.yml changed=false output={{result}} var_name={{set_name}}
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable successfully updates a variable (issue:4568)
|
||||
#
|
||||
- set_fact:
|
||||
set_name: 'delay_key_write'
|
||||
set_value: 'ON'
|
||||
|
||||
- name: set mysql variable
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- name: update mysql variable to same value (expect changed=false)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
|
||||
- include: assert_var.yml changed=false output={{result}} var_name={{set_name}} var_value={{set_value}}
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable successfully updates a variable using single quotes
|
||||
#
|
||||
- set_fact:
|
||||
set_name: 'wait_timeout'
|
||||
set_value: '300'
|
||||
|
||||
- name: set mysql variable to a temp value
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '200'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
|
||||
- name: update mysql variable value (expect changed=true)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.queries == ["SET GLOBAL `{{ set_name }}` = {{ set_value }}"]
|
||||
|
||||
- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable successfully updates a variable using double quotes
|
||||
#
|
||||
- set_fact:
|
||||
set_name: "wait_timeout"
|
||||
set_value: "400"
|
||||
|
||||
- name: set mysql variable to a temp value
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: "200"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
|
||||
- name: update mysql variable value (expect changed=true)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
|
||||
- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable successfully updates a variable using no quotes
|
||||
#
|
||||
- set_fact:
|
||||
set_name: wait_timeout
|
||||
set_value: 500
|
||||
|
||||
- name: set mysql variable to a temp value
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: 200
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
|
||||
- name: update mysql variable value (expect changed=true)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
|
||||
- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable successfully updates a variable using an expression (e.g. 1024*4)
|
||||
#
|
||||
- name: set mysql variable value to an expression
|
||||
mysql_variables:
|
||||
variable: max_tmp_tables
|
||||
value: "1024*4"
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='Incorrect argument type to variable'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable fails when setting an incorrect value (out of range)
|
||||
#
|
||||
- name: set mysql variable value to a number out of range
|
||||
mysql_variables:
|
||||
variable: max_tmp_tables
|
||||
value: -1
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='Truncated incorrect'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable fails when setting an incorrect value (incorrect type)
|
||||
#
|
||||
- name: set mysql variable value to a non-valid value number
|
||||
mysql_variables:
|
||||
variable: max_tmp_tables
|
||||
value: TEST
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='Incorrect argument type to variable'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable fails when setting an unknown variable
|
||||
#
|
||||
- name: set a non mysql variable
|
||||
mysql_variables:
|
||||
variable: my_sql_variable
|
||||
value: ON
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='Variable not available'
|
||||
|
||||
# ============================================================
|
||||
# Verify mysql_variable fails when setting a read-only variable
|
||||
#
|
||||
- name: set value of a read only mysql variable
|
||||
mysql_variables:
|
||||
variable: character_set_system
|
||||
value: utf16
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='read only variable'
|
||||
|
||||
#=============================================================
|
||||
# Verify mysql_variable works with the login_user and login_password parameters
|
||||
#
|
||||
- name: create mysql user
|
||||
mysql_user:
|
||||
name: '{{user}}'
|
||||
password: '{{password}}'
|
||||
state: present
|
||||
priv: '*.*:ALL'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
|
||||
- set_fact:
|
||||
set_name: wait_timeout
|
||||
set_value: 77
|
||||
|
||||
- name: query mysql_variable using login_user and password_password
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
login_user: '{{user}}'
|
||||
login_password: '{{password}}'
|
||||
register: result
|
||||
|
||||
- include: assert_var_output.yml changed=false output={{result}} var_name={{set_name}}
|
||||
|
||||
- name: set mysql variable to temp value using user login and password (expect changed=true)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: 20
|
||||
login_user: '{{user}}'
|
||||
login_password: '{{password}}'
|
||||
register: result
|
||||
|
||||
- name: update mysql variable value using user login and password (expect changed=true)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_user: '{{user}}'
|
||||
login_password: '{{password}}'
|
||||
register: result
|
||||
|
||||
- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}'
|
||||
|
||||
#============================================================
|
||||
# Verify mysql_variable fails with an incorrect login_password parameter
|
||||
#
|
||||
- set_fact:
|
||||
set_name: connect_timeout
|
||||
set_value: 10
|
||||
|
||||
- name: query mysql_variable using incorrect login_password
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
login_user: '{{user}}'
|
||||
login_password: 'wrongpassword'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='unable to connect to database'
|
||||
|
||||
- name: update mysql variable value using incorrect login_password (expect failed=true)
|
||||
mysql_variables:
|
||||
variable: '{{set_name}}'
|
||||
value: '{{set_value}}'
|
||||
login_user: '{{user}}'
|
||||
login_password: 'this is an incorrect password'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='unable to connect to database'
|
||||
|
||||
#============================================================
|
||||
# Verify mysql_variable fails with an incorrect login_host parameter
|
||||
#
|
||||
- name: query mysql_variable using incorrect login_host
|
||||
mysql_variables:
|
||||
variable: wait_timeout
|
||||
login_host: '12.0.0.9'
|
||||
login_user: '{{user}}'
|
||||
login_password: '{{password}}'
|
||||
connect_timeout: 5
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
- include: assert_fail_msg.yml output={{result}} msg='unable to connect to database'
|
||||
|
||||
- name: remove mysql_user {{user}}
|
||||
mysql_user:
|
||||
name: '{{user}}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
|
||||
#=========================================
|
||||
# Check mode 'persist' and 'persist_only':
|
||||
#
|
||||
- name: update mysql variable value (expect changed=true) in persist mode
|
||||
mysql_variables:
|
||||
variable: '{{ set_name }}'
|
||||
value: '{{ set_value }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
mode: persist
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result.queries == ["SET PERSIST `{{ set_name }}` = {{ set_value }}"]
|
||||
|
||||
- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{set_value}}'
|
||||
|
||||
- name: try to update mysql variable value (expect changed=false) in persist mode again
|
||||
mysql_variables:
|
||||
variable: '{{ set_name }}'
|
||||
value: '{{ set_value }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
mode: persist
|
||||
register: result
|
||||
|
||||
- include: assert_var.yml changed=false output={{result}} var_name={{set_name}} var_value='{{set_value}}'
|
||||
|
||||
- name: set mysql variable to a temp value
|
||||
mysql_variables:
|
||||
variable: '{{ set_name }}'
|
||||
value: '200'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
mode: persist
|
||||
|
||||
- name: update mysql variable value (expect changed=true) in persist_only mode
|
||||
mysql_variables:
|
||||
variable: '{{ set_name }}'
|
||||
value: '{{ set_value }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
mode: persist_only
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["SET PERSIST_ONLY `{{ set_name }}` = {{ set_value }}"]
|
||||
|
||||
- name: try to update mysql variable value (expect changed=false) in persist_only mode again
|
||||
mysql_variables:
|
||||
variable: '{{ set_name }}'
|
||||
value: '{{ set_value }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
mode: persist_only
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is not changed
|
||||
|
||||
- set_fact:
|
||||
set_name: max_connections
|
||||
set_value: 105
|
||||
def_val: 151
|
||||
|
||||
- name: update mysql variable value (expect changed=true) in persist_only mode
|
||||
mysql_variables:
|
||||
variable: '{{ set_name }}'
|
||||
value: '{{ set_value }}'
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
mode: persist_only
|
||||
register: result
|
||||
|
||||
- include: assert_var.yml changed=true output={{result}} var_name={{set_name}} var_value='{{def_val}}'
|
||||
|
||||
# Bugfix of https://github.com/ansible/ansible/issues/54239
|
||||
- name: set variable containing dot
|
||||
mysql_variables:
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
login_user: root
|
||||
login_password: '{{ root_pass }}'
|
||||
variable: validate_password.policy
|
||||
value: LOW
|
||||
mode: persist_only
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- result is changed
|
||||
- result.queries == ["SET PERSIST_ONLY `validate_password`.`policy` = LOW"]
|
|
@ -1,21 +0,0 @@
|
|||
repo_link: https://repo.mysql.com/mysql80-community-release-el{{ ansible_facts.distribution_major_version }}.rpm
|
||||
my_cnf: /etc/my.cnf
|
||||
|
||||
mysql_data_dirs:
|
||||
- /var/lib/mysql
|
||||
- /usr/mysql
|
||||
|
||||
mysql_support_packages:
|
||||
- MySQL-python
|
||||
|
||||
mysql_server_packages:
|
||||
- mysql-community-server
|
||||
|
||||
mysql_cleanup_packages:
|
||||
- mysql-community-client
|
||||
- mysql-community-common
|
||||
- mysql-community-libs
|
||||
- mysql-community-libs-compat
|
||||
- mysql-community-server
|
||||
- mysql80-community-release
|
||||
- python3-PyMySQL
|
|
@ -1,7 +0,0 @@
|
|||
[mysqld]
|
||||
datadir=/var/lib/mysql
|
||||
socket=/var/lib/mysql/mysql.sock
|
||||
log-error=/var/log/mysqld.log
|
||||
pid-file=/var/run/mysqld/mysqld.pid
|
||||
default_authentication_plugin=mysql_native_password
|
||||
skip-grant-tables
|
|
@ -1,29 +0,0 @@
|
|||
- name: stop mysql service
|
||||
service:
|
||||
name: mysqld
|
||||
state: stopped
|
||||
listen: cleanup mysql8
|
||||
|
||||
- name: remove repo
|
||||
# yum:
|
||||
# name: mysql80-community-release
|
||||
# state: absent
|
||||
# Work around for a bug in the dnf module. Use the module once that gets fixed.
|
||||
# https://github.com/ansible/ansible/issues/64294
|
||||
command: "{{ ansible_facts.pkg_mgr}} -y erase mysql80-community-release"
|
||||
args:
|
||||
warn: no
|
||||
listen: cleanup mysql8
|
||||
|
||||
- name: remove mysql packages
|
||||
yum:
|
||||
name: '{{ mysql_support_packages | union(mysql_server_packages) | union(mysql_cleanup_packages) }}'
|
||||
state: absent
|
||||
listen: cleanup mysql8
|
||||
|
||||
- name: remove mysql data
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ mysql_data_dirs }}"
|
||||
listen: cleanup mysql8
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# Setup MySQL 8:
|
||||
- name: Include distribution specific variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- '{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml'
|
||||
- '{{ ansible_facts.os_family }}.yml'
|
||||
- 'default.yml'
|
||||
paths: vars
|
||||
|
||||
- import_tasks: setup_mysql8.yml
|
||||
when:
|
||||
- ansible_facts.distribution == 'CentOS'
|
||||
- ansible_facts.distribution_major_version is version_compare('7', '>=')
|
|
@ -1,71 +0,0 @@
|
|||
# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
- name: Install PyMySQL package via pip
|
||||
pip:
|
||||
name: PyMySQL
|
||||
state: present
|
||||
|
||||
- name: Install MySQL repo
|
||||
yum:
|
||||
name: '{{ repo_link }}'
|
||||
notify: cleanup mysql8
|
||||
|
||||
# These packages come from AppStream in RHEL 8, so they need to be done in a separate task
|
||||
- name: Install MySQL support packages
|
||||
yum:
|
||||
name: "{{ mysql_support_packages }}"
|
||||
notify: cleanup mysql8
|
||||
|
||||
- name: Install MySQL community server
|
||||
yum:
|
||||
name: '{{ mysql_server_packages }}'
|
||||
disablerepo: '{{ mysql_disablerepo | default(omit) }}'
|
||||
notify: cleanup mysql8
|
||||
|
||||
- name: Copy my.cnf
|
||||
copy:
|
||||
src: my.cnf
|
||||
dest: '{{ my_cnf }}'
|
||||
|
||||
- name: Start MySQL
|
||||
service:
|
||||
name: mysqld
|
||||
state: started
|
||||
|
||||
### Debug #######################
|
||||
#- name: Debug
|
||||
# shell: cat /var/log/mysqld.log
|
||||
#################################
|
||||
|
||||
- name: Check connection to the server
|
||||
shell: 'echo "SHOW DATABASES;" | mysql'
|
||||
|
||||
- name: Check connection to the server
|
||||
shell: "echo \"SHOW VARIABLES LIKE '%version%';\" | mysql"
|
||||
|
||||
- name: Detect socket path
|
||||
shell: 'echo "show variables like ''socket''\G" | mysql | grep ''Value: '' | sed ''s/[ ]\+Value: //'''
|
||||
register: _socket_path
|
||||
|
||||
- name: Set socket path
|
||||
set_fact:
|
||||
mysql_socket: '{{ _socket_path["stdout"] }}'
|
||||
|
||||
- name: Set root pass
|
||||
set_fact:
|
||||
root_pass: "dlsafjlkjdsaK1#"
|
||||
|
||||
- name: Set root password
|
||||
shell: 'echo "flush privileges; ALTER USER ''root''@''localhost'' IDENTIFIED WITH mysql_native_password BY ''{{ root_pass }}'';" | mysql'
|
||||
|
||||
- name: Change configuration
|
||||
lineinfile:
|
||||
path: '{{ my_cnf }}'
|
||||
line: skip-grant-tables
|
||||
state: absent
|
||||
|
||||
- name: Restart MySQL
|
||||
service:
|
||||
name: mysqld
|
||||
state: restarted
|
|
@ -1,4 +0,0 @@
|
|||
mysql_support_packages:
|
||||
- python3-PyMySQL
|
||||
|
||||
mysql_disablerepo: AppStream
|
|
@ -1,3 +0,0 @@
|
|||
mysql_data_dirs:
|
||||
- /var/lib/mysql
|
||||
- /usr/share/mysql
|
|
@ -1,4 +0,0 @@
|
|||
mysql_support_packages:
|
||||
- python3-PyMySQL
|
||||
|
||||
mysql_disablerepo: rhel-8-for-x86_64-appstream-rpms
|
|
@ -1,18 +0,0 @@
|
|||
mysql_service: mysqld
|
||||
|
||||
mysql_packages:
|
||||
- mysql-server
|
||||
- MySQL-python
|
||||
- bzip2
|
||||
|
||||
mysql_cleanup_packages:
|
||||
- mysql-community-client
|
||||
- mysql-community-common
|
||||
- mysql-community-libs
|
||||
- mysql-community-libs-compat
|
||||
- mysql-community-server
|
||||
- mysql80-community-release
|
||||
|
||||
mysql_data_dirs:
|
||||
- /var/lib/mysql
|
||||
- /usr/mysql
|
|
@ -1,25 +0,0 @@
|
|||
- name: stop mysql service
|
||||
service:
|
||||
name: "{{ mysql_service }}"
|
||||
state: stopped
|
||||
listen: cleanup mysql
|
||||
|
||||
- name: remove mysql packages
|
||||
action: '{{ ansible_facts.pkg_mgr }}'
|
||||
args:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ mysql_packages | union(mysql_cleanup_packages) }}"
|
||||
listen: cleanup mysql
|
||||
|
||||
- name: remove mysql data
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop: "{{ mysql_data_dirs }}"
|
||||
listen: cleanup mysql
|
||||
|
||||
- name: remove pip packages
|
||||
pip:
|
||||
name: mysql-python
|
||||
state: absent
|
|
@ -1,2 +0,0 @@
|
|||
dependencies:
|
||||
- setup_pkg_mgr
|
|
@ -1,112 +0,0 @@
|
|||
# setup code for the mysql_db module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# ============================================================
|
||||
- name: python 2
|
||||
set_fact:
|
||||
python_suffix: ""
|
||||
when: ansible_python_version is version('3', '<')
|
||||
|
||||
- name: python 3
|
||||
set_fact:
|
||||
python_suffix: "-py3"
|
||||
when: ansible_python_version is version('3', '>=')
|
||||
|
||||
- name: Include distribution specific variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- '{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}{{ python_suffix }}.yml'
|
||||
- '{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml'
|
||||
- '{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}{{ python_suffix }}.yml'
|
||||
- '{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml'
|
||||
- '{{ ansible_facts.distribution }}{{ python_suffix }}.yml'
|
||||
- '{{ ansible_facts.os_family }}{{ python_suffix }}.yml'
|
||||
- 'default{{ python_suffix }}.yml'
|
||||
paths: "{{ role_path }}/vars"
|
||||
|
||||
- name: install mysqldb_test rpm dependencies
|
||||
yum:
|
||||
name: "{{ mysql_packages }}"
|
||||
state: latest
|
||||
when: ansible_facts.pkg_mgr == 'yum'
|
||||
notify: cleanup mysql
|
||||
|
||||
- name: install mysqldb_test rpm dependencies
|
||||
dnf:
|
||||
name: '{{ mysql_packages }}'
|
||||
state: latest
|
||||
install_weak_deps: False # mariadb-server has a weak dep on python2 which break Python 3 test environments
|
||||
when: ansible_facts.pkg_mgr == 'dnf'
|
||||
notify: cleanup mysql
|
||||
|
||||
- name: install mysqldb_test debian dependencies
|
||||
apt:
|
||||
name: "{{ mysql_packages }}"
|
||||
state: latest
|
||||
when: ansible_facts.pkg_mgr == 'apt'
|
||||
notify: cleanup mysql
|
||||
|
||||
- name: install mysqldb_test opensuse dependencies
|
||||
zypper:
|
||||
name: "{{ mysql_packages }}"
|
||||
state: latest
|
||||
when: ansible_facts.pkg_mgr in ['zypper', 'community.general.zypper']
|
||||
notify: cleanup mysql
|
||||
|
||||
- name: install mysqldb_test FreeBSD dependencies
|
||||
package:
|
||||
name: "{{ mysql_packages }}"
|
||||
state: present
|
||||
when: ansible_os_family == "FreeBSD"
|
||||
notify: cleanup mysql
|
||||
|
||||
- name: install mysql-python package via pip (FreeBSD)
|
||||
pip:
|
||||
name: mysql-python
|
||||
state: present
|
||||
when: ansible_os_family == "FreeBSD"
|
||||
notify:
|
||||
- cleanup mysql
|
||||
- remove pip packages
|
||||
|
||||
- name: enable mysql-server service (FreeBSD)
|
||||
lineinfile:
|
||||
path: /etc/rc.conf
|
||||
line: 'mysql_server_enable="YES"'
|
||||
when: ansible_os_family == "FreeBSD"
|
||||
|
||||
- name: apply work-around for OverlayFS issue
|
||||
# https://github.com/docker/for-linux/issues/72#issuecomment-319904698
|
||||
command: find {{ mysql_data_dirs[0] }} -type f -exec touch {} ;
|
||||
# find will fail if mysql has never been started, as the directory won't exist
|
||||
ignore_errors: yes
|
||||
|
||||
- name: restart mysql_db service
|
||||
service:
|
||||
name: "{{ mysql_service }}"
|
||||
state: restarted
|
||||
|
||||
- name: Detect socket path
|
||||
shell: 'echo "show variables like ''socket''\G" | mysql | grep ''Value: '' | sed ''s/[ ]\+Value: //'''
|
||||
register: _socket_path
|
||||
|
||||
- name: Set socket path
|
||||
set_fact:
|
||||
mysql_socket: '{{ _socket_path["stdout"] }}'
|
|
@ -1,16 +0,0 @@
|
|||
mysql_service: 'mysql'
|
||||
|
||||
mysql_packages:
|
||||
- mysql-server
|
||||
- python-mysqldb
|
||||
- bzip2
|
||||
|
||||
mysql_cleanup_packages:
|
||||
- mysql-client*
|
||||
- mysql-server*
|
||||
- mysql-common
|
||||
- mysql-sandbox
|
||||
|
||||
mysql_data_dirs:
|
||||
- /var/lib/mysql
|
||||
- /usr/share/mysql
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mariadb'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb-server
|
||||
- python3-PyMySQL
|
||||
- bzip2
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mariadb'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb-server
|
||||
- python-PyMySQL
|
||||
- bzip2
|
|
@ -1,5 +0,0 @@
|
|||
mysql_service: 'mysql-server'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb101-server
|
||||
- py-pymysql
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mariadb'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb-server
|
||||
- MySQL-python
|
||||
- bzip2
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mariadb'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb-server
|
||||
- python3-PyMySQL
|
||||
- bzip2
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mysqld'
|
||||
|
||||
mysql_packages:
|
||||
- mysql-server
|
||||
- MySQL-python
|
||||
- bzip2
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mysql'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb
|
||||
- python3-PyMySQL
|
||||
- bzip2
|
|
@ -1,6 +0,0 @@
|
|||
mysql_service: 'mysql'
|
||||
|
||||
mysql_packages:
|
||||
- mariadb
|
||||
- python-PyMySQL
|
||||
- bzip2
|
|
@ -1,16 +0,0 @@
|
|||
mysql_service: 'mysql'
|
||||
|
||||
mysql_packages:
|
||||
- mysql-server
|
||||
- python3-pymysql
|
||||
- bzip2
|
||||
|
||||
mysql_cleanup_packages:
|
||||
- mysql-client*
|
||||
- mysql-server*
|
||||
- mysql-common
|
||||
- mysql-sandbox
|
||||
|
||||
mysql_data_dirs:
|
||||
- /var/lib/mysql
|
||||
- /usr/share/mysql
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue