diff --git a/database/mysql/mysql_user.py b/database/mysql/mysql_user.py index 7b11cf8fe74..90521e759fe 100644 --- a/database/mysql/mysql_user.py +++ b/database/mysql/mysql_user.py @@ -100,7 +100,12 @@ options: version_added: "2.0" description: - C(always) will update passwords if they differ. C(on_create) will only set the password for newly created users. - + config_file: + description: + - Specify a config file from which user and password are to be read + required: false + default: null + version_added: "1.8" notes: - Requires the MySQLdb Python package on the remote host. For Ubuntu, this is as easy as apt-get install python-mysqldb. @@ -114,7 +119,7 @@ notes: the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from the file." -requirements: [ "ConfigParser", "MySQLdb" ] +requirements: [ "MySQLdb" ] author: Mark Theunissen ''' @@ -152,7 +157,6 @@ user=root password=n<_665{vS43y """ -import ConfigParser import getpass import tempfile try: @@ -179,6 +183,30 @@ class InvalidPrivsError(Exception): # MySQL module specific support methods. # +def connect(module, login_user, login_password, config_file): + default_file = '~/.my.cnf' + if config_file is not None: + default_file = config_file + + config = { + 'host': module.params['login_host'], + 'db': 'mysql' + } + + if module.params['login_unix_socket']: + config['unix_socket'] = module.params['login_unix_socket'] + else: + config['port'] = module.params['login_port'] + + if os.path.exists(default_file): + config['read_default_file'] = default_file + else: + config['user'] = login_user + config['passwd'] = login_password + + db_connection = MySQLdb.connect(**config) + return db_connection.cursor() + def user_exists(cursor, user, host): cursor.execute("SELECT count(*) FROM user WHERE user = %s AND host = %s", (user,host)) count = cursor.fetchone() @@ -341,100 +369,6 @@ def privileges_grant(cursor, user,host,db_table,priv): query = ' '.join(query) cursor.execute(query, (user, host)) -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 _safe_cnf_load(config, path): - - data = {'user':'', 'password':''} - - # read in user/pass - f = open(path, 'r') - for line in f.readlines(): - line = line.strip() - if line.startswith('user='): - data['user'] = line.split('=', 1)[1].strip() - if line.startswith('password=') or line.startswith('pass='): - data['password'] = line.split('=', 1)[1].strip() - f.close() - - # write out a new cnf file with only user/pass - fh, newpath = tempfile.mkstemp(prefix=path + '.') - f = open(newpath, 'wb') - f.write('[client]\n') - f.write('user=%s\n' % data['user']) - f.write('password=%s\n' % data['password']) - f.close() - - config.readfp(open(newpath)) - os.remove(newpath) - return config - -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 - except: - config = _safe_cnf_load(config, mycnf) - - # 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 connect(module, login_user, login_password): - 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="mysql") - else: - db_connection = MySQLdb.connect(host=module.params["login_host"], port=module.params["login_port"], user=login_user, passwd=login_password, db="mysql") - return db_connection.cursor() - # =========================================== # Module execution. # @@ -455,14 +389,18 @@ def main(): append_privs=dict(type="bool", default="no"), check_implicit_admin=dict(default=False), update_password=dict(default="always", choices=["always", "on_create"]), + config_file=dict(default=None), ) ) + login_user = module.params["login_user"] + login_password = module.params["login_password"] user = module.params["user"] password = module.params["password"] host = module.params["host"] state = module.params["state"] priv = module.params["priv"] check_implicit_admin = module.params['check_implicit_admin'] + config_file = module.params['config_file'] append_privs = module.boolean(module.params["append_privs"]) update_password = module.params['update_password'] @@ -475,32 +413,16 @@ def main(): except Exception, e: module.fail_json(msg="invalid privileges string: %s" % str(e)) - # 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") - cursor = None try: if check_implicit_admin: try: - cursor = connect(module, 'root', '') + cursor = connect(module, 'root', '', config_file) except: pass if not cursor: - cursor = connect(module, login_user, login_password) + cursor = connect(module, login_user, login_password, config_file) except Exception, e: module.fail_json(msg="unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials")