Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.

Commit 2101c00

Browse files
Support replica set members reconfiguration from UnderGreen PR UnderGreen#220
1 parent a78312f commit 2101c00

File tree

1 file changed

+66
-21
lines changed

1 file changed

+66
-21
lines changed

library/mongodb_replication.py

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,12 @@
125125
EXAMPLES = '''
126126
# Add 'mongo1.dev:27017' host into replica set as replica (Replica will be initiated if it not exists)
127127
- mongodb_replication: replica_set=replSet host_name=mongo1.dev host_port=27017 state=present
128-
129128
# Add 'mongo2.dev:30000' host into replica set as arbiter
130129
- mongodb_replication: replica_set=replSet host_name=mongo2.dev host_port=30000 host_type=arbiter state=present
131-
132130
# Add 'mongo3.dev:27017' host into replica set as replica and authorization params
133131
- mongodb_replication: replica_set=replSet login_host=mongo1.dev login_user=siteRootAdmin login_password=123456 host_name=mongo3.dev host_port=27017 state=present
134-
135132
# Add 'mongo4.dev:27017' host into replica set as replica via SSL
136133
- mongodb_replication: replica_set=replSet host_name=mongo4.dev host_port=27017 ssl=True state=present
137-
138134
# Remove 'mongo4.dev:27017' host from the replica set
139135
- mongodb_replication: replica_set=replSet host_name=mongo4.dev host_port=27017 state=absent
140136
'''
@@ -204,6 +200,7 @@ def check_members(state, module, client, host_name, host_port, host_type):
204200
if not cfg:
205201
module.fail_json(msg='no config object retrievable from local.system.replset')
206202

203+
found_on_remove = False
207204
for member in cfg['members']:
208205
if state == 'present':
209206
if host_type == 'replica':
@@ -212,13 +209,17 @@ def check_members(state, module, client, host_name, host_port, host_type):
212209
else:
213210
if "{0}:{1}".format(host_name, host_port) in member['host'] and member['arbiterOnly']:
214211
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
215-
else:
212+
213+
if state == 'absent':
216214
if host_type == 'replica':
217-
if "{0}:{1}".format(host_name, host_port) not in member['host']:
218-
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
215+
if "{0}:{1}".format(host_name, host_port) in member['host']:
216+
found_on_remove = True
219217
else:
220-
if "{0}:{1}".format(host_name, host_port) not in member['host'] and member['arbiterOnly']:
221-
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
218+
if "{0}:{1}".format(host_name, host_port) in member['host'] and member['arbiterOnly']:
219+
found_on_remove = True
220+
if not found_on_remove and state == 'absent':
221+
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
222+
222223

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

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

266-
def remove_host(module, client, host_name, timeout=180):
268+
def remove_host(module, client, host_name, host_port, timeout=180):
267269
start_time = dtdatetime.now()
268270
while True:
269271
try:
@@ -281,12 +283,21 @@ def remove_host(module, client, host_name, timeout=180):
281283

282284
if len(cfg['members']) == 1:
283285
module.fail_json(msg="You can't delete last member of replica set")
286+
287+
removed_host = False
284288
for member in cfg['members']:
285-
if host_name in member['host']:
289+
if host_name + ":" + host_port in member['host']:
286290
cfg['members'].remove(member)
287-
else:
288-
fail_msg = "couldn't find member with hostname: {0} in replica set members list".format(host_name)
289-
module.fail_json(msg=fail_msg)
291+
292+
if remove_host:
293+
cfg['version'] += 1
294+
admin_db.command('replSetReconfig', cfg)
295+
module.exit_json(changed=True, host_name=host_name, host_port=host_port)
296+
297+
if not removed_host:
298+
fail_msg = "couldn't find member with hostname: {0} in replica set members list".format(host_name)
299+
module.fail_json(msg=fail_msg)
300+
290301
except (OperationFailure, AutoReconnect) as e:
291302
if (dtdatetime.now() - start_time).seconds > timeout:
292303
module.fail_json(msg='reached timeout while waiting for rs.reconfig(): %s' % str(e))
@@ -382,9 +393,10 @@ def main():
382393
ssl = module.params['ssl']
383394
state = module.params['state']
384395
priority = float(module.params['priority'])
385-
396+
hidden = module.params['hidden']
397+
votes = module.params['votes']
386398
replica_set_created = False
387-
399+
388400
try:
389401
if replica_set is None:
390402
module.fail_json(msg='replica_set parameter is required')
@@ -398,7 +410,6 @@ def main():
398410
"serverselectiontimeoutms": 5000,
399411
"replicaset": replica_set,
400412
}
401-
402413
if ssl:
403414
connection_params["ssl"] = ssl
404415
connection_params["ssl_cert_reqs"] = getattr(ssl_lib, module.params['ssl_cert_reqs'])
@@ -407,6 +418,38 @@ def main():
407418
authenticate(client, login_user, login_password)
408419
client['admin'].command('replSetGetStatus')
409420

421+
# Successful RS connection
422+
repl_set_status = None
423+
current_host_found = False
424+
try:
425+
repl_set_status = client['admin'].command('replSetGetStatus')
426+
current_host_found = host_name + ":" + host_port in [x["name"] for x in repl_set_status["members"]]
427+
except Exception as e:
428+
if not "no replset config has been received" in str(e):
429+
raise e
430+
431+
if current_host_found and state == 'present':
432+
requires_changes = False
433+
current_config = client['admin'].command('replSetGetConfig')
434+
for this_host in current_config['config']['members']:
435+
if this_host['host'] == host_name + ":" + host_port:
436+
if priority != this_host['priority']:
437+
requires_changes = True
438+
this_host['priority'] = priority
439+
if hidden != this_host['hidden']:
440+
requires_changes = True
441+
this_host['hidden'] = hidden
442+
if votes != this_host['votes']:
443+
requires_changes = True
444+
this_host['votes'] = votes
445+
if requires_changes:
446+
current_config['config']['version'] += 1
447+
client['admin'].command('replSetReconfig', current_config['config'])
448+
module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
449+
if not requires_changes:
450+
module.exit_json(changed=False, host_name=host_name, host_port=host_port, host_type=host_type)
451+
452+
410453
except ServerSelectionTimeoutError:
411454
try:
412455
connection_params = {
@@ -433,7 +476,8 @@ def main():
433476
wait_for_ok_and_master(module, connection_params)
434477
replica_set_created = True
435478
module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
436-
except OperationFailure as e:
479+
480+
except OperationFailure as e:
437481
module.fail_json(msg='Unable to initiate replica set: %s' % str(e))
438482
except ConnectionFailure as e:
439483
module.fail_json(msg='unable to connect to database: %s' % str(e))
@@ -461,12 +505,13 @@ def main():
461505

462506
elif state == 'absent':
463507
try:
464-
remove_host(module, client, host_name)
508+
remove_host(module, client, host_name, host_port)
465509
except OperationFailure as e:
466510
module.fail_json(msg='Unable to remove member of replica set: %s' % str(e))
467511

468-
module.exit_json(changed=True, host_name=host_name, host_port=host_port, host_type=host_type)
512+
module.fail_json(msg='Operation error')
513+
469514

470515
# import module snippets
471516
from ansible.module_utils.basic import *
472-
main()
517+
main()

0 commit comments

Comments
 (0)