Guest User

Untitled

a guest
May 24th, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.49 KB | None | 0 0
  1. # Usage:
  2. # python clean_mysql_binlogs.py --help
  3. #
  4. # For a MySQL Master server with replication turned ON,
  5. # this script performs the deletion of older (as per defined threshold)
  6. # binlog files in accordance with the status of the Slave Server replication.
  7.  
  8. from argparse import ArgumentParser
  9. import getpass
  10. import MySQLdb
  11. import shutil
  12. import os
  13. import logging
  14. from datetime import datetime
  15.  
  16.  
  17. def clean_older_binlogs(current_binlog):
  18. try:
  19. file_mtime = os.stat(current_binlog).st_mtime
  20. except OSError:
  21. LOG.error("Looks like we don't have the stated binlog. \n Is replication ON?", exc_info=True)
  22. raise
  23.  
  24. actual_oldts = file_mtime - args.OLD_DAYS * 86400
  25. LOG.info('Moving files older than %d' % actual_oldts)
  26.  
  27. deletable_files = list()
  28. for f in os.listdir(BINLOG_DIRECTORY):
  29. file_abspath = os.path.join(BINLOG_DIRECTORY, f)
  30. if os.path.isfile(file_abspath) and file_abspath != current_binlog and not file_abspath.endswith('.index'):
  31. mod_ts = os.stat(file_abspath).st_mtime
  32. if mod_ts < actual_oldts:
  33. deletable_files.append(file_abspath)
  34. LOG.info('%d older binlog files found' % len(deletable_files))
  35.  
  36. if len(deletable_files) > 0:
  37. BACKUP_DIR = os.path.join(BINLOG_DIRECTORY, 'BACKUP')
  38. if args.RETENTION_ENABLED:
  39. LOG.info('Retention enabled. Retaining current list of deletable binlogs as backup and purging older.')
  40. INTERMEDIATE_BACKUP_DIR = os.path.join(BACKUP_DIR, datetime.now().strftime('%Y%m%d'))
  41. if os.path.exists(INTERMEDIATE_BACKUP_DIR):
  42. shutil.rmtree(INTERMEDIATE_BACKUP_DIR)
  43. os.makedirs(INTERMEDIATE_BACKUP_DIR)
  44. for dfile in deletable_files:
  45. LOG.debug('Moving %s file to %s directory' % (dfile, INTERMEDIATE_BACKUP_DIR))
  46. shutil.move(dfile, INTERMEDIATE_BACKUP_DIR)
  47.  
  48. LOG.debug('Purging older backups from %s' % BACKUP_DIR)
  49. for backup_dir in os.listdir(BACKUP_DIR):
  50. backup_dir_fp = os.path.join(BACKUP_DIR, backup_dir)
  51. if backup_dir_fp != INTERMEDIATE_BACKUP_DIR:
  52. shutil.rmtree(backup_dir_fp)
  53. else:
  54. LOG.info('Retention disabled. Deleting current list of deletable binlogs and purging all available backups.')
  55. for dfile in deletable_files:
  56. LOG.debug('Deleting %s' % dfile)
  57. os.remove(dfile)
  58. shutil.rmtree(BACKUP_DIR)
  59. LOG.info('Cleanup Completed.')
  60.  
  61.  
  62. if __name__ == '__main__':
  63. argparser = ArgumentParser()
  64. mandatory_args = argparser.add_argument_group('mandatory arguments')
  65. mandatory_args.add_argument('--host',
  66. dest='SLAVE_HOST',
  67. type=str,
  68. required=True,
  69. help='IP Address / FQDN / Hostname of Slave MySQL Server')
  70. mandatory_args.add_argument('--user',
  71. dest='SLAVE_USER',
  72. type=str,
  73. required=True,
  74. help='MySQL user to connect with Slave MySQL Server')
  75. mandatory_args_password = mandatory_args.add_mutually_exclusive_group(required=True)
  76. mandatory_args_password.add_argument('-P',
  77. dest='SLAVE_PASSWORD_PROMPT',
  78. action="store_true",
  79. help='Prompt user for Password to connect with Slave MySQL Server')
  80. mandatory_args_password.add_argument('--password-file',
  81. dest='SLAVE_PASSWORD_FILE',
  82. type=str,
  83. help='Path of plain text file containing password to connect with Slave MySQL Server ')
  84. mandatory_args_password.add_argument('--password',
  85. dest='SLAVE_PASSWORD',
  86. type=str,
  87. help='Password to connect with Slave MySQL Server')
  88. mandatory_args.add_argument('--binlog-dir',
  89. dest='BINLOG_DIRECTORY',
  90. type=str,
  91. required=True,
  92. help='Binlog files directory path of Master MySQL Server')
  93. argparser.add_argument('--threshold',
  94. dest='OLD_DAYS',
  95. default=7,
  96. type=int,
  97. help='Delete Binlog files older than threshold (days). Defaults to 7 days')
  98. argparser.add_argument('--enable-retention',
  99. dest='RETENTION_ENABLED',
  100. default=True,
  101. action="store_true",
  102. help='Performs soft delete on the current deletable logs and Purges older backups')
  103.  
  104. args = argparser.parse_args()
  105.  
  106. if args.SLAVE_PASSWORD_PROMPT:
  107. PASSWORD = getpass.getpass('Enter Slave MySQL Password for %s : ' % args.SLAVE_USER)
  108. elif args.SLAVE_PASSWORD_FILE:
  109. password_file = os.path.abspath(args.SLAVE_PASSWORD_FILE)
  110. try:
  111. with open(password_file, 'r') as pf:
  112. PASSWORD = pf.read().strip()
  113. except:
  114. raise
  115. else:
  116. PASSWORD = args.SLAVE_PASSWORD
  117.  
  118. BINLOG_DIRECTORY = os.path.abspath(args.BINLOG_DIRECTORY)
  119.  
  120. logging.basicConfig(level=logging.INFO,
  121. filename=os.path.join(os.path.abspath(os.path.dirname(__file__)), 'clean_mysql_binlogs.log'),
  122. format='%(asctime)s - %(levelname)s - %(message)s')
  123. LOG = logging.getLogger(__name__)
  124.  
  125. LOG.info('Starting MySQL Binlog Cleaner for Host: %s' % args.SLAVE_HOST)
  126.  
  127. try:
  128. connection = MySQLdb.connect(args.SLAVE_HOST, args.SLAVE_USER, PASSWORD)
  129. cursor = connection.cursor(MySQLdb.cursors.DictCursor)
  130. cursor.execute('show slave status')
  131. slave_status = cursor.fetchone()
  132. if slave_status:
  133. binlog_inuse = os.path.join(BINLOG_DIRECTORY, slave_status["Relay_Master_Log_File"])
  134. LOG.info('Current Binlog file in use: %s' % binlog_inuse)
  135. clean_older_binlogs(current_binlog=binlog_inuse)
  136. else:
  137. err_message = 'Unable to get the status of the Slave Server.'
  138. LOG.error(err_message)
  139. raise Exception(err_message)
  140. except Exception as e:
  141. LOG.error(msg=e.message, exc_info=True)
  142. raise e
Add Comment
Please, Sign In to add comment