2013-07-30 17:02:07 +02:00
#!/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 .
2013-08-08 00:10:06 +02:00
2013-07-30 17:02:07 +02:00
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
2013-08-08 00:10:06 +02:00
description :
- Manages MySQL server replication , slave , master status get and change master host .
2013-09-06 19:23:41 +02:00
version_added : " 1.3 "
2015-06-16 20:32:39 +02:00
author : " Balazs Pocze (@banyek) "
2013-07-30 17:02:07 +02:00
options :
mode :
description :
2015-09-17 09:52:58 +02:00
- module operating mode . Could be getslave ( SHOW SLAVE STATUS ) , getmaster ( SHOW MASTER STATUS ) , changemaster ( CHANGE MASTER TO ) , startslave ( START SLAVE ) , stopslave ( STOP SLAVE ) , resetslave ( RESET SLAVE ) , resetslaveall ( RESET SLAVE ALL )
2013-07-30 17:02:07 +02:00
required : False
2013-08-08 00:10:06 +02:00
choices :
2013-07-30 17:02:07 +02:00
- getslave
- getmaster
- changemaster
- stopslave
- startslave
2015-09-15 12:57:37 +02:00
- resetslave
- resetslaveall
2013-07-30 17:02:07 +02:00
default : getslave
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
2015-05-13 19:59:24 +02:00
choices : [ 0 , 1 ]
2013-07-30 17:02:07 +02:00
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
2015-04-15 02:35:13 +02:00
master_auto_position :
2015-05-13 19:59:24 +02:00
description :
2014-12-01 15:16:40 +01:00
- does the host uses GTID based replication or not
2015-05-13 19:59:24 +02:00
required : false
default : null
version_added : " 2.0 "
2015-11-17 05:54:57 +01:00
extends_documentation_fragment : mysql
2013-07-30 17:02:07 +02:00
'''
2013-08-08 00:10:06 +02:00
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
2014-09-30 01:49:42 +02:00
# Check slave status using port 3308
- mysql_replication : mode = getslave login_host = ansible . example . com login_port = 3308
2013-08-08 00:10:06 +02:00
'''
2013-07-30 17:02:07 +02:00
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
2015-09-15 12:57:37 +02:00
def reset_slave ( cursor ) :
try :
cursor . execute ( " RESET SLAVE " )
reset = True
except :
reset = False
return reset
def reset_slave_all ( cursor ) :
try :
cursor . execute ( " RESET SLAVE ALL " )
reset = True
except :
reset = False
return reset
2013-07-30 17:02:07 +02:00
def start_slave ( cursor ) :
2013-08-08 00:10:06 +02:00
try :
2013-07-30 17:02:07 +02:00
cursor . execute ( " START SLAVE " )
started = True
except :
started = False
return started
2014-11-25 10:34:25 +01:00
def changemaster ( cursor , chm , chm_params ) :
sql_param = " , " . join ( chm )
query = ' CHANGE MASTER TO %s ' % sql_param
cursor . execute ( query , chm_params )
2013-07-30 17:02:07 +02:00
def main ( ) :
module = AnsibleModule (
argument_spec = dict (
login_user = dict ( default = None ) ,
login_password = dict ( default = None ) ,
login_host = dict ( default = " localhost " ) ,
2015-03-26 19:42:08 +01:00
login_port = dict ( default = 3306 , type = ' int ' ) ,
2013-07-30 17:02:07 +02:00
login_unix_socket = dict ( default = None ) ,
2015-10-07 19:04:37 +02:00
mode = dict ( default = " getslave " , choices = [ " getmaster " , " getslave " , " changemaster " , " stopslave " , " startslave " , " resetslave " , " resetslaveall " ] ) ,
2015-04-15 22:45:26 +02:00
master_auto_position = dict ( default = False , type = ' bool ' ) ,
2013-07-30 17:02:07 +02:00
master_host = dict ( default = None ) ,
master_user = dict ( default = None ) ,
master_password = dict ( default = None ) ,
2015-01-05 14:46:27 +01:00
master_port = dict ( default = None , type = ' int ' ) ,
master_connect_retry = dict ( default = None , type = ' int ' ) ,
2013-07-30 17:02:07 +02:00
master_log_file = dict ( default = None ) ,
2015-01-05 14:46:27 +01:00
master_log_pos = dict ( default = None , type = ' int ' ) ,
2013-07-30 17:02:07 +02:00
relay_log_file = dict ( default = None ) ,
2015-01-05 14:46:27 +01:00
relay_log_pos = dict ( default = None , type = ' int ' ) ,
2014-06-09 15:58:55 +02:00
master_ssl = dict ( default = False , type = ' bool ' ) ,
2013-07-30 17:02:07 +02:00
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 ) ,
2016-03-10 23:11:16 +01:00
connect_timeout = dict ( default = 30 , type = ' int ' ) ,
2015-11-17 05:54:57 +01:00
config_file = dict ( default = " ~/.my.cnf " ) ,
ssl_cert = dict ( default = None ) ,
ssl_key = dict ( default = None ) ,
ssl_ca = dict ( default = None ) ,
2013-07-30 17:02:07 +02:00
)
)
user = module . params [ " login_user " ]
password = module . params [ " login_password " ]
host = module . params [ " login_host " ]
2014-09-30 01:49:42 +02:00
port = module . params [ " login_port " ]
2013-07-30 17:02:07 +02:00
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 " ]
2015-04-15 02:35:13 +02:00
master_auto_position = module . params [ " master_auto_position " ]
2015-11-17 05:54:57 +01:00
ssl_cert = module . params [ " ssl_cert " ]
ssl_key = module . params [ " ssl_key " ]
ssl_ca = module . params [ " ssl_ca " ]
2016-03-10 23:11:16 +01:00
connect_timeout = module . params [ ' connect_timeout ' ]
2015-11-17 05:54:57 +01:00
config_file = module . params [ ' config_file ' ]
config_file = os . path . expanduser ( os . path . expandvars ( config_file ) )
2013-07-30 17:02:07 +02:00
if not mysqldb_found :
module . fail_json ( msg = " the python mysqldb module is required " )
else :
2015-11-09 10:07:15 +01:00
warnings . filterwarnings ( ' error ' , category = MySQLdb . Warning )
2013-07-30 17:02:07 +02:00
login_password = module . params [ " login_password " ]
login_user = module . params [ " login_user " ]
try :
2016-03-10 23:11:16 +01:00
cursor = mysql_connect ( module , login_user , login_password , config_file , ssl_cert , ssl_key , ssl_ca , None , ' MySQLdb.cursors.DictCursor ' ,
connect_timeout = connect_timeout )
2013-10-02 22:00:07 +02:00
except Exception , e :
2015-11-17 05:54:57 +01:00
if os . path . exists ( config_file ) :
module . fail_json ( msg = " unable to connect to database, check login_user and login_password are correct or %s has the credentials. Exception message: %s " % ( config_file , e ) )
else :
module . fail_json ( msg = " unable to find %s . Exception message: %s " % ( config_file , e ) )
2013-07-30 17:02:07 +02:00
if mode in " getmaster " :
2016-08-04 17:27:39 +02:00
status = get_master_status ( cursor )
if not isinstance ( status , dict ) :
status = dict ( Is_Master = False , msg = " Server is not configured as mysql master " )
else :
status [ ' Is_Master ' ] = True
module . exit_json ( * * status )
2013-07-30 17:02:07 +02:00
elif mode in " getslave " :
2016-08-04 17:27:39 +02:00
status = get_slave_status ( cursor )
if not isinstance ( status , dict ) :
status = dict ( Is_Slave = False , msg = " Server is not configured as mysql slave " )
else :
status [ ' Is_Slave ' ] = True
module . exit_json ( * * status )
2013-08-08 00:10:06 +02:00
2013-07-30 17:02:07 +02:00
elif mode in " changemaster " :
chm = [ ]
2014-11-25 10:34:25 +01:00
chm_params = { }
2016-08-04 17:17:10 +02:00
result = { }
2013-08-08 00:10:06 +02:00
if master_host :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_HOST= %(master_host)s " )
chm_params [ ' master_host ' ] = master_host
2013-07-30 17:02:07 +02:00
if master_user :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_USER= %(master_user)s " )
chm_params [ ' master_user ' ] = master_user
2013-07-30 17:02:07 +02:00
if master_password :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_PASSWORD= %(master_password)s " )
chm_params [ ' master_password ' ] = master_password
2015-01-07 18:20:45 +01:00
if master_port is not None :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_PORT= %(master_port)s " )
chm_params [ ' master_port ' ] = master_port
2015-01-07 18:20:45 +01:00
if master_connect_retry is not None :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_CONNECT_RETRY= %(master_connect_retry)s " )
chm_params [ ' master_connect_retry ' ] = master_connect_retry
2013-07-30 17:02:07 +02:00
if master_log_file :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_LOG_FILE= %(master_log_file)s " )
chm_params [ ' master_log_file ' ] = master_log_file
2015-01-07 18:20:45 +01:00
if master_log_pos is not None :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_LOG_POS= %(master_log_pos)s " )
chm_params [ ' master_log_pos ' ] = master_log_pos
2013-07-30 17:02:07 +02:00
if relay_log_file :
2014-11-25 10:34:25 +01:00
chm . append ( " RELAY_LOG_FILE= %(relay_log_file)s " )
chm_params [ ' relay_log_file ' ] = relay_log_file
2015-01-07 18:20:45 +01:00
if relay_log_pos is not None :
2014-11-25 10:34:25 +01:00
chm . append ( " RELAY_LOG_POS= %(relay_log_pos)s " )
chm_params [ ' relay_log_pos ' ] = relay_log_pos
2013-07-30 17:02:07 +02:00
if master_ssl :
2014-06-09 15:58:55 +02:00
chm . append ( " MASTER_SSL=1 " )
2013-07-30 17:02:07 +02:00
if master_ssl_ca :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_SSL_CA= %(master_ssl_ca)s " )
chm_params [ ' master_ssl_ca ' ] = master_ssl_ca
2013-07-30 17:02:07 +02:00
if master_ssl_capath :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_SSL_CAPATH= %(master_ssl_capath)s " )
chm_params [ ' master_ssl_capath ' ] = master_ssl_capath
2013-07-30 17:02:07 +02:00
if master_ssl_cert :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_SSL_CERT= %(master_ssl_cert)s " )
chm_params [ ' master_ssl_cert ' ] = master_ssl_cert
2013-07-30 17:02:07 +02:00
if master_ssl_key :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_SSL_KEY= %(master_ssl_key)s " )
chm_params [ ' master_ssl_key ' ] = master_ssl_key
2013-07-30 17:02:07 +02:00
if master_ssl_cipher :
2014-11-25 10:34:25 +01:00
chm . append ( " MASTER_SSL_CIPHER= %(master_ssl_cipher)s " )
chm_params [ ' master_ssl_cipher ' ] = master_ssl_cipher
2015-04-15 02:35:13 +02:00
if master_auto_position :
2014-12-01 15:16:40 +01:00
chm . append ( " MASTER_AUTO_POSITION = 1 " )
2015-11-17 05:54:57 +01:00
try :
changemaster ( cursor , chm , chm_params )
2016-08-04 17:17:10 +02:00
except MySQLdb . Warning , e :
result [ ' warning ' ] = str ( e )
2015-11-17 05:54:57 +01:00
except Exception , e :
module . fail_json ( msg = ' %s . Query == CHANGE MASTER TO %s ' % ( e , chm ) )
2016-08-04 17:17:10 +02:00
result [ ' changed ' ] = True
module . exit_json ( * * result )
2013-07-30 17:02:07 +02:00
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 )
2015-09-15 12:57:37 +02:00
elif mode in " resetslave " :
reset = reset_slave ( cursor )
if reset is True :
module . exit_json ( msg = " Slave reset " , changed = True )
else :
module . exit_json ( msg = " Slave already reset " , changed = False )
elif mode in " resetslaveall " :
reset = reset_slave_all ( cursor )
if reset is True :
module . exit_json ( msg = " Slave reset " , changed = True )
else :
module . exit_json ( msg = " Slave already reset " , changed = False )
2013-07-30 17:02:07 +02:00
2013-12-02 21:13:49 +01:00
# import module snippets
2013-12-02 21:11:23 +01:00
from ansible . module_utils . basic import *
2015-11-17 05:54:57 +01:00
from ansible . module_utils . mysql import *
2013-07-30 17:02:07 +02:00
main ( )
2014-11-25 10:34:25 +01:00
warnings . simplefilter ( " ignore " )