redis 10.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#!/usr/bin/python
# -*- coding: utf-8 -*-

# 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/>.

DOCUMENTATION = '''
---
module: redis
short_description: Various redis commands, slave and flush
description:
   - Unified utility to interact with redis instances.
25 26 27
     'slave' sets a redis instance in slave or master mode.
     'flush' flushes all the instance or a specified db.
     'config' (new in 1.6), ensures a configuration setting on an instance.
28 29
version_added: "1.3"
options:
30
    command:
31
        description:
32
            - The selected redis command
33 34
        required: true
        default: null
35
        choices: [ "slave", "flush", "config" ]
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
    login_password:
        description:
            - The password used to authenticate with (usually not used)
        required: false
        default: null
    login_host:
        description:
            - The host running the database
        required: false
        default: localhost
    login_port:
        description:
            - The port to connect to
        required: false
        default: 6379
    master_host:
        description:
53
            - The host of the master instance [slave command]
54 55 56 57
        required: false
        default: null
    master_port:
        description:
58
            - The port of the master instance [slave command]
59 60 61 62
        required: false
        default: null
    slave_mode:
        description:
63
            - the mode of the redis instance [slave command]
64 65 66 67 68
        required: false
        default: slave
        choices: [ "master", "slave" ]
    db:
        description:
69
            - The database to flush (used in db mode) [flush command]
70 71 72 73 74
        required: false
        default: null
    flush_mode:
        description:
            - Type of flush (all the dbs in a redis instance or a specific one)
75
              [flush command]
76 77 78
        required: false
        default: all
        choices: [ "all", "db" ]
79
    name:
80
        version_added: 1.6
81 82 83 84 85
        description:
            - A redis config key.
        required: false
        default: null
    value:
86
        version_added: 1.6
87 88 89 90
        description:
            - A redis config value.
        required: false
        default: null
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105


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 slave of is password protected
     this needs to be in the redis.conf in the masterauth variable

requirements: [ redis ]
author: Xabier Larrakoetxea
'''

EXAMPLES = '''
# Set local redis instance to be slave of melee.island on port 6377
106
- redis: command=slave master_host=melee.island master_port=6377
107 108

# Deactivate slave mode
109
- redis: command=slave slave_mode=master
110 111

# Flush all the redis db
112
- redis: command=flush flush_mode=all
113 114

# Flush only one db in a redis instance
115
- redis: command=flush db=1 flush_mode=db
116 117 118 119 120 121

# Configure local redis to have 10000 max clients
- redis: command=config name=maxclients value=10000

# Configure local redis to have lua time limit of 100 ms
- redis: command=config name=lua-time-limit value=100
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
'''

try:
    import redis
except ImportError:
    redis_found = False
else:
    redis_found = True


# ===========================================
# Redis module specific support methods.
#

def set_slave_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 type(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():
    module = AnsibleModule(
        argument_spec = dict(
168
            command=dict(default=None, choices=['slave', 'flush', 'config']),
169 170 171 172 173 174 175 176
            login_password=dict(default=None),
            login_host=dict(default='localhost'),
            login_port=dict(default='6379'),
            master_host=dict(default=None),
            master_port=dict(default=None),
            slave_mode=dict(default='slave', choices=['master', 'slave']),
            db=dict(default=None),
            flush_mode=dict(default='all', choices=['all', 'db']),
177 178
            name=dict(default=None),
            value=dict(default=None)
179 180 181 182 183 184 185 186 187 188
        ),
        supports_check_mode = True
    )

    if not redis_found:
        module.fail_json(msg="python redis module is required")

    login_password = module.params['login_password']
    login_host = module.params['login_host']
    login_port = int(module.params['login_port'])
189
    command = module.params['command']
190 191

    # Slave Command section -----------
192
    if command == "slave":
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
        master_host = module.params['master_host']
        master_port = module.params['master_port']
        try:
            master_port = int(module.params['master_port'])
        except Exception:
            pass
        mode = module.params['slave_mode']

        #Check if we ahve all the data
        if mode == "slave":  # Only need data if we want to be slave
            if not master_host:
                module.fail_json(
                            msg='In slave mode master host must be provided')

            if not master_port:
                module.fail_json(
                            msg='In slave mode master port must be provided')

        #Connect and check
        r = redis.StrictRedis(host=login_host,
                              port=login_port,
                              password=login_password)
        try:
            r.ping()
        except Exception, e:
            module.fail_json(msg="unable to connect to database: %s" % e)

        #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 == "slave" and\
             info["role"] == "slave" and\
             info["master_host"] == master_host and\
             info["master_port"] == master_port:
            status = {
                'status': mode,
                'master_host': master_host,
                'master_port': master_port,
            }
            module.exit_json(changed=False, mode=status)
        else:
            # Do the stuff
237
            # (Check Check_mode before commands so the commands aren't evaluated
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
            # if not necesary)
            if mode == "slave":
                if module.check_mode or\
                   set_slave_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 slave 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 -----------
259
    elif command == "flush":
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
        try:
            db = int(module.params['db'])
        except Exception:
            db = 0
        mode = module.params['flush_mode']

        #Check if we have all the data
        if mode == "db":
            if type(db) != int:
                module.fail_json(
                            msg="In db mode the db number must be provided")

        #Connect and check
        r = redis.StrictRedis(host=login_host,
                              port=login_port,
                              password=login_password,
                              db=db)
        try:
            r.ping()
        except Exception, e:
            module.fail_json(msg="unable to connect to database: %s" % e)

        # Do the stuff
283
        # (Check Check_mode before commands so the commands aren't evaluated
284 285 286 287 288 289 290 291 292 293 294 295
        # if not necesary)
        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)
296 297 298
    elif command == 'config':
        name = module.params['name']
        value = module.params['value']
299

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
        r = redis.StrictRedis(host=login_host,
                              port=login_port,
                              password=login_password)

        try:
            r.ping()
        except Exception, e:
            module.fail_json(msg="unable to connect to database: %s" % e)

        
        try:
            old_value = r.config_get(name)[name]
        except Exception, e:
            module.fail_json(msg="unable to read config: %s" % e)
        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, e:
                module.fail_json(msg="unable to write config: %s" % e)
            module.exit_json(changed=changed, name=name, value=value)
324
    else:
325
        module.fail_json(msg='A valid command must be provided')
326

327
# import module snippets
328
from ansible.module_utils.basic import *
329
main()