def __str__(self): # Returns the first line if self.args and self.args[0]: from frabit.utils import force_str return force_str(self.args[0]).splitlines()[0].strip() else: return ''
def has_backup_privileges(self): """ Returns true if current user has efficient privileges,include below: super : processlist: replicate client: replicate slave : """ privileges_info = """ SELECT concat(Select_priv ,Insert_priv ,Update_priv ,Delete_priv ,Create_priv ,Drop_priv ,Reload_priv ,Shutdown_priv ,Process_priv ,File_priv ,Grant_priv ,References_priv ,Index_priv ,Alter_priv ,Show_db_priv ,Super_priv ,Lock_tables_priv ,Execute_priv ,Repl_slave_priv ,Repl_client_priv ,Create_view_priv ,Show_view_priv ,Create_routine_priv ,Alter_routine_priv ,Create_user_priv ,Event_priv ,Trigger_priv ) FROM mysql.user WHERE user = CURRENT_USER AND host = CURRENT_HOST """ try: cur = self._cursor() cur.execute(privileges_info) return cur.fetchone()[0] except (MysqlInterfaceError, MysqlException) as e: _logger.debug( "Error checking privileges needed for backups: {}".format( force_str(e).strip())) return None
def connect(self): """ Generic function for MySQL connection (using mysql-connector-python) """ if not self._check_connection(): try: self._conn = mysql.connector.connect(self.conn_parameters) # If mysql-connector-python fails to connect to the host, raise the appropriate exception except connector.PoolError as e: raise MysqlConnectError(force_str(e).strip()) # Register the connection to the list of live connections _live_connections.append(self) return self._conn
def decompress(self, src, dst): """ Decompress using the object defined in the sublcass :param src: source file to decompress :param dst: destination of the decompression """ try: with closing(self._decompressor(src)) as istream: with open(dst, 'wb') as ostream: shutil.copyfileobj(istream, ostream) except Exception as e: # you won't get more information from the compressors anyway raise CommandFailedException( dict(ret=None, err=force_str(e), out=None)) return 0
def current_size(self): """ Returns the total size of the MySQL server """ if not self.has_backup_privileges: return None try: cur = self._cursor() cur.execute("SELECT sum(pg_tablespace_size(oid)) " "FROM pg_tablespace") return cur.fetchone()[0] except (MysqlInterfaceError, MysqlException) as e: _logger.debug("Error retrieving MySQL total size: %s", force_str(e).strip()) return None
def list_files(args): """ List all the files for a single backup """ server = get_server(args) # Retrieves the backup backup_info = parse_backup_id(server, args) try: for line in backup_info.get_list_of_files(args.target): output.info(line, log=False) except BinlogHasPurged as e: output.error( "invalid xlog segment name %r\n" "HINT: Please run \"barman rebuild-xlogdb %s\" " "to solve this issue", force_str(e), server.config.name) output.close_and_exit()
def server_txt_version(self): """ Human readable version of MySQL (returned by the server) """ try: cur = self._cursor() cur.execute("SELECT version()") info = cur.fetchall()[0][0].split("-")[0] version = { "major": info.split(".")[0], "minor": info.split(".")[1], "patch": info.split(".")[2] } return version except (MysqlInterfaceError, MysqlException) as e: _logger.debug("Error retrieving MySQL version: {}".format( force_str(e).strip())) return None
def current_binlog_info(self): """ Get detailed information about the current Binlog position in MySQL. This method returns a dictionary containing the following data: * file_name * position * gtid :rtype: dict """ try: cur = self._cursor() cur.execute("SHOW MASTER STATUS;") return cur.fetchone() except (MysqlInterfaceError, MysqlException) as e: _logger.debug( "Error retrieving current binlog detailed information: {}". format(force_str(e).strip())) return None
def from_command_error(cls, cmd, e, msg): """ This method build a DataTransferFailure exception and report the provided message to the user (both console and log file) along with the output of the failed command. :param str cmd: The command that failed the transfer :param CommandFailedException e: The exception we are handling :param str msg: a descriptive message on what we are trying to do :return DataTransferFailure: will contain the message provided in msg """ try: details = msg details += "\n%s error:\n" % cmd details += e.args[0]['out'] details += e.args[0]['err'] return cls(details) except (TypeError, NameError): # If it is not a dictionary just convert it to a string from frabit.utils import force_str return cls(force_str(e.args))
def get_status(self, name, is_global=True): """ Get MySQL global or session status :rtype: str """ cur = self._cursor() if is_global: pass try: cur.execute( "SELECT name, setting FROM pg_settings " "WHERE name IN ('config_file', 'hba_file', 'ident_file')") for cname, cpath in cur.fetchall(): self.configuration_files[cname] = cpath except (MysqlInterfaceError, MysqlException) as e: _logger.debug( "Error retrieving MySQL configuration files " "location: %s", force_str(e).strip()) self.configuration_files = {} return self.configuration_files
def get_replication_stats(self): """ Returns replication information dict """ try: cur = self._cursor() if not self.has_backup_privileges: raise BackupInefficientPrivilege() ''' Slave_IO_State: Master_Host: Master_User: Master_Port: Connect_Retry: Master_Log_File: Read_Master_Log_Pos: Relay_Log_File: Relay_Log_Pos: Relay_Master_Log_File: Slave_IO_Running: Slave_SQL_Running: Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: Last_Error: Skip_Counter: Exec_Master_Log_Pos: Relay_Log_Space: Until_Condition: Until_Log_File: Until_Log_Pos: Master_SSL_Allowed: Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: Master_SSL_Verify_Server_Cert: Last_IO_Errno: Last_IO_Error: Last_SQL_Errno: Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: Master_UUID: Master_Info_File: SQL_Delay: SQL_Remaining_Delay: Slave_SQL_Running_State: Master_Retry_Count: Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: ''' # Execute the query cur.execute("".format()) # Generate a list of standby objects return cur.fetchall() except (MysqlInterfaceError, MysqlException) as e: _logger.debug( "Error retrieving status of replica servers: {}".format( force_str(e).strip())) return None
def recover(args): """ Recover a server at a given time, name, LSN or xid """ server = get_server(args) # Retrieves the backup backup_id = parse_backup_id(server, args) if backup_id.status not in BackupInfo.STATUS_COPY_DONE: output.error("Cannot recover from backup '{id}' of server '{name}': " "backup status is not DONE".format( id=args.backup_id, name=server.config.name)) output.close_and_exit() # decode the tablespace relocation rules tablespaces = {} if args.tablespace: for rule in args.tablespace: try: tablespaces.update([rule.split(':', 1)]) except ValueError: output.error( "Invalid tablespace relocation rule '%s'\n" "HINT: The valid syntax for a relocation rule is " "NAME:LOCATION", rule) output.close_and_exit() # validate the rules against the tablespace list valid_tablespaces = [] if backup_id.tablespaces: valid_tablespaces = [ tablespace_data.name for tablespace_data in backup_id.tablespaces ] for item in tablespaces: if item not in valid_tablespaces: output.error( "Invalid tablespace name '%s'\n" "HINT: Please use any of the following " "tablespaces: %s", item, ', '.join(valid_tablespaces)) output.close_and_exit() # explicitly disallow the rsync remote syntax (common mistake) if ':' in args.destination_directory: output.error( "The destination directory parameter " "cannot contain the ':' character\n" "HINT: If you want to do a remote recovery you have to use " "the --remote-ssh-command option") output.close_and_exit() if args.retry_sleep is not None: server.config.basebackup_retry_sleep = args.retry_sleep if args.retry_times is not None: server.config.basebackup_retry_times = args.retry_times if hasattr(args, 'get_wal'): if args.get_wal: server.config.recovery_options.add(RecoveryOptions.GET_WAL) else: server.config.recovery_options.remove(RecoveryOptions.GET_WAL) if args.jobs is not None: server.config.parallel_jobs = args.jobs if hasattr(args, 'bwlimit'): server.config.bandwidth_limit = args.bwlimit target_options = [ 'target_tli', 'target_time', 'target_xid', 'target_lsn', 'target_name', 'target_immediate' ] specified_target_options = len( [option for option in target_options if getattr(args, option)]) if specified_target_options > 1: output.error( "You cannot specify multiple targets for the recovery operation") output.close_and_exit() if hasattr(args, 'network_compression'): if args.network_compression and args.remote_ssh_command is None: output.error("Network compression can only be used with " "remote recovery.\n" "HINT: If you want to do a remote recovery " "you have to use the --remote-ssh-command option") output.close_and_exit() server.config.network_compression = args.network_compression with closing(server): try: server.recover(backup_id, args.destination_directory, tablespaces=tablespaces, target_tli=args.target_tli, target_time=args.target_time, target_xid=args.target_xid, target_lsn=args.target_lsn, target_name=args.target_name, target_immediate=args.target_immediate, exclusive=args.exclusive, remote_command=args.remote_ssh_command, target_action=getattr(args, 'target_action', None), standby_mode=getattr(args, 'standby_mode', None)) except RecoveryException as exc: output.error(force_str(exc)) output.close_and_exit()