diff --git a/.github/workflows/ansible-test-plugins.yml b/.github/workflows/ansible-test-plugins.yml index 0b6c184..efc1537 100644 --- a/.github/workflows/ansible-test-plugins.yml +++ b/.github/workflows/ansible-test-plugins.yml @@ -13,7 +13,7 @@ on: # yamllint disable-line rule:truthy - '.github/workflows/ansible-test-plugins.yml' schedule: - cron: '0 6 * * *' - workflow_dispatch: + jobs: sanity: @@ -22,9 +22,9 @@ jobs: strategy: matrix: ansible: + - stable-2.15 - stable-2.16 - stable-2.17 - - stable-2.18 - devel steps: # https://github.com/ansible-community/ansible-test-gh-action @@ -44,9 +44,9 @@ jobs: fail-fast: false matrix: ansible: + - stable-2.15 - stable-2.16 - stable-2.17 - - stable-2.18 - devel db_engine_name: - mysql @@ -54,8 +54,8 @@ jobs: db_engine_version: - '8.0.38' - '8.4.1' + - '10.5.25' - '10.11.8' - - '11.4.5' connector_name: - pymysql - mysqlclient @@ -87,10 +87,10 @@ jobs: exclude: - db_engine_name: mysql - db_engine_version: '10.11.8' + db_engine_version: '10.5.25' - db_engine_name: mysql - db_engine_version: '11.4.5' + db_engine_version: '10.11.8' - db_engine_name: mariadb db_engine_version: '8.0.38' @@ -119,13 +119,13 @@ jobs: - db_engine_version: '8.0.38' ansible: stable-2.17 - - db_engine_version: '10.11.8' + - db_engine_version: '10.5.25' ansible: stable-2.17 - db_engine_version: '8.0.38' ansible: devel - - db_engine_version: '10.11.8' + - db_engine_version: '10.5.25' ansible: devel - db_engine_version: '8.4.1' @@ -162,7 +162,7 @@ jobs: db_engine_version: '8.0.38' - connector_version: '1.1.1' - db_engine_version: '10.11.8' + db_engine_version: '10.5.25' services: db_primary: @@ -175,7 +175,7 @@ jobs: # We write our own health-cmd because the mariadb container does not # provide a healthcheck options: >- - --health-cmd "${{ matrix.db_engine_name == 'mysql' && 'mysqladmin' || 'mariadb-admin' }} ping -P 3306 -pmsandbox |grep alive || exit 1" + --health-cmd "mysqladmin ping -P 3306 -pmsandbox |grep alive || exit 1" --health-start-period 10s --health-interval 10s --health-timeout 5s @@ -189,7 +189,7 @@ jobs: ports: - 3308:3306 options: >- - --health-cmd "${{ matrix.db_engine_name == 'mysql' && 'mysqladmin' || 'mariadb-admin' }} ping -P 3306 -pmsandbox |grep alive || exit 1" + --health-cmd "mysqladmin ping -P 3306 -pmsandbox |grep alive || exit 1" --health-start-period 10s --health-interval 10s --health-timeout 5s @@ -203,7 +203,7 @@ jobs: ports: - 3309:3306 options: >- - --health-cmd "${{ matrix.db_engine_name == 'mysql' && 'mysqladmin' || 'mariadb-admin' }} ping -P 3306 -pmsandbox |grep alive || exit 1" + --health-cmd "mysqladmin ping -P 3306 -pmsandbox |grep alive || exit 1" --health-start-period 10s --health-interval 10s --health-timeout 5s @@ -282,9 +282,9 @@ jobs: fail-fast: true matrix: ansible: + - stable-2.15 - stable-2.16 - stable-2.17 - - stable-2.18 - devel python: - '3.8' diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b318076..76d83fe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,64 +6,6 @@ Community MySQL and MariaDB Collection Release Notes This changelog describes changes after version 2.0.0. -v3.13.0 -======= - -Release Summary ---------------- - -This is a minor release of the ``community.mysql`` collection. -This changelog contains all changes to the modules and plugins in this -collection that have been made after the previous release. - -Minor Changes -------------- - -- Integration tests for MariaDB 11.4 have replaced those for 10.5. The previous version is now 10.11. -- mysql_user - add ``locked`` option to lock/unlock users, this is mainly used to have users that will act as definers on stored procedures. - -Bugfixes --------- - -- mysql_db - fix dump and import to find MariaDB binaries (mariadb and mariadb-dump) when MariaDB 11+ is used and symbolic links to MySQL binaries are absent. - -v3.12.0 -======= - -Release Summary ---------------- - -This is a minor release of the ``community.mysql`` collection. -This changelog contains all changes to the modules and plugins in this -collection that have been made after the previous release. - -Minor Changes -------------- - -- mysql_db - added ``zstd`` (de)compression support for ``import``/``dump`` states (https://github.com/ansible-collections/community.mysql/issues/696). -- mysql_query - returns the ``execution_time_ms`` list containing execution time per query in milliseconds. - -v3.11.0 -======= - -Release Summary ---------------- - -This is a minor release of the ``community.mysql`` collection. - -This changelog contains all changes to the modules and plugins in this -collection that have been made after the previous release. - -Minor Changes -------------- - -- mysql_info - adds the count of tables for each database to the returned values. It is possible to exclude this new field using the ``db_table_count`` exclusion filter. (https://github.com/ansible-collections/community.mysql/pull/691) - -Bugfixes --------- - -- mysql_user,mysql_role - The sql_mode ANSI_QUOTES affects how the modules mysql_user and mysql_role compare the existing privileges with the configured privileges, as well as decide whether double quotes or backticks should be used in the GRANT statements. Pointing out in issue 671, the modules mysql_user and mysql_role allow users to enable/disable ANSI_QUOTES in session variable (within a DB session, the session variable always overwrites the global one). But due to the issue, the modules do not check for ANSI_MODE in the session variable, instead, they only check in the GLOBAL one.That behavior is not only limiting the users' flexibility, but also not allowing users to explicitly disable ANSI_MODE to work around such bugs like https://bugs.mysql.com/bug.php?id=115953. (https://github.com/ansible-collections/community.mysql/issues/671) - v3.10.3 ======= @@ -103,8 +45,8 @@ Release Summary This is a patch release of the ``community.mysql`` collection. Besides a bugfix, it contains an important upcoming breaking-change information. -Deprecated Features -------------------- +Breaking Changes / Porting Guide +-------------------------------- - mysql_user - the ``user`` alias of the ``name`` argument has been deprecated and will be removed in collection version 5.0.0. Use the ``name`` argument instead. @@ -133,8 +75,8 @@ Minor Changes - mysql_replication - Improve detection of IsReplica and IsPrimary by inspecting the dictionary returned from the SQL query instead of relying on variable types. This ensures compatibility with changes in the connector or the output of SHOW REPLICA STATUS and SHOW MASTER STATUS, allowing for easier maintenance if these change in the future. - mysql_user - Add salt parameter to generate static hash for `caching_sha2_password` and `sha256_password` plugins. -Deprecated Features -------------------- +Breaking Changes / Porting Guide +-------------------------------- - collection - support of mysqlclient connector is deprecated - use PyMySQL connector instead! We will stop testing against it in collection version 4.0.0 and remove the related code in 5.0.0 (https://github.com/ansible-collections/community.mysql/issues/654). - mysql_info - The ``users_info`` filter returned variable ``plugin_auth_string`` contains the hashed password and it's misleading, it will be removed from community.mysql 4.0.0. Use the `plugin_hash_string` return value instead (https://github.com/ansible-collections/community.mysql/pull/629). diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 6d946cc..06fb579 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -17,11 +17,9 @@ amitk79 amree Andersson007 andrewhowdencom -aneustroev ansibot anthonyxpalermo antonioribeiro -Aohzan apollo13 aquach arcmop @@ -35,8 +33,6 @@ baldpale banyek BarbzYHOOL Berbe -betanummeric -bigo8525 bizmate bjne bmalynovytch @@ -50,7 +46,6 @@ candeira caphrim007 cdalbergue checkphi -chriscroome chrismeyersfsu ChristopherGAndrews cmodijk @@ -61,14 +56,13 @@ CormacBracken cosmix cptMikky crashes -d-lee -d-rupp dagwieers damianmoore Davidffry denisemauldin -dennisurtubia diclophis +d-lee +d-rupp dmp1ce dnelson dramaley @@ -78,11 +72,9 @@ DSpeichert dungdm93 dwagelaar dylanjbarth -E-M einarc -elpavel +E-M eowin -eRadical Ernest0x esamattis Everspace @@ -90,30 +82,24 @@ F21 faitno felixfontein flatrocks -FlorianPerrot fourjay fraff -francescsanjuanmrf g00fy- geerlingguy georgeOsdDev ghjm ghost -GhostLyrics giacmir giorgio-v gkoller -gotmax23 gottwald gstorme gundalow hansbaer hchargois hluaces -hubiongithub hwali hyperfocus1338 -IBims1NicerTobi igormukhingmailcom imjoseangel infigoKriti @@ -178,8 +164,8 @@ markdorison markotitel marktheunissen markuman -matt-horwood-mayden mattclay +matt-horwood-mayden mavimo maxamillion maxbube @@ -198,15 +184,11 @@ mkrizek mmoya mohag mohsenSy -moledzki mpdehaan -MRMegaNova MRwangyd -mstinsky mverwijs mvgrimes mysqlbox -n-cc netmonk nhojpatrick nicolas-g @@ -220,9 +202,7 @@ organman91 p53 pakal paulbadcock -paulcampbell-ayroc pennycoders -perlun petoju petracvv pgrenaud @@ -243,14 +223,12 @@ richlv riupie rndmh3ro robertdebock -robertsilen robpblake rokka-n Roxyrob roysmith rsicart rthouvenin -rujschafer ruudk samccann samdoran @@ -264,7 +242,6 @@ shrikeh sivel skalfyfan skoriy88 -SoledaD208 sperantus spoyd steverweber @@ -285,22 +262,19 @@ time-palominodb timorunge Tomasthanes tomdymond -tompal3 Tronde tuhoanganh tvlooy tyll UncertaintyP unnecessary-username -v-zhuravlev vamshi8 vanne vdboor vmahadev -webknjaz +v-zhuravlev webmat wedi -wfelipew whysthatso willthames windowsansiblernew diff --git a/Makefile b/Makefile index b503e2f..5a11d1b 100644 --- a/Makefile +++ b/Makefile @@ -11,17 +11,6 @@ ifdef continue_on_errors _continue_on_errors = --continue-on-error endif -# Set command variables based on database engine -# Required for MariaDB 11+ which no longer includes mysql named compatible -# executable symlinks -ifeq ($(db_engine_name),mysql) - _command = mysqld - _health_cmd = mysqladmin -else - _command = mariadbd - _health_cmd = mariadb-admin -endif - .PHONY: test-integration test-integration: @echo -n $(db_engine_name) > tests/integration/db_engine_name @@ -40,9 +29,9 @@ test-integration: --env MYSQL_ROOT_PASSWORD=msandbox \ --network podman \ --publish 3307:3306 \ - --health-cmd '$(_health_cmd) ping -P 3306 -pmsandbox | grep alive || exit 1' \ + --health-cmd 'mysqladmin ping -P 3306 -pmsandbox | grep alive || exit 1' \ docker.io/library/$(db_engine_name):$(db_engine_version) \ - $(_command) + mysqld podman run \ --detach \ --replace \ @@ -51,9 +40,9 @@ test-integration: --env MYSQL_ROOT_PASSWORD=msandbox \ --network podman \ --publish 3308:3306 \ - --health-cmd '$(_health_cmd) ping -P 3306 -pmsandbox | grep alive || exit 1' \ + --health-cmd 'mysqladmin ping -P 3306 -pmsandbox | grep alive || exit 1' \ docker.io/library/$(db_engine_name):$(db_engine_version) \ - $(_command) + mysqld podman run \ --detach \ --replace \ @@ -62,9 +51,9 @@ test-integration: --env MYSQL_ROOT_PASSWORD=msandbox \ --network podman \ --publish 3309:3306 \ - --health-cmd '$(_health_cmd) ping -P 3306 -pmsandbox | grep alive || exit 1' \ + --health-cmd 'mysqladmin ping -P 3306 -pmsandbox | grep alive || exit 1' \ docker.io/library/$(db_engine_name):$(db_engine_version) \ - $(_command) + mysqld # Setup replication and restart containers using the same subshell to keep variables alive db_ver=$(db_engine_version); \ maj="$${db_ver%.*.*}"; \ diff --git a/README.md b/README.md index df2404f..1f5b47a 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,9 @@ Here is the table for the support timeline: ### ansible-core +- stable-2.15 - stable-2.16 - stable-2.17 -- stable-2.18 - current development version ### Python @@ -112,10 +112,10 @@ For MariaDB, only Long Term releases are tested. When multiple LTS are available - mariadb:10.3.34 (collection version < 3.5.1) - mariadb:10.4.24 (collection version >= 3.5.2, < 3.10.0) - mariadb:10.5.18 (collection version >= 3.5.2, < 3.10.0) -- mariadb:10.5.25 (collection version >= 3.10.0, <3.13.0) +- mariadb:10.5.25 (collection version >= 3.10.0) - mariadb:10.6.11 (collection version >= 3.5.2, < 3.10.0) - mariadb:10.11.8 (collection version >= 3.10.0) -- mariadb:11.4.5 (collection version >= 3.13.0) + ### Database connectors diff --git a/TESTING.md b/TESTING.md index 45e6bba..1a22832 100644 --- a/TESTING.md +++ b/TESTING.md @@ -65,8 +65,8 @@ The Makefile accept the following options - Choices: - "8.0.38" <- mysql - "8.4.1" <- mysql (NOT WORKING YET, ansible-test uses Ubuntu 20.04 which is too old to install mysql-community-client 8.4) + - "10.5.25" <- mariadb - "10.11.8" <- mariadb - - "11.4.5" <- mariadb - Description: The tag of the container to use for the service containers that will host a primary database and two replicas. Do not use short version, like `mysql:8` (don't do that) because our tests expect a full version to filter tests precisely. For instance: `when: db_version is version ('8.0.22', '>')`. You can use any tag available on [hub.docker.com/_/mysql](https://hub.docker.com/_/mysql) and [hub.docker.com/_/mariadb](https://hub.docker.com/_/mariadb) but GitHub Action will only use the versions listed above. - `connector_name` @@ -121,7 +121,7 @@ make ansible="stable-2.16" db_engine_name="mysql" db_engine_version="8.0.31" con make ansible="stable-2.17" db_engine_name="mysql" db_engine_version="8.0.31" connector_name="mysqlclient" connector_version="2.0.3" target="test_mysql_query" keep_containers_alive=1 continue_on_errors=1 # If your system has an usupported version of Python: -make local_python_version="3.10" ansible="stable-2.17" db_engine_name="mariadb" db_engine_version="11.4.5" connector_name="pymysql" connector_version="1.0.2" +make local_python_version="3.10" ansible="stable-2.17" db_engine_name="mariadb" db_engine_version="10.6.11" connector_name="pymysql" connector_version="1.0.2" ``` diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 5ec7dc9..ea7c09f 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -99,6 +99,13 @@ releases: release_date: '2022-04-26' 3.10.0: changes: + breaking_changes: + - collection - support of mysqlclient connector is deprecated - use PyMySQL + connector instead! We will stop testing against it in collection version 4.0.0 + and remove the related code in 5.0.0 (https://github.com/ansible-collections/community.mysql/issues/654). + - mysql_info - The ``users_info`` filter returned variable ``plugin_auth_string`` + contains the hashed password and it's misleading, it will be removed from + community.mysql 4.0.0. Use the `plugin_hash_string` return value instead (https://github.com/ansible-collections/community.mysql/pull/629). bugfixes: - mysql_info - Add ``plugin_hash_string`` to ``users_info`` filter's output. The existing ``plugin_auth_string`` contained the hashed password and thus @@ -115,13 +122,6 @@ releases: avoid versions 3.8.0 to 3.9.0 (https://github.com/ansible-collections/community.mysql/pull/642). - mysql_user - add correct ``ed25519`` auth plugin handling (https://github.com/ansible-collections/community.mysql/issues/6). - mysql_variables - fix the module always changes on boolean values (https://github.com/ansible-collections/community.mysql/issues/652). - deprecated_features: - - collection - support of mysqlclient connector is deprecated - use PyMySQL - connector instead! We will stop testing against it in collection version 4.0.0 - and remove the related code in 5.0.0 (https://github.com/ansible-collections/community.mysql/issues/654). - - mysql_info - The ``users_info`` filter returned variable ``plugin_auth_string`` - contains the hashed password and it's misleading, it will be removed from - community.mysql 4.0.0. Use the `plugin_hash_string` return value instead (https://github.com/ansible-collections/community.mysql/pull/629). minor_changes: - mysql_info - Add ``tls_requires`` returned value for the ``users_info`` filter (https://github.com/ansible-collections/community.mysql/pull/628). @@ -158,13 +158,13 @@ releases: release_date: '2024-08-22' 3.10.1: changes: - bugfixes: - - mysql_user - module makes changes when is executed with ``plugin_auth_string`` - parameter and check mode. - deprecated_features: + breaking_changes: - mysql_user - the ``user`` alias of the ``name`` argument has been deprecated and will be removed in collection version 5.0.0. Use the ``name`` argument instead. + bugfixes: + - mysql_user - module makes changes when is executed with ``plugin_auth_string`` + parameter and check mode. release_summary: 'This is a patch release of the ``community.mysql`` collection. Besides a bugfix, it contains an important upcoming breaking-change information.' @@ -201,72 +201,6 @@ releases: - 0-mysql_user.yml - 3.10.3.yml release_date: '2024-09-09' - 3.11.0: - changes: - bugfixes: - - mysql_user,mysql_role - The sql_mode ANSI_QUOTES affects how the modules mysql_user - and mysql_role compare the existing privileges with the configured privileges, - as well as decide whether double quotes or backticks should be used in the - GRANT statements. Pointing out in issue 671, the modules mysql_user and mysql_role - allow users to enable/disable ANSI_QUOTES in session variable (within a DB - session, the session variable always overwrites the global one). But due to - the issue, the modules do not check for ANSI_MODE in the session variable, - instead, they only check in the GLOBAL one.That behavior is not only limiting - the users' flexibility, but also not allowing users to explicitly disable - ANSI_MODE to work around such bugs like https://bugs.mysql.com/bug.php?id=115953. - (https://github.com/ansible-collections/community.mysql/issues/671) - minor_changes: - - mysql_info - adds the count of tables for each database to the returned values. - It is possible to exclude this new field using the ``db_table_count`` exclusion - filter. (https://github.com/ansible-collections/community.mysql/pull/691) - release_summary: 'This is a minor release of the ``community.mysql`` collection. - - - This changelog contains all changes to the modules and plugins in this - - collection that have been made after the previous release.' - fragments: - - 3.11.0.yml - - 591-mysql_info-db_tables_count.yml - - 671-modules_util_user.yml - release_date: '2024-11-19' - 3.12.0: - changes: - minor_changes: - - mysql_db - added ``zstd`` (de)compression support for ``import``/``dump`` - states (https://github.com/ansible-collections/community.mysql/issues/696). - - mysql_query - returns the ``execution_time_ms`` list containing execution - time per query in milliseconds. - release_summary: 'This is a minor release of the ``community.mysql`` collection. - - This changelog contains all changes to the modules and plugins in this - - collection that have been made after the previous release.' - fragments: - - 0-mysql_query-returns-exec-time-ms.yml - - 3.12.0.yml - - 696-mysql-db-add-zstd-support.yml - release_date: '2025-01-17' - 3.13.0: - changes: - bugfixes: - - mysql_db - fix dump and import to find MariaDB binaries (mariadb and mariadb-dump) - when MariaDB 11+ is used and symbolic links to MySQL binaries are absent. - minor_changes: - - Integration tests for MariaDB 11.4 have replaced those for 10.5. The previous - version is now 10.11. - - mysql_user - add ``locked`` option to lock/unlock users, this is mainly used - to have users that will act as definers on stored procedures. - release_summary: 'This is a minor release of the ``community.mysql`` collection. - - This changelog contains all changes to the modules and plugins in this - - collection that have been made after the previous release.' - fragments: - - 3.13.0.yml - - 702-user_locking.yaml - - tests_mariadb_11_4.yml - release_date: '2025-03-21' 3.2.0: changes: bugfixes: diff --git a/galaxy.yml b/galaxy.yml index 624c7d6..0046b5a 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: community name: mysql -version: 3.13.0 +version: 3.10.3 readme: README.md authors: - Ansible community diff --git a/plugins/module_utils/user.py b/plugins/module_utils/user.py index 9de1c6d..7b6914f 100644 --- a/plugins/module_utils/user.py +++ b/plugins/module_utils/user.py @@ -32,7 +32,7 @@ class InvalidPrivsError(Exception): def get_mode(cursor): - cursor.execute('SELECT @@sql_mode') + cursor.execute('SELECT @@GLOBAL.sql_mode') result = cursor.fetchone() mode_str = result[0] if 'ANSI' in mode_str: @@ -52,25 +52,6 @@ def user_exists(cursor, user, host, host_all): return count[0] > 0 -def user_is_locked(cursor, user, host): - cursor.execute("SHOW CREATE USER %s@%s", (user, host)) - - # Per discussions on irc:libera.chat:#maria the query may return up to 2 rows but "ACCOUNT LOCK" should always be in the first row. - result = cursor.fetchone() - - # ACCOUNT LOCK does not have to be the last option in the CREATE USER query. - # Need to handle both DictCursor and non-DictCursor - if isinstance(result, tuple): - if result[0].find('ACCOUNT LOCK') > 0: - return True - elif isinstance(result, dict): - for res in result.values(): - if res.find('ACCOUNT LOCK') > 0: - return True - - return False - - def sanitize_requires(tls_requires): sanitized_requires = {} if tls_requires: @@ -179,7 +160,7 @@ def get_existing_authentication(cursor, user, host=None): def user_add(cursor, user, host, host_all, password, encrypted, plugin, plugin_hash_string, plugin_auth_string, salt, new_priv, attributes, tls_requires, reuse_existing_password, module, - password_expire, password_expire_interval, locked=False): + password_expire, password_expire_interval): # If attributes are set, perform a sanity check to ensure server supports user attributes before creating user if attributes and not get_attribute_support(cursor): module.fail_json(msg="user attributes were specified but the server does not support user attributes") @@ -269,9 +250,6 @@ def user_add(cursor, user, host, host_all, password, encrypted, cursor.execute("ALTER USER %s@%s ATTRIBUTE %s", (user, host, json.dumps(attributes))) final_attributes = attributes_get(cursor, user, host) - if locked: - cursor.execute("ALTER USER %s@%s ACCOUNT LOCK", (user, host)) - return {'changed': True, 'password_changed': not used_existing_password, 'attributes': final_attributes} @@ -286,7 +264,7 @@ def is_hash(password): def user_mod(cursor, user, host, host_all, password, encrypted, plugin, plugin_hash_string, plugin_auth_string, salt, new_priv, append_privs, subtract_privs, attributes, tls_requires, module, - password_expire, password_expire_interval, locked=None, role=False, maria_role=False): + password_expire, password_expire_interval, role=False, maria_role=False): changed = False msg = "User unchanged" grant_option = False @@ -558,22 +536,6 @@ def user_mod(cursor, user, host, host_all, password, encrypted, if attribute_support: final_attributes = attributes_get(cursor, user, host) - if not role and locked is not None and user_is_locked(cursor, user, host) != locked: - if not module.check_mode: - if locked: - cursor.execute("ALTER USER %s@%s ACCOUNT LOCK", (user, host)) - msg = 'User locked' - else: - cursor.execute("ALTER USER %s@%s ACCOUNT UNLOCK", (user, host)) - msg = 'User unlocked' - else: - if locked: - msg = 'User will be locked' - else: - msg = 'User will be unlocked' - - changed = True - if role: continue diff --git a/plugins/modules/mysql_db.py b/plugins/modules/mysql_db.py index 6ef578c..4a2c954 100644 --- a/plugins/modules/mysql_db.py +++ b/plugins/modules/mysql_db.py @@ -46,8 +46,8 @@ options: 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)), - xz (Added in 2.0) and zstd (C(.zst)) (Added in 3.12.0) compressed files are supported. + - 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: @@ -159,7 +159,7 @@ options: pipefail: description: - Use C(bash) instead of C(sh) and add C(-o pipefail) to catch errors from the - mysql_dump command when I(state=dump) and compression is used. + mysql_dump command when I(state=import) and compression is used. - The default is C(no) to prevent issues on systems without bash as a default interpreter. - The default will change to C(yes) in community.mysql 4.0.0. type: bool @@ -386,75 +386,67 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port, 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, pipefail=False): - - cmd_str = 'mysqldump' - if server_implementation == 'mariadb' and LooseVersion(server_version) >= LooseVersion("10.4.6"): - cmd_str = 'mariadb-dump' - try: - cmd = [module.get_bin_path(cmd_str, True)] - except Exception as e: - return 1, "", "Error determining dump command: %s" % str(e) - + 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.append("--defaults-file=%s" % shlex_quote(config_file)) + cmd += " --defaults-file=%s" % shlex_quote(config_file) else: - cmd.append("--defaults-extra-file=%s" % shlex_quote(config_file)) + cmd += " --defaults-extra-file=%s" % shlex_quote(config_file) if check_implicit_admin: - cmd.append("--user=root --password=''") + cmd += " --user=root --password=''" else: if user is not None: - cmd.append("--user=%s" % shlex_quote(user)) + cmd += " --user=%s" % shlex_quote(user) if password is not None: if not unsafe_password: - cmd.append("--password=%s" % shlex_quote(password)) + cmd += " --password=%s" % shlex_quote(password) else: - cmd.append("--password=%s" % password) + cmd += " --password=%s" % password if ssl_cert is not None: - cmd.append("--ssl-cert=%s" % shlex_quote(ssl_cert)) + cmd += " --ssl-cert=%s" % shlex_quote(ssl_cert) if ssl_key is not None: - cmd.append("--ssl-key=%s" % shlex_quote(ssl_key)) + cmd += " --ssl-key=%s" % shlex_quote(ssl_key) if ssl_ca is not None: - cmd.append("--ssl-ca=%s" % shlex_quote(ssl_ca)) + cmd += " --ssl-ca=%s" % shlex_quote(ssl_ca) if force: - cmd.append("--force") + cmd += " --force" if socket is not None: - cmd.append("--socket=%s" % shlex_quote(socket)) + cmd += " --socket=%s" % shlex_quote(socket) else: - cmd.append("--host=%s --port=%i" % (shlex_quote(host), port)) + cmd += " --host=%s --port=%i" % (shlex_quote(host), port) if all_databases: - cmd.append("--all-databases") + cmd += " --all-databases" elif len(db_name) > 1: - cmd.append("--databases {0}".format(' '.join(db_name))) + cmd += " --databases {0}".format(' '.join(db_name)) else: - cmd.append("%s" % shlex_quote(' '.join(db_name))) + cmd += " %s" % shlex_quote(' '.join(db_name)) if skip_lock_tables: - cmd.append("--skip-lock-tables") + cmd += " --skip-lock-tables" if (encoding is not None) and (encoding != ""): - cmd.append("--default-character-set=%s" % shlex_quote(encoding)) + cmd += " --default-character-set=%s" % shlex_quote(encoding) if single_transaction: - cmd.append("--single-transaction=true") + cmd += " --single-transaction=true" if quick: - cmd.append("--quick") + cmd += " --quick" if ignore_tables: for an_ignored_table in ignore_tables: - cmd.append("--ignore-table={0}".format(an_ignored_table)) + cmd += " --ignore-table={0}".format(an_ignored_table) if hex_blob: - cmd.append("--hex-blob") + cmd += " --hex-blob" if master_data: if (server_implementation == 'mysql' and LooseVersion(server_version) >= LooseVersion("8.2.0")): - cmd.append("--source-data=%s" % master_data) + cmd += " --source-data=%s" % master_data else: - cmd.append("--master-data=%s" % master_data) + cmd += " --master-data=%s" % master_data if dump_extra_args is not None: - cmd.append(dump_extra_args) + cmd += " " + dump_extra_args path = None if os.path.splitext(target)[-1] == '.gz': @@ -463,10 +455,6 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port, path = module.get_bin_path('bzip2', True) elif os.path.splitext(target)[-1] == '.xz': path = module.get_bin_path('xz', True) - elif os.path.splitext(target)[-1] == '.zst': - path = module.get_bin_path('zstd', True) - - cmd = ' '.join(cmd) if path: cmd = '%s | %s > %s' % (cmd, path, shlex_quote(target)) @@ -486,21 +474,13 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port, def db_import(module, host, user, password, db_name, target, all_databases, port, config_file, - server_implementation, server_version, socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None, - encoding=None, force=False, + 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_str = 'mysql' - if server_implementation == 'mariadb' and LooseVersion(server_version) >= LooseVersion("10.4.6"): - cmd_str = 'mariadb' - try: - cmd = [module.get_bin_path(cmd_str, True)] - except Exception as e: - return 1, "", "Error determining mysql/mariadb command: %s" % str(e) - + cmd = [module.get_bin_path('mysql', True)] # --defaults-file must go first, or errors out if config_file: if restrict_config_file: @@ -546,8 +526,6 @@ def db_import(module, host, user, password, db_name, target, all_databases, port 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) - elif os.path.splitext(target)[-1] == '.zst': - comp_prog_path = module.get_bin_path('zstd', 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)) @@ -790,8 +768,8 @@ def main(): rc, stdout, stderr = db_import(module, login_host, login_user, login_password, db, target, all_databases, - login_port, config_file, server_implementation, - server_version, socket, ssl_cert, ssl_key, ssl_ca, + 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: diff --git a/plugins/modules/mysql_info.py b/plugins/modules/mysql_info.py index 2360d01..2d1fe94 100644 --- a/plugins/modules/mysql_info.py +++ b/plugins/modules/mysql_info.py @@ -1,10 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # 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 @@ -36,7 +35,7 @@ options: exclude_fields: description: - List of fields which are not needed to collect. - - "Supports elements: C(db_size), C(db_table_count). Unsupported elements will be ignored." + - "Supports elements: C(db_size). Unsupported elements will be ignored." type: list elements: str version_added: '0.1.0' @@ -205,19 +204,13 @@ databases: returned: if not excluded by filter type: dict sample: - - { "mysql": { "size": 656594, "tables": 31 }, "information_schema": { "size": 73728, "tables": 79 } } + - { "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 } - tables: - description: Count of tables and views in that database. - returned: if not excluded by filter - type: dict - sample: { 'tables': 12 } - version_added: '3.11.0' settings: description: Global settings (variables) information. returned: if not excluded by filter @@ -319,7 +312,6 @@ from ansible_collections.community.mysql.plugins.module_utils.user import ( get_resource_limits, get_existing_authentication, get_user_implementation, - user_is_locked, ) from ansible.module_utils.six import iteritems from ansible.module_utils._text import to_native @@ -654,10 +646,8 @@ class MySQL_Info(object): if authentications: output_dict.update(authentications[0]) - if line.get('is_role') and line['is_role'] == 'N': - output_dict['locked'] = user_is_locked(self.cursor, user, host) - # TODO password_option + # TODO lock_option # but both are not supported by mysql_user atm. So no point yet. output.append(output_dict) @@ -666,39 +656,40 @@ class MySQL_Info(object): 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') - def is_field_included(field_name): - return not exclude_fields or 'db_{}'.format(field_name) not in exclude_fields + res = self.__exec_sql(query) - def create_db_info(db_data): - info = {} - if is_field_included('size'): - info['size'] = int(db_data.get('size', 0) or 0) - if is_field_included('table_count'): - info['tables'] = int(db_data.get('tables', 0) or 0) - return info + if res: + for db in res: + self.info['databases'][db['name']] = {} - # Build the main query - query_parts = ['SELECT table_schema AS "name"'] - if is_field_included('size'): - query_parts.append('SUM(data_length + index_length) AS "size"') - if is_field_included('table_count'): - query_parts.append('COUNT(table_name) as "tables"') + if not exclude_fields or 'db_size' not in exclude_fields: + if db['size'] is None: + db['size'] = 0 - query = "{} FROM information_schema.TABLES GROUP BY table_schema".format(", ".join(query_parts)) + self.info['databases'][db['name']]['size'] = int(db['size']) - # Get and process databases with tables - databases = self.__exec_sql(query) or [] - for db in databases: - self.info['databases'][db['name']] = create_db_info(db) + # If empty dbs are not needed in the returned dict, exit from the method + if not return_empty_dbs: + return None - # Handle empty databases if requested - if return_empty_dbs: - empty_databases = self.__exec_sql('SHOW DATABASES') or [] - for db in empty_databases: - db_name = db['Database'] - if db_name not in self.info['databases']: - self.info['databases'][db_name] = create_db_info({}) + # 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. diff --git a/plugins/modules/mysql_query.py b/plugins/modules/mysql_query.py index 35beeb3..13a07de 100644 --- a/plugins/modules/mysql_query.py +++ b/plugins/modules/mysql_query.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) # 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) @@ -62,6 +62,7 @@ author: - Andrew Klychkov (@Andersson007) extends_documentation_fragment: - community.mysql.mysql + ''' EXAMPLES = r''' @@ -116,18 +117,8 @@ rowcount: returned: changed type: list sample: [5, 1] -execution_time_ms: - description: - - A list containing execution time per query in milliseconds. - - The measurements are done right before and after passing - the query to the driver for execution. - returned: success - type: list - sample: [7104, 85] - version_added: '3.12.0' ''' -import time import warnings from ansible.module_utils.basic import AnsibleModule @@ -148,18 +139,6 @@ DDL_QUERY_KEYWORDS = ('CREATE', 'DROP', 'ALTER', 'RENAME', 'TRUNCATE') # Module execution. # - -def execute_and_return_time(cursor, query, args): - # Measure query execution time in milliseconds - start_time = time.perf_counter() - - cursor.execute(query, args) - - # Calculate the execution time rounding it to 4 decimal places - exec_time_ms = round((time.perf_counter() - start_time) * 1000, 4) - return cursor, exec_time_ms - - def main(): argument_spec = mysql_common_argument_spec() argument_spec.update( @@ -234,7 +213,6 @@ def main(): query_result = [] executed_queries = [] rowcount = [] - execution_time_ms = [] already_exists = False for q in query: @@ -245,8 +223,7 @@ def main(): category=mysql_driver.Warning) try: - cursor, exec_time_ms = execute_and_return_time(cursor, q, arguments) - execution_time_ms.append(exec_time_ms) + cursor.execute(q, arguments) except mysql_driver.Warning: # When something is run with IF NOT EXISTS # and there's "already exists" MySQL warning, @@ -303,7 +280,6 @@ def main(): 'executed_queries': executed_queries, 'query_result': query_result, 'rowcount': rowcount, - 'execution_time_ms': execution_time_ms, } # Exit: diff --git a/plugins/modules/mysql_replication.py b/plugins/modules/mysql_replication.py index b902da0..723fc35 100644 --- a/plugins/modules/mysql_replication.py +++ b/plugins/modules/mysql_replication.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Copyright: (c) 2013, Balazs Pocze -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # 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) @@ -284,6 +284,7 @@ EXAMPLES = r''' community.mysql.mysql_replication: mode: changeprimary fail_on_error: true + ''' RETURN = r''' diff --git a/plugins/modules/mysql_role.py b/plugins/modules/mysql_role.py index 382445c..032b41e 100644 --- a/plugins/modules/mysql_role.py +++ b/plugins/modules/mysql_role.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright: (c) 2021, Andrew Klychkov +# Copyright: (c) 2021, Andrew Klychkov # 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 @@ -930,12 +930,11 @@ class Role(): set_default_role_all=set_default_role_all) if privs: - result = user_mod(cursor=self.cursor, user=self.name, host=self.host, - host_all=None, password=None, encrypted=None, plugin=None, - plugin_auth_string=None, plugin_hash_string=None, salt=None, - new_priv=privs, append_privs=append_privs, subtract_privs=subtract_privs, - attributes=None, tls_requires=None, module=self.module, password_expire=None, - password_expire_interval=None, role=True, maria_role=self.is_mariadb) + result = user_mod(self.cursor, self.name, self.host, + None, None, None, None, None, None, None, + privs, append_privs, subtract_privs, None, None, + self.module, None, None, role=True, + maria_role=self.is_mariadb) changed = result['changed'] if admin: diff --git a/plugins/modules/mysql_user.py b/plugins/modules/mysql_user.py index 2a5855c..78f11a9 100644 --- a/plugins/modules/mysql_user.py +++ b/plugins/modules/mysql_user.py @@ -46,7 +46,6 @@ options: priv: description: - "MySQL privileges string in the format: C(db.table:priv1,priv2)." - - Additionally, there must be no spaces between the table and the privilege as this will yield a non-idempotent check mode. - "Multiple privileges can be specified by separating each one using a forward slash: C(db.table1:priv/db.table2:priv)." - The format is based on MySQL C(GRANT) statement. @@ -189,15 +188,6 @@ options: fields names in privileges. type: bool version_added: '3.8.0' - - locked: - description: - - Lock account to prevent connections using it. - - This is primarily used for creating a user that will act as a DEFINER on stored procedures. - - If not specified leaves the lock state as is (for a new user creates unlocked). - type: bool - version_added: '3.13.0' - attributes: description: - "Create, update, or delete user attributes (arbitrary 'key: value' comments) for the user." @@ -234,7 +224,6 @@ author: - Lukasz Tomaszkiewicz (@tomaszkiewicz) - kmarse (@kmarse) - Laurent Indermühle (@laurent-indermuehle) -- E.S. Rosenberg (@Keeper-of-the-Keys) extends_documentation_fragment: - community.mysql.mysql @@ -279,7 +268,7 @@ EXAMPLES = r''' priv: '*.*:ALL,GRANT' state: present session_vars: - wsrep_on: 'off' + wsrep_on: off - name: Create user with password, all database privileges and 'WITH GRANT OPTION' in db1 and db2 community.mysql.mysql_user: @@ -410,13 +399,6 @@ EXAMPLES = r''' priv: 'db1.*': DELETE -- name: Create locked user to act as a definer on procedures - community.mysql.mysql_user: - name: readonly_procedures_locked - locked: true - priv: - db1.*: SELECT - # Example .my.cnf file for setting the root password # [client] # user=root @@ -487,7 +469,6 @@ def main(): column_case_sensitive=dict(type='bool', default=None), # TODO 4.0.0 add default=True password_expire=dict(type='str', choices=['now', 'never', 'default', 'interval'], no_log=True), password_expire_interval=dict(type='int', required_if=[('password_expire', 'interval', True)], no_log=True), - locked=dict(type='bool'), ) module = AnsibleModule( argument_spec=argument_spec, @@ -528,7 +509,6 @@ def main(): column_case_sensitive = module.params["column_case_sensitive"] password_expire = module.params["password_expire"] password_expire_interval = module.params["password_expire_interval"] - locked = module.boolean(module.params['locked']) if priv and not isinstance(priv, (str, dict)): module.fail_json(msg="priv parameter must be str or dict but %s was passed" % type(priv)) @@ -596,15 +576,13 @@ def main(): result = user_mod(cursor, user, host, host_all, password, encrypted, plugin, plugin_hash_string, plugin_auth_string, salt, priv, append_privs, subtract_privs, attributes, tls_requires, module, - password_expire, password_expire_interval, locked=locked) + password_expire, password_expire_interval) else: - result = user_mod(cursor=cursor, user=user, host=host, host_all=host_all, password=None, - encrypted=encrypted, plugin=None, plugin_hash_string=None, plugin_auth_string=None, - salt=None, new_priv=priv, append_privs=append_privs, subtract_privs=subtract_privs, - attributes=attributes, tls_requires=tls_requires, module=module, - password_expire=password_expire, password_expire_interval=password_expire_interval, - locked=locked) + result = user_mod(cursor, user, host, host_all, None, encrypted, + None, None, None, None, + priv, append_privs, subtract_privs, attributes, tls_requires, module, + password_expire, password_expire_interval) changed = result['changed'] msg = result['msg'] password_changed = result['password_changed'] @@ -622,7 +600,7 @@ def main(): result = user_add(cursor, user, host, host_all, password, encrypted, plugin, plugin_hash_string, plugin_auth_string, salt, priv, attributes, tls_requires, reuse_existing_password, module, - password_expire, password_expire_interval, locked=locked) + password_expire, password_expire_interval) changed = result['changed'] password_changed = result['password_changed'] final_attributes = result['attributes'] diff --git a/tests/integration/old_mariadb_replication/tasks/main.yml b/tests/integration/old_mariadb_replication/tasks/main.yml index 321ba4d..4ea76a9 100644 --- a/tests/integration/old_mariadb_replication/tasks/main.yml +++ b/tests/integration/old_mariadb_replication/tasks/main.yml @@ -1,4 +1,4 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # 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 diff --git a/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml b/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml index 8977c10..699b61f 100644 --- a/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml +++ b/tests/integration/old_mariadb_replication/tasks/mariadb_master_use_gtid.yml @@ -1,4 +1,4 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # Tests for master_use_gtid parameter. diff --git a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml index 337a839..3928c78 100644 --- a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml +++ b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_connection_name.yml @@ -1,4 +1,4 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # Needs for further tests: diff --git a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml index 1a95a55..f65d090 100644 --- a/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml +++ b/tests/integration/old_mariadb_replication/tasks/mariadb_replication_initial.yml @@ -1,4 +1,4 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # Preparation: diff --git a/tests/integration/targets/test_mysql_info/tasks/filter_databases.yml b/tests/integration/targets/test_mysql_info/tasks/filter_databases.yml deleted file mode 100644 index da1058b..0000000 --- a/tests/integration/targets/test_mysql_info/tasks/filter_databases.yml +++ /dev/null @@ -1,161 +0,0 @@ ---- - -- module_defaults: - community.mysql.mysql_db: &mysql_defaults - login_user: "{{ mysql_user }}" - login_password: "{{ mysql_password }}" - login_host: "{{ mysql_host }}" - login_port: "{{ mysql_primary_port }}" - community.mysql.mysql_query: *mysql_defaults - community.mysql.mysql_info: *mysql_defaults - community.mysql.mysql_user: *mysql_defaults - - block: - - # ================================ Prepare ============================== - - name: Mysql_info databases | Prepare | Create databases - community.mysql.mysql_db: - name: - - db_tables_count_empty - - db_tables_count_1 - - db_tables_count_2 - - db_only_views # https://github.com/ansible-Getions/community.mysql/issues/204 - state: present - - - name: Mysql_info databases | Prepare | Create tables - community.mysql.mysql_query: - query: - - >- - CREATE TABLE IF NOT EXISTS db_tables_count_1.t1 - (id int, name varchar(9)) - - >- - CREATE TABLE IF NOT EXISTS db_tables_count_2.t1 - (id int, name1 varchar(9)) - - >- - CREATE TABLE IF NOT EXISTS db_tables_count_2.t2 - (id int, name1 varchar(9)) - - >- - CREATE VIEW db_only_views.v_today (today) AS SELECT CURRENT_DATE - - # ================================== Tests ============================== - - - name: Mysql_info databases | Get all non-empty databases fields - community.mysql.mysql_info: - filter: - - databases - register: result - failed_when: - - > - result.databases['db_tables_count_1'].size != 16384 or - result.databases['db_tables_count_1'].tables != 1 or - result.databases['db_tables_count_2'].size != 32768 or - result.databases['db_tables_count_2'].tables != 2 or - result.databases['db_only_views'].size != 0 or - result.databases['db_only_views'].tables != 1 or - 'db_tables_count_empty' in result.databases | dict2items - | map(attribute='key') - - - name: Mysql_info databases | Get all dbs fields except db_size - community.mysql.mysql_info: - filter: - - databases - exclude_fields: - - db_size - register: result - failed_when: - - > - result.databases['db_tables_count_1'].size is defined or - result.databases['db_tables_count_1'].tables != 1 or - result.databases['db_tables_count_2'].size is defined or - result.databases['db_tables_count_2'].tables != 2 or - result.databases['db_only_views'].size is defined or - result.databases['db_only_views'].tables != 1 or - 'db_tables_count_empty' in result.databases | dict2items - | map(attribute='key') - - # 'unsupported' element is passed to check that an unsupported value - # won't break anything (will be ignored regarding to the module's - # documentation). - - name: Mysql_info databases | Get all dbs fields with unsupported value - community.mysql.mysql_info: - filter: - - databases - exclude_fields: - - db_size - - unsupported - register: result - failed_when: - - > - result.databases['db_tables_count_1'].size is defined or - result.databases['db_tables_count_1'].tables != 1 or - result.databases['db_tables_count_2'].size is defined or - result.databases['db_tables_count_2'].tables != 2 or - result.databases['db_only_views'].size is defined or - result.databases['db_only_views'].tables != 1 or - 'db_tables_count_empty' in result.databases | dict2items - | map(attribute='key') - - - name: Mysql_info databases | Get all dbs fields except tables - community.mysql.mysql_info: - filter: - - databases - exclude_fields: - - db_table_count - register: result - failed_when: - - > - result.databases['db_tables_count_1'].size != 16384 or - result.databases['db_tables_count_1'].tables is defined or - result.databases['db_tables_count_2'].size != 32768 or - result.databases['db_tables_count_2'].tables is defined or - result.databases['db_only_views'].size != 0 or - result.databases['db_only_views'].tables is defined or - 'db_tables_count_empty' in result.databases | dict2items - | map(attribute='key') - - - name: Mysql_info databases | Get all dbs even empty ones - community.mysql.mysql_info: - filter: - - databases - return_empty_dbs: true - register: result - failed_when: - - > - result.databases['db_tables_count_1'].size != 16384 or - result.databases['db_tables_count_1'].tables != 1 or - result.databases['db_tables_count_2'].size != 32768 or - result.databases['db_tables_count_2'].tables != 2 or - result.databases['db_only_views'].size != 0 or - result.databases['db_only_views'].tables != 1 or - result.databases['db_tables_count_empty'].size != 0 or - result.databases['db_tables_count_empty'].tables != 0 - - - name: Mysql_info databases | Get all dbs even empty ones without size - community.mysql.mysql_info: - filter: - - databases - exclude_fields: - - db_size - return_empty_dbs: true - register: result - failed_when: - - > - result.databases['db_tables_count_1'].size is defined or - result.databases['db_tables_count_1'].tables != 1 or - result.databases['db_tables_count_2'].size is defined or - result.databases['db_tables_count_2'].tables != 2 or - result.databases['db_only_views'].size is defined or - result.databases['db_only_views'].tables != 1 or - result.databases['db_tables_count_empty'].size is defined or - result.databases['db_tables_count_empty'].tables != 0 - - # ================================== Cleanup ============================ - - - name: Mysql_info databases | Cleanup databases - community.mysql.mysql_db: - name: - - db_tables_count_empty - - db_tables_count_1 - - db_tables_count_2 - - db_only_views - state: absent diff --git a/tests/integration/targets/test_mysql_info/tasks/filter_users_info.yml b/tests/integration/targets/test_mysql_info/tasks/filter_users_info.yml index 558d309..36508f3 100644 --- a/tests/integration/targets/test_mysql_info/tasks/filter_users_info.yml +++ b/tests/integration/targets/test_mysql_info/tasks/filter_users_info.yml @@ -261,7 +261,6 @@ resource_limits: "{{ item.resource_limits | default(omit) }}" column_case_sensitive: true state: present - locked: "{{ item.locked | default(omit) }}" loop: "{{ result.users_info }}" loop_control: label: "{{ item.name }}@{{ item.host }}" @@ -276,7 +275,6 @@ - item.name != 'mariadb.sys' - item.name != 'mysql.sys' - item.name != 'mysql.infoschema' - - item.name != 'mysql.session' # ================================== Cleanup ============================ diff --git a/tests/integration/targets/test_mysql_info/tasks/main.yml b/tests/integration/targets/test_mysql_info/tasks/main.yml index 61f238f..93570f2 100644 --- a/tests/integration/targets/test_mysql_info/tasks/main.yml +++ b/tests/integration/targets/test_mysql_info/tasks/main.yml @@ -5,7 +5,7 @@ #################################################################### # Test code for mysql_info module -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ################### @@ -132,11 +132,94 @@ - 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 is not changed + - 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 is not changed + - 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 is not changed + - result.databases.acme == {} + - result.databases.mysql == {} + + - name: Remove acme database + mysql_db: + <<: *mysql_params + name: acme + state: absent + - include_tasks: issue-28.yml - - name: Import tasks file to tests tables count in database filter - ansible.builtin.import_tasks: - file: filter_databases.yml + # https://github.com/ansible-collections/community.mysql/issues/204 + - name: Create database containing only views + mysql_db: + <<: *mysql_params + name: allviews + + - name: Create view + mysql_query: + <<: *mysql_params + login_db: allviews + query: 'CREATE VIEW v_today (today) AS SELECT CURRENT_DATE' + + - name: Fetch info + mysql_info: + <<: *mysql_params + register: result + + - name: Check + assert: + that: + - result.databases.allviews.size == 0 - name: Import tasks file to tests users_info filter ansible.builtin.import_tasks: diff --git a/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml b/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml index 310f925..82665af 100644 --- a/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml +++ b/tests/integration/targets/test_mysql_query/tasks/mysql_query_initial.yml @@ -1,6 +1,6 @@ --- # Test code for mysql_query module -# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - vars: mysql_parameters: &mysql_params @@ -35,7 +35,6 @@ that: - result is changed - result.executed_queries == ['CREATE TABLE {{ test_table1 }} (id int)'] - - result.execution_time_ms[0] > 0 - name: Insert test data mysql_query: @@ -53,8 +52,6 @@ - result is changed - result.rowcount == [2, 1] - result.executed_queries == ['INSERT INTO {{ test_table1 }} VALUES (1), (2)', 'INSERT INTO {{ test_table1 }} VALUES (3)'] - - result.execution_time_ms[0] > 0 - - result.execution_time_ms[1] > 0 - name: Check data in {{ test_table1 }} mysql_query: diff --git a/tests/integration/targets/test_mysql_replication/tasks/main.yml b/tests/integration/targets/test_mysql_replication/tasks/main.yml index 32ce553..a65cabd 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/main.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/main.yml @@ -4,7 +4,7 @@ # and should not be used as examples of how to write Ansible roles # #################################################################### -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # 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: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml index 0bcc6e6..802865c 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_channel.yml @@ -1,5 +1,5 @@ --- -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - vars: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml index 00699c1..30cd99f 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_initial.yml @@ -1,5 +1,5 @@ --- -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - vars: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml index 2093b70..3ae4339 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_primary_delay.yml @@ -1,4 +1,4 @@ -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - vars: diff --git a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml index cdd5fa7..8968049 100644 --- a/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml +++ b/tests/integration/targets/test_mysql_replication/tasks/mysql_replication_resetprimary_mode.yml @@ -1,5 +1,5 @@ --- -# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2019, Andrew Klychkov (@Andersson007) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - vars: diff --git a/tests/integration/targets/test_mysql_user/tasks/issue-671.yaml b/tests/integration/targets/test_mysql_user/tasks/issue-671.yaml deleted file mode 100644 index 3696cf0..0000000 --- a/tests/integration/targets/test_mysql_user/tasks/issue-671.yaml +++ /dev/null @@ -1,112 +0,0 @@ ---- -# Due to https://bugs.mysql.com/bug.php?id=115953, in Mysql 8, if ANSI_QUOTES is enabled, -# backticks will be used instead of double quotes to quote functions or procedures name. -# As a consequence, mysql_user and mysql_roles will always report "changed" for functions -# and procedures no matter the privileges are granted or not. -# Workaround for the mysql bug 116953 is removing ANSI_QUOTES from the module's session -# sql_mode. But because issue 671, ANSI_QUOTES is always got from GLOBAL sql_mode, thus -# this workaround can't work. Even without the Mysql bug, because sql_mode in session -# precedes GLOBAL sql_mode. we should check for sql_mode in session variable instead of -# the GLOBAL one. -- vars: - mysql_parameters: &mysql_params - login_user: '{{ mysql_user }}' - login_password: '{{ mysql_password }}' - login_host: '{{ mysql_host }}' - login_port: '{{ mysql_primary_port }}' - - block: - - name: Issue-671| test setup | drop database - community.mysql.mysql_db: - <<: *mysql_params - name: "{{ item }}" - state: absent - loop: - - foo - - bar - - - name: Issue-671| test setup | create database - community.mysql.mysql_db: - <<: *mysql_params - name: "{{ item }}" - state: present - loop: - - foo - - bar - - - name: Issue-671| test setup | get value of GLOBAL.sql_mode - community.mysql.mysql_query: - <<: *mysql_params - query: 'select @@GLOBAL.sql_mode AS sql_mode' - register: sql_mode_orig - - - name: Issue-671| Assert sql_mode_orig - ansible.builtin.assert: - that: - - sql_mode_orig.query_result[0][0].sql_mode != None - - - name: Issue-671| enable sql_mode ANSI_QUOTES - community.mysql.mysql_variables: - <<: *mysql_params - variable: sql_mode - value: '{{ sql_mode_orig.query_result[0][0].sql_mode }},ANSI_QUOTES' - mode: "{% if db_engine == 'mariadb' %}global{% else %}persist{% endif %}" - - - name: Issue-671| Copy SQL scripts to remote - ansible.builtin.copy: - src: "{{ item }}" - dest: "{{ remote_tmp_dir }}/{{ item | basename }}" - loop: - - create-function.sql - - create-procedure.sql - - - name: Issue-671| Create function for test - ansible.builtin.shell: - cmd: "{{ mysql_command }} < {{ remote_tmp_dir }}/create-function.sql" - - - name: Issue-671| Create procedure for test - ansible.builtin.shell: - cmd: "{{ mysql_command }} < {{ remote_tmp_dir }}/create-procedure.sql" - - - name: Issue-671| Create user with FUNCTION and PROCEDURE privileges - community.mysql.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' - - - name: Issue-671| Grant the privileges again, remove ANSI_QUOTES from the session variable - community.mysql.mysql_user: - <<: *mysql_params - session_vars: - sql_mode: "" - name: '{{ user_name_2 }}' - password: '{{ user_password_2 }}' - state: present - priv: 'FUNCTION foo.function:EXECUTE/foo.*:SELECT/PROCEDURE bar.procedure:EXECUTE' - register: result - failed_when: - - result is failed or result is changed - - - name: Issue-671| Test teardown | cleanup databases - community.mysql.mysql_db: - <<: *mysql_params - name: "{{ item }}" - state: absent - loop: - - foo - - bar - - - name: Issue-671| set sql_mode back to original value - community.mysql.mysql_variables: - <<: *mysql_params - variable: sql_mode - value: '{{ sql_mode_orig.query_result[0][0].sql_mode }}' - mode: "{% if db_engine == 'mariadb' %}global{% else %}persist{% endif %}" - - - name: Issue-671| Teardown user_name_2 - ansible.builtin.include_tasks: - file: utils/remove_user.yml - vars: - user_name: "{{ user_name_2 }}" \ No newline at end of file diff --git a/tests/integration/targets/test_mysql_user/tasks/main.yml b/tests/integration/targets/test_mysql_user/tasks/main.yml index 7212886..e77c443 100644 --- a/tests/integration/targets/test_mysql_user/tasks/main.yml +++ b/tests/integration/targets/test_mysql_user/tasks/main.yml @@ -282,12 +282,6 @@ - import_tasks: issue-64560.yaml tags: - issue-64560 - - - name: Test ANSI_QUOTES - ansible.builtin.import_tasks: - file: issue-671.yaml - tags: - - issue-671 # Test that mysql_user still works with force_context enabled (database set to "mysql") # (https://github.com/ansible-collections/community.mysql/issues/265) @@ -305,7 +299,3 @@ - name: Mysql_user - test update_password ansible.builtin.import_tasks: file: test_update_password.yml - - - name: Mysql_user - test user_locking - ansible.builtin.import_tasks: - file: test_user_locking.yml diff --git a/tests/integration/targets/test_mysql_user/tasks/test_user_locking.yml b/tests/integration/targets/test_mysql_user/tasks/test_user_locking.yml deleted file mode 100644 index 3990610..0000000 --- a/tests/integration/targets/test_mysql_user/tasks/test_user_locking.yml +++ /dev/null @@ -1,200 +0,0 @@ ---- - -- vars: - mysql_parameters: &mysql_params - login_user: '{{ mysql_user }}' - login_password: '{{ mysql_password }}' - login_host: '{{ mysql_host }}' - login_port: '{{ mysql_primary_port }}' - - block: - - # ========================= Prepare ======================================= - - name: Mysql_user Lock user | Create a test database - community.mysql.mysql_db: - <<: *mysql_params - name: mysql_lock_user_test - state: present - - # ========================== Tests ======================================== - - - name: Mysql_user Lock user | create locked | Create test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - password: 'msandbox' - locked: true - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create locked | Assert that test user is locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is not search('ACCOUNT LOCK') - - - name: 'Mysql_user Lock user | create locked | Idempotence check' - check_mode: true - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - locked: true - priv: - 'mysql_lock_user_test.*': 'SELECT' - register: idempotence_check - failed_when: idempotence_check is changed - - - name: 'Mysql_user Lock user | create locked | Check that absense of locked does not unlock the user' - check_mode: true - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - priv: - 'mysql_lock_user_test.*': 'SELECT' - register: idempotence_check - failed_when: idempotence_check is changed - - - name: 'Mysql_user Lock user | create locked | Unlock test user check_mode: true' - check_mode: true - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - locked: false - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create locked | Assert that test user is locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is not search('ACCOUNT LOCK') - - - name: Mysql_user Lock user | create locked | Unlock test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - locked: false - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create locked | Assert that test user is not locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK') - - - name: Mysql_user Lock user | create locked | Remove test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - state: absent - - - name: Mysql_user Lock user | create unlocked | Create test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - password: 'msandbox' - locked: false - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create unlocked | Assert that test user is not locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK') - - - name: 'Mysql_user Lock user | create unlocked | Idempotence check' - check_mode: true - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - locked: false - priv: - 'mysql_lock_user_test.*': 'SELECT' - register: idempotence_check - failed_when: idempotence_check is changed - - - name: 'Mysql_user Lock user | create unlocked | Lock test user check_mode: true' - check_mode: true - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - locked: true - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create unlocked | Assert that test user is not locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK') - - - name: Mysql_user Lock user | create unlocked | Lock test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - locked: true - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create unlocked | Assert that test user is locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is not search('ACCOUNT LOCK') - - - name: Mysql_user Lock user | create unlocked | Remove test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - state: absent - - - name: Mysql_user Lock user | create default | Create test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - password: 'msandbox' - priv: - 'mysql_lock_user_test.*': 'SELECT' - - - name: Mysql_user Lock user | create default | Assert that test user is not locked - community.mysql.mysql_query: - <<: *mysql_params - query: - - SHOW CREATE USER 'mysql_locked_user'@'localhost' - register: locked_user_creation - failed_when: - - locked_user_creation.query_result[0][0] is search('ACCOUNT LOCK') - - - name: Mysql_user Lock user | create default | Remove test user - community.mysql.mysql_user: - <<: *mysql_params - name: mysql_locked_user - state: absent - - # ========================= Teardown ====================================== - - - name: Mysql_user Lock user | Delete test database - community.mysql.mysql_db: - <<: *mysql_params - name: mysql_lock_user_test - state: absent diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt deleted file mode 100644 index 152162d..0000000 --- a/tests/sanity/ignore-2.19.txt +++ /dev/null @@ -1,3 +0,0 @@ -plugins/modules/mysql_db.py validate-modules:use-run-command-not-popen -plugins/module_utils/mysql.py pylint:unused-import -plugins/module_utils/version.py pylint:unused-import diff --git a/tests/unit/plugins/module_utils/test_mariadb_replication.py b/tests/unit/plugins/module_utils/test_mariadb_replication.py index 513d8cf..deb3099 100644 --- a/tests/unit/plugins/module_utils/test_mariadb_replication.py +++ b/tests/unit/plugins/module_utils/test_mariadb_replication.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) from __future__ import (absolute_import, division, print_function) __metaclass__ = type diff --git a/tests/unit/plugins/module_utils/test_mysql_replication.py b/tests/unit/plugins/module_utils/test_mysql_replication.py index c4126a5..96d4d9a 100644 --- a/tests/unit/plugins/module_utils/test_mysql_replication.py +++ b/tests/unit/plugins/module_utils/test_mysql_replication.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) +# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) from __future__ import (absolute_import, division, print_function) __metaclass__ = type