Repository URL to install this package:
Version:
6.0.0 ▾
|
#!/usr/bin/python
# Copyright: (c) 2020, Rhys Campbell <rhys.james.campbell@googlemail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
---
module: mongodb_balancer
short_description: Manages the MongoDB Sharded Cluster Balancer.
description:
- Manages the MongoDB Sharded Cluster Balancer.
- Start or stop the balancer.
- Adjust the cluster chunksize.
- Enable or disable autosplit.
- Add or remove a balancer window.
author: Rhys Campbell (@rhysmeister)
version_added: "1.0.0"
extends_documentation_fragment:
- community.mongodb.login_options
- community.mongodb.ssl_options
options:
autosplit:
description:
- Disable or enable the autosplit flag in the config.settings collection.
required: false
type: bool
chunksize:
description:
- Control the size of chunks in the sharded cluster.
- Value should be given in MB.
required: false
type: int
state:
description:
- Manage the Balancer for the Cluster
required: false
type: str
choices:
- "started"
- "stopped"
default: "started"
mongos_process:
description:
- Provide a custom name for the mongos process.
- Most users can ignore this setting.
required: false
type: str
default: "mongos"
window:
description:
- Schedule the balancer window.
- Provide the following dictionary keys start, stop, state
- The state key should be "present" or "absent".
- The start and stop keys are ignored when state is "absent".
- start and stop should be strings in "HH:MM" format indicating the time bounds of the window.
type: raw
required: false
notes:
- Requires the pymongo Python package on the remote host, version 2.4.2+. This
can be installed using pip or the OS package manager. @see U(http://api.mongodb.org/python/current/installation.html)
requirements:
- pymongo
'''
EXAMPLES = r'''
- name: Start the balancer
community.mongodb.mongodb_balancer:
state: started
- name: Stop the balancer and disable autosplit
community.mongodb.mongodb_balancer:
state: stopped
autosplit: false
- name: Enable autosplit
community.mongodb.mongodb_balancer:
autosplit: true
- name: Change the default chunksize to 128MB
community.mongodb.mongodb_balancer:
chunksize: 128
- name: Add or update a balancing window
community.mongodb.mongodb_balancer:
window:
start: "23:00"
stop: "06:00"
state: "present"
- name: Remove a balancing window
community.mongodb.mongodb_balancer:
window:
state: "absent"
'''
RETURN = r'''
changed:
description: Whether the balancer state or autosplit changed.
returned: success
type: bool
old_balancer_state:
description: The previous state of the balancer
returned: When balancer state is changed
type: str
new_balancer_state:
description: The new state of the balancer.
returned: When balancer state is changed
type: str
old_autosplit:
description: The previous state of autosplit.
returned: When autosplit is changed.
type: str
new_autosplit:
description: The new state of autosplit.
returned: When autosplit is changed.
type: str
old_chunksize:
description: The previous value for chunksize.
returned: When chunksize is changed.
type: int
new_chunksize:
description: The new value for chunksize.
returned: When chunksize is changed.
type: int
msg:
description: A short description of what happened.
returned: failure
type: str
failed:
description: If something went wrong
returned: failed
type: bool
'''
import time
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
from ansible_collections.community.mongodb.plugins.module_utils.mongodb_common import (
missing_required_lib,
mongodb_common_argument_spec,
mongo_auth,
PYMONGO_IMP_ERR,
pymongo_found,
get_mongodb_client,
)
has_ordereddict = False
try:
from collections import OrderedDict
has_ordereddict = True
except ImportError as excep:
try:
from ordereddict import OrderedDict
has_ordereddict = True
except ImportError as excep:
pass
def get_balancer_state(client):
'''
Gets the state of the MongoDB balancer. The config.settings collection does
not exist until the balancer has been started for the first time
{ "_id" : "balancer", "mode" : "full", "stopped" : false }
{ "_id" : "autosplit", "enabled" : true }
'''
balancer_state = None
result = client["config"].settings.find_one({"_id": "balancer"})
if not result:
balancer_state = "stopped"
else:
if result['stopped'] is False:
balancer_state = "started"
else:
balancer_state = "stopped"
return balancer_state
def stop_balancer(client):
'''
Stops MongoDB balancer
'''
cmd_doc = OrderedDict([
('balancerStop', 1),
('maxTimeMS', 60000)
])
client['admin'].command(cmd_doc)
time.sleep(1)
def start_balancer(client):
'''
Starts MongoDB balancer
'''
cmd_doc = OrderedDict([
('balancerStart', 1),
('maxTimeMS', 60000)
])
client['admin'].command(cmd_doc)
time.sleep(1)
def enable_autosplit(client):
client["config"].settings.update_one({"_id": "autosplit"},
{"$set": {"enabled": True}},
upsert=True)
def disable_autosplit(client):
client["config"].settings.update_one({"_id": "autosplit"},
{"$set": {"enabled": False}},
upsert=True)
def get_autosplit(client):
autosplit = False
result = client["config"].settings.find_one({"_id": "autosplit"})
if result is not None:
autosplit = result['enabled']
return autosplit
def get_chunksize(client):
'''
Default chunksize is 64MB
'''
chunksize = None
result = client["config"].settings.find_one({"_id": "chunksize"})
if not result:
chunksize = 64
else:
chunksize = result['value']
return chunksize
def set_chunksize(client, chunksize):
client["config"].settings.update_one({"_id": "chunksize"},
{"$set": {"value": chunksize}},
upsert=True)
def set_balancing_window(client, start, stop):
s = False
result = client["config"].settings.update_one({"_id": "balancer"},
{"$set": {
"activeWindow": {
"start": start,
"stop": stop}}},
upsert=True)
if result.modified_count == 1 or result.upserted_id is not None:
s = True
return s
def remove_balancing_window(client):
s = False
result = client["config"].settings.update_one({"_id": "balancer"},
{"$unset": {"activeWindow": True}})
if result.modified_count == 1:
s = True
return s
def balancing_window(client, start, stop):
s = False
if start is not None and stop is not None:
result = client["config"].settings.find_one({"_id": "balancer",
"activeWindow.start": start,
"activeWindow.stop": stop})
else:
result = client["config"].settings.find_one({"_id": "balancer", "activeWindow": {"$exists": True}})
if result:
s = True
return s
def validate_window(window, module):
if window is not None:
if 'state' not in window.keys():
module.fail_json(msg="Balancing window state must be specified")
elif window['state'] not in ['present', 'absent']:
module.fail_json(msg="Balancing window state must be present or absent")
elif window['state'] == "present" \
and ("start" not in window.keys()
or "stop" not in window.keys()):
module.fail_json(msg="Balancing window start and stop values must be specified")
return True
def main():
argument_spec = mongodb_common_argument_spec()
argument_spec.update(
autosplit=dict(type='bool', default=None),
chunksize=dict(type='int', default=None),
mongos_process=dict(type='str', required=False, default="mongos"),
state=dict(type='str', default="started", choices=["started", "stopped"]),
window=dict(type='raw', default=None)
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_together=[['login_user', 'login_password']],
)
if not has_ordereddict:
module.fail_json(msg='Cannot import OrderedDict class. You can probably install with: pip install ordereddict')
if not pymongo_found:
module.fail_json(msg=missing_required_lib('pymongo'),
exception=PYMONGO_IMP_ERR)
login_host = module.params['login_host']
login_port = module.params['login_port']
balancer_state = module.params['state']
autosplit = module.params['autosplit']
chunksize = module.params['chunksize']
mongos_process = module.params['mongos_process']
window = module.params['window']
# Validate window
validate_window(window, module)
result = dict(
changed=False,
)
try:
client = get_mongodb_client(module)
client = mongo_auth(module, client)
except Exception as excep:
module.fail_json(msg='Unable to connect to MongoDB: %s' % to_native(excep))
changed = False
cluster_balancer_state = None
cluster_autosplit = None
cluster_chunksize = None
old_balancer_state = None
new_balancer_state = None
old_autosplit = None
new_autosplit = None
old_chunksize = None
new_chunksize = None
try:
if client["admin"].command("serverStatus")["process"] != mongos_process:
module.fail_json(msg="Process running on {0}:{1} is not a {2}".format(login_host, login_port, mongos_process))
cluster_balancer_state = get_balancer_state(client)
if autosplit is not None:
cluster_autosplit = get_autosplit(client)
if chunksize is not None:
cluster_chunksize = get_chunksize(client)
if module.check_mode:
if balancer_state != cluster_balancer_state:
old_balancer_state = cluster_balancer_state
new_balancer_state = balancer_state
changed = True
if (autosplit is not None
and autosplit != cluster_autosplit):
old_autosplit = cluster_autosplit
new_autosplit = autosplit
changed = True
if (chunksize is not None
and chunksize != cluster_chunksize):
old_chunksize = cluster_chunksize
new_chunksize = chunksize
changed = True
if window is not None:
if balancing_window(client, window.get('start'), window.get('stop')):
if window['state'] == "present":
pass
else:
changed = True
else:
if window['state'] == "present":
changed = True
else:
pass
else:
if balancer_state is not None \
and balancer_state != cluster_balancer_state:
if balancer_state == "started":
start_balancer(client)
old_balancer_state = cluster_balancer_state
new_balancer_state = get_balancer_state(client)
changed = True
else:
stop_balancer(client)
old_balancer_state = cluster_balancer_state
new_balancer_state = get_balancer_state(client)
changed = True
if autosplit is not None \
and autosplit != cluster_autosplit:
if autosplit:
enable_autosplit(client)
old_autosplit = cluster_autosplit
new_autosplit = autosplit
changed = True
else:
disable_autosplit(client)
old_autosplit = cluster_autosplit
new_autosplit = autosplit
changed = True
if (chunksize is not None
and chunksize != cluster_chunksize):
set_chunksize(client, chunksize)
old_chunksize = cluster_chunksize
new_chunksize = chunksize
changed = True
if window is not None:
if balancing_window(client, window.get('start'), window.get('stop')):
if window['state'] == "present":
pass
else:
remove_balancing_window(client)
changed = True
else:
if window['state'] == "present":
set_balancing_window(client,
window['start'],
window['stop'])
changed = True
else:
pass
except Exception as excep:
result["msg"] = "An error occurred: {0}".format(excep)
result['changed'] = changed
if old_balancer_state is not None:
result['old_balancer_state'] = old_balancer_state
result['new_balancer_state'] = new_balancer_state
if old_autosplit is not None:
result['old_autosplit'] = old_autosplit
result['new_autosplit'] = new_autosplit
if old_chunksize is not None:
result['old_chunksize'] = old_chunksize
result['new_chunksize'] = new_chunksize
module.exit_json(**result)
if __name__ == '__main__':
main()