Skip to content

Commit

Permalink
Support replica set members reconfiguration from UnderGreen PR UnderG…
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-gonzaga committed Dec 1, 2020
1 parent a78312f commit 2101c00
Showing 1 changed file with 66 additions and 21 deletions.
87 changes: 66 additions & 21 deletions library/mongodb_replication.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,12 @@
EXAMPLES = '''
# Add 'mongo1.dev:27017' host into replica set as replica (Replica will be initiated if it not exists)
- mongodb_replication: replica_set=replSet host_name=mongo1.dev host_port=27017 state=present
# Add 'mongo2.dev:30000' host into replica set as arbiter
- mongodb_replication: replica_set=replSet host_name=mongo2.dev host_port=30000 host_type=arbiter state=present
# Add 'mongo3.dev:27017' host into replica set as replica and authorization params
- mongodb_replication: replica_set=replSet login_host=mongo1.dev login_user=siteRootAdmin login_password=123456 host_name=mongo3.dev host_port=27017 state=present
# Add 'mongo4.dev:27017' host into replica set as replica via SSL
- mongodb_replication: replica_set=replSet host_name=mongo4.dev host_port=27017 ssl=True state=present
# Remove 'mongo4.dev:27017' host from the replica set
- mongodb_replication: replica_set=replSet host_name=mongo4.dev host_port=27017 state=absent
'''
Expand Down Expand Up @@ -204,6 +200,7 @@ def check_members(state, module, client, host_name, host_port, host_type):
if not cfg:
module.fail_json(msg='no config object retrievable from local.system.replset')

found_on_remove = False
for member in cfg['members']:
if state == 'present':
if host_type == 'replica':
Expand All @@ -212,13 +209,17 @@ def check_members(state, module, client, host_name, host_port, host_type):
else:
if "{0}:{1}".format(host_name, host_port) in member['host'] and member['arbiterOnly']:
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
else:

if state == 'absent':
if host_type == 'replica':
if "{0}:{1}".format(host_name, host_port) not in member['host']:
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
if "{0}:{1}".format(host_name, host_port) in member['host']:
found_on_remove = True
else:
if "{0}:{1}".format(host_name, host_port) not in member['host'] and member['arbiterOnly']:
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
if "{0}:{1}".format(host_name, host_port) in member['host'] and member['arbiterOnly']:
found_on_remove = True
if not found_on_remove and state == 'absent':
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)


def add_host(module, client, host_name, host_port, host_type, timeout=180, **kwargs):
start_time = dtdatetime.now()
Expand Down Expand Up @@ -257,13 +258,14 @@ def add_host(module, client, host_name, host_port, host_type, timeout=180, **kwa

cfg['members'].append(new_host)
admin_db.command('replSetReconfig', cfg)
module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
return
except (OperationFailure, AutoReconnect) as e:
if (dtdatetime.now() - start_time).seconds > timeout:
module.fail_json(msg='reached timeout while waiting for rs.reconfig(): %s' % str(e))
time.sleep(5)

def remove_host(module, client, host_name, timeout=180):
def remove_host(module, client, host_name, host_port, timeout=180):
start_time = dtdatetime.now()
while True:
try:
Expand All @@ -281,12 +283,21 @@ def remove_host(module, client, host_name, timeout=180):

if len(cfg['members']) == 1:
module.fail_json(msg="You can't delete last member of replica set")

removed_host = False
for member in cfg['members']:
if host_name in member['host']:
if host_name + ":" + host_port in member['host']:
cfg['members'].remove(member)
else:
fail_msg = "couldn't find member with hostname: {0} in replica set members list".format(host_name)
module.fail_json(msg=fail_msg)

if remove_host:
cfg['version'] += 1
admin_db.command('replSetReconfig', cfg)
module.exit_json(changed=True, host_name=host_name, host_port=host_port)

if not removed_host:
fail_msg = "couldn't find member with hostname: {0} in replica set members list".format(host_name)
module.fail_json(msg=fail_msg)

except (OperationFailure, AutoReconnect) as e:
if (dtdatetime.now() - start_time).seconds > timeout:
module.fail_json(msg='reached timeout while waiting for rs.reconfig(): %s' % str(e))
Expand Down Expand Up @@ -382,9 +393,10 @@ def main():
ssl = module.params['ssl']
state = module.params['state']
priority = float(module.params['priority'])

hidden = module.params['hidden']
votes = module.params['votes']
replica_set_created = False

try:
if replica_set is None:
module.fail_json(msg='replica_set parameter is required')
Expand All @@ -398,7 +410,6 @@ def main():
"serverselectiontimeoutms": 5000,
"replicaset": replica_set,
}

if ssl:
connection_params["ssl"] = ssl
connection_params["ssl_cert_reqs"] = getattr(ssl_lib, module.params['ssl_cert_reqs'])
Expand All @@ -407,6 +418,38 @@ def main():
authenticate(client, login_user, login_password)
client['admin'].command('replSetGetStatus')

# Successful RS connection
repl_set_status = None
current_host_found = False
try:
repl_set_status = client['admin'].command('replSetGetStatus')
current_host_found = host_name + ":" + host_port in [x["name"] for x in repl_set_status["members"]]
except Exception as e:
if not "no replset config has been received" in str(e):
raise e

if current_host_found and state == 'present':
requires_changes = False
current_config = client['admin'].command('replSetGetConfig')
for this_host in current_config['config']['members']:
if this_host['host'] == host_name + ":" + host_port:
if priority != this_host['priority']:
requires_changes = True
this_host['priority'] = priority
if hidden != this_host['hidden']:
requires_changes = True
this_host['hidden'] = hidden
if votes != this_host['votes']:
requires_changes = True
this_host['votes'] = votes
if requires_changes:
current_config['config']['version'] += 1
client['admin'].command('replSetReconfig', current_config['config'])
module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
if not requires_changes:
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)


except ServerSelectionTimeoutError:
try:
connection_params = {
Expand All @@ -433,7 +476,8 @@ def main():
wait_for_ok_and_master(module, connection_params)
replica_set_created = True
module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
except OperationFailure as e:

except OperationFailure as e:
module.fail_json(msg='Unable to initiate replica set: %s' % str(e))
except ConnectionFailure as e:
module.fail_json(msg='unable to connect to database: %s' % str(e))
Expand Down Expand Up @@ -461,12 +505,13 @@ def main():

elif state == 'absent':
try:
remove_host(module, client, host_name)
remove_host(module, client, host_name, host_port)
except OperationFailure as e:
module.fail_json(msg='Unable to remove member of replica set: %s' % str(e))

module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
module.fail_json(msg='Operation error')


# import module snippets
from ansible.module_utils.basic import *
main()
main()

0 comments on commit 2101c00

Please sign in to comment.