From 66eb08bea4dde5ca84581eb412cef39d24a67583 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Mon, 16 Dec 2024 15:51:37 +0100 Subject: [PATCH] Adding Ansible role 389ds-ensure-repl-agmt and using it in playbooks/enable-ldap-server-replication.yaml --- inventory/dpx-ldap-dev1.yaml | 13 ++ inventory/spk-ldap-stage.yaml | 10 ++ playbooks/disable-ldap-server.yaml | 2 +- playbooks/enable-ldap-server-replication.yaml | 4 + .../389ds-ensure-repl-agmt/defaults/main.yaml | 55 +++++++ roles/389ds-ensure-repl-agmt/tasks/main.yaml | 41 +++++ .../tasks/repl-agmts-suffix.yaml | 152 ++++++++++++++++++ 7 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 roles/389ds-ensure-repl-agmt/defaults/main.yaml create mode 100644 roles/389ds-ensure-repl-agmt/tasks/main.yaml create mode 100644 roles/389ds-ensure-repl-agmt/tasks/repl-agmts-suffix.yaml diff --git a/inventory/dpx-ldap-dev1.yaml b/inventory/dpx-ldap-dev1.yaml index 4c592ce..35d884b 100644 --- a/inventory/dpx-ldap-dev1.yaml +++ b/inventory/dpx-ldap-dev1.yaml @@ -55,6 +55,12 @@ all: - 'o=isp' 'blaBlub': ensure: absent + ds389_repl_agmt_frac_list: + - 'authorityRevocationList' + - 'accountUnlockTime' + - 'memberOf' + ds389_repl_agmt_frac_list_total: + - 'memberOf' # Tempporary # ds389_logging_config: false @@ -70,6 +76,13 @@ all: vars: ansible_user: root haproxy_backend_name: 'be-ldap-dev1' + ds389_repl_agmt_port: 389 + ds389_repl_agmt_conn_proto: 'LDAP' + ds389_repl_agmt_bind_method: 'SIMPLE' + ds389_repl_agmt_frac_list: + - 'authorityRevocationList' + - 'accountUnlockTime' + - 'memberOf' # vim: filetype=yaml diff --git a/inventory/spk-ldap-stage.yaml b/inventory/spk-ldap-stage.yaml index bc88321..4be4adb 100644 --- a/inventory/spk-ldap-stage.yaml +++ b/inventory/spk-ldap-stage.yaml @@ -11,6 +11,16 @@ all: replica_id: 2 slapd_instance: stage-ds02-spk ldap_uri: 'ldaps://stage-ds02-spk.spk.pixelpark.net' + vars: + ds389_repl_agmt_port: 389 + ds389_repl_agmt_conn_proto: 'LDAP' + ds389_repl_agmt_bind_method: 'SIMPLE' + ds389_repl_agmt_frac_list: + - 'authorityRevocationList' + - 'accountUnlockTime' + - 'memberOf' + ds389_repl_agmt_frac_list_total: + - 'memberOf' haproxy_servers: hosts: live-ldap-hap01.spk.pixelpark.net: {} diff --git a/playbooks/disable-ldap-server.yaml b/playbooks/disable-ldap-server.yaml index c1c4198..ebe3e29 100644 --- a/playbooks/disable-ldap-server.yaml +++ b/playbooks/disable-ldap-server.yaml @@ -131,7 +131,7 @@ state: stopped when: ldapserver_to_disable == inventory_hostname - - name: "Retrieve all backends from {{ ldapserver_to_disable | quote }}." + - name: "Retrieve all backends." ansible.builtin.shell: "dsconf {{ slapd_instance | quote }} backend suffix list" register: backend_suffix_list changed_when: false diff --git a/playbooks/enable-ldap-server-replication.yaml b/playbooks/enable-ldap-server-replication.yaml index 022280b..09cd1c4 100644 --- a/playbooks/enable-ldap-server-replication.yaml +++ b/playbooks/enable-ldap-server-replication.yaml @@ -78,5 +78,9 @@ include_role: name: '389ds-enable-replication' + - name: 'Ensuring all necessary replication agreements.' + include_role: + name: '389ds-ensure-repl-agmt' + # vim: filetype=yaml diff --git a/roles/389ds-ensure-repl-agmt/defaults/main.yaml b/roles/389ds-ensure-repl-agmt/defaults/main.yaml new file mode 100644 index 0000000..0e4b9ae --- /dev/null +++ b/roles/389ds-ensure-repl-agmt/defaults/main.yaml @@ -0,0 +1,55 @@ +--- + +# Sets the port number of the replica +ds389_repl_agmt_port: 389 + +# Sets the replication connection protocol: LDAP, LDAPS, or StartTLS +# Possible values: 'LDAP', 'LDAPS', 'StartTLS' +ds389_repl_agmt_conn_proto: 'LDAP' + +# Sets the bind method. +# Possible values: "SIMPLE", "SSLCLIENTAUTH", "SASL/DIGEST", or "SASL/GSSAPI" +ds389_repl_agmt_bind_method: 'SIMPLE' + +# Sets the list of attributes to NOT replicate to the consumer during incremental updates +ds389_repl_agmt_frac_list: + - 'authorityRevocationList' + - 'accountUnlockTime' + - 'memberOf' + +# Sets the list of attributes to NOT replicate during a total initialization +ds389_repl_agmt_frac_list_total: + - 'memberOf' + +# Sets a list of attributes that are removed from updates only if the event would otherwise be empty. +ds389_repl_agmt_strip_list: + - 'modifiersName' + - 'modifyTimestmap' + +# Sets the replication update schedule: 'HHMM-HHMM DDDDDDD' D = 0-6 (Sunday - Saturday). +# ds389_repl_agmt_schedule: '0500-2100 0123456' +ds389_repl_agmt_schedule: ~ + +# Sets the timeout used for replication connections +ds389_repl_agmt_conn_timeout: 300 + +# Sets a timeout in seconds on how long to wait before stopping replication when the server is under load +ds389_repl_agmt_protocol_timeout: 600 + +# Sets the amount of time in milliseconds the server waits if the consumer is not ready before resending data. +ds389_repl_agmt_wait_async_results: 3000 + +# Sets the amount of time in seconds a supplier should wait +# after a consumer sends back a busy response before making another attempt to acquire access. +ds389_repl_agmt_busy_wait_time: 300 + +# Sets the amount of time in seconds a supplier should wait between update sessions. +ds389_repl_agmt_session_pause_time: ~ + +# Sets the maximum number of entries and updates sent by a supplier, which are not acknowledged by the consumer. +ds389_repl_agmt_flow_control_window: ~ + +# Sets the time in milliseconds to pause after reaching the number of entries and updates set in 'ds389_repl_agmt_flow_control_window'. +ds389_repl_agmt_flow_control_pause: ~ + +# vim: filetype=yaml diff --git a/roles/389ds-ensure-repl-agmt/tasks/main.yaml b/roles/389ds-ensure-repl-agmt/tasks/main.yaml new file mode 100644 index 0000000..4b6bfaa --- /dev/null +++ b/roles/389ds-ensure-repl-agmt/tasks/main.yaml @@ -0,0 +1,41 @@ +--- + +- debug: + msg: "Ensuring existence of all necessary replication agreements." + +- name: "Retrieve all backends." + ansible.builtin.shell: "dsconf {{ slapd_instance | quote }} backend suffix list" + register: get_backend_suffix_list + changed_when: false + check_mode: false + +- name: "Show current get_backend_suffix_list" + debug: + var: get_backend_suffix_list + verbosity: 2 + +- name: "Set backend variable" + no_log: true + set_fact: + suffix_names: "{{ get_backend_suffix_list.stdout_lines | map('regex_replace', '\\s+\\(.+\\)\\s*$', '') | list }}" + backend_names: "{{ get_backend_suffix_list.stdout_lines | map('regex_replace', '^.*\\((.+)\\)\\s*$', '\\1') | list }}" + +- name: "Set suffixes dict" + no_log: true + set_fact: + suffixes: "{{ dict( suffix_names | zip(backend_names) ) }}" + +- name: "Show current suffixes" + debug: + var: suffixes + verbosity: 1 + +- name: "Ensure replication agreements on all suffixes." + include_tasks: 'repl-agmts-suffix.yaml' + when: item[1] != inventory_hostname + vars: + suffix: "{{ item[0].key }}" + other_host: "{{ item[1] }}" + loop: "{{ suffixes | dict2items | product( ansible_play_batch ) | list }}" + +# vim: filetype=yaml diff --git a/roles/389ds-ensure-repl-agmt/tasks/repl-agmts-suffix.yaml b/roles/389ds-ensure-repl-agmt/tasks/repl-agmts-suffix.yaml new file mode 100644 index 0000000..ad5abef --- /dev/null +++ b/roles/389ds-ensure-repl-agmt/tasks/repl-agmts-suffix.yaml @@ -0,0 +1,152 @@ +--- + +- debug: + msg: "Ensuring existence of replication agreement from {{ inventory_hostname | quote }} \ + to {{ other_host | quote }} for suffix {{ suffix | quote }}." + verbosity: 0 + +- name: "Set fact agreement_name." + set_fact: + agreement_name: "{{ slapd_instance }} to {{ other_host }} agreement" + +- name: "Show replication agreement name for suffix {{ suffix | quote }}." + debug: + var: agreement_name + verbosity: 0 + +- name: "Get list of current replication agreements for suffix {{ suffix | quote }}." + ansible.builtin.shell: "dsconf {{ slapd_instance | quote }} repl-agmt list --suffix {{ suffix | quote }} | \ + grep -i '^cn: ' | sed -e 's/^cn:[ ]*//i' -e 's/[ ]*$//'" + register: get_repl_agmt_list + changed_when: false + check_mode: false + +- name: "Show current get_repl_agmt_list" + debug: + var: get_repl_agmt_list + verbosity: 2 + +- name: "Predefine repl_agmt_exists" + set_fact: + repl_agmt_exists: false + +- name: "Search for current replication agreement." + set_fact: + repl_agmt_exists: true + when: (line | lower) == (agreement_name | lower) + loop: "{{ get_repl_agmt_list.stdout_lines }}" + loop_control: + loop_var: line + +- name: "Replication agreement already exists." + when: repl_agmt_exists == true + block: + + - debug: + msg: "The replication agreement {{ agreement_name | quote }} is already existing for suffix {{ suffix | quote }}." + +- name: "Create replication agreement." + # when: repl_agmt_exists != true + block: + + - debug: + var: hostvars[other_host] + verbosity: 3 + + - name: "Predefine variables." + set_fact: + used_port: "{{ ds389_repl_agmt_port }}" + used_proto: "{{ ds389_repl_agmt_conn_proto }}" + used_frac_list: "{{ ds389_repl_agmt_frac_list }}" + used_frac_list_total: "{{ ds389_repl_agmt_frac_list_total }}" + + - name: "Get port from other host." + set_fact: + used_port: "{{ hostvars[other_host].ds389_repl_agmt_port }}" + when: "'ds389_repl_agmt_port' in hostvars[other_host] and hostvars[other_host].ds389_repl_agmt_port is not empty" + + - name: "Get connection protocol from other host." + set_fact: + used_conn_proto: "{{ hostvars[other_host].ds389_repl_agmt_conn_proto }}" + when: "'ds389_repl_agmt_conn_proto' in hostvars[other_host] and hostvars[other_host].ds389_repl_agmt_conn_proto is not empty" + + - name: "Get fractional list from other host." + set_fact: + used_frac_list: "{{ hostvars[other_host].ds389_repl_agmt_frac_list }}" + when: "'ds389_repl_agmt_frac_list' in hostvars[other_host] and hostvars[other_host].ds389_repl_agmt_frac_list is not empty" + + - name: "Get fractional list for total from other host." + set_fact: + used_frac_list_total: "{{ hostvars[other_host].ds389_repl_agmt_frac_list_total }}" + when: "'ds389_repl_agmt_frac_list_total' in hostvars[other_host] and hostvars[other_host].ds389_repl_agmt_frac_list_total is not empty" + + - name: "Define command for creating replication agreement." + set_fact: + create_cmd: "dsconf {{ slapd_instance | quote }} repl-agmt create --suffix {{ suffix | quote }} \ + --host {{ other_host | quote }} --port {{ used_port | int }} \ + --conn-protocol {{ used_conn_proto | quote }} \ + --bind-dn {{ replication_manager_dn | quote }} \ + --bind-passwd-file {{ replication_manager_password_file | quote }} \ + --bind-method {{ ds389_repl_agmt_bind_method | quote }} \ + --frac-list {{ used_frac_list | map('quote') | join(' ') }} \ + --frac-list-total {{ used_frac_list_total | map('quote') | join(' ') }}" + + - name: "Add --strip-list to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --strip-list {{ ds389_repl_agmt_strip_list | map('quote') | join(' ') }}" + when: ds389_repl_agmt_strip_list is not empty + + - name: "Add --schedule to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --schedule {{ ds389_repl_agmt_schedule | quote }}" + when: ds389_repl_agmt_schedule is not empty + + - name: "Add --conn-timeout to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --conn-timeout {{ ds389_repl_agmt_conn_timeout | quote }}" + when: ds389_repl_agmt_conn_timeout is not empty + + - name: "Add --protocol-timeout to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --protocol-timeout {{ ds389_repl_agmt_protocol_timeout | quote }}" + when: ds389_repl_agmt_protocol_timeout is not empty + + - name: "Add --wait-async-results to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --wait-async-results {{ ds389_repl_agmt_wait_async_results | quote }}" + when: ds389_repl_agmt_wait_async_results is not empty + + - name: "Add --busy-wait-time to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --busy-wait-time {{ ds389_repl_agmt_busy_wait_time | quote }}" + when: ds389_repl_agmt_busy_wait_time is not empty + + - name: "Add --session-pause-time to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --session-pause-time {{ cds389_repl_agmt_session_pause_time | quote }}" + when: cds389_repl_agmt_session_pause_time is not empty + + - name: "Add --flow-control-window to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --flow-control-window {{ ds389_repl_agmt_flow_control_window | quote }}" + when: ds389_repl_agmt_flow_control_window is not empty + + - name: "Add --flow-control-pause to command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} --flow-control-pause {{ ds389_repl_agmt_flow_control_pause | quote }}" + when: ds389_repl_agmt_flow_control_pause is not empty + + - name: "Complete command for creating replication agreement." + set_fact: + create_cmd: "{{ create_cmd }} {{ agreement_name | quote }}" + + - name: "Command for creating replication agreement:" + debug: + var: create_cmd + verbosity: 0 + + - name: "Finally create the replication agreement." + ansible.builtin.shell: "{{ create_cmd }}" + + +# vim: filetype=yaml -- 2.39.5