Add granularity option to purge_deleted

Allow users to choose how many days, hours, minutes, or seconds to preserve
deleted data.

Closes-bug: #1237195

Change-Id: I9227f5bf53c5b099a7a691a49c4ff2f5f0662098
This commit is contained in:
Liang Chen 2013-10-12 16:08:05 +08:00
parent a1ad08354b
commit b61cd62db6
4 changed files with 73 additions and 17 deletions

View File

@ -48,7 +48,7 @@ def purge_deleted():
"""
Remove database records that have been previously soft deleted
"""
utils.purge_deleted(CONF.command.age)
utils.purge_deleted(CONF.command.age, CONF.command.granularity)
def add_command_parsers(subparsers):
@ -62,8 +62,12 @@ def add_command_parsers(subparsers):
parser = subparsers.add_parser('purge_deleted')
parser.set_defaults(func=purge_deleted)
parser.add_argument('age', nargs='?',
help=_('Number of days to preserve.'))
parser.add_argument('age', nargs='?', default='90',
help=_('How long to preserve deleted data.'))
parser.add_argument(
'-g', '--granularity', default='days',
choices=['days', 'hours', 'minutes', 'seconds'],
help=_('Granularity to use for age argument, defaults to days.'))
command_opt = cfg.SubCommandOpt('command',
title='Commands',

View File

@ -470,18 +470,26 @@ def watch_data_get_all(context):
return results
def purge_deleted(age):
if age is not None:
try:
age = int(age)
except ValueError:
raise exception.Error(_("age should be an integer"))
if age < 0:
raise exception.Error(_("age should be a positive integer"))
else:
age = 90
def purge_deleted(age, granularity='days'):
try:
age = int(age)
except ValueError:
raise exception.Error(_("age should be an integer"))
if age < 0:
raise exception.Error(_("age should be a positive integer"))
time_line = datetime.now() - timedelta(days=age)
if granularity not in ('days', 'hours', 'minutes', 'seconds'):
raise exception.Error(
_("granularity should be days, hours, minutes, or seconds"))
if granularity == 'days':
age = age * 86400
elif granularity == 'hours':
age = age * 3600
elif granularity == 'minutes':
age = age * 60
time_line = datetime.now() - timedelta(seconds=age)
engine = get_engine()
meta = sqlalchemy.MetaData()
meta.bind = engine

View File

@ -45,5 +45,5 @@ IMPL = LazyPluggable('db_backend',
sqlalchemy='heat.db.sqlalchemy.api')
def purge_deleted(age):
IMPL.purge_deleted(age)
def purge_deleted(age, granularity='days'):
IMPL.purge_deleted(age, granularity)

View File

@ -10,9 +10,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import mox
from datetime import datetime
from datetime import timedelta
import fixtures
from json import loads
from json import dumps
import mox
from heat.db.sqlalchemy import api as db_api
from heat.engine import environment
@ -659,6 +664,45 @@ class DBAPIStackTest(HeatTestCase):
self.assertEqual(2, db_api.stack_count_all_by_tenant(self.ctx))
def test_purge_deleted(self):
now = datetime.now()
delta = timedelta(seconds=3600 * 7)
deleted = [now - delta * i for i in range(1, 6)]
templates = [create_raw_template(self.ctx) for i in range(5)]
creds = [create_user_creds(self.ctx) for i in range(5)]
stacks = [create_stack(self.ctx, templates[i], creds[i],
deleted_at=deleted[i]) for i in range(5)]
class MyDatetime():
def now(self):
return now
self.useFixture(fixtures.MonkeyPatch('heat.db.sqlalchemy.api.datetime',
MyDatetime()))
db_api.purge_deleted(age=1, granularity='days')
self._deleted_stack_existance(utils.dummy_context(), stacks,
(0, 1, 2), (3, 4))
db_api.purge_deleted(age=22, granularity='hours')
self._deleted_stack_existance(utils.dummy_context(), stacks,
(0, 1, 2), (3, 4))
db_api.purge_deleted(age=1100, granularity='minutes')
self._deleted_stack_existance(utils.dummy_context(), stacks,
(0, 1), (2, 3, 4))
db_api.purge_deleted(age=3600, granularity='seconds')
self._deleted_stack_existance(utils.dummy_context(), stacks,
(), (0, 1, 2, 3, 4))
def _deleted_stack_existance(self, ctx, stacks, existing, deleted):
for s in existing:
self.assertIsNotNone(db_api.stack_get(ctx, stacks[s].id,
show_deleted=True))
for s in deleted:
self.assertIsNone(db_api.stack_get(ctx, stacks[s].id,
show_deleted=True))
class DBAPIResourceTest(HeatTestCase):
def setUp(self):