From a50b661e547f4018b5ec1a5cf3a6052711eef61d Mon Sep 17 00:00:00 2001 From: Jonathan Mainguy Date: Mon, 16 Nov 2015 23:54:57 -0500 Subject: [PATCH] Unify mysql modules. Added config_file, ssl_ca, ssl_cert, ssl_key, changed connect method, added error checking where a backtrace previoussly was possible --- .../database/mysql/mysql_replication.py | 135 ++++-------------- 1 file changed, 26 insertions(+), 109 deletions(-) diff --git a/lib/ansible/modules/extras/database/mysql/mysql_replication.py b/lib/ansible/modules/extras/database/mysql/mysql_replication.py index 18df690ff0..ae7587c292 100644 --- a/lib/ansible/modules/extras/database/mysql/mysql_replication.py +++ b/lib/ansible/modules/extras/database/mysql/mysql_replication.py @@ -45,27 +45,6 @@ options: - resetslave - resetslaveall default: getslave - login_user: - description: - - username to connect mysql host, if defined login_password also needed. - required: False - login_password: - description: - - password to connect mysql host, if defined login_user also needed. - required: False - login_host: - description: - - mysql host to connect - required: False - login_port: - description: - - Port of the MySQL server. Requires login_host be defined as other then localhost if login_port is used - required: False - default: 3306 - version_added: "1.9" - login_unix_socket: - description: - - unix socket to connect mysql server master_host: description: - same as mysql variable @@ -115,6 +94,11 @@ options: master_auto_position: descrtiption: - does the host uses GTID based replication or not + required: false + default: null + version_added: "2.0" + +extends_documentation_fragment: mysql ''' EXAMPLES = ''' @@ -131,7 +115,6 @@ EXAMPLES = ''' - mysql_replication: mode=getslave login_host=ansible.example.com login_port=3308 ''' -import ConfigParser import os import warnings @@ -197,65 +180,6 @@ def changemaster(cursor, chm, chm_params): cursor.execute(query, chm_params) -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)) - 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') - except (ConfigParser.NoOptionError): - try: - passwd = config_get(config, 'client', 'pass') - except (ConfigParser.NoOptionError): - return False - - # If .my.cnf doesn't specify a user, default to user login name - try: - user = config_get(config, 'client', 'user') - except (ConfigParser.NoOptionError): - user = getpass.getuser() - creds = dict(user=user, passwd=passwd) - return creds - - def main(): module = AnsibleModule( argument_spec = dict( @@ -281,6 +205,10 @@ def main(): master_ssl_cert=dict(default=None), master_ssl_key=dict(default=None), master_ssl_cipher=dict(default=None), + config_file=dict(default="~/.my.cnf"), + ssl_cert=dict(default=None), + ssl_key=dict(default=None), + ssl_ca=dict(default=None), ) ) user = module.params["login_user"] @@ -304,42 +232,27 @@ def main(): master_ssl_key = module.params["master_ssl_key"] master_ssl_cipher = module.params["master_ssl_cipher"] master_auto_position = module.params["master_auto_position"] + ssl_cert = module.params["ssl_cert"] + ssl_key = module.params["ssl_key"] + ssl_ca = module.params["ssl_ca"] + config_file = module.params['config_file'] + config_file = os.path.expanduser(os.path.expandvars(config_file)) if not mysqldb_found: module.fail_json(msg="the python mysqldb module is required") else: warnings.filterwarnings('error', category=MySQLdb.Warning) - # 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") 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) - elif module.params["login_port"] != 3306 and module.params["login_host"] == "localhost": - module.fail_json(msg="login_host is required when login_port is defined, login_host cannot be localhost when login_port is defined") + cursor = mysql_connect(module, login_user, login_password, config_file, ssl_cert, ssl_key, ssl_ca, None, 'MySQLdb.cursors.DictCursor') + except Exception, e: + 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: - db_connection = MySQLdb.connect(host=module.params["login_host"], port=module.params["login_port"], user=login_user, passwd=login_password) - except Exception, e: - errno, errstr = e.args - module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials (%s: %s)" % (errno, errstr) ) - try: - cursor = db_connection.cursor(cursorclass=MySQLdb.cursors.DictCursor) - except Exception, e: - module.fail_json(msg="Trouble getting DictCursor from db_connection: %s" % e) + module.fail_json(msg="unable to find %s. Exception message: %s" % (config_file, e)) if mode in "getmaster": masterstatus = get_master_status(cursor) @@ -352,8 +265,8 @@ def main(): slavestatus = get_slave_status(cursor) try: module.exit_json( **slavestatus ) - except TypeError: - module.fail_json(msg="Server is not configured as mysql slave") + except TypeError, e: + module.fail_json(msg="Server is not configured as mysql slave. ERROR: %s" % e) elif mode in "changemaster": chm=[] @@ -404,7 +317,10 @@ def main(): chm_params['master_ssl_cipher'] = master_ssl_cipher if master_auto_position: chm.append("MASTER_AUTO_POSITION = 1") - changemaster(cursor, chm, chm_params) + try: + changemaster(cursor, chm, chm_params) + except Exception, e: + module.fail_json(msg='%s. Query == CHANGE MASTER TO %s' % (e, chm)) module.exit_json(changed=True) elif mode in "startslave": started = start_slave(cursor) @@ -433,5 +349,6 @@ def main(): # import module snippets from ansible.module_utils.basic import * +from ansible.module_utils.mysql import * main() warnings.simplefilter("ignore")