summaryrefslogtreecommitdiffstats
path: root/collections-debian-merged/ansible_collections/community/mysql
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-14 20:03:01 +0000
commita453ac31f3428614cceb99027f8efbdb9258a40b (patch)
treef61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/community/mysql
parentInitial commit. (diff)
downloadansible-upstream.tar.xz
ansible-upstream.zip
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/community/mysql')
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-plugins.yml145
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-roles.yml56
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/.gitignore135
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/COPYING674
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/FILES.json1118
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/MANIFEST.json32
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/README.md54
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/changelogs/CHANGELOG.rst179
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/changelogs/changelog.yaml211
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/changelogs/config.yaml29
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/codecov.yml2
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/meta/runtime.yml2
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/README.md31
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/doc_fragments/mysql.py97
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/database.py189
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/mysql.py148
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_db.py720
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_info.py545
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_query.py253
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_replication.py594
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_user.py1151
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_variables.py268
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/aliases7
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/defaults/main.yml7
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/meta/main.yml3
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/main.yml21
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml173
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml118
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml96
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml13
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/handlers/main.yml6
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/config.yml15
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/dir.yml11
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/install.yml59
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/main.yml11
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/setvars.yml28
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/verify.yml27
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/templates/installed_file.j21
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/vars/main.yml31
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml5
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml5
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml11
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml15
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/defaults/main.yml17
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/meta/main.yml2
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/config_overrides_defaults.yml108
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/encoding_dump_import.yml98
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/issue-28.yml81
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/main.yml326
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/multi_db_create_delete.yml626
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml459
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/defaults/main.yml11
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/meta/main.yml3
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/issue-28.yml78
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/main.yml193
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/templates/my.cnf.j25
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/defaults/main.yml13
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/meta/main.yml2
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/issue-28.yml78
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/main.yml9
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml301
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/defaults/main.yml17
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/meta/main.yml3
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/issue-28.yml79
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/main.yml21
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml127
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml243
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_master_delay.yml45
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetmaster_mode.yml56
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/defaults/main.yml26
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-function.sql8
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-procedure.sql5
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/meta/main.yml3
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_no_user.yml25
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_user.yml38
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/create_user.yml40
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-28.yml86
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-29511.yaml86
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-64560.yaml45
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/main.yml282
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/remove_user.yml74
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/resource_limits.yml118
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_append.yml114
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_dict.yml55
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_privs.yml186
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_password.yml269
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_plugin_auth.yml386
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/tls_requirements.yml195
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/defaults/main.yml8
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/meta/main.yml2
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_fail_msg.yml25
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var.yml36
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var_output.yml40
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/issue-28.yml79
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/main.yml8
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/mysql_variables.yml379
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.10.txt8
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.11.txt8
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.9.txt3
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/test_mysql.py24
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/__init__.py0
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_replication.py32
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_user.py33
-rw-r--r--collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/utils.py19
105 files changed, 13042 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-plugins.yml b/collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-plugins.yml
new file mode 100644
index 00000000..cd27b107
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-plugins.yml
@@ -0,0 +1,145 @@
+name: Plugins CI
+on:
+ push:
+ paths:
+ - 'plugins/**'
+ - 'tests/**'
+ - '.github/workflows/ansible-test-plugins.yml'
+ pull_request:
+ paths:
+ - 'plugins/**'
+ - 'tests/**'
+ - '.github/workflows/ansible-test-plugins.yml'
+ schedule:
+ - cron: '0 6 * * *'
+
+
+env:
+ mysql_version_file: "./ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml"
+ connector_version_file: "./ansible_collections/community/mysql/tests/integration/targets/setup_mysql/vars/main.yml"
+
+jobs:
+ sanity:
+ name: "Sanity (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }})"
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ ansible:
+ - stable-2.9
+ - stable-2.10
+ - devel
+ steps:
+
+ - name: Check out code
+ uses: actions/checkout@v2
+ with:
+ path: ansible_collections/community/mysql
+
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+
+ - name: Install ansible-base (${{ matrix.ansible }})
+ run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check
+
+ - name: Run sanity tests
+ run: ansible-test sanity --docker -v --color
+ working-directory: ./ansible_collections/community/mysql
+
+ integration:
+ name: "Integration (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, MySQL: ${{ matrix.mysql }}, Connector: ${{ matrix.connector }})"
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ mysql:
+ - 5.7.31
+ - 8.0.22
+ ansible:
+ - stable-2.9
+ - stable-2.10
+ - devel
+ python:
+ - 3.6
+ connector:
+ - pymysql==0.7.10
+ - pymysql==0.9.3
+ - mysqlclient==2.0.1
+ exclude:
+ - mysql: 8.0.22
+ connector: pymysql==0.7.10
+ steps:
+
+ - name: Check out code
+ uses: actions/checkout@v2
+ with:
+ path: ansible_collections/community/mysql
+
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+
+ - name: Install ansible-base (${{ matrix.ansible }})
+ run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check
+
+ - name: Set MySQL version (${{ matrix.mysql }})
+ run: "sed -i 's/^mysql_version:.*/mysql_version: \"${{ matrix.mysql }}\"/g' ${{ env.mysql_version_file }}"
+
+ - name: Set Connector version (${{ matrix.connector }})
+ run: "sed -i 's/^python_packages:.*/python_packages: [${{ matrix.connector }}]/' ${{ env.connector_version_file }}"
+
+ - name: Run integration tests
+ run: ansible-test integration --docker -v --color --retry-on-error --continue-on-error --python ${{ matrix.python }} --diff --coverage
+ working-directory: ./ansible_collections/community/mysql
+
+ - name: Generate coverage report.
+ run: ansible-test coverage xml -v --requirements --group-by command --group-by version
+ working-directory: ./ansible_collections/community/mysql
+
+ - uses: codecov/codecov-action@v1
+ with:
+ fail_ci_if_error: false
+
+ units:
+ runs-on: ubuntu-latest
+ name: Units (â’¶${{ matrix.ansible }})
+ strategy:
+ # As soon as the first unit test fails,
+ # cancel the others to free up the CI queue
+ fail-fast: true
+ matrix:
+ ansible:
+ - stable-2.9
+ - stable-2.10
+ - devel
+
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v2
+ with:
+ path: ./ansible_collections/community/mysql
+
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+
+ - name: Install ansible-base (${{matrix.ansible}})
+ run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check
+
+ # Run the unit tests
+ - name: Run unit test
+ run: ansible-test units -v --color --docker --coverage
+ working-directory: ./ansible_collections/community/mysql
+
+ # ansible-test support producing code coverage date
+ - name: Generate coverage report
+ run: ansible-test coverage xml -v --requirements --group-by command --group-by version
+ working-directory: ./ansible_collections/community/mysql
+
+ # See the reports at https://codecov.io/gh/GITHUBORG/REPONAME
+ - uses: codecov/codecov-action@v1
+ with:
+ fail_ci_if_error: false
diff --git a/collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-roles.yml b/collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-roles.yml
new file mode 100644
index 00000000..0bc32f6c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/.github/workflows/ansible-test-roles.yml
@@ -0,0 +1,56 @@
+name: Roles CI
+on:
+ push:
+ paths:
+ - 'roles/**'
+ - '.github/workflows/ansible-test-roles.yml'
+ pull_request:
+ paths:
+ - 'roles/**'
+ - '.github/workflows/ansible-test-roles.yml'
+ schedule:
+ - cron: '0 6 * * *'
+
+jobs:
+ molecule:
+ name: "Molecule (Python: ${{ matrix.python }}, Ansible: ${{ matrix.ansible }}, MySQL: ${{ matrix.mysql }})"
+ runs-on: ubuntu-latest
+ env:
+ PY_COLORS: 1
+ ANSIBLE_FORCE_COLOR: 1
+ strategy:
+ matrix:
+ mysql:
+ - 2.0.12
+ ansible:
+ - stable-2.9
+ ### it looks like there's errors for 2.10+ with ansible-lint (https://github.com/ansible/ansible-lint/pull/878)
+ ### and molecule (_maybe_ relating to https://github.com/ansible-community/molecule/pull/2547)
+ # - stable-2.10
+ # - devel
+ python:
+ - 2.7
+ - 3.8
+
+ steps:
+
+ - name: Check out code
+ uses: actions/checkout@v2
+ with:
+ path: ansible_collections/community/mysql
+
+ - name: Set up Python ${{ matrix.python }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python }}
+
+ - name: Install ansible-base (${{ matrix.ansible }})
+ run: pip install https://github.com/ansible/ansible/archive/${{ matrix.ansible }}.tar.gz --disable-pip-version-check
+
+ - name: Install molecule and related dependencies
+ run: |
+ pip install ansible-lint docker flake8 molecule testinfra yamllint
+
+ # - name: Run molecule default test scenario
+ # run: for d in roles/*/; do (cd "$d" && molecule --version && molecule test) done
+ # working-directory: ./ansible_collections/community/mysql
diff --git a/collections-debian-merged/ansible_collections/community/mysql/.gitignore b/collections-debian-merged/ansible_collections/community/mysql/.gitignore
new file mode 100644
index 00000000..f4407229
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/.gitignore
@@ -0,0 +1,135 @@
+/tests/output/
+/changelogs/.plugin-cache.yaml
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# MacOS
+.DS_Store
diff --git a/collections-debian-merged/ansible_collections/community/mysql/COPYING b/collections-debian-merged/ansible_collections/community/mysql/COPYING
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program 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.
+
+ This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/collections-debian-merged/ansible_collections/community/mysql/FILES.json b/collections-debian-merged/ansible_collections/community/mysql/FILES.json
new file mode 100644
index 00000000..50a541e1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/FILES.json
@@ -0,0 +1,1118 @@
+{
+ "files": [
+ {
+ "name": ".",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".gitignore",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4173598eb51b77c32e7d2b31d13e97ebfd353c4037b1d771c17d740727c97b92",
+ "format": 1
+ },
+ {
+ "name": ".github",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".github/workflows",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": ".github/workflows/ansible-test-plugins.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7ee6bec0a191ba7d9e34f2617d817c36dfa5145c83f0e2bcc3904e4d7213e0e2",
+ "format": 1
+ },
+ {
+ "name": ".github/workflows/ansible-test-roles.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "cac5855d222e18d6d9719e095605418d872d8d219427c9144d538b202c60d5f9",
+ "format": 1
+ },
+ {
+ "name": "meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "meta/runtime.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "df18179bb2f5447a56ac92261a911649b96821c0b2c08eea62d5cc6b0195203f",
+ "format": 1
+ },
+ {
+ "name": "codecov.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "74ef69719758a63944e959504d5396e442ed023c1c589ed987d9fc4676096d53",
+ "format": 1
+ },
+ {
+ "name": "plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/mysql.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "12ee683d4c29d91a5c417bf150b4a365e9576a34b9e1155d4c39ddc9e653db21",
+ "format": 1
+ },
+ {
+ "name": "plugins/module_utils/database.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bf286e7fb228799c46b66d7e34bacb613838f65d8faa42efd2a1f29ee6759c1b",
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/doc_fragments/mysql.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2b229a7470c782933b1821d207ef1604d6e19257d68dc55085b262bf07f45d67",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mysql_query.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "692a41e039e78a2f1cdd04d74ffc420e198984a8de9a1ffd70178de5ff9ed34f",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mysql_db.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dd4e11f7c08a921d4e213d74332ef2538fcb7ca7093e16d13faeb7d2e77fb333",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mysql_variables.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c3bb6c5f18937a765c08112019e0af75e7feb9ea0e70d7dce105cfdfd3d2465e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mysql_replication.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a95c1a5b10f6f7d46756e0864f0b3a7e4680dca4776522e7370a8254244d514e",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mysql_user.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dba5eb7d4c3356ec61154f873fa2e6fe7392b9180513e36d438b5ed2a54162af",
+ "format": 1
+ },
+ {
+ "name": "plugins/modules/mysql_info.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1a292c3492c42c7020dc80c4bc09db0b3c018a70c25c8244e83c7306d5559c27",
+ "format": 1
+ },
+ {
+ "name": "plugins/README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "533dcaafc018b89297cf72098c36ea97dd2352a3f734fb8cc7568af8818fd7f1",
+ "format": 1
+ },
+ {
+ "name": "tests",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/sanity",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.10.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "26cf6bdacb30652f6105a97ed3695fbfcdce39966654c8f1179427f5ccd10267",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.9.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "563f5bc68b05205c5545785c81b9245355ed09596c5ab5f01dcc92fd3a54c876",
+ "format": 1
+ },
+ {
+ "name": "tests/sanity/ignore-2.11.txt",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "26cf6bdacb30652f6105a97ed3695fbfcdce39966654c8f1179427f5ccd10267",
+ "format": 1
+ },
+ {
+ "name": "tests/integration",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/templates/my.cnf.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "28ce9e82a1512ce76200e2953ccfaf069f01a7ec915afbeec8c10a677c4f68d8",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7134f696d30f85b5e664425adf33a405686fbd05e00b6bfc4e74445298d692c8",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "960ad89a5857c865b2b26151b82b0df4bb877ee24faea2cde9fa2ff612cd7604",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7cf1f6c0a2f5ce3d8071f4811e14a1c661c39564cf69df83bf9bd394a8bb5538",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_info/tasks/issue-28.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "052b4bf6ed05482ae3de02bb4b191d6bba441905bba24d0349718e504171a71b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "60880581b8217fe6a3e8f1c2c3db334efb3fda0358eec8a70aa12375fdbda23b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "eec8d6142beb55aa9cb81e0c777e81a1150e77f9e7362147274def8a08a62721",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks/assert_var.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c5e415bc7d84bb9473dd92fdf55978e039ed6f1663f930514f87a7badd921c46",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d1bb5e0c23980c724051ddcd1bd3e69f5b3fec2ad10b6c6530f691fed676d574",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks/assert_var_output.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "5d2e6e2cc11027c3c50e8125a71fcffaf6a42a6a4569fea3f8983f7652a24af6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks/assert_fail_msg.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e5f806fc413f2a63986657c7b07a6bb6fe016da206d5eccf8fe1d217ce2fff6e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks/issue-28.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f7e9ce89d1ff9dcc901eb088745dcb022be866d2f8907562a7688d176e08ee98",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_variables/tasks/mysql_variables.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "63a70ef9cf2f4fe7f27cabc5d084ed742d0f05b5ffc2e66a05a17753568309ef",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/files",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/files/create-function.sql",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f9ba9df87bc460be46fb77cabaceef9f93a73474d30e07d3d90648909d3c63f1",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/files/create-procedure.sql",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8386a2c5a55abf33d19484765d8354947773a81141e92434f8e736f766e6932d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "7134f696d30f85b5e664425adf33a405686fbd05e00b6bfc4e74445298d692c8",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2f06b510846ba09c579c74790cb1a3bbf767eb6393377f2c7650fe2e97b751b9",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "985efddbd9c391eb914b28622d10a8d09cced51f78ebfc31984b6de202aa9fca",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/test_user_plugin_auth.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "af6476045ffe9c84a6177bd151c2e72fccf102cc5f6dace48dc8ff9680aae650",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/resource_limits.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3d1c15602321921d2cbed6fef60895586c4e0d343bc9f972aec623c41ccc95b8",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/remove_user.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "65323965a44c6ad75ff6eefa19a8e44f316c4f9a7b106f2d6cf423f3ef0e45a7",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/assert_user.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8f9d7cee387e3305757dd0dda7726418683f8e5e2fe95c501b96471e38007b42",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/create_user.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "408781add05c8da3c25a32f8b448d681fe8cc298756da23cb844dd3f570ff187",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/test_priv_dict.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d359516ae1db55bd7eba631e6648bdd42d63f743730f2a0c03efe69ef459c0b0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/issue-28.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "47c0add7c5f604913ea7f2825be546e30456c34a67c9d80e0c72c1570f5c1740",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/test_privs.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2fd72e53d7b4f4462259df2a6dbfaa094e7b73c939eb6f86845fa2bf94836698",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/issue-64560.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f64b1fce4343b6241116a1eb047cc32a47bfa568f1ffe74bf6134c624854adb0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/assert_no_user.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a58e6271928c420c2152b96ee8d2e633ad0014603b53397217a8154234d1b479",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/issue-29511.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d8217bdeda7811c559d7b307e132c57f9c71c2f216750f90665785bd19484e7c",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/test_priv_append.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "6113d89dc494855c495dd64032fc26c7b56351d18ac2fefc8555f52b5b8eeffb",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/tls_requirements.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a2828bf065501f398d13a8cbb6b444e36e026dd56a89df6adf4962cb5c733806",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_user/tasks/test_user_password.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4a96934cdae644523768cd6ada405b915399e037d81561ba6aa53b39f757fdee",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir/handlers",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "050157a29c48915cf220b3cdcf5a032e53e359bdc4a210cd457c4836e8e32a4d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bcb3221a68dc87c7eae0c7ea50c0c0e81380932bf2e21a3dfdee1acc2266c3f3",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2441ac1753320d2cd3bea299c160540e6ae31739ed235923ca478284d1fcfe09",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e273324ab90d72180a971d99b9ab69f08689c8be2e6adb991154fc294cf1056e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "60880581b8217fe6a3e8f1c2c3db334efb3fda0358eec8a70aa12375fdbda23b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0d071894d4222537457256b7e182297d5320673c8a2d0d861d76cc4744f3a3ba",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "bfa2b6892f4d9ebc688c8402bae5ca8874f25443405e48d096e83e2680e1b583",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "df6493282bf3f755ab68064c8b2274358abaf9d34488c6fdd0635c24fcb77436",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks/issue-28.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ccf8129fb5e1f862f7ff6895879be19965d7d30bdabdd9cd60f5073f695f3f4e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks/multi_db_create_delete.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "a763b489d5505a93b8077b4b3f5c6485441559b284fc2f90716feb739ad9e324",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks/config_overrides_defaults.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fbfb028039d91608ce298dea1d2db4dd51b34d602a8a2b879ed20376e031fad4",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_db/tasks/encoding_dump_import.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d1df019e7552691341672c9f5a9197d8f31c0ddf295521b22176e1bfb4429ab3",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "34abefbbfb4c1805c1b0cbc9e1d6f12e5d1ad95bf54fe594e3c09b4d3ede586d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "78c7018a77755f45b1c3c3910607fcf85bf9101e6a2ef8258b367e7a511d9cb2",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0e4d3adf69babe324829102178fedda78da7b81b307155bddace17abe71e990d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks/mysql_replication_master_delay.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f9b32d2ff5a44d247d142c8ef07e32923f0ab6ea98754a6df89dddd767096ead",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks/issue-28.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "92fe48b5a3432a675c2261f4069010aa3f8c474d888aeac6155b19afa7bbf583",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "0f7c61173ec459b7c51fcd574a4c705ac55f7ba911e2e07384e29bd5fc22f293",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetmaster_mode.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "108b216f31d36ce17de9de4b8406ec0a01826f2823f24c3283d82b361701a8e8",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "126a7e473ea923a8ae45b09090111530489111fcfe01101dcb17a8145e717513",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/templates",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/templates/installed_file.j2",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "838bee046d60c7cf9707aba8f8f33eaa4962aa4798b9d9cb5b66d7648dcf790d",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/vars",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/vars/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "010d577b5077e75b93a758e5684a541bdc812b5d292f0448974da65bc1fa7f06",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "09ee5a41655ddad9ac0b6eb81fd27eabadd2e0ec2c68a50c1d33fa818a7306e0",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/handlers",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/handlers/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "d2d84831a7ceff91296440763b396692baf3ce3d0027cf1884d205dac31b36d6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f72b32f08568947efa72b811dfec154b66a6cdf0e5bb169b02d0d902f4a0f3c6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks/config.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2cc75e51ab0dd44f14b269cedd9f70df61d41ba8968342ed2f4868fcd892f5b8",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks/install.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2acc35c5783ca591aac07e375758a43238130d4260127f72f41af3dee244ff0b",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks/verify.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "c49c298c50f85b99525cc6cfed8445594afa6e05c5009a00136838d7a46597f9",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks/setvars.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4c6f650681985cb8d366ce865ec0b682a3a38a4162c67cfb6a4bf8cbba852c7f",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/setup_mysql/tasks/dir.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ebeb28b1ad217cb860646d17484f788091c33634365f59f6016f92742345b1b6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "12b1878a7556f7ef5ee2146014c54d1e0a490ba6a8889c426377246522d5ce97",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "00072ee6cf98e8246594d9455e6d0714fc4d21b145ec00dd52a5fbe226c3903f",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "325ea1e1dc136098d51af19b3aa194a5d5082e5426f7dd421fcc12661e60fdb6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "dde0a80e078e1a878250f27a22df8a1802faa48edfcf5f812a1dc3eda83bf33f",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/targets/test_mysql_query/tasks/issue-28.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1d2c5af3bd669045ae6e292a7fc338b96ef7cbcb48fd8b98739f9b7436109a8e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/meta",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/meta/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "4dd18f7c3640db3c083acd4bccd974f5ee92ab73485be0b71fd3431feefcd0e3",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/defaults",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/defaults/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "615fbd5ffed3471edd2f8f6417532a4f25ccfb973a3c1f00e89a955ceee1f8b6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/tasks",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/tasks/main.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "44e2227e478af9f735498ec9805b1a1076885e4159dcaa21be440115948f0221",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "f3b513a57566d276388c2613fa9e1f94066d7ea657f87bad8d25c3abb2fb0cf6",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "fab156b92a4f740f1032b629c2c02726b716d570e6e306f36e3a7d904b702c83",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "033d2e2921c996279846f6d60a91335a8df55ebce7a1ca0428ad33029f21869e",
+ "format": 1
+ },
+ {
+ "name": "tests/integration/old_mariadb_replication/aliases",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "8598fbccb4b6eaa7150d066f699e7bd937338c9de6cabbe1d630c7b9cf057ca4",
+ "format": 1
+ },
+ {
+ "name": "tests/unit",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/module_utils/test_mysql.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "20d9ead509f38c46b09e931439e186ce1b678f9ca4ca9cc3242c725c20d05f57",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/utils.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ba7638619ea90f4ac4f0f0a7639db544f4c4ca8c224bb1ccb4f7fd9cee953d66",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_mysql_user.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "73c4ae75827ac173f8452a864776299aa731cf433a13d2351905780c1006436c",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/test_mysql_replication.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "091a1941b905c6047c2ac8067fabdb386f810840a25829dbca0d4c53bf11a343",
+ "format": 1
+ },
+ {
+ "name": "tests/unit/plugins/modules/__init__.py",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ "format": 1
+ },
+ {
+ "name": "changelogs",
+ "ftype": "dir",
+ "chksum_type": null,
+ "chksum_sha256": null,
+ "format": 1
+ },
+ {
+ "name": "changelogs/CHANGELOG.rst",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3899e95f900720abaad6d3311400193716b3cde93ade77dbebc1cd386f122f8c",
+ "format": 1
+ },
+ {
+ "name": "changelogs/config.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "ecadf8e8a36a265fea678ebfaed9ea226d805d9c40e1076f47bf2251c439a123",
+ "format": 1
+ },
+ {
+ "name": "changelogs/changelog.yaml",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "2d396684c7fa227f10efb6a1745820e64e9635a40bfea2e3a6e3d930bd4029a4",
+ "format": 1
+ },
+ {
+ "name": "COPYING",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "3972dc9744f6499f0f9b2dbf76696f2ae7ad8af9b23dde66d6af86c9dfb36986",
+ "format": 1
+ },
+ {
+ "name": "README.md",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "1518261d9b56867ca7e98060bd12ee0bd636df68539569e9d6de901416bc8773",
+ "format": 1
+ }
+ ],
+ "format": 1
+} \ No newline at end of file
diff --git a/collections-debian-merged/ansible_collections/community/mysql/MANIFEST.json b/collections-debian-merged/ansible_collections/community/mysql/MANIFEST.json
new file mode 100644
index 00000000..a978f332
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/MANIFEST.json
@@ -0,0 +1,32 @@
+{
+ "collection_info": {
+ "namespace": "community",
+ "name": "mysql",
+ "version": "1.2.0",
+ "authors": [
+ "MySQL Working Group (https://github.com/ansible-collections/community.mysql/wiki/MySQL-Working-Group)"
+ ],
+ "readme": "README.md",
+ "tags": [
+ "database",
+ "mysql",
+ "mariadb"
+ ],
+ "description": "MySQL collection for Ansible",
+ "license": [],
+ "license_file": "COPYING",
+ "dependencies": {},
+ "repository": "https://github.com/ansible-collections/community.mysql",
+ "documentation": "https://github.com/ansible-collections/community.mysql",
+ "homepage": "https://github.com/ansible-collections/community.mysql",
+ "issues": "https://github.com/ansible-collections/community.mysql/issues"
+ },
+ "file_manifest_file": {
+ "name": "FILES.json",
+ "ftype": "file",
+ "chksum_type": "sha256",
+ "chksum_sha256": "10670665fed7c2ccd73a3261420b18eeae119eb81883141750ee00f319f339b1",
+ "format": 1
+ },
+ "format": 1
+} \ No newline at end of file
diff --git a/collections-debian-merged/ansible_collections/community/mysql/README.md b/collections-debian-merged/ansible_collections/community/mysql/README.md
new file mode 100644
index 00000000..3ca1b0f9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/README.md
@@ -0,0 +1,54 @@
+# MySQL collection for Ansible
+[![Plugins CI](https://github.com/ansible-collections/community.mysql/workflows/Plugins%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Plugins+CI") [![Roles CI](https://github.com/ansible-collections/community.mysql/workflows/Roles%20CI/badge.svg?event=push)](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Roles+CI") [![Codecov](https://img.shields.io/codecov/c/github/ansible-collections/community.mysql)](https://codecov.io/gh/ansible-collections/community.mysql)
+
+## Included content
+
+- **Modules**:
+ - [mysql_db](https://docs.ansible.com/ansible/latest/modules/mysql_db_module.html)
+ - [mysql_info](https://docs.ansible.com/ansible/latest/modules/mysql_info_module.html)
+ - [mysql_query](https://docs.ansible.com/ansible/latest/modules/mysql_query_module.html)
+ - [mysql_replication](https://docs.ansible.com/ansible/latest/modules/mysql_replication_module.html)
+ - [mysql_user](https://docs.ansible.com/ansible/latest/modules/mysql_user_module.html)
+ - [mysql_variables](https://docs.ansible.com/ansible/latest/modules/mysql_variables_module.html)
+
+## Tested with Ansible
+
+- 2.9
+- 2.10
+- devel
+
+## External requirements
+
+The MySQL modules rely on a MySQL connector. The list of supported drivers is below:
+
+- [PyMySQL](https://github.com/PyMySQL/PyMySQL)
+- [MySQLdb](https://github.com/PyMySQL/mysqlclient-python)
+- Support for other Python MySQL connectors may be added in a future release.
+
+## Using this collection
+
+### Installing the Collection from Ansible Galaxy
+
+Before using the MySQL collection, you need to install it with the Ansible Galaxy CLI:
+
+```bash
+ansible-galaxy collection install community.mysql
+```
+
+You can also include it in a `requirements.yml` file and install it via `ansible-galaxy collection install -r requirements.yml`, using the format:
+
+```yaml
+---
+collections:
+ - name: community.mysql
+```
+
+See [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html) for more details.
+
+## Licensing
+
+<!-- Include the appropriate license information here and a pointer to the full licensing details. If the collection contains modules migrated from the ansible/ansible repo, you must use the same license that existed in the ansible/ansible repo. See the GNU license example below. -->
+
+GNU General Public License v3.0 or later.
+
+See [LICENSE](https://www.gnu.org/licenses/gpl-3.0.txt) to see the full text.
diff --git a/collections-debian-merged/ansible_collections/community/mysql/changelogs/CHANGELOG.rst b/collections-debian-merged/ansible_collections/community/mysql/changelogs/CHANGELOG.rst
new file mode 100644
index 00000000..aad2202a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/changelogs/CHANGELOG.rst
@@ -0,0 +1,179 @@
+========================================
+Community MySQL Collection Release Notes
+========================================
+
+.. contents:: Topics
+
+
+v1.2.0
+======
+
+Release Summary
+---------------
+
+This is the minor release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection
+that have been added after the release of ``community.mysql`` 1.1.2.
+
+Minor Changes
+-------------
+
+- mysql_user - refactor to reduce cursor.execute() calls in preparation for adding query logging (https://github.com/ansible-collections/community.mysql/pull/76).
+
+Bugfixes
+--------
+
+- mysql_user - add ``SHOW_ROUTINE`` privilege support (https://github.com/ansible-collections/community.mysql/issues/86).
+- mysql_user - fixed creating user with encrypted password in MySQL 8.0 (https://github.com/ansible-collections/community.mysql/pull/79).
+
+v1.1.2
+======
+
+Release Summary
+---------------
+
+This is the patch release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection that
+have been added after the release of ``community.mysql`` 1.1.1.
+
+Minor Changes
+-------------
+
+- mysql_query - simple refactoring of query type check (https://github.com/ansible-collections/community.mysql/pull/58).
+- mysql_user - simple refactoring of priv type check (https://github.com/ansible-collections/community.mysql/pull/58).
+
+Bugfixes
+--------
+
+- mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
+- mysql_replication - fix crashes of mariadb >= 10.5.1 and mysql >= 8.0.22 caused by using deprecated terminology (https://github.com/ansible-collections/community.mysql/issues/70).
+- mysql_user - fixed change detection when using append_privs (https://github.com/ansible-collections/community.mysql/pull/72).
+
+v1.1.1
+======
+
+Release Summary
+---------------
+
+This is the patch release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection that
+have been added after the release of ``community.mysql`` 1.1.0.
+
+
+Bugfixes
+--------
+
+- mysql_query - fix failing when single-row query contains commas (https://github.com/ansible-collections/community.mysql/issues/51).
+
+v1.1.0
+======
+
+Release Summary
+---------------
+
+This is the minor release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 1.0.2.
+
+
+Minor Changes
+-------------
+
+- mysql modules - add the ``check_hostname`` option (https://github.com/ansible-collections/community.mysql/issues/28).
+- mysql modules - patch the ``Connection`` class to add a destructor that ensures connections to the server are explicitly closed (https://github.com/ansible-collections/community.mysql/pull/44).
+
+Bugfixes
+--------
+
+- mysql modules - fix crash when ``!includedir`` option is in config file (https://github.com/ansible-collections/community.mysql/issues/46).
+
+v1.0.2
+======
+
+Release Summary
+---------------
+
+This is the patch release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 1.0.1.
+
+
+Bugfixes
+--------
+
+- mysql_user - fix module's crash when modifying a user with ``host_all`` (https://github.com/ansible-collections/community.mysql/issues/39).
+
+v1.0.1
+======
+
+Release Summary
+---------------
+
+This is the patch release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection that have been added after the release of ``community.mysql`` 1.0.0.
+
+
+Bugfixes
+--------
+
+- mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
+- mysql_user - added tests to verify that TLS requirements are removed with an empty ``tls_requires`` option (https://github.com/ansible-collections/community.mysql/issues/20).
+- mysql_user - correct procedure to check existing TLS requirements (https://github.com/ansible-collections/community.mysql/pull/26).
+- mysql_user - minor syntax fixes (https://github.com/ansible-collections/community.mysql/pull/26).
+
+v1.0.0
+======
+
+Release Summary
+---------------
+
+This is the first proper release of the ``community.mysql`` collection.
+This changelog contains all changes to the modules in this collection that were added after the release of Ansible 2.9.0.
+
+
+Minor Changes
+-------------
+
+- mysql_db - add ``master_data`` parameter (https://github.com/ansible/ansible/pull/66048).
+- mysql_db - add ``skip_lock_tables`` option (https://github.com/ansible/ansible/pull/66688).
+- mysql_db - add the ``check_implicit_admin`` parameter (https://github.com/ansible/ansible/issues/24418).
+- mysql_db - add the ``dump_extra_args`` parameter (https://github.com/ansible/ansible/pull/67747).
+- mysql_db - add the ``executed_commands`` returned value (https://github.com/ansible/ansible/pull/65498).
+- mysql_db - add the ``force`` parameter (https://github.com/ansible/ansible/pull/65547).
+- mysql_db - add the ``restrict_config_file`` parameter (https://github.com/ansible/ansible/issues/34488).
+- mysql_db - add the ``unsafe_login_password`` parameter (https://github.com/ansible/ansible/issues/63955).
+- mysql_db - add the ``use_shell`` parameter (https://github.com/ansible/ansible/issues/20196).
+- mysql_info - add ``exclude_fields`` parameter (https://github.com/ansible/ansible/issues/63319).
+- mysql_info - add ``global_status`` filter parameter option and return (https://github.com/ansible/ansible/pull/63189).
+- mysql_info - add ``return_empty_dbs`` parameter to list empty databases (https://github.com/ansible/ansible/issues/65727).
+- mysql_replication - add ``channel`` parameter (https://github.com/ansible/ansible/issues/29311).
+- mysql_replication - add ``connection_name`` parameter (https://github.com/ansible/ansible/issues/46243).
+- mysql_replication - add ``fail_on_error`` parameter (https://github.com/ansible/ansible/pull/66252).
+- mysql_replication - add ``master_delay`` parameter (https://github.com/ansible/ansible/issues/51326).
+- mysql_replication - add ``master_use_gtid`` parameter (https://github.com/ansible/ansible/pull/62648).
+- mysql_replication - add ``queries`` return value (https://github.com/ansible/ansible/pull/63036).
+- mysql_replication - add support of ``resetmaster`` choice to ``mode`` parameter (https://github.com/ansible/ansible/issues/42870).
+- mysql_user - ``priv`` parameter can be string or dictionary (https://github.com/ansible/ansible/issues/57533).
+- mysql_user - add TLS REQUIRES parameters (https://github.com/ansible-collections/community.mysql/pull/9).
+- mysql_user - add ``plugin_auth_string`` parameter (https://github.com/ansible/ansible/pull/44267).
+- mysql_user - add ``plugin_hash_string`` parameter (https://github.com/ansible/ansible/pull/44267).
+- mysql_user - add ``plugin`` parameter (https://github.com/ansible/ansible/pull/44267).
+- mysql_user - add the resource_limits parameter (https://github.com/ansible-collections/community.general/issues/133).
+- mysql_variables - add ``mode`` parameter (https://github.com/ansible/ansible/issues/60119).
+
+Bugfixes
+--------
+
+- mysql - dont mask ``mysql_connect`` function errors from modules (https://github.com/ansible/ansible/issues/64560).
+- mysql_db - fix Broken pipe error appearance when state is import and the target file is compressed (https://github.com/ansible/ansible/issues/20196).
+- mysql_db - fix bug in the ``db_import`` function introduced by https://github.com/ansible/ansible/pull/56721 (https://github.com/ansible/ansible/issues/65351).
+- mysql_info - add parameter for __collect to get only what are wanted (https://github.com/ansible-collections/community.general/pull/136).
+- mysql_replication - allow to pass empty values to parameters (https://github.com/ansible/ansible/issues/23976).
+- mysql_user - Fix idempotence when long grant lists are used (https://github.com/ansible/ansible/issues/68044)
+- mysql_user - Remove false positive ``no_log`` warning for ``update_password`` option
+- mysql_user - add ``INVOKE LAMBDA`` privilege support (https://github.com/ansible-collections/community.general/issues/283).
+- mysql_user - add missed privileges to support (https://github.com/ansible-collections/community.general/issues/617).
+- mysql_user - fix ``host_all`` arguments conversion string formatting error (https://github.com/ansible/ansible/issues/29644).
+- mysql_user - fix overriding password to the same (https://github.com/ansible-collections/community.general/issues/543).
+- mysql_user - fix support privileges with underscore (https://github.com/ansible/ansible/issues/66974).
+- mysql_user - fix the error No database selected (https://github.com/ansible/ansible/issues/68070).
+- mysql_user - make sure current_pass_hash is a string before using it in comparison (https://github.com/ansible/ansible/issues/60567).
+- mysql_variable - fix the module doesn't support variables name with dot (https://github.com/ansible/ansible/issues/54239).
diff --git a/collections-debian-merged/ansible_collections/community/mysql/changelogs/changelog.yaml b/collections-debian-merged/ansible_collections/community/mysql/changelogs/changelog.yaml
new file mode 100644
index 00000000..426fef59
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/changelogs/changelog.yaml
@@ -0,0 +1,211 @@
+ancestor: null
+releases:
+ 1.0.0:
+ changes:
+ bugfixes:
+ - mysql - dont mask ``mysql_connect`` function errors from modules (https://github.com/ansible/ansible/issues/64560).
+ - mysql_db - fix Broken pipe error appearance when state is import and the target
+ file is compressed (https://github.com/ansible/ansible/issues/20196).
+ - mysql_db - fix bug in the ``db_import`` function introduced by https://github.com/ansible/ansible/pull/56721
+ (https://github.com/ansible/ansible/issues/65351).
+ - mysql_info - add parameter for __collect to get only what are wanted (https://github.com/ansible-collections/community.general/pull/136).
+ - mysql_replication - allow to pass empty values to parameters (https://github.com/ansible/ansible/issues/23976).
+ - mysql_user - Fix idempotence when long grant lists are used (https://github.com/ansible/ansible/issues/68044)
+ - mysql_user - Remove false positive ``no_log`` warning for ``update_password``
+ option
+ - mysql_user - add ``INVOKE LAMBDA`` privilege support (https://github.com/ansible-collections/community.general/issues/283).
+ - mysql_user - add missed privileges to support (https://github.com/ansible-collections/community.general/issues/617).
+ - mysql_user - fix ``host_all`` arguments conversion string formatting error
+ (https://github.com/ansible/ansible/issues/29644).
+ - mysql_user - fix overriding password to the same (https://github.com/ansible-collections/community.general/issues/543).
+ - mysql_user - fix support privileges with underscore (https://github.com/ansible/ansible/issues/66974).
+ - mysql_user - fix the error No database selected (https://github.com/ansible/ansible/issues/68070).
+ - mysql_user - make sure current_pass_hash is a string before using it in comparison
+ (https://github.com/ansible/ansible/issues/60567).
+ - mysql_variable - fix the module doesn't support variables name with dot (https://github.com/ansible/ansible/issues/54239).
+ minor_changes:
+ - mysql_db - add ``master_data`` parameter (https://github.com/ansible/ansible/pull/66048).
+ - mysql_db - add ``skip_lock_tables`` option (https://github.com/ansible/ansible/pull/66688).
+ - mysql_db - add the ``check_implicit_admin`` parameter (https://github.com/ansible/ansible/issues/24418).
+ - mysql_db - add the ``dump_extra_args`` parameter (https://github.com/ansible/ansible/pull/67747).
+ - mysql_db - add the ``executed_commands`` returned value (https://github.com/ansible/ansible/pull/65498).
+ - mysql_db - add the ``force`` parameter (https://github.com/ansible/ansible/pull/65547).
+ - mysql_db - add the ``restrict_config_file`` parameter (https://github.com/ansible/ansible/issues/34488).
+ - mysql_db - add the ``unsafe_login_password`` parameter (https://github.com/ansible/ansible/issues/63955).
+ - mysql_db - add the ``use_shell`` parameter (https://github.com/ansible/ansible/issues/20196).
+ - mysql_info - add ``exclude_fields`` parameter (https://github.com/ansible/ansible/issues/63319).
+ - mysql_info - add ``global_status`` filter parameter option and return (https://github.com/ansible/ansible/pull/63189).
+ - mysql_info - add ``return_empty_dbs`` parameter to list empty databases (https://github.com/ansible/ansible/issues/65727).
+ - mysql_replication - add ``channel`` parameter (https://github.com/ansible/ansible/issues/29311).
+ - mysql_replication - add ``connection_name`` parameter (https://github.com/ansible/ansible/issues/46243).
+ - mysql_replication - add ``fail_on_error`` parameter (https://github.com/ansible/ansible/pull/66252).
+ - mysql_replication - add ``master_delay`` parameter (https://github.com/ansible/ansible/issues/51326).
+ - mysql_replication - add ``master_use_gtid`` parameter (https://github.com/ansible/ansible/pull/62648).
+ - mysql_replication - add ``queries`` return value (https://github.com/ansible/ansible/pull/63036).
+ - mysql_replication - add support of ``resetmaster`` choice to ``mode`` parameter
+ (https://github.com/ansible/ansible/issues/42870).
+ - mysql_user - ``priv`` parameter can be string or dictionary (https://github.com/ansible/ansible/issues/57533).
+ - mysql_user - add TLS REQUIRES parameters (https://github.com/ansible-collections/community.mysql/pull/9).
+ - mysql_user - add ``plugin_auth_string`` parameter (https://github.com/ansible/ansible/pull/44267).
+ - mysql_user - add ``plugin_hash_string`` parameter (https://github.com/ansible/ansible/pull/44267).
+ - mysql_user - add ``plugin`` parameter (https://github.com/ansible/ansible/pull/44267).
+ - mysql_user - add the resource_limits parameter (https://github.com/ansible-collections/community.general/issues/133).
+ - mysql_variables - add ``mode`` parameter (https://github.com/ansible/ansible/issues/60119).
+ release_summary: 'This is the first proper release of the ``community.mysql``
+ collection.
+
+ This changelog contains all changes to the modules in this collection that
+ were added after the release of Ansible 2.9.0.
+
+ '
+ fragments:
+ - 1.0.0.yml
+ - 142-mysql_user_add_resource_limit_parameter.yml
+ - 151-mysql_db_add_use_shell_parameter.yml
+ - 18-mysql_user-update_password-no_log.yml
+ - 225-mysql_user_fix_no_database_selected.yml
+ - 285-mysql_user_invoke_lambda_support.yml
+ - 369-mysql_user_add_tls_requires.yml
+ - 428-mysql_db_add_unsafe_login_password_param.yml
+ - 468-mysql_db_add_restrict_config_file_param.yml
+ - 486-mysql_db_add_check_implicit_admin_parameter.yml
+ - 490-mysql_user_fix_cursor_errors.yml
+ - 609-mysql_user_fix_overriding_password_to_the_same.yml
+ - 618-mysql_user_add_missed_privileges.yml
+ - 62648-mysql_replication_add_master_use_gtid_param.yml
+ - 63036-mysql_replication_add_return_value.yml
+ - 63130-mysql_replication_add_master_delay_parameter.yml
+ - 63189-mysql_info-global-status.yml
+ - 63229-mysql_replication_add_connection_name_parameter.yml
+ - 63271-mysql_replication_add_channel_parameter.yml
+ - 63321-mysql_replication_add_resetmaster_to_mode.yml
+ - 63371-mysql_info_add_exclude_fields_parameter.yml
+ - 63546-mysql_replication_allow_to_pass_empty_values.yml
+ - 63547-mysql_variables_add_mode_param.yml
+ - 64059-mysql_user_fix_password_comparison.yaml
+ - 64585-mysql_dont_mask_mysql_connect_errors_from_modules.yml
+ - 65498-mysql_db_add_executed_commands_return_val.yml
+ - 65547-mysql_db_add_force_param.yml
+ - 65755-mysql_info_doesnt_list_empty_dbs.yml
+ - 65789-mysql_user_add_plugin_authentication_parameters.yml
+ - 66048-mysql_add_master_data_parameter.yml
+ - 66252-mysql_replication_fail_on_error.yml
+ - 66688-mysql_db_add_skip_lock_tables_option.yml
+ - 66801-mysql_user_priv_can_be_dict.yml
+ - 66806-mysql_variables_not_support_variables_with_dot.yml
+ - 66974-mysql_user_doesnt_support_privs_with_underscore.yml
+ - 67337-fix-proxysql-mysql-cursor.yaml
+ - 67747-mysql_db_add_dump_extra_args_param.yml
+ - 67767-mysql_db_fix_bug_introduced_by_56721.yml
+ - mysql_info_add_parameter.yml
+ - mysql_user_idempotency.yml
+ release_date: '2020-08-17'
+ 1.0.1:
+ changes:
+ bugfixes:
+ - mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
+ - mysql_user - added tests to verify that TLS requirements are removed with
+ an empty ``tls_requires`` option (https://github.com/ansible-collections/community.mysql/issues/20).
+ - mysql_user - correct procedure to check existing TLS requirements (https://github.com/ansible-collections/community.mysql/pull/26).
+ - mysql_user - minor syntax fixes (https://github.com/ansible-collections/community.mysql/pull/26).
+ release_summary: 'This is the patch release of the ``community.mysql`` collection.
+
+ This changelog contains all changes to the modules in this collection that
+ have been added after the release of ``community.mysql`` 1.0.0.
+
+ '
+ fragments:
+ - 1.0.1.yml
+ - 26-remove_tls_requirements.yml
+ - 34-mysql_db_fix_false_warning.yml
+ release_date: '2020-09-29'
+ 1.0.2:
+ changes:
+ bugfixes:
+ - mysql_user - fix module's crash when modifying a user with ``host_all`` (https://github.com/ansible-collections/community.mysql/issues/39).
+ release_summary: 'This is the patch release of the ``community.mysql`` collection.
+
+ This changelog contains all changes to the modules in this collection that
+ have been added after the release of ``community.mysql`` 1.0.1.
+
+ '
+ fragments:
+ - 1.0.2.yml
+ - 40-mysql_user_fix_error_when_host_all_used.yml
+ release_date: '2020-10-01'
+ 1.1.0:
+ changes:
+ bugfixes:
+ - mysql modules - fix crash when ``!includedir`` option is in config file (https://github.com/ansible-collections/community.mysql/issues/46).
+ minor_changes:
+ - mysql modules - add the ``check_hostname`` option (https://github.com/ansible-collections/community.mysql/issues/28).
+ - mysql modules - patch the ``Connection`` class to add a destructor that ensures
+ connections to the server are explicitly closed (https://github.com/ansible-collections/community.mysql/pull/44).
+ release_summary: 'This is the minor release of the ``community.mysql`` collection.
+
+ This changelog contains all changes to the modules in this collection that
+ have been added after the release of ``community.mysql`` 1.0.2.
+
+ '
+ fragments:
+ - 1.1.0.yml
+ - 35-disable-hostname-check.yml
+ - 44-close-connection.yml
+ - 47-mysql_modules_fix_failings_when_include_dir_in_config_file.yml
+ release_date: '2020-10-13'
+ 1.1.1:
+ changes:
+ bugfixes:
+ - mysql_query - fix failing when single-row query contains commas (https://github.com/ansible-collections/community.mysql/issues/51).
+ release_summary: 'This is the patch release of the ``community.mysql`` collection.
+
+ This changelog contains all changes to the modules in this collection that
+
+ have been added after the release of ``community.mysql`` 1.1.0.
+
+ '
+ fragments:
+ - 1.1.1.yml
+ - 53-mysql_query_fix_single_query_with_commas.yml
+ release_date: '2020-11-03'
+ 1.1.2:
+ changes:
+ bugfixes:
+ - mysql_db - fix false warning related to ``unsafe_login_password`` option (https://github.com/ansible-collections/community.mysql/issues/33).
+ - mysql_replication - fix crashes of mariadb >= 10.5.1 and mysql >= 8.0.22 caused
+ by using deprecated terminology (https://github.com/ansible-collections/community.mysql/issues/70).
+ - mysql_user - fixed change detection when using append_privs (https://github.com/ansible-collections/community.mysql/pull/72).
+ minor_changes:
+ - mysql_query - simple refactoring of query type check (https://github.com/ansible-collections/community.mysql/pull/58).
+ - mysql_user - simple refactoring of priv type check (https://github.com/ansible-collections/community.mysql/pull/58).
+ release_summary: 'This is the patch release of the ``community.mysql`` collection.
+
+ This changelog contains all changes to the modules in this collection that
+
+ have been added after the release of ``community.mysql`` 1.1.1.'
+ fragments:
+ - 1.1.2.yml
+ - 58-mysql_query_refactoring.yml
+ - 71-mysql_replication_add_replica_keyword_support.yml
+ - 72-mysql_db_fix_false_warning.yml
+ - 72-mysql_user_change_detection.yml
+ release_date: '2020-12-18'
+ 1.2.0:
+ changes:
+ bugfixes:
+ - mysql_user - add ``SHOW_ROUTINE`` privilege support (https://github.com/ansible-collections/community.mysql/issues/86).
+ - mysql_user - fixed creating user with encrypted password in MySQL 8.0 (https://github.com/ansible-collections/community.mysql/pull/79).
+ minor_changes:
+ - mysql_user - refactor to reduce cursor.execute() calls in preparation for
+ adding query logging (https://github.com/ansible-collections/community.mysql/pull/76).
+ release_summary: 'This is the minor release of the ``community.mysql`` collection.
+
+ This changelog contains all changes to the modules in this collection
+
+ that have been added after the release of ``community.mysql`` 1.1.2.'
+ fragments:
+ - 1.2.0.yml
+ - 76-mysql-user-query-refact.yaml
+ - 79-mysql-user-tests-and-fixes.yml
+ - 87-mysql_user_show_routine_support.yml
+ release_date: '2021-01-18'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/changelogs/config.yaml b/collections-debian-merged/ansible_collections/community/mysql/changelogs/config.yaml
new file mode 100644
index 00000000..559e6c4e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/changelogs/config.yaml
@@ -0,0 +1,29 @@
+changelog_filename_template: CHANGELOG.rst
+changelog_filename_version_depth: 0
+changes_file: changelog.yaml
+changes_format: combined
+keep_fragments: false
+mention_ancestor: true
+new_plugins_after_name: removed_features
+notesdir: fragments
+prelude_section_name: release_summary
+prelude_section_title: Release Summary
+sections:
+- - major_changes
+ - Major Changes
+- - minor_changes
+ - Minor Changes
+- - breaking_changes
+ - Breaking Changes / Porting Guide
+- - deprecated_features
+ - Deprecated Features
+- - removed_features
+ - Removed Features (previously deprecated)
+- - security_fixes
+ - Security Fixes
+- - bugfixes
+ - Bugfixes
+- - known_issues
+ - Known Issues
+title: Community MySQL Collection
+trivial_section_name: trivial
diff --git a/collections-debian-merged/ansible_collections/community/mysql/codecov.yml b/collections-debian-merged/ansible_collections/community/mysql/codecov.yml
new file mode 100644
index 00000000..e832c21f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/codecov.yml
@@ -0,0 +1,2 @@
+fixes:
+ - "/ansible_collections/community/mysql/::"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/meta/runtime.yml b/collections-debian-merged/ansible_collections/community/mysql/meta/runtime.yml
new file mode 100644
index 00000000..2ee3c9fa
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/meta/runtime.yml
@@ -0,0 +1,2 @@
+---
+requires_ansible: '>=2.9.10'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/README.md b/collections-debian-merged/ansible_collections/community/mysql/plugins/README.md
new file mode 100644
index 00000000..5b4711b5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/README.md
@@ -0,0 +1,31 @@
+# Collections Plugins Directory
+
+This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that
+is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that
+would contain module utils and modules respectively.
+
+Here is an example directory of the majority of plugins currently supported by Ansible:
+
+```
+└── plugins
+ ├── action
+ ├── become
+ ├── cache
+ ├── callback
+ ├── cliconf
+ ├── connection
+ ├── filter
+ ├── httpapi
+ ├── inventory
+ ├── lookup
+ ├── module_utils
+ ├── modules
+ ├── netconf
+ ├── shell
+ ├── strategy
+ ├── terminal
+ ├── test
+ └── vars
+```
+
+A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/latest/plugins/plugins.html).
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/doc_fragments/mysql.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/doc_fragments/mysql.py
new file mode 100644
index 00000000..a9c3a991
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/doc_fragments/mysql.py
@@ -0,0 +1,97 @@
+# -*- 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 ]
+ check_hostname:
+ description:
+ - Whether to validate the server host name when an SSL connection is required.
+ - Setting this to C(false) disables hostname verification. Use with caution.
+ - Requires pymysql >= 0.7.11.
+ - This optoin has no effect on MySQLdb.
+ type: bool
+ version_added: '1.1.0'
+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 installed 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).
+ - Be sure you have PyMySQL or MySQLdb library installed on the target machine
+ for the Python interpreter Ansible uses, for example, if it is Python 3,
+ you must install the library for Python 3. You can also change the interpreter.
+ For more information, see U(https://docs.ansible.com/ansible/latest/reference_appendices/interpreter_discovery.html).
+ - 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``.
+ - Alternatively, you can use the mysqlclient library instead of MySQL-python (MySQLdb)
+ which supports both Python 2.X and Python >=3.5.
+ See U(https://pypi.org/project/mysqlclient/) how to install it.
+'''
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/database.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/database.py
new file mode 100644
index 00000000..67850308
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/database.py
@@ -0,0 +1,189 @@
+# 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) 2014, Toshio Kuratomi <tkuratomi@ansible.com>
+#
+# 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 re
+
+
+# Input patterns for is_input_dangerous function:
+#
+# 1. '"' in string and '--' in string or
+# "'" in string and '--' in string
+PATTERN_1 = re.compile(r'(\'|\").*--')
+
+# 2. union \ intersect \ except + select
+PATTERN_2 = re.compile(r'(UNION|INTERSECT|EXCEPT).*SELECT', re.IGNORECASE)
+
+# 3. ';' and any KEY_WORDS
+PATTERN_3 = re.compile(r';.*(SELECT|UPDATE|INSERT|DELETE|DROP|TRUNCATE|ALTER)', re.IGNORECASE)
+
+
+class SQLParseError(Exception):
+ pass
+
+
+class UnclosedQuoteError(SQLParseError):
+ pass
+
+
+# maps a type of identifier to the maximum number of dot levels that are
+# allowed to specify that identifier. For example, a database column can be
+# specified by up to 4 levels: database.schema.table.column
+_PG_IDENTIFIER_TO_DOT_LEVEL = dict(
+ database=1,
+ schema=2,
+ table=3,
+ column=4,
+ role=1,
+ tablespace=1,
+ sequence=3,
+ publication=1,
+)
+_MYSQL_IDENTIFIER_TO_DOT_LEVEL = dict(database=1, table=2, column=3, role=1, vars=1)
+
+
+def _find_end_quote(identifier, quote_char):
+ accumulate = 0
+ while True:
+ try:
+ quote = identifier.index(quote_char)
+ except ValueError:
+ raise UnclosedQuoteError
+ accumulate = accumulate + quote
+ try:
+ next_char = identifier[quote + 1]
+ except IndexError:
+ return accumulate
+ if next_char == quote_char:
+ try:
+ identifier = identifier[quote + 2:]
+ accumulate = accumulate + 2
+ except IndexError:
+ raise UnclosedQuoteError
+ else:
+ return accumulate
+
+
+def _identifier_parse(identifier, quote_char):
+ if not identifier:
+ raise SQLParseError('Identifier name unspecified or unquoted trailing dot')
+
+ already_quoted = False
+ if identifier.startswith(quote_char):
+ already_quoted = True
+ try:
+ end_quote = _find_end_quote(identifier[1:], quote_char=quote_char) + 1
+ except UnclosedQuoteError:
+ already_quoted = False
+ else:
+ if end_quote < len(identifier) - 1:
+ if identifier[end_quote + 1] == '.':
+ dot = end_quote + 1
+ first_identifier = identifier[:dot]
+ next_identifier = identifier[dot + 1:]
+ further_identifiers = _identifier_parse(next_identifier, quote_char)
+ further_identifiers.insert(0, first_identifier)
+ else:
+ raise SQLParseError('User escaped identifiers must escape extra quotes')
+ else:
+ further_identifiers = [identifier]
+
+ if not already_quoted:
+ try:
+ dot = identifier.index('.')
+ except ValueError:
+ identifier = identifier.replace(quote_char, quote_char * 2)
+ identifier = ''.join((quote_char, identifier, quote_char))
+ further_identifiers = [identifier]
+ else:
+ if dot == 0 or dot >= len(identifier) - 1:
+ identifier = identifier.replace(quote_char, quote_char * 2)
+ identifier = ''.join((quote_char, identifier, quote_char))
+ further_identifiers = [identifier]
+ else:
+ first_identifier = identifier[:dot]
+ next_identifier = identifier[dot + 1:]
+ further_identifiers = _identifier_parse(next_identifier, quote_char)
+ first_identifier = first_identifier.replace(quote_char, quote_char * 2)
+ first_identifier = ''.join((quote_char, first_identifier, quote_char))
+ further_identifiers.insert(0, first_identifier)
+
+ return further_identifiers
+
+
+def pg_quote_identifier(identifier, id_type):
+ identifier_fragments = _identifier_parse(identifier, quote_char='"')
+ if len(identifier_fragments) > _PG_IDENTIFIER_TO_DOT_LEVEL[id_type]:
+ raise SQLParseError('PostgreSQL does not support %s with more than %i dots' % (id_type, _PG_IDENTIFIER_TO_DOT_LEVEL[id_type]))
+ return '.'.join(identifier_fragments)
+
+
+def mysql_quote_identifier(identifier, id_type):
+ identifier_fragments = _identifier_parse(identifier, quote_char='`')
+ if (len(identifier_fragments) - 1) > _MYSQL_IDENTIFIER_TO_DOT_LEVEL[id_type]:
+ raise SQLParseError('MySQL does not support %s with more than %i dots' % (id_type, _MYSQL_IDENTIFIER_TO_DOT_LEVEL[id_type]))
+
+ special_cased_fragments = []
+ for fragment in identifier_fragments:
+ if fragment == '`*`':
+ special_cased_fragments.append('*')
+ else:
+ special_cased_fragments.append(fragment)
+
+ return '.'.join(special_cased_fragments)
+
+
+def is_input_dangerous(string):
+ """Check if the passed string is potentially dangerous.
+ Can be used to prevent SQL injections.
+
+ Note: use this function only when you can't use
+ psycopg2's cursor.execute method parametrized
+ (typically with DDL queries).
+ """
+ if not string:
+ return False
+
+ for pattern in (PATTERN_1, PATTERN_2, PATTERN_3):
+ if re.search(pattern, string):
+ return True
+
+ return False
+
+
+def check_input(module, *args):
+ """Wrapper for is_input_dangerous function."""
+ needs_to_check = args
+
+ dangerous_elements = []
+
+ for elem in needs_to_check:
+ if isinstance(elem, str):
+ if is_input_dangerous(elem):
+ dangerous_elements.append(elem)
+
+ elif isinstance(elem, list):
+ for e in elem:
+ if is_input_dangerous(e):
+ dangerous_elements.append(e)
+
+ elif elem is None or isinstance(elem, bool):
+ pass
+
+ else:
+ elem = str(elem)
+ if is_input_dangerous(elem):
+ dangerous_elements.append(elem)
+
+ if dangerous_elements:
+ module.fail_json(msg="Passed input '%s' is "
+ "potentially dangerous" % ', '.join(dangerous_elements))
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/mysql.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/mysql.py
new file mode 100644
index 00000000..5af9c202
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/module_utils/mysql.py
@@ -0,0 +1,148 @@
+# 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)
+from functools import reduce
+__metaclass__ = type
+
+import os
+
+from ansible.module_utils.six.moves import configparser
+from ansible.module_utils._text import to_native
+
+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):
+ # Default values of comment_prefix is '#' and ';'.
+ # '!' added to prevent a parsing error
+ # when a config file contains !includedir parameter.
+ cp = configparser.ConfigParser(comment_prefixes=('#', ';', '!'))
+ 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, check_hostname=None):
+ config = {}
+
+ if config_file and os.path.exists(config_file):
+ config['read_default_file'] = config_file
+
+ if config_overrides_defaults:
+ try:
+ cp = parse_from_mysql_config_file(config_file)
+ except Exception as e:
+ module.fail_json(msg="Failed to parse %s: %s" % (config_file, to_native(e)))
+
+ # Override some commond defaults with values from config file if needed
+ if cp and cp.has_section('client'):
+ 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(msg='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 or check_hostname 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 check_hostname is not None:
+ if mysql_driver.__name__ == "pymysql":
+ version_tuple = (n for n in mysql_driver.__version__.split('.') if n != 'None')
+ if reduce(lambda x, y: int(x) * 100 + int(y), version_tuple) >= 711:
+ config['ssl']['check_hostname'] = check_hostname
+ else:
+ module.fail_json(msg='To use check_hostname, pymysql >= 0.7.11 is required on the target host')
+
+ 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)
+
+ # Monkey patch the Connection class to close the connection when garbage collected
+ def _conn_patch(conn_self):
+ conn_self.close()
+ db_connection.__class__.__del__ = _conn_patch
+ # Patched
+
+ 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']),
+ check_hostname=dict(type='bool', default=None),
+ )
+
+
+def get_server_version(cursor):
+ """Returns a string representation of the server version."""
+ cursor.execute("SELECT VERSION() AS version")
+ result = cursor.fetchone()
+
+ if isinstance(result, dict):
+ version_str = result['version']
+ else:
+ version_str = result[0]
+
+ return version_str
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_db.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_db.py
new file mode 100644
index 00000000..aa9ade06
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_db.py
@@ -0,0 +1,720 @@
+#!/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
+ default: []
+ hex_blob:
+ description:
+ - Dump binary columns using hexadecimal notation.
+ type: bool
+ default: no
+ version_added: '0.1.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).
+ type: bool
+ default: no
+ version_added: '0.1.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).
+ type: int
+ choices: [0, 1, 2]
+ default: 0
+ version_added: '0.1.0'
+ skip_lock_tables:
+ description:
+ - Skip locking tables for read. Used when I(state=dump), ignored otherwise.
+ type: bool
+ default: no
+ version_added: '0.1.0'
+ dump_extra_args:
+ description:
+ - Provide additional arguments for mysqldump.
+ Used when I(state=dump) only, ignored otherwise.
+ type: str
+ version_added: '0.1.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.
+ type: bool
+ default: no
+ version_added: '0.1.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.1.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.1.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.1.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.1.0'
+
+seealso:
+- module: community.mysql.mysql_info
+- module: community.mysql.mysql_variables
+- module: community.mysql.mysql_user
+- module: community.mysql.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:
+ - Supports C(check_mode).
+ - 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.mysql.mysql
+
+'''
+
+EXAMPLES = r'''
+- name: Create a new database with name 'bobdata'
+ community.mysql.mysql_db:
+ name: bobdata
+ state: present
+
+- name: Create new databases with names 'foo' and 'bar'
+ community.mysql.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
+ community.mysql.mysql_db:
+ name: my_db
+ state: import
+ target: /tmp/dump.sql.bz2
+
+- name: Restore database ignoring errors
+ community.mysql.mysql_db:
+ name: my_db
+ state: import
+ target: /tmp/dump.sql.bz2
+ force: yes
+
+- name: Dump multiple databases
+ community.mysql.mysql_db:
+ state: dump
+ name: db_1,db_2
+ target: /tmp/dump.sql
+
+- name: Dump multiple databases
+ community.mysql.mysql_db:
+ state: dump
+ name:
+ - db_1
+ - db_2
+ target: /tmp/dump.sql
+
+- name: Dump all databases to hostname.sql
+ community.mysql.mysql_db:
+ state: dump
+ name: all
+ target: /tmp/dump.sql
+
+- name: Dump all databases to hostname.sql including master data
+ community.mysql.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
+ community.mysql.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>
+ community.mysql.mysql_db:
+ state: dump
+ name: db_1
+ encoding: latin1
+ target: /tmp/dump.sql
+
+- name: Delete database with name 'bobdata'
+ community.mysql.mysql_db:
+ name: bobdata
+ state: absent
+
+- name: Make sure there is neither a database with name 'foo', nor one with name 'bar'
+ community.mysql.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
+ community.mysql.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
+ community.mysql.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.1.0'
+'''
+
+import os
+import subprocess
+import traceback
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.mysql.plugins.module_utils.database import mysql_quote_identifier
+from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
+from ansible.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():
+ argument_spec = mysql_common_argument_spec()
+ argument_spec.update(
+ 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']),
+ 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, no_log=True),
+ restrict_config_file=dict(type='bool', default=False),
+ check_implicit_admin=dict(type='bool', default=False),
+ config_overrides_defaults=dict(type='bool', default=False),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ 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"]
+ check_hostname = module.params["check_hostname"]
+ 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, check_hostname=check_hostname,
+ 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,
+ check_hostname=check_hostname)
+ 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()
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_info.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_info.py
new file mode 100644
index 00000000..c7d9e89b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_info.py
@@ -0,0 +1,545 @@
+#!/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.1.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).
+- Supports C(check_mode).
+
+seealso:
+- module: community.mysql.mysql_variables
+- module: community.mysql.mysql_db
+- module: community.mysql.mysql_user
+- module: community.mysql.mysql_replication
+
+author:
+- Andrew Klychkov (@Andersson007)
+
+extends_documentation_fragment:
+- community.mysql.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
+ community.mysql.mysql_info:
+ login_user: root
+
+- name: Get MySQL version with non-default credentials
+ community.mysql.mysql_info:
+ login_user: mysuperuser
+ login_password: mysuperpass
+ filter: version
+
+- name: Collect all info except settings and users by root
+ community.mysql.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
+ community.mysql.mysql_info:
+ filter:
+ - databases
+ - version
+
+- name: Collect info about databases and version using ~alice/.my.cnf as a credential file
+ become: yes
+ community.mysql.mysql_info:
+ config_file: /home/alice/.my.cnf
+ filter:
+ - databases
+ - version
+
+- name: Collect info about databases including empty and excluding their sizes
+ become: yes
+ community.mysql.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.mysql.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']
+ check_hostname = module.params['check_hostname']
+ 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,
+ check_hostname=check_hostname,
+ 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()
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_query.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_query.py
new file mode 100644
index 00000000..ed3ace4a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_query.py
@@ -0,0 +1,253 @@
+#!/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.
+- To run SQL queries from a file, use M(community.mysql.mysql_db) module.
+version_added: '0.1.0'
+options:
+ query:
+ description:
+ - SQL query to run. Multiple queries can be passed using YAML list syntax.
+ - Must be a string or YAML list containing strings.
+ type: raw
+ 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
+seealso:
+- module: community.mysql.mysql_db
+author:
+- Andrew Klychkov (@Andersson007)
+extends_documentation_fragment:
+- community.mysql.mysql
+
+'''
+
+EXAMPLES = r'''
+- name: Simple select query to acme db
+ community.mysql.mysql_query:
+ login_db: acme
+ query: SELECT * FROM orders
+
+- name: Select query to db acme with positional arguments
+ community.mysql.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
+ community.mysql.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
+ community.mysql.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.mysql.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='raw', 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']
+ check_hostname = module.params['check_hostname']
+ config_file = module.params['config_file']
+ query = module.params["query"]
+
+ if not isinstance(query, (str, list)):
+ module.fail_json(msg="the query option value must be a string or list, passed %s" % type(query))
+
+ if isinstance(query, str):
+ query = [query]
+
+ for elem in query:
+ if not isinstance(elem, str):
+ module.fail_json(msg="the elements in query list must be strings, passed '%s' %s" % (elem, type(elem)))
+
+ 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,
+ check_hostname=check_hostname,
+ 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
+
+ try:
+ executed_queries.append(cursor._last_executed)
+ except AttributeError:
+ # MySQLdb removed cursor._last_executed as a duplicate of cursor._executed
+ executed_queries.append(cursor._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()
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_replication.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_replication.py
new file mode 100644
index 00000000..322a6a8c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_replication.py
@@ -0,0 +1,594 @@
+#!/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.mysql 0.1.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
+ default: false
+ 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
+ default: false
+ 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.1.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.1.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.1.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.1.0'
+ fail_on_error:
+ description:
+ - Fails on error when calling mysql.
+ type: bool
+ default: False
+ version_added: '0.1.0'
+
+notes:
+- If an empty value for the parameter of string type is needed, use an empty string.
+
+extends_documentation_fragment:
+- community.mysql.mysql
+
+
+seealso:
+- module: community.mysql.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
+ community.mysql.mysql_replication:
+ mode: stopslave
+
+- name: Get master binlog file name and binlog position
+ community.mysql.mysql_replication:
+ mode: getmaster
+
+- name: Change master to master server 192.0.2.1 and use binary log 'mysql-bin.000009' with position 4578
+ community.mysql.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
+ community.mysql.mysql_replication:
+ mode: getslave
+ login_host: ansible.example.com
+ login_port: 3308
+
+- name: On MariaDB change master to use GTID current_pos
+ community.mysql.mysql_replication:
+ mode: changemaster
+ master_use_gtid: current_pos
+
+- name: Change master to use replication delay 3600 seconds
+ community.mysql.mysql_replication:
+ mode: changemaster
+ master_host: 192.0.2.1
+ master_delay: 3600
+
+- name: Start MariaDB standby with connection name master-1
+ community.mysql.mysql_replication:
+ mode: startslave
+ connection_name: master-1
+
+- name: Stop replication in channel master-1
+ community.mysql.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
+ community.mysql.mysql_replication:
+ mode: resetmaster
+
+- name: Run start slave and fail the task on errors
+ community.mysql.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)
+ community.mysql.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.1.0'
+'''
+
+import os
+import warnings
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
+from ansible.module_utils._text import to_native
+from distutils.version import LooseVersion
+
+executed_queries = []
+
+
+def uses_replica_terminology(cursor):
+ """Checks if REPLICA must be used instead of SLAVE"""
+ cursor.execute("SELECT VERSION() AS version")
+ result = cursor.fetchone()
+
+ if isinstance(result, dict):
+ version_str = result['version']
+ else:
+ version_str = result[0]
+
+ version = LooseVersion(version_str)
+
+ if 'mariadb' in version_str.lower():
+ return version >= LooseVersion('10.5.1')
+ else:
+ return version >= LooseVersion('8.0.22')
+
+
+def get_master_status(cursor):
+ cursor.execute("SHOW MASTER STATUS")
+ masterstatus = cursor.fetchone()
+ return masterstatus
+
+
+def get_slave_status(cursor, connection_name='', channel='', term='REPLICA'):
+ if connection_name:
+ query = "SHOW %s '%s' STATUS" % (term, connection_name)
+ else:
+ query = "SHOW %s STATUS" % term
+
+ 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, term='REPLICA'):
+ if connection_name:
+ query = "STOP %s '%s'" % (term, connection_name)
+ else:
+ query = 'STOP %s' % term
+
+ 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, term='REPLICA'):
+ if connection_name:
+ query = "RESET %s '%s'" % (term, connection_name)
+ else:
+ query = 'RESET %s' % term
+
+ 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, term='REPLICA'):
+ if connection_name:
+ query = "RESET %s '%s' ALL" % (term, connection_name)
+ else:
+ query = 'RESET %s ALL' % term
+
+ 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, term='REPLICA'):
+ if connection_name:
+ query = "START %s '%s'" % (term, connection_name)
+ else:
+ query = 'START %s' % term
+
+ 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():
+ argument_spec = mysql_common_argument_spec()
+ argument_spec.update(
+ 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'),
+ 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),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ 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"]
+ check_hostname = module.params["check_hostname"]
+ 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, check_hostname=check_hostname)
+ 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)))
+
+ # Since MySQL 8.0.22 and MariaDB 10.5.1,
+ # "REPLICA" must be used instead of "SLAVE"
+ if uses_replica_terminology(cursor):
+ replica_term = 'REPLICA'
+ else:
+ replica_term = 'SLAVE'
+
+ if mode in "getmaster":
+ status = get_master_status(cursor)
+ if not isinstance(status, dict):
+ 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, replica_term)
+ 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, replica_term)
+ if started is True:
+ module.exit_json(msg="Slave started ", changed=True, queries=executed_queries)
+ else:
+ module.exit_json(msg="Slave already started (Or cannot be started)", changed=False, queries=executed_queries)
+ elif mode in "stopslave":
+ stopped = stop_slave(module, cursor, connection_name, channel, fail_on_error, replica_term)
+ 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, replica_term)
+ if reset is True:
+ module.exit_json(msg="Slave reset", changed=True, queries=executed_queries)
+ else:
+ module.exit_json(msg="Slave already reset", changed=False, queries=executed_queries)
+ elif mode in "resetslaveall":
+ reset = reset_slave_all(module, cursor, connection_name, channel, fail_on_error, replica_term)
+ if reset is True:
+ module.exit_json(msg="Slave reset", changed=True, queries=executed_queries)
+ else:
+ module.exit_json(msg="Slave already reset", changed=False, queries=executed_queries)
+
+ warnings.simplefilter("ignore")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_user.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_user.py
new file mode 100644
index 00000000..0c757fa8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_user.py
@@ -0,0 +1,1151 @@
+#!/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
+ tls_requires:
+ description:
+ - Set requirement for secure transport as a dictionary of requirements (see the examples).
+ - Valid requirements are SSL, X509, SUBJECT, ISSUER, CIPHER.
+ - SUBJECT, ISSUER and CIPHER are complementary, and mutually exclusive with SSL and X509.
+ - U(https://mariadb.com/kb/en/securing-connections-for-client-and-server/#requiring-tls).
+ type: dict
+ version_added: 1.0.0
+ 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.1.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.1.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.1.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.1.0'
+
+notes:
+ - "MySQL server installs with default I(login_user) of C(root) and no password.
+ To secure this user as part of an idempotent playbook, you must create at least two tasks:
+ 1) change the root user's password, without providing any I(login_user)/I(login_password) details,
+ 2) drop a C(~/.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 C(mysql_native_password) encrypted password hash module.
+ - Supports (check_mode).
+
+seealso:
+- module: community.mysql.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.mysql.mysql
+
+'''
+
+EXAMPLES = r'''
+- name: Removes anonymous user account for localhost
+ community.mysql.mysql_user:
+ name: ''
+ host: localhost
+ state: absent
+
+- name: Removes all anonymous user accounts
+ community.mysql.mysql_user:
+ name: ''
+ host_all: yes
+ state: absent
+
+- name: Create database user with name 'bob' and password '12345' with all database privileges
+ community.mysql.mysql_user:
+ name: bob
+ password: 12345
+ priv: '*.*:ALL'
+ state: present
+
+- name: Create database user using hashed password with all database privileges
+ community.mysql.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'
+ community.mysql.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
+ community.mysql.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.
+# Setting this privilege in this manner is supported for backwards compatibility only.
+# Use 'tls_requires' instead.
+- name: Modify user to require SSL connections
+ community.mysql.mysql_user:
+ name: bob
+ append_privs: yes
+ priv: '*.*:REQUIRESSL'
+ state: present
+
+- name: Modify user to require TLS connection with a valid client certificate
+ community.mysql.mysql_user:
+ name: bob
+ tls_requires:
+ x509:
+ state: present
+
+- name: Modify user to require TLS connection with a specific client certificate and cipher
+ community.mysql.mysql_user:
+ name: bob
+ tls_requires:
+ subject: '/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
+ cipher: 'ECDHE-ECDSA-AES256-SHA384'
+
+- name: Modify user to no longer require SSL
+ community.mysql.mysql_user:
+ name: bob
+ tls_requires:
+
+- name: Ensure no user named 'sally'@'localhost' exists, also passing in the auth credentials
+ community.mysql.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
+ community.mysql.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
+ community.mysql.mysql_user:
+ name: sally
+ host_all: yes
+ state: absent
+
+- name: Specify grants composed of more than one word
+ community.mysql.mysql_user:
+ name: replication
+ password: 12345
+ priv: "*.*:REPLICATION CLIENT"
+ state: present
+
+- name: Revoke all privileges for user 'bob' and password '12345'
+ community.mysql.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
+ community.mysql.mysql_user:
+ name: root
+ password: abc123
+ login_unix_socket: /var/run/mysqld/mysqld.sock
+
+- name: Example of skipping binary logging while adding user 'bob'
+ community.mysql.mysql_user:
+ name: bob
+ password: 12345
+ priv: "*.*:USAGE"
+ state: present
+ sql_log_bin: no
+
+- name: Create user 'bob' authenticated with plugin 'AWSAuthenticationPlugin'
+ community.mysql.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
+ community.mysql.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
+'''
+
+RETURN = '''#'''
+
+import re
+import string
+from distutils.version import LooseVersion
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError
+from ansible_collections.community.mysql.plugins.module_utils.mysql import (
+ mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec, get_server_version
+)
+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',
+ 'SHOW_ROUTINE',))
+
+
+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 supports_identified_by_password(cursor):
+ """
+ Determines whether the 'CREATE USER %s@%s IDENTIFIED BY PASSWORD %s' syntax is supported. This was dropped in
+ MySQL 8.0.
+ """
+ version_str = get_server_version(cursor)
+
+ if 'mariadb' in version_str.lower():
+ return True
+ else:
+ return LooseVersion(version_str) < LooseVersion('8')
+
+
+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 sanitize_requires(tls_requires):
+ sanitized_requires = {}
+ if tls_requires:
+ for key in tls_requires.keys():
+ sanitized_requires[key.upper()] = tls_requires[key]
+ if any([key in ["CIPHER", "ISSUER", "SUBJECT"] for key in sanitized_requires.keys()]):
+ sanitized_requires.pop("SSL", None)
+ sanitized_requires.pop("X509", None)
+ return sanitized_requires
+
+ if "X509" in sanitized_requires.keys():
+ sanitized_requires = "X509"
+ else:
+ sanitized_requires = "SSL"
+
+ return sanitized_requires
+ return None
+
+
+def mogrify_requires(query, params, tls_requires):
+ if tls_requires:
+ if isinstance(tls_requires, dict):
+ k, v = zip(*tls_requires.items())
+ requires_query = " AND ".join(("%s %%s" % key for key in k))
+ params += v
+ else:
+ requires_query = tls_requires
+ query = " REQUIRE ".join((query, requires_query))
+ return query, params
+
+
+def do_not_mogrify_requires(query, params, tls_requires):
+ return query, params
+
+
+def get_tls_requires(cursor, user, host):
+ if user:
+ if not use_old_user_mgmt(cursor):
+ query = "SHOW CREATE USER '%s'@'%s'" % (user, host)
+ else:
+ query = "SHOW GRANTS for '%s'@'%s'" % (user, host)
+
+ cursor.execute(query)
+ require_list = [tuple[0] for tuple in filter(lambda x: "REQUIRE" in x[0], cursor.fetchall())]
+ require_line = require_list[0] if require_list else ""
+ pattern = r"(?<=\bREQUIRE\b)(.*?)(?=(?:\bPASSWORD\b|$))"
+ requires_match = re.search(pattern, require_line)
+ requires = requires_match.group().strip() if requires_match else ""
+ if any((requires.startswith(req) for req in ('SSL', 'X509', 'NONE'))):
+ requires = requires.split()[0]
+ if requires == 'NONE':
+ requires = None
+ else:
+ import shlex
+
+ items = iter(shlex.split(requires))
+ requires = dict(zip(items, items))
+ return requires or None
+
+
+def get_grants(cursor, user, host):
+ cursor.execute("SHOW GRANTS FOR %s@%s", (user, host))
+ grants_line = list(filter(lambda x: "ON *.*" in x[0], cursor.fetchall()))[0]
+ pattern = r"(?<=\bGRANT\b)(.*?)(?=(?:\bON\b))"
+ grants = re.search(pattern, grants_line[0]).group().strip()
+ return grants.split(", ")
+
+
+def user_add(cursor, user, host, host_all, password, encrypted,
+ plugin, plugin_hash_string, plugin_auth_string, new_priv,
+ tls_requires, 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)
+
+ mogrify = do_not_mogrify_requires if old_user_mgmt else mogrify_requires
+
+ if password and encrypted:
+ if supports_identified_by_password(cursor):
+ query_with_args = "CREATE USER %s@%s IDENTIFIED BY PASSWORD %s", (user, host, password)
+ else:
+ query_with_args = "CREATE USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, password)
+ elif password and not encrypted:
+ if old_user_mgmt:
+ query_with_args = "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]
+ query_with_args = "CREATE USER %s@%s IDENTIFIED WITH mysql_native_password AS %s", (user, host, encrypted_password)
+ elif plugin and plugin_hash_string:
+ query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string)
+ elif plugin and plugin_auth_string:
+ query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string)
+ elif plugin:
+ query_with_args = "CREATE USER %s@%s IDENTIFIED WITH %s", (user, host, plugin)
+ else:
+ query_with_args = "CREATE USER %s@%s", (user, host)
+
+ query_with_args_and_tls_requires = query_with_args + (tls_requires,)
+ cursor.execute(*mogrify(*query_with_args_and_tls_requires))
+
+ if new_priv is not None:
+ for db_table, priv in iteritems(new_priv):
+ privileges_grant(cursor, user, host, db_table, priv, tls_requires)
+ if tls_requires is not None:
+ privileges_grant(cursor, user, host, "*.*", get_grants(cursor, user, host), tls_requires)
+ 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, tls_requires, module):
+ changed = False
+ msg = "User unchanged"
+ grant_option = False
+
+ # Determine what user management method server uses
+ old_user_mgmt = use_old_user_mgmt(cursor)
+
+ 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):
+
+ # 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:
+ query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s AS %s", (user, host, plugin, plugin_hash_string)
+ elif plugin_auth_string:
+ query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s BY %s", (user, host, plugin, plugin_auth_string)
+ else:
+ query_with_args = "ALTER USER %s@%s IDENTIFIED WITH %s", (user, host, plugin)
+
+ cursor.execute(*query_with_args)
+ 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, tls_requires)
+ 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:
+
+ # If appending privileges, only the set difference between new privileges and current privileges matter.
+ # The symmetric difference isn't relevant for append because existing privileges will not be revoked.
+ if append_privs:
+ priv_diff = set(new_priv[db_table]) - set(curr_priv[db_table])
+ else:
+ 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], tls_requires)
+ changed = True
+
+ # Handle TLS requirements
+ current_requires = get_tls_requires(cursor, user, host)
+ if current_requires != tls_requires:
+ msg = "TLS requires updated"
+ if module.check_mode:
+ return (True, msg)
+ if not old_user_mgmt:
+ pre_query = "ALTER USER"
+ else:
+ pre_query = "GRANT %s ON *.* TO" % ",".join(get_grants(cursor, user, host))
+
+ if tls_requires is not None:
+ query = " ".join((pre_query, "%s@%s"))
+ query_with_args = mogrify_requires(query, (user, host), tls_requires)
+ else:
+ query = " ".join((pre_query, "%s@%s REQUIRE NONE"))
+ query_with_args = query, (user, host)
+
+ cursor.execute(*query_with_args)
+ 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)
+ else:
+ hostnames = [host]
+
+ for hostname in hostnames:
+ cursor.execute("DROP USER %s@%s", (user, hostname))
+
+ 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, tls_requires):
+ # 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")
+ params = (user, host)
+ if tls_requires and use_old_user_mgmt(cursor):
+ query, params = mogrify_requires(" ".join(query), params, tls_requires)
+ query = [query]
+ if 'REQUIRESSL' in priv and not tls_requires:
+ query.append("REQUIRE SSL")
+ if 'GRANT' in priv:
+ query.append("WITH GRANT OPTION")
+ query = ' '.join(query)
+ cursor.execute(query, params)
+
+
+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():
+ argument_spec = mysql_common_argument_spec()
+ argument_spec.update(
+ 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'),
+ tls_requires=dict(type='dict'),
+ 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),
+ sql_log_bin=dict(type='bool', default=True),
+ 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'),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ 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"]
+ tls_requires = sanitize_requires(module.params["tls_requires"])
+ 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"]
+ check_hostname = module.params["check_hostname"]
+ 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, 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, check_hostname=check_hostname)
+ 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, check_hostname=check_hostname)
+ 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, tls_requires, module)
+ else:
+ changed, msg = user_mod(cursor, user, host, host_all, None, encrypted,
+ plugin, plugin_hash_string, plugin_auth_string,
+ priv, append_privs, tls_requires, 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, tls_requires, 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()
diff --git a/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_variables.py b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_variables.py
new file mode 100644
index 00000000..06beee3c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/plugins/modules/mysql_variables.py
@@ -0,0 +1,268 @@
+#!/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.1.0'
+
+notes:
+- Does not support C(check_mode).
+
+seealso:
+- module: community.mysql.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.mysql.mysql
+'''
+
+EXAMPLES = r'''
+- name: Check for sync_binlog setting
+ community.mysql.mysql_variables:
+ variable: sync_binlog
+
+- name: Set read_only variable to 1 persistently
+ community.mysql.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.1.0'
+'''
+
+import os
+import warnings
+from re import match
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.mysql.plugins.module_utils.database import SQLParseError, mysql_quote_identifier
+from ansible_collections.community.mysql.plugins.module_utils.mysql import mysql_connect, mysql_driver, mysql_driver_fail_msg, mysql_common_argument_spec
+from ansible.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():
+ argument_spec = mysql_common_argument_spec()
+ argument_spec.update(
+ variable=dict(type='str'),
+ value=dict(type='str'),
+ mode=dict(type='str', choices=['global', 'persist', 'persist_only'], default='global'),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec
+ )
+ 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"]
+ check_hostname = module.params["check_hostname"]
+ 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, check_hostname=check_hostname)
+ 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()
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/aliases b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/aliases
new file mode 100644
index 00000000..1065935f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/aliases
@@ -0,0 +1,7 @@
+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
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/defaults/main.yml
new file mode 100644
index 00000000..3751f4ef
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/defaults/main.yml
@@ -0,0 +1,7 @@
+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
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/meta/main.yml
new file mode 100644
index 00000000..71986171
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+- setup_mariadb
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/main.yml
new file mode 100644
index 00000000..4ea76a9e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/main.yml
@@ -0,0 +1,21 @@
+# 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', '>=')
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml
new file mode 100644
index 00000000..e3e76058
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml
@@ -0,0 +1,173 @@
+# 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
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml
new file mode 100644
index 00000000..98fa5fe6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml
@@ -0,0 +1,118 @@
+# 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"]
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml
new file mode 100644
index 00000000..86a67606
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml
@@ -0,0 +1,96 @@
+# 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"]
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml
new file mode 100644
index 00000000..7bcb2d23
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/defaults/main.yml
@@ -0,0 +1,13 @@
+dbdeployer_version: 1.52.0
+dbdeployer_home_dir: /opt/dbdeployer
+
+home_dir: /root
+
+percona_client_version: 5.7
+
+mariadb_install: false
+
+mysql_version: 8.0.22
+mariadb_version: 10.5.4
+
+mysql_base_port: 3306
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/handlers/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/handlers/main.yml
new file mode 100644
index 00000000..090a5e77
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/handlers/main.yml
@@ -0,0 +1,6 @@
+---
+- name: "{{ role_name }} | handler | create dbdeployer installed file"
+ template:
+ src: installed_file.j2
+ dest: "{{ dbdeployer_installed_file }}"
+ listen: create zookeeper installed file
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/config.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/config.yml
new file mode 100644
index 00000000..57be29aa
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/config.yml
@@ -0,0 +1,15 @@
+---
+- name: "{{ role_name }} | config | download mysql tarball"
+ get_url:
+ url: "{{ install_src }}"
+ dest: "{{ dbdeployer_sandbox_download_dir }}/{{ install_tarball }}"
+
+- name: "{{ role_name }} | config | run unpack tarball"
+ shell:
+ cmd: "dbdeployer unpack {{ dbdeployer_sandbox_download_dir }}/{{ install_tarball }}"
+ creates: "{{ dbdeployer_sandbox_binary_dir }}/{{ install_version }}"
+
+- name: "{{ role_name }} | config | setup replication topology"
+ shell:
+ cmd: "dbdeployer deploy multiple {{ install_version }} --base-port {{ mysql_base_port }} --my-cnf-options=\"master_info_repository='TABLE'\" --my-cnf-options=\"relay_log_info_repository='TABLE'\""
+ creates: "{{ dbdeployer_sandbox_home_dir }}/multi_msb_{{ install_version|replace('.','_') }}"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/dir.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/dir.yml
new file mode 100644
index 00000000..dc028799
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/dir.yml
@@ -0,0 +1,11 @@
+---
+- name: "{{ role_name }} | dir | create dbdeployer directories"
+ file:
+ state: directory
+ path: "{{ item }}"
+ loop:
+ - "{{ dbdeployer_home_dir }}"
+ - "{{ dbdeployer_install_dir }}"
+ - "{{ dbdeployer_sandbox_download_dir }}"
+ - "{{ dbdeployer_sandbox_binary_dir }}"
+ - "{{ dbdeployer_sandbox_home_dir }}"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/install.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/install.yml
new file mode 100644
index 00000000..aacdddc3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/install.yml
@@ -0,0 +1,59 @@
+---
+- name: "{{ role_name }} | install | add apt signing key for percona"
+ apt_key:
+ keyserver: keyserver.ubuntu.com
+ id: 4D1BB29D63D98E422B2113B19334A25F8507EFA5
+ state: present
+
+- name: "{{ role_name }} | install | add percona repositories"
+ apt_repository:
+ repo: "{{ item }}"
+ state: present
+ loop: "{{ percona_mysql_repos }}"
+
+- name: "{{ role_name }} | install | install packages required by percona"
+ apt:
+ name: "{{ percona_mysql_packages }}"
+ state: present
+ environment:
+ DEBIAN_FRONTEND: noninteractive
+
+- name: "{{ role_name }} | install | install packages required by mysql connector"
+ apt:
+ name: "{{ install_python_prereqs }}"
+ state: present
+ environment:
+ DEBIAN_FRONTEND: noninteractive
+
+- name: "{{ role_name }} | install | install python packages"
+ pip:
+ name: "{{ python_packages }}"
+
+- name: "{{ role_name }} | install | install packages required by mysql"
+ apt:
+ name: "{{ install_prereqs }}"
+ state: present
+ environment:
+ DEBIAN_FRONTEND: noninteractive
+
+- name: "{{ role_name }} | install | download and unpack dbdeployer"
+ unarchive:
+ remote_src: true
+ src: "{{ dbdeployer_src }}"
+ dest: "{{ dbdeployer_install_dir }}"
+ creates: "{{ dbdeployer_installed_file }}"
+ register: dbdeployer_tarball_install
+ notify:
+ - create zookeeper installed file
+ until: dbdeployer_tarball_install is not failed
+ retries: 6
+ delay: 5
+
+- name: "{{ role_name }} | install | create symlink"
+ file:
+ src: "{{ dbdeployer_install_dir }}/dbdeployer-{{ dbdeployer_version }}.linux"
+ dest: /usr/local/bin/dbdeployer
+ follow: false
+ state: link
+
+- meta: flush_handlers
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/main.yml
new file mode 100644
index 00000000..c6a83489
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/main.yml
@@ -0,0 +1,11 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- import_tasks: setvars.yml
+- import_tasks: dir.yml
+- import_tasks: install.yml
+- import_tasks: config.yml
+- import_tasks: verify.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/setvars.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/setvars.yml
new file mode 100644
index 00000000..14a2c545
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/setvars.yml
@@ -0,0 +1,28 @@
+---
+- name: "{{ role_name }} | setvars | split mysql version in parts"
+ set_fact:
+ mysql_version_parts: "{{ mysql_version.split('.') }}"
+
+- name: "{{ role_name }} | setvars | get mysql major version"
+ set_fact:
+ mysql_major_version: "{{ mysql_version_parts[0] + '.' + mysql_version_parts[1] }}"
+
+- name: "{{ role_name }} | setvars | set the appropriate extension dependent on the mysql version"
+ set_fact:
+ mysql_compression_extension: "{{ mysql_version is version('8.0.0', '<') | ternary('gz', 'xz') }}"
+
+- name: "{{ role_name }} | setvars | set the install type"
+ set_fact:
+ install_type: "{{ mariadb_install | ternary('mariadb', 'mysql') }}"
+
+- name: "{{ role_name }} | setvars | set install_version"
+ set_fact:
+ install_version: "{{ lookup('vars', install_type + '_version') }}"
+
+- name: "{{ role_name }} | setvars | set install_tarball"
+ set_fact:
+ install_tarball: "{{ lookup('vars', install_type + '_tarball') }}"
+
+- name: "{{ role_name }} | setvars | set install_src"
+ set_fact:
+ install_src: "{{ lookup('vars', install_type + '_src') }}"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/verify.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/verify.yml
new file mode 100644
index 00000000..ca383d98
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/tasks/verify.yml
@@ -0,0 +1,27 @@
+---
+- name: "{{ role_name }} | verify | confirm primary is running and get the port"
+ shell: "{{ dbdeployer_sandbox_home_dir }}/multi_msb_{{ install_version|replace('.','_') }}/n1 -BNe'select @@port'"
+ register: primary_port
+
+- name: "{{ role_name }} | verify | confirm replica1 is running and get the port"
+ shell: "{{ dbdeployer_sandbox_home_dir }}/multi_msb_{{ install_version|replace('.','_') }}/n2 -BNe'select @@port'"
+ register: replica1_port
+
+- name: "{{ role_name }} | verify | confirm replica2 is running and get the port"
+ shell: "{{ dbdeployer_sandbox_home_dir }}/multi_msb_{{ install_version|replace('.','_') }}/n3 -BNe'select @@port'"
+ register: replica2_port
+
+- name: "{{ role_name }} | verify | confirm primary is running on expected port"
+ assert:
+ that:
+ - primary_port.stdout|int == 3307
+
+- name: "{{ role_name }} | verify | confirm replica1 is running on expected port"
+ assert:
+ that:
+ - replica1_port.stdout|int == 3308
+
+- name: "{{ role_name }} | verify | confirm replica2 is running on expected port"
+ assert:
+ that:
+ - replica2_port.stdout|int == 3309
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/templates/installed_file.j2 b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/templates/installed_file.j2
new file mode 100644
index 00000000..862a357a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/templates/installed_file.j2
@@ -0,0 +1 @@
+{{ dbdeployer_version }}
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/vars/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/vars/main.yml
new file mode 100644
index 00000000..e2673079
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_mysql/vars/main.yml
@@ -0,0 +1,31 @@
+---
+dbdeployer_install_dir: "{{ dbdeployer_home_dir }}/dbdeployer_{{ dbdeployer_version }}"
+dbdeployer_src: "https://github.com/datacharmer/dbdeployer/releases/download/v{{ dbdeployer_version }}/dbdeployer-{{ dbdeployer_version }}.linux.tar.gz"
+dbdeployer_installed_file: "{{ dbdeployer_home_dir }}/dbdeployer_installed"
+
+dbdeployer_sandbox_download_dir: "{{ home_dir }}/downloads"
+dbdeployer_sandbox_binary_dir: "{{ home_dir }}/opt/mysql"
+dbdeployer_sandbox_home_dir: "{{ home_dir }}/sandboxes"
+
+percona_mysql_repos:
+ - deb http://repo.percona.com/apt {{ ansible_lsb.codename }} main
+ - deb-src http://repo.percona.com/apt {{ ansible_lsb.codename }} main
+
+percona_mysql_packages:
+ - percona-server-client-{{ percona_client_version }}
+
+python_packages: [pymysql == 0.9.3]
+
+install_prereqs:
+ - libaio1
+ - libnuma1
+
+install_python_prereqs:
+ - python3-dev
+ - default-libmysqlclient-dev
+ - build-essential
+
+mysql_tarball: "mysql-{{ mysql_version }}-linux-glibc2.12-x86_64.tar.{{ mysql_compression_extension }}"
+mysql_src: "https://dev.mysql.com/get/Downloads/MySQL-{{ mysql_major_version }}/{{ mysql_tarball }}"
+mariadb_tarball: "mariadb-{{ mariadb_version }}-linux-x86_64.tar.gz"
+mariadb_src: "https://downloads.mariadb.com/MariaDB/mariadb-{{ mariadb_version }}/bintar-linux-x86_64/{{ mariadb_tarball }}"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml
new file mode 100644
index 00000000..229037c8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/handlers/main.yml
@@ -0,0 +1,5 @@
+- name: delete temporary directory
+ include_tasks: default-cleanup.yml
+
+- name: delete temporary directory (windows)
+ include_tasks: windows-cleanup.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml
new file mode 100644
index 00000000..39872d74
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default-cleanup.yml
@@ -0,0 +1,5 @@
+- name: delete temporary directory
+ file:
+ path: "{{ remote_tmp_dir }}"
+ state: absent
+ no_log: yes
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml
new file mode 100644
index 00000000..1e0f51b8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/default.yml
@@ -0,0 +1,11 @@
+- name: create temporary directory
+ tempfile:
+ state: directory
+ suffix: .test
+ register: remote_tmp_dir
+ notify:
+ - delete temporary directory
+
+- name: record temporary directory
+ set_fact:
+ remote_tmp_dir: "{{ remote_tmp_dir.path }}"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml
new file mode 100644
index 00000000..93d786f0
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/setup_remote_tmp_dir/tasks/main.yml
@@ -0,0 +1,15 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- name: make sure we have the ansible_os_family and ansible_distribution_version facts
+ setup:
+ gather_subset: distribution
+ when: ansible_facts == {}
+
+- include_tasks: "{{ lookup('first_found', files)}}"
+ vars:
+ files:
+ - "{{ ansible_os_family | lower }}.yml"
+ - "default.yml"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/defaults/main.yml
new file mode 100644
index 00000000..b6ae7806
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/defaults/main.yml
@@ -0,0 +1,17 @@
+---
+# defaults file for test_mysql_db
+mysql_user: root
+mysql_password: msandbox
+mysql_primary_port: 3307
+
+db_name: 'data'
+db_name2: 'data2'
+db_user1: 'datauser1'
+db_user2: 'datauser2'
+
+tmp_dir: '/tmp'
+db_latin1_name: 'db_latin1'
+file4: 'latin1_file'
+
+user_name_1: 'db_user1'
+user_password_1: 'gadfFDSdtTU^Sdfuj'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/meta/main.yml
new file mode 100644
index 00000000..f1174ff2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_mysql
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/config_overrides_defaults.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/config_overrides_defaults.yml
new file mode 100644
index 00000000..42d8fd70
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/config_overrides_defaults.yml
@@ -0,0 +1,108 @@
+- set_fact:
+ db_to_create: testdb1
+ config_file: "/root/.my1.cnf"
+ fake_port: 9999
+ fake_host: "blahblah.local"
+ include_dir: "/root/mycnf.d"
+
+- 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: Get pymysql version
+ shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+- name: Add blank line
+ shell: 'echo "" >> {{ config_file }}'
+ when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '>=')
+
+- name: Create include_dir
+ file:
+ path: '{{ include_dir }}'
+ state: directory
+ mode: '0777'
+ when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '>=')
+
+- name: Add include_dir
+ lineinfile:
+ path: '{{ config_file }}'
+ line: '!includedir {{ include_dir }}'
+ insertafter: EOF
+ when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '>=')
+
+- name: Create database using fake port to connect to, must fail
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_to_create }}'
+ state: present
+ check_implicit_admin: yes
+ config_file: '{{ config_file }}'
+ config_overrides_defaults: no
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ 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 }}'") or result.msg is search("Unknown MySQL server host '{{ fake_host }}'")
+
+# Clean up
+- name: Remove test db
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_to_create }}'
+ state: absent
+ check_implicit_admin: yes
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/encoding_dump_import.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/encoding_dump_import.yml
new file mode 100644
index 00000000..173386ca
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/encoding_dump_import.yml
@@ -0,0 +1,98 @@
+---
+
+- set_fact:
+ latin1_file1: "{{tmp_dir}}/{{file}}"
+
+- name: Deleting Latin1 encoded Database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_latin1_name }}'
+ state: absent
+
+- name: create Latin1 encoded database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_latin1_name }}'
+ state: present
+ encoding: latin1
+
+- name: create a table in Latin1 database
+ command: "{{ mysql_command }} {{ 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_command }} {{ db_latin1_name }} -e \"insert into testlatin1 value(47,'Amédée Bôlüt')\""
+
+- name: selecting table
+ command: "{{ mysql_command }} {{ db_latin1_name }} -e \"select * from testlatin1\""
+ register: output
+
+- name: Dumping a table in Latin1 database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: "{{ db_latin1_name }}"
+ encoding: latin1
+ target: "{{ latin1_file1 }}"
+ state: dump
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_latin1_name }}'
+ state: absent
+
+- name: Importing the latin1 mysql script
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ state: import
+ encoding: latin1
+ name: '{{ db_latin1_name }}'
+ target: "{{ latin1_file1 }}"
+
+- assert:
+ that:
+ - result is changed
+
+- name: check encoding of table
+ shell: "{{ mysql_command }} {{ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_latin1_name }}'
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/issue-28.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/issue-28.yml
new file mode 100644
index 00000000..871e92d0
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/issue-28.yml
@@ -0,0 +1,81 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: get server certificate
+ copy:
+ content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
+ dest: /tmp/cert.pem
+ delegate_to: localhost
+
+ - name: Drop mysql user if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+ ignore_errors: yes
+
+ - name: create user with ssl requirement
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ priv: '*.*:ALL,GRANT'
+ tls_requires:
+ SSL:
+
+ - name: attempt connection with newly created user (expect failure)
+ mysql_db:
+ name: '{{ db_name }}'
+ state: absent
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ when: pymysql_version.stdout != ""
+
+ - assert:
+ that:
+ - result is succeeded
+ when: pymysql_version.stdout == ""
+
+ - name: attempt connection with newly created user ignoring hostname
+ mysql_db:
+ name: '{{ db_name }}'
+ state: absent
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ check_hostname: no
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
+
+ - name: Drop mysql user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ host: 127.0.0.1
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/main.yml
new file mode 100644
index 00000000..139d5bbc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/main.yml
@@ -0,0 +1,326 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# 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: alias mysql command to include default options
+ set_fact:
+ mysql_command: "mysql -u{{ mysql_user }} -p{{ mysql_password }} -P{{ mysql_primary_port }} --protocol=tcp"
+
+- name: remove database if it exists
+ command: >
+ "{{ mysql_command }} -sse 'drop database {{ db_name }}'"
+ ignore_errors: True
+
+- name: make sure the test database is not there
+ command: "{{ mysql_command }} {{ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: present
+ 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_command }} -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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: absent
+ 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_command }} -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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: datanotvalid
+ state: present
+ encoding: notvalid
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: 'en{{ db_name }}'
+ state: present
+ encoding: utf8
+ 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_command }} -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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: 'en{{ db_name }}'
+ state: absent
+
+# ============================================================
+- name: test mysql_db using valid encoding binary (expect changed=true)
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: 'en{{ db_name }}'
+ state: present
+ encoding: binary
+ 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_command }} -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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: 'en{{ db_name }}'
+ state: absent
+
+# ============================================================
+- name: create user1 to access database dbuser1
+ mysql_user:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: user1
+ password: 'Hfd6fds^dfA8Ga'
+ priv: '*.*:ALL'
+ state: present
+
+- name: create database dbuser1 using user1
+ mysql_db:
+ login_user: user1
+ login_password: 'Hfd6fds^dfA8Ga'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_user1 }}'
+ state: present
+ 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_command }} -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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: user2
+ password: 'kjsfd&F7safjad'
+ priv: '*.*:SELECT'
+ state: present
+
+- name: create database dbuser2 using user2 with no privilege to create (expect failed=true)
+ mysql_db:
+ login_user: user2
+ login_password: 'kjsfd&F7safjad'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_user2 }}'
+ state: present
+ 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_command }} -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:
+ login_user: user2
+ login_password: 'kjsfd&F7safjad'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_user1 }}'
+ state: absent
+ 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_command }} -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:
+ login_user: user1
+ login_password: 'Hfd6fds^dfA8Ga'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_user1 }}'
+ state: absent
+ 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_command }} -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
+
+- include: issue-28.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/multi_db_create_delete.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/multi_db_create_delete.yml
new file mode 100644
index 00000000..6bada1c5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/multi_db_create_delete.yml
@@ -0,0 +1,626 @@
+# 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: present
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: present
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: present
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: present
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db2_name }}'
+ state: absent
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: present
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: present
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db3_name }}'
+ state: dump
+ target: '{{ dump1_file }}'
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - "{{ db1_name }}"
+ - "{{ db4_name }}"
+ - "{{ db3_name }}"
+ state: dump
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - "{{ db4_name }}"
+ - "{{ db5_name }}"
+ state: dump
+ target: "{{ dump1_file }}"
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: dump
+ target: '{{ dump1_file }}'
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: all
+ state: dump
+ target: '{{ dump2_file }}'
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: absent
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ state: absent
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db2_name }}'
+ - '{{ db4_name }}'
+ state: absent
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db2_name }}'
+ - '{{ db4_name }}'
+ state: absent
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db1_name }}'
+ - '{{ db2_name }}'
+ - '{{ db3_name }}'
+ - '{{ db4_name }}'
+ - '{{ db5_name }}'
+ state: absent
+ 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_command }} \"-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
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml
new file mode 100644
index 00000000..1de74395
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_db/tasks/state_dump_import.yml
@@ -0,0 +1,459 @@
+# 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_user }}'
+ password: '{{ db_user_unsafe_password }}'
+ priv: '*.*:ALL'
+ state: present
+
+- name: state dump/import - create database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: present
+ check_implicit_admin: yes
+
+- name: create database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: present
+ check_implicit_admin: no
+
+- name: state dump/import - create table department
+ command: "{{ mysql_command }} {{ db_name }} \"-e create table department(id int, name varchar(100))\""
+
+- name: state dump/import - create table employee
+ command: "{{ mysql_command }} {{ db_name }} \"-e create table employee(id int, name varchar(100))\""
+
+- name: state dump/import - insert data into table employee
+ command: "{{ mysql_command }} {{ db_name }} \"-e insert into employee value(47,'Joe Smith')\""
+
+- name: state dump/import - insert data into table department
+ command: "{{ mysql_command }} {{ 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 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ unsafe_login_password: yes
+ name: '{{ db_name }}'
+ state: dump
+ target: '{{ db_file_name }}'
+ ignore_tables:
+ - "{{ db_name }}.department"
+ 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 --host=127.0.0.1 --port={{ mysql_primary_port }} {{ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: "{{ db_name }},{{ db_name2 }}"
+ state: dump
+ target: '{{ dump_file1 }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db_name }}'
+ - '{{ db_name2 }}'
+ state: dump
+ target: '{{ dump_file2 }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name:
+ - '{{ db_name }}'
+ - '{{ db_name2 }}'
+ state: dump
+ target: '{{ dump_file2 }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: absent
+
+- name: remove database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: absent
+
+- 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 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ unsafe_login_password: yes
+ name: '{{ db_name }}'
+ state: import
+ target: '{{ db_file_name }}'
+ use_shell: yes
+ register: result
+
+- name: show the tables
+ command: "{{ mysql_command }} {{ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: import
+ target: '{{ dump_file1 }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: absent
+
+- name: run command to list databases
+ command: "{{ mysql_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: import
+ target: '{{ dump_file2 }}'
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: import
+ target: '{{ dump_file2 }}'
+ 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_command }} \"-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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: dump
+ target: '{{ db_file_name }}'
+ 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_command }} {{ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: import
+ target: '{{ db_file_name }}'
+ register: result
+
+- name: assert output message restore the database
+ assert:
+ that:
+ - "result.changed == true"
+
+- name: select data from table employee
+ command: "{{ mysql_command }} {{ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: import
+ target: '{{ wrong_sql_file }}'
+ force: no
+ register: result
+ ignore_errors: yes
+
+- assert:
+ that:
+ - result.failed == true
+
+- name: try to import with force parameter
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: import
+ target: '{{ wrong_sql_file }}'
+ force: yes
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+##########
+# Clean up
+##########
+
+- name: remove database name
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: absent
+
+- name: remove database
+ mysql_db:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name2 }}'
+ state: absent
+
+- 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
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/defaults/main.yml
new file mode 100644
index 00000000..e1b932cc
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/defaults/main.yml
@@ -0,0 +1,11 @@
+---
+# defaults file for test_mysql_info
+mysql_user: root
+mysql_password: msandbox
+mysql_host: 127.0.0.1
+mysql_primary_port: 3307
+
+db_name: data
+
+user_name_1: 'db_user1'
+user_password_1: 'gadfFDSdtTU^Sdfuj'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/meta/main.yml
new file mode 100644
index 00000000..a7ace5d1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/meta/main.yml
@@ -0,0 +1,3 @@
+dependencies:
+ - setup_mysql
+ - setup_remote_tmp_dir
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/issue-28.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/issue-28.yml
new file mode 100644
index 00000000..955683d4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/issue-28.yml
@@ -0,0 +1,78 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: get server certificate
+ copy:
+ content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
+ dest: /tmp/cert.pem
+ delegate_to: localhost
+
+ - name: Drop mysql user if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+ ignore_errors: yes
+
+ - name: create user with ssl requirement
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ tls_requires:
+ SSL:
+
+ - name: attempt connection with newly created user (expect failure)
+ mysql_info:
+ filter: version
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ when: pymysql_version.stdout != ""
+
+ - assert:
+ that:
+ - result is succeeded
+ when: pymysql_version.stdout == ""
+
+ - name: attempt connection with newly created user ignoring hostname
+ mysql_info:
+ filter: version
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ check_hostname: no
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
+
+ - name: Drop mysql user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ host: 127.0.0.1
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/main.yml
new file mode 100644
index 00000000..13ddbcb2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/tasks/main.yml
@@ -0,0 +1,193 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# 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
+#
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # 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: '{{ mysql_user }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ 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: '{{ mysql_user }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ 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:
+ <<: *mysql_params
+ register: result
+
+ - assert:
+ that:
+ - result.changed == false
+ - result.version != {}
+
+ # Test excluding
+ - name: Collect all info except settings and users
+ mysql_info:
+ <<: *mysql_params
+ 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:
+ <<: *mysql_params
+ 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:
+ <<: *mysql_params
+ 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:
+ <<: *mysql_params
+ name: acme
+
+ - name: Collect info about databases
+ mysql_info:
+ <<: *mysql_params
+ 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:
+ <<: *mysql_params
+ 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:
+ <<: *mysql_params
+ name: acme
+ state: absent
+
+ - include: issue-28.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/templates/my.cnf.j2 b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/templates/my.cnf.j2
new file mode 100644
index 00000000..7d159a21
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_info/templates/my.cnf.j2
@@ -0,0 +1,5 @@
+[client]
+user={{ mysql_user }}
+password={{ mysql_password }}
+host={{ mysql_host }}
+port={{ mysql_primary_port }}
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/defaults/main.yml
new file mode 100644
index 00000000..51a3bd7f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/defaults/main.yml
@@ -0,0 +1,13 @@
+mysql_user: root
+mysql_password: msandbox
+mysql_primary_port: 3307
+
+db_name: data
+test_db: testdb
+test_table1: test1
+test_table2: test2
+test_table3: test3
+test_script_path: /tmp/test.sql
+
+user_name_1: 'db_user1'
+user_password_1: 'gadfFDSdtTU^Sdfuj'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/meta/main.yml
new file mode 100644
index 00000000..ce08dc4a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+- setup_mysql
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/issue-28.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/issue-28.yml
new file mode 100644
index 00000000..e2b51a6b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/issue-28.yml
@@ -0,0 +1,78 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: get server certificate
+ copy:
+ content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
+ dest: /tmp/cert.pem
+ delegate_to: localhost
+
+ - name: Drop mysql user if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+ ignore_errors: yes
+
+ - name: create user with ssl requirement
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ tls_requires:
+ SSL:
+
+ - name: attempt connection with newly created user (expect failure)
+ mysql_query:
+ query: 'SHOW DATABASES'
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ when: pymysql_version.stdout != ""
+
+ - assert:
+ that:
+ - result is succeeded
+ when: pymysql_version.stdout == ""
+
+ - name: attempt connection with newly created user ignoring hostname
+ mysql_query:
+ query: 'SHOW DATABASES'
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ check_hostname: no
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
+
+ - name: Drop mysql user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ host: 127.0.0.1
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/main.yml
new file mode 100644
index 00000000..6d17308f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/main.yml
@@ -0,0 +1,9 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# mysql_query module initial CI tests
+- import_tasks: mysql_query_initial.yml
+
+- include: issue-28.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml
new file mode 100644
index 00000000..b01de55b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml
@@ -0,0 +1,301 @@
+# 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_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ 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: Create {{ test_table3 }}
+ mysql_query:
+ <<: *mysql_params
+ login_db: '{{ test_db }}'
+ query: 'CREATE TABLE {{ test_table3 }} (id int, story text)'
+
+ - name: Add data to {{ test_table3 }}
+ mysql_query:
+ <<: *mysql_params
+ login_db: '{{ test_db }}'
+ query: "INSERT INTO {{ test_table3 }} (id, story) VALUES (1, 'first'), (2, 'second')"
+
+ - name: Select from {{ test_table3 }}
+ mysql_query:
+ <<: *mysql_params
+ login_db: '{{ test_db }}'
+ query: 'SELECT id, story FROM {{ test_table3 }}'
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount == [2]
+
+ - name: Pass wrong query type
+ mysql_query:
+ <<: *mysql_params
+ login_db: '{{ test_db }}'
+ query: {'this type is': 'wrong'}
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('the query option value must be a string or list')
+
+ - name: Pass wrong query element
+ mysql_query:
+ <<: *mysql_params
+ login_db: '{{ test_db }}'
+ query:
+ - 'SELECT now()'
+ - {'this type is': 'wrong'}
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is search('the elements in query list must be strings')
+
+ - 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 }}']
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/defaults/main.yml
new file mode 100644
index 00000000..fefcf293
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/defaults/main.yml
@@ -0,0 +1,17 @@
+mysql_user: root
+mysql_password: msandbox
+mysql_host: 127.0.0.1
+mysql_primary_port: 3307
+mysql_replica1_port: 3308
+mysql_replica2_port: 3309
+
+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
+
+user_name_1: 'db_user1'
+user_password_1: 'gadfFDSdtTU^Sdfuj'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/meta/main.yml
new file mode 100644
index 00000000..36e111c3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+- setup_mysql
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/issue-28.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/issue-28.yml
new file mode 100644
index 00000000..c6668201
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/issue-28.yml
@@ -0,0 +1,79 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: get server certificate
+ copy:
+ content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
+ dest: /tmp/cert.pem
+ delegate_to: localhost
+
+ - name: Drop mysql user if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+ ignore_errors: yes
+
+ - name: create user with ssl requirement
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ priv: '*.*:ALL,GRANT'
+ tls_requires:
+ SSL:
+
+ - name: attempt connection with newly created user (expect failure)
+ mysql_replication:
+ mode: getmaster
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ when: pymysql_version.stdout != ""
+
+ - assert:
+ that:
+ - result is succeeded
+ when: pymysql_version.stdout == ""
+
+ - name: attempt connection with newly created user ignoring hostname
+ mysql_replication:
+ mode: getmaster
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ check_hostname: no
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
+
+ - name: Drop mysql user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ host: 127.0.0.1
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/main.yml
new file mode 100644
index 00000000..239598a6
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/main.yml
@@ -0,0 +1,21 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# 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
+
+# Tests of master_delay parameter:
+- import_tasks: mysql_replication_master_delay.yml
+
+# Tests of channel parameter:
+- import_tasks: mysql_replication_channel.yml
+
+# Tests of resetmaster mode:
+- import_tasks: mysql_replication_resetmaster_mode.yml
+
+- include: issue-28.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml
new file mode 100644
index 00000000..cb6d23a9
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml
@@ -0,0 +1,127 @@
+# 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)
+
+- vars:
+ mysql_params: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+
+ block:
+ # Get master log file and log pos:
+ - name: Get master status
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ mode: getmaster
+ register: mysql_primary_status
+
+ # Test changemaster mode:
+ - name: Run replication with channel
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica2_port }}'
+ mode: changemaster
+ master_host: '{{ mysql_host }}'
+ master_port: '{{ mysql_primary_port }}'
+ master_user: '{{ replication_user }}'
+ master_password: '{{ replication_pass }}'
+ master_log_file: '{{ mysql_primary_status.File }}'
+ master_log_pos: '{{ mysql_primary_status.Position }}'
+ channel: '{{ test_channel }}'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE='{{ mysql_primary_status.File }}',MASTER_LOG_POS={{ mysql_primary_status.Position }} FOR CHANNEL '{{ test_channel }}'"]
+
+ # Test startslave mode:
+ - name: Start slave with channel
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica2_port }}'
+ mode: startslave
+ channel: '{{ test_channel }}'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["START SLAVE FOR CHANNEL '{{ test_channel }}'"] or result.queries == ["START REPLICA FOR CHANNEL '{{ test_channel }}'"]
+
+ # Test getslave mode:
+ - name: Get standby status with channel
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica2_port }}'
+ mode: getslave
+ channel: '{{ test_channel }}'
+ register: slave_status
+
+ - assert:
+ that:
+ - slave_status.Is_Slave == true
+ - slave_status.Master_Host == '{{ mysql_host }}'
+ - slave_status.Exec_Master_Log_Pos == mysql_primary_status.Position
+ - slave_status.Master_Port == {{ mysql_primary_port }}
+ - slave_status.Last_IO_Errno == 0
+ - slave_status.Last_IO_Error == ''
+ - slave_status.Channel_Name == '{{ test_channel }}'
+ - slave_status is not changed
+ when: mysql8022_and_higher == false
+
+ - assert:
+ that:
+ - slave_status.Is_Slave == true
+ - slave_status.Source_Host == '{{ mysql_host }}'
+ - slave_status.Exec_Source_Log_Pos == mysql_primary_status.Position
+ - slave_status.Source_Port == {{ mysql_primary_port }}
+ - slave_status.Last_IO_Errno == 0
+ - slave_status.Last_IO_Error == ''
+ - slave_status.Channel_Name == '{{ test_channel }}'
+ - slave_status is not changed
+ when: mysql8022_and_higher == true
+
+
+ # Test stopslave mode:
+ - name: Stop slave with channel
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica2_port }}'
+ mode: stopslave
+ channel: '{{ test_channel }}'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["STOP SLAVE FOR CHANNEL '{{ test_channel }}'"] or result.queries == ["STOP REPLICA FOR CHANNEL '{{ test_channel }}'"]
+
+ # Test reset
+ - name: Reset slave with channel
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica2_port }}'
+ mode: resetslave
+ channel: '{{ test_channel }}'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["RESET SLAVE FOR CHANNEL '{{ test_channel }}'"] or result.queries == ["RESET REPLICA FOR CHANNEL '{{ test_channel }}'"]
+
+ # Test reset all
+ - name: Reset slave all with channel
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica2_port }}'
+ mode: resetslaveall
+ channel: '{{ test_channel }}'
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["RESET SLAVE ALL FOR CHANNEL '{{ test_channel }}'"] or result.queries == ["RESET REPLICA ALL FOR CHANNEL '{{ test_channel }}'"]
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml
new file mode 100644
index 00000000..e26dcd23
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml
@@ -0,0 +1,243 @@
+# 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)
+
+- vars:
+ mysql_params: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+
+ block:
+ - name: find out the database version
+ mysql_info:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: db
+
+ - name: Set mysql8022_and_higher
+ set_fact:
+ mysql8022_and_higher: false
+
+ - name: Set mysql8022_and_higher
+ set_fact:
+ mysql8022_and_higher: true
+ when:
+ - db.version.major > 8 or (db.version.major == 8 and db.version.minor > 0) or (db.version.major == 8 and db.version.minor == 0 and db.version.release >= 22)
+
+ - name: alias mysql command to include default options
+ set_fact:
+ mysql_command: "mysql -u{{ mysql_user }} -p{{ mysql_password }} --protocol=tcp"
+
+ # Preparation:
+ - name: Create user for replication
+ shell: "echo \"CREATE USER '{{ replication_user }}'@'localhost' IDENTIFIED WITH mysql_native_password BY '{{ replication_pass }}'; GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'localhost';\" | {{ mysql_command }} -P{{ mysql_primary_port }}"
+
+ - name: Create test database
+ mysql_db:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ state: present
+ name: '{{ test_db }}'
+
+ - name: Dump all databases from the primary
+ shell: 'mysqldump -u{{ mysql_user }} -p{{ mysql_password }} -h{{ mysql_host }} --protocol=tcp -P{{ mysql_primary_port }} --all-databases --ignore-table=mysql.innodb_index_stats --ignore-table=mysql.innodb_table_stats --master-data=2 > {{ dump_path }}'
+
+ - name: Restore the dump to replica1
+ shell: '{{ mysql_command }} -P{{ mysql_replica1_port }} < {{ dump_path }}'
+
+ - name: Restore the dump to replica2
+ shell: '{{ mysql_command }} -P{{ mysql_replica2_port }} < {{ dump_path }}'
+
+ # Test getmaster mode:
+ - name: Get master status
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ mode: getmaster
+ register: mysql_primary_status
+
+ - assert:
+ that:
+ - mysql_primary_status.Is_Master == true
+ - mysql_primary_status.Position != 0
+ - mysql_primary_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:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_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:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_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:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_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:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: changemaster
+ master_host: '{{ mysql_host }}'
+ master_port: '{{ mysql_primary_port }}'
+ master_user: '{{ replication_user }}'
+ master_password: '{{ replication_pass }}'
+ master_log_file: '{{ mysql_primary_status.File }}'
+ master_log_pos: '{{ mysql_primary_status.Position }}'
+ master_ssl_ca: ''
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["CHANGE MASTER TO MASTER_HOST='{{ mysql_host }}',MASTER_USER='{{ replication_user }}',MASTER_PASSWORD='********',MASTER_PORT={{ mysql_primary_port }},MASTER_LOG_FILE='{{ mysql_primary_status.File }}',MASTER_LOG_POS={{ mysql_primary_status.Position }},MASTER_SSL_CA=''"]
+
+ # Test startslave mode:
+ - name: Start slave
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: startslave
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["START SLAVE"] or result.queries == ["START REPLICA"]
+
+ # Test getslave mode:
+ - name: Get standby status
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: getslave
+ register: slave_status
+
+ - assert:
+ that:
+ - slave_status.Is_Slave == true
+ - slave_status.Master_Host == '{{ mysql_host }}'
+ - slave_status.Exec_Master_Log_Pos == mysql_primary_status.Position
+ - slave_status.Master_Port == {{ mysql_primary_port }}
+ - slave_status.Last_IO_Errno == 0
+ - slave_status.Last_IO_Error == ''
+ - slave_status is not changed
+ when: mysql8022_and_higher == false
+
+ - assert:
+ that:
+ - slave_status.Is_Slave == true
+ - slave_status.Source_Host == '{{ mysql_host }}'
+ - slave_status.Exec_Source_Log_Pos == mysql_primary_status.Position
+ - slave_status.Source_Port == {{ mysql_primary_port }}
+ - slave_status.Last_IO_Errno == 0
+ - slave_status.Last_IO_Error == ''
+ - slave_status is not changed
+ when: mysql8022_and_higher == true
+
+ # Create test table and add data to it:
+ - name: Create test table
+ shell: "echo \"CREATE TABLE {{ test_table }} (id int);\" | {{ mysql_command }} -P{{ mysql_primary_port }} {{ test_db }}"
+
+ - name: Insert data
+ shell: "echo \"INSERT INTO {{ test_table }} (id) VALUES (1), (2), (3); FLUSH LOGS;\" | {{ mysql_command }} -P{{ mysql_primary_port }} {{ 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:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: getslave
+ register: slave_status
+
+ # mysql_primary_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 != mysql_primary_status.Position
+ when: mysql8022_and_higher == false
+
+ - assert:
+ that:
+ - slave_status.Exec_Source_Log_Pos != mysql_primary_status.Position
+ when: mysql8022_and_higher == true
+
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: Start slave that is already running
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: startslave
+ fail_on_error: true
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+ when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '<=')
+
+ # Test stopslave mode:
+ - name: Stop slave
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: stopslave
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["STOP SLAVE"] or result.queries == ["STOP REPLICA"]
+
+ # Test stopslave mode:
+ - name: Stop slave that is no longer running
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: stopslave
+ fail_on_error: true
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+ when: (pymysql_version.stdout | default('1000', true)) is version('0.9.3', '<=')
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_master_delay.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_master_delay.yml
new file mode 100644
index 00000000..dcf977d1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_master_delay.yml
@@ -0,0 +1,45 @@
+# 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)
+
+- vars:
+ mysql_params: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+
+ block:
+
+ # Test master_delay mode:
+ - name: Run replication
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_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:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: startslave
+ register: result
+
+ # Check master_delay:
+ - name: Get standby status
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: getslave
+ register: slave_status
+
+ - assert:
+ that:
+ - slave_status.SQL_Delay == {{ test_master_delay }}
+ - slave_status is not changed
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetmaster_mode.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetmaster_mode.yml
new file mode 100644
index 00000000..36ed47d5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetmaster_mode.yml
@@ -0,0 +1,56 @@
+# 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)
+
+- vars:
+ mysql_params: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+
+ block:
+
+ # Needs for further tests:
+ - name: Stop slave
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: stopslave
+
+ - name: Reset slave all
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_replica1_port }}'
+ mode: resetslaveall
+
+ # Get master initial status:
+ - name: Get master status
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ mode: getmaster
+ register: mysql_primary_initial_status
+
+ # Test resetmaster mode:
+ - name: Reset master
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ mode: resetmaster
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result.queries == ["RESET MASTER"]
+
+ # Get master final status:
+ - name: Get master status
+ mysql_replication:
+ <<: *mysql_params
+ login_port: '{{ mysql_primary_port }}'
+ mode: getmaster
+ register: mysql_primary_final_status
+
+ - assert:
+ that:
+ - mysql_primary_initial_status.File != mysql_primary_final_status.File
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/defaults/main.yml
new file mode 100644
index 00000000..5cf9074b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/defaults/main.yml
@@ -0,0 +1,26 @@
+---
+# defaults file for test_mysql_user
+mysql_user: root
+mysql_password: msandbox
+mysql_host: 127.0.0.1
+mysql_primary_port: 3307
+
+db_name: 'data'
+user_name_1: 'db_user1'
+user_name_2: 'db_user2'
+user_name_3: 'db_user3'
+user_name_4: 'db_user4'
+
+user_password_1: 'gadfFDSdtTU^Sdfuj'
+user_password_2: 'jkFKUdfhdso78yi&td'
+user_password_3: 'jkFKUdfhdso78yi&tk'
+user_password_4: 's2R#7pLV31!ZJrXPa3'
+
+root_password: 'zevuR6oPh7'
+
+db_names:
+ - clientdb
+ - employeedb
+ - providerdb
+
+tmp_dir: '/tmp'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-function.sql b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-function.sql
new file mode 100644
index 00000000..d16118cd
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-function.sql
@@ -0,0 +1,8 @@
+USE foo;
+DELIMITER ;;
+CREATE FUNCTION `function` () RETURNS tinyint(4) DETERMINISTIC
+BEGIN
+ DECLARE NAME_FOUND tinyint DEFAULT 0;
+ RETURN NAME_FOUND;
+END;;
+DELIMITER ;
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-procedure.sql b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-procedure.sql
new file mode 100644
index 00000000..d0d45aa4
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/files/create-procedure.sql
@@ -0,0 +1,5 @@
+USE bar;
+DELIMITER ;;
+CREATE PROCEDURE `procedure` ()
+SELECT * FROM bar;;
+DELIMITER ;
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/meta/main.yml
new file mode 100644
index 00000000..a7ace5d1
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/meta/main.yml
@@ -0,0 +1,3 @@
+dependencies:
+ - setup_mysql
+ - setup_remote_tmp_dir
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_no_user.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_no_user.yml
new file mode 100644
index 00000000..98610842
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_no_user.yml
@@ -0,0 +1,25 @@
+# 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_command }} -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" }
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_user.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_user.yml
new file mode 100644
index 00000000..d95d9d21
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/assert_user.yml
@@ -0,0 +1,38 @@
+# 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_command }} -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_command }} -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
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/create_user.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/create_user.yml
new file mode 100644
index 00000000..790d9bb2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/create_user.yml
@@ -0,0 +1,40 @@
+# 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/>.
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - name: create mysql user {{user_name}}
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name }}'
+ password: '{{ user_password }}'
+ state: present
+ register: result
+
+ - name: assert output message mysql user was created
+ assert:
+ that:
+ - "result.changed == true"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-28.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-28.yml
new file mode 100644
index 00000000..a5b3d2a7
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-28.yml
@@ -0,0 +1,86 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: get server certificate
+ copy:
+ content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
+ dest: /tmp/cert.pem
+ delegate_to: localhost
+
+ - name: Drop mysql user if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+ ignore_errors: yes
+
+ - name: create user with ssl requirement
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ priv: '*.*:ALL,GRANT'
+ tls_requires:
+ SSL:
+
+ - name: attempt connection with newly created user (expect failure)
+ mysql_user:
+ name: "{{ user_name_2 }}"
+ password: "{{ user_password_2 }}"
+ host: 127.0.0.1
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ when: pymysql_version.stdout != ""
+
+ - assert:
+ that:
+ - result is succeeded
+ when: pymysql_version.stdout == ""
+
+ - name: attempt connection with newly created user ignoring hostname
+ mysql_user:
+ name: "{{ user_name_2 }}"
+ password: "{{ user_password_2 }}"
+ host: 127.0.0.1
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ check_hostname: no
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
+
+ - name: Drop mysql user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ item }}'
+ host: 127.0.0.1
+ state: absent
+ with_items:
+ - "{{ user_name_1 }}"
+ - "{{ user_name_2 }}"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-29511.yaml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-29511.yaml
new file mode 100644
index 00000000..31e6edfe
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-29511.yaml
@@ -0,0 +1,86 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ - name: Issue test setup - drop database
+ mysql_db:
+ <<: *mysql_params
+ name: "{{ item }}"
+ state: absent
+ loop:
+ - foo
+ - bar
+
+ - name: Issue test setup - create database
+ mysql_db:
+ <<: *mysql_params
+ name: "{{ item }}"
+ state: present
+ 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_command }} < {{ remote_tmp_dir }}/create-function.sql"
+
+ - name: Create procedure for test
+ shell: "{{ mysql_command }} < {{ remote_tmp_dir }}/create-procedure.sql"
+
+ - name: Create user with FUNCTION and PROCEDURE privileges
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ state: present
+ priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE'
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ state: present
+ priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE'
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ state: absent
+
+ - name: Issue test teardown - cleanup databases
+ mysql_db:
+ <<: *mysql_params
+ name: "{{ item }}"
+ state: absent
+ loop:
+ - foo
+ - bar
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-64560.yaml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-64560.yaml
new file mode 100644
index 00000000..46078b2f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/issue-64560.yaml
@@ -0,0 +1,45 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ - name: Set root password
+ mysql_user:
+ <<: *mysql_params
+ name: root
+ password: '{{ root_password }}'
+ check_implicit_admin: yes
+ register: result
+
+ - name: assert root password is changed
+ assert: { that: "result.changed == true" }
+
+ - name: Set root password again
+ mysql_user:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ root_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: root
+ password: '{{ root_password }}'
+ check_implicit_admin: yes
+ register: result
+
+ - name: Assert root password is not changed
+ assert: { that: "result.changed == false" }
+
+ - name: Set root password again
+ mysql_user:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ root_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ name: root
+ password: '{{ mysql_password }}'
+ check_implicit_admin: yes
+ register: result
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/main.yml
new file mode 100644
index 00000000..a744050a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/main.yml
@@ -0,0 +1,282 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# 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
+#
+- name: alias mysql command to include default options
+ set_fact:
+ mysql_command: "mysql -u{{ mysql_user }} -p{{ mysql_password }} -P{{ mysql_primary_port }} --protocol=tcp"
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ - include: issue-28.yml
+
+ - 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:
+ <<: *mysql_params
+ name: '{{user_name_1}}'
+ password: '{{user_password_1}}'
+ state: present
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: absent
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: absent
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: present
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ state: present
+ priv: '*.*:SELECT'
+ 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:
+ <<: *mysql_params
+ name: '{{ item[0] }}'
+ priv: '{{ item[1] }}.*:ALL'
+ append_privs: yes
+ password: '{{ user_password_1 }}'
+ with_nested:
+ - [ '{{ user_name_1 }}', '{{ user_name_2 }}']
+ - "{{db_names}}"
+
+ - name: show grants access for user1 on multiple database
+ command: "{{ mysql_command }} -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_command }} -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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ priv: '%db.*:SELECT'
+ append_privs: yes
+ password: '{{ user_password_1 }}'
+
+ - name: show grants access for user1 on multiple database
+ command: "{{ mysql_command }} -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: test priv type check, must fail
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ priv:
+ - unsuitable
+ - type
+ append_privs: yes
+ host_all: yes
+ password: '{{ user_password_1 }}'
+ register: result
+ ignore_errors: yes
+
+ - name: check fail message
+ assert:
+ that:
+ - result is failed
+ - result.msg is search('priv parameter must be str or dict')
+
+ - name: change user access to database via wildcard
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ priv: '%db.*:INSERT'
+ append_privs: yes
+ host_all: yes
+ password: '{{ user_password_1 }}'
+
+ - name: show grants access for user1 on multiple database
+ command: "{{ mysql_command }} -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 }}
+
+ # ============================================================
+ # Test plaintext and encrypted password scenarios.
+ #
+ - include: test_user_password.yml
+
+ # ============================================================
+ # Test plugin authentication scenarios.
+ #
+ - include: test_user_plugin_auth.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
+
+ # Test that append_privs will not attempt to make a change where current privileges are a superset of new privileges
+ # (https://github.com/ansible-collections/community.mysql/issues/69)
+ - include: test_priv_append.yml enable_check_mode=no
+ - include: test_priv_append.yml enable_check_mode=yes
+
+ # Tests for the TLS requires dictionary
+ - include: tls_requirements.yml
+
+ - import_tasks: issue-29511.yaml
+ tags:
+ - issue-29511
+
+ - import_tasks: issue-64560.yaml
+ tags:
+ - issue-64560
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/remove_user.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/remove_user.yml
new file mode 100644
index 00000000..45a0ad4c
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/remove_user.yml
@@ -0,0 +1,74 @@
+# 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/>.
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - name: remove mysql user {{user_name}}
+ mysql_user:
+ <<: *mysql_params
+ name: '{{user_name}}'
+ password: '{{user_password}}'
+ state: absent
+ 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:
+ <<: *mysql_params
+ name: ""
+ state: present
+ password: 'KJFDY&D*Sfuydsgf'
+
+ - name: remove blank mysql user with hosts=all (expect changed)
+ mysql_user:
+ <<: *mysql_params
+ user: ""
+ host_all: true
+ state: absent
+ 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:
+ <<: *mysql_params
+ user: ""
+ host_all: true
+ state: absent
+ register: result
+
+ - name: assert changed is true for removing all blank users
+ assert:
+ that:
+ - "result.changed == false"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/resource_limits.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/resource_limits.yml
new file mode 100644
index 00000000..736adb33
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/resource_limits.yml
@@ -0,0 +1,118 @@
+# test code for resource_limits parameter
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ - name: Drop mysql user {{ user_name_1 }} if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+
+ - name: Create mysql user {{ user_name_1 }} with resource limits in check_mode
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: present
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: present
+ resource_limits:
+ MAX_QUERIES_PER_HOUR: 10
+ MAX_CONNECTIONS_PER_HOUR: 5
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Check
+ mysql_query:
+ <<: *mysql_params
+ query: >
+ SELECT User FROM mysql.user WHERE User = '{{ user_name_1 }}' AND Host = 'localhost'
+ AND max_questions = 10 AND max_connections = 5
+ register: result
+
+ - assert:
+ that:
+ - result.rowcount[0] == 1
+
+ - name: Try to set the same limits again in check mode
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: present
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: present
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ state: present
+ resource_limits:
+ MAX_QUERIES_PER_HOUR: 5
+ MAX_CONNECTIONS_PER_HOUR: 5
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+
+ - name: Check
+ mysql_query:
+ <<: *mysql_params
+ query: >
+ SELECT User FROM mysql.user WHERE User = '{{ user_name_1 }}' AND Host = 'localhost'
+ AND max_questions = 5 AND max_connections = 5
+ 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')
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_append.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_append.yml
new file mode 100644
index 00000000..7dc15ca0
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_append.yml
@@ -0,0 +1,114 @@
+# Test code to ensure that appending privileges will not result in unnecessary changes when the current privileges
+# are a superset of the new privileges that have been defined.
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ - name: Create test databases
+ mysql_db:
+ <<: *mysql_params
+ name: '{{ item }}'
+ state: present
+ loop:
+ - data1
+ - data2
+
+ - name: Create a user with an initial set of privileges
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_4 }}'
+ password: '{{ user_password_4 }}'
+ priv: 'data1.*:SELECT,INSERT/data2.*:SELECT,DELETE'
+ state: present
+
+ - name: Run command to show privileges for user (expect privileges in stdout)
+ command: "{{ mysql_command }} -e \"SHOW GRANTS FOR '{{ user_name_4 }}'@'localhost'\""
+ register: result
+
+ - name: Assert that the initial set of privileges matches what is expected
+ assert:
+ that:
+ - "'GRANT SELECT, INSERT ON `data1`.*' in result.stdout"
+ - "'GRANT SELECT, DELETE ON `data2`.*' in result.stdout"
+
+ - name: Append privileges that are a subset of the current privileges, which should be a no-op
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_4 }}'
+ password: '{{ user_password_4 }}'
+ priv: 'data1.*:SELECT/data2.*:SELECT'
+ append_privs: yes
+ state: present
+ check_mode: '{{ enable_check_mode }}'
+ register: result
+
+ - name: Assert that there wasn't a change in permissions
+ assert:
+ that:
+ - "result.changed == false"
+
+ - name: Run command to show privileges for user (expect privileges in stdout)
+ command: "{{ mysql_command }} -e \"SHOW GRANTS FOR '{{ user_name_4 }}'@'localhost'\""
+ register: result
+
+ - name: Assert that the permissions still match what was originally granted
+ assert:
+ that:
+ - "'GRANT SELECT, INSERT ON `data1`.*' in result.stdout"
+ - "'GRANT SELECT, DELETE ON `data2`.*' in result.stdout"
+
+ - name: Append privileges that are not included in the current set of privileges to test that privileges are updated
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_4 }}'
+ password: '{{ user_password_4 }}'
+ priv: 'data1.*:DELETE/data2.*:SELECT'
+ append_privs: yes
+ state: present
+ check_mode: '{{ enable_check_mode }}'
+ register: result
+
+ - name: Assert that there was a change because permissions were added to data1.*
+ assert:
+ that:
+ - "result.changed == true"
+
+ - name: Run command to show privileges for user (expect privileges in stdout)
+ command: "{{ mysql_command }} -e \"SHOW GRANTS FOR '{{ user_name_4 }}'@'localhost'\""
+ register: result
+
+ - name: Assert that the permissions were changed as expected if check_mode is set to 'no'
+ assert:
+ that:
+ - "'GRANT SELECT, INSERT, DELETE ON `data1`.*' in result.stdout"
+ - "'GRANT SELECT, DELETE ON `data2`.*' in result.stdout"
+ when: enable_check_mode == 'no'
+
+ - name: Assert that the permissions were not actually changed if check_mode is set to 'yes'
+ assert:
+ that:
+ - "'GRANT SELECT, INSERT ON `data1`.*' in result.stdout"
+ - "'GRANT SELECT, DELETE ON `data2`.*' in result.stdout"
+ when: enable_check_mode == 'yes'
+
+ ##########
+ # Clean up
+ - name: Drop test databases
+ mysql_db:
+ <<: *mysql_params
+ name: '{{ item }}'
+ state: present
+ loop:
+ - data1
+ - data2
+
+ - name: Drop test user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_4 }}'
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_dict.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_dict.yml
new file mode 100644
index 00000000..ec7b05ee
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_priv_dict.yml
@@ -0,0 +1,55 @@
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # Tests for priv parameter value passed as a dict
+ - name: Create test databases
+ mysql_db:
+ <<: *mysql_params
+ name: '{{ item }}'
+ state: present
+ loop:
+ - data1
+ - data2
+
+ - name: Create user with privileges
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_3 }}'
+ password: '{{ user_password_3 }}'
+ priv:
+ "data1.*": "SELECT"
+ "data2.*": "SELECT"
+ state: present
+
+ - name: Run command to show privileges for user (expect privileges in stdout)
+ command: "{{ mysql_command }} -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:
+ <<: *mysql_params
+ name: '{{ item }}'
+ state: present
+ loop:
+ - data1
+ - data2
+
+ - name: Drop test user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_3 }}'
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_privs.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_privs.yml
new file mode 100644
index 00000000..4ed75d17
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_privs.yml
@@ -0,0 +1,186 @@
+# 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/>.
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - name: create user with basic select privileges
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ priv: '*.*:SELECT'
+ state: present
+ 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:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ priv: '*.*:{{current_privilege}}'
+ append_privs: '{{current_append_privs}}'
+ state: present
+ 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_command }} -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:
+ login_user: '{{ user_name_2 }}'
+ login_password: '{{ user_password_2 }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: present
+ ignore_errors: true
+
+ - name: run command to test that database was not created
+ command: "{{ mysql_command }} -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:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ priv: 'jmainguy.jmainguy:ALL'
+ state: present
+ register: result
+
+ - name: Assert that priv changed
+ assert:
+ that:
+ - "result.changed == true"
+
+ - name: Add privs to a specific table (expect ok)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ priv: 'jmainguy.jmainguy:ALL'
+ state: present
+ register: result
+
+ - name: Assert that priv did not change
+ assert:
+ that:
+ - "result.changed == false"
+
+ # ============================================================
+ - name: update user with all privileges
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ priv: '*.*:ALL'
+ state: present
+
+ # - include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'
+
+ - name: create database using user
+ mysql_db:
+ login_user: '{{ user_name_2 }}'
+ login_password: '{{ user_password_2 }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: present
+
+ - name: run command to test database was created using user new privileges
+ command: "{{ mysql_command }} -e \"SHOW CREATE DATABASE {{ db_name }}\""
+
+ - name: drop database using user
+ mysql_db:
+ login_user: '{{ user_name_2 }}'
+ login_password: '{{ user_password_2 }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ name: '{{ db_name }}'
+ state: absent
+
+ # ============================================================
+ - name: update user with a long privileges list (mysql has a special multiline grant output)
+ mysql_user:
+ <<: *mysql_params
+ 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
+ register: result
+
+ - name: Assert that priv changed
+ assert:
+ that:
+ - "result.changed == true"
+
+ - name: Test idempotency (expect ok)
+ mysql_user:
+ <<: *mysql_params
+ 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
+ register: result
+
+ - name: Assert that priv did not change
+ assert:
+ that:
+ - "result.changed == false"
+
+ - name: remove username
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_2 }}'
+ password: '{{ user_password_2 }}'
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_password.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_password.yml
new file mode 100644
index 00000000..f3b0e06a
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_password.yml
@@ -0,0 +1,269 @@
+# Tests scenarios for both plaintext and encrypted user passwords.
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ test_user_name: 'test_user_password'
+ initial_password: 'a5C8SN*DBa0%a75sGz'
+ initial_password_encrypted: '*0A12D4DF68C2A50716111674E565CA3D7D68B048'
+ new_password: 'NkN&qECv33vuQzf3bJg'
+ new_password_encrypted: '*B6559186FAD0953589F54383AD8EE9E9172296DA'
+ test_default_priv_type: 'SELECT'
+ test_default_priv: '*.*:{{ test_default_priv_type }}'
+
+ block:
+
+ # ============================================================
+ # Test setting plaintext password and changing it.
+ #
+
+ - name: Create user with initial password
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ initial_password }}'
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that a change occurred because the user was added
+ assert:
+ that:
+ - "result.changed == true"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version using the newly created used creds
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ initial_password }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Run mysql_user again without any changes
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ initial_password }}'
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that there weren't any changes because username/password didn't change
+ assert:
+ that:
+ - "result.changed == false"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Update the user password
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ new_password }}'
+ state: present
+ register: result
+
+ - name: Assert that a change occurred because the password was updated
+ assert:
+ that:
+ - "result.changed == true"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version data using the original password (should fail)
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ initial_password }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that the mysql_info module failed because we used the old password
+ assert:
+ that:
+ - "result.failed == true"
+
+ - name: Get the MySQL version data using the new password (should work)
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ new_password }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that the mysql_info module succeeded because we used the new password
+ assert:
+ that:
+ - "result.failed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ new_password }}
+
+ # ============================================================
+ # Test setting a plaintext password and then the same password encrypted to ensure there isn't a change detected.
+ #
+
+ - name: Create user with initial password
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ initial_password }}'
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that a change occurred because the user was added
+ assert:
+ that:
+ - "result.changed == true"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Pass in the same password as before, but in the encrypted form (no change expected)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ initial_password_encrypted }}'
+ encrypted: yes
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that there weren't any changes because username/password didn't change
+ assert:
+ that:
+ - "result.changed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ new_password }}
+
+ # ============================================================
+ # Test setting an encrypted password and then the same password in plaintext to ensure there isn't a change.
+ #
+
+ - name: Create user with initial password
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ initial_password_encrypted }}'
+ encrypted: yes
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that a change occurred because the user was added
+ assert:
+ that:
+ - "result.changed == true"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version data using the new creds
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ initial_password }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that the mysql_info module succeeded because we used the new password
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Pass in the same password as before, but in the encrypted form (no change expected)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ password: '{{ initial_password }}'
+ state: present
+ register: result
+
+ - name: Assert that there weren't any changes because username/password didn't change
+ assert:
+ that:
+ - "result.changed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ new_password }}
+
+ # ============================================================
+ # Test setting an empty password.
+ #
+
+ - name: Create user with empty password
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that a change occurred because the user was added
+ assert:
+ that:
+ - "result.changed == true"
+
+ - name: Get the MySQL version using an empty password for the newly created user
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: ''
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Get the MySQL version using an non-empty password (should fail)
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: 'some_password'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that mysql_info failed
+ assert:
+ that:
+ - "result.failed == true"
+
+ - name: Update the user without changing the password
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ priv: '{{ test_default_priv }}'
+ state: present
+ register: result
+
+ - name: Assert that the user wasn't changed because the password is still empty
+ assert:
+ that:
+ - "result.changed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password=''
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_plugin_auth.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_plugin_auth.yml
new file mode 100644
index 00000000..3ce9f1b8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/test_user_plugin_auth.yml
@@ -0,0 +1,386 @@
+# Test user plugin auth scenarios.
+
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ test_user_name: 'test_user_plugin_auth'
+ test_plugin_type: 'mysql_native_password'
+ test_plugin_hash: '*0CB5B86F23FDC24DB19A29B8854EB860CBC47793'
+ test_plugin_auth_string: 'Fdt8fd^34ds'
+ test_plugin_new_hash: '*E74368AC90460FA669F6D41BFB7F2A877DB73745'
+ test_plugin_new_auth_string: 'c$K01LsmK7nJnIR4!h'
+ test_default_priv_type: 'SELECT'
+ test_default_priv: '*.*:{{ test_default_priv_type }}'
+
+ block:
+
+ # ============================================================
+ # Test plugin auth initially setting a hash and then changing to a different hash.
+ #
+
+ - name: Create user with plugin auth (with hash string)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_hash_string: '{{ test_plugin_hash }}'
+ priv: '{{ test_default_priv }}'
+ register: result
+
+ - name: Get user information
+ command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ test_user_name }}'@'localhost'\""
+ register: show_create_user
+
+ - name: Check that the module made a change and that the expected plugin type is set
+ assert:
+ that:
+ - "result.changed == true"
+ - "'{{ test_plugin_type }}' in show_create_user.stdout"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version using the newly created creds
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ test_plugin_auth_string }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Update the user with a different hash
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_hash_string: '{{ test_plugin_new_hash }}'
+ register: result
+
+ - name: Check that the module makes the change because the hash changed
+ assert:
+ that:
+ - "result.changed == true"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Getting the MySQL info with the new password should work
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ test_plugin_new_auth_string }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ test_plugin_new_auth_string }}
+
+ # ============================================================
+ # Test plugin auth initially setting a hash and then switching to a plaintext auth string.
+ #
+
+ - name: Create user with plugin auth (with hash string)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_hash_string: '{{ test_plugin_hash }}'
+ priv: '{{ test_default_priv }}'
+ register: result
+
+ - name: Get user information
+ command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ test_user_name }}'@'localhost'\""
+ register: show_create_user
+
+ - name: Check that the module made a change and that the expected plugin type is set
+ assert:
+ that:
+ - "result.changed == true"
+ - "'{{ test_plugin_type }}' in show_create_user.stdout"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version using the newly created creds
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ test_plugin_auth_string }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Update the user with the same hash (no change expected)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_hash_string: '{{ test_plugin_hash }}'
+ register: result
+
+ - name: Check that the module doesn't make a change when the same hash is passed in
+ assert:
+ that:
+ - "result.changed == false"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Change the user using the same plugin, but switch to the same auth string in plaintext form
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_auth_string: '{{ test_plugin_auth_string }}'
+ register: result
+
+ # Expecting a change is currently by design (see comment in source).
+ - name: Check that the module did not change the password
+ assert:
+ that:
+ - "result.changed == true"
+
+ - name: Getting the MySQL info should still work
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ test_plugin_auth_string }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ test_plugin_auth_string }}
+
+ # ============================================================
+ # Test plugin auth initially setting a plaintext auth string and then switching to a hash.
+ #
+
+ - name: Create user with plugin auth (with auth string)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_auth_string: '{{ test_plugin_auth_string }}'
+ priv: '{{ test_default_priv }}'
+ register: result
+
+ - name: Get user information
+ command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ test_user_name }}'@'localhost'\""
+ register: show_create_user
+
+ - name: Check that the module made a change and that the expected plugin type is set
+ assert:
+ that:
+ - "result.changed == true"
+ - "'{{ test_plugin_type }}' in show_create_user.stdout"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version using the newly created creds
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ test_plugin_auth_string }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Update the user with the same auth string
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_auth_string: '{{ test_plugin_auth_string }}'
+ register: result
+
+ # This is the current expected behavior because there isn't a reliable way to hash the password in the mysql_user
+ # module in order to be able to compare this password with the stored hash. See the source for more info.
+ - name: The module should detect a change even though the password is the same
+ assert:
+ that:
+ - "result.changed == true"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Change the user using the same plugin, but switch to the same auth string in hash form
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ plugin_hash_string: '{{ test_plugin_hash }}'
+ register: result
+
+ - name: Check that the module did not change the password
+ assert:
+ that:
+ - "result.changed == false"
+
+ - name: Get the MySQL version using the newly created creds
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: '{{ test_plugin_auth_string }}'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ test_plugin_auth_string }}
+
+ # ============================================================
+ # Test plugin auth with an empty auth string.
+ #
+
+ - name: Create user with plugin auth (empty auth string)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ priv: '{{ test_default_priv }}'
+ register: result
+
+ - name: Get user information
+ command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ test_user_name }}'@'localhost'\""
+ register: show_create_user
+
+ - name: Check that the module made a change and that the expected plugin type is set
+ assert:
+ that:
+ - "result.changed == true"
+ - "'{{ test_plugin_type }}' in show_create_user.stdout"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Get the MySQL version using an empty password for the newly created user
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: ''
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that mysql_info was successful
+ assert:
+ that:
+ - "result.failed == false"
+
+ - name: Get the MySQL version using an non-empty password (should fail)
+ mysql_info:
+ login_user: '{{ test_user_name }}'
+ login_password: 'some_password'
+ login_host: '{{ mysql_host }}'
+ login_port: '{{ mysql_primary_port }}'
+ filter: version
+ register: result
+ ignore_errors: true
+
+ - name: Assert that mysql_info failed
+ assert:
+ that:
+ - "result.failed == true"
+
+ - name: Update the user without changing the auth mechanism
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ state: present
+ register: result
+
+ - name: Assert that the user wasn't changed because the auth string is still empty
+ assert:
+ that:
+ - "result.changed == false"
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ test_plugin_auth_string }}
+
+ # ============================================================
+ # Test plugin auth switching from one type of plugin to another without an auth string or hash. The only other
+ # plugins that are loaded by default are sha2*, but these aren't compatible with pymysql < 0.9, so skip these tests
+ # for those versions.
+ #
+ - name: Get pymysql version
+ shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: Test plugin auth switching which doesn't work on pymysql < 0.9
+ when: pymysql_version.stdout == "" or (pymysql_version.stdout != "" and pymysql_version.stdout is version('0.9', '>='))
+ block:
+
+ - name: Create user with plugin auth (empty auth string)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: '{{ test_plugin_type }}'
+ priv: '{{ test_default_priv }}'
+ register: result
+
+ - name: Get user information
+ command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ test_user_name }}'@'localhost'\""
+ register: show_create_user
+
+ - name: Check that the module made a change and that the expected plugin type is set
+ assert:
+ that:
+ - "result.changed == true"
+ - "'{{ test_plugin_type }}' in show_create_user.stdout"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ - name: Switch user to sha256_password auth plugin
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ test_user_name }}'
+ plugin: sha256_password
+ priv: '{{ test_default_priv }}'
+ register: result
+
+ - name: Get user information
+ command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ test_user_name }}'@'localhost'\""
+ register: show_create_user
+
+ - name: Check that the module made a change and that the expected plugin type is set
+ assert:
+ that:
+ - "result.changed == true"
+ - "'sha256_password' in show_create_user.stdout"
+
+ - include: assert_user.yml user_name={{ test_user_name }} priv={{ test_default_priv_type }}
+
+ # Cleanup
+ - include: remove_user.yml user_name={{ test_user_name }} user_password={{ test_plugin_auth_string }}
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/tls_requirements.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/tls_requirements.yml
new file mode 100644
index 00000000..8de9401e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_user/tasks/tls_requirements.yml
@@ -0,0 +1,195 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - name: find out the database version
+ mysql_info:
+ <<: *mysql_params
+ filter: version
+ register: db_version
+
+ - name: Drop mysql user {{ item }} if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ item }}'
+ state: absent
+ with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
+
+ - name: create user with TLS requirements in check mode (expect changed=true)
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ tls_requires:
+ SSL:
+ check_mode: yes
+ register: result
+
+ - name: Assert check mode user create reports changed state
+ assert:
+ that:
+ - result is changed
+
+ - include: assert_no_user.yml user_name={{user_name_1}}
+
+ - name: create user with TLS requirements state=present (expect changed=true)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ item[0] }}'
+ password: '{{ user_password_1 }}'
+ tls_requires: '{{ item[1] }}'
+ with_together:
+ - [ '{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
+ -
+ - SSL:
+ - X509:
+ - subject: '/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
+ cipher: 'ECDHE-ECDSA-AES256-SHA384'
+ issuer: '/CN=org/O=MyDom, Inc./C=US/ST=Oregon/L=Portland'
+
+ - block:
+ - name: retrieve TLS requiremets for users in old database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ item }}'@'localhost'\""
+ register: old_result
+ with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
+
+ - name: set old database separator
+ set_fact:
+ separator: '\n'
+ # Semantically: when mysql version <= 5.6 or MariaDB version <= 10.1
+ when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
+
+ - block:
+ - name: retrieve TLS requiremets for users in new database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ item }}'@'localhost'\""
+ register: new_result
+ with_items: ['{{ user_name_1 }}', '{{ user_name_2 }}', '{{ user_name_3 }}']
+
+ - name: set new database separator
+ set_fact:
+ separator: 'PASSWORD'
+ # Semantically: when mysql version >= 5.7 or MariaDB version >= 10.2
+ when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
+
+ - block:
+ - name: assert user1 TLS requirements
+ assert:
+ that:
+ - "'SSL' in reqs"
+ vars:
+ - reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_1) | first).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
+
+ - name: assert user2 TLS requirements
+ assert:
+ that:
+ - "'X509' in reqs"
+ vars:
+ - reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_2) | first).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
+
+ - name: assert user3 TLS requirements
+ assert:
+ that:
+ - "'/CN=alice/O=MyDom, Inc./C=US/ST=Oregon/L=Portland' in (reqs | select('contains', 'SUBJECT') | first)"
+ - "'/CN=org/O=MyDom, Inc./C=US/ST=Oregon/L=Portland' in (reqs | select('contains', 'ISSUER') | first)"
+ - "'ECDHE-ECDSA-AES256-SHA384' in (reqs | select('contains', 'CIPHER') | first)"
+ vars:
+ - reqs: "{{((old_result.results[0] is skipped | ternary(new_result, old_result)).results | selectattr('item', 'contains', user_name_3) | first).stdout.split('REQUIRE')[1].split(separator)[0].replace(\"' \", \"':\").split(\":\")}}"
+ # CentOS 6 uses an older version of jinja that does not provide the selectattr filter.
+ when: ansible_distribution != 'CentOS' or ansible_distribution_major_version != '6'
+
+ - name: modify user with TLS requirements state=present in check mode (expect changed=true)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ tls_requires:
+ X509:
+ check_mode: yes
+ register: result
+
+ - name: Assert check mode user update reports changed state
+ assert:
+ that:
+ - result is changed
+
+ - name: retrieve TLS requiremets for users in old database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
+ register: old_result
+ when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
+
+ - name: retrieve TLS requiremets for users in new database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
+ register: new_result
+ when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
+
+ - name: assert user1 TLS requirements was not changed
+ assert:
+ that: "'SSL' in reqs"
+ vars:
+ - reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
+
+ - name: modify user with TLS requirements state=present (expect changed=true)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ tls_requires:
+ X509:
+
+ - name: retrieve TLS requiremets for users in old database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
+ register: old_result
+ when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
+
+ - name: retrieve TLS requiremets for users in new database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
+ register: new_result
+ when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
+
+ - name: assert user1 TLS requirements
+ assert:
+ that: "'X509' in reqs"
+ vars:
+ - reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip()}}"
+
+ - name: remove TLS requiremets from user (expect changed=true)
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ password: '{{ user_password_1 }}'
+ tls_requires:
+
+ - name: retrieve TLS requiremets for users in old database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW GRANTS for '{{ user_name_1 }}'@'localhost'\""
+ register: old_result
+ when: db_version.version.major <= 5 and db_version.version.minor <= 6 or db_version.version.major == 10 and db_version.version.minor < 2
+
+ - name: retrieve TLS requiremets for users in new database version
+ command: "{{ mysql_command }} -L -N -s -e \"SHOW CREATE USER '{{ user_name_1 }}'@'localhost'\""
+ register: new_result
+ when: db_version.version.major == 5 and db_version.version.minor >= 7 or db_version.version.major > 5 and db_version.version.major < 10 or db_version.version.major == 10 and db_version.version.minor >= 2
+
+ - name: assert user1 TLS requirements
+ assert:
+ that: "'NONE' in reqs"
+ vars:
+ - reqs: "{{(old_result is skipped | ternary(new_result, old_result)).stdout.split('REQUIRE')[1].split(separator)[0].strip() | default('NONE') }}"
+
+ - 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 }}
+
+ - include: remove_user.yml user_name={{user_name_3}} user_password={{ user_password_1 }}
+
+ - include: assert_no_user.yml user_name={{user_name_1}}
+
+ - include: assert_no_user.yml user_name={{user_name_2}}
+
+ - include: assert_no_user.yml user_name={{user_name_3}}
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/defaults/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/defaults/main.yml
new file mode 100644
index 00000000..6d0e2ec8
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+# defaults file for test_mysql_variables
+mysql_user: root
+mysql_password: msandbox
+mysql_primary_port: 3307
+
+user_name_1: 'db_user1'
+user_password_1: 'gadfFDSdtTU^Sdfuj'
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/meta/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/meta/main.yml
new file mode 100644
index 00000000..f1174ff2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+ - setup_mysql
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_fail_msg.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_fail_msg.yml
new file mode 100644
index 00000000..4a840b9d
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_fail_msg.yml
@@ -0,0 +1,25 @@
+# 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"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var.yml
new file mode 100644
index 00000000..5419f34b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var.yml
@@ -0,0 +1,36 @@
+# 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_command }} \"-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"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var_output.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var_output.yml
new file mode 100644
index 00000000..f84a468b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/assert_var_output.yml
@@ -0,0 +1,40 @@
+# 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_command }} \"-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"
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/issue-28.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/issue-28.yml
new file mode 100644
index 00000000..056771e2
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/issue-28.yml
@@ -0,0 +1,79 @@
+---
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ # ============================================================
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: get server certificate
+ copy:
+ content: "{{ lookup('pipe', \"openssl s_client -starttls mysql -connect localhost:3307 -showcerts 2>/dev/null </dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p'\") }}"
+ dest: /tmp/cert.pem
+ delegate_to: localhost
+
+ - name: Drop mysql user if exists
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ state: absent
+ ignore_errors: yes
+
+ - name: create user with ssl requirement
+ mysql_user:
+ <<: *mysql_params
+ name: "{{ user_name_1 }}"
+ password: "{{ user_password_1 }}"
+ priv: '*.*:ALL,GRANT'
+ tls_requires:
+ SSL:
+
+ - name: attempt connection with newly created user (expect failure)
+ mysql_variables:
+ variable: '{{ set_name }}'
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is failed
+ when: pymysql_version.stdout != ""
+
+ - assert:
+ that:
+ - result is succeeded
+ when: pymysql_version.stdout == ""
+
+ - name: attempt connection with newly created user ignoring hostname
+ mysql_variables:
+ variable: '{{ set_name }}'
+ login_user: '{{ user_name_1 }}'
+ login_password: '{{ user_password_1 }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ ca_cert: /tmp/cert.pem
+ check_hostname: no
+ register: result
+ ignore_errors: yes
+
+ - assert:
+ that:
+ - result is succeeded or 'pymysql >= 0.7.11 is required' in result.msg
+
+ - name: Drop mysql user
+ mysql_user:
+ <<: *mysql_params
+ name: '{{ user_name_1 }}'
+ host: 127.0.0.1
+ state: absent
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/main.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/main.yml
new file mode 100644
index 00000000..9c4cd7d5
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/main.yml
@@ -0,0 +1,8 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+- import_tasks: mysql_variables.yml
+
+- include: issue-28.yml
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/mysql_variables.yml b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/mysql_variables.yml
new file mode 100644
index 00000000..8a25849e
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/integration/targets/test_mysql_variables/tasks/mysql_variables.yml
@@ -0,0 +1,379 @@
+# 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
+#
+- vars:
+ mysql_parameters: &mysql_params
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+
+ block:
+
+ - name: alias mysql command to include default options
+ set_fact:
+ mysql_command: "mysql -u{{ mysql_user }} -p{{ mysql_password }} -P{{ mysql_primary_port }} --protocol=tcp"
+
+ - set_fact:
+ set_name: 'version'
+
+ - name: read mysql variables (expect changed=false)
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+
+ - name: update mysql variable to same value (expect changed=false)
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '200'
+
+ - name: update mysql variable value (expect changed=true)
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: "200"
+
+ - name: update mysql variable value (expect changed=true)
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: 200
+
+ - name: update mysql variable value (expect changed=true)
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: max_connect_errors
+ value: "1024*4"
+ 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)
+ #
+ - shell: pip show pymysql | awk '/Version/ {print $2}'
+ register: pymysql_version
+
+ - name: set mysql variable value to a number out of range
+ mysql_variables:
+ <<: *mysql_params
+ variable: max_connect_errors
+ value: -1
+ register: oor_result
+ ignore_errors: true
+
+ - include: assert_var.yml changed=true output={{ oor_result }} var_name=max_connect_errors var_value=1
+ when: pymysql_version.stdout == ""
+
+ - include: assert_fail_msg.yml output={{ oor_result }} msg='Truncated incorrect'
+ when: pymysql_version.stdout != ""
+
+ # ============================================================
+ # Verify mysql_variable fails when setting an incorrect value (incorrect type)
+ #
+ - name: set mysql variable value to a non-valid value number
+ mysql_variables:
+ <<: *mysql_params
+ variable: max_connect_errors
+ value: TEST
+ register: nvv_result
+ ignore_errors: true
+
+ - include: assert_fail_msg.yml output={{ nvv_result }} msg='Incorrect argument type to variable'
+
+ # ============================================================
+ # Verify mysql_variable fails when setting an unknown variable
+ #
+ - name: set a non mysql variable
+ mysql_variables:
+ <<: *mysql_params
+ variable: my_sql_variable
+ value: ON
+ 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:
+ <<: *mysql_params
+ variable: character_set_system
+ value: utf16
+ 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
+ #
+ - set_fact:
+ set_name: wait_timeout
+ set_value: 77
+
+ - name: query mysql_variable using login_user and password_password
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: 20
+ register: result
+
+ - name: update mysql variable value using user login and password (expect changed=true)
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{set_name}}'
+ value: '{{set_value}}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: 'wrongpassword'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ variable: '{{ set_name }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: 'wrongpassword'
+ login_host: 127.0.0.1
+ login_port: '{{ mysql_primary_port }}'
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ login_user: '{{ mysql_user }}'
+ login_password: '{{ mysql_password }}'
+ login_host: '12.0.0.9'
+ login_port: '{{ mysql_primary_port }}'
+ variable: wait_timeout
+ connect_timeout: 5
+ register: result
+ ignore_errors: true
+
+ - include: assert_fail_msg.yml output={{ result }} msg='unable to connect to database'
+
+ - block:
+
+ #=========================================
+ # Check mode 'persist' and 'persist_only':
+ #
+ - name: update mysql variable value (expect changed=true) in persist mode
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '200'
+ mode: persist
+
+ - name: update mysql variable value (expect changed=true) in persist_only mode
+ mysql_variables:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ 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:
+ <<: *mysql_params
+ variable: '{{ set_name }}'
+ value: '{{ set_value }}'
+ mode: persist_only
+ register: result
+
+ - include: assert_var.yml changed=true output={{ result }} var_name={{ set_name }} var_value='{{ def_val }}'
+
+ when: mysql_version is not version('8.0', '<')
+
+ # Bugfix of https://github.com/ansible/ansible/issues/54239
+ # - name: set variable containing dot
+ # mysql_variables:
+ # <<: *mysql_params
+ # 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"]
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.10.txt b/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.10.txt
new file mode 100644
index 00000000..c0323aff
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.10.txt
@@ -0,0 +1,8 @@
+plugins/modules/mysql_db.py validate-modules:doc-elements-mismatch
+plugins/modules/mysql_db.py validate-modules:parameter-list-no-elements
+plugins/modules/mysql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/mysql_info.py validate-modules:doc-elements-mismatch
+plugins/modules/mysql_info.py validate-modules:parameter-list-no-elements
+plugins/modules/mysql_query.py validate-modules:parameter-list-no-elements
+plugins/modules/mysql_user.py validate-modules:undocumented-parameter
+plugins/modules/mysql_variables.py validate-modules:doc-required-mismatch
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.11.txt b/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.11.txt
new file mode 100644
index 00000000..c0323aff
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.11.txt
@@ -0,0 +1,8 @@
+plugins/modules/mysql_db.py validate-modules:doc-elements-mismatch
+plugins/modules/mysql_db.py validate-modules:parameter-list-no-elements
+plugins/modules/mysql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/mysql_info.py validate-modules:doc-elements-mismatch
+plugins/modules/mysql_info.py validate-modules:parameter-list-no-elements
+plugins/modules/mysql_query.py validate-modules:parameter-list-no-elements
+plugins/modules/mysql_user.py validate-modules:undocumented-parameter
+plugins/modules/mysql_variables.py validate-modules:doc-required-mismatch
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.9.txt b/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.9.txt
new file mode 100644
index 00000000..dabd55d3
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/sanity/ignore-2.9.txt
@@ -0,0 +1,3 @@
+plugins/modules/mysql_db.py validate-modules:use-run-command-not-popen
+plugins/modules/mysql_user.py validate-modules:parameter-type-not-in-doc
+plugins/modules/mysql_user.py validate-modules:undocumented-parameter
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/__init__.py b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/__init__.py
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/test_mysql.py b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/test_mysql.py
new file mode 100644
index 00000000..ac4de24f
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/module_utils/test_mysql.py
@@ -0,0 +1,24 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from ansible_collections.community.mysql.plugins.module_utils.mysql import get_server_version
+from ..utils import dummy_cursor_class
+
+
+@pytest.mark.parametrize(
+ 'cursor_return_version,cursor_return_type',
+ [
+ ('5.7.0-mysql', 'dict'),
+ ('8.0.0-mysql', 'list'),
+ ('10.5.0-mariadb', 'dict'),
+ ('10.5.1-mariadb', 'list'),
+ ]
+)
+def test_get_server_version(cursor_return_version, cursor_return_type):
+ """
+ Test that server versions are handled properly by get_server_version() whether they're returned as a list or dict.
+ """
+ cursor = dummy_cursor_class(cursor_return_version, cursor_return_type)
+ assert get_server_version(cursor) == cursor_return_version
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/__init__.py b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/__init__.py
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_replication.py b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_replication.py
new file mode 100644
index 00000000..27473b74
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_replication.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from ansible_collections.community.mysql.plugins.modules.mysql_replication import uses_replica_terminology
+from ..utils import dummy_cursor_class
+
+
+@pytest.mark.parametrize(
+ 'f_output,c_output,c_ret_type',
+ [
+ (False, '5.5.1-mysql', 'list'),
+ (False, '5.7.0-mysql', 'dict'),
+ (False, '8.0.0-mysql', 'list'),
+ (False, '8.0.11-mysql', 'dict'),
+ (False, '8.0.21-mysql', 'list'),
+ (False, '10.5.0-mariadb', 'dict'),
+ (True, '8.0.22-mysql', 'list'),
+ (True, '8.1.2-mysql', 'dict'),
+ (True, '9.0.0-mysql', 'list'),
+ (True, '10.5.1-mariadb', 'dict'),
+ (True, '10.6.0-mariadb', 'dict'),
+ (True, '11.5.1-mariadb', 'dict'),
+ ]
+)
+def test_uses_replica_terminology(f_output, c_output, c_ret_type):
+ cursor = dummy_cursor_class(c_output, c_ret_type)
+ assert uses_replica_terminology(cursor) == f_output
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_user.py b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_user.py
new file mode 100644
index 00000000..2dae9c59
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/modules/test_mysql_user.py
@@ -0,0 +1,33 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+
+from ansible_collections.community.mysql.plugins.modules.mysql_user import supports_identified_by_password
+from ..utils import dummy_cursor_class
+
+
+@pytest.mark.parametrize(
+ 'function_return,cursor_output,cursor_ret_type',
+ [
+ (True, '5.5.1-mysql', 'list'),
+ (True, '5.7.0-mysql', 'dict'),
+ (True, '10.5.0-mariadb', 'dict'),
+ (True, '10.5.1-mariadb', 'dict'),
+ (True, '10.6.0-mariadb', 'dict'),
+ (True, '11.5.1-mariadb', 'dict'),
+ (False, '8.0.22-mysql', 'list'),
+ (False, '8.1.2-mysql', 'dict'),
+ (False, '9.0.0-mysql', 'list'),
+ (False, '8.0.0-mysql', 'list'),
+ (False, '8.0.11-mysql', 'dict'),
+ (False, '8.0.21-mysql', 'list'),
+ ]
+)
+def test_supports_identified_by_password(function_return, cursor_output, cursor_ret_type):
+ """
+ Tests whether 'CREATE USER %s@%s IDENTIFIED BY PASSWORD %s' is supported, which is currently supported by everything
+ besides MySQL >= 8.0.
+ """
+ cursor = dummy_cursor_class(cursor_output, cursor_ret_type)
+ assert supports_identified_by_password(cursor) == function_return
diff --git a/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/utils.py b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/utils.py
new file mode 100644
index 00000000..7712d1cd
--- /dev/null
+++ b/collections-debian-merged/ansible_collections/community/mysql/tests/unit/plugins/utils.py
@@ -0,0 +1,19 @@
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+class dummy_cursor_class():
+ """Dummy class for returning an answer for SELECT VERSION()."""
+ def __init__(self, output, ret_val_type='dict'):
+ self.output = output
+ self.ret_val_type = ret_val_type
+
+ def execute(self, query):
+ pass
+
+ def fetchone(self):
+ if self.ret_val_type == 'dict':
+ return {'version': self.output}
+
+ elif self.ret_val_type == 'list':
+ return [self.output]