Commit cda15d39 by Kevin Falcone

Upgrades to mongo_replica_set to handle initialization

Do a bit more error checking before asking for the replica set.

The new mongo play wants to be able to run this against an unconfigured
Mongo to find out "Do you even have a replica set?" which this can now
answer (the try/except portion).

In the case where mongo was started without a replSet configured (or
running getCmdLineOpts fails for an OperationFailure reason), we
return a dictionary where status is not included to allow tasks to
detect the failure.  Unfortunately, Ansible doesn't provide an easy way
to log back why you didn't get a replica set doc.

Documentation improvements for this module

Discusses the return values and some examples of checking them.

Simplify get_replset

If the find_one for a replset fails, just return None.
We don't need to worry about getting back a dictionary which contains a
'config' key (presumably this is from an earlier iteration of the code
which called getReplSetStatus which does act like that).

Rather than raising the exception, use the friendly error here.

I assume the raise/module.fail_json double header was leftover cruft
from earlier development.

Make the module work without rs being configured

Initially, this module always tried to find and connect to a primary,
however, during replica set initialization, there is no primary so we
need to fall back to just connecting to the machine specified.

This is detected by catching the replSetGetStatus failure and falling
back to the bare get_client method which refactors the connection.
parent 015b8bce
......@@ -100,25 +100,20 @@ def get_replset():
try:
rs_config = client.local.system.replset.find_one()
except OperationFailure as e:
module.fail_json(msg="Failed to retrieve existing replSet config: {}".format(e.message))
if rs_config is None or 'config' not in rs_config:
return None
else:
return rs_config['config']
return rs_config
def initialize_replset(rs_config):
try:
client.admin.command("replSetInitiate", rs_config)
except OperationFailure as e:
raise
module.fail_json(msg="Failed to initiate replSet: {}".format(e.message))
def reconfig_replset(rs_config):
try:
client.admin.command("replSetReconfig", rs_config, force=module.params['force'])
except OperationFailure as e:
raise
module.fail_json(msg="Failed to reconfigure replSet: {}".format(e.message))
def get_rs_config_id():
......@@ -256,13 +251,16 @@ def primary_client(some_host, some_port, username, password, auth_database):
Given a member of a replica set, find out who the primary is
and provide a client that is connected to the primary for running
commands.
Because this function attempts to find the primary of your replica set,
it can fail and throw PyMongo exceptions. You should handle these and
fall back to get_client.
'''
mongo_uri = get_mongo_uri(some_host, some_port, username, password, auth_database)
client = MongoClient(mongo_uri)
try:
status = client.admin.command("replSetGetStatus")
except OperationFailure as e:
module.fail_json(msg="Failed to get replica set status from host {}: {}".format(some_host, e.message))
client = get_client(some_host, some_port, username, password, auth_database)
# This can fail (throws OperationFailure), in which case code will need to
# fall back to using get_client since there either is no primary, or we can't
# know it for some reason.
status = client.admin.command("replSetGetStatus")
# Find out who the primary is.
rs_primary = filter(lambda member: member['stateStr']=='PRIMARY', status['members'])[0]
......@@ -276,6 +274,18 @@ def primary_client(some_host, some_port, username, password, auth_database):
return client
def get_client(some_host, some_port, username, password, auth_database):
'''
Connects to the given host. Does not have any of the logic of primary_client,
so is safer to use when handling an uninitialized replica set or some other
mongo instance that requires special logic.
This function connects to Mongo, and as such can throw any of the PyMongo
exceptions.
'''
mongo_uri = get_mongo_uri(some_host, some_port, username, password, auth_database)
client = MongoClient(mongo_uri)
return client
################ Main ################
def validate_args():
......@@ -311,5 +321,9 @@ if __name__ == '__main__':
rs_host = module.params['rs_host']
rs_port = module.params['rs_port']
client = primary_client(module, rs_host, rs_port, username, password, auth_database)
update_replset(module['rs_config'])
try:
client = primary_client(rs_host, rs_port, username, password, auth_database)
except OperationFailure:
client = get_client(rs_host, rs_port, username, password, auth_database)
update_replset(module.params['rs_config'])
......@@ -6,8 +6,13 @@ module: mongodb_rs_status
short_description: Get the status of a replica set of a mongo cluster.
description:
- Get the status of the replica set of a mongo cluster. Provide the same info as rs.status() or replSetGetStatus.
Returns a status dictionary key containing the replica set JSON document from Mongo, or no status key if there
was no status found. This usually indicates that either Mongo was configured to run without replica sets or
that the replica set has not been initiated yet.
version_added: "1.9"
author: Feanil Patel
author:
- Feanil Patel
- Kevin Falcone
options:
host:
description:
......@@ -35,17 +40,26 @@ options:
EXAMPLES = '''
- name: Get status for the stage cluster
mongo_status:
mongodb_rs_status:
host: localhost:27017
username: root
password: password
register: mongo_status
Note that you're testing for the presence of the status member of the dictionary not the contents of it
- debug: msg="I don't have a replica set available"
when: mongo_status.status is not defined
- debug: var=mongo_status.status
'''
# Magic import
from ansible.module_utils.basic import *
try:
from pymongo import MongoClient
from pymongo.errors import OperationFailure
from bson import json_util
except ImportError:
pymongo_found = False
......@@ -89,7 +103,22 @@ def main():
mongo_uri += '/{}'.format(quote_plus(auth_database))
client = MongoClient(mongo_uri)
status = client.admin.command("replSetGetStatus")
# This checks to see if you have a replSetName configured
# This generally means that /etc/mongod.conf has been changed
# from the default to use a replica set and mongo has been
# restarted to use it.
try:
repl_set = client.admin.command('getCmdLineOpts')['parsed']['replication']['replSetName']
except (OperationFailure, KeyError):
module.exit_json(changed=False)
# If mongo was started with a repl_set, it is safe to run replSetGetStatus
if repl_set:
status = client.admin.command("replSetGetStatus")
else:
module.exit_json(changed=False)
# This converts the bson into a python dictionary that ansible's standard
# jsonify function can process and output without throwing errors on bson
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment