diff --git a/library/mongodb_replication.py b/library/mongodb_replication.py index ac1e1487..20afc48d 100644 --- a/library/mongodb_replication.py +++ b/library/mongodb_replication.py @@ -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 ''' @@ -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': @@ -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() @@ -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: @@ -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)) @@ -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') @@ -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']) @@ -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 = { @@ -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)) @@ -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() \ No newline at end of file