ansible/library/database/mysql_db

300 lines
9.8 KiB
Text
Raw Normal View History

2012-07-12 01:00:55 +02:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
2012-07-12 01:00:55 +02:00
# (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/>.
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
required: true
default: null
2013-07-14 13:11:03 +02:00
aliases: [ db ]
login_user:
description:
- The username used to authenticate with
required: false
default: null
login_password:
description:
2012-09-29 16:43:21 +02:00
- The password used to authenticate with
required: false
default: null
login_host:
description:
- Host running the database
required: false
default: localhost
login_port:
description:
- Port of the MySQL server
required: false
default: 3306
login_unix_socket:
description:
- The path to a Unix domain socket for local connections
required: false
default: null
state:
description:
- The database state
required: false
default: present
choices: [ "present", "absent", "dump", "import" ]
all_databases:
description:
- May only be provided if I(state) is C(dump) or C(import). (added in Ansible 1.4)
- Requires I(name) = a valid database name.
- Uses --all-databases option for mysqldump.
- When used for import, works similiar to mysql -u <username> -p <pasword> < target.sql
required: no
default: false
choices: [ "yes", "no", "true", "false", "0", "1" ]
collation:
description:
- Collation mode
required: false
default: null
encoding:
description:
- Encoding mode
required: false
default: null
target:
description:
2012-11-21 18:49:30 +01:00
- Where to dump/get the C(.sql) file
required: false
notes:
2012-10-01 09:18:54 +02:00
- Requires the MySQLdb Python package on the remote host. For Ubuntu, this
2012-11-21 18:49:30 +01:00
is as easy as apt-get install python-mysqldb. (See M(apt).)
- Both I(login_password) and I(login_user) are required when you are
2012-10-01 09:18:54 +02:00
passing credentials. If none are present, the module will attempt to read
the credentials from C(~/.my.cnf), and finally fall back to using the MySQL
2012-11-21 18:49:30 +01:00
default login of C(root) with no password.
requirements: [ ConfigParser ]
author: Mark Theunissen
'''
EXAMPLES = '''
# Create a new database with name 'bobdata'
2013-07-14 13:11:03 +02:00
- mysql_db: name=bobdata state=present
# Dumps all databases to hostname.sql
- mysql_db: name=bobdata state=dump all_databases=true target=/tmp/{{ inventory_hostname }}.sql
# Imports file.sql similiar to mysql -u <username> -p <password> < hostname.sql
- mysql_db: name=bobdata state=import all_databases=true target=/tmp/{{ inventory_hostname }}.sql
'''
import ConfigParser
import os
2012-07-12 01:00:55 +02:00
try:
2012-07-22 01:02:34 +02:00
import MySQLdb
2012-07-12 01:00:55 +02:00
except ImportError:
2012-07-22 01:02:34 +02:00
mysqldb_found = False
else:
mysqldb_found = True
2012-07-12 01:00:55 +02:00
# ===========================================
# MySQL module specific support methods.
#
2012-07-22 01:02:34 +02:00
def db_exists(cursor, db):
2012-07-12 01:00:55 +02:00
res = cursor.execute("SHOW DATABASES LIKE %s", (db,))
return bool(res)
2012-07-22 01:02:34 +02:00
def db_delete(cursor, db):
2013-04-26 20:48:38 +02:00
query = "DROP DATABASE `%s`" % db
2012-07-12 01:00:55 +02:00
cursor.execute(query)
return True
def db_dump(host, user, password, db_name, target, all_databases):
if all_databases:
res = os.system("/usr/bin/mysqldump -q -h "+host+" -u "+user+ " --password="+password+" --all-databases > "
+target)
else:
res = os.system("/usr/bin/mysqldump -q -h "+host+" -u "+user+ " --password="+password+" "
+db_name+" > "
+target)
return (res == 0)
def db_import(host, user, password, db_name, target, all_databases):
if all_databases:
res = os.system("/usr/bin/mysql -h "+host+" -u "+user+" --password="+password+" < "
+target)
else:
res = os.system("/usr/bin/mysql -h "+host+" -u "+user+" --password="+password+" "
+db_name+" < "
+target)
return (res == 0)
def db_create(cursor, db, encoding, collation):
if encoding:
encoding = " CHARACTER SET %s" % encoding
if collation:
collation = " COLLATE %s" % collation
2013-04-26 20:48:38 +02:00
query = "CREATE DATABASE `%s`%s%s" % (db, encoding, collation)
2012-07-12 01:00:55 +02:00
res = cursor.execute(query)
return True
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))
2013-02-26 23:27:23 +01:00
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')
2013-02-26 23:27:23 +01:00
except (ConfigParser.NoOptionError):
try:
passwd = config_get(config, 'client', 'pass')
2013-02-26 23:27:23 +01:00
except (ConfigParser.NoOptionError):
return False
try:
creds = dict(user=config_get(config, 'client', 'user'),passwd=passwd)
2013-02-26 23:27:23 +01:00
except (ConfigParser.NoOptionError):
return False
return creds
2012-07-12 01:00:55 +02:00
# ===========================================
# Module execution.
#
2012-07-22 01:02:34 +02:00
def main():
module = AnsibleModule(
argument_spec = dict(
login_user=dict(default=None),
login_password=dict(default=None),
login_host=dict(default="localhost"),
login_port=dict(default="3306"),
login_unix_socket=dict(default=None),
db=dict(required=True, aliases=['name']),
encoding=dict(default=""),
collation=dict(default=""),
all_databases=dict(default="no", choices=BOOLEANS, type='bool'),
target=dict(default=None),
state=dict(default="present", choices=["absent", "present","dump", "import"]),
2012-07-22 01:02:34 +02:00
)
)
if not mysqldb_found:
module.fail_json(msg="the python mysqldb module is required")
db = module.params["db"]
all_databases = module.params["all_databases"]
encoding = module.params["encoding"]
collation = module.params["collation"]
2012-07-22 01:02:34 +02:00
state = module.params["state"]
target = module.params["target"]
# 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")
login_host = module.params["login_host"]
if state in ['dump','import']:
if target is None:
2013-01-10 19:24:23 +01:00
module.fail_json(msg="with state=%s target is required" % (state))
connect_to_db = db
else:
connect_to_db = 'mysql'
2012-07-12 01:00:55 +02:00
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, db=connect_to_db)
else:
db_connection = MySQLdb.connect(host=module.params["login_host"], port=int(module.params["login_port"]), user=login_user, passwd=login_password, db=connect_to_db)
2012-07-12 01:00:55 +02:00
cursor = db_connection.cursor()
2012-11-09 14:27:03 +01:00
except Exception, e:
module.fail_json(msg="unable to connect, check login_user and login_password are correct, or alternatively check ~/.my.cnf contains credentials")
2012-07-12 01:00:55 +02:00
changed = False
2012-07-22 01:02:34 +02:00
if db_exists(cursor, db):
2012-07-12 01:00:55 +02:00
if state == "absent":
2012-07-22 01:02:34 +02:00
changed = db_delete(cursor, db)
elif state == "dump":
changed = db_dump(login_host, login_user, login_password, db, target, all_databases)
if not changed:
module.fail_json(msg="dump failed!")
elif state == "import":
changed = db_import(login_host, login_user, login_password, db, target, all_databases)
if not changed:
module.fail_json(msg="import failed!")
2012-07-12 01:00:55 +02:00
else:
if state == "present":
changed = db_create(cursor, db, encoding, collation)
2012-07-22 01:02:34 +02:00
module.exit_json(changed=changed, db=db)
2012-07-12 01:00:55 +02:00
2012-07-22 01:02:34 +02:00
# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()