#!/usr/bin/python # -*- coding: utf-8 -*- import os import dropbox import docker from dropbox.files import WriteMode from dropbox.exceptions import ApiError, AuthError from docker.errors import NotFound, APIError from io import BytesIO import time BACKUP_AGE = 30 DOCKER_SOCK = 'unix://var/run/docker.sock' POSTGRES_CONTAINER = 'postgres' POSTGRES_USER = 'postgres' ODOO_IMAGE = 'odoo/robert:8.0' ''' ''' def get_dropbox_connection(): token_file = open('token', 'r') token_str = token_file.read() token_file.close() dbx = dropbox.Dropbox(token_str) try: dbx.users_get_current_account() return dbx except AuthError: return None ''' ''' def delete_dropbox_old_folder(dbx=None): if dbx == None: return ''' ''' def create_folder_path(): return '/' + time.strftime('%Y_%m_%d') ''' ''' def create_dropbox_folder(folder_path, dbx=None): if dbx == None: return False try: result = dbx.files_search('', folder_path) if len(result.matches) > 0: return False dbx.files_create_folder(folder_path) return True except ApiError: return False ''' ''' def get_docker_client(): return docker.DockerClient(base_url=DOCKER_SOCK) ''' ''' def get_pg_container(docker_client): try: pg_container = docker_client.containers.get(POSTGRES_CONTAINER) return pg_container except (NotFound, APIError): return None ''' ''' def list_postgres_databases(docker_client): pg_container = get_pg_container(docker_client) if pg_container is None or pg_container.status == 'exited': return [] command = "psql -U %s -t -c 'SELECT datname FROM pg_database'" % POSTGRES_USER result = pg_container.exec_run(command) if result.exit_code == -1: return [] output = result.output.split('\n') output = map(lambda x: x.strip(), output) output = filter(lambda x: x != '', output) BLACK_LIST = ['postgres', 'template1', 'template0'] output = filter(lambda x: x not in BLACK_LIST, output) return output ''' ''' def filter_databases_by_active_containers(databases, docker_client): try: containers = docker_client.containers.list(filters={'status': 'running', 'ancestor': ODOO_IMAGE}) containers_name = map(lambda x: x.name, containers) return filter(lambda x: x in containers_name, databases) except APIError: return [] ''' ''' def create_postgres_backup(database, docker_client): pg_container = get_pg_container(docker_client) if pg_container is None or pg_container.status == 'exited': return (False, None) tmp_file = '%s_%s.tar' % (database, time.strftime('%Y-%m-%d_%H:%M:%S')) command = 'pg_dump -U %s -d %s -F tar -C -b -c -f %s' % (POSTGRES_USER, database, tmp_file) result = pg_container.exec_run(command) if result.exit_code == -1: (False, tmp_file) return (True, tmp_file) ''' ''' def upload_to_dropbox(backup_file_name, backup_path, docket_client, dbx): pg_container = get_pg_container(docket_client) if pg_container is None or pg_container.status == 'exited': return False try: (backup_file, _) = pg_container.get_archive('/%s' % backup_file_name) raw_data = BytesIO() for chunk in backup_file: raw_data.write(chunk) raw_data.seek(0) remote_path = ('%s/%s') % (backup_path, backup_file_name) dbx.files_upload(raw_data.read(), remote_path, mode=WriteMode('overwrite')) raw_data.close() return True except (APIError, ApiError): return False ''' ''' def delete_backup_file(backup_name, docker_client): pg_container = get_pg_container(docker_client) if pg_container is None or pg_container.status == 'exited': return False command = 'rm %s' % backup_name result = pg_container.exec_run(command) if result.exit_code == -1: return False print(result.output) return True ''' ''' def run_backup(): # 1. get connection dbx = get_dropbox_connection() # 2. create folder name folder_path = create_folder_path() # 3. create dropbox folder create_dropbox_folder(folder_path, dbx) # 4. get docker client docker_client = get_docker_client() # 5. list database databases = list_postgres_databases(docker_client) # 6. filter databases by active containers databases = filter_databases_by_active_containers(databases, docker_client) # 7. backup databases for db in databases: (backup_ok, backup_name) = create_postgres_backup(db, docker_client) if not backup_ok: if backup_name: delete_backup_file(backup_name, docker_client) continue upload_to_dropbox(backup_name, folder_path, docker_client, dbx) delete_backup_file(backup_name, docker_client) time.sleep(1) docker_client.close() run_backup()