added more collections ans roles
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
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.21
|
||||
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.21
|
||||
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
|
||||
+56
@@ -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
|
||||
@@ -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
|
||||
@@ -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>.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"collection_info": {
|
||||
"namespace": "community",
|
||||
"name": "mysql",
|
||||
"version": "1.1.1",
|
||||
"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": "6816b7b3bf099cb031eeaee6ae91041052ed09bfe19d0644a8fc85e76572e296",
|
||||
"format": 1
|
||||
},
|
||||
"format": 1
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
# MySQL collection for Ansible
|
||||
[](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Plugins+CI") [](https://github.com/ansible-collections/community.mysql/actions?query=workflow%3A"Roles+CI") [](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
|
||||
version: 1.1.0
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,135 @@
|
||||
========================================
|
||||
Community MySQL Collection Release Notes
|
||||
========================================
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
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).
|
||||
@@ -0,0 +1,170 @@
|
||||
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'
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
fixes:
|
||||
- "/ansible_collections/community/mysql/::"
|
||||
@@ -0,0 +1,2 @@
|
||||
---
|
||||
requires_ansible: '>=2.9.10'
|
||||
@@ -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).
|
||||
@@ -0,0 +1,94 @@
|
||||
# -*- 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``.
|
||||
'''
|
||||
@@ -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))
|
||||
@@ -0,0 +1,135 @@
|
||||
# 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),
|
||||
)
|
||||
@@ -0,0 +1,722 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2012, Mark Theunissen <mark.theunissen@gmail.com>
|
||||
# Sponsored by Four Kitchens http://fourkitchens.com.
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_db
|
||||
short_description: Add or remove MySQL databases from a remote host
|
||||
description:
|
||||
- Add or remove MySQL databases from a remote host.
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- Name of the database to add or remove.
|
||||
- I(name=all) may only be provided if I(state) is C(dump) or C(import).
|
||||
- List of databases is provided with I(state=dump), I(state=present) and I(state=absent).
|
||||
- If I(name=all) it works like --all-databases option for mysqldump (Added in 2.0).
|
||||
required: true
|
||||
type: list
|
||||
elements: str
|
||||
aliases: [db]
|
||||
state:
|
||||
description:
|
||||
- The database state
|
||||
type: str
|
||||
default: present
|
||||
choices: ['absent', 'dump', 'import', 'present']
|
||||
collation:
|
||||
description:
|
||||
- Collation mode (sorting). This only applies to new table/databases and
|
||||
does not update existing ones, this is a limitation of MySQL.
|
||||
type: str
|
||||
default: ''
|
||||
encoding:
|
||||
description:
|
||||
- Encoding mode to use, examples include C(utf8) or C(latin1_swedish_ci),
|
||||
at creation of database, dump or importation of sql script.
|
||||
type: str
|
||||
default: ''
|
||||
target:
|
||||
description:
|
||||
- Location, on the remote host, of the dump file to read from or write to.
|
||||
- Uncompressed SQL files (C(.sql)) as well as bzip2 (C(.bz2)), gzip (C(.gz)) and
|
||||
xz (Added in 2.0) compressed files are supported.
|
||||
type: path
|
||||
single_transaction:
|
||||
description:
|
||||
- Execute the dump in a single transaction.
|
||||
type: bool
|
||||
default: no
|
||||
quick:
|
||||
description:
|
||||
- Option used for dumping large tables.
|
||||
type: bool
|
||||
default: yes
|
||||
ignore_tables:
|
||||
description:
|
||||
- A list of table names that will be ignored in the dump
|
||||
of the form database_name.table_name.
|
||||
type: list
|
||||
elements: str
|
||||
required: no
|
||||
default: []
|
||||
hex_blob:
|
||||
description:
|
||||
- Dump binary columns using hexadecimal notation.
|
||||
required: no
|
||||
default: no
|
||||
type: bool
|
||||
version_added: '0.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).
|
||||
required: no
|
||||
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).
|
||||
required: no
|
||||
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.
|
||||
required: no
|
||||
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.
|
||||
required: no
|
||||
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.
|
||||
required: no
|
||||
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:
|
||||
- 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),
|
||||
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()
|
||||
@@ -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).
|
||||
|
||||
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()
|
||||
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
---
|
||||
module: mysql_query
|
||||
short_description: Run MySQL queries
|
||||
description:
|
||||
- Runs arbitrary MySQL queries.
|
||||
- Pay attention, the module does not support check mode!
|
||||
All queries will be executed in autocommit mode.
|
||||
version_added: '0.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
|
||||
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) and not isinstance(query, 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()
|
||||
@@ -0,0 +1,566 @@
|
||||
#!/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
|
||||
master_ssl_ca:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_capath:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_cert:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_key:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_ssl_cipher:
|
||||
description:
|
||||
- Same as mysql variable.
|
||||
type: str
|
||||
master_auto_position:
|
||||
description:
|
||||
- Whether the host uses GTID based replication or not.
|
||||
type: bool
|
||||
master_use_gtid:
|
||||
description:
|
||||
- Configures the slave to use the MariaDB Global Transaction ID.
|
||||
- C(disabled) equals MASTER_USE_GTID=no command.
|
||||
- To find information about available values see
|
||||
U(https://mariadb.com/kb/en/library/change-master-to/#master_use_gtid).
|
||||
- Available since MariaDB 10.0.2.
|
||||
choices: [current_pos, slave_pos, disabled]
|
||||
type: str
|
||||
version_added: '0.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
|
||||
|
||||
executed_queries = []
|
||||
|
||||
|
||||
def get_master_status(cursor):
|
||||
cursor.execute("SHOW MASTER STATUS")
|
||||
masterstatus = cursor.fetchone()
|
||||
return masterstatus
|
||||
|
||||
|
||||
def get_slave_status(cursor, connection_name='', channel=''):
|
||||
if connection_name:
|
||||
query = "SHOW SLAVE '%s' STATUS" % connection_name
|
||||
else:
|
||||
query = "SHOW SLAVE STATUS"
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
cursor.execute(query)
|
||||
slavestatus = cursor.fetchone()
|
||||
return slavestatus
|
||||
|
||||
|
||||
def stop_slave(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "STOP SLAVE '%s'" % connection_name
|
||||
else:
|
||||
query = 'STOP SLAVE'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
stopped = True
|
||||
except mysql_driver.Warning as e:
|
||||
stopped = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="STOP SLAVE failed: %s" % to_native(e))
|
||||
stopped = False
|
||||
return stopped
|
||||
|
||||
|
||||
def reset_slave(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "RESET SLAVE '%s'" % connection_name
|
||||
else:
|
||||
query = 'RESET SLAVE'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
reset = True
|
||||
except mysql_driver.Warning as e:
|
||||
reset = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="RESET SLAVE failed: %s" % to_native(e))
|
||||
reset = False
|
||||
return reset
|
||||
|
||||
|
||||
def reset_slave_all(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "RESET SLAVE '%s' ALL" % connection_name
|
||||
else:
|
||||
query = 'RESET SLAVE ALL'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
reset = True
|
||||
except mysql_driver.Warning as e:
|
||||
reset = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="RESET SLAVE ALL failed: %s" % to_native(e))
|
||||
reset = False
|
||||
return reset
|
||||
|
||||
|
||||
def reset_master(module, cursor, fail_on_error=False):
|
||||
query = 'RESET MASTER'
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
reset = True
|
||||
except mysql_driver.Warning as e:
|
||||
reset = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="RESET MASTER failed: %s" % to_native(e))
|
||||
reset = False
|
||||
return reset
|
||||
|
||||
|
||||
def start_slave(module, cursor, connection_name='', channel='', fail_on_error=False):
|
||||
if connection_name:
|
||||
query = "START SLAVE '%s'" % connection_name
|
||||
else:
|
||||
query = 'START SLAVE'
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
try:
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
started = True
|
||||
except mysql_driver.Warning as e:
|
||||
started = False
|
||||
except Exception as e:
|
||||
if fail_on_error:
|
||||
module.fail_json(msg="START SLAVE failed: %s" % to_native(e))
|
||||
started = False
|
||||
return started
|
||||
|
||||
|
||||
def changemaster(cursor, chm, connection_name='', channel=''):
|
||||
if connection_name:
|
||||
query = "CHANGE MASTER '%s' TO %s" % (connection_name, ','.join(chm))
|
||||
else:
|
||||
query = 'CHANGE MASTER TO %s' % ','.join(chm)
|
||||
|
||||
if channel:
|
||||
query += " FOR CHANNEL '%s'" % channel
|
||||
|
||||
executed_queries.append(query)
|
||||
cursor.execute(query)
|
||||
|
||||
|
||||
def main():
|
||||
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)))
|
||||
|
||||
if mode in "getmaster":
|
||||
status = get_master_status(cursor)
|
||||
if not isinstance(status, dict):
|
||||
status = dict(Is_Master=False, msg="Server is not configured as mysql master")
|
||||
else:
|
||||
status['Is_Master'] = True
|
||||
module.exit_json(queries=executed_queries, **status)
|
||||
|
||||
elif mode in "getslave":
|
||||
status = get_slave_status(cursor, connection_name, channel)
|
||||
if not isinstance(status, dict):
|
||||
status = dict(Is_Slave=False, msg="Server is not configured as mysql slave")
|
||||
else:
|
||||
status['Is_Slave'] = True
|
||||
module.exit_json(queries=executed_queries, **status)
|
||||
|
||||
elif mode in "changemaster":
|
||||
chm = []
|
||||
result = {}
|
||||
if master_host is not None:
|
||||
chm.append("MASTER_HOST='%s'" % master_host)
|
||||
if master_user is not None:
|
||||
chm.append("MASTER_USER='%s'" % master_user)
|
||||
if master_password is not None:
|
||||
chm.append("MASTER_PASSWORD='%s'" % master_password)
|
||||
if master_port is not None:
|
||||
chm.append("MASTER_PORT=%s" % master_port)
|
||||
if master_connect_retry is not None:
|
||||
chm.append("MASTER_CONNECT_RETRY=%s" % master_connect_retry)
|
||||
if master_log_file is not None:
|
||||
chm.append("MASTER_LOG_FILE='%s'" % master_log_file)
|
||||
if master_log_pos is not None:
|
||||
chm.append("MASTER_LOG_POS=%s" % master_log_pos)
|
||||
if master_delay is not None:
|
||||
chm.append("MASTER_DELAY=%s" % master_delay)
|
||||
if relay_log_file is not None:
|
||||
chm.append("RELAY_LOG_FILE='%s'" % relay_log_file)
|
||||
if relay_log_pos is not None:
|
||||
chm.append("RELAY_LOG_POS=%s" % relay_log_pos)
|
||||
if master_ssl:
|
||||
chm.append("MASTER_SSL=1")
|
||||
if master_ssl_ca is not None:
|
||||
chm.append("MASTER_SSL_CA='%s'" % master_ssl_ca)
|
||||
if master_ssl_capath is not None:
|
||||
chm.append("MASTER_SSL_CAPATH='%s'" % master_ssl_capath)
|
||||
if master_ssl_cert is not None:
|
||||
chm.append("MASTER_SSL_CERT='%s'" % master_ssl_cert)
|
||||
if master_ssl_key is not None:
|
||||
chm.append("MASTER_SSL_KEY='%s'" % master_ssl_key)
|
||||
if master_ssl_cipher is not None:
|
||||
chm.append("MASTER_SSL_CIPHER='%s'" % master_ssl_cipher)
|
||||
if master_auto_position:
|
||||
chm.append("MASTER_AUTO_POSITION=1")
|
||||
if master_use_gtid is not None:
|
||||
chm.append("MASTER_USE_GTID=%s" % master_use_gtid)
|
||||
try:
|
||||
changemaster(cursor, chm, connection_name, channel)
|
||||
except mysql_driver.Warning as e:
|
||||
result['warning'] = to_native(e)
|
||||
except Exception as e:
|
||||
module.fail_json(msg='%s. Query == CHANGE MASTER TO %s' % (to_native(e), chm))
|
||||
result['changed'] = True
|
||||
module.exit_json(queries=executed_queries, **result)
|
||||
elif mode in "startslave":
|
||||
started = start_slave(module, cursor, connection_name, channel, fail_on_error)
|
||||
if started is True:
|
||||
module.exit_json(msg="Slave started ", changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.exit_json(msg="Slave already started (Or cannot be started)", changed=False, queries=executed_queries)
|
||||
elif mode in "stopslave":
|
||||
stopped = stop_slave(module, cursor, connection_name, channel, fail_on_error)
|
||||
if stopped is True:
|
||||
module.exit_json(msg="Slave stopped", changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.exit_json(msg="Slave already stopped", changed=False, queries=executed_queries)
|
||||
elif mode in "resetmaster":
|
||||
reset = reset_master(module, cursor, fail_on_error)
|
||||
if reset is True:
|
||||
module.exit_json(msg="Master reset", changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.exit_json(msg="Master already reset", changed=False, queries=executed_queries)
|
||||
elif mode in "resetslave":
|
||||
reset = reset_slave(module, cursor, connection_name, channel, fail_on_error)
|
||||
if reset is True:
|
||||
module.exit_json(msg="Slave reset", changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.exit_json(msg="Slave already reset", changed=False, queries=executed_queries)
|
||||
elif mode in "resetslaveall":
|
||||
reset = reset_slave_all(module, cursor, connection_name, channel, fail_on_error)
|
||||
if reset is True:
|
||||
module.exit_json(msg="Slave reset", changed=True, queries=executed_queries)
|
||||
else:
|
||||
module.exit_json(msg="Slave already reset", changed=False, queries=executed_queries)
|
||||
|
||||
warnings.simplefilter("ignore")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
||||
#!/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'
|
||||
|
||||
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()
|
||||
+7
@@ -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
|
||||
+7
@@ -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
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- setup_mariadb
|
||||
+21
@@ -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', '>=')
|
||||
+173
@@ -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
|
||||
+118
@@ -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"]
|
||||
+96
@@ -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"]
|
||||
+13
@@ -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.21
|
||||
mariadb_version: 10.5.4
|
||||
|
||||
mysql_base_port: 3306
|
||||
+6
@@ -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
|
||||
+15
@@ -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('.','_') }}"
|
||||
+11
@@ -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 }}"
|
||||
+59
@@ -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
|
||||
+11
@@ -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
|
||||
+28
@@ -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') }}"
|
||||
+27
@@ -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
|
||||
+1
@@ -0,0 +1 @@
|
||||
{{ dbdeployer_version }}
|
||||
+31
@@ -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 }}"
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
- name: delete temporary directory
|
||||
include_tasks: default-cleanup.yml
|
||||
|
||||
- name: delete temporary directory (windows)
|
||||
include_tasks: windows-cleanup.yml
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
- name: delete temporary directory
|
||||
file:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: absent
|
||||
no_log: yes
|
||||
+11
@@ -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 }}"
|
||||
+15
@@ -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"
|
||||
+17
@@ -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'
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
- setup_mysql
|
||||
+108
@@ -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
|
||||
+98
@@ -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
|
||||
+81
@@ -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
|
||||
+326
@@ -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
|
||||
+626
@@ -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
|
||||
+459
@@ -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
|
||||
+11
@@ -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'
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- setup_mysql
|
||||
- setup_remote_tmp_dir
|
||||
+78
@@ -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
|
||||
+193
@@ -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
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
[client]
|
||||
user={{ mysql_user }}
|
||||
password={{ mysql_password }}
|
||||
host={{ mysql_host }}
|
||||
port={{ mysql_primary_port }}
|
||||
+13
@@ -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'
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
- setup_mysql
|
||||
+78
@@ -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
|
||||
+9
@@ -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
|
||||
+301
@@ -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 }}']
|
||||
+17
@@ -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'
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
---
|
||||
dependencies:
|
||||
- setup_mysql
|
||||
+79
@@ -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
|
||||
+21
@@ -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
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
# 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 }}'"]
|
||||
|
||||
# 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
|
||||
|
||||
# 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 }}'"]
|
||||
|
||||
# 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 }}'"]
|
||||
|
||||
# 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 }}'"]
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
# 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: 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"]
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
- 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"]
|
||||
|
||||
# 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', '<=')
|
||||
+45
@@ -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
|
||||
+56
@@ -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
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
---
|
||||
# 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_password_1: 'gadfFDSdtTU^Sdfuj'
|
||||
user_password_2: 'jkFKUdfhdso78yi&td'
|
||||
user_password_3: 'jkFKUdfhdso78yi&tk'
|
||||
|
||||
root_password: 'zevuR6oPh7'
|
||||
|
||||
db_names:
|
||||
- clientdb
|
||||
- employeedb
|
||||
- providerdb
|
||||
|
||||
tmp_dir: '/tmp'
|
||||
+8
@@ -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 ;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
USE bar;
|
||||
DELIMITER ;;
|
||||
CREATE PROCEDURE `procedure` ()
|
||||
SELECT * FROM bar;;
|
||||
DELIMITER ;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
- setup_mysql
|
||||
- setup_remote_tmp_dir
|
||||
+25
@@ -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" }
|
||||
+38
@@ -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
|
||||
+40
@@ -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"
|
||||
+86
@@ -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 }}"
|
||||
+86
@@ -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
|
||||
+45
@@ -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
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
####################################################################
|
||||
# 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: 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 }}
|
||||
|
||||
# ============================================================
|
||||
# Update user password for a user.
|
||||
# Assert the user password is updated and old password can no longer be used.
|
||||
#
|
||||
#- include: user_password_update_test.yml
|
||||
|
||||
# ============================================================
|
||||
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege=SELECT current_append_privs=no
|
||||
|
||||
# ============================================================
|
||||
# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege=DROP current_append_privs=yes
|
||||
|
||||
# ============================================================
|
||||
# Assert create user with SELECT privileges, attempt to create database and update privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege='UPDATE,ALTER' current_append_privs=no
|
||||
|
||||
# ============================================================
|
||||
# Assert creating user with SELECT privileges, attempt to create database and append privileges to create database
|
||||
#
|
||||
- include: test_privs.yml current_privilege='INSERT,DELETE' current_append_privs=yes
|
||||
|
||||
# Tests for the priv parameter with dict value (https://github.com/ansible/ansible/issues/57533)
|
||||
- include: test_priv_dict.yml
|
||||
|
||||
# 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
|
||||
+74
@@ -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"
|
||||
+118
@@ -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')
|
||||
+55
@@ -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
|
||||
+186
@@ -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
|
||||
+195
@@ -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}}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
# test code update password for the mysql_user module
|
||||
# (c) 2014, Wayne Rosario <wrosario@ansible.com>
|
||||
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 dof the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
- 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:
|
||||
|
||||
# ============================================================
|
||||
# Update user password for a user.
|
||||
# Assert the user password is updated and old password can no longer be used.
|
||||
#
|
||||
- name: create user1 state=present with a password
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: '{{ user_name_1 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
|
||||
- name: create user2 state=present with a password
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
|
||||
- name: store user2 grants with old password (mysql 5.7.6 and newer)
|
||||
command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ user_name_2 }}'@'localhost'\""
|
||||
register: user_password_old_create
|
||||
ignore_errors: yes
|
||||
|
||||
- name: store user2 grants with old password (mysql 5.7.5 and older)
|
||||
command: "{{ mysql_command }} -e \"SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost'\""
|
||||
register: user_password_old
|
||||
when: user_password_old_create is failed
|
||||
|
||||
- name: update user2 state=present with same password (expect changed=false)
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_2 }}'
|
||||
priv: '*.*:ALL'
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: assert output user2 was not updated
|
||||
assert:
|
||||
that:
|
||||
- "result.changed == false"
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'
|
||||
|
||||
- name: update user2 state=present with a new password (expect changed=true)
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: '{{ user_name_2 }}'
|
||||
password: '{{ user_password_1 }}'
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- include: assert_user.yml user_name={{user_name_2}} priv='ALL PRIVILEGES'
|
||||
|
||||
- name: store user2 grants with old password (mysql 5.7.6 and newer)
|
||||
command: "{{ mysql_command }} -e \"SHOW CREATE USER '{{ user_name_2 }}'@'localhost'\""
|
||||
register: user_password_new_create
|
||||
ignore_errors: yes
|
||||
|
||||
- name: store user2 grants with new password
|
||||
command: "{{ mysql_command }} -e SHOW GRANTS FOR '{{ user_name_2 }}'@'localhost'\""
|
||||
register: user_password_new
|
||||
when: user_password_new_create is failed
|
||||
|
||||
- name: assert output message password was update for user2 (mysql 5.7.6 and newer)
|
||||
assert:
|
||||
that:
|
||||
- "user_password_old_create.stdout != user_password_new_create.stdout"
|
||||
when: user_password_new_create is not failed
|
||||
|
||||
- name: assert output message password was update for user2 (mysql 5.7.5 and older)
|
||||
assert:
|
||||
that:
|
||||
- "user_password_old.stdout != user_password_new.stdout"
|
||||
when: user_password_new_create is failed
|
||||
|
||||
- name: create database using user2 and old password
|
||||
mysql_db:
|
||||
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
|
||||
register: result
|
||||
|
||||
- debug: var=result.msg
|
||||
- name: assert output message that database not create with old password
|
||||
assert:
|
||||
that:
|
||||
- "result.failed == true"
|
||||
|
||||
- name: create database using user2 and new password
|
||||
mysql_db:
|
||||
login_user: '{{ user_name_2 }}'
|
||||
login_password: '{{ user_password_1 }}'
|
||||
login_host: '{{ mysql_host }}'
|
||||
login_port: '{{ mysql_primary_port }}'
|
||||
name: '{{ db_name }}'
|
||||
state: present
|
||||
register: result
|
||||
|
||||
- name: assert output message that database is created with new password
|
||||
assert:
|
||||
that:
|
||||
- "result.changed == true"
|
||||
|
||||
- name: remove database
|
||||
mysql_db:
|
||||
<<: *mysql_params
|
||||
name: '{{ db_name }}'
|
||||
state: absent
|
||||
login_unix_socket: '{{ mysql_socket }}'
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_1}} user_password={{ user_password_1 }}
|
||||
|
||||
- include: remove_user.yml user_name={{user_name_2}} user_password={{ user_password_1 }}
|
||||
|
||||
- name: Create user with Fdt8fd^34ds using hash. (expect changed=true)
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: jmainguy
|
||||
password: '*0cb5b86f23fdc24db19a29b8854eb860cbc47793'
|
||||
encrypted: yes
|
||||
register: encrypt_result
|
||||
|
||||
- name: Check that the module made a change
|
||||
assert:
|
||||
that:
|
||||
- "encrypt_result.changed == True"
|
||||
|
||||
- name: See if the password needs to be updated. (expect changed=false)
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: jmainguy
|
||||
password: 'Fdt8fd^34ds'
|
||||
register: plain_result
|
||||
|
||||
- name: Check that the module did not change the password
|
||||
assert:
|
||||
that:
|
||||
- "plain_result.changed == False"
|
||||
|
||||
- name: Remove user (cleanup)
|
||||
mysql_user:
|
||||
<<: *mysql_params
|
||||
name: jmainguy
|
||||
state: absent
|
||||
+8
@@ -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'
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
- setup_mysql
|
||||
+25
@@ -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"
|
||||
+36
@@ -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"
|
||||
+40
@@ -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"
|
||||
+79
@@ -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
|
||||
+8
@@ -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
|
||||
+379
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
env:
|
||||
# Provide a list of OSes we want to use for testing.
|
||||
- distro: centos6
|
||||
- distro: centos7
|
||||
- distro: centos8
|
||||
- distro: debian9
|
||||
- distro: debian10
|
||||
# - distro: ubuntu1604
|
||||
- distro: ubuntu1804
|
||||
services:
|
||||
# Tell Travis to start Docker when it brings up an environment.
|
||||
- docker
|
||||
script:
|
||||
# Run tests.
|
||||
- ${PWD}/tests/test.sh
|
||||
notifications:
|
||||
# Notify Ansible Galaxy when a role builds successfully.
|
||||
webhooks: https://galaxy.ansible.com/api/v1/notifications/
|
||||
...
|
||||
@@ -0,0 +1,280 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) 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
|
||||
this service 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 make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. 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.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
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
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the 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 a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE 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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -0,0 +1,104 @@
|
||||
# [install-check_mk-server](https://galaxy.ansible.com/kso512/install-check_mk-server/)
|
||||
|
||||
[](https://travis-ci.org/kso512/install-check_mk-server)  [](https://www.gnu.org/software/bash/) [](http://commonmark.org) 
|
||||
|
||||
[](https://github.com/kso512/install-check_mk-server)    [](https://GitHub.com/Naereen/StrapDown.js/network/)
|
||||
|
||||
An [Ansible](https://www.ansible.com/) [Role](http://docs.ansible.com/ansible/playbooks_roles.html#roles) to install [Check_MK RAW](http://mathias-kettner.com/check_mk_introduction.html) and set up an initial site.
|
||||
|
||||
All tasks are tagged with `install-check-mk-server`.
|
||||
|
||||
**I do NOT recommend the default configuration for unprotected connection directly to the Internet, as the server configuration includes unencrypted HTTP access.**
|
||||
|
||||
Tested with [Travis continuous integration](https://travis-ci.org/) on the following distributions:
|
||||
|
||||
- [CentOS-6](https://wiki.centos.org/Manuals/ReleaseNotes/CentOS6.9)
|
||||
- [CentOS-7](https://wiki.centos.org/Manuals/ReleaseNotes/CentOS7)
|
||||
- [CentOS-8](https://wiki.centos.org/Manuals/ReleaseNotes/CentOS8.1905)
|
||||
- [Debian 9 "Stretch"](https://www.debian.org/releases/stretch/)
|
||||
- [Debian 10 "Buster"](https://www.debian.org/releases/buster/)
|
||||
- [Ubuntu 18.04 LTS "Bionic Beaver"](http://releases.ubuntu.com/bionic/)
|
||||
|
||||
## Requirements
|
||||
|
||||
Required on host that executes role with APT:
|
||||
|
||||
- python-apt (python 2)
|
||||
- python3-apt (python 3)
|
||||
|
||||
Required on host that executes role with YUM:
|
||||
|
||||
- EPEL
|
||||
- yum
|
||||
- perl-Net-SNMP (minimal CentOS7)
|
||||
|
||||
If the server has a firewall enabled, it may need to be altered to allow incoming packets on TCP port 80 for the web portal access, and/or TCP port 514, plus UDP ports 162 & 514 for event console input.
|
||||
|
||||
As with any modern Linux deployment, SELinux may come into play.
|
||||
|
||||
To fulfill these requirements, I recommend using another Ansible Role. For example, [this role from Jeff Geerling may be used to handle EPEL](https://galaxy.ansible.com/geerlingguy/repo-epel) if needed.
|
||||
|
||||
## Role Variables
|
||||
|
||||
To enable multi-distro support, the role defines distro-specific variables with the [`include_vars` and `with_first_found`](http://docs.ansible.com/ansible/include_vars_module.html) mechanisms.
|
||||
|
||||
### Defaults
|
||||
|
||||
| Variable | Description | Value |
|
||||
| -------- | ----------- | ----- |
|
||||
| install_check_mk_server_adminpw | Optional password for `cmkadmin` user | undefined |
|
||||
| install_check_mk_server_build | Build number included in RPM source filename | `38` |
|
||||
| install_check_mk_server_key_url | URL of Check_MK GPG key file to download | `https://checkmk.com/support/Check_MK-pubkey.gpg` |
|
||||
| install_check_mk_server_prereqs | List of packages to install before installing Check_MK RAW | `apache2` `apt-utils` `cron` `dpkg-sig` `python-passlib` |
|
||||
| install_check_mk_server_site | Name of initial Check_MK RAW 'site' to provision | `test` |
|
||||
| install_check_mk_server_source | Filename of the installation source | `check-mk-raw-{{ install_check_mk_server_version }}_0.{{ ansible_distribution_release }}_amd64.deb` |
|
||||
| install_check_mk_server_source_url | URL of Check_MK RAW installation file to download | `https://checkmk.com/support/{{ install_check_mk_server_version }}/{{ install_check_mk_server_source }}` |
|
||||
| install_check_mk_server_version | Version of Check_MK RAW to install | `1.6.0p19` |
|
||||
| install_check_mk_server_web_service | Name of the Apache2 service to control | `apache2` |
|
||||
|
||||
### CentOS Distro Overrides
|
||||
|
||||
| Variable | Description | Value |
|
||||
| -------- | ----------- | ----- |
|
||||
| install_check_mk_server_prereqs | List of packages to install before installing Check_MK RAW | `cronie` `python-passlib` |
|
||||
| install_check_mk_server_source | Filename of the installation source | `check-mk-raw-{{ install_check_mk_server_version }}-el{{ ansible_distribution_major_version }}-{{ install_check_mk_server_build }}.x86_64.rpm` |
|
||||
| install_check_mk_server_web_service | Name of the Apache2 service to control | `httpd` |
|
||||
|
||||
#### CentOS 8 Distro Overrides
|
||||
|
||||
| Variable | Description | Value |
|
||||
| -------- | ----------- | ----- |
|
||||
| install_check_mk_server_prereqs | List of packages to install before installing Check_MK RAW | `cronie` `python3-passlib` `graphviz-gd` |
|
||||
| install_check_mk_server_source | Filename of the installation source | `check-mk-raw-{{ install_check_mk_server_version }}-el{{ ansible_distribution_major_version }}-{{ install_check_mk_server_build }}.x86_64.rpm` |
|
||||
| install_check_mk_server_web_service | Name of the Apache2 service to control | `httpd` |
|
||||
|
||||
### Ubuntu 18.04 Distro Overrides
|
||||
|
||||
| Variable | Description | Value |
|
||||
| -------- | ----------- | ----- |
|
||||
| install_check_mk_server_prereqs | List of packages to install before installing Check_MK RAW | `apache2` `apt-utils` `aptitude` `cron` `dpkg-sig` `iproute2` `libfl2` `man` `python-passlib` `rsync` `xz-utils` |
|
||||
|
||||
## Dependencies
|
||||
|
||||
This role depends on none other.
|
||||
|
||||
## Example Playbook
|
||||
|
||||
Complete example:
|
||||
|
||||
- hosts: monitoring-servers
|
||||
roles:
|
||||
- { role: install-check_mk-server, install_check_mk_server_site: boom }
|
||||
|
||||
## License
|
||||
|
||||
[GNU General Public License version 2](https://www.gnu.org/licenses/gpl-2.0.txt)
|
||||
|
||||
## Author Information
|
||||
|
||||
> Chris Lindbergh @kso512 with contributions from Github users:
|
||||
|
||||
- sylekta
|
||||
- timorunge
|
||||
- judouk
|
||||
- JWhy
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user