Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple databases(not all) to be dumped from mysql #56721

Merged
merged 3 commits into from
Jun 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- mysql_db now supports multiple databases in dump operation (https://github.com/ansible/ansible/issues/56059)
124 changes: 71 additions & 53 deletions lib/ansible/modules/database/mysql/mysql_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
options:
name:
description:
- name of the database to add or remove
- name=all May only be provided if I(state) is C(dump) or C(import).
- if name=all Works like --all-databases option for mysqldump (Added in 2.0)
- name of the database to add or remove.
- I(name=all) May only be provided if I(state) is C(dump) or C(import).
- List of databases is provided with I(state=dump) only.
- if name=all Works like --all-databases option for mysqldump (Added in 2.0).
required: true
pratikgadiya12 marked this conversation as resolved.
Show resolved Hide resolved
type: list
aliases: [ db ]
state:
description:
Expand Down Expand Up @@ -81,23 +83,38 @@
copy:
src: dump.sql.bz2
dest: /tmp

- name: Restore database
mysql_db:
name: my_db
state: import
target: /tmp/dump.sql.bz2

- name: Dump multiple databases
mysql_db:
state: dump
name: db_1,db_2
target: /tmp/dump.sql

- name: Dump multiple databases
mysql_db:
state: dump
name:
- db_1
- db_2
target: /tmp/dump.sql

- name: Dump all databases to hostname.sql
mysql_db:
state: dump
name: all
target: /tmp/{{ inventory_hostname }}.sql
target: /tmp/dump.sql

- name: Import file.sql similar to mysql -u <username> -p <password> < hostname.sql
mysql_db:
state: import
name: all
target: /tmp/{{ inventory_hostname }}.sql
target: /tmp/dump.sql
'''

import os
Expand All @@ -117,12 +134,14 @@


def db_exists(cursor, db):
res = cursor.execute("SHOW DATABASES LIKE %s", (db.replace("_", r"\_"),))
return bool(res)
res = 0
for each_db in db:
res += cursor.execute("SHOW DATABASES LIKE %s", (each_db.strip().replace("_", r"\_"),))
return res == len(db)


def db_delete(cursor, db):
query = "DROP DATABASE %s" % mysql_quote_identifier(db, 'database')
query = "DROP DATABASE %s" % mysql_quote_identifier(''.join(db), 'database')
cursor.execute(query)
return True

Expand Down Expand Up @@ -150,7 +169,7 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port,
if all_databases:
cmd += " --all-databases"
else:
cmd += " %s" % shlex_quote(db_name)
cmd += " --databases {0} --skip-lock-tables".format(' '.join(db_name))
if single_transaction:
cmd += " --single-transaction=true"
if quick:
Expand Down Expand Up @@ -201,7 +220,7 @@ def db_import(module, host, user, password, db_name, target, all_databases, port
cmd.append("--port=%i" % port)
if not all_databases:
cmd.append("-D")
cmd.append(shlex_quote(db_name))
cmd.append(shlex_quote(''.join(db_name)))

comp_prog_path = None
if os.path.splitext(target)[-1] == '.gz':
Expand All @@ -210,7 +229,6 @@ def db_import(module, host, user, password, db_name, target, all_databases, port
comp_prog_path = module.get_bin_path('bzip2', required=True)
elif os.path.splitext(target)[-1] == '.xz':
comp_prog_path = module.get_bin_path('xz', required=True)

if comp_prog_path:
p1 = subprocess.Popen([comp_prog_path, '-dc', target], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen(cmd, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Expand All @@ -231,7 +249,7 @@ def db_import(module, host, user, password, db_name, target, all_databases, port

def db_create(cursor, db, encoding, collation):
query_params = dict(enc=encoding, collate=collation)
query = ['CREATE DATABASE %s' % mysql_quote_identifier(db, 'database')]
query = ['CREATE DATABASE %s' % mysql_quote_identifier(''.join(db), 'database')]
if encoding:
query.append("CHARACTER SET %(enc)s")
if collation:
Expand All @@ -253,7 +271,7 @@ def main():
login_host=dict(type='str', default='localhost'),
login_port=dict(type='int', default=3306),
login_unix_socket=dict(type='str'),
name=dict(type='str', required=True, aliases=['db']),
name=dict(type='list', required=True, aliases=['db']),
encoding=dict(type='str', default=''),
collation=dict(type='str', default=''),
target=dict(type='path'),
Expand All @@ -274,6 +292,9 @@ def main():
module.fail_json(msg=mysql_driver_fail_msg)

db = module.params["name"]
if not db:
module.fail_json(msg="Please provide at least one database name")

encoding = module.params["encoding"]
collation = module.params["collation"]
state = module.params["state"]
Expand All @@ -297,16 +318,20 @@ def main():
single_transaction = module.params["single_transaction"]
quick = module.params["quick"]

if len(db) > 1 and state != 'dump':
pratikgadiya12 marked this conversation as resolved.
Show resolved Hide resolved
module.fail_json(msg="Multiple databases is only supported with state=dump")
db_name = ' '.join(db)

if state in ['dump', 'import']:
if target is None:
module.fail_json(msg="with state=%s target is required" % state)
if db == 'all':
db = 'mysql'
if db == ['all']:
db = ['mysql']
all_databases = True
else:
all_databases = False
else:
if db == 'all':
if db == ['all']:
module.fail_json(msg="name is not allowed to equal 'all' unless state equals import, or dump.")
try:
cursor = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca,
Expand All @@ -324,45 +349,40 @@ def main():
if db_exists(cursor, db):
if state == "absent":
if module.check_mode:
module.exit_json(changed=True, db=db)
else:
try:
changed = db_delete(cursor, db)
except Exception as e:
module.fail_json(msg="error deleting database: %s" % to_native(e))
module.exit_json(changed=changed, db=db)
module.exit_json(changed=True, db=db_name)
try:
changed = db_delete(cursor, db)
except Exception as e:
module.fail_json(msg="error deleting database: %s" % to_native(e))
module.exit_json(changed=changed, db=db_name)

elif state == "dump":
if module.check_mode:
module.exit_json(changed=True, db=db)
module.exit_json(changed=True, db=db_name)
rc, stdout, stderr = db_dump(module, login_host, login_user,
login_password, db, target, all_databases,
login_port, config_file, socket, ssl_cert, ssl_key,
ssl_ca, single_transaction, quick, ignore_tables)
if rc != 0:
module.fail_json(msg="%s" % stderr)
else:
rc, stdout, stderr = db_dump(module, login_host, login_user,
login_password, db, target, all_databases,
login_port, config_file, socket, ssl_cert, ssl_key,
ssl_ca, single_transaction, quick, ignore_tables)
if rc != 0:
module.fail_json(msg="%s" % stderr)
else:
module.exit_json(changed=True, db=db, msg=stdout)
module.exit_json(changed=True, db=db_name, msg=stdout)

elif state == "import":
if module.check_mode:
module.exit_json(changed=True, db=db)
module.exit_json(changed=True, db=db_name)
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:
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)
module.exit_json(changed=True, db=db_name, msg=stdout)

elif state == "present":
if module.check_mode:
module.exit_json(changed=False, db=db)
module.exit_json(changed=False, db=db)
module.exit_json(changed=False, db=db_name)

else:
if state == "present":
Expand All @@ -374,11 +394,11 @@ def main():
except Exception as e:
module.fail_json(msg="error creating database: %s" % to_native(e),
exception=traceback.format_exc())
module.exit_json(changed=changed, db=db)
module.exit_json(changed=changed, db=db_name)

elif state == "import":
if module.check_mode:
module.exit_json(changed=True, db=db)
module.exit_json(changed=True, db=db_name)
else:
try:
changed = db_create(cursor, db, encoding, collation)
Expand All @@ -389,20 +409,18 @@ def main():
if rc != 0:
module.fail_json(msg="%s" % stderr)
else:
module.exit_json(changed=True, db=db, msg=stdout)
module.exit_json(changed=True, db=db_name, msg=stdout)
except Exception as e:
module.fail_json(msg="error creating database: %s" % to_native(e),
exception=traceback.format_exc())

elif state == "absent":
if module.check_mode:
module.exit_json(changed=False, db=db)
module.exit_json(changed=False, db=db)
module.exit_json(changed=False, db=db_name)

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))
module.exit_json(changed=False, db=db_name)
module.fail_json(msg="Cannot dump database %r - not found" % (db_name))


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions test/integration/targets/mysql_db/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
# defaults file for test_mysql_db
db_name: 'data'
db_name2: 'data2'
db_user1: 'datauser1'
db_user2: 'datauser2'

Expand Down
6 changes: 3 additions & 3 deletions test/integration/targets/mysql_db/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@
assert: { that: "'{{ db_user1 }}' not in result.stdout" }

# ============================================================
- include: state_dump_import.yml format_type=sql file=dbdata.sql format_msg_type=ASCII
- include: state_dump_import.yml format_type=sql file=dbdata.sql format_msg_type=ASCII file2=dump2.sql file3=dump3.sql

- include: state_dump_import.yml format_type=gz file=dbdata.gz format_msg_type=gzip
- include: state_dump_import.yml format_type=gz file=dbdata.gz format_msg_type=gzip file2=dump2.gz file3=dump3.gz

- include: state_dump_import.yml format_type=bz2 file=dbdata.bz2 format_msg_type=bzip2
- include: state_dump_import.yml format_type=bz2 file=dbdata.bz2 format_msg_type=bzip2 file2=dump2.bz2 file3=dump3.bz2
Loading