backup.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import dropbox
  5. import docker
  6. from dropbox.files import WriteMode
  7. from dropbox.exceptions import ApiError, AuthError
  8. from docker.errors import NotFound, APIError
  9. from io import BytesIO
  10. from datetime import datetime, timedelta
  11. import time
  12. BACKUP_AGE = 30
  13. DOCKER_SOCK = 'unix://var/run/docker.sock'
  14. POSTGRES_CONTAINER = 'postgres'
  15. POSTGRES_USER = 'postgres'
  16. ODOO_IMAGE = 'odoo/robert:8.0'
  17. '''
  18. '''
  19. def get_dropbox_connection():
  20. token_file = open('token', 'r')
  21. token_str = token_file.read()
  22. token_file.close()
  23. dbx = dropbox.Dropbox(token_str)
  24. try:
  25. dbx.users_get_current_account()
  26. return dbx
  27. except AuthError:
  28. return None
  29. '''
  30. '''
  31. def delete_dropbox_old_folder(dbx=None):
  32. if dbx == None:
  33. return False
  34. try:
  35. result = dbx.files_list_folder('')
  36. date_now = datetime.now()
  37. for folder in result.entries:
  38. create_date = datetime.strptime(folder.name, '%Y_%m_%d')
  39. if create_date <= date_now - timedelta(BACKUP_AGE):
  40. # dbx.files_delete(folder.path_lower)
  41. dbx.files_permanently_delete(folder.path_lower)
  42. return True
  43. except ApiError:
  44. return False
  45. '''
  46. '''
  47. def create_folder_path():
  48. return '/' + time.strftime('%Y_%m_%d')
  49. '''
  50. '''
  51. def create_dropbox_folder(folder_path, dbx=None):
  52. if dbx == None:
  53. return False
  54. try:
  55. result = dbx.files_search('', folder_path)
  56. if len(result.matches) > 0:
  57. return False
  58. dbx.files_create_folder_v2(folder_path)
  59. return True
  60. except ApiError:
  61. return False
  62. '''
  63. '''
  64. def get_docker_client():
  65. return docker.DockerClient(base_url=DOCKER_SOCK)
  66. '''
  67. '''
  68. def get_pg_container(docker_client):
  69. try:
  70. pg_container = docker_client.containers.get(POSTGRES_CONTAINER)
  71. return pg_container
  72. except (NotFound, APIError):
  73. return None
  74. '''
  75. '''
  76. def list_postgres_databases(docker_client):
  77. pg_container = get_pg_container(docker_client)
  78. if pg_container is None or pg_container.status == 'exited':
  79. return []
  80. command = "psql -U %s -t -c 'SELECT datname FROM pg_database'" % POSTGRES_USER
  81. result = pg_container.exec_run(command)
  82. if result.exit_code == -1:
  83. return []
  84. output = result.output.split('\n')
  85. output = map(lambda x: x.strip(), output)
  86. output = filter(lambda x: x != '', output)
  87. BLACK_LIST = ['postgres', 'template1', 'template0']
  88. output = filter(lambda x: x not in BLACK_LIST, output)
  89. return output
  90. '''
  91. '''
  92. def filter_databases_by_active_containers(databases, docker_client):
  93. try:
  94. containers = docker_client.containers.list(filters={'status': 'running', 'ancestor': ODOO_IMAGE})
  95. containers_name = map(lambda x: x.name, containers)
  96. return filter(lambda x: x in containers_name, databases)
  97. except APIError:
  98. return []
  99. '''
  100. '''
  101. def create_postgres_backup(database, docker_client):
  102. pg_container = get_pg_container(docker_client)
  103. if pg_container is None or pg_container.status == 'exited':
  104. return (False, None)
  105. tmp_file = '%s_%s.tar' % (database, time.strftime('%Y-%m-%d_%H:%M:%S'))
  106. command = 'pg_dump -U %s -d %s -F tar -C -b -c -f %s' % (POSTGRES_USER, database, tmp_file)
  107. result = pg_container.exec_run(command)
  108. if result.exit_code == -1:
  109. (False, tmp_file)
  110. return (True, tmp_file)
  111. '''
  112. '''
  113. def upload_to_dropbox(backup_file_name, backup_path, docket_client, dbx):
  114. pg_container = get_pg_container(docket_client)
  115. if pg_container is None or pg_container.status == 'exited':
  116. return False
  117. try:
  118. (backup_file, _) = pg_container.get_archive('/%s' % backup_file_name)
  119. raw_data = BytesIO()
  120. for chunk in backup_file:
  121. raw_data.write(chunk)
  122. raw_data.seek(0)
  123. remote_path = ('%s/%s') % (backup_path, backup_file_name)
  124. dbx.files_upload(raw_data.read(), remote_path, mode=WriteMode('overwrite'))
  125. raw_data.close()
  126. return True
  127. except (APIError, ApiError):
  128. return False
  129. '''
  130. '''
  131. def delete_backup_file(backup_name, docker_client):
  132. pg_container = get_pg_container(docker_client)
  133. if pg_container is None or pg_container.status == 'exited':
  134. return False
  135. command = 'rm %s' % backup_name
  136. result = pg_container.exec_run(command)
  137. if result.exit_code == -1:
  138. return False
  139. return True
  140. '''
  141. '''
  142. def run_backup():
  143. # 1. get connection
  144. dbx = get_dropbox_connection()
  145. # 2. delete old folders
  146. delete_dropbox_old_folder(dbx)
  147. # 4. create folder name
  148. folder_path = create_folder_path()
  149. # 4. create dropbox folder
  150. create_dropbox_folder(folder_path, dbx)
  151. # 5. get docker client
  152. docker_client = get_docker_client()
  153. # 6. list database
  154. databases = list_postgres_databases(docker_client)
  155. # 7. filter databases by active containers
  156. databases = filter_databases_by_active_containers(databases, docker_client)
  157. # 8. backup databases
  158. for db in databases:
  159. (backup_ok, backup_name) = create_postgres_backup(db, docker_client)
  160. if not backup_ok:
  161. if backup_name:
  162. delete_backup_file(backup_name, docker_client)
  163. continue
  164. upload_to_dropbox(backup_name, folder_path, docker_client, dbx)
  165. delete_backup_file(backup_name, docker_client)
  166. time.sleep(1)
  167. docker_client.close()
  168. run_backup()