Skip to content

Commit 95df669

Browse files
#681 Refactored backup's, compressing is now mandatory
1 parent 3e675ad commit 95df669

21 files changed

+183
-91
lines changed

.coveragerc

+3
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ omit = ./*/__init__.py
88
./dsmr_backend/mixins.py
99
./dsmr_plugins/modules/*
1010
./dsmr_dropbox/dropboxinc/*
11+
12+
[html]
13+
directory = coverage_report/html

backups/.dummy

-1
This file was deleted.

backups/.gitkeep

Whitespace-only changes.

backups/archive/.gitkeep

Whitespace-only changes.

backups/archive/README.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Backups met alleen dagstatistieken, voor noodgevallen en tegen corrupte filesystems. Worden niet geroteerd.

dsmr_backup/admin.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class BackupSettingsAdmin(SingletonModelAdmin):
3030
),
3131
(
3232
_('Advanced'), {
33-
'fields': ['folder', 'compress'],
33+
'fields': ['folder'],
3434
}
3535
),
3636
(

dsmr_backup/backend/__init__.py

Whitespace-only changes.

dsmr_backup/management/__init__.py

Whitespace-only changes.

dsmr_backup/management/commands/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import os
2+
3+
from django.core.management.base import BaseCommand, CommandError
4+
5+
from dsmr_stats.models.statistics import DayStatistics, HourStatistics
6+
import dsmr_backup.services.backup
7+
8+
9+
class Command(BaseCommand):
10+
help = 'Forces compact (statistics) backup creation.'
11+
12+
def add_arguments(self, parser):
13+
super(Command, self).add_arguments(parser)
14+
parser.add_argument(
15+
'--full',
16+
action='store_true',
17+
dest='full',
18+
default=False,
19+
help='Whether to create a full backup'
20+
)
21+
parser.add_argument(
22+
'--compact',
23+
action='store_true',
24+
dest='compact',
25+
default=False,
26+
help='Whether to create a compact backup, only containing day- and hour statistics'
27+
)
28+
29+
def handle(self, **options):
30+
if not options.get('full') and not options.get('compact'):
31+
raise CommandError('Missing --full or --compact argument')
32+
33+
base_folder = dsmr_backup.services.backup.get_backup_directory()
34+
35+
if options.get('full'):
36+
dsmr_backup.services.backup.create_full(
37+
folder=base_folder
38+
)
39+
40+
if options.get('compact'):
41+
dsmr_backup.services.backup.create_partial(
42+
folder=os.path.join(base_folder, 'archive'),
43+
models_to_backup=(DayStatistics, HourStatistics)
44+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 2.2.4 on 2019-08-15 18:10
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('dsmr_backup', '0006_scheduled_email_backup'),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name='backupsettings',
15+
name='compress',
16+
),
17+
]

dsmr_backup/models/settings.py

-5
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ class BackupSettings(SingletonModel):
1212
verbose_name=_('Backup daily'),
1313
help_text=_('Create a backup of your data daily. Stored locally, but can be exported using Dropbox.')
1414
)
15-
compress = models.BooleanField(
16-
default=True,
17-
verbose_name=_('Compress'),
18-
help_text=_('Create backups in compressed (gzip) format, saving a significant amount of disk space.')
19-
)
2015
backup_time = models.TimeField(
2116
default=time(hour=2),
2217
verbose_name=_('Backup timestamp'),

dsmr_backup/services/backup.py

+34-25
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from django.conf import settings
1010
from django.utils import formats
1111

12-
from dsmr_stats.models.statistics import DayStatistics, HourStatistics
12+
from dsmr_stats.models.statistics import DayStatistics
1313
from dsmr_backup.models.settings import BackupSettings
1414
import dsmr_dropbox.services
1515

@@ -38,7 +38,18 @@ def check():
3838
# Postpone when the user's backup time preference has not been passed yet.
3939
return
4040

41-
create()
41+
# Create a partial, minimal backup first.
42+
create_partial(
43+
folder=os.path.join(get_backup_directory(), 'archive'),
44+
models_to_backup=(DayStatistics, )
45+
)
46+
47+
# Now create full.
48+
create_full(folder=get_backup_directory())
49+
50+
backup_settings = BackupSettings.get_solo()
51+
backup_settings.latest_backup = timezone.now()
52+
backup_settings.save()
4253

4354

4455
def get_backup_directory():
@@ -51,18 +62,18 @@ def get_backup_directory():
5162
return os.path.abspath(os.path.join(settings.BASE_DIR, '..', backup_directory))
5263

5364

54-
def create():
65+
def create_full(folder):
5566
""" Creates a backup of the database. Optionally gzipped. """
56-
backup_directory = get_backup_directory()
57-
58-
if not os.path.exists(backup_directory):
59-
os.mkdir(backup_directory)
67+
if not os.path.exists(folder):
68+
logger.info(' - Creating non-existing backup folder: %s', folder)
69+
os.makedirs(folder)
6070

6171
# Backup file with day name included, for weekly rotation.
62-
backup_file = os.path.join(backup_directory, 'dsmrreader-{}-backup-{}.sql'.format(
72+
backup_file = os.path.join(folder, 'dsmrreader-{}-backup-{}.sql'.format(
6373
connection.vendor, formats.date_format(timezone.now().date(), 'l')
6474
))
65-
logger.info(' - Creating new backup: %s', backup_file)
75+
76+
logger.info(' - Creating new full backup: %s', backup_file)
6677

6778
# PostgreSQL backup.
6879
if connection.vendor == 'postgresql': # pragma: no cover
@@ -107,35 +118,33 @@ def create():
107118
raise NotImplementedError('Unsupported backup backend: {}'.format(connection.vendor)) # pragma: no cover
108119

109120
backup_process.wait()
110-
backup_settings = BackupSettings.get_solo()
111-
logger.debug(' - Created backup: %s', backup_file)
112-
113-
if backup_settings.compress:
114-
backup_file = compress(file_path=backup_file)
115-
logger.debug(' - Compressed backup: %s', backup_file)
116-
117-
backup_settings.latest_backup = timezone.now()
118-
backup_settings.save()
121+
backup_file = compress(file_path=backup_file)
122+
logger.debug(' - Created and compressed statistics backup: %s', backup_file)
119123

120124

121-
def create_statistics_backup(folder): # pragma: no cover
122-
""" Creates a backup of the database, but only containing the day and hour statistics."""
125+
def create_partial(folder, models_to_backup): # pragma: no cover
126+
""" Creates a backup of the database, but only containing a subset specified by models."""
123127
if connection.vendor != 'postgresql':
124128
# Only PostgreSQL support for newer features.
125129
raise NotImplementedError('Unsupported backup backend: {}'.format(connection.vendor))
126130

127-
backup_file = os.path.join(folder, 'dsmrreader-{}-backup-{}.sql'.format(
128-
connection.vendor, formats.date_format(timezone.now().date(), 'l')
131+
if not os.path.exists(folder):
132+
logger.info(' - Creating non-existing backup folder: %s', folder)
133+
os.makedirs(folder)
134+
135+
backup_file = os.path.join(folder, 'dsmrreader-{}-partial-backup-{}.sql'.format(
136+
connection.vendor, formats.date_format(timezone.now().date(), 'Y-m-d')
129137
))
130138

139+
logger.info(' - Creating new partial backup: %s', backup_file)
131140
backup_process = subprocess.Popen(
132141
[
133142
settings.DSMRREADER_BACKUP_PG_DUMP,
143+
settings.DATABASES['default']['NAME'],
134144
'--host={}'.format(settings.DATABASES['default']['HOST']),
135145
'--user={}'.format(settings.DATABASES['default']['USER']),
136-
'--table={}'.format(DayStatistics._meta.db_table),
137-
'--table={}'.format(HourStatistics._meta.db_table),
138-
settings.DATABASES['default']['NAME'],
146+
] + [
147+
'--table={}'.format(x._meta.db_table) for x in models_to_backup
139148
], env={
140149
'PGPASSWORD': settings.DATABASES['default']['PASSWORD']
141150
},

dsmr_backup/services/email.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
from dsmr_backup.models.settings import EmailBackupSettings
88
from dsmr_backend.models.settings import BackendSettings, EmailSettings
9+
from dsmr_stats.models.statistics import DayStatistics, HourStatistics
910
import dsmr_backend.services.email
1011
import dsmr_backup.services.backup
1112

12-
1313
logger = logging.getLogger('commands')
1414

1515

@@ -22,7 +22,10 @@ def run(scheduled_process):
2222
return scheduled_process.delay(timezone.timedelta(days=1))
2323

2424
temp_dir = tempfile.TemporaryDirectory()
25-
backup_file = dsmr_backup.services.backup.create_statistics_backup(folder=temp_dir.name)
25+
backup_file = dsmr_backup.services.backup.create_partial(
26+
folder=temp_dir.name,
27+
models_to_backup=(DayStatistics, HourStatistics)
28+
)
2629

2730
with translation.override(language=BackendSettings.get_solo().language):
2831
subject = _('DSMR-reader day/hour statistics backup')

dsmr_backup/tests/models/test_settings.py

-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ def test_to_string(self):
2020
def test_daily_backup(self):
2121
self.assertTrue(self.instance.daily_backup)
2222

23-
def test_compress(self):
24-
self.assertTrue(self.instance.compress)
25-
2623
def test_backup_time(self):
2724
self.assertTrue(self.instance.backup_time, time(hour=2))
2825

0 commit comments

Comments
 (0)