Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/mongodb_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ The following table contains the most commonly overridden variables.
| `mongodb_tls_enabled` | Boolean | Flag to enable MongoDB TLS. | `false` |
| `mongodb_user_admin_password` | String | The MongoDB admin user password. | `admin` |
| `mongodb_user_itential_password` | String | The MongoDB itential user password. | `itential` |
| `mongodb_preferred_primary` | String | Node hostname that will be considered primary, empty means the first hostname in the inventory | `` |

> :warning: It is assumed that these default passwords will be changed to meet more rigorous
security standards. These are intended to be defaults strictly used just for ease of the
Expand Down Expand Up @@ -301,3 +302,9 @@ state that the installation tag produced you can run this command:
```bash
ansible-playbook itential.deployer.mongodb -i <inventory> --tags initialize_mongo_config
```

This tag is used to dynamically adjust the **MongoDB replica set member priorities** and influence which node becomes the **primary**:

```bash
ansible-playbook itential.deployer.mongodb -i <inventory> --tags reconfigure_priority
```
2 changes: 1 addition & 1 deletion plugins/modules/mongodb_config_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def run_module():

uri = build_connection_string(module.params)

client = MongoClient(uri)
client = MongoClient(uri, directConnection=True)
database = client.get_database("admin")
hello = database.command("hello")

Expand Down
8 changes: 8 additions & 0 deletions roles/mongodb/defaults/main/mongodb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,11 @@ mongodb_user_itential_password: itential

# The name of the mongo replica set
mongodb_replset_name: "{{ mongodb_replset_name_default }}"

# Preferred primary member for MongoDB replica sets.
# Leave empty to use the first host in the mongodb inventory group.
mongodb_preferred_primary: ""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of setting the default to empty, why not default it to the first server in the list? So something like:

Suggested change
mongodb_preferred_primary: ""
mongodb_preferred_primary: "{{ groups.mongodb[0] }}"

That way you don't have to write any code to set it.


# Replica set member priorities.
mongodb_primary_priority: 10
mongodb_secondary_priority: 5
192 changes: 185 additions & 7 deletions roles/mongodb/tasks/configure-mongodb-replicaset.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
register: mongodb_state
vars:
ansible_python_interpreter: "{{ mongodb_python_venv }}/bin/python3"
tags: reconfigure_priority
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we need this tag. And if it is needed, I'm not sure it's name appropriately.


- name: Print MongoDB configuration state
ansible.builtin.debug:
msg: "{{ mongodb_state }}"
tags: reconfigure_priority

# Execute the template to apply changes to the mongo.conf for replication
- name: Create MongoDB config file (replicaset)
Expand Down Expand Up @@ -56,28 +58,116 @@
- name: Set empty array of mongo servers
ansible.builtin.set_fact:
mongodb_servers: []
when: not mongodb_state.replication_enabled
when:
- inventory_hostname in groups.mongodb
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need any of these when clauses. The role is only executed on mongodb hosts.

- groups.mongodb.index(inventory_hostname) == 0
tags: reconfigure_priority

- name: Set preferred primary host
ansible.builtin.set_fact:
mongodb_preferred_primary_host: "{{ groups.mongodb[0] }}"
mongodb_preferred_primary_resolved: "{{ mongodb_preferred_primary | default('', true) | length == 0 }}"
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
tags: reconfigure_priority

- name: Normalize preferred primary override
ansible.builtin.set_fact:
mongodb_preferred_primary_input: >-
{{ mongodb_preferred_primary | regex_replace(':' + (mongodb_port | string) + '$', '') }}
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- mongodb_preferred_primary | default('', true) | length > 0
tags: reconfigure_priority

- name: Resolve preferred primary override by inventory hostname
ansible.builtin.set_fact:
mongodb_preferred_primary_host: "{{ mongodb_preferred_primary_input }}"
mongodb_preferred_primary_resolved: true
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- mongodb_preferred_primary | default('', true) | length > 0
- mongodb_preferred_primary_input in groups.mongodb
tags: reconfigure_priority

- name: Resolve preferred primary override by ansible_host or short hostname
ansible.builtin.set_fact:
mongodb_preferred_primary_host: "{{ item }}"
mongodb_preferred_primary_resolved: true
loop: "{{ groups.mongodb }}"
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- mongodb_preferred_primary | default('', true) | length > 0
- mongodb_preferred_primary_input not in groups.mongodb
- mongodb_preferred_primary_input in [
item,
(item.split('.')[0]),
(hostvars[item].ansible_host | default(''))
]
tags: reconfigure_priority

- name: Validate preferred primary override
ansible.builtin.assert:
that:
- mongodb_preferred_primary_resolved | bool
Comment on lines +115 to +116
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
that:
- mongodb_preferred_primary_resolved | bool
that: mongodb_preferred_primary_resolved | bool

fail_msg: >-
mongodb_preferred_primary must match a host in groups.mongodb.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mongodb_preferred_primary must match a host in groups.mongodb.
mongodb_preferred_primary must match a host in groups.mongodb

Received {{ mongodb_preferred_primary }}.
Valid inventory hosts: {{ groups.mongodb | join(', ') }}
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- mongodb_preferred_primary | default('', true) | length > 0
tags: reconfigure_priority

# This task should always run, arbiter or not
- name: Create the replicaset members list (no arbiter)
ansible.builtin.set_fact:
mongodb_servers: "{{ mongodb_servers + [item + ':' + mongodb_port | string] }}"
with_items: "{{ groups.mongodb }}"
mongodb_servers: >-
{{ mongodb_servers + [{'host': item + ':' + (mongodb_port | string),
'priority': ((item == mongodb_preferred_primary_host) |
ternary(mongodb_primary_priority, mongodb_secondary_priority))}]
}}
loop: "{{ groups.mongodb }}"
when:
- not mongodb_state.replication_enabled
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
tags: reconfigure_priority

# This task will only run when there is an arbiter defined in the hosts file
- name: Add the arbiter to the list of servers when there is one
ansible.builtin.set_fact:
mongodb_servers: "{{ mongodb_servers + [item + ':' + mongodb_port | string] }}"
with_items: "{{ groups.mongodb_arbiter }}"
mongodb_servers: >-
{{ mongodb_servers + [{'host': item + ':' + (mongodb_port | string), 'priority': 0}] }}
loop: "{{ groups.mongodb_arbiter }}"
when:
- not mongodb_state.replication_enabled
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- groups.mongodb_arbiter is defined
tags: reconfigure_priority

- name: Debug replication state
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- name: Debug replication state
- name: Print replication state

ansible.builtin.debug:
msg: "replication_enabled={{ mongodb_state.replication_enabled }} primary={{ mongodb_state.primary }} members={{ mongodb_state.members }}"
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
tags: reconfigure_priority

- name: Wait for all MongoDB members to be reachable before creating the replicaset
ansible.builtin.wait_for:
host: "{{ item }}"
port: "{{ mongodb_port }}"
timeout: 60
loop: "{{ groups.mongodb + (groups.mongodb_arbiter | default([])) }}"
when:
- not mongodb_state.replication_enabled
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0

- name: Create the replicaset
community.mongodb.mongodb_replicaset:
Expand Down Expand Up @@ -119,6 +209,94 @@
vars:
ansible_python_interpreter: "{{ mongodb_python_venv }}/bin/python3"

- name: Refresh MongoDB configuration state after replicaset changes
itential.deployer.mongodb_config_state:
login_database: admin
login_host: "{{ inventory_hostname }}"
login_port: "{{ mongodb_port }}"
register: mongodb_state_after_replicaset
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
vars:
ansible_python_interpreter: "{{ mongodb_python_venv }}/bin/python3"
tags: reconfigure_priority

- name: Reconfigure the replicaset priorities
community.mongodb.mongodb_replicaset:
arbiter_at_index: "{{ (groups.mongodb_arbiter | default([]) | length > 0) | ternary(mongodb_servers | length - 1, omit) }}"
auth_mechanism: "SCRAM-SHA-256"
login_user: "{{ (mongodb_state.auth_enabled | bool) | ternary(mongodb_user_admin, omit) }}"
login_password: "{{ (mongodb_state.auth_enabled | bool) | ternary(mongodb_user_admin_password, omit) }}"
login_port: "{{ mongodb_port }}"
login_database: admin
login_host: "{{ mongodb_state_after_replicaset.primary | regex_replace(':.*$', '') }}"
members: "{{ mongodb_servers }}"
replica_set: "{{ mongodb_replset_name }}"
reconfigure: true
validate: true
register: reconfig_result
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registered variables should be prefixed by the role name.

Suggested change
register: reconfig_result
register: mongodb_reconfig_result

when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- mongodb_state_after_replicaset.primary is defined
- mongodb_state_after_replicaset.primary | length > 0
vars:
ansible_python_interpreter: "{{ mongodb_python_venv }}/bin/python3"
tags: reconfigure_priority

- name: Print reconfigure result
ansible.builtin.debug:
msg: "{{ reconfig_result }}"
when:
- reconfig_result is defined
- reconfig_result is not skipped
tags: reconfigure_priority

- name: Step down non-preferred primary after replicaset changes
community.mongodb.mongodb_shell:
mongo_cmd: auto
login_user: "{{ mongodb_state.auth_enabled | ternary(mongodb_user_admin, omit) }}"
login_password: "{{ mongodb_state.auth_enabled | ternary(mongodb_user_admin_password, omit) }}"
login_port: "{{ mongodb_port }}"
login_database: admin
login_host: "{{ mongodb_state_after_replicaset.primary | regex_replace(':.*$', '') }}"
eval: "db.adminCommand({replSetStepDown: 60, force: true})"
register: stepdown_result
failed_when: false
when:
- inventory_hostname in groups.mongodb
- groups.mongodb.index(inventory_hostname) == 0
- reconfig_result is changed
- mongodb_state_after_replicaset.primary is defined
- (mongodb_state_after_replicaset.primary | regex_replace(':.*$', '')) != mongodb_preferred_primary_host
vars:
ansible_python_interpreter: "{{ mongodb_python_venv }}/bin/python3"
tags: reconfigure_priority

- name: Ensure replicaset is stable after primary stepdown
community.mongodb.mongodb_status:
login_user: "{{ mongodb_state.auth_enabled | ternary(mongodb_user_admin, omit) }}"
login_password: "{{ mongodb_state.auth_enabled | ternary(mongodb_user_admin_password, omit) }}"
login_port: "{{ mongodb_port }}"
login_database: admin
login_host: "{{ inventory_hostname }}"
replica_set: "{{ mongodb_replset_name }}"
poll: "{{ mongodb_status_poll }}"
interval: "{{ mongodb_status_interval }}"
validate: minimal
register: rs_after_stepdown
failed_when:
- "'Unable to determine if auth is enabled' not in rs_after_stepdown.msg"
- "'replicaset is in a converged state' not in rs_after_stepdown.msg"
when:
- inventory_hostname in groups.mongodb
- stepdown_result is defined
- stepdown_result is not skipped
vars:
ansible_python_interpreter: "{{ mongodb_python_venv }}/bin/python3"
tags: reconfigure_priority

# Starting in MongoDB 5.0, the implicit default write concern is w: majority.
# However, special considerations are made for deployments containing arbiters:
# The voting majority of a replica set is 1 plus half the number of voting
Expand Down
3 changes: 3 additions & 0 deletions roles/mongodb/tasks/configure-mongodb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
- name: Configure MongoDB replica set
ansible.builtin.include_tasks:
file: configure-mongodb-replicaset.yml
apply:
tags: reconfigure_priority
when: mongodb_replication_enabled | bool
tags: reconfigure_priority

# Configure auth
- name: Configure MongoDB Auth
Expand Down
Loading