2014-09-26 01:01:01 +00: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 .
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 / > .
"""
2016-12-06 10:35:25 +00:00
ANSIBLE_METADATA = { ' status ' : [ ' preview ' ] ,
' supported_by ' : ' community ' ,
' version ' : ' 1.0 ' }
2014-09-26 01:01:01 +00:00
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 "
2017-01-27 23:20:31 +00:00
author : " Balazs Pocze (@banyek) "
2014-09-26 01:01:01 +00:00
options :
mode :
description :
2015-09-17 07:52:58 +00: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 )
2014-09-26 01:01:01 +00:00
required : False
choices :
- getslave
- getmaster
- changemaster
- stopslave
- startslave
2015-09-15 10:57:37 +00:00
- resetslave
- resetslaveall
2014-09-26 01:01:01 +00: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
2016-12-08 05:34:16 +00:00
choices : [ 0 , 1 ]
2014-09-26 01:01:01 +00: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 00:35:13 +00:00
master_auto_position :
2016-12-08 05:34:16 +00:00
description :
2014-12-01 14:16:40 +00:00
- does the host uses GTID based replication or not
2015-11-17 04:54:57 +00:00
required : false
default : null
version_added : " 2.0 "
extends_documentation_fragment : mysql
2014-09-26 01:01:01 +00:00
'''
EXAMPLES = '''
# Stop mysql slave thread
2016-12-01 14:03:07 +00:00
- mysql_replication :
mode : stopslave
2014-09-26 01:01:01 +00:00
# Get master binlog file name and binlog position
2016-12-01 14:03:07 +00:00
- mysql_replication :
mode : getmaster
2014-09-26 01:01:01 +00:00
2016-12-01 14:03:07 +00:00
# Change master to master server 192.0.2.1 and use binary log 'mysql-bin.000009' with position 4578
- mysql_replication :
mode : changemaster
master_host : 192.0 .2 .1
master_log_file : mysql - bin .000009
master_log_pos : 4578
2014-09-29 23:49:42 +00:00
# Check slave status using port 3308
2016-12-01 14:03:07 +00:00
- mysql_replication :
mode : getslave
login_host : ansible . example . com
login_port : 3308
2014-09-26 01:01:01 +00:00
'''
import os
import warnings
try :
import MySQLdb
except ImportError :
mysqldb_found = False
else :
mysqldb_found = True
2016-10-23 23:24:38 +00:00
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . mysql import mysql_connect
from ansible . module_utils . pycompat24 import get_exception
2014-09-26 01:01:01 +00:00
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 10:57:37 +00: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
2014-09-26 01:01:01 +00:00
def start_slave ( cursor ) :
try :
cursor . execute ( " START SLAVE " )
started = True
except :
started = False
return started
2014-11-25 09:34:25 +00:00
def changemaster ( cursor , chm , chm_params ) :
sql_param = " , " . join ( chm )
query = ' CHANGE MASTER TO %s ' % sql_param
cursor . execute ( query , chm_params )
2014-09-26 01:01:01 +00:00
def main ( ) :
module = AnsibleModule (
2017-01-29 07:28:53 +00:00
argument_spec = dict (
2014-09-26 01:01:01 +00:00
login_user = dict ( default = None ) ,
2016-09-17 16:33:11 +00:00
login_password = dict ( default = None , no_log = True ) ,
2014-09-26 01:01:01 +00:00
login_host = dict ( default = " localhost " ) ,
2015-03-26 18:42:08 +00:00
login_port = dict ( default = 3306 , type = ' int ' ) ,
2014-09-26 01:01:01 +00:00
login_unix_socket = dict ( default = None ) ,
2015-10-07 17:04:37 +00:00
mode = dict ( default = " getslave " , choices = [ " getmaster " , " getslave " , " changemaster " , " stopslave " , " startslave " , " resetslave " , " resetslaveall " ] ) ,
2015-04-15 20:45:26 +00:00
master_auto_position = dict ( default = False , type = ' bool ' ) ,
2014-09-26 01:01:01 +00:00
master_host = dict ( default = None ) ,
master_user = dict ( default = None ) ,
2016-09-17 16:33:11 +00:00
master_password = dict ( default = None , no_log = True ) ,
2015-01-05 13:46:27 +00:00
master_port = dict ( default = None , type = ' int ' ) ,
master_connect_retry = dict ( default = None , type = ' int ' ) ,
2014-09-26 01:01:01 +00:00
master_log_file = dict ( default = None ) ,
2015-01-05 13:46:27 +00:00
master_log_pos = dict ( default = None , type = ' int ' ) ,
2014-09-26 01:01:01 +00:00
relay_log_file = dict ( default = None ) ,
2015-01-05 13:46:27 +00:00
relay_log_pos = dict ( default = None , type = ' int ' ) ,
2014-09-26 01:01:01 +00:00
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 ) ,
2016-03-10 22:11:16 +00:00
connect_timeout = dict ( default = 30 , type = ' int ' ) ,
2016-09-17 16:33:11 +00:00
config_file = dict ( default = " ~/.my.cnf " , type = ' path ' ) ,
2015-11-17 04:54:57 +00:00
ssl_cert = dict ( default = None ) ,
ssl_key = dict ( default = None ) ,
ssl_ca = dict ( default = None ) ,
2014-09-26 01:01:01 +00: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 00:35:13 +00:00
master_auto_position = module . params [ " master_auto_position " ]
2015-11-17 04:54:57 +00:00
ssl_cert = module . params [ " ssl_cert " ]
ssl_key = module . params [ " ssl_key " ]
ssl_ca = module . params [ " ssl_ca " ]
2016-03-10 22:11:16 +00:00
connect_timeout = module . params [ ' connect_timeout ' ]
2015-11-17 04:54:57 +00:00
config_file = module . params [ ' config_file ' ]
2014-09-26 01:01:01 +00:00
if not mysqldb_found :
module . fail_json ( msg = " the python mysqldb module is required " )
else :
2015-11-09 09:07:15 +00:00
warnings . filterwarnings ( ' error ' , category = MySQLdb . Warning )
2014-09-26 01:01:01 +00:00
login_password = module . params [ " login_password " ]
login_user = module . params [ " login_user " ]
try :
2016-03-10 22:11:16 +00: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 )
2016-10-23 23:24:38 +00:00
except Exception :
e = get_exception ( )
2015-11-17 04:54:57 +00: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 ) )
2014-09-26 01:01:01 +00:00
if mode in " getmaster " :
2016-08-04 15:27:39 +00: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 )
2014-09-26 01:01:01 +00:00
elif mode in " getslave " :
2016-08-04 15:27:39 +00: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 )
2014-09-26 01:01:01 +00:00
elif mode in " changemaster " :
chm = [ ]
2014-11-25 09:34:25 +00:00
chm_params = { }
2016-08-04 15:17:10 +00:00
result = { }
2014-09-26 01:01:01 +00:00
if master_host :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_HOST= %(master_host)s " )
chm_params [ ' master_host ' ] = master_host
2014-09-26 01:01:01 +00:00
if master_user :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_USER= %(master_user)s " )
chm_params [ ' master_user ' ] = master_user
2014-09-26 01:01:01 +00:00
if master_password :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_PASSWORD= %(master_password)s " )
chm_params [ ' master_password ' ] = master_password
2015-01-07 17:20:45 +00:00
if master_port is not None :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_PORT= %(master_port)s " )
chm_params [ ' master_port ' ] = master_port
2015-01-07 17:20:45 +00:00
if master_connect_retry is not None :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_CONNECT_RETRY= %(master_connect_retry)s " )
chm_params [ ' master_connect_retry ' ] = master_connect_retry
2014-09-26 01:01:01 +00:00
if master_log_file :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_LOG_FILE= %(master_log_file)s " )
chm_params [ ' master_log_file ' ] = master_log_file
2015-01-07 17:20:45 +00:00
if master_log_pos is not None :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_LOG_POS= %(master_log_pos)s " )
chm_params [ ' master_log_pos ' ] = master_log_pos
2014-09-26 01:01:01 +00:00
if relay_log_file :
2014-11-25 09:34:25 +00:00
chm . append ( " RELAY_LOG_FILE= %(relay_log_file)s " )
chm_params [ ' relay_log_file ' ] = relay_log_file
2015-01-07 17:20:45 +00:00
if relay_log_pos is not None :
2014-11-25 09:34:25 +00:00
chm . append ( " RELAY_LOG_POS= %(relay_log_pos)s " )
chm_params [ ' relay_log_pos ' ] = relay_log_pos
2014-09-26 01:01:01 +00:00
if master_ssl :
chm . append ( " MASTER_SSL=1 " )
if master_ssl_ca :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_SSL_CA= %(master_ssl_ca)s " )
chm_params [ ' master_ssl_ca ' ] = master_ssl_ca
2014-09-26 01:01:01 +00:00
if master_ssl_capath :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_SSL_CAPATH= %(master_ssl_capath)s " )
chm_params [ ' master_ssl_capath ' ] = master_ssl_capath
2014-09-26 01:01:01 +00:00
if master_ssl_cert :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_SSL_CERT= %(master_ssl_cert)s " )
chm_params [ ' master_ssl_cert ' ] = master_ssl_cert
2014-09-26 01:01:01 +00:00
if master_ssl_key :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_SSL_KEY= %(master_ssl_key)s " )
chm_params [ ' master_ssl_key ' ] = master_ssl_key
2014-09-26 01:01:01 +00:00
if master_ssl_cipher :
2014-11-25 09:34:25 +00:00
chm . append ( " MASTER_SSL_CIPHER= %(master_ssl_cipher)s " )
chm_params [ ' master_ssl_cipher ' ] = master_ssl_cipher
2015-04-15 00:35:13 +00:00
if master_auto_position :
2014-12-01 14:16:40 +00:00
chm . append ( " MASTER_AUTO_POSITION = 1 " )
2015-11-17 04:54:57 +00:00
try :
changemaster ( cursor , chm , chm_params )
2016-10-23 23:24:38 +00:00
except MySQLdb . Warning :
e = get_exception ( )
result [ ' warning ' ] = str ( e )
except Exception :
e = get_exception ( )
2015-11-17 04:54:57 +00:00
module . fail_json ( msg = ' %s . Query == CHANGE MASTER TO %s ' % ( e , chm ) )
2016-08-04 15:17:10 +00:00
result [ ' changed ' ] = True
module . exit_json ( * * result )
2014-09-26 01:01:01 +00: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 10:57:37 +00: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 )
2014-09-26 01:01:01 +00:00
2016-10-23 23:24:38 +00:00
if __name__ == ' __main__ ' :
main ( )
warnings . simplefilter ( " ignore " )