Projet

Général

Profil

0001-ctl-add-dump-in-backup-command-if-postgresql-is-true.patch

Jean-Baptiste Jaillet, 26 juillet 2017 16:12

Télécharger (8,35 ko)

Voir les différences:

Subject: [PATCH] ctl: add dump in backup command if postgresql is true
 (#17726)

 tests/test_ctl.py |  40 ++++++++++++++++++
 wcs/ctl/backup.py | 120 +++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 141 insertions(+), 19 deletions(-)
tests/test_ctl.py
4 4
from email.mime.text import MIMEText
5 5
from email.mime.multipart import MIMEMultipart
6 6
import psycopg2
7
import shutil
8
import tarfile
7 9

  
8 10
from wcs.formdef import FormDef
9 11
from wcs.workflows import Workflow
......
15 17
from wcs.ctl.wipe_data import CmdWipeData
16 18
from wcs.ctl.trigger_jumps import select_and_jump_formdata
17 19
from wcs.ctl.delete_tenant import CmdDeleteTenant
20
from wcs.ctl.backup import CmdBackup
18 21
from wcs.sql import get_connection_and_cursor, cleanup_connection
19 22

  
20 23
from utilities import create_temporary_pub, clean_temporary_pub
......
324 327
        assert False
325 328

  
326 329
    clean_temporary_pub()
330

  
331

  
332
def test_backup_tenant(tmpdir_factory):
333
    pub = create_temporary_pub(sql_mode=True)
334
    backup_cmd = CmdBackup()
335
    test_dir = tmpdir_factory.mktemp('test').strpath
336

  
337
    sub_options_class = collections.namedtuple('Options', ['destination'])
338
    sub_options = sub_options_class(test_dir)
339

  
340
    backup_cmd.backup_instance(pub, sub_options, ['example.test.com'])
341

  
342
    tar = [tar for tar in os.listdir(test_dir) if tar.endswith('.tar.gz')]
343
    assert tar
344

  
345
    with tarfile.open(os.path.join(test_dir, tar[0]), 'r:gz') as tar_file:
346
        tar_list = tar_file.getnames()
347

  
348
    assert [dump for dump in tar_list if dump.endswith('.sql')]
349

  
350
    clean_temporary_pub()
351

  
352
    pub = create_temporary_pub()
353
    test_dir = tmpdir_factory.mktemp('test2').strpath
354

  
355
    sub_options = sub_options_class(test_dir)
356
    backup_cmd.backup_instance(pub, sub_options, ['example.test.com'])
357

  
358
    tar = [t for t in os.listdir(test_dir) if t.endswith('.tar.gz')]
359
    assert tar
360
    with tarfile.open(os.path.join(test_dir, tar[0]), 'r:gz') as tar_file:
361
        tar_list = tar_file.getnames()
362

  
363
    assert tar_list
364
    assert not [dump for dump in tar_list if dump.endswith('.sql')]
365

  
366
    clean_temporary_pub()
wcs/ctl/backup.py
15 15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
import tarfile
18
import time
19 18
import os
19
import sys
20
import shutil
21
import subprocess
22
import tempfile
23
import datetime
20 24

  
21 25
from qommon.ctl import Command, make_option
22 26

  
23 27

  
28
class CmdError(Exception):
29
    pass
30

  
31

  
24 32
class CmdBackup(Command):
25 33
    name = 'backup'
26 34

  
27 35
    def __init__(self):
28 36
        Command.__init__(self, [
29
                make_option('--file', metavar='FILENAME', action='store',
30
                            dest='filename', default=None)
37
                make_option('--destination', action='store',
38
                            dest='destination', default=None)
31 39
                ])
32 40

  
33 41
    def execute(self, base_options, sub_options, args):
......
42 50
        if not os.path.exists(pub.app_dir):
43 51
            return 1
44 52

  
45
        if sub_options.filename:
46
            backup_filepath = sub_options.filename
53
        pub.set_config()
54
        try:
55
            self.backup_instance(pub, sub_options, args)
56
        except CmdError as e:
57
            print >> sys.stderr, str(e)
58
            return 1
59

  
60
    def backup_instance(self, pub, sub_options, args):
61
        instance_name = args[0].replace('.', '_').replace('-', '_')
62
        file_name = 'wcs__%s__%s' % (instance_name,
63
                                     datetime.date.today().isoformat())
64
        dest_dir = sub_options.destination
65
        if dest_dir:
66
            if not os.path.exists(dest_dir):
67
                raise CmdError('destination directory does not exist')
47 68
        else:
48
            backup_dir = os.path.join(pub.app_dir, 'backups')
49
            if not os.path.exists(backup_dir):
50
                os.mkdir(backup_dir)
51
            backup_filepath = os.path.join(backup_dir,
52
                            'backup-%s%s%s-%s%s%s.tar.gz' % time.localtime()[:6])
53

  
54
        backup = tarfile.open(backup_filepath, mode='w:gz')
55
        for basename, dirnames, filenames in os.walk(pub.app_dir):
56
            if 'backups' in dirnames: # do not recurse in backup directory
57
                idx = dirnames.index('backups')
58
                dirnames[idx:idx+1] = []
59
            for filename in filenames:
60
                backup.add(os.path.join(basename, filename),
61
                           os.path.join(basename, filename)[len(pub.app_dir):])
69
            dest_dir = os.path.join(pub.app_dir, 'backups')
70
            if not os.path.exists(dest_dir):
71
                os.mkdir(dest_dir)
72
        try:
73
            temp_dir = tempfile.mkdtemp()
74
        except IOError as e:
75
            raise CmdError('an error occurred while creating temporary directory: %s' % e.message)
76

  
77
        try:
78
            dump_path = ''
79
            if pub.is_using_postgresql():
80
                dump_path = self.backup_postgresql(pub, file_name, temp_dir)
81

  
82
            instance_tar_path = self.backup_instance_dir(pub, file_name, temp_dir, dump_path)
83
            try:
84
                shutil.move(instance_tar_path, dest_dir)
85
            except IOError as e:
86
                raise CmdError('could not move instance tar %s to %s: %s' % (instance_tar_path,
87
                                                                                 dest_dir,
88
                                                                                 e.message))
89
        finally:
90
            try:
91
                shutil.rmtree(temp_dir)
92
            except IOError as e:
93
                print 'could not remove temporary directory %s: %s' % (temp_dir, e.message)
94

  
95

  
96
    def backup_instance_dir(self, pub, file_name, temp_dir, dump_path=''):
97
        backup_filepath = os.path.join(temp_dir, '%s.tar.gz' % file_name)
98
        try:
99
            backup = tarfile.open(backup_filepath, mode='w:gz')
100

  
101
            for basename, dirnames, filenames in os.walk(pub.app_dir):
102
                if 'backups' in dirnames: # do not recurse in backup directory
103
                    idx = dirnames.index('backups')
104
                    dirnames[idx:idx+1] = []
105
                for filename in filenames:
106
                    backup.add(os.path.join(basename, filename),
107
                               os.path.join(basename, filename)[len(pub.app_dir):])
108
            if dump_path:
109
                backup.add(dump_path, os.path.basename(dump_path))
110

  
111
        except IOError as e:
112
            raise CmdError('could not open tar file %s: %s' % (backup_filepath,
113
                                                               e.message))
114
        except tarfile.TarError:
115
            raise CmdError('an error occured while making tar archive for %s' % file_name)
62 116

  
63 117
        backup.close()
64 118

  
119
        return backup_filepath
120

  
121
    def backup_postgresql(self, pub, file_name, temp_dir):
122
        db_conf = pub.cfg['postgresql']
123
        # use 'or' as value are at None when not set
124
        db_name = db_conf.get('database') or ''
125
        db_host = db_conf.get('host') or ''
126
        db_user = db_conf.get('user') or ''
127
        db_port = db_conf.get('port') or ''
128
        db_pwd = db_conf.get('password') or ''
129
        if db_pwd:
130
            os.environ['PGPASSWORD'] = db_pwd
131

  
132
        dump_path = os.path.join(temp_dir, '%s.sql' % file_name)
133

  
134
        try:
135
            subprocess.check_call(['pg_dump', '-Oc', '-n', 'public',
136
                                   '-h', db_host, '-U', db_user,
137
                                   '-p', db_port, '-w', '-f', dump_path,
138
                                   '-d', db_name])
139
        except subprocess.CalledProcessError as e:
140
            raise CmdError('an error occured while dumping instance database %s: %s' % (db_name,
141
                                                                                        e.output))
142
        if db_pwd:
143
            os.environ.pop('PGPASSWORD')
144

  
145
        return dump_path
146

  
65 147

  
66 148
CmdBackup.register()
67
-