Migrated to community.mongo

pull/67091/head
Ansible Core Team 4 years ago committed by Matt Martz
parent 0ed8af9021
commit 46b865c7d6

@ -1,444 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2020, Andrew Klychkov (@Andersson007) <aaklychkov@mail.ru>
# 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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: mongodb_info
short_description: Gather information about MongoDB instance.
description:
- Gather information about MongoDB instance.
author: Andrew Klychkov (@Andersson007)
version_added: '2.10'
options:
filter:
description:
- Limit the collected information by comma separated string or YAML list.
- Allowable values are C(general), C(databases), C(total_size), C(parameters), C(users), C(roles).
- By default, collects all subsets.
- You can use '!' before value (for example, C(!users)) to exclude it from the information.
- If you pass including and excluding values to the filter, for example, I(filter=!general,users),
the excluding values, C(!general) in this case, will be ignored.
required: no
type: list
elements: str
login_user:
description:
- The MongoDB user to login with.
- Required when I(login_password) is specified.
required: no
type: str
login_password:
description:
- The password used to authenticate with.
- Required when I(login_user) is specified.
required: no
type: str
login_database:
description:
- The database where login credentials are stored.
required: no
type: str
default: 'admin'
login_host:
description:
- The host running MongoDB instance to login to.
required: no
type: str
default: 'localhost'
login_port:
description:
- The MongoDB server port to login to.
required: no
type: int
default: 27017
ssl:
description:
- Whether to use an SSL connection when connecting to the database.
required: no
type: bool
default: no
ssl_cert_reqs:
description:
- Specifies whether a certificate is required from the other side of the connection,
and whether it will be validated if provided.
required: no
type: str
default: 'CERT_REQUIRED'
choices: ['CERT_NONE', 'CERT_OPTIONAL', 'CERT_REQUIRED']
notes:
- Requires the pymongo Python package on the remote host, version 2.4.2+.
requirements: [ 'pymongo' ]
'''
EXAMPLES = r'''
- name: Gather all supported information
mongodb_info:
login_user: admin
login_password: secret
register: result
- name: Show gathered info
debug:
msg: '{{ result }}'
- name: Gather only information about databases and their total size
mongodb_info:
login_user: admin
login_password: secret
filter: databases, total_size
- name: Gather all information except parameters
mongodb_info:
login_user: admin
login_password: secret
filter: '!parameters'
'''
RETURN = r'''
general:
description: General instance information.
returned: always
type: dict
sample: {"allocator": "tcmalloc", "bits": 64, "storageEngines": ["biggie"], "version": "4.2.3", "maxBsonObjectSize": 16777216}
databases:
description: Database information.
returned: always
type: dict
sample: {"admin": {"empty": false, "sizeOnDisk": 245760}, "config": {"empty": false, "sizeOnDisk": 110592}}
total_size:
description: Total size of all databases in bytes.
returned: always
type: int
sample: 397312
users:
description: User information.
returned: always
type: dict
sample: {"new_user": {"_id": "config.new_user", "db": "config", "mechanisms": ["SCRAM-SHA-1", "SCRAM-SHA-256"], "roles": []}}
roles:
description: Role information.
returned: always
type: dict
sample: {"restore": {"db": "admin", "inheritedRoles": [], "isBuiltin": true, "roles": []}}
parameters:
description: Server parameters information.
returned: always
type: dict
sample: {"maxOplogTruncationPointsAfterStartup": 100, "maxOplogTruncationPointsDuringStartup": 100, "maxSessions": 1000000}
'''
import traceback
from uuid import UUID
import ssl as ssl_lib
from distutils.version import LooseVersion
PYMONGO_IMP_ERR = None
try:
from pymongo import version as PyMongoVersion
from pymongo import MongoClient
except ImportError:
PYMONGO_IMP_ERR = traceback.format_exc()
pymongo_found = False
else:
pymongo_found = True
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
from ansible.module_utils.six import iteritems
# =========================================
# MongoDB module specific support methods.
#
def check_compatibility(module, srv_version, driver_version):
"""Check the compatibility between the driver and the database.
See: https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
Args:
module: Ansible module.
srv_version (LooseVersion): MongoDB server version.
driver_version (LooseVersion): Pymongo version.
"""
msg = 'pymongo driver version and MongoDB version are incompatible: '
if srv_version >= LooseVersion('4.2') and driver_version < LooseVersion('3.9'):
msg += 'you must use pymongo 3.9+ with MongoDB >= 4.2'
module.fail_json(msg=msg)
elif srv_version >= LooseVersion('4.0') and driver_version < LooseVersion('3.7'):
msg += 'you must use pymongo 3.7+ with MongoDB >= 4.0'
module.fail_json(msg=msg)
elif srv_version >= LooseVersion('3.6') and driver_version < LooseVersion('3.6'):
msg += 'you must use pymongo 3.6+ with MongoDB >= 3.6'
module.fail_json(msg=msg)
elif srv_version >= LooseVersion('3.4') and driver_version < LooseVersion('3.4'):
msg += 'you must use pymongo 3.4+ with MongoDB >= 3.4'
module.fail_json(msg=msg)
elif srv_version >= LooseVersion('3.2') and driver_version < LooseVersion('3.2'):
msg += 'you must use pymongo 3.2+ with MongoDB >= 3.2'
module.fail_json(msg=msg)
elif srv_version >= LooseVersion('3.0') and driver_version <= LooseVersion('2.8'):
msg += 'you must use pymongo 2.8+ with MongoDB 3.0'
module.fail_json(msg=msg)
elif srv_version >= LooseVersion('2.6') and driver_version <= LooseVersion('2.7'):
msg += 'you must use pymongo 2.7+ with MongoDB 2.6'
module.fail_json(msg=msg)
class MongoDbInfo():
"""Class for gathering MongoDB instance information.
Args:
module (AnsibleModule): Object of AnsibleModule class.
client (pymongo): pymongo client object to interact with the database.
"""
def __init__(self, module, client):
self.module = module
self.client = client
self.admin_db = self.client.admin
self.info = {
'general': {},
'databases': {},
'total_size': {},
'parameters': {},
'users': {},
'roles': {},
}
def get_info(self, filter_):
"""Get MongoDB instance information and return it based on filter_.
Args:
filter_ (list): List of collected subsets (e.g., general, users, etc.),
when it is empty, return all available information.
"""
self.__collect()
inc_list = []
exc_list = []
if filter_:
partial_info = {}
for fi in filter_:
if fi.lstrip('!') not in self.info:
self.module.warn("filter element '%s' is not allowable, ignored" % fi)
continue
if fi[0] == '!':
exc_list.append(fi.lstrip('!'))
else:
inc_list.append(fi)
if inc_list:
for i in self.info:
if i in inc_list:
partial_info[i] = self.info[i]
else:
for i in self.info:
if i not in exc_list:
partial_info[i] = self.info[i]
return partial_info
else:
return self.info
def __collect(self):
"""Collect information."""
# Get general info:
self.info['general'] = self.client.server_info()
# Get parameters:
self.info['parameters'] = self.get_parameters_info()
# Gather info about databases and their total size:
self.info['databases'], self.info['total_size'] = self.get_db_info()
for dbname, val in iteritems(self.info['databases']):
# Gather info about users for each database:
self.info['users'].update(self.get_users_info(dbname))
# Gather info about roles for each database:
self.info['roles'].update(self.get_roles_info(dbname))
def get_roles_info(self, dbname):
"""Gather information about roles.
Args:
dbname (str): Database name to get role info from.
Returns a dictionary with role information.
"""
db = self.client[dbname]
result = db.command({'rolesInfo': 1, 'showBuiltinRoles': True})['roles']
roles_dict = {}
for elem in result:
roles_dict[elem['role']] = {}
for key, val in iteritems(elem):
if key == 'role':
continue
roles_dict[elem['role']][key] = val
return roles_dict
def get_users_info(self, dbname):
"""Gather information about users.
Args:
dbname (str): Database name to get user info from.
Returns a dictionary with user information.
"""
db = self.client[dbname]
result = db.command({'usersInfo': 1})['users']
users_dict = {}
for elem in result:
users_dict[elem['user']] = {}
for key, val in iteritems(elem):
if key == 'user':
continue
if isinstance(val, UUID):
val = val.hex
users_dict[elem['user']][key] = val
return users_dict
def get_db_info(self):
"""Gather information about databases.
Returns a dictionary with database information.
"""
result = self.admin_db.command({'listDatabases': 1})
total_size = int(result['totalSize'])
result = result['databases']
db_dict = {}
for elem in result:
db_dict[elem['name']] = {}
for key, val in iteritems(elem):
if key == 'name':
continue
if key == 'sizeOnDisk':
val = int(val)
db_dict[elem['name']][key] = val
return db_dict, total_size
def get_parameters_info(self):
"""Gather parameters information.
Returns a dictionary with parameters.
"""
return self.admin_db.command({'getParameter': '*'})
# ================
# Module execution
#
def main():
argument_spec = dict(
login_user=dict(type='str', required=False),
login_password=dict(type='str', required=False, no_log=True),
login_database=dict(type='str', required=False, default='admin'),
login_host=dict(type='str', required=False, default='localhost'),
login_port=dict(type='int', required=False, default=27017),
ssl=dict(type='bool', required=False, default=False),
ssl_cert_reqs=dict(type='str', required=False, default='CERT_REQUIRED',
choices=['CERT_NONE', 'CERT_OPTIONAL', 'CERT_REQUIRED']),
filter=dict(type='list', elements='str', required=False),
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_together=[['login_user', 'login_password']],
)
if not pymongo_found:
module.fail_json(msg=missing_required_lib('pymongo'), exception=PYMONGO_IMP_ERR)
login_user = module.params['login_user']
login_password = module.params['login_password']
login_database = module.params['login_database']
login_host = module.params['login_host']
login_port = module.params['login_port']
ssl = module.params['ssl']
filter_ = module.params['filter']
if filter_:
filter_ = [f.strip() for f in filter_]
connection_params = {
'host': login_host,
'port': login_port,
}
if ssl:
connection_params['ssl'] = ssl
connection_params['ssl_cert_reqs'] = getattr(ssl_lib, module.params['ssl_cert_reqs'])
client = MongoClient(**connection_params)
if login_user:
try:
client.admin.authenticate(login_user, login_password, source=login_database)
except Exception as e:
module.fail_json(msg='Unable to authenticate: %s' % to_native(e))
# Get server version:
try:
srv_version = LooseVersion(client.server_info()['version'])
except Exception as e:
module.fail_json(msg='Unable to get MongoDB server version: %s' % to_native(e))
# Get driver version::
driver_version = LooseVersion(PyMongoVersion)
# Check driver and server version compatibility:
check_compatibility(module, srv_version, driver_version)
# Initialize an object and start main work:
mongodb = MongoDbInfo(module, client)
module.exit_json(changed=False, **mongodb.get_info(filter_))
if __name__ == '__main__':
main()

@ -1,223 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2016, Loic Blot <loic.blot@unix-experience.fr>
# Sponsored by Infopro Digital. http://www.infopro-digital.com/
# Sponsored by E.T.A.I. http://www.etai.fr/
#
# 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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: mongodb_parameter
short_description: Change an administrative parameter on a MongoDB server
description:
- Change an administrative parameter on a MongoDB server.
version_added: "2.1"
options:
login_user:
description:
- The MongoDB username used to authenticate with.
type: str
login_password:
description:
- The login user's password used to authenticate with.
type: str
login_host:
description:
- The host running the database.
type: str
default: localhost
login_port:
description:
- The MongoDB port to connect to.
default: 27017
type: int
login_database:
description:
- The database where login credentials are stored.
type: str
replica_set:
description:
- Replica set to connect to (automatically connects to primary for writes).
type: str
ssl:
description:
- Whether to use an SSL connection when connecting to the database.
type: bool
default: no
param:
description:
- MongoDB administrative parameter to modify.
type: str
required: true
value:
description:
- MongoDB administrative parameter value to set.
type: str
required: true
param_type:
description:
- Define the type of parameter value.
default: str
type: str
choices: [int, str]
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 also U(http://api.mongodb.org/python/current/installation.html)
requirements: [ "pymongo" ]
author: "Loic Blot (@nerzhul)"
'''
EXAMPLES = r'''
- name: Set MongoDB syncdelay to 60 (this is an int)
mongodb_parameter:
param: syncdelay
value: 60
param_type: int
'''
RETURN = r'''
before:
description: value before modification
returned: success
type: str
after:
description: value after modification
returned: success
type: str
'''
import os
import traceback
try:
from pymongo.errors import ConnectionFailure
from pymongo.errors import OperationFailure
from pymongo import version as PyMongoVersion
from pymongo import MongoClient
except ImportError:
try: # for older PyMongo 2.2
from pymongo import Connection as MongoClient
except ImportError:
pymongo_found = False
else:
pymongo_found = True
else:
pymongo_found = True
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.six.moves import configparser
from ansible.module_utils._text import to_native
# =========================================
# MongoDB module specific support methods.
#
def load_mongocnf():
config = configparser.RawConfigParser()
mongocnf = os.path.expanduser('~/.mongodb.cnf')
try:
config.readfp(open(mongocnf))
creds = dict(
user=config.get('client', 'user'),
password=config.get('client', 'pass')
)
except (configparser.NoOptionError, IOError):
return False
return creds
# =========================================
# Module execution.
#
def main():
module = AnsibleModule(
argument_spec=dict(
login_user=dict(default=None),
login_password=dict(default=None, no_log=True),
login_host=dict(default='localhost'),
login_port=dict(default=27017, type='int'),
login_database=dict(default=None),
replica_set=dict(default=None),
param=dict(required=True),
value=dict(required=True),
param_type=dict(default="str", choices=['str', 'int']),
ssl=dict(default=False, type='bool'),
)
)
if not pymongo_found:
module.fail_json(msg=missing_required_lib('pymongo'))
login_user = module.params['login_user']
login_password = module.params['login_password']
login_host = module.params['login_host']
login_port = module.params['login_port']
login_database = module.params['login_database']
replica_set = module.params['replica_set']
ssl = module.params['ssl']
param = module.params['param']
param_type = module.params['param_type']
value = module.params['value']
# Verify parameter is coherent with specified type
try:
if param_type == 'int':
value = int(value)
except ValueError:
module.fail_json(msg="value '%s' is not %s" % (value, param_type))
try:
if replica_set:
client = MongoClient(login_host, int(login_port), replicaset=replica_set, ssl=ssl)
else:
client = MongoClient(login_host, int(login_port), ssl=ssl)
if login_user is None and login_password is None:
mongocnf_creds = load_mongocnf()
if mongocnf_creds is not False:
login_user = mongocnf_creds['user']
login_password = mongocnf_creds['password']
elif login_password is None or login_user is None:
module.fail_json(msg='when supplying login arguments, both login_user and login_password must be provided')
if login_user is not None and login_password is not None:
client.admin.authenticate(login_user, login_password, source=login_database)
except ConnectionFailure as e:
module.fail_json(msg='unable to connect to database: %s' % to_native(e), exception=traceback.format_exc())
db = client.admin
try:
after_value = db.command("setParameter", **{param: value})
except OperationFailure as e:
module.fail_json(msg="unable to change parameter: %s" % to_native(e), exception=traceback.format_exc())
if "was" not in after_value:
module.exit_json(changed=True, msg="Unable to determine old value, assume it changed.")
else:
module.exit_json(changed=(value != after_value["was"]), before=after_value["was"],
after=value)
if __name__ == '__main__':
main()

@ -1,418 +0,0 @@
#!/usr/bin/python
# Copyright: (c) 2018, 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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: mongodb_replicaset
short_description: Initialises a MongoDB replicaset
description:
- Initialises a MongoDB replicaset in a new deployment.
- Validates the replicaset name for existing deployments.
author: Rhys Campbell (@rhysmeister)
version_added: "2.8"
options:
login_user:
description:
- The username to authenticate with.
type: str
login_password:
description:
- The password to authenticate with.
type: str
login_database:
description:
- The database where login credentials are stored.
type: str
default: admin
login_host:
description:
- The MongoDB hostname.
type: str
default: localhost
login_port:
description:
- The MongoDB port to login to.
type: int
default: 27017
replica_set:
description:
- Replicaset name.
type: str
default: rs0
members:
description:
- A comma-separated string or a yaml list consisting of the replicaset members.
- Supply as a simple csv string, i.e. mongodb1:27017,mongodb2:27017,mongodb3:27017.
- If a port number is not provided then 27017 is assumed.
type: list
elements: raw
validate:
description:
- Performs some basic validation on the provided replicaset config.
type: bool
default: yes
ssl:
description:
- Whether to use an SSL connection when connecting to the database
type: bool
default: no
ssl_cert_reqs:
description:
- Specifies whether a certificate is required from the other side of the connection, and whether it will be validated if provided.
type: str
default: CERT_REQUIRED
choices: [ CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED ]
arbiter_at_index:
description:
- Identifies the position of the member in the array that is an arbiter.
type: int
chaining_allowed:
description:
- When I(settings.chaining_allowed=true), the replicaset allows secondary members to replicate from other
secondary members.
- When I(settings.chaining_allowed=false), secondaries can replicate only from the primary.
type: bool
default: yes
heartbeat_timeout_secs:
description:
- Number of seconds that the replicaset members wait for a successful heartbeat from each other.
- If a member does not respond in time, other members mark the delinquent member as inaccessible.
- The setting only applies when using I(protocol_version=0). When using I(protocol_version=1) the relevant
setting is I(settings.election_timeout_millis).
type: int
default: 10
election_timeout_millis:
description:
- The time limit in milliseconds for detecting when a replicaset's primary is unreachable.
type: int
default: 10000
protocol_version:
description: Version of the replicaset election protocol.
type: int
choices: [ 0, 1 ]
default: 1
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'''
# Create a replicaset called 'rs0' with the 3 provided members
- name: Ensure replicaset rs0 exists
mongodb_replicaset:
login_host: localhost
login_user: admin
login_password: admin
replica_set: rs0
members:
- mongodb1:27017
- mongodb2:27017
- mongodb3:27017
when: groups.mongod.index(inventory_hostname) == 0
# Create two single-node replicasets on the localhost for testing
- name: Ensure replicaset rs0 exists
mongodb_replicaset:
login_host: localhost
login_port: 3001
login_user: admin
login_password: secret
login_database: admin
replica_set: rs0
members: localhost:3001
validate: no
- name: Ensure replicaset rs1 exists
mongodb_replicaset:
login_host: localhost
login_port: 3002
login_user: admin
login_password: secret
login_database: admin
replica_set: rs1
members: localhost:3002
validate: no
'''
RETURN = r'''
mongodb_replicaset:
description: The name of the replicaset that has been created.
returned: success
type: str
'''
from copy import deepcopy
import os
import ssl as ssl_lib
from distutils.version import LooseVersion
try:
from pymongo.errors import ConnectionFailure
from pymongo.errors import OperationFailure
from pymongo import version as PyMongoVersion
from pymongo import MongoClient
HAS_PYMONGO = True
except ImportError:
try: # for older PyMongo 2.2
from pymongo import Connection as MongoClient
HAS_PYMONGO = True
except ImportError:
HAS_PYMONGO = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import binary_type, text_type
from ansible.module_utils.six.moves import configparser
from ansible.module_utils._text import to_native
# =========================================
# MongoDB module specific support methods.
#
def check_compatibility(module, client):
"""Check the compatibility between the driver and the database.
See: https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
Args:
module: Ansible module.
client (cursor): Mongodb cursor on admin database.
"""
loose_srv_version = LooseVersion(client.server_info()['version'])
loose_driver_version = LooseVersion(PyMongoVersion)
if loose_srv_version >= LooseVersion('3.2') and loose_driver_version < LooseVersion('3.2'):
module.fail_json(msg=' (Note: you must use pymongo 3.2+ with MongoDB >= 3.2)')
elif loose_srv_version >= LooseVersion('3.0') and loose_driver_version <= LooseVersion('2.8'):
module.fail_json(msg=' (Note: you must use pymongo 2.8+ with MongoDB 3.0)')
elif loose_srv_version >= LooseVersion('2.6') and loose_driver_version <= LooseVersion('2.7'):
module.fail_json(msg=' (Note: you must use pymongo 2.7+ with MongoDB 2.6)')
elif LooseVersion(PyMongoVersion) <= LooseVersion('2.5'):
module.fail_json(msg=' (Note: you must be on mongodb 2.4+ and pymongo 2.5+ to use the roles param)')
def replicaset_find(client):
"""Check if a replicaset exists.
Args:
client (cursor): Mongodb cursor on admin database.
replica_set (str): replica_set to check.
Returns:
dict: when user exists, False otherwise.
"""
for rs in client["local"].system.replset.find({}):
return rs["_id"]
return False
def replicaset_add(module, client, replica_set, members, arbiter_at_index, protocol_version,
chaining_allowed, heartbeat_timeout_secs, election_timeout_millis):
try:
from collections import OrderedDict
except ImportError as excep:
try:
from ordereddict import OrderedDict
except ImportError as excep:
module.fail_json(msg='Cannot import OrderedDict class. You can probably install with: pip install ordereddict: %s'
% to_native(excep))
members_dict_list = []
index = 0
settings = {
"chainingAllowed": bool(chaining_allowed),
}
if protocol_version == 0:
settings['heartbeatTimeoutSecs'] = heartbeat_timeout_secs
else:
settings['electionTimeoutMillis'] = election_timeout_millis
for member in members:
if ':' not in member: # No port supplied. Assume 27017
member += ":27017"
members_dict_list.append(OrderedDict([("_id", index), ("host", member)]))
if index == arbiter_at_index:
members_dict_list[index]['arbiterOnly'] = True
index += 1
conf = OrderedDict([("_id", replica_set),
("protocolVersion", protocol_version),
("members", members_dict_list),
("settings", settings)])
client["admin"].command('replSetInitiate', conf)
def replicaset_remove(module, client, replica_set):
raise NotImplementedError
# exists = replicaset_find(client, replica_set)
# if exists:
# if module.check_mode:
# module.exit_json(changed=True, replica_set=replica_set)
# db = client[db_name]
# db.remove_user(replica_set)
# else:
# module.exit_json(changed=False, user=user)
def load_mongocnf():
config = configparser.RawConfigParser()
mongocnf = os.path.expanduser('~/.mongodb.cnf')
try:
config.readfp(open(mongocnf))
except (configparser.NoOptionError, IOError):
return False
creds = dict(
user=config.get('client', 'user'),
password=config.get('client', 'pass')
)
return creds
# =========================================
# Module execution.
#
def main():
module = AnsibleModule(
argument_spec=dict(
login_user=dict(type='str'),
login_password=dict(type='str', no_log=True),
login_database=dict(type='str', default="admin"),
login_host=dict(type='str', default="localhost"),
login_port=dict(type='int', default=27017),
replica_set=dict(type='str', default="rs0"),
members=dict(type='list', elements='raw'),
arbiter_at_index=dict(type='int'),
validate=dict(type='bool', default=True),
ssl=dict(type='bool', default=False),
ssl_cert_reqs=dict(type='str', default='CERT_REQUIRED', choices=['CERT_NONE', 'CERT_OPTIONAL', 'CERT_REQUIRED']),
protocol_version=dict(type='int', default=1, choices=[0, 1]),
chaining_allowed=dict(type='bool', default=True),
heartbeat_timeout_secs=dict(type='int', default=10),
election_timeout_millis=dict(type='int', default=10000)
),
supports_check_mode=True,
)
if not HAS_PYMONGO:
module.fail_json(msg='the python pymongo module is required')
login_user = module.params['login_user']
login_password = module.params['login_password']
login_database = module.params['login_database']
login_host = module.params['login_host']
login_port = module.params['login_port']
replica_set = module.params['replica_set']
members = module.params['members']
arbiter_at_index = module.params['arbiter_at_index']
validate = module.params['validate']
ssl = module.params['ssl']
protocol_version = module.params['protocol_version']
chaining_allowed = module.params['chaining_allowed']
heartbeat_timeout_secs = module.params['heartbeat_timeout_secs']
election_timeout_millis = module.params['election_timeout_millis']
if validate:
if len(members) <= 2 or len(members) % 2 == 0:
module.fail_json(msg="MongoDB Replicaset validation failed. Invalid number of replicaset members.")
if arbiter_at_index is not None and len(members) - 1 < arbiter_at_index:
module.fail_json(msg="MongoDB Replicaset validation failed. Invalid arbiter index.")
result = dict(
changed=False,
replica_set=replica_set,
)
connection_params = dict(
host=login_host,
port=int(login_port),
)
if ssl:
connection_params["ssl"] = ssl
connection_params["ssl_cert_reqs"] = getattr(ssl_lib, module.params['ssl_cert_reqs'])
try:
client = MongoClient(**connection_params)
except Exception as e:
module.fail_json(msg='Unable to connect to database: %s' % to_native(e))
try:
check_compatibility(module, client)
except Exception as excep:
if "not authorized on" not in str(excep) and "there are no users authenticated" not in str(excep):
raise excep
if login_user is None or login_password is None:
raise excep
client.admin.authenticate(login_user, login_password, source=login_database)
check_compatibility(module, client)
if login_user is None and login_password is None:
mongocnf_creds = load_mongocnf()
if mongocnf_creds is not False:
login_user = mongocnf_creds['user']
login_password = mongocnf_creds['password']
elif login_password is None or login_user is None:
module.fail_json(msg="When supplying login arguments, both 'login_user' and 'login_password' must be provided")
try:
client['admin'].command('listDatabases', 1.0) # if this throws an error we need to authenticate
except Exception as excep:
if "not authorized on" in str(excep) or "command listDatabases requires authentication" in str(excep):
if login_user is not None and login_password is not None:
client.admin.authenticate(login_user, login_password, source=login_database)
else:
raise excep
else:
raise excep
if len(replica_set) == 0:
module.fail_json(msg="Parameter 'replica_set' must not be an empty string")
try:
rs = replicaset_find(client)
except Exception as e:
module.fail_json(msg='Unable to query replica_set info: %s' % to_native(e))
if not rs:
if not module.check_mode:
try:
replicaset_add(module, client, replica_set, members, arbiter_at_index, protocol_version,
chaining_allowed, heartbeat_timeout_secs, election_timeout_millis)
result['changed'] = True
except Exception as e:
module.fail_json(msg='Unable to create replica_set: %s' % to_native(e))
else:
if not module.check_mode:
try:
rs = replicaset_find(client)
except Exception as e:
module.fail_json(msg='Unable to query replica_set info: %s' % to_native(e))
if rs is not None and rs != replica_set:
module.fail_json(msg="The replica_set name of '{0}' does not match the expected: '{1}'".format(rs, replica_set))
result['changed'] = False
module.exit_json(**result)
if __name__ == '__main__':
main()

@ -1,351 +0,0 @@
#!/usr/bin/python
# (c) 2018, 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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: mongodb_shard
short_description: Add or remove shards from a MongoDB Cluster
description:
- Add or remove shards from a MongoDB Cluster.
author: Rhys Campbell (@rhysmeister)
version_added: "2.8"
options:
login_user:
description:
- The MongoDB user to login with.
required: false
type: str
login_password:
description:
- The login user's password used to authenticate with.
required: false
type: str
login_database:
description:
- The database where login credentials are stored.
required: false
type: str
default: admin
login_host:
description:
- The host to login to.
- This must be a mongos.
required: false
type: str
default: localhost
login_port:
description:
- The MongoDB port to login to.
required: false
type: int
default: 27017
shard:
description:
- The shard connection string.
- Should be supplied in the form <replicaset>/host:port as detailed
in U(https://docs.mongodb.com/manual/tutorial/add-shards-to-shard-cluster/).
- For example rs0/example1.mongodb.com:27017.
required: true
type: str
ssl:
description:
- Whether to use an SSL connection when connecting to the database.
default: False
type: bool
ssl_cert_reqs:
description:
- Specifies whether a certificate is required from the other side of the connection,
and whether it will be validated if provided.
required: false
type: str
default: CERT_REQUIRED
choices: [CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED]
state:
description:
- Whether the shard should be present or absent from the Cluster.
required: false
type: str
default: present
choices: [absent, present]
notes:
- Requires the pymongo Python package on the remote host, version 2.4.2+.
requirements: [ pymongo ]
'''
EXAMPLES = '''
- name: Add a replicaset shard named rs1 with a member running on port 27018 on mongodb0.example.net
mongodb_shard:
login_user: admin
login_password: admin
shard: "rs1/mongodb0.example.net:27018"
state: present
- name: Add a standalone mongod shard running on port 27018 of mongodb0.example.net
mongodb_shard:
login_user: admin
login_password: admin
shard: "mongodb0.example.net:27018"
state: present
- name: To remove a shard called 'rs1'
mongodb_shard:
login_user: admin
login_password: admin
shard: rs1
state: absent
# Single node shard running on localhost
- name: Ensure shard rs0 exists
mongodb_shard:
login_user: admin
login_password: secret
shard: "rs0/localhost:3001"
state: present
# Single node shard running on localhost
- name: Ensure shard rs1 exists
mongodb_shard:
login_user: admin
login_password: secret
shard: "rs1/localhost:3002"
state: present
'''
RETURN = '''
mongodb_shard:
description: The name of the shard to create.
returned: success
type: str
'''
import os
import ssl as ssl_lib
import traceback
from distutils.version import LooseVersion
try:
from pymongo.errors import ConnectionFailure
from pymongo.errors import OperationFailure
from pymongo import version as PyMongoVersion
from pymongo import MongoClient
except ImportError:
try: # for older PyMongo 2.2
from pymongo import Connection as MongoClient
except ImportError:
pymongo_found = False
else:
pymongo_found = True
else:
pymongo_found = True
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.six import binary_type, text_type
from ansible.module_utils.six.moves import configparser
from ansible.module_utils._text import to_native
# =========================================
# MongoDB module specific support methods.
#
def check_compatibility(module, client):
"""Check the compatibility between the driver and the database.
See: https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
Args:
module: Ansible module.
client (cursor): Mongodb cursor on admin database.
"""
loose_srv_version = LooseVersion(client.server_info()['version'])
loose_driver_version = LooseVersion(PyMongoVersion)
if loose_srv_version >= LooseVersion('3.2') and loose_driver_version < LooseVersion('3.2'):
module.fail_json(msg=' (Note: you must use pymongo 3.2+ with MongoDB >= 3.2)')
elif loose_srv_version >= LooseVersion('3.0') and loose_driver_version <= LooseVersion('2.8'):
module.fail_json(msg=' (Note: you must use pymongo 2.8+ with MongoDB 3.0)')
elif loose_srv_version >= LooseVersion('2.6') and loose_driver_version <= LooseVersion('2.7'):
module.fail_json(msg=' (Note: you must use pymongo 2.7+ with MongoDB 2.6)')
elif LooseVersion(PyMongoVersion) <= LooseVersion('2.5'):
module.fail_json(msg=' (Note: you must be on mongodb 2.4+ and pymongo 2.5+ to use the roles param)')
def shard_find(client, shard):
"""Check if a shard exists.
Args:
client (cursor): Mongodb cursor on admin database.
shard (str): shard to check.
Returns:
dict: when user exists, False otherwise.
"""
if '/' in shard:
s = shard.split('/')[0]
else:
s = shard
for shard in client["config"].shards.find({"_id": s}):
return shard
return False
def shard_add(client, shard):
try:
sh = client["admin"].command('addShard', shard)
except Exception as excep:
raise excep
return sh
def shard_remove(client, shard):
try:
sh = client["admin"].command('removeShard', shard)
except Exception as excep:
raise excep
return sh
def load_mongocnf():
config = configparser.RawConfigParser()
mongocnf = os.path.expanduser('~/.mongodb.cnf')
try:
config.readfp(open(mongocnf))
creds = dict(
user=config.get('client', 'user'),
password=config.get('client', 'pass')
)
except (configparser.NoOptionError, IOError):
return False
return creds
# =========================================
# Module execution.
#
def main():
module = AnsibleModule(argument_spec=dict(login_user=dict(type='str', required=False),
login_password=dict(type='str', required=False, no_log=True),
login_database=dict(type='str', required=False, default='admin'),
login_host=dict(type='str', required=False, default='localhost'),
login_port=dict(type='int', default=27017, required=False),
ssl=dict(type='bool', default=False, required=False),
ssl_cert_reqs=dict(type='str', required=False, default='CERT_REQUIRED',
choices=['CERT_NONE', 'CERT_OPTIONAL', 'CERT_REQUIRED']),
shard=dict(type='str', required=True),
state=dict(type='str', required=False, default='present', choices=['absent', 'present'])),
supports_check_mode=True)
if not pymongo_found:
module.fail_json(msg=missing_required_lib('pymongo'))
login_user = module.params['login_user']
login_password = module.params['login_password']
login_database = module.params['login_database']
login_host = module.params['login_host']
login_port = module.params['login_port']
ssl = module.params['ssl']
shard = module.params['shard']
state = module.params['state']
try:
connection_params = {
"host": login_host,
"port": int(login_port)
}
if ssl:
connection_params["ssl"] = ssl
connection_params["ssl_cert_reqs"] = getattr(ssl_lib, module.params['ssl_cert_reqs'])
client = MongoClient(**connection_params)
try:
check_compatibility(module, client)
except Exception as excep:
if "not authorized on" in str(excep) or "there are no users authenticated" in str(excep):
if login_user is not None and login_password is not None:
client.admin.authenticate(login_user, login_password, source=login_database)
check_compatibility(module, client)
else:
raise excep
else:
raise excep
if login_user is None and login_password is None:
mongocnf_creds = load_mongocnf()
if mongocnf_creds is not False:
login_user = mongocnf_creds['user']
login_password = mongocnf_creds['password']
elif login_password is None or login_user is None:
module.fail_json(msg='when supplying login arguments, both login_user and login_password must be provided')
try:
client['admin'].command('listDatabases', 1.0) # if this throws an error we need to authenticate
except Exception as excep:
if "not authorized on" in str(excep) or "command listDatabases requires authentication" in str(excep):
if login_user is not None and login_password is not None:
client.admin.authenticate(login_user, login_password, source=login_database)
else:
raise excep
else:
raise excep
except Exception as e:
module.fail_json(msg='unable to connect to database: %s' % to_native(e), exception=traceback.format_exc())
try:
if client["admin"].command("serverStatus")["process"] != "mongos":
module.fail_json(msg="Process running on {0}:{1} is not a mongos".format(login_host, login_port))
shard_created = False
if module.check_mode:
if state == "present":
if not shard_find(client, shard):
changed = True
else:
changed = False
elif state == "absent":
if not shard_find(client, shard):
changed = False
else:
changed = True
else:
if state == "present":
if not shard_find(client, shard):
shard_add(client, shard)
changed = True
else:
changed = False
elif state == "absent":
if shard_find(client, shard):
shard_remove(client, shard)
changed = True
else:
changed = False
except Exception as e:
action = "add"
if state == "absent":
action = "remove"
module.fail_json(msg='Unable to {0} shard: %s'.format(action) % to_native(e), exception=traceback.format_exc())
module.exit_json(changed=changed, shard=shard)
if __name__ == '__main__':
main()

@ -1,474 +0,0 @@
#!/usr/bin/python
# (c) 2012, Elliott Foster <elliott@fourkitchens.com>
# Sponsored by Four Kitchens http://fourkitchens.com.
# (c) 2014, Epic Games, Inc.
#
# 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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: mongodb_user
short_description: Adds or removes a user from a MongoDB database
description:
- Adds or removes a user from a MongoDB database.
version_added: "1.1"
options:
login_user:
description:
- The MongoDB username used to authenticate with.
type: str
login_password:
description:
- The login user's password used to authenticate with.
type: str
login_host:
description:
- The host running the database.
default: localhost
type: str
login_port:
description:
- The MongoDB port to connect to.
default: '27017'
type: str
login_database:
version_added: "2.0"
description:
- The database where login credentials are stored.
type: str
replica_set:
version_added: "1.6"
description:
- Replica set to connect to (automatically connects to primary for writes).
type: str
database:
description:
- The name of the database to add/remove the user from.
required: true
type: str
aliases: [db]
name:
description:
- The name of the user to add or remove.
required: true
aliases: [user]
type: str
password:
description:
- The password to use for the user.
type: str
aliases: [pass]
ssl:
version_added: "1.8"
description:
- Whether to use an SSL connection when connecting to the database.
type: bool
ssl_cert_reqs:
version_added: "2.2"
description:
- Specifies whether a certificate is required from the other side of the connection,
and whether it will be validated if provided.
default: CERT_REQUIRED
choices: [CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED]
type: str
roles:
version_added: "1.3"
type: list
elements: raw
description:
- >
The database user roles valid values could either be one or more of the following strings:
'read', 'readWrite', 'dbAdmin', 'userAdmin', 'clusterAdmin', 'readAnyDatabase', 'readWriteAnyDatabase', 'userAdminAnyDatabase',
'dbAdminAnyDatabase'
- "Or the following dictionary '{ db: DATABASE_NAME, role: ROLE_NAME }'."
- "This param requires pymongo 2.5+. If it is a string, mongodb 2.4+ is also required. If it is a dictionary, mongo 2.6+ is required."
state:
description:
- The database user state.
default: present
choices: [absent, present]
type: str
update_password:
default: always
choices: [always, on_create]
version_added: "2.1"
description:
- C(always) will update passwords if they differ.
- C(on_create) will only set the password for newly created users.
type: str
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 http://api.mongodb.org/python/current/installation.html
requirements: [ "pymongo" ]
author:
- "Elliott Foster (@elliotttf)"
- "Julien Thebault (@Lujeni)"
'''
EXAMPLES = '''
- name: Create 'burgers' database user with name 'bob' and password '12345'.
mongodb_user:
database: burgers
name: bob
password: 12345
state: present
- name: Create a database user via SSL (MongoDB must be compiled with the SSL option and configured properly)
mongodb_user:
database: burgers
name: bob
password: 12345
state: present
ssl: True
- name: Delete 'burgers' database user with name 'bob'.
mongodb_user:
database: burgers
name: bob
state: absent
- name: Define more users with various specific roles (if not defined, no roles is assigned, and the user will be added via pre mongo 2.2 style)
mongodb_user:
database: burgers
name: ben
password: 12345
roles: read
state: present
- name: Define roles
mongodb_user:
database: burgers
name: jim
password: 12345
roles: readWrite,dbAdmin,userAdmin
state: present
- name: Define roles
mongodb_user:
database: burgers
name: joe
password: 12345
roles: readWriteAnyDatabase
state: present
- name: Add a user to database in a replica set, the primary server is automatically discovered and written to
mongodb_user:
database: burgers
name: bob
replica_set: belcher
password: 12345
roles: readWriteAnyDatabase
state: present
# add a user 'oplog_reader' with read only access to the 'local' database on the replica_set 'belcher'. This is useful for oplog access (MONGO_OPLOG_URL).
# please notice the credentials must be added to the 'admin' database because the 'local' database is not synchronized and can't receive user credentials
# To login with such user, the connection string should be MONGO_OPLOG_URL="mongodb://oplog_reader:oplog_reader_password@server1,server2/local?authSource=admin"
# This syntax requires mongodb 2.6+ and pymongo 2.5+
- name: Roles as a dictionary
mongodb_user:
login_user: root
login_password: root_password
database: admin
user: oplog_reader
password: oplog_reader_password
state: present
replica_set: belcher
roles:
- db: local
role: read
'''
RETURN = '''
user:
description: The name of the user to add or remove.
returned: success
type: str
'''
import os
import ssl as ssl_lib
import traceback
from distutils.version import LooseVersion
from operator import itemgetter
try:
from pymongo.errors import ConnectionFailure
from pymongo.errors import OperationFailure
from pymongo import version as PyMongoVersion
from pymongo import MongoClient
except ImportError:
try: # for older PyMongo 2.2
from pymongo import Connection as MongoClient
except ImportError:
pymongo_found = False
else:
pymongo_found = True
else:
pymongo_found = True
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.six import binary_type, text_type
from ansible.module_utils.six.moves import configparser
from ansible.module_utils._text import to_native
# =========================================
# MongoDB module specific support methods.
#
def check_compatibility(module, client):
"""Check the compatibility between the driver and the database.
See: https://docs.mongodb.com/ecosystem/drivers/driver-compatibility-reference/#python-driver-compatibility
Args:
module: Ansible module.
client (cursor): Mongodb cursor on admin database.
"""
loose_srv_version = LooseVersion(client.server_info()['version'])
loose_driver_version = LooseVersion(PyMongoVersion)
if loose_srv_version >= LooseVersion('3.2') and loose_driver_version < LooseVersion('3.2'):
module.fail_json(msg=' (Note: you must use pymongo 3.2+ with MongoDB >= 3.2)')
elif loose_srv_version >= LooseVersion('3.0') and loose_driver_version <= LooseVersion('2.8'):
module.fail_json(msg=' (Note: you must use pymongo 2.8+ with MongoDB 3.0)')
elif loose_srv_version >= LooseVersion('2.6') and loose_driver_version <= LooseVersion('2.7'):
module.fail_json(msg=' (Note: you must use pymongo 2.7+ with MongoDB 2.6)')
elif LooseVersion(PyMongoVersion) <= LooseVersion('2.5'):
module.fail_json(msg=' (Note: you must be on mongodb 2.4+ and pymongo 2.5+ to use the roles param)')
def user_find(client, user, db_name):
"""Check if the user exists.
Args:
client (cursor): Mongodb cursor on admin database.
user (str): User to check.
db_name (str): User's database.
Returns:
dict: when user exists, False otherwise.
"""
for mongo_user in client["admin"].system.users.find():
if mongo_user['user'] == user:
# NOTE: there is no 'db' field in mongo 2.4.
if 'db' not in mongo_user:
return mongo_user
if mongo_user["db"] == db_name:
return mongo_user
return False
def user_add(module, client, db_name, user, password, roles):
# pymongo's user_add is a _create_or_update_user so we won't know if it was changed or updated
# without reproducing a lot of the logic in database.py of pymongo
db = client[db_name]
if roles is None:
db.add_user(user, password, False)
else:
db.add_user(user, password, None, roles=roles)
def user_remove(module, client, db_name, user):
exists = user_find(client, user, db_name)
if exists:
if module.check_mode:
module.exit_json(changed=True, user=user)
db = client[db_name]
db.remove_user(user)
else:
module.exit_json(changed=False, user=user)
def load_mongocnf():
config = configparser.RawConfigParser()
mongocnf = os.path.expanduser('~/.mongodb.cnf')
try:
config.readfp(open(mongocnf))
creds = dict(
user=config.get('client', 'user'),
password=config.get('client', 'pass')
)
except (configparser.NoOptionError, IOError):
return False
return creds
def check_if_roles_changed(uinfo, roles, db_name):
# We must be aware of users which can read the oplog on a replicaset
# Such users must have access to the local DB, but since this DB does not store users credentials
# and is not synchronized among replica sets, the user must be stored on the admin db
# Therefore their structure is the following :
# {
# "_id" : "admin.oplog_reader",
# "user" : "oplog_reader",
# "db" : "admin", # <-- admin DB
# "roles" : [
# {
# "role" : "read",
# "db" : "local" # <-- local DB
# }
# ]
# }
def make_sure_roles_are_a_list_of_dict(roles, db_name):
output = list()
for role in roles:
if isinstance(role, (binary_type, text_type)):
new_role = {"role": role, "db": db_name}
output.append(new_role)
else:
output.append(role)
return output
roles_as_list_of_dict = make_sure_roles_are_a_list_of_dict(roles, db_name)
uinfo_roles = uinfo.get('roles', [])
if sorted(roles_as_list_of_dict, key=itemgetter('db')) == sorted(uinfo_roles, key=itemgetter('db')):
return False
return True
# =========================================
# Module execution.
#
def main():
module = AnsibleModule(
argument_spec=dict(
login_user=dict(default=None),
login_password=dict(default=None, no_log=True),
login_host=dict(default='localhost'),
login_port=dict(default='27017'),
login_database=dict(default=None),
replica_set=dict(default=None),
database=dict(required=True, aliases=['db']),
name=dict(required=True, aliases=['user']),
password=dict(aliases=['pass'], no_log=True),
ssl=dict(default=False, type='bool'),
roles=dict(default=None, type='list', elements='raw'),
state=dict(default='present', choices=['absent', 'present']),
update_password=dict(default="always", choices=["always", "on_create"]),
ssl_cert_reqs=dict(default='CERT_REQUIRED', choices=['CERT_NONE', 'CERT_OPTIONAL', 'CERT_REQUIRED']),
),
supports_check_mode=True
)
if not pymongo_found:
module.fail_json(msg=missing_required_lib('pymongo'))
login_user = module.params['login_user']
login_password = module.params['login_password']
login_host = module.params['login_host']
login_port = module.params['login_port']
login_database = module.params['login_database']
replica_set = module.params['replica_set']
db_name = module.params['database']
user = module.params['name']
password = module.params['password']
ssl = module.params['ssl']
roles = module.params['roles'] or []
state = module.params['state']
update_password = module.params['update_password']
try:
connection_params = {
"host": login_host,
"port": int(login_port),
}
if replica_set:
connection_params["replicaset"] = replica_set
if ssl:
connection_params["ssl"] = ssl
connection_params["ssl_cert_reqs"] = getattr(ssl_lib, module.params['ssl_cert_reqs'])
client = MongoClient(**connection_params)
# NOTE: this check must be done ASAP.
# We doesn't need to be authenticated (this ability has lost in PyMongo 3.6)
if LooseVersion(PyMongoVersion) <= LooseVersion('3.5'):
check_compatibility(module, client)
if login_user is None and login_password is None:
mongocnf_creds = load_mongocnf()
if mongocnf_creds is not False:
login_user = mongocnf_creds['user']
login_password = mongocnf_creds['password']
elif login_password is None or login_user is None:
module.fail_json(msg='when supplying login arguments, both login_user and login_password must be provided')
if login_user is not None and login_password is not None:
client.admin.authenticate(login_user, login_password, source=login_database)
elif LooseVersion(PyMongoVersion) >= LooseVersion('3.0'):
if db_name != "admin":
module.fail_json(msg='The localhost login exception only allows the first admin account to be created')
# else: this has to be the first admin user added
except Exception as e:
module.fail_json(msg='unable to connect to database: %s' % to_native(e), exception=traceback.format_exc())
if state == 'present':
if password is None and update_password == 'always':
module.fail_json(msg='password parameter required when adding a user unless update_password is set to on_create')
try:
if update_password != 'always':
uinfo = user_find(client, user, db_name)
if uinfo:
password = None
if not check_if_roles_changed(uinfo, roles, db_name):
module.exit_json(changed=False, user=user)
if module.check_mode:
module.exit_json(changed=True, user=user)
user_add(module, client, db_name, user, password, roles)
except Exception as e:
module.fail_json(msg='Unable to add or update user: %s' % to_native(e), exception=traceback.format_exc())
finally:
try:
client.close()
except Exception:
pass
# Here we can check password change if mongo provide a query for that : https://jira.mongodb.org/browse/SERVER-22848
# newuinfo = user_find(client, user, db_name)
# if uinfo['role'] == newuinfo['role'] and CheckPasswordHere:
# module.exit_json(changed=False, user=user)
elif state == 'absent':
try:
user_remove(module, client, db_name, user)
except Exception as e:
module.fail_json(msg='Unable to remove user: %s' % to_native(e), exception=traceback.format_exc())
finally:
try:
client.close()
except Exception:
pass
module.exit_json(changed=True, user=user)
if __name__ == '__main__':
main()

@ -1,181 +0,0 @@
# (c) 2018, Matt Martz <matt@sivel.net>
# 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
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
cache: mongodb
short_description: Use MongoDB for caching
description:
- This cache uses per host records saved in MongoDB.
version_added: "2.5"
requirements:
- pymongo>=3
options:
_uri:
description:
- MongoDB Connection String URI
required: False
env:
- name: ANSIBLE_CACHE_PLUGIN_CONNECTION
ini:
- key: fact_caching_connection
section: defaults
_prefix:
description: User defined prefix to use when creating the DB entries
default: ansible_facts
env:
- name: ANSIBLE_CACHE_PLUGIN_PREFIX
ini:
- key: fact_caching_prefix
section: defaults
_timeout:
default: 86400
description: Expiration timeout in seconds for the cache plugin data. Set to 0 to never expire
env:
- name: ANSIBLE_CACHE_PLUGIN_TIMEOUT
ini:
- key: fact_caching_timeout
section: defaults
type: integer
'''
import datetime
from contextlib import contextmanager
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.plugins.cache import BaseCacheModule
from ansible.utils.display import Display
try:
import pymongo
except ImportError:
raise AnsibleError("The 'pymongo' python module is required for the mongodb fact cache, 'pip install pymongo>=3.0'")
display = Display()
class CacheModule(BaseCacheModule):
"""
A caching module backed by mongodb.
"""
def __init__(self, *args, **kwargs):
try:
super(CacheModule, self).__init__(*args, **kwargs)
self._connection = self.get_option('_uri')
self._timeout = int(self.get_option('_timeout'))
self._prefix = self.get_option('_prefix')
except KeyError:
display.deprecated('Rather than importing CacheModules directly, '
'use ansible.plugins.loader.cache_loader', version='2.12')
self._connection = C.CACHE_PLUGIN_CONNECTION
self._timeout = int(C.CACHE_PLUGIN_TIMEOUT)
self._prefix = C.CACHE_PLUGIN_PREFIX
self._cache = {}
self._managed_indexes = False
def _manage_indexes(self, collection):
'''
This function manages indexes on the mongo collection.
We only do this once, at run time based on _managed_indexes,
rather than per connection instantiation as that would be overkill
'''
_timeout = self._timeout
if _timeout and _timeout > 0:
try:
collection.create_index(
'date',
name='ttl',
expireAfterSeconds=_timeout
)
except pymongo.errors.OperationFailure:
# We make it here when the fact_caching_timeout was set to a different value between runs
collection.drop_index('ttl')
return self._manage_indexes(collection)
else:
collection.drop_index('ttl')
@contextmanager
def _collection(self):
'''
This is a context manager for opening and closing mongo connections as needed. This exists as to not create a global
connection, due to pymongo not being fork safe (http://api.mongodb.com/python/current/faq.html#is-pymongo-fork-safe)
'''
mongo = pymongo.MongoClient(self._connection)
try:
db = mongo.get_default_database()
except pymongo.errors.ConfigurationError:
# We'll fall back to using ``ansible`` as the database if one was not provided
# in the MongoDB Connection String URI
db = mongo['ansible']
# The collection is hard coded as ``cache``, there are no configuration options for this
collection = db['cache']
if not self._managed_indexes:
# Only manage the indexes once per run, not per connection
self._manage_indexes(collection)
self._managed_indexes = True
yield collection
mongo.close()
def _make_key(self, key):
return '%s%s' % (self._prefix, key)
def get(self, key):
if key not in self._cache:
with self._collection() as collection:
value = collection.find_one({'_id': self._make_key(key)})
self._cache[key] = value['data']
return self._cache.get(key)
def set(self, key, value):
self._cache[key] = value
with self._collection() as collection:
collection.update_one(
{'_id': self._make_key(key)},
{
'$set': {
'_id': self._make_key(key),
'data': value,
'date': datetime.datetime.utcnow()
}
},
upsert=True
)
def keys(self):
with self._collection() as collection:
return [doc['_id'] for doc in collection.find({}, {'_id': True})]
def contains(self, key):
with self._collection() as collection:
return bool(collection.count({'_id': self._make_key(key)}))
def delete(self, key):
del self._cache[key]
with self._collection() as collection:
collection.delete_one({'_id': self._make_key(key)})
def flush(self):
with self._collection() as collection:
collection.delete_many({})
def copy(self):
with self._collection() as collection:
return dict((d['_id'], d['data']) for d in collection.find({}))
def __getstate__(self):
return dict()
def __setstate__(self, data):
self.__init__()

@ -1,238 +0,0 @@
# (c) 2016, Marcos Diez <marcos@unitron.com.br>
# https://github.com/marcosdiez/
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
from ansible.module_utils.six import string_types, integer_types
__metaclass__ = type
DOCUMENTATION = '''
author: 'Marcos Diez <marcos (at) unitron.com.br>'
lookup: mongodb
version_added: "2.3"
short_description: lookup info from MongoDB
description:
- 'The ``MongoDB`` lookup runs the *find()* command on a given *collection* on a given *MongoDB* server.'
- 'The result is a list of jsons, so slightly different from what PyMongo returns. In particular, *timestamps* are converted to epoch integers.'
options:
connect_string:
description:
- Can be any valid MongoDB connection string, supporting authentication, replicasets, etc.
- "More info at U(https://docs.mongodb.org/manual/reference/connection-string/)"
default: "mongodb://localhost/"
database:
description:
- Name of the database which the query will be made
required: True
collection:
description:
- Name of the collection which the query will be made
required: True
filter:
description:
- Criteria of the output
type: 'dict'
default: '{}'
projection:
description:
- Fields you want returned
type: dict
default: "{}"
skip:
description:
- How many results should be skipped
type: integer
limit:
description:
- How many results should be shown
type: integer
sort:
description:
- Sorting rules. Please notice the constats are replaced by strings.
type: list
default: "[]"
notes:
- "Please check https://api.mongodb.org/python/current/api/pymongo/collection.html?highlight=find#pymongo.collection.Collection.find for more details."
requirements:
- pymongo >= 2.4 (python library)
'''
EXAMPLES = '''
- hosts: all
gather_facts: false
vars:
mongodb_parameters:
#mandatory parameters
database: 'local'
#optional
collection: "startup_log"
connection_string: "mongodb://localhost/"
extra_connection_parameters: { "ssl" : True , "ssl_certfile": /etc/self_signed_certificate.pem" }
#optional query parameters, we accept any parameter from the normal mongodb query.
filter: { "hostname": "batman" }
projection: { "pid": True , "_id" : False , "hostname" : True }
skip: 0
limit: 1
sort: [ [ "startTime" , "ASCENDING" ] , [ "age", "DESCENDING" ] ]
tasks:
- debug: msg="Mongo has already started with the following PID [{{ item.pid }}]"
with_mongodb: "{{mongodb_parameters}}"
'''
import datetime
try:
from pymongo import ASCENDING, DESCENDING
from pymongo.errors import ConnectionFailure
from pymongo import MongoClient
except ImportError:
try: # for older PyMongo 2.2
from pymongo import Connection as MongoClient
except ImportError:
pymongo_found = False
else:
pymongo_found = True
else:
pymongo_found = True
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
def _fix_sort_parameter(self, sort_parameter):
if sort_parameter is None:
return sort_parameter
if not isinstance(sort_parameter, list):
raise AnsibleError(u"Error. Sort parameters must be a list, not [ {0} ]".format(sort_parameter))
for item in sort_parameter:
self._convert_sort_string_to_constant(item)
return sort_parameter
def _convert_sort_string_to_constant(self, item):
original_sort_order = item[1]
sort_order = original_sort_order.upper()
if sort_order == u"ASCENDING":
item[1] = ASCENDING
elif sort_order == u"DESCENDING":
item[1] = DESCENDING
# else the user knows what s/he is doing and we won't predict. PyMongo will return an error if necessary
def convert_mongo_result_to_valid_json(self, result):
if result is None:
return result
if isinstance(result, integer_types + (float, bool)):
return result
if isinstance(result, string_types):
return result
elif isinstance(result, list):
new_list = []
for elem in result:
new_list.append(self.convert_mongo_result_to_valid_json(elem))
return new_list
elif isinstance(result, dict):
new_dict = {}
for key in result.keys():
value = result[key] # python2 and 3 compatible....
new_dict[key] = self.convert_mongo_result_to_valid_json(value)
return new_dict
elif isinstance(result, datetime.datetime):
# epoch
return (result - datetime.datetime(1970, 1, 1)). total_seconds()
else:
# failsafe
return u"{0}".format(result)
def run(self, terms, variables, **kwargs):
ret = []
for term in terms:
u'''
Makes a MongoDB query and returns the output as a valid list of json.
Timestamps are converted to epoch integers/longs.
Here is a sample playbook that uses it:
-------------------------------------------------------------------------------
- hosts: all
gather_facts: false
vars:
mongodb_parameters:
#optional parameter, default = "mongodb://localhost/"
# connection_string: "mongodb://localhost/"
#mandatory parameters
database: 'local'
collection: "startup_log"
#optional query parameters
#we accept any parameter from the normal mongodb query.
# the official documentation is here
# https://api.mongodb.org/python/current/api/pymongo/collection.html?highlight=find#pymongo.collection.Collection.find
# filter: { "hostname": "batman" }
# projection: { "pid": True , "_id" : False , "hostname" : True }
# skip: 0
# limit: 1
# sort: [ [ "startTime" , "ASCENDING" ] , [ "age", "DESCENDING" ] ]
# extra_connection_parameters = { }
# dictionary with extra parameters like ssl, ssl_keyfile, maxPoolSize etc...
# the full list is available here. It varies from PyMongo version
# https://api.mongodb.org/python/current/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient
tasks:
- debug: msg="Mongo has already started with the following PID [{{ item.pid }}] - full_data {{ item }} "
with_items:
- "{{ lookup('mongodb', mongodb_parameters) }}"
-------------------------------------------------------------------------------
'''
connection_string = term.get(u'connection_string', u"mongodb://localhost")
database = term[u"database"]
collection = term[u'collection']
extra_connection_parameters = term.get(u'extra_connection_parameters', {})
if u"extra_connection_parameters" in term:
del term[u"extra_connection_parameters"]
if u"connection_string" in term:
del term[u"connection_string"]
del term[u"database"]
del term[u"collection"]
if u"sort" in term:
term[u"sort"] = self._fix_sort_parameter(term[u"sort"])
# all other parameters are sent to mongo, so we are future and past proof
try:
client = MongoClient(connection_string, **extra_connection_parameters)
results = client[database][collection].find(**term)
for result in results:
result = self.convert_mongo_result_to_valid_json(result)
ret.append(result)
except ConnectionFailure as e:
raise AnsibleError(u'unable to connect to database: %s' % str(e))
return ret

@ -1,5 +0,0 @@
destructive
shippable/posix/group4
skip/aix
skip/osx
skip/freebsd

@ -1,4 +0,0 @@
mongodb_default_port: 27017
mongodb_admin_user: admin
mongodb_admin_password: admin
mongodb_default_db: admin

@ -1,3 +0,0 @@
dependencies:
- setup_mongodb_v4
- setup_remote_tmp_dir

@ -1,8 +0,0 @@
# Copyright 2020, Andrew Klychkov <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Main mongodb_info module tests
- import_tasks: mongodb_info.yml
when:
- ansible_distribution == 'CentOS'
- ansible_distribution_major_version is version('7', '>=')

@ -1,60 +0,0 @@
# Copyright 2020, Andrew Klychkov <aaklychkov@mail.ru>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
- vars:
task_parameters: &task_parameters
register: result
mongo_parameters: &mongo_parameters
login_port: '{{ mongodb_default_port }}'
login_user: '{{ mongodb_admin_user }}'
login_password: '{{ mongodb_admin_password }}'
login_database: '{{ mongodb_default_db }}'
block:
- name: Get info
<<: *task_parameters
mongodb_info:
<<: *mongo_parameters
- assert:
that:
- result is not changed
- result.general.version == '4.2.3'
- result.databases.admin
- result.total_size
- result.users.admin
- result.roles.backup
- result.parameters.logLevel == 0
- name: Get info with filter
<<: *task_parameters
mongodb_info:
<<: *mongo_parameters
filter: general, total_size
- assert:
that:
- result is not changed
- result.general.version == '4.2.3'
- result.total_size
- result.databases is not defined
- result.parameters is not defined
- result.users is not defined
- result.roles is not defined
- name: Get info with filter
<<: *task_parameters
mongodb_info:
<<: *mongo_parameters
filter: '!parameters'
- assert:
that:
- result is not changed
- result.general.version == '4.2.3'
- result.databases.admin
- result.total_size
- result.users.admin
- result.roles.backup
- result.parameters is not defined

@ -1,7 +0,0 @@
destructive
shippable/posix/group1
skip/aix
skip/osx
skip/freebsd
skip/rhel
needs/root

@ -1,21 +0,0 @@
---
# defaults file for test_mongodb_user
mongodb_admin_user: test_root
mongodb_admin_password: saE_Rr9!gE6gh#e~R#nZ
mongod_auth: false
kill_signal: SIGTERM
# Should be one of
# --storageEngine wiredTiger --wiredTigerEngineConfigString="cache_size=200M"
# --storageEngine mmapv1 --nojournal
mongod_storage_engine_opts: "--storageEngine wiredTiger --wiredTigerEngineConfigString='cache_size=200M'"
mongodb_user: mongodb
mongodb_user_list:
- { "name": "user1", "password": "password1", "roles": "read", "database": "test" }
- { "name": "user2", "password": "password2", "roles": "readWrite", "database": "test" }
- { "name": "user3", "password": "password3", "roles": "dbAdmin", "database": "test" }
- { "name": "user4", "password": "password4", "roles": "userAdmin", "database": "test" }
- { "name": "user5", "password": "password5", "roles": "clusterAdmin", "database": "admin" }
- { "name": "user6", "password": "password6", "roles": "readAnyDatabase", "database": "admin" }
- { "name": "user7", "password": "password7", "roles": "readWriteAnyDatabase", "database": "admin" }
- { "name": "user8", "password": "password8", "roles": "userAdminAnyDatabase", "database": "admin" }
- { "name": "user9", "password": "password9", "roles": "dbAdminAnyDatabase", "database": "admin" }

@ -1,3 +0,0 @@
dependencies:
- setup_mongodb
- setup_remote_tmp_dir

@ -1,143 +0,0 @@
# test code for the mongodb_parameter module
# (c) 2019, Rhys Campbell <rhys.james.campbell@googlemail.com>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# ============================================================
- name: Ensure tests home exists
file:
path: "{{ remote_tmp_dir }}/tests"
state: directory
- include_tasks: mongod_teardown.yml
- include_tasks: mongod_singlenode.yml
- name: Set syncdelay to 99
mongodb_parameter:
login_port: 3001
param: syncdelay
value: 99
param_type: int
register: sd_change
- assert:
that:
- sd_change.before | int == 60
- sd_change.after | int == 99
- sd_change.changed == True
- name: Set syncdelay to 99 (again)
mongodb_parameter:
login_port: 3001
param: syncdelay
value: 99
param_type: int
register: sd_change
- assert:
that:
- sd_change.before | int == 99
- sd_change.after | int == 99
- sd_change.changed == False
- name: Create admin user with module
mongodb_user:
login_port: 3001
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: root
state: present
register: mongodb_admin_user_created
- assert:
that:
- mongodb_admin_user_created.changed == True
- name: Kill all mongod processes
command: pkill -{{ kill_signal }} mongod
ignore_errors: true
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
delay: 3
with_items: "{{ pids_of_mongod }}"
- set_fact:
mongod_auth: true
- include_tasks: mongod_singlenode.yml
# Tests with auth enabled
- name: Set syncdelay to 59 with auth
mongodb_parameter:
login_port: 3001
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
param: syncdelay
value: 59
param_type: int
register: sd_change
- assert:
that:
- sd_change.before | int == 60
- sd_change.after | int == 59
- sd_change.changed == True
- name: Set syncdelay to 59 (again) with auth
mongodb_parameter:
login_port: 3001
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
param: syncdelay
value: 59
param_type: int
register: sd_change
- assert:
that:
- sd_change.before | int == 59
- sd_change.after | int == 59
- sd_change.changed == False
- name: Set authenticationMechanisms to MONGODB-X509 with auth (will fail)
mongodb_parameter:
login_port: 3001
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
param: authenticationMechanisms
value: "MONGODB-X509"
param_type: str
register: diag_change
ignore_errors: yes
- assert:
that:
- '"unable to change parameter" in diag_change.msg'
- diag_change.failed == True
# Clean up
- include_tasks: mongod_teardown.yml

@ -1,55 +0,0 @@
- name: Set mongodb_user user for redhat
set_fact:
mongodb_user: "mongod"
when: ansible_os_family == "RedHat"
- set_fact:
mongodb_nodes:
- 3001
- name: Create directories for mongod processes
file:
path: "{{ remote_tmp_dir }}/mongod{{ item }}"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
recurse: yes
with_items: "{{ mongodb_nodes }}"
- name: Ensure {{ remote_tmp_dir }}/config dir exists
file:
path: "{{ remote_tmp_dir }}/config"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
- name: Create keyfile
copy:
dest: "{{ remote_tmp_dir }}/my.key"
content: |
fd2CUrbXBJpB4rt74A6F
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0600
when: mongod_auth == True
- name: Spawn mongod process without auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --logpath mongod{{ item }}/log.log --fork
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == False
- name: Spawn mongod process with auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --logpath mongod{{ item }}/log.log --fork --auth --keyFile my.key
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == True
- name: Wait for mongod to start responding
wait_for:
port: "{{ item }}"
with_items: "{{ mongodb_nodes }}"

@ -1,25 +0,0 @@
- name: Kill all mongod processes
command: pkill -{{ kill_signal }} mongod
ignore_errors: true
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
delay: 1
with_items: "{{ pids_of_mongod }}"
- name: Remove all mongod folders
file:
path: "{{ remote_tmp_dir }}/{{ item }}"
state: absent
with_items:
- mongod3001
- name: Remove all mongod sock files
shell: rm -Rf /tmp/mongodb*.sock

@ -1,7 +0,0 @@
destructive
shippable/posix/group1
skip/aix
skip/osx
skip/freebsd
skip/rhel
needs/root

@ -1,24 +0,0 @@
---
# defaults file for test_mongodb_replicaset
mongodb_replicaset1: rs1
mongodb_replicaset2: rs2
mongodb_replicaset3: rs3
mongodb_replicaset4: rs4
mongodb_replicaset5: rs5
mongodb_replicaset6: rs6
mongodb_replicaset7: rs7
mongodb_replicaset8: rs8
test_mongo_auth: yes
mongodb_admin_user: test_root
mongodb_admin_password: saE_Rr9!gE6gh#e~R#nZ
debug: False
mongodb_nodes:
- 3001
- 3002
- 3003
mongod_auth: false
kill_signal: SIGTERM
# Should be one of
mongod_storage_engine_opts: "--storageEngine wiredTiger --wiredTigerEngineConfigString='cache_size=200M'"
#mongod_storage_engine_opts: "--storageEngine mmapv1 --nojournal"
mongodb_user: mongodb

@ -1,13 +0,0 @@
var done = false;
var iterations = 0;
while(rs.status()['myState'] != 1) {
if (!done) {
//print("State is not yet PRIMARY. Waiting...");
done = true
}
sleep(1000);
iterations++;
if (iterations == 100) {
throw new Error("Exceeded iterations limit.");
}
}

@ -1,3 +0,0 @@
dependencies:
- setup_mongodb
- setup_remote_tmp_dir

@ -1,540 +0,0 @@
# test code for the mongodb_replicaset module
# (c) 2019, Rhys Campbell <rhys.james.campbell@googlemail.com>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# ============================================================
- name: Ensure tests home exists
file:
path: "{{ remote_tmp_dir }}/tests"
state: directory
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset1 }}"
- include_tasks: mongod_replicaset.yml
# test with yaml list
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset1 }}"
heartbeat_timeout_secs: 1
election_timeout_millis: 1000
members:
- "localhost:3001"
- "localhost:3002"
- "localhost:3003"
- name: Ensure is_primary script exists on host
copy:
src: js/is_primary.js
dest: "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Get replicaset info
command: mongo admin --eval "rs.status()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- name: Add mongodb admin user
mongodb_user:
login_host: localhost
login_port: 3001
replica_set: "{{ mongodb_replicaset1 }}"
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: ["root"]
state: present
register: mongo_admin_user
when: test_mongo_auth
- name: Murder all mongod processes
shell: pkill -{{ kill_signal }} mongod;
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
with_items: "{{ pids_of_mongod.pids }}"
- set_fact:
current_replicaset: "{{ mongodb_replicaset1 }}"
- set_fact:
mongod_auth: true
- name: Execute mongod script to restart with auth enabled
include_tasks: mongod_replicaset.yml
- name: Validate replicaset previously created
mongodb_replicaset:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset1 }}"
election_timeout_millis: 1000
members:
- "localhost:3001"
- "localhost:3002"
- "localhost:3003"
register: mongodb_replicaset
- name: Assert replicaset name has not changed
assert:
that: mongodb_replicaset.changed == False
- name: Test with bad password
mongodb_replicaset:
login_user: "{{ mongodb_admin_user }}"
login_password: XXXXXXXXXXXXXXXX
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset1 }}"
election_timeout_millis: 1000
members:
- "localhost:3001"
- "localhost:3002"
- "localhost:3003"
register: mongodb_replicaset_bad_pw
ignore_errors: True
- name: Assert login failed
assert:
that:
- "mongodb_replicaset_bad_pw.rc == 1"
- "'Authentication failed' in mongodb_replicaset_bad_pw.module_stderr"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset2 }}"
- set_fact:
mongod_auth: false
- name: Execute mongod script to restart with auth enabled
include_tasks: mongod_replicaset.yml
# Test with python style list
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset2 }}"
members: [ "localhost:3001", "localhost:3002", "localhost:3003" ]
election_timeout_millis: 1000
heartbeat_timeout_secs: 1
- name: Get replicaset info
command: mongo admin --eval "rs.status()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset2 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset3 }}"
- set_fact:
mongod_auth: false
- name: Launch mongod processes
include_tasks: mongod_replicaset.yml
# Test with csv string
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset3 }}"
members: "localhost:3001,localhost:3002,localhost:3003"
election_timeout_millis: 1000
- name: Get replicaset info
command: mongo admin --eval "rs.status()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset3 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset4 }}"
- set_fact:
mongod_auth: false
- name: Launch mongod processes
include_tasks: mongod_replicaset.yml
# Test with arbiter_at_index
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
arbiter_at_index: 2
replica_set: "{{ mongodb_replicaset4 }}"
members: "localhost:3001,localhost:3002,localhost:3003"
election_timeout_millis: 1000
- name: Ensure host reaches primary before proceeding 3001
command: mongo admin --port 3001 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Get replicaset info
command: mongo admin --eval "rs.status()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset4 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- "'ARBITER' in mongo_output.stdout"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset5 }}"
- set_fact:
mongod_auth: false
- name: Launch mongod processes
include_tasks: mongod_replicaset.yml
# Test with chainingAllowed
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
chaining_allowed: no
replica_set: "{{ mongodb_replicaset5 }}"
election_timeout_millis: 1000
members:
- localhost:3001
- localhost:3002
- localhost:3003
- name: Get replicaset info
command: mongo admin --eval "rs.conf()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset5 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- "'chainingAllowed\" : false,' in mongo_output.stdout"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset6 }}"
- set_fact:
mongodb_nodes: [ 3001, 3002, 3003, 3004, 3005]
- set_fact:
mongod_auth: false
- name: Launch mongod processes
include_tasks: mongod_replicaset.yml
# Test with 5 mongod processes
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset6 }}"
election_timeout_millis: 1000
members:
- localhost:3001
- localhost:3002
- localhost:3003
- localhost:3004
- localhost:3005
- name: Get replicaset info
command: mongo admin --eval "rs.conf()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset6 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- "'localhost:3004' in mongo_output.stdout"
- "'localhost:3005' in mongo_output.stdout"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset7 }}"
- set_fact:
mongod_auth: false
- set_fact:
mongodb_nodes: [ 3001, 3002, 3003 ]
- name: Launch mongod processes
include_tasks: mongod_replicaset.yml
# Test withheartbeatTimeoutSecs
- name: Create replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
election_timeout_millis: 9999
replica_set: "{{ mongodb_replicaset7 }}"
members:
- localhost:3001
- localhost:3002
- localhost:3003
- name: Get replicaset info
command: mongo admin --eval "rs.conf()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset7 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- "'electionTimeoutMillis\" : 9999,' in mongo_output.stdout"
#############################################################
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset8 }}"
- name: Launch mongod processes
include_tasks: mongod_replicaset.yml
# Test with heartbeatTimeoutSecs
- name: Create replicaset with module protocolVersion 0 (Mongodb 3.0)
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
protocol_version: 0
heartbeat_timeout_secs: 9
replica_set: "{{ mongodb_replicaset8 }}"
election_timeout_millis: 1000
members:
- localhost:3001
- localhost:3002
- localhost:3003
when: mongodb_version.startswith('3') == True
- name: Create replicaset with module protocolVersion 1 (MongoDB 4.0+)
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
protocol_version: 1
election_timeout_millis: 9000
replica_set: "{{ mongodb_replicaset8 }}"
members:
- localhost:3001
- localhost:3002
- localhost:3003
when: mongodb_version.startswith('4') == True
- name: Get replicaset info
command: mongo admin --eval "rs.conf()" --port 3001
register: mongo_output
- name: Assert replicaset name is in mongo_output MongoDB 3.0+
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset8 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- "'heartbeatTimeoutSecs\" : 9,' in mongo_output.stdout"
when: mongodb_version.startswith('3') == True
- name: Assert replicaset name is in mongo_output MongoDB 4.0+
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset8 }}' in mongo_output.stdout"
- "'localhost:3001' in mongo_output.stdout"
- "'localhost:3002' in mongo_output.stdout"
- "'localhost:3003' in mongo_output.stdout"
- "'electionTimeoutMillis\" : 9000,' in mongo_output.stdout"
when: mongodb_version.startswith('4') == True
# TODO - Readd this test once we support serverSelectionTimeoutMS / connectTimeoutMS
#- name: Run test with unknown host
# mongodb_replicaset:
# login_user: admin
# login_password: secret
# login_host: "idonotexist"
# login_port: 3001
# login_database: "admin"
# protocol_version: 0
# heartbeat_timeout_secs: 9
# replica_set: "{{ mongodb_replicaset8 }}"
# election_timeout_millis: 1000
# members:
# - idonotexist:3001
# - idonotexist:3002
# - idonotexist:3003
# ignore_errors: True
# register: host_does_not_exist
#- name: Assert that "Name or service not known" is in error
# assert:
# that:
# - "host_does_not_exist.rc == 1"
# - "'Name or service not known' in host_does_not_exist.module_stderr"
# Final clean up to prevent "directory not empty" error
# Test invalid arbiter handling
# Test with arbiter_at_index
- name: Test invalid arbiter handling 1
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
arbiter_at_index: 0
replica_set: "{{ mongodb_replicaset4 }}"
members:
- localhost:3001
- localhost:3002
- localhost:3003
- localhost:3004
ignore_errors: yes
register: invalid_num
- assert:
that:
- "'MongoDB Replicaset validation failed. Invalid number of replicaset members.' == invalid_num.msg"
- name: Test invalid arbiter handling 2
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
arbiter_at_index: 4
replica_set: "{{ mongodb_replicaset4 }}"
members:
- localhost:3001
- localhost:3002
- localhost:3003
ignore_errors: yes
register: invalid_num2
- assert:
that:
- "'MongoDB Replicaset validation failed. Invalid arbiter index.' == invalid_num2.msg"
- include_tasks: mongod_teardown.yml

@ -1,43 +0,0 @@
- name: Set mongodb_user user for redhat
set_fact:
mongodb_user: "mongod"
when: ansible_os_family == "RedHat"
- name: Create directories for mongod processes
file:
path: "{{ remote_tmp_dir }}/mongod{{ item }}"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
recurse: yes
with_items: "{{ mongodb_nodes }}"
- name: Create keyfile
copy:
dest: "{{ remote_tmp_dir }}/my.key"
content: |
fd2CUrbXBJpB4rt74A6F
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0600
when: mongod_auth == True
- name: Spawn mongod process without auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --replSet {{ current_replicaset }} --logpath mongod{{ item }}/log.log --fork
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == False
- name: Spawn mongod process with auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --replSet {{ current_replicaset }} --logpath mongod{{ item }}/log.log --fork --auth --keyFile my.key
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == True
- name: Wait for mongod to start responding
wait_for:
port: "{{ item }}"
with_items: "{{ mongodb_nodes }}"

@ -1,27 +0,0 @@
- name: Kill all mongod processes
command: pkill -{{ kill_signal }} mongod
ignore_errors: true
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
delay: 1
with_items: "{{ pids_of_mongod }}"
- name: Remove all mongod folders
file:
path: "{{ remote_tmp_dir }}/{{ item }}"
state: absent
with_items:
- mongod3001
- mongod3002
- mongod3003
- name: Remove all mongod sock files
shell: rm -Rf /tmp/mongodb*.sock

@ -1,7 +0,0 @@
destructive
shippable/posix/group1
skip/aix
skip/osx
skip/freebsd
skip/rhel
needs/root

@ -1,16 +0,0 @@
---
# defaults file for test_mongodb_shard
mongodb_replicaset1: rs1
mongodb_replicaset2: rs2
configsrv_replicaset: cfg
test_mongo_auth: yes
mongodb_admin_user: test_root
mongodb_admin_password: saE_Rr9!gE6gh#e~R#nZ
debug: yes
mongod_auth: false
kill_signal: SIGTERM
# Should be one of
# --storageEngine wiredTiger --wiredTigerEngineConfigString="cache_size=200M"
# --storageEngine mmapv1 --nojournal
mongod_storage_engine_opts: "--storageEngine wiredTiger --wiredTigerEngineConfigString='cache_size=200M'"
mongodb_user: mongodb

@ -1,38 +0,0 @@
#!/usr/bin/env bash
# This script is here because on some occassions the head mongodb does not become primary.
PRIMARY=$1;
AUTH=$2;
if [ "$AUTH" == "1" ]; then
if [ "$PRIMARY" == "3001" ]; then
mongo admin --port 3002 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.freeze(120)";
mongo admin --port 3003 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.freeze(120)";
mongo admin --port 3002 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.stepDown()";
mongo admin --port 3003 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.stepDown()";
elif [ "$PRIMARY" == "3004" ]; then
mongo admin --port 3005 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.freeze(120)";
mongo admin --port 3006 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.freeze(120)";
mongo admin --port 3005 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.stepDown()";
mongo admin --port 3006 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} --eval "rs.stepDown()";
else
echo "Unsupported port!";
exit 1;
fi;
else
if [ "$PRIMARY" == "3001" ]; then
mongo admin --port 3002 --eval "rs.freeze(120)";
mongo admin --port 3003 --eval "rs.freeze(120)";
mongo admin --port 3002 --eval "rs.stepDown()";
mongo admin --port 3003 --eval "rs.stepDown()";
elif [ "$PRIMARY" == "3004" ]; then
mongo admin --port 3005 --eval "rs.freeze(120)";
mongo admin --port 3006 --eval "rs.freeze(120)";
mongo admin --port 3005 --eval "rs.stepDown()";
mongo admin --port 3006 --eval "rs.stepDown()";
else
echo "Unsupported port!";
exit 1;
fi;
fi;

@ -1,13 +0,0 @@
var done = false;
var iterations = 0;
while(rs.status()['myState'] != 1) {
if (!done) {
//print("State is not yet PRIMARY. Waiting...");
done = true
}
sleep(1000);
iterations++;
if (iterations == 100) {
throw new Error("Exceeded iterations limit.");
}
}

@ -1,3 +0,0 @@
dependencies:
- setup_mongodb
- setup_remote_tmp_dir

@ -1,458 +0,0 @@
# test code for the mongodb_shard module
# (c) 2019, Rhys Campbell <rhys.james.campbell@googlemail.com>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# ============================================================
- name: Ensure tests home exists
file:
path: "{{ remote_tmp_dir }}/tests"
state: directory
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset1 }}"
- set_fact:
mongodb_nodes: [ 3001, 3002, 3003 ]
- include_tasks: mongod_replicaset.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset2 }}"
- set_fact:
mongodb_nodes: [ 3004, 3005, 3006 ]
- include_tasks: mongod_replicaset.yml
- name: Launch cfg server
command: mongod --configsvr --port 4000 --dbpath {{ remote_tmp_dir }}/config --logpath {{ remote_tmp_dir }}/config.log --smallfiles --replSet "{{ configsrv_replicaset }}" --fork
- name: Create replicaset1 with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset1 }}"
members:
- "localhost:3001"
- "localhost:3002"
- "localhost:3003"
- name: Create replicaset2 with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3004
login_database: "admin"
replica_set: "{{ mongodb_replicaset2 }}"
members:
- "localhost:3004"
- "localhost:3005"
- "localhost:3006"
- name: Create config srv replicaset with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 4000
login_database: "admin"
replica_set: "{{ configsrv_replicaset }}"
validate: no
members:
- "localhost:4000"
- name: Get config server replset mongo_output
command: mongo admin --port 4000 --eval "rs.status();"
register: cfg_replset_output
- name: Assert that replset is a config server
assert:
that:
- "'\"configsvr\" : true' in cfg_replset_output.stdout"
- "'\"set\" : \"{{ configsrv_replicaset }}\"' in cfg_replset_output.stdout"
- name: Launch mongos
command: mongos --configdb "{{ configsrv_replicaset }}/localhost:4000" --logpath "{{ remote_tmp_dir }}/tests/mongos.log" --port 27017 --fork
- name: Ensure is_primary script exists on host
copy:
src: js/is_primary.js
dest: "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Ensure host reaches primary before proceeding 3001
command: mongo admin --port 3001 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Ensure host reaches primary before proceeding 3004
command: mongo admin --port 3004 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Add shard 1
mongodb_shard:
login_user: admin
login_password: admin
shard: "{{ mongodb_replicaset1 }}/localhost:3001"
state: present
- name: Add shard 2
mongodb_shard:
login_user: admin
login_password: admin
shard: "{{ mongodb_replicaset2 }}/localhost:3004"
state: present
- name: Get replicaset info
command: mongo admin --eval "sh.status()" --port 27017
register: mongo_output
- name: Assert shard name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
- "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' in mongo_output.stdout"
- "'balancer' in mongo_output.stdout"
- name: Remove shard 2
mongodb_shard:
login_user: admin
login_password: admin
shard: "{{ mongodb_replicaset2 }}"
state: absent
- name: Get replicaset info
command: mongo admin --eval "sh.status()" --port 27017
register: mongo_output
- name: Assert shard 2 is draining
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
- "'\"draining\" : true' in mongo_output.stdout"
- "'balancer' in mongo_output.stdout"
- name: Run remove command again to finalize shard removal
mongodb_shard:
login_user: admin
login_password: admin
shard: "{{ mongodb_replicaset2 }}"
state: absent
- name: Get replicaset info
command: mongo admin --eval "sh.status()" --port 27017
register: mongo_output
- name: Assert shard 2 is not present
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
- "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' not in mongo_output.stdout"
- "'balancer' in mongo_output.stdout"
# Repeat of above with auth enabled
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset1 }}"
- set_fact:
mongodb_nodes: [ 3001, 3002, 3003 ]
- include_tasks: mongod_replicaset.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset2 }}"
- set_fact:
mongodb_nodes: [ 3004, 3005, 3006 ]
- include_tasks: mongod_replicaset.yml
- name: Create replicaset1 with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3001
login_database: "admin"
replica_set: "{{ mongodb_replicaset1 }}"
members:
- "localhost:3001"
- "localhost:3002"
- "localhost:3003"
- name: Create replicaset2 with module
mongodb_replicaset:
login_user: admin
login_password: secret
login_host: "localhost"
login_port: 3004
login_database: "admin"
replica_set: "{{ mongodb_replicaset2 }}"
members:
- "localhost:3004"
- "localhost:3005"
- "localhost:3006"
- name: Launch cfg server
command: mongod --configsvr --port 4000 --dbpath {{ remote_tmp_dir }}/config --logpath {{ remote_tmp_dir }}/config.log --smallfiles --replSet "{{ configsrv_replicaset }}" --fork
- name: Create config srv replicaset with module
mongodb_replicaset:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_port: 4000
login_database: "admin"
replica_set: "{{ configsrv_replicaset }}"
validate: no
members:
- "localhost:4000"
- name: Ensure host reaches primary before proceeding 3001
command: mongo admin --port 3001 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Ensure host reaches primary before proceeding 3004
command: mongo admin --port 3004 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Ensure host reaches primary before proceeding 4000
command: mongo admin --port 4000 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Add mongodb admin user to each shard 3.4+
mongodb_user:
login_host: localhost
login_port: "{{ item.port }}"
replica_set: "{{ item.rs }}"
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: ["root"]
state: present
register: mongo_admin_user
with_items:
- { "port": 3001, "rs": "{{ mongodb_replicaset1 }}" }
- { "port": 3004, "rs": "{{ mongodb_replicaset2 }}" }
- { "port": 4000, "rs": "{{ configsrv_replicaset }}" }
when: mongodb_version not in ["3.2", "4.0"] and test_mongo_auth == True
- name: Add mongodb admin user to each shard 3.2 ^ 4.0
mongodb_user:
login_host: localhost
login_port: "{{ item.port }}"
replica_set: "{{ item.rs }}"
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: ["root"]
state: present
register: mongo_admin_user
with_items:
- { "port": 3001, "rs": "{{ mongodb_replicaset1 }}" }
- { "port": 3004, "rs": "{{ mongodb_replicaset2 }}" }
when: mongodb_version not in ["3.2", "4.0"] and test_mongo_auth == True
# mongodb_user throws an error when creating a user on 3.2 (also on 4.0 with Ubuntu 18.04)
# 'majority' is the only valid write concern when writing to config server replica sets
- name: Copy create_user_root_3.2.js.j2 template to host
template:
src: create_user_root_3.2.js.j2
dest: /root/create_user_root_3.2.js
when: mongodb_version in ["3.2", "4.0"]
- name: Copy script to host
template:
src: files/bash/ensure_primary.sh.j2
dest: /root/ensure_primary.sh
- name: Execute script for 3001
script: /root/ensure_primary.sh 3001 0
# We do this here because sometimes 3004 instance seems to be demoted
- name: Execute script for 3004
script: /root/ensure_primary.sh 3004 0
- name: Create admin user on 3.2 and 4.0 config replset
shell: mongo admin --port {{ item }} /root/create_user_root_3.2.js
with_items:
- 4000
- 3001
- 3004
when: mongodb_version in ["3.2", "4.0"]
- name: Murder all mongod processes
shell: pkill -{{ kill_signal }} mongod || true;
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
with_items: "{{ pids_of_mongod }}"
- set_fact:
current_replicaset: "{{ mongodb_replicaset1 }}"
- set_fact:
mongodb_nodes: [ 3001, 3002, 3003 ]
- set_fact:
mongod_auth: true
- include_tasks: mongod_replicaset.yml
- set_fact:
current_replicaset: "{{ mongodb_replicaset2 }}"
- set_fact:
mongodb_nodes: [ 3004, 3005, 3006 ]
- set_fact:
mongod_auth: true
- include_tasks: mongod_replicaset.yml
- name: Launch cfg server with auth
command: mongod --configsvr --port 4000 --dbpath {{ remote_tmp_dir }}/config --logpath {{ remote_tmp_dir }}/config.log --smallfiles --replSet "{{ configsrv_replicaset }}" --fork --auth --keyFile {{ remote_tmp_dir }}/my.key
- name: Execute script for 3001
script: /root/ensure_primary.sh 3001 1
- name: Execute script for 3004
script: /root/ensure_primary.sh 3004 1
- name: Ensure host reaches primary before proceeding 3001
command: mongo admin --port 3001 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Ensure host reaches primary before proceeding 3004
command: mongo admin --port 3004 -u {{ mongodb_admin_user }} -p {{ mongodb_admin_password }} "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Launch mongos
command: mongos --configdb "{{ configsrv_replicaset }}/localhost:4000" --logpath "{{ remote_tmp_dir }}/mongos.log" --port 27017 --fork --keyFile {{ remote_tmp_dir }}/my.key
- name: Wait for mongos to become active
wait_for:
host: localhost
port: 4000
delay: 1
- name: Add shard 1
mongodb_shard:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
shard: "{{ mongodb_replicaset1 }}/localhost:3001"
state: present
- name: Add shard 2
mongodb_shard:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
shard: "{{ mongodb_replicaset2 }}/localhost:3004"
state: present
- name: Test with bad password
mongodb_shard:
login_user: "{{ mongodb_admin_user }}"
login_password: XXXXXXXXXXXX
shard: "{{ mongodb_replicaset2 }}/localhost:3004"
state: present
register: mongodb_shard_bad_pw
ignore_errors: True
- name: Assert login failed
assert:
that:
- "mongodb_shard_bad_pw.changed == False"
- "'unable to connect to database: Authentication failed.' == mongodb_shard_bad_pw.msg"
- name: Get replicaset info
command: mongo admin --eval "sh.status()" --port 27017 -u "{{ mongodb_admin_user }}" -p "{{ mongodb_admin_password }}"
register: mongo_output
- name: Assert shard name is in mongo_output
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
- "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' in mongo_output.stdout"
- "'balancer' in mongo_output.stdout"
- name: Remove shard 2
mongodb_shard:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
shard: "{{ mongodb_replicaset2 }}"
state: absent
- name: Get replicaset info
command: mongo admin --eval "sh.status()" --port 27017 -u "{{ mongodb_admin_user }}" -p "{{ mongodb_admin_password }}"
register: mongo_output
- name: Assert shard 2 is draining
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
- "'\"draining\" : true' in mongo_output.stdout"
- "'balancer' in mongo_output.stdout"
- name: Run remove command again to finalize shard removal
mongodb_shard:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
shard: "{{ mongodb_replicaset2 }}"
state: absent
- name: Get replicaset info
command: mongo admin --eval "sh.status()" --port 27017 -u "{{ mongodb_admin_user }}" -p "{{ mongodb_admin_password }}"
register: mongo_output
- name: Assert shard 2 is not present
assert:
that:
- "mongo_output.changed == true"
- "'{{ mongodb_replicaset1 }}/localhost:3001,localhost:3002,localhost:3003' in mongo_output.stdout"
- "'{{ mongodb_replicaset2 }}/localhost:3004,localhost:3005,localhost:3006' not in mongo_output.stdout"
- "'balancer' in mongo_output.stdout"
# TODO - Readd this test once we support serverSelectionTimeoutMS / connectTimeoutMS
#- name: Run test with unknown host
# mongodb_shard:
# login_user: "{{ mongodb_admin_user }}"
# login_password: "{{ mongodb_admin_password }}"
# login_host: "idonotexist"
# shard: "{{ mongodb_replicaset2 }}"
# state: absent
# ignore_errors: True
# register: host_does_not_exist
#- name: Assert that "Name or service not known" is in error
# assert:
# that:
# - "host_does_not_exist.changed == False"
# - "'unable to connect to database: idonotexist:27017: [Errno -2] Name or service not known' == host_does_not_exist.msg"
# Final clean up to prevent "directory not empty" error
- include_tasks: mongod_teardown.yml

@ -1,51 +0,0 @@
- name: Set mongodb_user user for redhat
set_fact:
mongodb_user: "mongod"
when: ansible_os_family == "RedHat"
- name: Create directories for mongod processes
file:
path: "{{ remote_tmp_dir }}/mongod{{ item }}"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
recurse: yes
with_items: "{{ mongodb_nodes }}"
- name: Ensure {{ remote_tmp_dir }}/config dir exists
file:
path: "{{ remote_tmp_dir }}/config"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
- name: Create keyfile
copy:
dest: "{{ remote_tmp_dir }}/my.key"
content: |
fd2CUrbXBJpB4rt74A6F
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0600
when: mongod_auth == True
- name: Spawn mongod process without auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --replSet {{ current_replicaset }} --logpath mongod{{ item }}/log.log --fork
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == False
- name: Spawn mongod process with auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --replSet {{ current_replicaset }} --logpath mongod{{ item }}/log.log --fork --auth --keyFile my.key
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == True
- name: Wait for mongod to start responding
wait_for:
port: "{{ item }}"
with_items: "{{ mongodb_nodes }}"

@ -1,35 +0,0 @@
- name: Kill all mongod processes
command: pkill -{{ kill_signal }} mongod
ignore_errors: true
- name: Kill all mongos processes
command: pkill -{{ kill_signal }} mongos
ignore_errors: true
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
delay: 1
with_items: "{{ pids_of_mongod }}"
- name: Remove all mongod folders
file:
path: "{{ remote_tmp_dir }}/{{ item }}"
state: absent
with_items:
- mongod3001
- mongod3002
- mongod3003
- mongod3004
- mongod3005
- mongod3006
- config
- name: Remove all mongod sock files
shell: rm -Rf /tmp/mongodb*.sock

@ -1,8 +0,0 @@
db.createUser(
{
user: "{{ mongodb_admin_user }}",
pwd: "{{ mongodb_admin_password }}",
roles: [ { role: "root", db: "admin" } ]
},
{ w: "majority", j: true, wtimeout: 10000 }
)

@ -1,8 +0,0 @@
destructive
shippable/posix/group1
skip/aix
skip/osx
skip/freebsd
skip/rhel
needs/root
disabled

@ -1,21 +0,0 @@
---
# defaults file for test_mongodb_user
mongodb_admin_user: test_root
mongodb_admin_password: saE_Rr9!gE6gh#e~R#nZ
mongod_auth: false
kill_signal: SIGTERM
# Should be one of
# --storageEngine wiredTiger --wiredTigerEngineConfigString="cache_size=200M"
# --storageEngine mmapv1 --nojournal
mongod_storage_engine_opts: "--storageEngine wiredTiger --wiredTigerEngineConfigString='cache_size=200M'"
mongodb_user: mongodb
mongodb_user_list:
- { "name": "user1", "password": "password1", "roles": "read", "database": "test" }
- { "name": "user2", "password": "password2", "roles": "readWrite", "database": "test" }
- { "name": "user3", "password": "password3", "roles": "dbAdmin", "database": "test" }
- { "name": "user4", "password": "password4", "roles": "userAdmin", "database": "test" }
- { "name": "user5", "password": "password5", "roles": "clusterAdmin", "database": "admin" }
- { "name": "user6", "password": "password6", "roles": "readAnyDatabase", "database": "admin" }
- { "name": "user7", "password": "password7", "roles": "readWriteAnyDatabase", "database": "admin" }
- { "name": "user8", "password": "password8", "roles": "userAdminAnyDatabase", "database": "admin" }
- { "name": "user9", "password": "password9", "roles": "dbAdminAnyDatabase", "database": "admin" }

@ -1,13 +0,0 @@
var done = false;
var iterations = 0;
while(rs.status()['myState'] != 1) {
if (!done) {
//print("State is not yet PRIMARY. Waiting...");
done = true
}
sleep(1000);
iterations++;
if (iterations == 100) {
throw new Error("Exceeded iterations limit.");
}
}

@ -1,3 +0,0 @@
dependencies:
- setup_mongodb
- setup_remote_tmp_dir

@ -1,226 +0,0 @@
# test code for the mongodb_user module
# (c) 2019, Rhys Campbell <rhys.james.campbell@googlemail.com>
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# ============================================================
- name: Ensure tests home exists
file:
path: "{{ remote_tmp_dir }}/tests"
state: directory
- include_tasks: mongod_teardown.yml
- set_fact:
current_replicaset: mongodb_user_tests_replicaset
- set_fact:
mongodb_nodes: [ 3001, 3002, 3003 ]
- include_tasks: mongod_replicaset.yml
- name: Create current_replicaset with module
mongodb_replicaset:
login_port: 3001
replica_set: "{{ current_replicaset }}"
members:
- "localhost:3001"
- "localhost:3002"
- "localhost:3003"
- name: Ensure is_primary script exists on host
copy:
src: js/is_primary.js
dest: "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Ensure host reaches primary before proceeding 3001
command: mongo admin --port 3001 "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Create admin user with module
mongodb_user:
login_port: 3001
replica_set: "{{ current_replicaset }}"
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: root
state: present
register: mongodb_admin_user_created
- assert:
that:
- mongodb_admin_user_created.changed == True
- name: Kill all mongod processes
command: pkill -{{ kill_signal }} mongod
ignore_errors: true
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
delay: 3
with_items: "{{ pids_of_mongod }}"
- set_fact:
mongod_auth: true
- include_tasks: mongod_replicaset.yml
# Tests with auth enable
- name: Ensure host reaches primary before proceeding 3001
command: mongo admin --port 3001 --username "{{ mongodb_admin_user }}" --password "{{ mongodb_admin_password }}" "{{ remote_tmp_dir }}/tests/is_primary.js"
- name: Run admin user creation again
mongodb_user:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_database: admin
login_port: 3001
replica_set: "{{ current_replicaset }}"
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: root
state: present
update_password: on_create
register: mongodb_admin_user_created
- assert:
that:
- mongodb_admin_user_created.changed == False
- name: Run admin user creation again with forced pw update
mongodb_user:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_database: admin
login_port: 3001
replica_set: "{{ current_replicaset }}"
database: admin
name: "{{ mongodb_admin_user }}"
password: "{{ mongodb_admin_password }}"
roles: root
state: present
update_password: always
register: mongodb_admin_user_created
- assert:
that:
- mongodb_admin_user_created.changed == True
- name: Run user creation
mongodb_user:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_database: admin
login_port: 3001
replica_set: "{{ current_replicaset }}"
database: "{{ item.database }}"
name: "{{ item.name }}"
password: "{{ item.password }}"
roles: "{{ item.roles }}"
state: present
with_items: "{{ mongodb_user_list }}"
- name: Test login for all users
shell: mongo "{{ item.database }}" --port 3001 --username "{{ item.name }}" --password "{{ item.password }}" --eval "printjson(db.getCollectionNames())"
with_items: "{{ mongodb_user_list }}"
register: login_test
- name: Assert all logins successful
assert:
that: "{{ item.rc == 0 }}"
with_items: "{{ login_test.results }}"
- name: Get admin db users
shell: mongo admin --port 3001 --username "{{ mongodb_admin_user }}" --password "{{ mongodb_admin_password }}" --eval "printjson(db.getUsers())"
register: admin_db_users
- name: Assert that roles exist in admin db output
assert:
that:
- "'clusterAdmin' in admin_db_users.stdout"
- "'readAnyDatabase' in admin_db_users.stdout"
- "'readWriteAnyDatabase' in admin_db_users.stdout"
- "'userAdminAnyDatabase' in admin_db_users.stdout"
- "'dbAdminAnyDatabase' in admin_db_users.stdout"
- name: Get test db users
shell: mongo test --authenticationDatabase admin --port 3001 --username "{{ mongodb_admin_user }}" --password "{{ mongodb_admin_password }}" --eval "printjson(db.getUsers())"
register: test_db_users
- name: Assert that roles exist in test db output
assert:
that:
- "'\"read\"' in test_db_users.stdout"
- "'readWrite' in test_db_users.stdout"
- "'dbAdmin' in test_db_users.stdout"
- "'userAdmin' in test_db_users.stdout"
- name: Drop users in test db
mongodb_user:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_database: admin
login_port: 3001
replica_set: "{{ current_replicaset }}"
database: "{{ item.database }}"
name: "{{ item.name }}"
state: absent
with_items: "{{ mongodb_user_list }}"
when: item.database == "test"
- name: Get test db users
shell: mongo test --authenticationDatabase admin --port 3001 --username "{{ mongodb_admin_user }}" --password "{{ mongodb_admin_password }}" --eval "printjson(db.getUsers())"
register: test_db_users
- name: Assert that roles do not exist in test db output
assert:
that:
- "'user1' not in test_db_users.stdout"
- "'user2' not in test_db_users.stdout"
- "'user3' not in test_db_users.stdout"
- "'user4' not in test_db_users.stdout"
- name: Create user with multiple roles in test db
mongodb_user:
login_user: "{{ mongodb_admin_user }}"
login_password: "{{ mongodb_admin_password }}"
login_database: admin
login_port: 3001
replica_set: "{{ current_replicaset }}"
database: test
name: test_multiple_roles
password: secret
roles: readWrite,dbAdmin,userAdmin
state: present
- name: Get test db users
shell: mongo test --authenticationDatabase admin --port 3001 --username "{{ mongodb_admin_user }}" --password "{{ mongodb_admin_password }}" --eval "printjson(db.getUsers())"
register: test_db_users
- debug:
var: test_db_users
# Clean up
- include_tasks: mongod_teardown.yml

@ -1,51 +0,0 @@
- name: Set mongodb_user user for redhat
set_fact:
mongodb_user: "mongod"
when: ansible_os_family == "RedHat"
- name: Create directories for mongod processes
file:
path: "{{ remote_tmp_dir }}/mongod{{ item }}"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
recurse: yes
with_items: "{{ mongodb_nodes }}"
- name: Ensure {{ remote_tmp_dir }}/config dir exists
file:
path: "{{ remote_tmp_dir }}/config"
state: directory
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0755
- name: Create keyfile
copy:
dest: "{{ remote_tmp_dir }}/my.key"
content: |
fd2CUrbXBJpB4rt74A6F
owner: "{{ mongodb_user }}"
group: "{{ mongodb_user }}"
mode: 0600
when: mongod_auth == True
- name: Spawn mongod process without auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --replSet {{ current_replicaset }} --logpath mongod{{ item }}/log.log --fork
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == False
- name: Spawn mongod process with auth
command: mongod --shardsvr --smallfiles {{ mongod_storage_engine_opts }} --dbpath mongod{{ item }} --port {{ item }} --replSet {{ current_replicaset }} --logpath mongod{{ item }}/log.log --fork --auth --keyFile my.key
args:
chdir: "{{ remote_tmp_dir }}"
with_items: "{{ mongodb_nodes | sort }}"
when: mongod_auth == True
- name: Wait for mongod to start responding
wait_for:
port: "{{ item }}"
with_items: "{{ mongodb_nodes }}"

@ -1,27 +0,0 @@
- name: Kill all mongod processes
command: pkill -{{ kill_signal }} mongod
ignore_errors: true
- name: Getting pids for mongod
pids:
name: mongod
register: pids_of_mongod
- name: Wait for all mongod processes to exit
wait_for:
path: "/proc/{{ item }}/status"
state: absent
delay: 1
with_items: "{{ pids_of_mongod }}"
- name: Remove all mongod folders
file:
path: "{{ remote_tmp_dir }}/{{ item }}"
state: absent
with_items:
- mongod3001
- mongod3002
- mongod3003
- name: Remove all mongod sock files
shell: rm -Rf /tmp/mongodb*.sock

@ -1679,10 +1679,6 @@ lib/ansible/modules/commands/command.py validate-modules:nonexistent-parameter-d
lib/ansible/modules/commands/command.py validate-modules:parameter-list-no-elements
lib/ansible/modules/commands/command.py validate-modules:undocumented-parameter
lib/ansible/modules/commands/expect.py validate-modules:doc-missing-type
lib/ansible/modules/database/mongodb/mongodb_parameter.py use-argspec-type-path
lib/ansible/modules/database/mongodb/mongodb_replicaset.py use-argspec-type-path
lib/ansible/modules/database/mongodb/mongodb_shard.py use-argspec-type-path
lib/ansible/modules/database/mongodb/mongodb_user.py use-argspec-type-path
lib/ansible/modules/files/acl.py validate-modules:parameter-type-not-in-doc
lib/ansible/modules/files/assemble.py validate-modules:nonexistent-parameter-documented
lib/ansible/modules/files/blockinfile.py validate-modules:doc-choices-do-not-match-spec

Loading…
Cancel
Save