backup.py 5.5 KB

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