From 0e9cda65476c8105861aad41dcde8065fc8fe735 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sat, 11 Aug 2012 08:40:47 -0400 Subject: [PATCH] Add backup job list, create, edit, test views --- apps/backups/__init__.py | 16 ++- apps/backups/api.py | 104 +++++++++++++++--- apps/backups/forms.py | 23 ++++ apps/backups/links.py | 10 +- apps/backups/migrations/0001_initial.py | 55 +++++++++ ...ckupjob_label__add_field_backupjob_name.py | 45 ++++++++ ...ge_module__add_field_backupjob_storage_.py | 45 ++++++++ .../0004_auto__add_field_backupjob_enabled.py | 40 +++++++ apps/backups/migrations/__init__.py | 0 apps/backups/models.py | 75 ++++++++++++- apps/backups/permissions.py | 12 ++ apps/backups/urls.py | 6 +- apps/backups/views.py | 97 +++++++++++++++- apps/documents/__init__.py | 4 +- 14 files changed, 506 insertions(+), 26 deletions(-) create mode 100644 apps/backups/forms.py create mode 100644 apps/backups/migrations/0001_initial.py create mode 100644 apps/backups/migrations/0002_auto__del_field_backupjob_label__add_field_backupjob_name.py create mode 100644 apps/backups/migrations/0003_auto__del_field_backupjob_storage_module__add_field_backupjob_storage_.py create mode 100644 apps/backups/migrations/0004_auto__add_field_backupjob_enabled.py create mode 100644 apps/backups/migrations/__init__.py create mode 100644 apps/backups/permissions.py diff --git a/apps/backups/__init__.py b/apps/backups/__init__.py index 2c6779eaff..0842371b8f 100644 --- a/apps/backups/__init__.py +++ b/apps/backups/__init__.py @@ -5,10 +5,12 @@ from django.utils.translation import ugettext_lazy as _ from job_processor.models import JobQueue, JobType from job_processor.exceptions import JobQueuePushError -from navigation.api import bind_links +from navigation.api import bind_links, register_model_list_columns from project_tools.api import register_tool +from project_setup.api import register_setup -from .links import backup_tool_link, restore_tool_link +from .links import backup_tool_link, restore_tool_link, backup_job_list, backup_job_create, backup_job_edit, backup_job_test +from .models import BackupJob # TODO: move to literals BACKUP_JOB_QUEUE_NAME = 'backups_queue' @@ -26,5 +28,13 @@ def create_backups_job_queue(): create_backups_job_queue() #backup_job_type = JobType('remote_backup', _(u'Remove backup'), do_backup) -register_tool(backup_tool_link) +register_setup(backup_tool_link) register_tool(restore_tool_link) +bind_links([BackupJob, 'backup_job_list', 'backup_job_create'], [backup_job_list], menu_name='secondary_menu') +bind_links([BackupJob, 'backup_job_list', 'backup_job_create'], [backup_job_create], menu_name='sidebar') +bind_links([BackupJob], [backup_job_edit, backup_job_test]) + +register_model_list_columns(BackupJob, [ + {'name':_(u'begin date time'), 'attribute': 'begin_datetime'}, + {'name':_(u'storage module'), 'attribute': 'storage_module'}, +]) diff --git a/apps/backups/api.py b/apps/backups/api.py index 83d0090e34..b822dcd1ec 100644 --- a/apps/backups/api.py +++ b/apps/backups/api.py @@ -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 diff --git a/apps/backups/forms.py b/apps/backups/forms.py new file mode 100644 index 0000000000..2c57f703a0 --- /dev/null +++ b/apps/backups/forms.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import + +from django import forms + +from .models import BackupJob + + +class BackupJobForm(forms.ModelForm): + #expiration_datetime = SplitTimeDeltaField() + + class Meta: + model = BackupJob + #exclude = ('checkout_datetime', 'user_content_type', 'user_object_id') + + #widgets = { + # 'document': forms.widgets.HiddenInput(), + #} + + #def clean_document(self): + # document = self.cleaned_data['document'] + # if document.is_checked_out(): + # raise DocumentAlreadyCheckedOut + # return document diff --git a/apps/backups/links.py b/apps/backups/links.py index 43117d73d4..87c1cc8614 100644 --- a/apps/backups/links.py +++ b/apps/backups/links.py @@ -4,7 +4,13 @@ from django.utils.translation import ugettext_lazy as _ from navigation.api import Link -#from .permissions import +from .permissions import PERMISSION_BACKUP_JOB_VIEW, PERMISSION_BACKUP_JOB_CREATE, PERMISSION_BACKUP_JOB_EDIT, PERMISSION_BACKUP_JOB_DELETE + +backup_tool_link = Link(text=_(u'backups'), view='backup_job_list', icon='cd_burn.png', permissions=[PERMISSION_BACKUP_JOB_VIEW]) +backup_job_list = Link(text=_(u'backup job list'), view='backup_job_list', sprite='cd_burn', permissions=[PERMISSION_BACKUP_JOB_VIEW]) +backup_job_create = Link(text=_(u'create'), view='backup_job_create', sprite='cd_add', permissions=[PERMISSION_BACKUP_JOB_CREATE]) +backup_job_edit = Link(text=_(u'edit'), view='backup_job_edit', args='object.pk', sprite='cd_edit', permissions=[PERMISSION_BACKUP_JOB_EDIT]) +backup_job_test = Link(text=_(u'test'), view='backup_job_test', args='object.pk', sprite='cd_go')#, permissions=[PERMISSION_BACKUP_JOB_TEST]) +backup_job_delete = Link(text=_(u'delete'), view='backup_job_delete', args='object.pk', sprite='cd_delete', permissions=[PERMISSION_BACKUP_JOB_DELETE]) -backup_tool_link = Link(text=_(u'backup'), view='backup_view', icon='cd_burn.png')#, permissions=[]) restore_tool_link = Link(text=_(u'restore'), view='restore_view', icon='cd_eject.png')#, permissions=[]) diff --git a/apps/backups/migrations/0001_initial.py b/apps/backups/migrations/0001_initial.py new file mode 100644 index 0000000000..e9bc72946a --- /dev/null +++ b/apps/backups/migrations/0001_initial.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'BackupJob' + db.create_table('backups_backupjob', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('label', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('begin_datetime', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2012, 8, 11, 0, 0))), + ('storage_module', self.gf('django.db.models.fields.CharField')(max_length=16)), + ('storage_arguments_json', self.gf('django.db.models.fields.TextField')(blank=True)), + )) + db.send_create_signal('backups', ['BackupJob']) + + # Adding model 'BackupJobApp' + db.create_table('backups_backupjobapp', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('backup_job', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['backups.BackupJob'])), + ('app_backup', self.gf('django.db.models.fields.CharField')(max_length=64)), + )) + db.send_create_signal('backups', ['BackupJobApp']) + + + def backwards(self, orm): + # Deleting model 'BackupJob' + db.delete_table('backups_backupjob') + + # Deleting model 'BackupJobApp' + db.delete_table('backups_backupjobapp') + + + models = { + 'backups.backupjob': { + 'Meta': {'object_name': 'BackupJob'}, + 'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 11, 0, 0)'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'storage_module': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'backups.backupjobapp': { + 'Meta': {'object_name': 'BackupJobApp'}, + 'app_backup': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'backup_job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backups.BackupJob']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['backups'] \ No newline at end of file diff --git a/apps/backups/migrations/0002_auto__del_field_backupjob_label__add_field_backupjob_name.py b/apps/backups/migrations/0002_auto__del_field_backupjob_label__add_field_backupjob_name.py new file mode 100644 index 0000000000..cb2f24e520 --- /dev/null +++ b/apps/backups/migrations/0002_auto__del_field_backupjob_label__add_field_backupjob_name.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting field 'BackupJob.label' + db.delete_column('backups_backupjob', 'label') + + # Adding field 'BackupJob.name' + db.add_column('backups_backupjob', 'name', + self.gf('django.db.models.fields.CharField')(default=' ', max_length=64), + keep_default=False) + + + def backwards(self, orm): + + # User chose to not deal with backwards NULL issues for 'BackupJob.label' + raise RuntimeError("Cannot reverse this migration. 'BackupJob.label' and its values cannot be restored.") + # Deleting field 'BackupJob.name' + db.delete_column('backups_backupjob', 'name') + + + models = { + 'backups.backupjob': { + 'Meta': {'object_name': 'BackupJob'}, + 'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 11, 0, 0)'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'storage_module': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'backups.backupjobapp': { + 'Meta': {'object_name': 'BackupJobApp'}, + 'app_backup': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'backup_job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backups.BackupJob']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['backups'] \ No newline at end of file diff --git a/apps/backups/migrations/0003_auto__del_field_backupjob_storage_module__add_field_backupjob_storage_.py b/apps/backups/migrations/0003_auto__del_field_backupjob_storage_module__add_field_backupjob_storage_.py new file mode 100644 index 0000000000..1d0479cf68 --- /dev/null +++ b/apps/backups/migrations/0003_auto__del_field_backupjob_storage_module__add_field_backupjob_storage_.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting field 'BackupJob.storage_module' + db.delete_column('backups_backupjob', 'storage_module') + + # Adding field 'BackupJob.storage_module_name' + db.add_column('backups_backupjob', 'storage_module_name', + self.gf('django.db.models.fields.CharField')(default=' ', max_length=16), + keep_default=False) + + + def backwards(self, orm): + + # User chose to not deal with backwards NULL issues for 'BackupJob.storage_module' + raise RuntimeError("Cannot reverse this migration. 'BackupJob.storage_module' and its values cannot be restored.") + # Deleting field 'BackupJob.storage_module_name' + db.delete_column('backups_backupjob', 'storage_module_name') + + + models = { + 'backups.backupjob': { + 'Meta': {'object_name': 'BackupJob'}, + 'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 11, 0, 0)'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'storage_module_name': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'backups.backupjobapp': { + 'Meta': {'object_name': 'BackupJobApp'}, + 'app_backup': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'backup_job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backups.BackupJob']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['backups'] \ No newline at end of file diff --git a/apps/backups/migrations/0004_auto__add_field_backupjob_enabled.py b/apps/backups/migrations/0004_auto__add_field_backupjob_enabled.py new file mode 100644 index 0000000000..8cbf704a3f --- /dev/null +++ b/apps/backups/migrations/0004_auto__add_field_backupjob_enabled.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'BackupJob.enabled' + db.add_column('backups_backupjob', 'enabled', + self.gf('django.db.models.fields.BooleanField')(default=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'BackupJob.enabled' + db.delete_column('backups_backupjob', 'enabled') + + + models = { + 'backups.backupjob': { + 'Meta': {'object_name': 'BackupJob'}, + 'begin_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2012, 8, 11, 0, 0)'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'storage_arguments_json': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'storage_module_name': ('django.db.models.fields.CharField', [], {'max_length': '16'}) + }, + 'backups.backupjobapp': { + 'Meta': {'object_name': 'BackupJobApp'}, + 'app_backup': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'backup_job': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['backups.BackupJob']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['backups'] \ No newline at end of file diff --git a/apps/backups/migrations/__init__.py b/apps/backups/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/backups/models.py b/apps/backups/models.py index 71a8362390..efe384decc 100644 --- a/apps/backups/models.py +++ b/apps/backups/models.py @@ -1,3 +1,74 @@ -from django.db import models +from __future__ import absolute_import -# Create your models here. +import logging +import datetime + +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes import generic + +from .api import AppBackup, StorageModuleBase + +logger = logging.getLogger(__name__) + + +class BackupJob(models.Model): + name = models.CharField(max_length=64, verbose_name=_(u'name')) + enabled = models.BooleanField(default=True, verbose_name=_(u'enabled')) + begin_datetime = models.DateTimeField(verbose_name=_(u'begin date and time'), default=lambda: datetime.datetime.now()) + + # * repetition = + # day - 1 days + # weekly - days of week checkbox + # month - day of month, day of week + # * repetition option field + # * ends + # - never + # - After # ocurrences + # - On date + # * end option field + # * type + # - Full + # - Incremental + storage_module_name = models.CharField(max_length=16, choices=StorageModuleBase.get_as_choices(), verbose_name=_(u'storage module')) + storage_arguments_json = models.TextField(verbose_name=_(u'storage module arguments (in JSON)'), blank=True) + + @property + def apps(self): + return self.backupjobapp_set + + def __unicode__(self): + return self.name + + @property + def storage_module(self): + return StorageModuleBase.get(self.storage_module_name) + + def backup(self, dry_run=False): + logger.debug('starting: %s', self) + logger.debug('dry_run: %s' % dry_run) + storage_module = self.storage_module + #TODO: loads + for app in self.apps.all(): + app.backup(storage_module(backup_path='/tmp', dry_run=dry_run), dry_run=dry_run) + + def save(self, *args, **kwargs): + #dump + super(BackupJob, self).save(*args, **kwargs) + + @models.permalink + def get_absolute_url(self): + return ('checkout_info', [self.document.pk]) + + class Meta: + verbose_name = _(u'document checkout') + verbose_name_plural = _(u'document checkouts') + + +class BackupJobApp(models.Model): + backup_job = models.ForeignKey(BackupJob) + app_backup = models.CharField(max_length=64, choices=AppBackup.get_as_choices()) + + +#class BackupJobLog diff --git a/apps/backups/permissions.py b/apps/backups/permissions.py new file mode 100644 index 0000000000..85412e30e9 --- /dev/null +++ b/apps/backups/permissions.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +from django.utils.translation import ugettext_lazy as _ + +from permissions.models import PermissionNamespace, Permission + +namespace = PermissionNamespace('backups', _(u'Backups')) + +PERMISSION_BACKUP_JOB_VIEW = Permission.objects.register(namespace, 'backup_job_view', _(u'View a backup job')) +PERMISSION_BACKUP_JOB_CREATE = Permission.objects.register(namespace, 'backup_job_view', _(u'Create backup jobs')) +PERMISSION_BACKUP_JOB_EDIT = Permission.objects.register(namespace, 'backup_job_edit', _(u'Edit an existing backup jobs')) +PERMISSION_BACKUP_JOB_DELETE = Permission.objects.register(namespace, 'backup_job_delete', _(u'Delete an existing backup jobs')) diff --git a/apps/backups/urls.py b/apps/backups/urls.py index 0e94bc9070..c6f5f47084 100644 --- a/apps/backups/urls.py +++ b/apps/backups/urls.py @@ -1,5 +1,9 @@ from django.conf.urls.defaults import patterns, url urlpatterns = patterns('backups.views', - url(r'^backup/$', 'backup_view', (), 'backup_view'), + url(r'^jobs/list/$', 'backup_job_list', (), 'backup_job_list'), + url(r'^jobs/create/$', 'backup_job_create', (), 'backup_job_create'), + url(r'^jobs/(?P\d+)/edit/$', 'backup_job_edit', (), 'backup_job_edit'), + url(r'^jobs/(?P\d+)/test/$', 'backup_job_test', (), 'backup_job_test'), + #url(r'^jobs/(?P\d+)/delete/$', 'backup_job_delete', (), 'backup_job_delete'), ) diff --git a/apps/backups/views.py b/apps/backups/views.py index ce930ab1f5..a0901ab1ea 100644 --- a/apps/backups/views.py +++ b/apps/backups/views.py @@ -2,7 +2,7 @@ from __future__ import absolute_import from django.utils.translation import ugettext_lazy as _ from django.http import HttpResponseRedirect -from django.shortcuts import render_to_response +from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext from django.contrib import messages from django.core.urlresolvers import reverse @@ -10,7 +10,100 @@ from django.core.urlresolvers import reverse from permissions.models import Permission from .api import AppBackup, TestStorageModule -#from .permissions import +from .models import BackupJob +from .forms import BackupJobForm +from .permissions import PERMISSION_BACKUP_JOB_VIEW, PERMISSION_BACKUP_JOB_CREATE, PERMISSION_BACKUP_JOB_EDIT + + +def backup_job_list(request): + pre_object_list = BackupJob.objects.all() + + try: + Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_VIEW]) + except PermissionDenied: + # If user doesn't have global permission, get a list of backup jobs + # for which he/she does have access use it to filter the + # provided object_list + final_object_list = AccessEntry.objects.filter_objects_by_access(PERMISSION_BACKUP_JOB_VIEW, request.user, pre_object_list) + else: + final_object_list = pre_object_list + + context = { + 'object_list': final_object_list, + 'title': _(u'backup jobs'), + 'hide_link': True, + #'extra_columns': [ + # {'name': _(u'info'), 'attribute': 'info'}, + #], + } + return render_to_response('generic_list.html', context, + context_instance=RequestContext(request)) + + +def backup_job_create(request): + Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_CREATE]) + + if request.method == 'POST': + form = BackupJobForm(data=request.POST) + if form.is_valid(): + try: + backup_job = form.save() + except Exception, exc: + messages.error(request, _(u'Error creating backup job; %s') % exc) + else: + messages.success(request, _(u'Backup job "%s" created successfully.') % backup_job) + return HttpResponseRedirect(reverse('backup_job_list')) + else: + form = BackupJobForm() + + return render_to_response('generic_form.html', { + 'form': form, + 'title': _(u'Create backup job') + }, context_instance=RequestContext(request)) + + +def backup_job_edit(request, backup_job_pk): + backup_job = get_object_or_404(BackupJob, pk=backup_job_pk) + try: + Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_EDIT]) + except PermissionDenied: + AccessEntry.objects.check_access(PERMISSION_BACKUP_JOB_EDIT, request.user, backup_job) + + if request.method == 'POST': + form = BackupJobForm(data=request.POST, instance=backup_job) + if form.is_valid(): + try: + backup_job = form.save() + except Exception, exc: + messages.error(request, _(u'Error editing backup job; %s') % exc) + else: + messages.success(request, _(u'Backup job "%s" edited successfully.') % backup_job) + return HttpResponseRedirect(reverse('backup_job_list')) + else: + form = BackupJobForm(instance=backup_job) + + return render_to_response('generic_form.html', { + 'form': form, + 'object': backup_job, + 'title': _(u'Edit backup job: %s') % backup_job + }, context_instance=RequestContext(request)) + + +def backup_job_test(request, backup_job_pk): + backup_job = get_object_or_404(BackupJob, pk=backup_job_pk) + #try: + # Permission.objects.check_permissions(request.user, [PERMISSION_BACKUP_JOB_EDIT]) + #except PermissionDenied: + # AccessEntry.objects.check_access(PERMISSION_BACKUP_JOB_EDIT, request.user, backup_job) + + try: + backup_job.backup(dry_run=True) + except Exception, exc: + messages.error(request, _(u'Error testing backup job; %s') % exc) + return HttpResponseRedirect(reverse('backup_job_list')) + else: + messages.success(request, _(u'Test for backup job "%s" finished successfully.') % backup_job) + return HttpResponseRedirect(reverse('backup_job_list')) def backup_view(request): diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py index 2c92762bf5..4bcfe9faf3 100644 --- a/apps/documents/__init__.py +++ b/apps/documents/__init__.py @@ -14,7 +14,7 @@ from history.permissions import PERMISSION_HISTORY_VIEW from project_setup.api import register_setup from acls.api import class_permissions from statistics.api import register_statistics -from backups.api import AppBackup, ElementBackupModel, ElementBackupFile +from backups.api import AppBackup, ModelBackup, FileBackup from .models import (Document, DocumentPage, DocumentPageTransformation, DocumentType, DocumentTypeFilename, @@ -137,4 +137,4 @@ class_permissions(Document, [ ]) register_statistics(get_statistics) -AppBackup('documents', _(u'Documents'), [ElementBackupModel(), ElementBackupFile(document_settings.STORAGE_BACKEND)]) +AppBackup('documents', _(u'Documents'), [ModelBackup(), FileBackup(document_settings.STORAGE_BACKEND)])