Skip to content

Commit

Permalink
mysql_info - add table count to the databases returned values (#691)
Browse files Browse the repository at this point in the history
* Add tables count per database
* Add integrations tests
* Deduplicate tests between main and new task file
  • Loading branch information
laurent-indermuehle authored Nov 19, 2024
1 parent d613fa1 commit 9057637
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 121 deletions.
3 changes: 3 additions & 0 deletions changelogs/fragments/591-mysql_info-db_tables_count.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
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)
75 changes: 40 additions & 35 deletions plugins/modules/mysql_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
exclude_fields:
description:
- List of fields which are not needed to collect.
- "Supports elements: C(db_size). Unsupported elements will be ignored."
- "Supports elements: C(db_size), C(db_table_count). Unsupported elements will be ignored."
type: list
elements: str
version_added: '0.1.0'
Expand Down Expand Up @@ -204,13 +204,19 @@
returned: if not excluded by filter
type: dict
sample:
- { "mysql": { "size": 656594 }, "information_schema": { "size": 73728 } }
- { "mysql": { "size": 656594, "tables": 31 }, "information_schema": { "size": 73728, "tables": 79 } }
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
Expand Down Expand Up @@ -656,40 +662,39 @@ def __get_users_info(self):

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:
if db['size'] is None:
db['size'] = 0

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 is_field_included(field_name):
return not exclude_fields or 'db_{}'.format(field_name) not in exclude_fields

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

# 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"')

query = "{} FROM information_schema.TABLES GROUP BY table_schema".format(", ".join(query_parts))

# 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)

# 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({})

def __exec_sql(self, query, ddl=False):
"""Execute SQL.
Expand Down
161 changes: 161 additions & 0 deletions tests/integration/targets/test_mysql_info/tasks/filter_databases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---

- 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
89 changes: 3 additions & 86 deletions tests/integration/targets/test_mysql_info/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,94 +132,11 @@
- 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

# 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 tables count in database filter
ansible.builtin.import_tasks:
file: filter_databases.yml

- name: Import tasks file to tests users_info filter
ansible.builtin.import_tasks:
Expand Down

0 comments on commit 9057637

Please sign in to comment.