Advertisement
Guest User

Untitled

a guest
May 21st, 2018
206
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.77 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. import os
  4. import subprocess
  5. import sys
  6. import datetime
  7. import shutil
  8. from datetime import datetime, timedelta
  9.  
  10. import glob
  11. import string
  12. import re
  13. import shlex
  14.  
  15. import smtplib
  16. import email
  17.  
  18. #basic config - please do not change
  19. HOSTNAME = os.uname()[1]
  20. LOCKDIR = "/tmp/rn-innobackup.lock" #LOCKDIR
  21. LOGFILE = "/var/log/rn-mysqlbackup.log" #LOGFILE
  22. INNOBACKUPEX = "/usr/bin/innobackupex"
  23. ALERTMAIL = "mmaros@reflected.net" #to use multiple recepients, this needs to be turned into a list ["email1@bla.com", "email2@bla.com", "email3@bla.com", ...]
  24. #ALERTMAIL = "support@reflected.net" #to use multiple recepients, this needs to be turned into a list ["email1@bla.com", "email2@bla.com", "email3@bla.com", ...]
  25.  
  26.  
  27. #backup configuration
  28. #to use one binary over other, please put it at the start of the list, if it doesnt exist it will get skipped
  29. COMPRESS = ["/usr/bin/pbzip2","/usr/local/sbin/pbzip2","/usr/bin/pigz","/usr/local/sbin/pigz"]
  30. BACKUPDIR = '/home/backups/mysql/'
  31. RETENTION =int(7)
  32. STREAMING = False
  33. PARALEL = "4"
  34. USEMEMORY = "1G"
  35. CUSTOMOPT = "" #separate them with a space between, use syntax same as on the cli "--option=value --option2=value"
  36. COMPRESSOPT = ""
  37.  
  38.  
  39.  
  40. ########Bunch of checks that need to be ran before we proceed
  41. ########
  42. #check which compression to use
  43. compression = ""
  44. for i in range(0, len(COMPRESS)):
  45. if os.path.isfile(COMPRESS[i]):
  46. compression = COMPRESS[i]
  47. break
  48. #exit if compression is not set up
  49. if compression == "":
  50. print ("none of compressions are available, please check which ones are available and which ones are provided")
  51. sys.exit()
  52.  
  53.  
  54. #compression being used:
  55. if ("pbzip2" in compression):
  56. backupSuffix = ".tar.bz2"
  57. elif ("pigz" in compression):
  58. backupSuffix = ".tar.gz"
  59. else:
  60. print ("wrong compression")
  61. sys.exit()
  62.  
  63. #set backupname
  64. createDate = datetime.today().strftime("%Y%m%d-%H%M%S")
  65. if STREAMING:
  66. createName = "mysql_backups_" + HOSTNAME + "_" + createDate + "-streaming"
  67. createNameCompressed = "mysql_backups_" + HOSTNAME + "_" + createDate + "-streaming" + backupSuffix
  68. else:
  69. createName = "mysql_backups_" + HOSTNAME + "_" + createDate
  70. createNameCompressed = "mysql_backups_" + HOSTNAME + "_" + createDate + backupSuffix
  71.  
  72. #check the mysql datadir
  73. commandToRun = "mysql -e 'select @@datadir;'"
  74. args = shlex.split(commandToRun)
  75. p = subprocess.Popen(args, stdout=subprocess.PIPE)
  76. DATADIR = p.stdout.read().splitlines()[-1]
  77. p.communicate()
  78. #print (output.splitlines()[-1])
  79. print (DATADIR)
  80. if os.path.isdir(DATADIR) is False:
  81. print (DATADIR + "does not exit")
  82. sys.exit()
  83.  
  84. #check if it is slave
  85. commandToRun = "mysql -e 'show slave status\G'"
  86. args = shlex.split(commandToRun)
  87. p = subprocess.Popen(args, stdout=subprocess.PIPE)
  88. ISSLAVE = p.stdout.read()
  89. p.communicate()
  90. #if the machine is a slave add --slave-info to options
  91. if ISSLAVE:
  92. CUSTOMOPT += " --no-timestamp --slave-info "
  93. else:
  94. CUSTOMOPT += " --no-timestamp "
  95. print ("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + CUSTOMOPT)
  96.  
  97.  
  98. #the fun starts here - Tuborg
  99. #skip disk check if .skipcheck is present in backupdir, remove after the run
  100. def skipCheck():
  101. if os.path.isfile(BACKUPDIR + ".skipcheck"):
  102. now = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
  103. print(now + ": Skipping prerun disk space checks and running backups. skipcheck file will be removed")
  104. #function that runs backups
  105. os.remove(BACKUPDIR + ".skipcheck")
  106. runBackup()
  107. else:
  108. isRunnable()
  109.  
  110. #check total freeDisk disk space on partition containting BACKUPDIR, in bytes
  111. def freeSpace(BACKUPDIR):
  112. st = os.statvfs(BACKUPDIR)
  113. freeDisk = int(st.f_bavail * st.f_frsize)
  114. return freeDisk
  115.  
  116. #get the size of the backup specified
  117. def listBackups(BACKUPDIR, backupSuffix):
  118. backupNameRegex = os.path.join(BACKUPDIR + 'mysql_backups_' + HOSTNAME + '_' + '*' + backupSuffix)
  119. listBackups = glob.glob(backupNameRegex)
  120. return listBackups
  121.  
  122. #checks the site of the latest backup
  123. def lastBackupSize():
  124. backupNames = listBackups(BACKUPDIR, backupSuffix)
  125. print (backupNames)
  126. if not backupNames:
  127. lastSize = 0
  128. else:
  129. lastSize = os.path.getsize(max(backupNames , key = os.path.getctime))
  130. return lastSize
  131.  
  132. #get size of the mysql datadir
  133. def dirSize(path):
  134. total_size = 0
  135. for dirpath, dirnames, filenames in os.walk(path):
  136. for f in filenames:
  137. fp = os.path.join(dirpath, f)
  138. total_size += os.path.getsize(fp)
  139. return total_size
  140.  
  141. #prerun checks
  142. def isRunnable():
  143. oldAge = 1
  144. lastSize = lastBackupSize()
  145. freeDisk = freeSpace(BACKUPDIR)
  146. #if streaming backup, we dont care about size of the datadir
  147. if STREAMING:
  148. datadirSize = 0
  149. else:
  150. datadirSize = dirSize(DATADIR)
  151. print("Size of last backup")
  152. print(lastSize)
  153. print("Free disk size")
  154. print(freeDisk)
  155. print("Datadir size")
  156. print(datadirSize)
  157. if ((freeDisk - lastSize - datadirSize) * 1.1) < (lastSize + datadirSize):
  158. msgBody = "Cant run backups as there is not enough free disk space"
  159. sendMail(msgBody)
  160. print("All gucci")
  161. runBackup()
  162.  
  163. def removeOldBackups():
  164. #get list of all current backups
  165. listAllBackups = sorted(listBackups(BACKUPDIR, backupSuffix), reverse=True)
  166. print (listAllBackups)
  167. backupsToRemove = listAllBackups[RETENTION:]
  168. print (backupsToRemove)
  169. for i in backupsToRemove:
  170. print(i)
  171. os.remove(i)
  172.  
  173. def runBackup():
  174. #how to split it
  175. #https://docs.python.org/2/library/subprocess.html
  176. #
  177. print("runBackup")
  178. if not STREAMING:
  179. commandToRun = "nice -n 19 " + INNOBACKUPEX + " " + CUSTOMOPT + "--parallel=" + PARALEL + " " + BACKUPDIR + createName
  180. args = shlex.split(commandToRun)
  181. with open("/tmp/innobackupex.stdout.log", "a+") as f_stdout, open("/tmp/innobackupex.stderr.log", "a+") as f_stderr:
  182. p = subprocess.Popen(args, stdout=f_stdout, stderr=f_stderr)
  183. p.communicate()
  184. #check if backup was successful
  185. lastline = file("/tmp/innobackupex.stderr.log", "r").readlines()[-1]
  186. if not re.search("completed OK!", lastline):
  187. msgBody = "Innobackupex failed, more information can be found in the log"
  188. sendMail(msgBody)
  189. print("innobackupex made it to the otherside")
  190. print("apply log now")
  191.  
  192. #apply log to backup
  193. commandToRun = "nice -n 19 " + INNOBACKUPEX + " " + "--apply-log " + "--use-memory=" + USEMEMORY + " " + BACKUPDIR + createName
  194. print(commandToRun)
  195. args = shlex.split(commandToRun)
  196. print(args)
  197. with open("/tmp/apply.innobackupex.stdout.log", "a+") as f_stdout, open("/tmp/apply.innobackupex.stderr.log", "a+") as f_stderr:
  198. p = subprocess.Popen(args, stdout=f_stdout, stderr=f_stderr)
  199. p.communicate()
  200.  
  201. #check if appylog was successful
  202. lastline = file("/tmp/apply.innobackupex.stderr.log", "r").readlines()[-1]
  203. if not re.search("completed OK!", lastline):
  204. msgBody = "Innobackupex failed at applying log, more information can be found in the log"
  205. sendMail(msgBody)
  206.  
  207. #compress mysql configs as well
  208. commandToRun = "tar -cz /etc/mysql/"
  209. print (commandToRun)
  210. args = shlex.split(commandToRun)
  211. print(args)
  212. with open(BACKUPDIR + createName + "/etc-mysql.tar.gz", "a+") as f_stdout, open("/tmp/tar_error.log", "a+") as f_stderr:
  213. p = subprocess.Popen(args, stdout=f_stdout, stderr=f_stderr)
  214. p.communicate()
  215.  
  216. #compress the backed up directory
  217. with open(BACKUPDIR + createNameCompressed, "a+") as f_stdout, open("/tmp/tar_error.log", "a+") as tar_stderr:
  218. tarItUp = "tar -v -c " + BACKUPDIR + createName
  219. argsTar = shlex.split(tarItUp)
  220. p1 = subprocess.Popen(argsTar, stdout=subprocess.PIPE, stderr=tar_stderr)
  221. compressItUp = "nice -n 19 " + compression + COMPRESSOPT + " -c "
  222. argsCompress = shlex.split(compressItUp)
  223. p2 = subprocess.Popen(argsCompress, stdin=p1.stdout, stdout=f_stdout, stderr=tar_stderr)
  224. p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
  225. p2.communicate()
  226. exitcodep2 = p2.returncode
  227. if exitcodep2 != 0:
  228. msgBody = "Compression of backup has failed. Please check"
  229. sendMail(msgBody)
  230. try:
  231. shutil.rmtree(BACKUPDIR + createName)
  232. except:
  233. msgBody = "Removal of the backupdir failed, please check. Tried to removed the following directory: " + BACKUPDIR + createName
  234. sendMail(msgBody)
  235. else:
  236. print("stuff")
  237. with open(BACKUPDIR + createNameCompressed, "a+") as f_stdout, open("/tmp/tar_error.log", "a+") as tar_stderr, open("/tmp/innobackupex.stderr.log", "a+") as inno_stderr:
  238. commandToRun = "nice -n 19 " + INNOBACKUPEX + " " + CUSTOMOPT + "--stream=tar" + " " + DATADIR
  239. argsCTR = shlex.split(commandToRun)
  240. print (argsCTR)
  241. print(commandToRun)
  242. p1 = subprocess.Popen(argsCTR, stdout=subprocess.PIPE, stderr=inno_stderr)
  243. compressItUp = compression + " " + COMPRESSOPT + " -c "
  244. argsCompress = shlex.split(compressItUp)
  245. p2 = subprocess.Popen(argsCompress, stdin=p1.stdout, stdout=f_stdout, stderr=tar_stderr)
  246. p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
  247. p2.communicate()
  248.  
  249. lastline = file("/tmp/innobackupex.stderr.log", "r").readlines()[-1]
  250. if not re.search("completed OK!", lastline):
  251. msgBody = "Innobackupex failed on streaming backup, more information can be found in the log"
  252. sendMail(msgBody)
  253.  
  254. removeOldBackups()
  255.  
  256.  
  257. #sendmail and exit function
  258. def sendMail(msgBody):
  259. sender = 'root@' + HOSTNAME + '.ded.reflected.net'
  260. receivers = ALERTMAIL
  261. msgBody = msgBody
  262.  
  263. #print ("this works")
  264. #message = """From: """ + sender + """\nTo: """ + receivers + """\nSubject: backup failed on """ + HOSTNAME + """\n\n""" + msgBody + """\n"""
  265.  
  266. #its still ugly, but ugly in mutliple lines
  267. message = ("From: " + sender + "\n"
  268. "To:" + receivers + "\n"
  269. "Subject: Backup failed on " + HOSTNAME + "\n\n"
  270. + msgBody + "\n")
  271.  
  272. #smtpObj = smtplib.SMTP('smtp-out.reflected.net')
  273. #smtpObj.sendmail(sender, receivers, message)
  274. print ("Successfully sent email")
  275. sys.exit()
  276.  
  277. if not os.path.exists(LOCKDIR):
  278. os.makedirs(LOCKDIR)
  279. skipCheck()
  280. else:
  281. print (LOCKDIR + " already exists, script exiting")
  282. sendMail(LOCKDIR + "already exists, backup can not start. Please check logs")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement