From 0c71249b030038ad362813006714561f48ac6fda Mon Sep 17 00:00:00 2001 From: Andrey Klychkov Date: Thu, 5 Dec 2019 13:44:22 +0300 Subject: [PATCH] mysql_db: add force parameter (#65547) * mysql_db: add force parameter * mysql_db: add force parameter * add changelog * add the param to state dump --- .../65547-mysql_db_add_force_param.yml | 2 + .../modules/database/mysql/mysql_db.py | 34 ++++++++++++--- .../mysql_db/tasks/state_dump_import.yml | 43 +++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 changelogs/fragments/65547-mysql_db_add_force_param.yml diff --git a/changelogs/fragments/65547-mysql_db_add_force_param.yml b/changelogs/fragments/65547-mysql_db_add_force_param.yml new file mode 100644 index 00000000000..b0a05883346 --- /dev/null +++ b/changelogs/fragments/65547-mysql_db_add_force_param.yml @@ -0,0 +1,2 @@ +minor_changes: +- mysql_db - add the ``force`` parameter (https://github.com/ansible/ansible/pull/65547). diff --git a/lib/ansible/modules/database/mysql/mysql_db.py b/lib/ansible/modules/database/mysql/mysql_db.py index 1dee7f89044..cb365f892ba 100644 --- a/lib/ansible/modules/database/mysql/mysql_db.py +++ b/lib/ansible/modules/database/mysql/mysql_db.py @@ -70,6 +70,14 @@ options: default: false type: bool version_added: "2.10" + force: + description: + - Continue dump or import even if we get an SQL error. + - Used only when I(state) is C(dump) or C(import). + required: no + type: bool + default: no + version_added: "2.10" author: "Ansible Core Team" requirements: - mysql (command line binary) @@ -105,6 +113,13 @@ EXAMPLES = r''' state: import target: /tmp/dump.sql.bz2 +- name: Restore database ignoring errors + mysql_db: + name: my_db + state: import + target: /tmp/dump.sql.bz2 + force: yes + - name: Dump multiple databases mysql_db: state: dump @@ -208,8 +223,10 @@ def db_delete(cursor, db): return True -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, ignore_tables=None, hex_blob=None, encoding=None): +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, ignore_tables=None, hex_blob=None, + encoding=None, force=False): cmd = module.get_bin_path('mysqldump', True) # If defined, mysqldump demands --defaults-extra-file be the first option if config_file: @@ -224,6 +241,8 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port, cmd += " --ssl-key=%s" % shlex_quote(ssl_key) if ssl_ca is not None: cmd += " --ssl-ca=%s" % shlex_quote(ssl_ca) + if force: + cmd += " --force" if socket is not None: cmd += " --socket=%s" % shlex_quote(socket) else: @@ -263,7 +282,7 @@ def db_dump(module, host, user, password, db_name, target, all_databases, port, 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, encoding=None): + socket=None, ssl_cert=None, ssl_key=None, ssl_ca=None, encoding=None, force=False): if not os.path.exists(target): return module.fail_json(msg="target %s does not exist on the host" % target) @@ -281,6 +300,8 @@ def db_import(module, host, user, password, db_name, target, all_databases, port cmd.append("--ssl-key=%s" % shlex_quote(ssl_key)) if ssl_ca is not None: cmd.append("--ssl-ca=%s" % shlex_quote(ssl_ca)) + if force: + cmd.append("-f") if socket is not None: cmd.append("--socket=%s" % shlex_quote(socket)) else: @@ -369,6 +390,7 @@ def main(): quick=dict(type='bool', default=True), ignore_tables=dict(type='list', default=[]), hex_blob=dict(default=False, type='bool'), + force=dict(type='bool', default=False), ), supports_check_mode=True, ) @@ -404,6 +426,7 @@ def main(): single_transaction = module.params["single_transaction"] quick = module.params["quick"] hex_blob = module.params["hex_blob"] + force = module.params["force"] if len(db) > 1 and state == 'import': module.fail_json(msg="Multiple databases are not supported with state=import") @@ -469,7 +492,8 @@ def main(): 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, hex_blob, encoding) + ssl_ca, single_transaction, quick, ignore_tables, + hex_blob, encoding, force) if rc != 0: module.fail_json(msg="%s" % stderr) module.exit_json(changed=True, db=db_name, db_list=db, msg=stdout, @@ -487,7 +511,7 @@ def main(): login_password, db, target, all_databases, login_port, config_file, - socket, ssl_cert, ssl_key, ssl_ca, encoding) + socket, ssl_cert, ssl_key, ssl_ca, encoding, force) if rc != 0: module.fail_json(msg="%s" % stderr) module.exit_json(changed=True, db=db_name, db_list=db, msg=stdout, diff --git a/test/integration/targets/mysql_db/tasks/state_dump_import.yml b/test/integration/targets/mysql_db/tasks/state_dump_import.yml index f7225cfda79..89e47bfdadf 100644 --- a/test/integration/targets/mysql_db/tasks/state_dump_import.yml +++ b/test/integration/targets/mysql_db/tasks/state_dump_import.yml @@ -19,6 +19,7 @@ # ============================================================ - set_fact: db_file_name="{{tmp_dir}}/{{file}}" + wrong_sql_file="{{tmp_dir}}/wrong.sql" dump_file1="{{tmp_dir}}/{{file2}}" dump_file2="{{tmp_dir}}/{{file3}}" @@ -63,6 +64,7 @@ ignore_tables: - "{{ db_name }}.department" login_unix_socket: '{{ mysql_socket }}' + force: yes register: result - name: assert successful completion of dump operation @@ -277,6 +279,44 @@ - "'47' in result.stdout" - "'Joe Smith' in result.stdout" +########################## +# Test ``force`` parameter +########################## + +- name: create wrong sql file + shell: echo 'CREATE TABLE hello (id int); CREATE ELBAT ehlo (int id);' >> '{{ wrong_sql_file }}' + +- name: try to import without force parameter, must fail + mysql_db: + name: '{{ db_name }}' + state: import + target: '{{ wrong_sql_file }}' + login_unix_socket: '{{ mysql_socket }}' + force: no + register: result + ignore_errors: yes + +- assert: + that: + - result.failed == true + +- name: try to import with force parameter + mysql_db: + name: '{{ db_name }}' + state: import + target: '{{ wrong_sql_file }}' + login_unix_socket: '{{ mysql_socket }}' + force: yes + register: result + +- assert: + that: + - result is changed + +########## +# Clean up +########## + - name: remove database name mysql_db: name: '{{ db_name }}' @@ -292,6 +332,9 @@ - name: remove file name file: name={{ db_file_name }} state=absent +- name: remove file name + file: name={{ wrong_sql_file }} state=absent + - name: remove dump file1 file: name={{ dump_file1 }} state=absent