def __init__(self, server1, user, passwd=None, verbosity=0): """Constructor :param server1: Server instance :type server1: Server :param user: MySQL user account name (user@host). :type user: string :param passwd: Password for the user. By default, None (no password). :type passwd: string :param verbosity: Verbosity level to log extra information during operations. By default, 0 (no extra information logged). :type verbosity: integer """ self.server1 = server1 self.sql_mode = '' self.user, self.host = parse_user_host(user) self.host = clean_IPv6(self.host) self.passwd = passwd self.verbosity = verbosity self.current_user = None self.grant_dict = None self.global_grant_dict = None self.grant_list = None self.global_grant_list = None self.query_options = { 'fetch': False }
def get_grants(self, globals_privs=False, as_dict=False, refresh=False): """Retrieve the grants for the current user :param globals_privs: Include global privileges in clone (i.e. user@%) :type globals_privs: boolean :param as_dict: If True, instead of a list of plain grant strings, return a dictionary with the grants. :type as_dict: boolean :param refresh: If True, reads grant privileges directly from the server and updates cached values, otherwise uses the cached values. :type refresh: boolean :return: result set or None if no grants defined :rtype: List, dict or None """ # only read values from server if needed if refresh or not self.grant_list or not self.global_grant_list: # Get the users' connection user@host if not retrieved if self.current_user is None: res = self.server1.exec_query("SELECT CURRENT_USER()") parts = res[0][0].split('@') # If we're connected as some other user, use the user@host # defined at instantiation if parts[0] != self.user: host = clean_IPv6(self.host) self.current_user = "******" % (self.user, host) else: self.current_user = "******" % (parts[0], parts[1]) grants = [] try: res = self.server1.exec_query("SHOW GRANTS FOR " "{0}".format(self.current_user)) for grant in res: grants.append(grant) except GadgetQueryError: pass # Error here is ok - no grants found. # Cache user grants self.grant_list = grants[:] self.sql_mode = self.server1.select_variable("SQL_MODE") self.grant_dict = User._get_grants_as_dict(self.grant_list, self.verbosity, self.sql_mode) # If current user is already using global host wildcard '%', there # is no need to run the show grants again. if globals_privs: if self.host != '%': try: res = self.server1.exec_query( "SHOW GRANTS FOR '{0}'{1}".format(self.user, "@'%'")) for grant in res: grants.append(grant) self.global_grant_list = grants[:] self.global_grant_dict = User._get_grants_as_dict( self.global_grant_list, self.verbosity) except GadgetQueryError: # User has no global privs, return the just the ones # for current host self.global_grant_list = self.grant_list self.global_grant_dict = self.grant_dict else: # if host is % then we already have the global privs self.global_grant_list = self.grant_list self.global_grant_dict = self.grant_dict if globals_privs: if as_dict: return self.global_grant_dict else: return self.global_grant_list else: if as_dict: return self.grant_dict else: return self.grant_list
def test_clean_IPv6(self): """Test clean_IPv6 function. """ self.assertEqual(clean_IPv6("[2001:db8::ff00:42:8329]"), "2001:db8::ff00:42:8329")
def resolve_gr_local_address(gr_host, server_host, server_port): """Resolves Group Replication local address (host, port). If a host is not found on gr_host, the returned host is the one given on server_host. If a port is not found on gr_host, the returned port is the one given on server_port plus 10000, unless the result is higher than 65535, in that case a random port number will be generated. :param gr_host: Group replication host address in the format: <host>[:<port>] (i.e., host or host and port separated by ':'). :type gr_host: string :param server_host: The host where the MySQL server is running. :type server_host: string :param server_port: The port that the MySQL server is using. :type server_port: string :raise GadgetError: If could not found a free port. :return: A tuple with host and port. :rtype: tuple """ # No info provided, use the server to generate it. if gr_host is None or gr_host == "": gr_host = server_host local_port = str(int(server_port) + 10000) # gr_host can have both elements; host and port, but be aware of IPv6 elif len(gr_host.rsplit(":", 1)) == 2 and gr_host[-1] != "]": gr_host, local_port = gr_host.rsplit(":", 1) if not gr_host: gr_host = server_host if not local_port: local_port = str(int(server_port) + 10000) elif not local_port.isdigit(): gr_host = "{0}:{1}".format(gr_host, local_port) local_port = str(int(server_port) + 10000) # Try to get the port only elif gr_host.isdigit(): local_port = gr_host gr_host = server_host # Generate a local port based on the + 10000 rule. else: local_port = str(int(server_port) + 10000) # in case the gr_host is a IPv6 remove the square brackets '[ ]' gr_host = clean_IPv6(gr_host) # Generate a random port if out of range. if int(local_port) < 0 or int(local_port) > 65535: local_port = str(random.randint(10000, 65535)) # gr_host is host address # verify the port is not in use. tries = 1 port_found = False while tries < 5 and not port_found: tries += 1 if not is_listening(server_host, int(local_port)): port_found = True else: local_port = str(random.randint(10000, 65535)) if not port_found: raise GadgetError("Unable to find an available port on which the " "member will expose itself to be contacted by the " "other members of the group. Please try again to " "attempt to use another random port or free port " "{0}.".format(str(int(server_port) + 10000))) return gr_host, local_port
def resolve_gr_local_address(gr_host, server_host, server_port): """Resolves Group Replication local address (host, port). If a host is not found on gr_host, the returned host is the one given on server_host. If a port is not found on gr_host, the returned port is the one given on server_port * 10 + 1, unless the result is higher than 65535, in that case a random port number will be generated. :param gr_host: Group replication host address in the format: <host>[:<port>] (i.e., host or host and port separated by ':'). :type gr_host: string :param server_host: The host where the MySQL server is running. :type server_host: string :param server_port: The port that the MySQL server is using. :type server_port: string :raise GadgetError: If the local address port is not valid or not free. :return: A tuple with host and port. :rtype: tuple """ is_port_specified = False # No info provided, use the server to generate it. if gr_host is None or gr_host == "": gr_host = server_host local_port = str(int(server_port) * 10 + 1) # gr_host can have both elements; host and port, but be aware of IPv6 elif len(gr_host.rsplit(":", 1)) == 2 and gr_host[-1] != "]": gr_host, local_port = gr_host.rsplit(":", 1) if not gr_host: gr_host = server_host if not local_port: local_port = str(int(server_port) * 10 + 1) elif (not local_port.isdigit() or int(local_port) <= 0 or int(local_port) > 65535): # Raise an error if the specified port part is invalid. raise GadgetError( _ERROR_INVALID_LOCAL_ADDRESS_PORT.format(local_port)) # Try to get the port only elif gr_host.isdigit(): local_port = gr_host gr_host = server_host # Raise an error if the specified port is invalid (out of range). if int(local_port) <= 0 or int(local_port) > 65535: raise GadgetError( _ERROR_INVALID_LOCAL_ADDRESS_PORT.format(local_port)) # Generate a local port based on the * 10 + 1 rule. else: local_port = str(int(server_port) * 10 + 1) # in case the gr_host is a IPv6 remove the square brackets '[ ]' gr_host = clean_IPv6(gr_host) # Generate a random port if out of range. if int(local_port) <= 0 or int(local_port) > 65535: local_port = str(random.randint(10000, 65535)) # gr_host is host address # verify the port is not in use. if is_listening(server_host, int(local_port)): raise GadgetError( _ERROR_LOCAL_ADDRESS_PORT_IN_USE.format(local_port)) return gr_host, local_port