diff --git a/README.md b/README.md index 34ddbaf..1ef977a 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,15 @@ See [Ansible Using collections](https://docs.ansible.com/ansible/latest/user_gui ### Usage example ```yaml +- name: Get server information + register: result + community.clickhouse.clickhouse_info: + +- name: Print server information + ansible.builtin.debug: + var: result.result + +# Note: the task above contains it too - name: Query DB using non-default user & DB to connect to register: result community.clickhouse.clickhouse_client: diff --git a/plugins/modules/clickhouse_info.py b/plugins/modules/clickhouse_info.py index 0f6346f..0270ad1 100644 --- a/plugins/modules/clickhouse_info.py +++ b/plugins/modules/clickhouse_info.py @@ -58,6 +58,8 @@ description: - The same as the C(Client(user='...')) argument. - If not passed, relies on the driver's default argument value. + - Be sure your the user has permissions to read the system tables + listed in the RETURN section. type: str login_password: @@ -99,6 +101,33 @@ returned: success type: dict sample: {"raw": "23.12.2.59", "year": 23, "feature": 12, "maintenance": 2, "build": 59, "type": null } +databases: + description: + - The content of the system.databases table with names as keys. + - Be sure your I(login_user) has permissions. + returned: success + type: dict + sample: { "system": "..." } +users: + description: + - The content of the system.users table with names as keys. + - Be sure your I(login_user) has permissions. + returned: success + type: dict + sample: { "default": "..." } +roles: + description: + - The content of the system.roles table with names as keys. + - Be sure your I(login_user) has permissions. + returned: success + type: dict + sample: { "accountant": "..." } +settings: + description: + - The content of the system.settings table with names as keys. + returned: success + type: dict + sample: { "zstd_window_log_max": "..." } ''' Client = None @@ -112,6 +141,8 @@ from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible.module_utils._text import to_native +PRIV_ERR_CODE = 497 + def get_main_conn_kwargs(module): """Retrieves main connection arguments values and translates @@ -144,6 +175,8 @@ def execute_query(module, client, query, execute_kwargs=None): try: result = client.execute(query, **execute_kwargs) except Exception as e: + if "Not enough privileges" in to_native(e): + return PRIV_ERR_CODE module.fail_json(msg="Failed to execute query: %s" % to_native(e)) return result @@ -157,6 +190,9 @@ def get_databases(module, client): query = "SELECT name, engine, data_path, uuid, comment FROM system.databases" result = execute_query(module, client, query) + if result == PRIV_ERR_CODE: + return {PRIV_ERR_CODE: "Not enough privileges"} + db_info = {} for row in result: db_info[row[0]] = { @@ -169,6 +205,55 @@ def get_databases(module, client): return db_info +def get_roles(module, client): + """Get roles. + + Returns a dictionary with roles names as keys. + """ + query = "SELECT name, id, storage FROM system.roles" + result = execute_query(module, client, query) + + if result == PRIV_ERR_CODE: + return {PRIV_ERR_CODE: "Not enough privileges"} + + roles_info = {} + for row in result: + roles_info[row[0]] = { + "id": str(row[1]), + "storage": row[2], + } + + return roles_info + + +def get_settings(module, client): + """Get settings. + + Returns a dictionary with settings names as keys. + """ + query = ("SELECT name, value, changed, description, min, max, readonly, default, " + "is_obsolete FROM system.settings") + result = execute_query(module, client, query) + + if result == PRIV_ERR_CODE: + return {PRIV_ERR_CODE: "Not enough privileges"} + + settings_info = {} + for row in result: + settings_info[row[0]] = { + "value": row[1], + "changed": row[2], + "description": row[3], + "min": row[4], + "max": row[5], + "readonly": row[6], + "default": row[7], + "is_obsolete": row[8], + } + + return settings_info + + def get_users(module, client): """Get users. @@ -179,6 +264,9 @@ def get_users(module, client): "default_roles_list, default_roles_except FROM system.users") result = execute_query(module, client, query) + if result == PRIV_ERR_CODE: + return {PRIV_ERR_CODE: "Not enough privileges"} + user_info = {} for row in result: user_info[row[0]] = { @@ -205,6 +293,9 @@ def get_server_version(module, client): """ result = execute_query(module, client, "SELECT version()") + if result == PRIV_ERR_CODE: + return {PRIV_ERR_CODE: "Not enough privileges"} + raw = result[0][0] split_raw = raw.split('.') @@ -292,7 +383,8 @@ def main(): srv_info['version'] = get_server_version(module, client) srv_info['databases'] = get_databases(module, client) srv_info['users'] = get_users(module, client) - # srv_info['settings'] = get_settings(module, client) + srv_info['roles'] = get_roles(module, client) + srv_info['settings'] = get_settings(module, client) # Close connection client.disconnect_connection() diff --git a/tests/integration/targets/clickhouse_info/tasks/initial.yml b/tests/integration/targets/clickhouse_info/tasks/initial.yml index b8706c5..fb046a8 100644 --- a/tests/integration/targets/clickhouse_info/tasks/initial.yml +++ b/tests/integration/targets/clickhouse_info/tasks/initial.yml @@ -3,6 +3,10 @@ # and should not be used as examples of how to write Ansible roles # #################################################################### +- name: Create role + community.clickhouse.clickhouse_client: + execute: "CREATE ROLE accountant" + - name: Get info register: result community.clickhouse.clickhouse_info: @@ -14,24 +18,29 @@ ansible.builtin.assert: that: - result is not changed + - result["users"]["default"] != {} + - result["roles"]["accountant"] != {} + - result["databases"]["default"]["engine"] == "Atomic" + - result["version"] != {} + - result["driver"]["version"] != {} + - result["settings"]["add_http_cors_header"] != {} - name: Debug ansible.builtin.debug: var: result -#- name: Create user -# community.clickhouse.clickhouse_client: -# execute: "CREATE USER alice IDENTIFIED WITH sha256_password BY 'my_password'" -# -#- name: Query DB using non-default user & DB to connect to -# register: result -# community.clickhouse.clickhouse_info: -# login_host: localhost -# login_user: alice -# login_db: foo -# login_password: my_password +- name: Create user + community.clickhouse.clickhouse_client: + execute: "CREATE USER alice IDENTIFIED WITH sha256_password BY 'my_password'" -#- name: -# ansible.builtin.assert: -# that: -# - result +- name: Query DB using non-default connect params + register: result + community.clickhouse.clickhouse_info: + login_host: localhost + login_user: alice + login_password: my_password + +- name: Check it returned something + ansible.builtin.assert: + that: + - result["users"]["497"] == "Not enough privileges"