simo 2.11.1__py3-none-any.whl → 2.11.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.
- simo/backups/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/backups/tasks.py +105 -8
- {simo-2.11.1.dist-info → simo-2.11.2.dist-info}/METADATA +1 -1
- {simo-2.11.1.dist-info → simo-2.11.2.dist-info}/RECORD +8 -8
- {simo-2.11.1.dist-info → simo-2.11.2.dist-info}/WHEEL +0 -0
- {simo-2.11.1.dist-info → simo-2.11.2.dist-info}/entry_points.txt +0 -0
- {simo-2.11.1.dist-info → simo-2.11.2.dist-info}/licenses/LICENSE.md +0 -0
- {simo-2.11.1.dist-info → simo-2.11.2.dist-info}/top_level.txt +0 -0
|
Binary file
|
simo/backups/tasks.py
CHANGED
|
@@ -196,30 +196,124 @@ def get_backup_device(lsblk_data):
|
|
|
196
196
|
partition’ – is used.
|
|
197
197
|
"""
|
|
198
198
|
|
|
199
|
+
_MIN_SIZE_BYTES = 32 * 1024 * 1024 * 1024 # 32 GiB – keep in sync with
|
|
200
|
+
# _find_blank_removable_device.
|
|
201
|
+
|
|
202
|
+
def _device_size_bytes(dev_name: str):
|
|
203
|
+
"""Return size of *dev_name* in bytes (or ``None`` on failure)."""
|
|
204
|
+
|
|
205
|
+
for cmd in (
|
|
206
|
+
f"blockdev --getsize64 /dev/{dev_name}",
|
|
207
|
+
f"lsblk -b -dn -o SIZE /dev/{dev_name}",
|
|
208
|
+
):
|
|
209
|
+
try:
|
|
210
|
+
out = subprocess.check_output(
|
|
211
|
+
cmd, shell=True, stderr=subprocess.DEVNULL
|
|
212
|
+
).strip()
|
|
213
|
+
return int(out)
|
|
214
|
+
except Exception:
|
|
215
|
+
continue
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
# ------------------------------------------------------------------
|
|
219
|
+
# Helper: does the filesystem already contain legacy backups?
|
|
220
|
+
# ------------------------------------------------------------------
|
|
221
|
+
|
|
222
|
+
def _entry_has_simo_backups(entry: dict) -> bool:
|
|
223
|
+
"""Return *True* when *entry* hosts legacy ``simo_backups`` folder.
|
|
224
|
+
|
|
225
|
+
The implementation borrows heavily from the _fs_is_empty() helper –
|
|
226
|
+
we temporarily mount the filesystem read-only when it is not mounted
|
|
227
|
+
yet, inspect the directory listing and clean everything up.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
mountpoint = entry.get("mountpoint")
|
|
231
|
+
cleanup = False
|
|
232
|
+
|
|
233
|
+
if not mountpoint:
|
|
234
|
+
tmp_dir = f"/tmp/simo-bk-{uuid.uuid4().hex[:8]}"
|
|
235
|
+
try:
|
|
236
|
+
os.makedirs(tmp_dir, exist_ok=True)
|
|
237
|
+
res = subprocess.run(
|
|
238
|
+
f"mount -o ro /dev/{entry['name']} {tmp_dir}",
|
|
239
|
+
shell=True,
|
|
240
|
+
stderr=subprocess.PIPE,
|
|
241
|
+
)
|
|
242
|
+
if res.returncode:
|
|
243
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
244
|
+
return False
|
|
245
|
+
mountpoint = tmp_dir
|
|
246
|
+
cleanup = True
|
|
247
|
+
except Exception:
|
|
248
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
has_backups = os.path.isdir(os.path.join(mountpoint, "simo_backups"))
|
|
252
|
+
|
|
253
|
+
if cleanup:
|
|
254
|
+
subprocess.run(f"umount {mountpoint}", shell=True)
|
|
255
|
+
shutil.rmtree(mountpoint, ignore_errors=True)
|
|
256
|
+
|
|
257
|
+
return has_backups
|
|
258
|
+
|
|
259
|
+
# ------------------------------------------------------------------
|
|
260
|
+
# Phase 1 – look for properly prepared BACKUP partition **>=32 GiB**.
|
|
261
|
+
# This is the preferred modern approach.
|
|
262
|
+
# ------------------------------------------------------------------
|
|
263
|
+
|
|
199
264
|
for device in lsblk_data:
|
|
200
265
|
if not device.get("hotplug"):
|
|
201
266
|
continue
|
|
202
267
|
|
|
268
|
+
# Capacity check – skip devices smaller than the required threshold.
|
|
269
|
+
size_bytes = _device_size_bytes(device["name"])
|
|
270
|
+
if size_bytes is None:
|
|
271
|
+
print(f"Could not obtain capacity of: {device['name']}")
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
if size_bytes < _MIN_SIZE_BYTES:
|
|
275
|
+
continue
|
|
276
|
+
|
|
203
277
|
# Prefer partitions explicitly labelled "BACKUP".
|
|
204
278
|
for child in device.get("children", []):
|
|
205
279
|
if _has_backup_label(child):
|
|
206
280
|
return child
|
|
207
281
|
|
|
208
|
-
# Legacy fallback – whole-disk
|
|
209
|
-
# exFAT are still
|
|
210
|
-
#
|
|
211
|
-
# NOTE: We intentionally keep this logic after the new BACKUP label
|
|
212
|
-
# check so that freshly provisioned media (ext4+label) wins.
|
|
282
|
+
# Legacy fallback (modern capacity) – whole-disk or partitioned
|
|
283
|
+
# exFAT volumes are still acceptable for backward compatibility when
|
|
284
|
+
# they are large enough.
|
|
213
285
|
|
|
214
|
-
# 1. Whole-disk (no partition table) exFAT volume.
|
|
215
286
|
if (device.get("fstype") or "").lower() == "exfat":
|
|
216
287
|
return device
|
|
217
288
|
|
|
218
|
-
# 2. Partitioned removable drive – look for any exFAT child.
|
|
219
289
|
for child in device.get("children", []):
|
|
220
290
|
if (child.get("fstype") or "").lower() == "exfat":
|
|
221
291
|
return child
|
|
222
292
|
|
|
293
|
+
|
|
294
|
+
# ------------------------------------------------------------------
|
|
295
|
+
# Phase 2 – look for **existing** legacy backup drives.
|
|
296
|
+
# ------------------------------------------------------------------
|
|
297
|
+
|
|
298
|
+
if _find_blank_removable_device(lsblk_data):
|
|
299
|
+
# New empty disk is available, let's use it instead of trying to find
|
|
300
|
+
# legacy media
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
for device in lsblk_data:
|
|
304
|
+
if not device.get("hotplug"):
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
# Check the whole device first.
|
|
308
|
+
if device.get("mountpoint") or device.get("fstype"):
|
|
309
|
+
if _entry_has_simo_backups(device):
|
|
310
|
+
return device
|
|
311
|
+
|
|
312
|
+
# Check its partitions (if any).
|
|
313
|
+
for child in device.get("children", []):
|
|
314
|
+
if _entry_has_simo_backups(child):
|
|
315
|
+
return child
|
|
316
|
+
|
|
223
317
|
# Nothing has been found.
|
|
224
318
|
return None
|
|
225
319
|
|
|
@@ -577,6 +671,9 @@ def perform_backup():
|
|
|
577
671
|
mac = str(hex(uuid.getnode()))
|
|
578
672
|
device_backups_path = f'{sd_mountpoint}/simo_backups/hub-{mac}'
|
|
579
673
|
|
|
674
|
+
if not os.path.exists(device_backups_path):
|
|
675
|
+
os.makedirs(device_backups_path)
|
|
676
|
+
|
|
580
677
|
drop_current_instance()
|
|
581
678
|
hub_meta = {
|
|
582
679
|
'instances': [inst.name for inst in Instance.objects.all()]
|
|
@@ -765,4 +862,4 @@ def setup_periodic_tasks(sender, **kwargs):
|
|
|
765
862
|
sender.add_periodic_task(60 * 60, check_backups.s())
|
|
766
863
|
# perform auto backup every 12 hours
|
|
767
864
|
sender.add_periodic_task(60 * 60 * 12, perform_backup.s())
|
|
768
|
-
sender.add_periodic_task(60 * 60, clean_old_logs.s())
|
|
865
|
+
sender.add_periodic_task(60 * 60, clean_old_logs.s())
|
|
@@ -65,7 +65,7 @@ simo/backups/admin.py,sha256=cEakxnQlOHvUf8LdBdekXpDAvnqPoVN8y7pnN3WK29A,2487
|
|
|
65
65
|
simo/backups/dynamic_settings.py,sha256=Q52RLa3UQsmAhqkwR16cM6pbBnIbXqmVQ2oIUP2ZVD0,416
|
|
66
66
|
simo/backups/models.py,sha256=-tgILgkqmBEuxBwoymKZN1a0UVQzmJvqWrIGYMMFDaQ,695
|
|
67
67
|
simo/backups/rescue.img.xz,sha256=sErJUejbS9wMlmeLXeTbzOjOEsXxLnHMn0tTJpn2ITo,62389472
|
|
68
|
-
simo/backups/tasks.py,sha256=
|
|
68
|
+
simo/backups/tasks.py,sha256=ixNslDRYHd2Ujeji5EmTVFrOFPqBLKzgp2D1QgxHJWM,30620
|
|
69
69
|
simo/backups/__pycache__/__init__.cpython-312.pyc,sha256=TAMZORllxjDG2r6KsDbR085kId1pURDKwMAaykHlt0w,125
|
|
70
70
|
simo/backups/__pycache__/__init__.cpython-38.pyc,sha256=vzOf-JIMeZ6P85FyvTpYev3mscFosUy-SJTshcQbOHU,161
|
|
71
71
|
simo/backups/__pycache__/admin.cpython-312.pyc,sha256=f0XBNqkwkFmJMx3C5328Axk_Ph6ZXElPrgOKN8pCuUY,3821
|
|
@@ -74,7 +74,7 @@ simo/backups/__pycache__/dynamic_settings.cpython-312.pyc,sha256=PpnbOuz78x3hFQY
|
|
|
74
74
|
simo/backups/__pycache__/dynamic_settings.cpython-38.pyc,sha256=51gJFjn_XqQBRoHeubo6ppb9pNuFQKI5hAR0ms9flE8,731
|
|
75
75
|
simo/backups/__pycache__/models.cpython-312.pyc,sha256=Re77ADeTs4A4SYXvAVJ3kscH4nAx9FtkagejTZRLI7s,1704
|
|
76
76
|
simo/backups/__pycache__/models.cpython-38.pyc,sha256=zclX7HwOT4_izweyKNQ8LmgSHb3hNcYcfsSiwwfQoLY,1220
|
|
77
|
-
simo/backups/__pycache__/tasks.cpython-312.pyc,sha256=
|
|
77
|
+
simo/backups/__pycache__/tasks.cpython-312.pyc,sha256=lOvO_kMGq0I92G4oyCpun0aC2g3ebiD9fzavZFE6uns,34499
|
|
78
78
|
simo/backups/__pycache__/tasks.cpython-38.pyc,sha256=bKz_Rxt_H0lC0d9_4Dxqv7cirQDSH9LVurZZC0LU94s,9179
|
|
79
79
|
simo/backups/migrations/0001_initial.py,sha256=0LzCusTUyYf61ksiepdnqXIuYYNTNd_djh_Wa484HA0,770
|
|
80
80
|
simo/backups/migrations/0002_backuplog_backup_level_backup_size.py,sha256=w9T9MQWuecy91OZE1fMExriwPuXA8HMPKsPwXhmC8_k,1023
|
|
@@ -11004,9 +11004,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
11004
11004
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11005
11005
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11006
11006
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11007
|
-
simo-2.11.
|
|
11008
|
-
simo-2.11.
|
|
11009
|
-
simo-2.11.
|
|
11010
|
-
simo-2.11.
|
|
11011
|
-
simo-2.11.
|
|
11012
|
-
simo-2.11.
|
|
11007
|
+
simo-2.11.2.dist-info/licenses/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
11008
|
+
simo-2.11.2.dist-info/METADATA,sha256=mX2Y773CRkKjFdGgxztQ7qqsJLS13ICLvS9yI-lfQ3g,2028
|
|
11009
|
+
simo-2.11.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11010
|
+
simo-2.11.2.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
|
|
11011
|
+
simo-2.11.2.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
11012
|
+
simo-2.11.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|