postgresql_info: add subscription info (#67464)
* postgresql_info: add subscription info * add changelog
This commit is contained in:
parent
23995fef48
commit
0dc08f6b97
8 changed files with 236 additions and 79 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- postgresql_info - add collection info about replication subscriptions (https://github.com/ansible/ansible/pull/67464).
|
|
@ -26,7 +26,7 @@ options:
|
||||||
- Limit the collected information by comma separated string or YAML list.
|
- Limit the collected information by comma separated string or YAML list.
|
||||||
- Allowable values are C(version),
|
- Allowable values are C(version),
|
||||||
C(databases), C(settings), C(tablespaces), C(roles),
|
C(databases), C(settings), C(tablespaces), C(roles),
|
||||||
C(replications), C(repl_slots).
|
C(replications), C(repl_slots), C(subscriptions) (since 2.10).
|
||||||
- By default, collects all subsets.
|
- By default, collects all subsets.
|
||||||
- You can use shell-style (fnmatch) wildcard to pass groups of values (see Examples).
|
- You can use shell-style (fnmatch) wildcard to pass groups of values (see Examples).
|
||||||
- You can use '!' before value (for example, C(!settings)) to exclude it from the information.
|
- You can use '!' before value (for example, C(!settings)) to exclude it from the information.
|
||||||
|
@ -453,6 +453,15 @@ settings:
|
||||||
returned: always
|
returned: always
|
||||||
type: bool
|
type: bool
|
||||||
sample: false
|
sample: false
|
||||||
|
subscriptions:
|
||||||
|
description:
|
||||||
|
- Information about replication subscriptions (available for PostgreSQL 10 and higher)
|
||||||
|
U(https://www.postgresql.org/docs/current/logical-replication-subscription.html).
|
||||||
|
- Content depends on PostgreSQL server version.
|
||||||
|
returned: if configured
|
||||||
|
type: dict
|
||||||
|
sample:
|
||||||
|
- {"acme_db": {"my_subscription": {"ownername": "postgres", "subenabled": true, "subpublications": ["first_publication"]}}}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
|
@ -470,6 +479,7 @@ from ansible.module_utils.postgres import (
|
||||||
get_conn_params,
|
get_conn_params,
|
||||||
postgres_common_argument_spec,
|
postgres_common_argument_spec,
|
||||||
)
|
)
|
||||||
|
from ansible.module_utils.six import iteritems
|
||||||
from ansible.module_utils._text import to_native
|
from ansible.module_utils._text import to_native
|
||||||
|
|
||||||
|
|
||||||
|
@ -533,6 +543,7 @@ class PgClusterInfo(object):
|
||||||
"repl_slots": {},
|
"repl_slots": {},
|
||||||
"settings": {},
|
"settings": {},
|
||||||
"roles": {},
|
"roles": {},
|
||||||
|
"subscriptions": {},
|
||||||
"pending_restart_settings": [],
|
"pending_restart_settings": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,6 +557,7 @@ class PgClusterInfo(object):
|
||||||
"repl_slots": self.get_rslot_info,
|
"repl_slots": self.get_rslot_info,
|
||||||
"settings": self.get_settings,
|
"settings": self.get_settings,
|
||||||
"roles": self.get_role_info,
|
"roles": self.get_role_info,
|
||||||
|
"subscriptions": self.get_subscr_info,
|
||||||
}
|
}
|
||||||
|
|
||||||
incl_list = []
|
incl_list = []
|
||||||
|
@ -588,6 +600,36 @@ class PgClusterInfo(object):
|
||||||
|
|
||||||
return self.pg_info
|
return self.pg_info
|
||||||
|
|
||||||
|
def get_subscr_info(self):
|
||||||
|
"""Get subscription statistics and fill out self.pg_info dictionary."""
|
||||||
|
if self.cursor.connection.server_version < 10000:
|
||||||
|
return
|
||||||
|
|
||||||
|
query = ("SELECT s.*, r.rolname AS ownername, d.datname AS dbname "
|
||||||
|
"FROM pg_catalog.pg_subscription s "
|
||||||
|
"JOIN pg_catalog.pg_database d "
|
||||||
|
"ON s.subdbid = d.oid "
|
||||||
|
"JOIN pg_catalog.pg_roles AS r "
|
||||||
|
"ON s.subowner = r.oid")
|
||||||
|
|
||||||
|
result = self.__exec_sql(query)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
result = [dict(row) for row in result]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
for elem in result:
|
||||||
|
if not self.pg_info['subscriptions'].get(elem['dbname']):
|
||||||
|
self.pg_info['subscriptions'][elem['dbname']] = {}
|
||||||
|
|
||||||
|
if not self.pg_info['subscriptions'][elem['dbname']].get(elem['subname']):
|
||||||
|
self.pg_info['subscriptions'][elem['dbname']][elem['subname']] = {}
|
||||||
|
|
||||||
|
for key, val in iteritems(elem):
|
||||||
|
if key not in ('subname', 'dbname'):
|
||||||
|
self.pg_info['subscriptions'][elem['dbname']][elem['subname']][key] = val
|
||||||
|
|
||||||
def get_tablespaces(self):
|
def get_tablespaces(self):
|
||||||
"""Get information about tablespaces."""
|
"""Get information about tablespaces."""
|
||||||
# Check spcoption exists:
|
# Check spcoption exists:
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
destructive
|
destructive
|
||||||
shippable/posix/group4
|
shippable/posix/group1
|
||||||
skip/aix
|
skip/aix
|
||||||
skip/osx
|
skip/osx
|
||||||
|
skip/centos
|
||||||
|
skip/freebsd
|
||||||
|
skip/rhel
|
||||||
|
skip/opensuse
|
||||||
|
skip/fedora
|
||||||
|
|
|
@ -1,2 +1,15 @@
|
||||||
---
|
---
|
||||||
|
pg_user: postgres
|
||||||
db_default: postgres
|
db_default: postgres
|
||||||
|
master_port: 5433
|
||||||
|
replica_port: 5434
|
||||||
|
|
||||||
|
test_table1: acme1
|
||||||
|
test_pub: first_publication
|
||||||
|
test_pub2: second_publication
|
||||||
|
replication_role: logical_replication
|
||||||
|
replication_pass: alsdjfKJKDf1#
|
||||||
|
test_db: acme_db
|
||||||
|
test_subscription: test
|
||||||
|
test_subscription2: test2
|
||||||
|
conn_timeout: 100
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
- setup_postgresql_db
|
- setup_postgresql_replication
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
|
# For testing getting publication and subscription info
|
||||||
|
- import_tasks: setup_publication.yml
|
||||||
|
when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18'
|
||||||
|
|
||||||
# Initial CI tests of postgresql_info module
|
# Initial CI tests of postgresql_info module
|
||||||
- import_tasks: postgresql_info_initial.yml
|
- import_tasks: postgresql_info_initial.yml
|
||||||
|
when: ansible_distribution == 'Ubuntu' and ansible_distribution_major_version >= '18'
|
||||||
|
|
|
@ -1,28 +1,67 @@
|
||||||
# Test code for the postgresql_info module
|
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
|
||||||
# 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)
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
- name: postgresql_info - create role to check session_role
|
- vars:
|
||||||
become_user: "{{ pg_user }}"
|
task_parameters: &task_parameters
|
||||||
become: yes
|
become_user: '{{ pg_user }}'
|
||||||
postgresql_user:
|
become: yes
|
||||||
db: "{{ db_default }}"
|
register: result
|
||||||
login_user: "{{ pg_user }}"
|
pg_parameters: &pg_parameters
|
||||||
name: session_superuser
|
login_user: '{{ pg_user }}'
|
||||||
role_attr_flags: SUPERUSER
|
login_db: '{{ db_default }}'
|
||||||
|
|
||||||
- name: postgresql_info - test return values and session_role param
|
block:
|
||||||
become_user: "{{ pg_user }}"
|
|
||||||
become: yes
|
|
||||||
postgresql_info:
|
|
||||||
db: "{{ db_default }}"
|
|
||||||
login_user: "{{ pg_user }}"
|
|
||||||
session_role: session_superuser
|
|
||||||
register: result
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- assert:
|
- name: Create test subscription
|
||||||
that:
|
<<: *task_parameters
|
||||||
|
postgresql_subscription:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ replica_port }}'
|
||||||
|
name: '{{ test_subscription }}'
|
||||||
|
login_db: '{{ test_db }}'
|
||||||
|
state: present
|
||||||
|
publications: '{{ test_pub }}'
|
||||||
|
connparams:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: '{{ master_port }}'
|
||||||
|
user: '{{ replication_role }}'
|
||||||
|
password: '{{ replication_pass }}'
|
||||||
|
dbname: '{{ test_db }}'
|
||||||
|
|
||||||
|
- name: Create test subscription
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_subscription:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ replica_port }}'
|
||||||
|
name: '{{ test_subscription2 }}'
|
||||||
|
login_db: '{{ test_db }}'
|
||||||
|
state: present
|
||||||
|
publications: '{{ test_pub2 }}'
|
||||||
|
connparams:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: '{{ master_port }}'
|
||||||
|
user: '{{ replication_role }}'
|
||||||
|
password: '{{ replication_pass }}'
|
||||||
|
dbname: '{{ test_db }}'
|
||||||
|
|
||||||
|
- name: postgresql_info - create role to check session_role
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_user:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ replica_port }}'
|
||||||
|
login_user: "{{ pg_user }}"
|
||||||
|
name: session_superuser
|
||||||
|
role_attr_flags: SUPERUSER
|
||||||
|
|
||||||
|
- name: postgresql_info - test return values and session_role param
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_info:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ replica_port }}'
|
||||||
|
session_role: session_superuser
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
- result.version != {}
|
- result.version != {}
|
||||||
- result.databases.{{ db_default }}.collate
|
- result.databases.{{ db_default }}.collate
|
||||||
- result.databases.{{ db_default }}.languages
|
- result.databases.{{ db_default }}.languages
|
||||||
|
@ -31,21 +70,39 @@
|
||||||
- result.settings
|
- result.settings
|
||||||
- result.tablespaces
|
- result.tablespaces
|
||||||
- result.roles
|
- result.roles
|
||||||
|
- result.subscriptions.{{ test_db }}.{{ test_subscription }}
|
||||||
|
- result.subscriptions.{{ test_db }}.{{ test_subscription2 }}
|
||||||
|
|
||||||
- name: postgresql_info - check filter param passed by list
|
- name: postgresql_info - check filter param passed by list
|
||||||
become_user: "{{ pg_user }}"
|
<<: *task_parameters
|
||||||
become: yes
|
postgresql_info:
|
||||||
postgresql_info:
|
<<: *pg_parameters
|
||||||
db: "{{ db_default }}"
|
login_port: '{{ replica_port }}'
|
||||||
login_user: "{{ pg_user }}"
|
filter:
|
||||||
filter:
|
- ver*
|
||||||
- ver*
|
- rol*
|
||||||
- rol*
|
- subscr*
|
||||||
register: result
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
|
- result.version != {}
|
||||||
|
- result.roles
|
||||||
|
- result.subscriptions.{{ test_db }}.{{ test_subscription }} != {}
|
||||||
|
- result.subscriptions.{{ test_db }}.{{ test_subscription2 }} != {}
|
||||||
|
- result.databases == {}
|
||||||
|
- result.repl_slots == {}
|
||||||
|
- result.replications == {}
|
||||||
|
- result.settings == {}
|
||||||
|
- result.tablespaces == {}
|
||||||
|
|
||||||
|
- name: postgresql_info - check filter param passed by string
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_info:
|
||||||
|
<<: *pg_parameters
|
||||||
|
filter: ver*,role*
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
- result.version != {}
|
- result.version != {}
|
||||||
- result.roles
|
- result.roles
|
||||||
- result.databases == {}
|
- result.databases == {}
|
||||||
|
@ -54,55 +111,27 @@
|
||||||
- result.settings == {}
|
- result.settings == {}
|
||||||
- result.tablespaces == {}
|
- result.tablespaces == {}
|
||||||
|
|
||||||
- name: postgresql_info - check filter param passed by string
|
- name: postgresql_info - check filter param passed by string
|
||||||
become_user: "{{ pg_user }}"
|
<<: *task_parameters
|
||||||
become: yes
|
postgresql_info:
|
||||||
postgresql_info:
|
<<: *pg_parameters
|
||||||
db: "{{ db_default }}"
|
filter: ver*
|
||||||
filter: ver*,role*
|
|
||||||
login_user: "{{ pg_user }}"
|
|
||||||
register: result
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
- result.version != {}
|
|
||||||
- result.roles
|
|
||||||
- result.databases == {}
|
|
||||||
- result.repl_slots == {}
|
|
||||||
- result.replications == {}
|
|
||||||
- result.settings == {}
|
|
||||||
- result.tablespaces == {}
|
|
||||||
|
|
||||||
- name: postgresql_info - check filter param passed by string
|
|
||||||
become_user: "{{ pg_user }}"
|
|
||||||
become: yes
|
|
||||||
postgresql_info:
|
|
||||||
db: "{{ db_default }}"
|
|
||||||
filter: ver*
|
|
||||||
login_user: "{{ pg_user }}"
|
|
||||||
register: result
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- assert:
|
|
||||||
that:
|
|
||||||
- result.version
|
- result.version
|
||||||
- result.roles == {}
|
- result.roles == {}
|
||||||
|
|
||||||
- name: postgresql_info - check excluding filter param passed by list
|
- name: postgresql_info - check excluding filter param passed by list
|
||||||
become_user: "{{ pg_user }}"
|
<<: *task_parameters
|
||||||
become: yes
|
postgresql_info:
|
||||||
postgresql_info:
|
<<: *pg_parameters
|
||||||
db: "{{ db_default }}"
|
filter:
|
||||||
filter:
|
|
||||||
- "!ver*"
|
- "!ver*"
|
||||||
- "!rol*"
|
- "!rol*"
|
||||||
login_user: "{{ pg_user }}"
|
|
||||||
register: result
|
|
||||||
ignore_errors: yes
|
|
||||||
|
|
||||||
- assert:
|
- assert:
|
||||||
that:
|
that:
|
||||||
- result.version == {}
|
- result.version == {}
|
||||||
- result.roles == {}
|
- result.roles == {}
|
||||||
- result.databases
|
- result.databases
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# 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 for further tests of postgresql_subscription module.
|
||||||
|
|
||||||
|
- vars:
|
||||||
|
task_parameters: &task_parameters
|
||||||
|
become_user: '{{ pg_user }}'
|
||||||
|
become: yes
|
||||||
|
register: result
|
||||||
|
pg_parameters: &pg_parameters
|
||||||
|
login_user: '{{ pg_user }}'
|
||||||
|
login_db: '{{ test_db }}'
|
||||||
|
|
||||||
|
block:
|
||||||
|
- name: Create test db
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_db:
|
||||||
|
login_user: '{{ pg_user }}'
|
||||||
|
login_port: '{{ master_port }}'
|
||||||
|
maintenance_db: '{{ db_default }}'
|
||||||
|
name: '{{ test_db }}'
|
||||||
|
|
||||||
|
- name: Create test role
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_user:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ master_port }}'
|
||||||
|
name: '{{ replication_role }}'
|
||||||
|
password: '{{ replication_pass }}'
|
||||||
|
role_attr_flags: LOGIN,REPLICATION
|
||||||
|
|
||||||
|
- name: Create test table
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_table:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ master_port }}'
|
||||||
|
name: '{{ test_table1 }}'
|
||||||
|
columns:
|
||||||
|
- id int
|
||||||
|
|
||||||
|
- name: Master - dump schema
|
||||||
|
<<: *task_parameters
|
||||||
|
shell: pg_dumpall -p '{{ master_port }}' -s > /tmp/schema.sql
|
||||||
|
|
||||||
|
- name: Replicat restore schema
|
||||||
|
<<: *task_parameters
|
||||||
|
shell: psql -p '{{ replica_port }}' -f /tmp/schema.sql
|
||||||
|
|
||||||
|
- name: Create publication
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_publication:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ master_port }}'
|
||||||
|
name: '{{ test_pub }}'
|
||||||
|
|
||||||
|
- name: Create publication
|
||||||
|
<<: *task_parameters
|
||||||
|
postgresql_publication:
|
||||||
|
<<: *pg_parameters
|
||||||
|
login_port: '{{ master_port }}'
|
||||||
|
name: '{{ test_pub2 }}'
|
Loading…
Reference in a new issue