Merge "Add action_purge to senlin-manage"

This commit is contained in:
Zuul 2019-01-25 01:50:52 +00:00 committed by Gerrit Code Review
commit 4f5a61433d
6 changed files with 128 additions and 1 deletions

View File

@ -69,6 +69,20 @@ You can use command purge three days ago data.
senlin-manage event_purge -p e127900ee5d94ff5aff30173aa607765 -g days 3
Senlin Action Manage
--------------------
``senlin-manage action_purge -p [<project1;project2...>] -g {days,hours,minutes,seconds} age``
Purge the specified action records in senlin's database.
You can use this command to purge actions that are older than 3 days.
::
senlin-manage action_purge -p e127900ee5d94ff5aff30173aa607765 -g days 3
FILES
~~~~~

View File

@ -0,0 +1,4 @@
---
features:
- A action_purge subcommand is added to senlin-manage tool for purging actions
from the actions table.

View File

@ -45,7 +45,7 @@ def do_db_sync():
def do_event_purge():
"""Purge the specified event records in senlin's database."""
if CONF.command.age < 0:
print(_("age must be a positive integer."))
print(_("Age must be a positive integer."))
return
api.event_purge(api.get_engine(),
CONF.command.project_id,
@ -53,6 +53,30 @@ def do_event_purge():
CONF.command.age)
def do_action_purge():
"""Purge the specified action records in senlin's database."""
age = CONF.command.age
if age < 0:
print(_("Age must be a positive integer."))
return
if CONF.command.granularity == 'days':
age = age * 86400
elif CONF.command.granularity == 'hours':
age = age * 3600
elif CONF.command.granularity == 'minutes':
age = age * 60
if age < CONF.default_action_timeout:
print(_("Age must be greater than the default action timeout."))
return
api.action_purge(api.get_engine(),
CONF.command.project_id,
CONF.command.granularity,
CONF.command.age)
class ServiceManageCommand(object):
def __init__(self):
self.ctx = context.get_admin_context()
@ -157,6 +181,35 @@ def add_command_parsers(subparsers):
"events created two hours ago. Defaults to "
"30."))
parser = subparsers.add_parser('action_purge')
parser.set_defaults(func=do_action_purge)
parser.add_argument('-p',
'--project-id',
nargs='?',
metavar='<project1;project2...>',
help=_("Purge action records with specified project. "
"This can be specified multiple times, or once "
"with parameters separated by semicolon."),
action='append')
parser.add_argument('-g',
'--granularity',
default='days',
choices=['days', 'hours', 'minutes', 'seconds'],
help=_("Purge action records which were created in "
"the specified time period. The time is "
"specified by age and granularity, whose value "
"must be one of 'days', 'hours', 'minutes' or "
"'seconds' (default)."))
parser.add_argument('age',
type=int,
default=30,
help=_("Purge action records which were created in "
"the specified time period. The time is "
"specified by age and granularity. For "
"example, granularity=hours and age=2 means "
"purging actions created two hours ago. "
"Defaults to 30."))
command_opt = cfg.SubCommandOpt('command',
title='Commands',

View File

@ -529,3 +529,8 @@ def db_version(engine):
def event_purge(engine, project, granularity, age):
"""Purge the event records in database."""
return IMPL.event_purge(project, granularity, age)
def action_purge(engine, project, granularity, age):
"""Purge the action records in database."""
return IMPL.action_purge(project, granularity, age)

View File

@ -1430,6 +1430,29 @@ def action_delete_by_target(context, target, action=None,
return q.delete(synchronize_session='fetch')
@retry_on_deadlock
def action_purge(project, granularity='days', age=30):
with session_for_write() as session:
query = session.query(models.Action).with_for_update()
if project is not None:
query = query.filter(models.Action.project.in_(project))
if granularity is not None and age is not None:
if granularity == 'days':
age = age * 86400
elif granularity == 'hours':
age = age * 3600
elif granularity == 'minutes':
age = age * 60
time_line = timeutils.utcnow() - datetime.timedelta(seconds=age)
query = query.filter(models.Action.created_at < time_line)
# Get dependants to delete
for d in query.all():
q = session.query(models.ActionDependency).filter_by(depended=d.id)
q.delete(synchronize_session='fetch')
return query.delete(synchronize_session='fetch')
# Receivers
@retry_on_deadlock
def receiver_create(context, values):

View File

@ -10,6 +10,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import six
import time
@ -724,3 +725,30 @@ class DBAPIActionTest(base.SenlinTestCase):
after_abandon.status_reason)
self.assertEqual(consts.ACTION_READY, after_abandon.status)
self.assertEqual({'retries': 1}, after_abandon.data)
def test_action_purge(self):
old_timestamp = tu.utcnow(True) - datetime.timedelta(days=6)
spec = {
"owner": "test_owner",
"created_at": old_timestamp
}
_create_action(self.ctx, **spec)
_create_action(self.ctx, **spec)
_create_action(self.ctx, **spec)
new_timestamp = tu.utcnow(True)
spec = {
"owner": "test_owner",
"created_at": new_timestamp
}
_create_action(self.ctx, **spec)
_create_action(self.ctx, **spec)
_create_action(self.ctx, **spec)
actions = db_api.action_get_all(self.ctx)
self.assertEqual(6, len(actions))
db_api.action_purge(project=None, granularity='days', age=5)
actions = db_api.action_get_all(self.ctx)
self.assertEqual(3, len(actions))