simo 2.4.1__py3-none-any.whl → 2.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of simo might be problematic. Click here for more details.

Files changed (26) hide show
  1. simo/backups/__pycache__/admin.cpython-38.pyc +0 -0
  2. simo/backups/__pycache__/models.cpython-38.pyc +0 -0
  3. simo/backups/__pycache__/tasks.cpython-38.pyc +0 -0
  4. simo/backups/admin.py +10 -13
  5. simo/backups/migrations/0003_alter_backuplog_options_alter_backup_size.py +22 -0
  6. simo/backups/migrations/0004_alter_backup_options_alter_backuplog_options_and_more.py +29 -0
  7. simo/backups/migrations/__pycache__/0003_alter_backuplog_options_alter_backup_size.cpython-38.pyc +0 -0
  8. simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_options_and_more.cpython-38.pyc +0 -0
  9. simo/backups/models.py +1 -7
  10. simo/backups/tasks.py +210 -144
  11. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  12. simo/core/migrations/0042_alter_instance_timezone.py +18 -0
  13. simo/core/migrations/__pycache__/0042_alter_instance_timezone.cpython-38.pyc +0 -0
  14. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  15. simo/fleet/forms.py +140 -176
  16. simo/fleet/migrations/0038_alter_colonel_type.py +18 -0
  17. simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-38.pyc +0 -0
  18. simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
  19. simo/users/migrations/0033_alter_user_ssh_key.py +18 -0
  20. simo/users/migrations/__pycache__/0033_alter_user_ssh_key.cpython-38.pyc +0 -0
  21. {simo-2.4.1.dist-info → simo-2.4.2.dist-info}/METADATA +1 -1
  22. {simo-2.4.1.dist-info → simo-2.4.2.dist-info}/RECORD +26 -15
  23. {simo-2.4.1.dist-info → simo-2.4.2.dist-info}/LICENSE.md +0 -0
  24. {simo-2.4.1.dist-info → simo-2.4.2.dist-info}/WHEEL +0 -0
  25. {simo-2.4.1.dist-info → simo-2.4.2.dist-info}/entry_points.txt +0 -0
  26. {simo-2.4.1.dist-info → simo-2.4.2.dist-info}/top_level.txt +0 -0
simo/backups/admin.py CHANGED
@@ -7,10 +7,10 @@ from .models import Backup, BackupLog
7
7
 
8
8
  @admin.register(Backup)
9
9
  class BackupAdmin(DjangoObjectActions, admin.ModelAdmin):
10
- list_display = 'datetime', 'device', 'level', 'size_display'
11
- fields = 'datetime', 'device', 'level', 'size_display', 'filepath'
12
- readonly_fields = 'datetime', 'device', 'level', 'size_display', 'filepath'
13
- list_filter = 'datetime', 'mac', 'level'
10
+ list_display = 'datetime', 'device', 'filepath'
11
+ fields = 'datetime', 'device', 'filepath'
12
+ readonly_fields = 'datetime', 'device', 'filepath'
13
+ list_filter = 'datetime', 'mac',
14
14
  actions = 'restore',
15
15
  changelist_actions = ('backup',)
16
16
 
@@ -23,15 +23,11 @@ class BackupAdmin(DjangoObjectActions, admin.ModelAdmin):
23
23
  def has_delete_permission(self, request, obj=None):
24
24
  return False
25
25
 
26
- def size_display(self, obj):
27
- if obj.size > 1024 * 1024 * 1024 * 1024:
28
- return f'{round(obj.size / (1024 * 1024 * 1024), 2)} Gb'
29
- elif obj.size > 1024 * 1024 * 1024:
30
- return f'{round(obj.size / (1024 * 1024), 2)} Mb'
31
- elif obj.size > 1024 * 1024:
32
- return f'{round(obj.size / (1024 * 1024), 2)} Kb'
33
- return f'{obj.size} bytes'
34
- size_display.short_description = 'size'
26
+ def changelist_view(self, *args, **kwargs):
27
+ from .tasks import check_backups
28
+ check_backups()
29
+ return super().changelist_view(*args, **kwargs)
30
+
35
31
 
36
32
  def restore(self, request, queryset):
37
33
  if queryset.count() > 1:
@@ -69,6 +65,7 @@ class BackupAdmin(DjangoObjectActions, admin.ModelAdmin):
69
65
  @admin.register(BackupLog)
70
66
  class BackupLogAdmin(admin.ModelAdmin):
71
67
  fields = 'datetime', 'level', 'msg'
68
+ list_display = fields
72
69
  readonly_fields = fields
73
70
  list_fields = fields
74
71
  list_filter = 'datetime', 'level'
@@ -0,0 +1,22 @@
1
+ # Generated by Django 4.2.10 on 2024-10-09 09:16
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('backups', '0002_backuplog_backup_level_backup_size'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterModelOptions(
14
+ name='backuplog',
15
+ options={'ordering': ('datetime',)},
16
+ ),
17
+ migrations.AlterField(
18
+ model_name='backup',
19
+ name='size',
20
+ field=models.BigIntegerField(default=0),
21
+ ),
22
+ ]
@@ -0,0 +1,29 @@
1
+ # Generated by Django 4.2.10 on 2024-10-10 12:05
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('backups', '0003_alter_backuplog_options_alter_backup_size'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterModelOptions(
14
+ name='backup',
15
+ options={},
16
+ ),
17
+ migrations.AlterModelOptions(
18
+ name='backuplog',
19
+ options={},
20
+ ),
21
+ migrations.RemoveField(
22
+ model_name='backup',
23
+ name='level',
24
+ ),
25
+ migrations.RemoveField(
26
+ model_name='backup',
27
+ name='size',
28
+ ),
29
+ ]
simo/backups/models.py CHANGED
@@ -6,12 +6,9 @@ class Backup(models.Model):
6
6
  datetime = models.DateTimeField(db_index=True)
7
7
  mac = models.CharField(max_length=100, db_index=True)
8
8
  filepath = models.CharField(max_length=200)
9
- level = models.IntegerField(default=0, db_index=True)
10
- size = models.IntegerField(default=0)
11
9
 
12
10
  class Meta:
13
11
  unique_together = 'datetime', 'mac'
14
- ordering = 'datetime',
15
12
 
16
13
  @property
17
14
  def device(self):
@@ -25,7 +22,4 @@ class BackupLog(models.Model):
25
22
  level = models.CharField(default='info', choices=(
26
23
  ('info', "INFO"), ('warning', "WARNING"), ('error', "ERROR")
27
24
  ))
28
- msg = models.TextField()
29
-
30
- class Meta:
31
- ordering = 'datetime',
25
+ msg = models.TextField()
simo/backups/tasks.py CHANGED
@@ -1,4 +1,4 @@
1
- import os, subprocess, json, uuid, datetime, shutil
1
+ import os, subprocess, json, uuid, datetime, shutil, pytz
2
2
  from datetime import datetime, timezone
3
3
  from celeryc import celery_app
4
4
  from simo.conf import dynamic_settings
@@ -10,15 +10,15 @@ def check_backups():
10
10
  '''
11
11
  syncs up backups on external medium to the database
12
12
  '''
13
- from simo.backups.models import Backup, BackupLog
13
+ from simo.backups.models import Backup
14
14
 
15
15
  try:
16
- lv_group, lv_name, mountpoint = get_partitions()
16
+ lv_group, lv_name, sd_mountpoint = get_partitions()
17
17
  except:
18
18
  return Backup.objects.all().delete()
19
19
 
20
20
 
21
- backups_dir = os.path.join(mountpoint, 'simo_backups')
21
+ backups_dir = os.path.join(sd_mountpoint, 'simo_backups')
22
22
  if not os.path.exists(backups_dir):
23
23
  return Backup.objects.all().delete()
24
24
 
@@ -34,24 +34,25 @@ def check_backups():
34
34
  year, month = int(year), int(month)
35
35
  except:
36
36
  continue
37
- for filename in os.listdir(os.path.join(hub_dir, month_folder)):
38
- try:
39
- day, time, level, back = filename.split('.')
40
- hour, minute, second = time.split('-')
41
- day, hour, minute, second, level = \
42
- int(day), int(hour), int(minute), int(second), int(level)
43
- except:
44
- continue
45
-
46
- filepath = os.path.join(hub_dir, month_folder, filename)
47
- file_stats = os.stat(filepath)
37
+
38
+ month_folder_path = os.path.join(hub_dir, month_folder)
39
+ res = subprocess.run(
40
+ f"borg list {month_folder_path} --json",
41
+ shell=True, stdout=subprocess.PIPE
42
+ )
43
+ try:
44
+ archives = json.loads(res.stdout.decode())['archives']
45
+ except Exception as e:
46
+ continue
47
+
48
+ for archive in archives:
49
+ make_datetime = datetime.fromisoformat(archive['start'])
50
+ make_datetime = make_datetime.replace(tzinfo=pytz.UTC)
51
+ filepath = f"{month_folder_path}::{archive['name']}"
52
+
48
53
  obj, new = Backup.objects.update_or_create(
49
- datetime=datetime(
50
- year, month, day, hour, minute, second,
51
- tzinfo=timezone.utc
52
- ), mac=hub_mac, defaults={
53
- 'filepath': filepath, 'level': level,
54
- 'size': file_stats.st_size
54
+ datetime=make_datetime, mac=hub_mac, defaults={
55
+ 'filepath': f"{month_folder_path}::{archive['name']}",
55
56
  }
56
57
  )
57
58
  backups_mentioned.append(obj.id)
@@ -61,14 +62,80 @@ def check_backups():
61
62
  dynamic_settings['backups__last_check'] = int(datetime.now().timestamp())
62
63
 
63
64
 
64
- def create_snap(lv_group, lv_name):
65
+ def clean_backup_snaps(lv_group, lv_name):
66
+ res = subprocess.run(
67
+ 'lvs --report-format json', shell=True, stdout=subprocess.PIPE
68
+ )
69
+ lvs_data = json.loads(res.stdout.decode())
70
+ for volume in lvs_data['report'][0]['lv']:
71
+ if volume['vg_name'] != lv_group:
72
+ continue
73
+ if volume['origin'] != lv_name:
74
+ continue
75
+ if not volume['lv_name'].startswith(f"{lv_name}-bk-"):
76
+ continue
77
+ subprocess.run(
78
+ f"lvremove -f {lv_group}/{volume['lv_name']}", shell=True
79
+ )
80
+
81
+
82
+
83
+ def create_snap(lv_group, lv_name, snap_name=None, size=None, try_no=1):
84
+ '''
85
+ :param lv_group:
86
+ :param lv_name:
87
+ :param snap_name: random snap name will be generated if not provided
88
+ :param size: Size in GB. If not provided, maximum available space in lvm will be used.
89
+ :return: snap_name
90
+ '''
91
+ if not snap_name:
92
+ snap_name = f"{lv_name}-bk-{get_random_string(5)}"
93
+
94
+ clean_backup_snaps(lv_group, lv_name)
95
+
96
+ res = subprocess.run(
97
+ 'vgs --report-format json', shell=True, stdout=subprocess.PIPE
98
+ )
65
99
  try:
66
- return subprocess.check_output(
67
- f'lvcreate -s -n {lv_name}-snap {lv_group}/{lv_name} -L 5G',
68
- shell=True
69
- ).decode()
100
+ vgs_data = json.loads(res.stdout.decode())
101
+ free_space = vgs_data['report'][0]['vg'][0]['vg_free']
70
102
  except:
71
- return
103
+ if try_no < 3:
104
+ clean_backup_snaps(lv_group, lv_name)
105
+ return create_snap(lv_group, lv_name, snap_name, size, try_no+1)
106
+ raise Exception("Unable to find free space on LVM!")
107
+
108
+ if not free_space.lower().endswith('g'):
109
+ if try_no < 3:
110
+ clean_backup_snaps(lv_group, lv_name)
111
+ return create_snap(lv_group, lv_name, snap_name, size, try_no+1)
112
+ raise Exception("Not enough free space on LVM!")
113
+
114
+ free_space = int(float(
115
+ vgs_data['report'][0]['vg'][0]['vg_free'].strip('g').strip('<')
116
+ ))
117
+
118
+ if not size:
119
+ size = free_space
120
+ else:
121
+ if size > free_space:
122
+ if try_no < 3:
123
+ clean_backup_snaps(lv_group, lv_name)
124
+ return create_snap(lv_group, lv_name, snap_name, size, try_no + 1)
125
+ raise Exception(
126
+ f"There's only {free_space}G available on LVM, "
127
+ f"but you asked for {size}G"
128
+ )
129
+
130
+ res = subprocess.run(
131
+ f'lvcreate -s -n {snap_name} {lv_group}/{lv_name} -L {size}G',
132
+ shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
133
+ )
134
+ if res.returncode:
135
+ raise Exception(res.stderr)
136
+
137
+ return snap_name
138
+
72
139
 
73
140
 
74
141
  def get_lvm_partition(lsblk_data):
@@ -145,140 +212,131 @@ def get_partitions():
145
212
  return
146
213
 
147
214
  if lvm_partition.get('partlabel'):
148
- mountpoint = f"/media/{backup_device['partlabel']}"
215
+ sd_mountpoint = f"/media/{backup_device['partlabel']}"
149
216
  elif lvm_partition.get('label'):
150
- mountpoint = f"/media/{backup_device['label']}"
217
+ sd_mountpoint = f"/media/{backup_device['label']}"
151
218
  else:
152
- mountpoint = f"/media/{backup_device['name']}"
219
+ sd_mountpoint = f"/media/{backup_device['name']}"
153
220
 
154
- if not os.path.exists(mountpoint):
155
- os.makedirs(mountpoint)
221
+ if not os.path.exists(sd_mountpoint):
222
+ os.makedirs(sd_mountpoint)
156
223
 
157
- if backup_device.get('mountpoint') != mountpoint:
224
+ if backup_device.get('mountpoint') != sd_mountpoint:
158
225
 
159
226
  if backup_device.get('mountpoint'):
160
227
  subprocess.call(f"umount {backup_device['mountpoint']}", shell=True)
161
228
 
162
229
  subprocess.call(
163
- f'mount /dev/{backup_device["name"]} {mountpoint}', shell=True,
230
+ f'mount /dev/{backup_device["name"]} {sd_mountpoint}', shell=True,
164
231
  stdout=subprocess.PIPE
165
232
  )
166
233
 
167
- return lv_group, lv_name, mountpoint
234
+ return lv_group, lv_name, sd_mountpoint
168
235
 
169
236
 
170
237
  @celery_app.task
171
238
  def perform_backup():
172
239
  from simo.backups.models import BackupLog
173
240
  try:
174
- lv_group, lv_name, mountpoint = get_partitions()
241
+ lv_group, lv_name, sd_mountpoint = get_partitions()
175
242
  except:
176
243
  return
177
244
 
178
- output = create_snap(lv_group, lv_name)
179
- if not output or f'Logical volume "{lv_name}-snap" created' not in output:
180
- try:
181
- subprocess.check_output(
182
- f'lvremove -f {lv_group}/{lv_name}-snap',
183
- shell=True
184
- )
185
- except:
186
- pass
187
- output = create_snap(lv_group, lv_name)
245
+ snap_mount_point = '/var/backups/simo-main'
246
+ subprocess.run(f'umount {snap_mount_point}', shell=True)
188
247
 
189
- if f'Logical volume "{lv_name}-snap" created' not in output:
190
- print(output)
191
- print(f"Unable to create {lv_name}-snap.")
248
+ try:
249
+ snap_name = create_snap(lv_group, lv_name)
250
+ except Exception as e:
251
+ print("Error creating temporary snap\n" + str(e))
252
+ BackupLog.objects.create(
253
+ level='error',
254
+ msg="Backup error. Unable to create temporary snap\n" + str(e)
255
+ )
192
256
  return
193
257
 
258
+ shutil.rmtree(snap_mount_point, ignore_errors=True)
259
+ os.makedirs(snap_mount_point)
260
+ subprocess.run([
261
+ "mount",
262
+ f"/dev/mapper/{lv_group}-{snap_name.replace('-', '--')}",
263
+ snap_mount_point
264
+ ])
265
+
194
266
  mac = str(hex(uuid.getnode()))
195
- device_backups_path = f'{mountpoint}/simo_backups/hub-{mac}'
196
- if not os.path.exists(device_backups_path):
197
- os.makedirs(device_backups_path)
267
+ device_backups_path = f'{sd_mountpoint}/simo_backups/hub-{mac}'
268
+
198
269
 
199
270
  now = datetime.now()
200
- level = now.day
201
271
  month_folder = os.path.join(
202
272
  device_backups_path, f'{now.year}-{now.month}'
203
273
  )
204
274
  if not os.path.exists(month_folder):
205
275
  os.makedirs(month_folder)
206
- level = 0
207
-
208
- if level != 0:
209
- # check if level 0 exists
210
- level_0_exists = False
211
- for filename in os.listdir(month_folder):
212
- if '-' not in filename:
213
- continue
214
- try:
215
- level, date = filename.split('-')
216
- level = int(level)
217
- if level == 0:
218
- level_0_exists = True
219
- break
220
- except:
221
- continue
222
- if not level_0_exists:
223
- print("Level 0 does not exist! Backups must be started from 0!")
276
+ subprocess.run(
277
+ f'borg init --encryption=none {month_folder}', shell=True
278
+ )
279
+ else:
280
+ res = subprocess.run(
281
+ f'borg info --json {month_folder}',
282
+ shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
283
+ )
284
+ if res.returncode:
224
285
  shutil.rmtree(month_folder)
225
- os.makedirs(month_folder)
226
- level = 0
286
+ subprocess.run(
287
+ f'borg init --encryption=none {month_folder}', shell=True
288
+ )
227
289
 
228
- time_mark = now.strftime("%H-%M-%S")
229
- backup_file = f"{month_folder}/{now.day}.{time_mark}.{level}.back"
230
- snap_mapper = f"/dev/mapper/{lv_group}-{lv_name.replace('-', '--')}--snap"
231
- label = f"simo {now.strftime('%Y-%m-%d')}"
232
- dumpdates_file = os.path.join(month_folder, 'dumpdates')
290
+ exclude_dirs = (
291
+ 'tmp', 'lost+found', 'proc', 'cdrom', 'dev', 'mnt', 'sys', 'run',
292
+ 'var/tmp', 'var/cache', 'var/log', 'media',
293
+ )
294
+ backup_command = 'borg create --compression lz4'
295
+ for dir in exclude_dirs:
296
+ backup_command += f' --exclude={dir}'
233
297
 
234
- estimated_size = int(subprocess.check_output(
235
- f'dump -{level} -Squz9 -b 1024 {snap_mapper}',
236
- shell=True
237
- )) * 0.5
238
298
 
239
- folders = []
299
+ other_month_folders = []
240
300
  for item in os.listdir(device_backups_path):
241
301
  if not os.path.isdir(os.path.join(device_backups_path, item)):
242
302
  continue
303
+ if os.path.join(device_backups_path, item) == month_folder:
304
+ continue
243
305
  try:
244
306
  year, month = item.split('-')
245
- folders.append([
307
+ other_month_folders.append([
246
308
  os.path.join(device_backups_path, item),
247
309
  int(year) * 12 + int(month)
248
310
  ])
249
311
  except:
250
312
  continue
251
- folders.sort(key=lambda v: v[1])
313
+ other_month_folders.sort(key=lambda v: v[1])
314
+
315
+ if other_month_folders:
316
+ # delete old backups to free up at least 20G of space
317
+ while shutil.disk_usage('/media/backup').free < 20 * 1024 * 1024 * 1024:
318
+ remove_folder = other_month_folders.pop()[0]
319
+ print(f"REMOVE: {remove_folder}")
320
+ shutil.rmtree(remove_folder)
321
+
322
+ backup_command += f' {month_folder}::{get_random_string()} .'
323
+ res = subprocess.run(
324
+ backup_command, shell=True, cwd=snap_mount_point,
325
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE
326
+ )
252
327
 
253
- # delete old backups to free up space required for this backup to take place
254
- while shutil.disk_usage('/media/backup').free < estimated_size:
255
- remove_folder = folders.pop()[0]
256
- print(f"REMOVE: {remove_folder}")
257
- shutil.rmtree(remove_folder)
328
+ subprocess.run(["umount", snap_mount_point])
329
+ subprocess.run(
330
+ f"lvremove -f {lv_group}/{snap_name}", shell=True
331
+ )
258
332
 
259
- success = False
260
- try:
261
- subprocess.check_call(
262
- f'dump -{level} -quz9 -b 1024 -L "{label}" -D {dumpdates_file} -f {backup_file} {snap_mapper}',
263
- shell=True,
333
+ if res.returncode:
334
+ print("Backup error!")
335
+ BackupLog.objects.create(
336
+ level='error', msg="Backup error: \n" + res.stderr.decode()
264
337
  )
265
- success = True
266
- except:
267
- try:
268
- os.remove(backup_file)
269
- BackupLog.objects.create(
270
- level='error', msg="Can't backup. Dump failed."
271
- )
272
- print("Dump failed!")
273
- except:
274
- pass
275
-
276
- subprocess.call(
277
- f"lvremove -f {lv_group}/{lv_name}-snap", shell=True,
278
- stdout=subprocess.PIPE
279
- )
280
- if success:
281
- print("DONE!")
338
+ else:
339
+ print("Backup done!")
282
340
  BackupLog.objects.create(
283
341
  level='info', msg="Backup success!"
284
342
  )
@@ -290,7 +348,7 @@ def restore_backup(backup_id):
290
348
  backup = Backup.objects.get(id=backup_id)
291
349
 
292
350
  try:
293
- lv_group, lv_name, mpt = get_partitions()
351
+ lv_group, lv_name, sd_mountpoint = get_partitions()
294
352
  except:
295
353
  BackupLog.objects.create(
296
354
  level='error',
@@ -298,51 +356,59 @@ def restore_backup(backup_id):
298
356
  )
299
357
  return
300
358
 
301
- snap_name = f'{lv_name}-{get_random_string(5)}'
302
- output = create_snap(lv_group, snap_name)
303
- if not output or f'Logical volume "{lv_name}-snap" created' not in output:
359
+ snap_mount_point = '/var/backups/simo-main'
360
+ subprocess.run(f'umount {snap_mount_point}', shell=True)
361
+
362
+ try:
363
+ snap_name = create_snap(lv_group, lv_name)
364
+ except Exception as e:
365
+ print("Error creating temporary snap\n" + str(e))
304
366
  BackupLog.objects.create(
305
367
  level='error',
306
- msg="Can't restore. Can't create LVM snapshot\n\n" + output
368
+ msg="Can't restore. \n\n" + str(e)
307
369
  )
308
370
  return
309
371
 
310
- if not os.path.exists('/var/backups/simo-main'):
311
- os.makedirs('/var/backups/simo-main')
312
-
313
- subprocess.call('umount /var/backup/simo-main', shell=True)
314
-
315
- subprocess.call('rm -rf /var/backup/simo-main/*', shell=True)
372
+ shutil.rmtree(snap_mount_point, ignore_errors=True)
373
+ os.makedirs(snap_mount_point)
374
+ subprocess.run([
375
+ "mount",
376
+ f"/dev/mapper/{lv_group}-{snap_name.replace('-', '--')}",
377
+ snap_mount_point
378
+ ])
379
+
380
+ # delete current contents of a snap
381
+ print("Delete original files and folders")
382
+ for f in os.listdir(snap_mount_point):
383
+ shutil.rmtree(os.path.join(snap_mount_point, f), ignore_errors=True)
384
+
385
+ print("Perform restoration")
386
+ res = subprocess.run(
387
+ f"borg extract {backup.filepath}", shell=True, cwd=snap_mount_point,
388
+ stderr=subprocess.PIPE
389
+ )
316
390
 
317
- subprocess.call(
318
- f"mount /dev/mapper/{lv_group}-{snap_name.replace('-', '--')} /var/backup/simo-main",
319
- shell=True, stdout=subprocess.PIPE
391
+ subprocess.run(["umount", snap_mount_point])
392
+ subprocess.run(
393
+ f"lvremove -f {lv_group}/{snap_name}", shell=True
320
394
  )
321
- try:
322
- subprocess.call(
323
- f"restore -C -v -b 1024 -f {backup.filepath} -D /var/backup/simo-main",
324
- shell=True
325
- )
326
- subprocess.call(
327
- f"lvconvert --mergesnapshot {lv_group}/{snap_name}",
328
- shell=True
329
- )
330
- except Exception as e:
395
+
396
+ if res.returncode:
331
397
  BackupLog.objects.create(
332
398
  level='error',
333
- msg="Can't restore. \n\n" + str(e)
399
+ msg="Can't restore. \n\n" + res.stderr.decode()
334
400
  )
335
- subprocess.call('umount /var/backup/simo-main', shell=True)
401
+ else:
402
+ print("Restore successful! Merge snapshot and reboot!")
336
403
  subprocess.call(
337
- f"lvremove -f {lv_group}/{snap_name}", shell=True,
338
- stdout=subprocess.PIPE
404
+ f"lvconvert --mergesnapshot {lv_group}/{snap_name}",
405
+ shell=True
339
406
  )
340
- else:
341
- print("All good! REBOOT!")
342
- subprocess.call('reboot')
407
+ subprocess.run('reboot', shell=True)
343
408
 
344
409
 
345
410
  @celery_app.on_after_finalize.connect
346
411
  def setup_periodic_tasks(sender, **kwargs):
347
412
  sender.add_periodic_task(60 * 60, check_backups.s())
348
- sender.add_periodic_task(60 * 60 * 8, perform_backup.s()) # perform auto backup every 8 hours
413
+ # perform auto backup every 12 hours
414
+ sender.add_periodic_task(60 * 60 * 12, perform_backup.s())
Binary file