diff options
Diffstat (limited to 'ansible_collections/community/general/plugins/modules/redis.py')
-rw-r--r-- | ansible_collections/community/general/plugins/modules/redis.py | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/ansible_collections/community/general/plugins/modules/redis.py b/ansible_collections/community/general/plugins/modules/redis.py new file mode 100644 index 000000000..1778a067e --- /dev/null +++ b/ansible_collections/community/general/plugins/modules/redis.py @@ -0,0 +1,335 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +--- +module: redis +short_description: Various redis commands, replica and flush +description: + - Unified utility to interact with redis instances. +extends_documentation_fragment: + - community.general.redis + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none +options: + command: + description: + - The selected redis command + - C(config) ensures a configuration setting on an instance. + - C(flush) flushes all the instance or a specified db. + - C(replica) sets a redis instance in replica or master mode. (C(slave) is an alias for C(replica).) + choices: [ config, flush, replica, slave ] + type: str + tls: + default: false + version_added: 4.6.0 + login_user: + version_added: 4.6.0 + validate_certs: + version_added: 4.6.0 + ca_certs: + version_added: 4.6.0 + master_host: + description: + - The host of the master instance [replica command] + type: str + master_port: + description: + - The port of the master instance [replica command] + type: int + replica_mode: + description: + - The mode of the redis instance [replica command] + - C(slave) is an alias for C(replica). + default: replica + choices: [ master, replica, slave ] + type: str + aliases: + - slave_mode + db: + description: + - The database to flush (used in db mode) [flush command] + type: int + flush_mode: + description: + - Type of flush (all the dbs in a redis instance or a specific one) + [flush command] + default: all + choices: [ all, db ] + type: str + name: + description: + - A redis config key. + type: str + value: + description: + - A redis config value. When memory size is needed, it is possible + to specify it in the usal form of 1KB, 2M, 400MB where the base is 1024. + Units are case insensitive i.e. 1m = 1mb = 1M = 1MB. + type: str + +notes: + - Requires the redis-py Python package on the remote host. You can + install it with pip (pip install redis) or with a package manager. + https://github.com/andymccurdy/redis-py + - If the redis master instance we are making replica of is password protected + this needs to be in the redis.conf in the masterauth variable + +seealso: + - module: community.general.redis_info +requirements: [ redis ] +author: "Xabier Larrakoetxea (@slok)" +''' + +EXAMPLES = ''' +- name: Set local redis instance to be a replica of melee.island on port 6377 + community.general.redis: + command: replica + master_host: melee.island + master_port: 6377 + +- name: Deactivate replica mode + community.general.redis: + command: replica + replica_mode: master + +- name: Flush all the redis db + community.general.redis: + command: flush + flush_mode: all + +- name: Flush only one db in a redis instance + community.general.redis: + command: flush + db: 1 + flush_mode: db + +- name: Configure local redis to have 10000 max clients + community.general.redis: + command: config + name: maxclients + value: 10000 + +- name: Configure local redis maxmemory to 4GB + community.general.redis: + command: config + name: maxmemory + value: 4GB + +- name: Configure local redis to have lua time limit of 100 ms + community.general.redis: + command: config + name: lua-time-limit + value: 100 +''' + +import traceback + +REDIS_IMP_ERR = None +try: + import redis +except ImportError: + REDIS_IMP_ERR = traceback.format_exc() + redis_found = False +else: + redis_found = True + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.common.text.formatters import human_to_bytes +from ansible.module_utils.common.text.converters import to_native +from ansible_collections.community.general.plugins.module_utils.redis import ( + fail_imports, redis_auth_argument_spec, redis_auth_params) +import re + + +# Redis module specific support methods. +def set_replica_mode(client, master_host, master_port): + try: + return client.slaveof(master_host, master_port) + except Exception: + return False + + +def set_master_mode(client): + try: + return client.slaveof() + except Exception: + return False + + +def flush(client, db=None): + try: + if not isinstance(db, int): + return client.flushall() + else: + # The passed client has been connected to the database already + return client.flushdb() + except Exception: + return False + + +# Module execution. +def main(): + redis_auth_args = redis_auth_argument_spec(tls_default=False) + module_args = dict( + command=dict(type='str', choices=['config', 'flush', 'replica', 'slave']), + master_host=dict(type='str'), + master_port=dict(type='int'), + replica_mode=dict(type='str', default='replica', choices=['master', 'replica', 'slave'], + aliases=["slave_mode"]), + db=dict(type='int'), + flush_mode=dict(type='str', default='all', choices=['all', 'db']), + name=dict(type='str'), + value=dict(type='str'), + ) + module_args.update(redis_auth_args) + module = AnsibleModule( + argument_spec=module_args, + supports_check_mode=True, + ) + + fail_imports(module, module.params['tls']) + + redis_params = redis_auth_params(module) + + command = module.params['command'] + if command == "slave": + command = "replica" + + # Replica Command section ----------- + if command == "replica": + master_host = module.params['master_host'] + master_port = module.params['master_port'] + mode = module.params['replica_mode'] + if mode == "slave": + mode = "replica" + + # Check if we have all the data + if mode == "replica": # Only need data if we want to be replica + if not master_host: + module.fail_json(msg='In replica mode master host must be provided') + + if not master_port: + module.fail_json(msg='In replica mode master port must be provided') + + # Connect and check + r = redis.StrictRedis(**redis_params) + try: + r.ping() + except Exception as e: + module.fail_json(msg="unable to connect to database: %s" % to_native(e), exception=traceback.format_exc()) + + # Check if we are already in the mode that we want + info = r.info() + if mode == "master" and info["role"] == "master": + module.exit_json(changed=False, mode=mode) + + elif mode == "replica" and info["role"] == "slave" and info["master_host"] == master_host and info["master_port"] == master_port: + status = dict( + status=mode, + master_host=master_host, + master_port=master_port, + ) + module.exit_json(changed=False, mode=status) + else: + # Do the stuff + # (Check Check_mode before commands so the commands aren't evaluated + # if not necessary) + if mode == "replica": + if module.check_mode or set_replica_mode(r, master_host, master_port): + info = r.info() + status = { + 'status': mode, + 'master_host': master_host, + 'master_port': master_port, + } + module.exit_json(changed=True, mode=status) + else: + module.fail_json(msg='Unable to set replica mode') + + else: + if module.check_mode or set_master_mode(r): + module.exit_json(changed=True, mode=mode) + else: + module.fail_json(msg='Unable to set master mode') + + # flush Command section ----------- + elif command == "flush": + db = module.params['db'] + mode = module.params['flush_mode'] + + # Check if we have all the data + if mode == "db": + if db is None: + module.fail_json(msg="In db mode the db number must be provided") + + # Connect and check + r = redis.StrictRedis(db=db, **redis_params) + try: + r.ping() + except Exception as e: + module.fail_json(msg="unable to connect to database: %s" % to_native(e), exception=traceback.format_exc()) + + # Do the stuff + # (Check Check_mode before commands so the commands aren't evaluated + # if not necessary) + if mode == "all": + if module.check_mode or flush(r): + module.exit_json(changed=True, flushed=True) + else: # Flush never fails :) + module.fail_json(msg="Unable to flush all databases") + + else: + if module.check_mode or flush(r, db): + module.exit_json(changed=True, flushed=True, db=db) + else: # Flush never fails :) + module.fail_json(msg="Unable to flush '%d' database" % db) + elif command == 'config': + name = module.params['name'] + + try: # try to parse the value as if it were the memory size + if re.match(r'^\s*(\d*\.?\d*)\s*([A-Za-z]+)?\s*$', module.params['value'].upper()): + value = str(human_to_bytes(module.params['value'].upper())) + else: + value = module.params['value'] + except ValueError: + value = module.params['value'] + + r = redis.StrictRedis(**redis_params) + + try: + r.ping() + except Exception as e: + module.fail_json(msg="unable to connect to database: %s" % to_native(e), exception=traceback.format_exc()) + + try: + old_value = r.config_get(name)[name] + except Exception as e: + module.fail_json(msg="unable to read config: %s" % to_native(e), exception=traceback.format_exc()) + changed = old_value != value + + if module.check_mode or not changed: + module.exit_json(changed=changed, name=name, value=value) + else: + try: + r.config_set(name, value) + except Exception as e: + module.fail_json(msg="unable to write config: %s" % to_native(e), exception=traceback.format_exc()) + module.exit_json(changed=changed, name=name, value=value) + else: + module.fail_json(msg='A valid command must be provided') + + +if __name__ == '__main__': + main() |