#!/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 from datetime import datetime, timedelta import time BACKUP_AGE = 30 DOCKER_SOCK = 'unix://var/run/docker.sock' POSTGRES_CONTAINER = 'postgres' POSTGRES_USER = 'postgres' ODOO_IMAGE = 'odoo/robert:8.0' ODOO_PATH = '/opt/odoo' ''' ''' 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 False try: result = dbx.files_list_folder('') date_now = datetime.now() for folder in result.entries: create_date = datetime.strptime(folder.name, '%Y_%m_%d') if create_date <= date_now - timedelta(BACKUP_AGE): # dbx.files_delete(folder.path_lower) dbx.files_permanently_delete(folder.path_lower) return True except ApiError: return False ''' ''' 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_v2(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 create_odoo_filestore_backup(): pass ''' ''' 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 return True ''' ''' def run_backup(): # 1. get connection dbx = get_dropbox_connection() # 2. delete old folders delete_dropbox_old_folder(dbx) # 4. create folder name folder_path = create_folder_path() # 4. create dropbox folder create_dropbox_folder(folder_path, dbx) # 5. get docker client docker_client = get_docker_client() # 6. list database databases = list_postgres_databases(docker_client) # 7. filter databases by active containers databases = filter_databases_by_active_containers(databases, docker_client) # 8. 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()