2023-07-20 15:24:26 +00:00
|
|
|
"""
|
|
|
|
Module for storing backup related data in redis.
|
|
|
|
"""
|
2023-04-10 13:22:33 +00:00
|
|
|
from typing import List, Optional
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from selfprivacy_api.models.backup.snapshot import Snapshot
|
|
|
|
from selfprivacy_api.models.backup.provider import BackupProviderModel
|
2023-08-28 17:02:45 +00:00
|
|
|
from selfprivacy_api.graphql.common_types.backup import (
|
|
|
|
AutobackupQuotas,
|
|
|
|
_AutobackupQuotas,
|
|
|
|
)
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
from selfprivacy_api.utils.redis_pool import RedisPool
|
2023-06-23 09:40:10 +00:00
|
|
|
from selfprivacy_api.utils.redis_model_storage import (
|
|
|
|
store_model_as_hash,
|
|
|
|
hash_as_model,
|
|
|
|
)
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
from selfprivacy_api.backup.providers.provider import AbstractBackupProvider
|
|
|
|
from selfprivacy_api.backup.providers import get_kind
|
|
|
|
|
|
|
|
REDIS_SNAPSHOTS_PREFIX = "backups:snapshots:"
|
|
|
|
REDIS_LAST_BACKUP_PREFIX = "backups:last-backed-up:"
|
2023-07-26 16:45:08 +00:00
|
|
|
REDIS_INITTED_CACHE = "backups:repo_initted"
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
REDIS_PROVIDER_KEY = "backups:provider"
|
|
|
|
REDIS_AUTOBACKUP_PERIOD_KEY = "backups:autobackup_period"
|
|
|
|
|
2023-08-28 17:02:45 +00:00
|
|
|
REDIS_AUTOBACKUP_QUOTAS_KEY = "backups:autobackup_quotas_key"
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
redis = RedisPool().get_connection()
|
|
|
|
|
|
|
|
|
|
|
|
class Storage:
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Static class for storing backup related data in redis"""
|
2023-07-20 16:37:01 +00:00
|
|
|
|
2023-04-10 13:22:33 +00:00
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def reset() -> None:
|
|
|
|
"""Deletes all backup related data from redis"""
|
2023-04-10 13:22:33 +00:00
|
|
|
redis.delete(REDIS_PROVIDER_KEY)
|
|
|
|
redis.delete(REDIS_AUTOBACKUP_PERIOD_KEY)
|
2023-07-26 16:45:08 +00:00
|
|
|
redis.delete(REDIS_INITTED_CACHE)
|
2023-08-28 17:02:45 +00:00
|
|
|
redis.delete(REDIS_AUTOBACKUP_QUOTAS_KEY)
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
prefixes_to_clean = [
|
|
|
|
REDIS_SNAPSHOTS_PREFIX,
|
|
|
|
REDIS_LAST_BACKUP_PREFIX,
|
|
|
|
]
|
|
|
|
|
|
|
|
for prefix in prefixes_to_clean:
|
|
|
|
for key in redis.keys(prefix + "*"):
|
|
|
|
redis.delete(key)
|
|
|
|
|
2023-06-01 14:03:26 +00:00
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def invalidate_snapshot_storage() -> None:
|
|
|
|
"""Deletes all cached snapshots from redis"""
|
2023-06-01 14:03:26 +00:00
|
|
|
for key in redis.keys(REDIS_SNAPSHOTS_PREFIX + "*"):
|
|
|
|
redis.delete(key)
|
|
|
|
|
2023-04-10 13:22:33 +00:00
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def __last_backup_key(service_id: str) -> str:
|
2023-04-10 13:22:33 +00:00
|
|
|
return REDIS_LAST_BACKUP_PREFIX + service_id
|
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def __snapshot_key(snapshot: Snapshot) -> str:
|
2023-04-10 13:22:33 +00:00
|
|
|
return REDIS_SNAPSHOTS_PREFIX + snapshot.id
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_last_backup_time(service_id: str) -> Optional[datetime]:
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Returns last backup time for a service or None if it was never backed up"""
|
2023-04-10 13:22:33 +00:00
|
|
|
key = Storage.__last_backup_key(service_id)
|
|
|
|
if not redis.exists(key):
|
|
|
|
return None
|
|
|
|
|
|
|
|
snapshot = hash_as_model(redis, key, Snapshot)
|
2023-07-19 12:59:51 +00:00
|
|
|
if not snapshot:
|
|
|
|
return None
|
2023-04-10 13:22:33 +00:00
|
|
|
return snapshot.created_at
|
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def store_last_timestamp(service_id: str, snapshot: Snapshot) -> None:
|
|
|
|
"""Stores last backup time for a service"""
|
2023-07-19 12:59:51 +00:00
|
|
|
store_model_as_hash(
|
|
|
|
redis,
|
|
|
|
Storage.__last_backup_key(service_id),
|
|
|
|
snapshot,
|
|
|
|
)
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def cache_snapshot(snapshot: Snapshot) -> None:
|
|
|
|
"""Stores snapshot metadata in redis for caching purposes"""
|
2023-04-10 13:22:33 +00:00
|
|
|
snapshot_key = Storage.__snapshot_key(snapshot)
|
|
|
|
store_model_as_hash(redis, snapshot_key, snapshot)
|
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def delete_cached_snapshot(snapshot: Snapshot) -> None:
|
|
|
|
"""Deletes snapshot metadata from redis"""
|
2023-04-10 13:22:33 +00:00
|
|
|
snapshot_key = Storage.__snapshot_key(snapshot)
|
|
|
|
redis.delete(snapshot_key)
|
|
|
|
|
2023-06-01 14:03:26 +00:00
|
|
|
@staticmethod
|
|
|
|
def get_cached_snapshot_by_id(snapshot_id: str) -> Optional[Snapshot]:
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Returns cached snapshot by id or None if it doesn't exist"""
|
2023-06-05 11:19:01 +00:00
|
|
|
key = REDIS_SNAPSHOTS_PREFIX + snapshot_id
|
2023-06-01 14:03:26 +00:00
|
|
|
if not redis.exists(key):
|
|
|
|
return None
|
|
|
|
return hash_as_model(redis, key, Snapshot)
|
|
|
|
|
2023-04-10 13:22:33 +00:00
|
|
|
@staticmethod
|
|
|
|
def get_cached_snapshots() -> List[Snapshot]:
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Returns all cached snapshots stored in redis"""
|
|
|
|
keys: list[str] = redis.keys(REDIS_SNAPSHOTS_PREFIX + "*") # type: ignore
|
|
|
|
result: list[Snapshot] = []
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
for key in keys:
|
|
|
|
snapshot = hash_as_model(redis, key, Snapshot)
|
2023-07-20 15:24:26 +00:00
|
|
|
if snapshot:
|
|
|
|
result.append(snapshot)
|
2023-04-10 13:22:33 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def autobackup_period_minutes() -> Optional[int]:
|
|
|
|
"""None means autobackup is disabled"""
|
|
|
|
if not redis.exists(REDIS_AUTOBACKUP_PERIOD_KEY):
|
|
|
|
return None
|
2023-07-20 15:24:26 +00:00
|
|
|
return int(redis.get(REDIS_AUTOBACKUP_PERIOD_KEY)) # type: ignore
|
2023-04-10 13:22:33 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def store_autobackup_period_minutes(minutes: int) -> None:
|
|
|
|
"""Set the new autobackup period in minutes"""
|
2023-04-10 13:22:33 +00:00
|
|
|
redis.set(REDIS_AUTOBACKUP_PERIOD_KEY, minutes)
|
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def delete_backup_period() -> None:
|
|
|
|
"""Set the autobackup period to none, effectively disabling autobackup"""
|
2023-04-10 13:22:33 +00:00
|
|
|
redis.delete(REDIS_AUTOBACKUP_PERIOD_KEY)
|
|
|
|
|
|
|
|
@staticmethod
|
2023-07-20 15:24:26 +00:00
|
|
|
def store_provider(provider: AbstractBackupProvider) -> None:
|
|
|
|
"""Stores backup stroage provider auth data in redis"""
|
2023-04-10 13:22:33 +00:00
|
|
|
store_model_as_hash(
|
|
|
|
redis,
|
|
|
|
REDIS_PROVIDER_KEY,
|
|
|
|
BackupProviderModel(
|
2023-05-29 15:34:26 +00:00
|
|
|
kind=get_kind(provider),
|
|
|
|
login=provider.login,
|
|
|
|
key=provider.key,
|
|
|
|
location=provider.location,
|
|
|
|
repo_id=provider.repo_id,
|
2023-04-10 13:22:33 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
@staticmethod
|
2023-06-23 09:40:10 +00:00
|
|
|
def load_provider() -> Optional[BackupProviderModel]:
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Loads backup storage provider auth data from redis"""
|
2023-06-23 09:40:10 +00:00
|
|
|
provider_model = hash_as_model(
|
|
|
|
redis,
|
|
|
|
REDIS_PROVIDER_KEY,
|
|
|
|
BackupProviderModel,
|
|
|
|
)
|
2023-04-10 13:22:33 +00:00
|
|
|
return provider_model
|
|
|
|
|
|
|
|
@staticmethod
|
2023-05-29 16:50:14 +00:00
|
|
|
def has_init_mark() -> bool:
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Returns True if the repository was initialized"""
|
2023-07-26 16:45:08 +00:00
|
|
|
if redis.exists(REDIS_INITTED_CACHE):
|
2023-04-10 13:22:33 +00:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
@staticmethod
|
2023-05-29 16:50:14 +00:00
|
|
|
def mark_as_init():
|
2023-07-20 15:24:26 +00:00
|
|
|
"""Marks the repository as initialized"""
|
2023-07-26 16:45:08 +00:00
|
|
|
redis.set(REDIS_INITTED_CACHE, 1)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def mark_as_uninitted():
|
|
|
|
"""Marks the repository as initialized"""
|
|
|
|
redis.delete(REDIS_INITTED_CACHE)
|
2023-08-21 12:45:31 +00:00
|
|
|
|
2023-08-28 17:02:45 +00:00
|
|
|
@staticmethod
|
|
|
|
def set_autobackup_quotas(quotas: AutobackupQuotas) -> None:
|
|
|
|
store_model_as_hash(redis, REDIS_AUTOBACKUP_QUOTAS_KEY, quotas.to_pydantic())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def autobackup_quotas() -> AutobackupQuotas:
|
|
|
|
quotas_model = hash_as_model(
|
|
|
|
redis, REDIS_AUTOBACKUP_QUOTAS_KEY, _AutobackupQuotas
|
|
|
|
)
|
|
|
|
if quotas_model is None:
|
|
|
|
unlimited_quotas = AutobackupQuotas(
|
2023-09-09 00:26:41 +00:00
|
|
|
last=-1,
|
2023-08-28 17:02:45 +00:00
|
|
|
daily=-1,
|
|
|
|
weekly=-1,
|
|
|
|
monthly=-1,
|
|
|
|
yearly=-1,
|
|
|
|
)
|
|
|
|
return unlimited_quotas
|
2023-09-09 00:26:41 +00:00
|
|
|
return AutobackupQuotas.from_pydantic(quotas_model) # pylint: disable=no-member
|