summaryrefslogtreecommitdiff
path: root/database/mysql/mysql_replication.py
diff options
context:
space:
mode:
authorMichael DeHaan <michael@ansible.com>2014-11-05 16:11:23 -0500
committerMichael DeHaan <michael@ansible.com>2014-11-05 16:11:23 -0500
commitad181b7aa949848e3085065e09195cb28c34fdf7 (patch)
treec30340674fd7cddc21ee3e9bacacb5315fc8e433 /database/mysql/mysql_replication.py
parent7e6fc7023d956d4c33d8596662e01f2678d35f58 (diff)
downloadansible-modules-extras-ad181b7aa949848e3085065e09195cb28c34fdf7.tar.gz
Categorize some modules.
Diffstat (limited to 'database/mysql/mysql_replication.py')
-rw-r--r--database/mysql/mysql_replication.py369
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