diff options
| author | Michael DeHaan <michael@ansible.com> | 2014-11-05 16:11:23 -0500 | 
|---|---|---|
| committer | Michael DeHaan <michael@ansible.com> | 2014-11-05 16:11:23 -0500 | 
| commit | ad181b7aa949848e3085065e09195cb28c34fdf7 (patch) | |
| tree | c30340674fd7cddc21ee3e9bacacb5315fc8e433 /database/mysql/mysql_replication.py | |
| parent | 7e6fc7023d956d4c33d8596662e01f2678d35f58 (diff) | |
| download | ansible-modules-extras-ad181b7aa949848e3085065e09195cb28c34fdf7.tar.gz | |
Categorize some modules.
Diffstat (limited to 'database/mysql/mysql_replication.py')
| -rw-r--r-- | database/mysql/mysql_replication.py | 369 | 
1 files changed, 369 insertions, 0 deletions
| diff --git a/database/mysql/mysql_replication.py b/database/mysql/mysql_replication.py new file mode 100644 index 00000000..1d61436a --- /dev/null +++ b/database/mysql/mysql_replication.py @@ -0,0 +1,369 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" + +Ansible module to manage mysql replication +(c) 2013, Balazs Pocze <banyek@gawker.com> +Certain parts are taken from Mark Theunissen's mysqldb module + +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: mysql_replication + +short_description: Manage MySQL replication +description: +    - Manages MySQL server replication, slave, master status get and change master host. +version_added: "1.3" +options: +    mode: +        description: +            - module operating mode. Could be getslave (SHOW SLAVE STATUS), getmaster (SHOW MASTER STATUS), changemaster (CHANGE MASTER TO), startslave (START SLAVE), stopslave (STOP SLAVE) +        required: False +        choices: +            - getslave +            - getmaster +            - changemaster +            - stopslave +            - startslave +        default: getslave +    login_user: +        description: +            - username to connect mysql host, if defined login_password also needed. +        required: False +    login_password: +        description: +            - password to connect mysql host, if defined login_user also needed. +        required: False +    login_host: +        description: +            - mysql host to connect +        required: False +    login_unix_socket: +        description: +            - unix socket to connect mysql server +    master_host: +        description: +            - same as mysql variable +    master_user: +        description: +            - same as mysql variable +    master_password: +        description: +            - same as mysql variable +    master_port: +        description: +            - same as mysql variable +    master_connect_retry: +        description: +            - same as mysql variable +    master_log_file: +        description: +            - same as mysql variable +    master_log_pos: +        description: +            - same as mysql variable +    relay_log_file: +        description: +            - same as mysql variable +    relay_log_pos: +        description: +            - same as mysql variable +    master_ssl: +        description: +            - same as mysql variable +        possible values: 0,1 +    master_ssl_ca: +        description: +            - same as mysql variable +    master_ssl_capath: +        description: +            - same as mysql variable +    master_ssl_cert: +        description: +            - same as mysql variable +    master_ssl_key: +        description: +            - same as mysql variable +    master_ssl_cipher: +        description: +            - same as mysql variable + +''' + +EXAMPLES = ''' +# Stop mysql slave thread +- mysql_replication: mode=stopslave + +# Get master binlog file name and binlog position +- mysql_replication: mode=getmaster + +# Change master to master server 192.168.1.1 and use binary log 'mysql-bin.000009' with position 4578 +- mysql_replication: mode=changemaster master_host=192.168.1.1 master_log_file=mysql-bin.000009 master_log_pos=4578 +''' + +import ConfigParser +import os +import warnings + +try: +    import MySQLdb +except ImportError: +    mysqldb_found = False +else: +    mysqldb_found = True + + +def get_master_status(cursor): +    cursor.execute("SHOW MASTER STATUS") +    masterstatus = cursor.fetchone() +    return masterstatus + + +def get_slave_status(cursor): +    cursor.execute("SHOW SLAVE STATUS") +    slavestatus = cursor.fetchone() +    return slavestatus + + +def stop_slave(cursor): +    try: +        cursor.execute("STOP SLAVE") +        stopped = True +    except: +        stopped = False +    return stopped + + +def start_slave(cursor): +    try: +        cursor.execute("START SLAVE") +        started = True +    except: +        started = False +    return started + + +def changemaster(cursor, chm): +    SQLPARAM = ",".join(chm) +    cursor.execute("CHANGE MASTER TO " + SQLPARAM) + + +def strip_quotes(s): +    """ Remove surrounding single or double quotes + +    >>> print strip_quotes('hello') +    hello +    >>> print strip_quotes('"hello"') +    hello +    >>> print strip_quotes("'hello'") +    hello +    >>> print strip_quotes("'hello") +    'hello + +    """ +    single_quote = "'" +    double_quote = '"' + +    if s.startswith(single_quote) and s.endswith(single_quote): +        s = s.strip(single_quote) +    elif s.startswith(double_quote) and s.endswith(double_quote): +        s = s.strip(double_quote) +    return s + + +def config_get(config, section, option): +    """ Calls ConfigParser.get and strips quotes + +    See: http://dev.mysql.com/doc/refman/5.0/en/option-files.html +    """ +    return strip_quotes(config.get(section, option)) + + +def load_mycnf(): +    config = ConfigParser.RawConfigParser() +    mycnf = os.path.expanduser('~/.my.cnf') +    if not os.path.exists(mycnf): +        return False +    try: +        config.readfp(open(mycnf)) +    except (IOError): +        return False +    # We support two forms of passwords in .my.cnf, both pass= and password=, +    # as these are both supported by MySQL. +    try: +        passwd = config_get(config, 'client', 'password') +    except (ConfigParser.NoOptionError): +        try: +            passwd = config_get(config, 'client', 'pass') +        except (ConfigParser.NoOptionError): +            return False + +    # If .my.cnf doesn't specify a user, default to user login name +    try: +        user = config_get(config, 'client', 'user') +    except (ConfigParser.NoOptionError): +        user = getpass.getuser() +    creds = dict(user=user, passwd=passwd) +    return creds + + +def main(): +    module = AnsibleModule( +            argument_spec = dict( +            login_user=dict(default=None), +            login_password=dict(default=None), +            login_host=dict(default="localhost"), +            login_unix_socket=dict(default=None), +            mode=dict(default="getslave", choices=["getmaster", "getslave", "changemaster", "stopslave", "startslave"]), +            master_host=dict(default=None), +            master_user=dict(default=None), +            master_password=dict(default=None), +            master_port=dict(default=None), +            master_connect_retry=dict(default=None), +            master_log_file=dict(default=None), +            master_log_pos=dict(default=None), +            relay_log_file=dict(default=None), +            relay_log_pos=dict(default=None), +            master_ssl=dict(default=False, type='bool'), +            master_ssl_ca=dict(default=None), +            master_ssl_capath=dict(default=None), +            master_ssl_cert=dict(default=None), +            master_ssl_key=dict(default=None), +            master_ssl_cipher=dict(default=None), +        ) +    ) +    user = module.params["login_user"] +    password = module.params["login_password"] +    host = module.params["login_host"] +    mode = module.params["mode"] +    master_host = module.params["master_host"] +    master_user = module.params["master_user"] +    master_password = module.params["master_password"] +    master_port = module.params["master_port"] +    master_connect_retry = module.params["master_connect_retry"] +    master_log_file = module.params["master_log_file"] +    master_log_pos = module.params["master_log_pos"] +    relay_log_file = module.params["relay_log_file"] +    relay_log_pos = module.params["relay_log_pos"] +    master_ssl = module.params["master_ssl"] +    master_ssl_ca = module.params["master_ssl_ca"] +    master_ssl_capath = module.params["master_ssl_capath"] +    master_ssl_cert = module.params["master_ssl_cert"] +    master_ssl_key = module.params["master_ssl_key"] +    master_ssl_cipher = module.params["master_ssl_cipher"] + +    if not mysqldb_found: +        module.fail_json(msg="the python mysqldb module is required") +    else: +        warnings.filterwarnings('error', category=MySQLdb.Warning) + +    # Either the caller passes both a username and password with which to connect to +    # mysql, or they pass neither and allow this module to read the credentials from +    # ~/.my.cnf. +    login_password = module.params["login_password"] +    login_user = module.params["login_user"] +    if login_user is None and login_password is None: +        mycnf_creds = load_mycnf() +        if mycnf_creds is False: +            login_user = "root" +            login_password = "" +        else: +            login_user = mycnf_creds["user"] +            login_password = mycnf_creds["passwd"] +    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: +        if module.params["login_unix_socket"]: +            db_connection = MySQLdb.connect(host=module.params["login_host"], unix_socket=module.params["login_unix_socket"], user=login_user, passwd=login_password) +        else: +            db_connection = MySQLdb.connect(host=module.params["login_host"], user=login_user, passwd=login_password) +    except Exception, e: +        module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials") +    try: +        cursor = db_connection.cursor(cursorclass=MySQLdb.cursors.DictCursor) +    except Exception, e: +        module.fail_json(msg="Trouble getting DictCursor from db_connection: %s" % e) + +    if mode in "getmaster": +        masterstatus = get_master_status(cursor) +        try: +            module.exit_json( **masterstatus ) +        except TypeError: +            module.fail_json(msg="Server is not configured as mysql master") + +    elif mode in "getslave": +        slavestatus = get_slave_status(cursor) +        try: +            module.exit_json( **slavestatus ) +        except TypeError: +            module.fail_json(msg="Server is not configured as mysql slave") + +    elif mode in "changemaster": +        print "Change master" +        chm=[] +        if master_host: +            chm.append("MASTER_HOST='" + master_host + "'") +        if master_user: +            chm.append("MASTER_USER='" + master_user + "'") +        if master_password: +            chm.append("MASTER_PASSWORD='" + master_password + "'") +        if master_port: +            chm.append("MASTER_PORT=" + master_port) +        if master_connect_retry: +            chm.append("MASTER_CONNECT_RETRY='" + master_connect_retry + "'") +        if master_log_file: +            chm.append("MASTER_LOG_FILE='" + master_log_file + "'") +        if master_log_pos: +            chm.append("MASTER_LOG_POS=" + master_log_pos) +        if relay_log_file: +            chm.append("RELAY_LOG_FILE='" + relay_log_file + "'") +        if relay_log_pos: +            chm.append("RELAY_LOG_POS=" + relay_log_pos) +        if master_ssl: +            chm.append("MASTER_SSL=1") +        if master_ssl_ca: +            chm.append("MASTER_SSL_CA='" + master_ssl_ca + "'") +        if master_ssl_capath: +            chm.append("MASTER_SSL_CAPATH='" + master_ssl_capath + "'") +        if master_ssl_cert: +            chm.append("MASTER_SSL_CERT='" + master_ssl_cert + "'") +        if master_ssl_key: +            chm.append("MASTER_SSL_KEY='" + master_ssl_key + "'") +        if master_ssl_cipher: +            chm.append("MASTER_SSL_CIPHER='" + master_ssl_cipher + "'") +        changemaster(cursor,chm) +        module.exit_json(changed=True) +    elif mode in "startslave": +        started = start_slave(cursor) +        if started is True: +            module.exit_json(msg="Slave started ", changed=True) +        else: +            module.exit_json(msg="Slave already started (Or cannot be started)", changed=False) +    elif mode in "stopslave": +        stopped = stop_slave(cursor) +        if stopped is True: +            module.exit_json(msg="Slave stopped", changed=True) +        else: +            module.exit_json(msg="Slave already stopped", changed=False) + +# import module snippets +from ansible.module_utils.basic import * +main() +warnings.simplefilter("ignore")
\ No newline at end of file | 
