backup.py 4.9 KB

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