2014-09-26 01:01:01 +00:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, Mark Theunissen <mark.theunissen@gmail.com>
# Sponsored by Four Kitchens http://fourkitchens.com.
#
# 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:05 +00:00
ANSIBLE_METADATA = { ' status ' : [ ' preview ' ] ,
' supported_by ' : ' community ' ,
' version ' : ' 1.0 ' }
2014-09-26 01:01:01 +00:00
DOCUMENTATION = '''
- - -
module : mysql_db
short_description : Add or remove MySQL databases from a remote host .
description :
- Add or remove MySQL databases from a remote host .
version_added : " 0.6 "
options :
name :
description :
- name of the database to add or remove
2015-12-08 17:09:50 +00:00
- name = all May only be provided if I ( state ) is C ( dump ) or C ( import ) .
2014-09-29 23:18:58 +00:00
- if name = all Works like - - all - databases option for mysqldump ( Added in 2.0 )
2014-09-26 01:01:01 +00:00
required : true
default : null
aliases : [ db ]
state :
description :
- The database state
required : false
default : present
choices : [ " present " , " absent " , " dump " , " import " ]
collation :
description :
2015-10-31 18:24:32 +00:00
- Collation mode ( sorting ) . This only applies to new table / databases and does not update existing ones , this is a limitation of MySQL .
2014-09-26 01:01:01 +00:00
required : false
default : null
encoding :
description :
2016-04-14 15:17:13 +00:00
- Encoding mode to use , examples include C ( utf8 ) or C ( latin1_swedish_ci )
2014-09-26 01:01:01 +00:00
required : false
default : null
target :
description :
- Location , on the remote host , of the dump file to read from or write to . Uncompressed SQL
2015-10-28 20:35:51 +00:00
files ( C ( . sql ) ) as well as bzip2 ( C ( . bz2 ) ) , gzip ( C ( . gz ) ) and xz ( Added in 2.0 ) compressed files are supported .
2014-09-26 01:01:01 +00:00
required : false
2016-04-12 09:33:58 +00:00
single_transaction :
description :
- Execute the dump in a single transaction
required : false
default : false
version_added : " 2.1 "
quick :
description :
- Option used for dumping large tables
required : false
2016-04-13 09:15:48 +00:00
default : true
2016-04-12 09:33:58 +00:00
version_added : " 2.1 "
2015-10-27 16:09:01 +00:00
author : " Ansible Core Team "
2016-04-11 05:46:39 +00:00
requirements :
- mysql ( command line binary )
- mysqldump ( command line binary )
notes :
- Requires the python - mysqldb package on the remote host , as well as mysql and mysqldump binaries .
2015-11-11 04:13:48 +00:00
extends_documentation_fragment : mysql
2014-09-26 01:01:01 +00:00
'''
EXAMPLES = '''
2016-11-16 17:24:21 +00:00
- name : Create a new database with name ' bobdata '
mysql_db :
2016-10-12 19:19:37 +00:00
name : bobdata
state : present
2014-09-26 01:01:01 +00:00
# Copy database dump file to remote host and restore it to database 'my_db'
2016-11-16 17:24:21 +00:00
- name : Copy database dump file
copy :
2016-10-12 19:19:37 +00:00
src : dump . sql . bz2
dest : / tmp
2016-11-16 17:24:21 +00:00
- name : Restore database
mysql_db :
2016-10-12 19:19:37 +00:00
name : my_db
state : import
target : / tmp / dump . sql . bz2
2014-09-29 23:18:58 +00:00
2016-11-16 17:24:21 +00:00
- name : Dump all databases to hostname . sql
mysql_db :
state : dump
name : all
target : / tmp / { { inventory_hostname } } . sql
2014-09-29 23:18:58 +00:00
2016-11-16 17:24:21 +00:00
- name : Import file . sql similar to mysql - u < username > - p < password > < hostname . sql
mysql_db :
state : import
name : all
target : / tmp / { { inventory_hostname } } . sql
2014-09-26 01:01:01 +00:00
'''
import os
import pipes
2014-09-29 23:11:45 +00:00
import stat
2015-05-25 12:40:15 +00:00
import subprocess
2015-11-11 04:13:48 +00:00
2014-09-26 01:01:01 +00:00
try :
import MySQLdb
except ImportError :
mysqldb_found = False
else :
mysqldb_found = True
# ===========================================
# MySQL module specific support methods.
#
def db_exists ( cursor , db ) :
res = cursor . execute ( " SHOW DATABASES LIKE %s " , ( db . replace ( " _ " , " \ _ " ) , ) )
return bool ( res )
def db_delete ( cursor , db ) :
2014-11-25 09:46:09 +00:00
query = " DROP DATABASE %s " % mysql_quote_identifier ( db , ' database ' )
2014-09-26 01:01:01 +00:00
cursor . execute ( query )
return True
2016-04-12 09:33:58 +00:00
def db_dump ( module , host , user , password , db_name , target , all_databases , port , config_file , socket = None , ssl_cert = None , ssl_key = None , ssl_ca = None , single_transaction = None , quick = None ) :
2014-09-26 01:01:01 +00:00
cmd = module . get_bin_path ( ' mysqldump ' , True )
2015-11-11 04:13:48 +00:00
# If defined, mysqldump demands --defaults-extra-file be the first option
2015-12-17 19:35:44 +00:00
if config_file :
cmd + = " --defaults-extra-file= %s " % pipes . quote ( config_file )
2015-11-11 04:13:48 +00:00
if user is not None :
cmd + = " --user= %s " % pipes . quote ( user )
if password is not None :
cmd + = " --password= %s " % pipes . quote ( password )
if ssl_cert is not None :
cmd + = " --ssl-cert= %s " % pipes . quote ( ssl_cert )
if ssl_key is not None :
cmd + = " --ssl-key= %s " % pipes . quote ( ssl_key )
if ssl_cert is not None :
cmd + = " --ssl-ca= %s " % pipes . quote ( ssl_ca )
2014-09-26 01:01:01 +00:00
if socket is not None :
cmd + = " --socket= %s " % pipes . quote ( socket )
else :
2015-03-03 22:23:07 +00:00
cmd + = " --host= %s --port= %i " % ( pipes . quote ( host ) , port )
2014-09-29 23:18:58 +00:00
if all_databases :
cmd + = " --all-databases "
else :
cmd + = " %s " % pipes . quote ( db_name )
2016-04-12 09:33:58 +00:00
if single_transaction :
cmd + = " --single-transaction=true "
if quick :
cmd + = " --quick "
2015-05-25 16:22:08 +00:00
path = None
2014-09-26 01:01:01 +00:00
if os . path . splitext ( target ) [ - 1 ] == ' .gz ' :
2015-05-25 16:22:08 +00:00
path = module . get_bin_path ( ' gzip ' , True )
2014-09-26 01:01:01 +00:00
elif os . path . splitext ( target ) [ - 1 ] == ' .bz2 ' :
2015-05-25 16:22:08 +00:00
path = module . get_bin_path ( ' bzip2 ' , True )
2015-05-12 21:28:18 +00:00
elif os . path . splitext ( target ) [ - 1 ] == ' .xz ' :
2015-05-25 16:22:08 +00:00
path = module . get_bin_path ( ' xz ' , True )
if path :
cmd = ' %s | %s > %s ' % ( cmd , path , pipes . quote ( target ) )
2014-09-26 01:01:01 +00:00
else :
cmd + = " > %s " % pipes . quote ( target )
2015-05-25 16:22:08 +00:00
2014-09-26 01:01:01 +00:00
rc , stdout , stderr = module . run_command ( cmd , use_unsafe_shell = True )
return rc , stdout , stderr
2015-11-11 04:13:48 +00:00
def db_import ( module , host , user , password , db_name , target , all_databases , port , config_file , socket = None , ssl_cert = None , ssl_key = None , ssl_ca = None ) :
2014-09-26 01:01:01 +00:00
if not os . path . exists ( target ) :
return module . fail_json ( msg = " target %s does not exist on the host " % target )
2015-05-28 03:27:51 +00:00
cmd = [ module . get_bin_path ( ' mysql ' , True ) ]
2015-11-11 04:13:48 +00:00
# --defaults-file must go first, or errors out
2015-12-17 19:35:44 +00:00
if config_file :
cmd . append ( " --defaults-extra-file= %s " % pipes . quote ( config_file ) )
2015-05-28 03:27:51 +00:00
if user :
cmd . append ( " --user= %s " % pipes . quote ( user ) )
if password :
cmd . append ( " --password= %s " % pipes . quote ( password ) )
2014-09-26 01:01:01 +00:00
if socket is not None :
2015-05-28 03:27:51 +00:00
cmd . append ( " --socket= %s " % pipes . quote ( socket ) )
2015-11-11 04:13:48 +00:00
if ssl_cert is not None :
cmd . append ( " --ssl-cert= %s " % pipes . quote ( ssl_cert ) )
if ssl_key is not None :
cmd . append ( " --ssl-key= %s " % pipes . quote ( ssl_key ) )
if ssl_cert is not None :
cmd . append ( " --ssl-ca= %s " % pipes . quote ( ssl_ca ) )
2014-09-26 01:01:01 +00:00
else :
2015-05-28 03:27:51 +00:00
cmd . append ( " --host= %s " % pipes . quote ( host ) )
cmd . append ( " --port= %i " % port )
2014-09-29 23:18:58 +00:00
if not all_databases :
2015-05-28 03:27:51 +00:00
cmd . append ( " -D " )
cmd . append ( pipes . quote ( db_name ) )
2015-05-25 19:22:49 +00:00
comp_prog_path = None
2014-09-26 01:01:01 +00:00
if os . path . splitext ( target ) [ - 1 ] == ' .gz ' :
2015-05-25 19:10:12 +00:00
comp_prog_path = module . get_bin_path ( ' gzip ' , required = True )
2014-09-26 01:01:01 +00:00
elif os . path . splitext ( target ) [ - 1 ] == ' .bz2 ' :
2015-05-25 19:10:12 +00:00
comp_prog_path = module . get_bin_path ( ' bzip2 ' , required = True )
2015-05-12 21:28:18 +00:00
elif os . path . splitext ( target ) [ - 1 ] == ' .xz ' :
2015-05-25 19:10:12 +00:00
comp_prog_path = module . get_bin_path ( ' xz ' , required = True )
2015-05-25 19:22:49 +00:00
if comp_prog_path :
2015-05-25 19:10:12 +00:00
p1 = subprocess . Popen ( [ comp_prog_path , ' -dc ' , target ] , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
2015-05-28 03:27:51 +00:00
p2 = subprocess . Popen ( cmd , stdin = p1 . stdout , stdout = subprocess . PIPE , stderr = subprocess . PIPE )
2015-05-25 12:40:15 +00:00
( stdout2 , stderr2 ) = p2 . communicate ( )
p1 . stdout . close ( )
p1 . wait ( )
if p1 . returncode != 0 :
stderr1 = p1 . stderr . read ( )
return p1 . returncode , ' ' , stderr1
else :
return p2 . returncode , stdout2 , stderr2
2014-09-26 01:01:01 +00:00
else :
2015-05-28 03:27:51 +00:00
cmd = ' ' . join ( cmd )
2014-09-26 01:01:01 +00:00
cmd + = " < %s " % pipes . quote ( target )
rc , stdout , stderr = module . run_command ( cmd , use_unsafe_shell = True )
2015-05-25 19:22:49 +00:00
return rc , stdout , stderr
2014-09-26 01:01:01 +00:00
def db_create ( cursor , db , encoding , collation ) :
2014-11-25 09:46:09 +00:00
query_params = dict ( enc = encoding , collate = collation )
query = [ ' CREATE DATABASE %s ' % mysql_quote_identifier ( db , ' database ' ) ]
2014-09-26 01:01:01 +00:00
if encoding :
2014-11-25 09:46:09 +00:00
query . append ( " CHARACTER SET %(enc)s " )
2014-09-26 01:01:01 +00:00
if collation :
2014-11-25 09:46:09 +00:00
query . append ( " COLLATE %(collate)s " )
query = ' ' . join ( query )
res = cursor . execute ( query , query_params )
2014-09-26 01:01:01 +00:00
return True
# ===========================================
# Module execution.
#
def main ( ) :
module = AnsibleModule (
argument_spec = dict (
login_user = dict ( default = None ) ,
2016-04-11 14:57:24 +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-03 22:23:07 +00:00
login_port = dict ( default = 3306 , type = ' int ' ) ,
2014-09-26 01:01:01 +00:00
login_unix_socket = dict ( default = None ) ,
name = dict ( required = True , aliases = [ ' db ' ] ) ,
encoding = dict ( default = " " ) ,
collation = dict ( default = " " ) ,
2016-04-11 14:57:24 +00:00
target = dict ( default = None , type = ' path ' ) ,
2014-09-26 01:01:01 +00:00
state = dict ( default = " present " , choices = [ " absent " , " present " , " dump " , " import " ] ) ,
2016-04-11 14:57:24 +00:00
ssl_cert = dict ( default = None , type = ' path ' ) ,
ssl_key = dict ( default = None , type = ' path ' ) ,
ssl_ca = dict ( default = None , type = ' path ' ) ,
2016-03-10 21:06:39 +00:00
connect_timeout = dict ( default = 30 , type = ' int ' ) ,
2016-04-11 14:57:24 +00:00
config_file = dict ( default = " ~/.my.cnf " , type = ' path ' ) ,
2016-04-12 09:33:58 +00:00
single_transaction = dict ( default = False , type = ' bool ' ) ,
2016-04-13 09:15:48 +00:00
quick = dict ( default = True , type = ' bool ' ) ,
2016-01-26 14:05:49 +00:00
) ,
supports_check_mode = True
2014-09-26 01:01:01 +00:00
)
if not mysqldb_found :
module . fail_json ( msg = " the python mysqldb module is required " )
db = module . params [ " name " ]
encoding = module . params [ " encoding " ]
collation = module . params [ " collation " ]
state = module . params [ " state " ]
target = module . params [ " target " ]
2014-09-29 23:11:45 +00:00
socket = module . params [ " login_unix_socket " ]
2015-03-03 22:23:07 +00:00
login_port = module . params [ " login_port " ]
if login_port < 0 or login_port > 65535 :
module . fail_json ( msg = " login_port must be a valid unix port number (0-65535) " )
2015-11-11 04:13:48 +00:00
ssl_cert = module . params [ " ssl_cert " ]
ssl_key = module . params [ " ssl_key " ]
ssl_ca = module . params [ " ssl_ca " ]
2016-03-10 21:06:39 +00:00
connect_timeout = module . params [ ' connect_timeout ' ]
2015-11-11 04:13:48 +00:00
config_file = module . params [ ' config_file ' ]
login_password = module . params [ " login_password " ]
login_user = module . params [ " login_user " ]
login_host = module . params [ " login_host " ]
2016-04-12 09:33:58 +00:00
single_transaction = module . params [ " single_transaction " ]
quick = module . params [ " quick " ]
2014-09-26 01:01:01 +00:00
if state in [ ' dump ' , ' import ' ] :
if target is None :
module . fail_json ( msg = " with state= %s target is required " % ( state ) )
2015-06-30 21:22:50 +00:00
if db == ' all ' :
2014-09-29 23:18:58 +00:00
db = ' mysql '
all_databases = True
else :
all_databases = False
2014-09-26 01:01:01 +00:00
else :
2014-09-29 23:18:58 +00:00
if db == ' all ' :
module . fail_json ( msg = " name is not allowed to equal ' all ' unless state equals import, or dump. " )
2014-09-26 01:01:01 +00:00
try :
2016-03-10 21:06:39 +00:00
cursor = mysql_connect ( module , login_user , login_password , config_file , ssl_cert , ssl_key , ssl_ca ,
connect_timeout = connect_timeout )
2016-05-18 14:07:21 +00:00
except Exception :
e = get_exception ( )
2015-11-11 04:13:48 +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 ) )
2014-09-26 01:01:01 +00:00
else :
2015-11-11 04:13:48 +00:00
module . fail_json ( msg = " unable to find %s . Exception message: %s " % ( config_file , e ) )
2014-09-26 01:01:01 +00:00
changed = False
2015-12-17 19:35:44 +00:00
if not os . path . exists ( config_file ) :
config_file = None
2014-09-26 01:01:01 +00:00
if db_exists ( cursor , db ) :
if state == " absent " :
2016-01-26 14:05:49 +00:00
if module . check_mode :
2016-08-08 19:36:18 +00:00
module . exit_json ( changed = True , db = db )
2016-01-26 14:05:49 +00:00
else :
try :
changed = db_delete ( cursor , db )
2016-05-18 14:07:21 +00:00
except Exception :
e = get_exception ( )
2016-01-26 14:05:49 +00:00
module . fail_json ( msg = " error deleting database: " + str ( e ) )
2016-08-08 19:36:18 +00:00
module . exit_json ( changed = changed , db = db )
2014-09-26 01:01:01 +00:00
elif state == " dump " :
2016-01-26 14:05:49 +00:00
if module . check_mode :
module . exit_json ( changed = True , db = db )
2014-09-26 01:01:01 +00:00
else :
2016-01-26 14:05:49 +00:00
rc , stdout , stderr = db_dump ( module , login_host , login_user ,
login_password , db , target , all_databases ,
2016-09-21 18:41:56 +00:00
login_port , config_file , socket , ssl_cert , ssl_key , ssl_ca , single_transaction , quick )
2016-01-26 14:05:49 +00:00
if rc != 0 :
module . fail_json ( msg = " %s " % stderr )
else :
module . exit_json ( changed = True , db = db , msg = stdout )
2016-08-10 10:35:01 +00:00
2014-09-26 01:01:01 +00:00
elif state == " import " :
2016-01-26 14:05:49 +00:00
if module . check_mode :
module . exit_json ( changed = True , db = db )
2014-09-26 01:01:01 +00:00
else :
2016-01-26 14:05:49 +00:00
rc , stdout , stderr = db_import ( module , login_host , login_user ,
login_password , db , target , all_databases ,
login_port , config_file , socket , ssl_cert , ssl_key , ssl_ca )
if rc != 0 :
module . fail_json ( msg = " %s " % stderr )
else :
module . exit_json ( changed = True , db = db , msg = stdout )
2016-08-10 10:35:01 +00:00
2016-08-08 19:36:18 +00:00
elif state == " present " :
if module . check_mode :
module . exit_json ( changed = False , db = db )
module . exit_json ( changed = False , db = db )
2014-09-26 01:01:01 +00:00
else :
if state == " present " :
2016-01-26 14:05:49 +00:00
if module . check_mode :
changed = True
else :
try :
changed = db_create ( cursor , db , encoding , collation )
2016-05-18 14:07:21 +00:00
except Exception :
e = get_exception ( )
2016-01-26 14:05:49 +00:00
module . fail_json ( msg = " error creating database: " + str ( e ) )
2016-08-08 19:36:18 +00:00
module . exit_json ( changed = changed , db = db )
2014-09-26 01:01:01 +00:00
2016-08-08 19:36:18 +00:00
elif state == " import " :
if module . check_mode :
module . exit_json ( changed = True , db = db )
else :
try :
changed = db_create ( cursor , db , encoding , collation )
if changed :
rc , stdout , stderr = db_import ( module , login_host , login_user ,
login_password , db , target , all_databases ,
login_port , config_file , socket , ssl_cert , ssl_key , ssl_ca )
if rc != 0 :
module . fail_json ( msg = " %s " % stderr )
else :
module . exit_json ( changed = True , db = db , msg = stdout )
2016-08-10 10:35:01 +00:00
except Exception :
e = get_exception ( )
2016-08-08 19:36:18 +00:00
module . fail_json ( msg = " error creating database: " + str ( e ) )
elif state == " absent " :
if module . check_mode :
module . exit_json ( changed = False , db = db )
module . exit_json ( changed = False , db = db )
elif state == " dump " :
if module . check_mode :
module . exit_json ( changed = False , db = db )
module . fail_json ( msg = " Cannot dump database %s - not found " % ( db ) )
2014-09-26 01:01:01 +00:00
# import module snippets
from ansible . module_utils . basic import *
2014-11-25 09:46:09 +00:00
from ansible . module_utils . database import *
2015-11-11 04:13:48 +00:00
from ansible . module_utils . mysql import *
2014-11-25 09:46:09 +00:00
if __name__ == ' __main__ ' :
main ( )