Add backup job list, create, edit, test views
This commit is contained in:
@@ -1,9 +1,47 @@
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ugettext
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.management.commands.dumpdata import Command
|
||||
from django.db import router, DEFAULT_DB_ALIAS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Data types
|
||||
class ElementDataBase(object):
|
||||
"""
|
||||
The basic unit of a backup, a data type
|
||||
it is produced or consumed by the ElementBackup classes
|
||||
"""
|
||||
def save(self):
|
||||
"""
|
||||
Must return a file like object
|
||||
"""
|
||||
raise NotImplemented
|
||||
|
||||
def load(self, file_object):
|
||||
"""
|
||||
Must read a file like object and store content
|
||||
"""
|
||||
raise NotImplemented
|
||||
|
||||
|
||||
class Fixture(ElementDataBase):
|
||||
name = 'fixture'
|
||||
|
||||
def __init__(self, model_backup, content):
|
||||
self.model_backup = model_backup
|
||||
self.content = content
|
||||
|
||||
def save(self):
|
||||
return ContentFile(name='%s_%s' % (self.__class__.__name__, self.model_backup.app_backup.name), content=self.content)
|
||||
|
||||
#def load(self):
|
||||
|
||||
|
||||
# Element backup
|
||||
class ElementBackupBase(object):
|
||||
"""
|
||||
Sub classes must provide at least:
|
||||
@@ -28,7 +66,7 @@ class ElementBackupBase(object):
|
||||
return unicode(self.__class__.label)
|
||||
|
||||
|
||||
class ElementBackupModel(ElementBackupBase):
|
||||
class ModelBackup(ElementBackupBase):
|
||||
label = _(u'Model fixtures')
|
||||
|
||||
def __init__(self, models=None):
|
||||
@@ -39,18 +77,23 @@ class ElementBackupModel(ElementBackupBase):
|
||||
|
||||
def backup(self):
|
||||
"""
|
||||
TODO: turn into a generator maybe?
|
||||
"""
|
||||
#TODO: turn into a generator
|
||||
|
||||
command = Command()
|
||||
if not self.model_list:
|
||||
result = [self.app_backup.name]
|
||||
else:
|
||||
result = [u'%s.%s' (self.app_backup.name, model) for model in self.model_list]
|
||||
result = command.handle(u' '.join(result), format='json', indent=4, using=DEFAULT_DB_ALIAS, exclude=[], user_base_manager=False, use_natural_keys=False)
|
||||
return result
|
||||
|
||||
#TODO: a single Fixture or a list of Fixtures for each model?
|
||||
return Fixture(
|
||||
model_backup=self,
|
||||
content=command.handle(u' '.join(result), format='json', indent=4, using=DEFAULT_DB_ALIAS, exclude=[], user_base_manager=False, use_natural_keys=False)
|
||||
)
|
||||
|
||||
|
||||
class ElementBackupFile(ElementBackupBase):
|
||||
class FileBackup(ElementBackupBase):
|
||||
label = _(u'File copy')
|
||||
|
||||
def __init__(self, storage_class, filepath=None):
|
||||
@@ -68,6 +111,7 @@ class ElementBackupFile(ElementBackupBase):
|
||||
return None
|
||||
|
||||
|
||||
# App config
|
||||
class AppBackup(object):
|
||||
_registry = {}
|
||||
|
||||
@@ -89,6 +133,10 @@ class AppBackup(object):
|
||||
def get_all(cls):
|
||||
return cls._registry.values()
|
||||
|
||||
@classmethod
|
||||
def get_as_choices(cls):
|
||||
return [(key, key.label) for key, values in cls._registry.items()]
|
||||
|
||||
def __init__(self, name, label, backup_managers):
|
||||
self.label = label
|
||||
self.name = name
|
||||
@@ -102,14 +150,17 @@ class AppBackup(object):
|
||||
results.append(u'%s - %s' % (manager, manager.info() or _(u'Nothing')))
|
||||
return u', '.join(results)
|
||||
|
||||
def backup(self, storage_module, *args, **kwargs):
|
||||
def backup(self, storage_module, dry_run=False):
|
||||
logger.debug('starting')
|
||||
|
||||
self.state = self.__class__.STATE_BACKING_UP
|
||||
for manager in self.backup_managers:
|
||||
result = manager.backup()
|
||||
storage_module.backup(result)
|
||||
storage_module.backup(result, dry_run=dry_run)
|
||||
self.state = self.__class__.STATE_IDLE
|
||||
|
||||
def restore(self, storage_module=None):
|
||||
logger.debug('starting')
|
||||
self.state = self.__class__.STATE_RESTORING
|
||||
for manager in self.backup_managers:
|
||||
manager.restore(storage_module.restore())
|
||||
@@ -119,8 +170,9 @@ class AppBackup(object):
|
||||
return unicode(self.label)
|
||||
|
||||
|
||||
#Storage
|
||||
class StorageModuleBase(object):
|
||||
_registry = []
|
||||
_registry = {}
|
||||
|
||||
# Local modules depend on hardware on a node and execute in the Scheduler
|
||||
# of a particular node
|
||||
@@ -135,16 +187,34 @@ class StorageModuleBase(object):
|
||||
(REALM_REMOTE, _(u'remote')),
|
||||
)
|
||||
|
||||
class UnknownStorageModule(Exception):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def register(cls, klass):
|
||||
"""
|
||||
Register a subclass of StorageModuleBase to make it available to the
|
||||
UI
|
||||
"""
|
||||
cls._registry.append(klass)
|
||||
cls._registry[klass.name] = klass
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
return cls._registry.values()
|
||||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
try:
|
||||
return cls._registry[name]
|
||||
except KeyError:
|
||||
raise cls.UnknownStorageModule
|
||||
|
||||
@classmethod
|
||||
def get_as_choices(cls):
|
||||
return cls._registry.items()
|
||||
|
||||
def get_arguments(self):
|
||||
return []
|
||||
|
||||
def is_local_realm(self):
|
||||
return self.realm == REALM_LOCAL
|
||||
@@ -152,7 +222,7 @@ class StorageModuleBase(object):
|
||||
def is_remote_realm(self):
|
||||
return self.realm == REALM_REMOTE
|
||||
|
||||
def backup(self, data):
|
||||
def backup(self, data, dry_run):
|
||||
raise NotImplemented
|
||||
|
||||
def restore(self):
|
||||
@@ -160,18 +230,24 @@ class StorageModuleBase(object):
|
||||
Must return data or a file like object
|
||||
"""
|
||||
raise NotImplemented
|
||||
|
||||
def __unicode__(self):
|
||||
return unicode(self.label)
|
||||
|
||||
|
||||
class TestStorageModule(StorageModuleBase):
|
||||
name = 'test_storage'
|
||||
label = _(u'Test storage module')
|
||||
realm = StorageModuleBase.REALM_LOCAL
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.backup_path = kwargs.pop('backup_path', None)
|
||||
self.restore_path = kwargs.pop('restore_path', None)
|
||||
return super(TestStorageModule, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_arguments(self):
|
||||
return ['backup_path', 'restore_path']
|
||||
|
||||
def backup(self, data):
|
||||
def backup(self, data, dry_run):
|
||||
print '***** received data'
|
||||
print data
|
||||
print '***** saving to path: %s' % self.backup_path
|
||||
|
||||
Reference in New Issue
Block a user