From 817e2d6eeaf2194e04f1100890fc07d7ca4c7e41 Mon Sep 17 00:00:00 2001 From: Robert Myers Date: Wed, 3 Apr 2013 11:10:24 -0500 Subject: [PATCH] Adding a running method to the Backup Model * Adds a query classmethod to Base Model for more complex queries such 'in_' * Adds a running classmethod to Backup Model which returns any currently running backups for an instance. * Add a checksum field to the Backup Model. * Refactor the DBBackup to reuse base methods Change-Id: Iaf6903af88c7f4caa20921f1ca63354999ec6348 --- reddwarf/backup/models.py | 60 +++++++++++++++---- reddwarf/db/models.py | 4 ++ .../migrate_repo/versions/012_backup.py | 1 + .../unittests/backup/test_backup_models.py | 56 +++++++++++++---- 4 files changed, 97 insertions(+), 24 deletions(-) diff --git a/reddwarf/backup/models.py b/reddwarf/backup/models.py index 3b0e780ff0..33281d4466 100644 --- a/reddwarf/backup/models.py +++ b/reddwarf/backup/models.py @@ -16,10 +16,7 @@ from reddwarf.common import cfg from reddwarf.common import exception -from reddwarf.common import utils - from reddwarf.db.models import DatabaseModelBase - from reddwarf.openstack.common import log as logging CONF = cfg.CONF @@ -33,6 +30,8 @@ class BackupState(object): SAVING = "SAVING" COMPLETED = "COMPLETED" FAILED = "FAILED" + RUNNING_STATES = [NEW, BUILDING, SAVING] + END_STATES = [COMPLETED, FAILED] class Backup(object): @@ -60,6 +59,37 @@ class Backup(object): LOG.exception("Unable to create Backup record:") raise exception.BackupCreationError(str(ex)) + @classmethod + def running(cls, instance_id, exclude=None): + """ + Returns the first running backup for instance_id + :param instance_id: Id of the instance + :param exclude: Backup ID to exclude from the query (any other running) + """ + query = DBBackup.query() + query = query.filter(DBBackup.instance_id == instance_id, + DBBackup.state.in_(BackupState.RUNNING_STATES)) + # filter out deleted backups, PEP8 does not like field == False! + query = query.filter_by(deleted=False) + if exclude: + query = query.filter(DBBackup.id != exclude) + return query.first() + + @classmethod + def get_by_id(cls, backup_id, deleted=False): + """ + get the backup for that id + :param cls: + :param backup_id: Id of the backup to return + :param deleted: Return deleted backups + :return: + """ + try: + db_info = DBBackup.find_by(id=backup_id, deleted=deleted) + return db_info + except exception.NotFound: + raise exception.NotFound(uuid=backup_id) + @classmethod def list(cls, context): """ @@ -85,22 +115,17 @@ class Backup(object): return db_info @classmethod - def delete(cls, id): + def delete(cls, backup_id): """ update Backup table on deleted flag for given Backup :param cls: - :param id: Backup uuid + :param backup_id: Backup uuid :return: """ #TODO: api (service.py) might take care of actual deletion # on remote swift - try: - db_info = DBBackup.find_by(id=id, deleted=False) - db_info.update(deleted=True, deleted_at=utils.utcnow()) - except exception.ReddwarfError as ex: - LOG.exception("Backup record cannot be updated for " - "backup %s :") % id - raise exception.BackupUpdateError(str(ex)) + db_info = cls.get_by_id(backup_id) + db_info.delete() def persisted_models(): @@ -111,5 +136,14 @@ class DBBackup(DatabaseModelBase): """A table for Backup records""" _data_fields = ['id', 'name', 'description', 'location', 'backup_type', 'size', 'tenant_id', 'state', 'instance_id', - 'backup_timestamp', 'deleted', 'created', + 'checksum', 'backup_timestamp', 'deleted', 'created', 'updated', 'deleted_at'] + preserve_on_delete = True + + @property + def is_running(self): + return self.state in BackupState.RUNNING_STATES + + @property + def is_done(self): + return self.state in BackupState.END_STATES diff --git a/reddwarf/db/models.py b/reddwarf/db/models.py index ceb27ce514..bf7bde82f7 100644 --- a/reddwarf/db/models.py +++ b/reddwarf/db/models.py @@ -47,6 +47,10 @@ class DatabaseModelBase(models.ModelBase): def preserve_on_delete(self): return hasattr(self, 'deleted') and hasattr(self, 'deleted_at') + @classmethod + def query(cls): + return get_db_api()._base_query(cls) + def save(self): if not self.is_valid(): raise exception.InvalidModelError(errors=self.errors) diff --git a/reddwarf/db/sqlalchemy/migrate_repo/versions/012_backup.py b/reddwarf/db/sqlalchemy/migrate_repo/versions/012_backup.py index 9c8db28434..e42f1ff6b6 100644 --- a/reddwarf/db/sqlalchemy/migrate_repo/versions/012_backup.py +++ b/reddwarf/db/sqlalchemy/migrate_repo/versions/012_backup.py @@ -36,6 +36,7 @@ backups = Table('backups', meta, Column('tenant_id', String(36)), Column('state', String(32), nullable=False), Column('instance_id', String(36)), + Column('checksum', String(32)), Column('backup_timestamp', DateTime()), Column('deleted', Boolean()), Column('created', DateTime()), diff --git a/reddwarf/tests/unittests/backup/test_backup_models.py b/reddwarf/tests/unittests/backup/test_backup_models.py index 48b7ad1a58..40eff47913 100644 --- a/reddwarf/tests/unittests/backup/test_backup_models.py +++ b/reddwarf/tests/unittests/backup/test_backup_models.py @@ -57,11 +57,11 @@ class BackupORMTest(testtools.TestCase): super(BackupORMTest, self).setUp() util.init_db() self.context, self.instance_id = _prep_conf(utils.utcnow()) - models.DBBackup.create(tenant_id=self.context.tenant, - name=BACKUP_NAME, - state=BACKUP_STATE, - instance_id=self.instance_id, - deleted=False) + self.backup = models.DBBackup.create(tenant_id=self.context.tenant, + name=BACKUP_NAME, + state=BACKUP_STATE, + instance_id=self.instance_id, + deleted=False) self.deleted = False def tearDown(self): @@ -82,11 +82,45 @@ class BackupORMTest(testtools.TestCase): db_record = models.Backup.list_for_instance(self.instance_id) self.assertEqual(2, db_record.count()) + def test_running(self): + running = models.Backup.running(instance_id=self.instance_id) + self.assertTrue(running) + + def test_not_running(self): + not_running = models.Backup.running(instance_id='non-existent') + self.assertFalse(not_running) + + def test_running_exclude(self): + not_running = models.Backup.running(instance_id=self.instance_id, + exclude=self.backup.id) + self.assertFalse(not_running) + + def test_is_running(self): + self.assertTrue(self.backup.is_running) + + def test_is_done(self): + self.backup.state = models.BackupState.COMPLETED + self.backup.save() + self.assertTrue(self.backup.is_done) + + def test_not_is_running(self): + self.backup.state = models.BackupState.COMPLETED + self.backup.save() + self.assertFalse(self.backup.is_running) + + def test_not_is_done(self): + self.assertFalse(self.backup.is_done) + + def test_backup_delete(self): + models.Backup.delete(self.backup.id) + query = models.Backup.list_for_instance(self.instance_id) + self.assertEqual(query.count(), 0) + def test_delete(self): - db_record = models.DBBackup.find_by(tenant_id=self.context.tenant) - uuid = db_record['id'] - print uuid - models.Backup.delete(uuid) - self.deleted = True - db_record = models.DBBackup.find_by(id=uuid, deleted=True) + self.backup.delete() + db_record = models.DBBackup.find_by(id=self.backup.id, deleted=True) self.assertEqual(self.instance_id, db_record['instance_id']) + + def test_deleted_not_running(self): + self.backup.delete() + self.assertFalse(models.Backup.running(self.instance_id))