1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
#!/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
DOCUMENTATION = r'''
---
module: mongodb_info
short_description: Gather information about MongoDB instance.
description:
- Gather information about MongoDB instance.
author: Andrew Klychkov (@Andersson007)
version_added: "1.0.0"
extends_documentation_fragment:
- community.mongodb.login_options
- community.mongodb.ssl_options
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
notes:
- Requires the pymongo Python package on the remote host, version 2.4.2+.
requirements:
- pymongo
'''
EXAMPLES = r'''
- name: Gather all supported information
community.mongodb.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
community.mongodb.mongodb_info:
login_user: admin
login_password: secret
filter: databases, total_size
- name: Gather all information except parameters
community.mongodb.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: { "db": {"new_user": {"_id": "config.new_user", "mechanisms": ["SCRAM-SHA-1", "SCRAM-SHA-256"], "roles": []}}}
roles:
description: Role information.
returned: always
type: dict
sample: { "db": {"restore": {"inheritedRoles": [], "isBuiltin": true, "roles": []}}}
parameters:
description: Server parameters information.
returned: always
type: dict
sample: {"maxOplogTruncationPointsAfterStartup": 100, "maxOplogTruncationPointsDuringStartup": 100, "maxSessions": 1000000}
'''
from uuid import UUID
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils._text import to_native
from ansible.module_utils.six import iteritems
from ansible_collections.community.mongodb.plugins.module_utils.mongodb_common import (
convert_bson_values_recur,
get_mongodb_client,
missing_required_lib,
mongodb_common_argument_spec,
mongo_auth,
PYMONGO_IMP_ERR,
pymongo_found,
)
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))
self.info = convert_bson_values_recur(self.info)
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 for the given db.
"""
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 in ['role', 'db']:
continue
roles_dict[elem['role']][key] = val
return {dbname: 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 for the given db.
"""
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 in ['user', 'db']:
continue
if isinstance(val, UUID):
val = val.hex
users_dict[elem['user']][key] = val
return {dbname: 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 = mongodb_common_argument_spec()
argument_spec.update(
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)
filter_ = module.params['filter']
if filter_:
filter_ = [f.strip() for f in filter_]
try:
client = get_mongodb_client(module)
client = mongo_auth(module, client)
except Exception as excep:
module.fail_json(msg='Unable to connect to MongoDB: %s' % to_native(excep))
# Initialize an object and start main work:
mongodb = MongoDbInfo(module, client)
module.exit_json(changed=False, **mongodb.get_info(filter_))
if __name__ == '__main__':
main()
|