mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 23:51:09 +03:00
Merge pull request #283 from chrismeyersfsu/feature-age_deleted
Added age_deleted command for support team
This commit is contained in:
commit
c7e2c333d4
129
awx/main/management/commands/age_deleted.py
Normal file
129
awx/main/management/commands/age_deleted.py
Normal file
@ -0,0 +1,129 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
import datetime
|
||||
from optparse import make_option
|
||||
|
||||
# Django
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.timezone import now, is_aware, make_aware
|
||||
from django.core.management.base import CommandError
|
||||
|
||||
# AWX
|
||||
from awx.main.models import * # noqa
|
||||
|
||||
class Command(BaseCommand):
|
||||
'''
|
||||
Management command to age deleted items.
|
||||
'''
|
||||
|
||||
help = 'Age deleted items in the database.'
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option('--days', dest='days', type='int', default=90, metavar='N',
|
||||
help='Age deleted items N days (90 if not specified)'),
|
||||
make_option('--id', dest='id', type='int', default=None,
|
||||
help='Object primary key'),
|
||||
make_option('--type', dest='type', default=None,
|
||||
help='Model to limit aging to'),
|
||||
)
|
||||
|
||||
def get_models(self, model):
|
||||
if not model._meta.abstract:
|
||||
yield model
|
||||
for sub in model.__subclasses__():
|
||||
for submodel in self.get_models(sub):
|
||||
yield submodel
|
||||
|
||||
def cleanup_model(self, model, id=None):
|
||||
'''
|
||||
Presume the '_deleted_' string to be in the 'name' field unless considering the User model.
|
||||
When considering the User model, presume the '_d_' string to be in the 'username' field.
|
||||
'''
|
||||
name_field = 'name'
|
||||
name_prefix = '_deleted_'
|
||||
n_aged_items = 0
|
||||
if model is User:
|
||||
name_field = 'username'
|
||||
name_prefix = '_d_'
|
||||
active_field = None
|
||||
for field in model._meta.fields:
|
||||
if field.name in ('is_active', 'active'):
|
||||
active_field = field.name
|
||||
if not active_field:
|
||||
#print("Skipping model %s, no active field" % model)
|
||||
print("Returning %s" % n_aged_items)
|
||||
return n_aged_items
|
||||
|
||||
kv = {
|
||||
active_field: False,
|
||||
}
|
||||
if id:
|
||||
kv['pk'] = id
|
||||
else:
|
||||
kv['%s__startswith' % name_field] = name_prefix
|
||||
|
||||
qs = model.objects.filter(**kv)
|
||||
#print("Aging model %s" % model)
|
||||
for instance in qs:
|
||||
name = getattr(instance, name_field)
|
||||
name_pieces = name.split('_')
|
||||
if not name_pieces or len(name_pieces) < 3:
|
||||
print("Unexpected deleted model name format %s" % name)
|
||||
return n_aged_items
|
||||
|
||||
if len(name_pieces) <= 3:
|
||||
name_append = ''
|
||||
else:
|
||||
name_append = '_' + name_pieces[3]
|
||||
|
||||
dt = parse_datetime(name_pieces[2])
|
||||
if not is_aware(dt):
|
||||
dt = make_aware(dt, self.cutoff.tzinfo)
|
||||
if not dt:
|
||||
print('unable to find deleted timestamp in %s field' % name_field)
|
||||
else:
|
||||
aged_date = dt - datetime.timedelta(days=self.days)
|
||||
instance.name = name_prefix + aged_date.isoformat() + name_append
|
||||
instance.save()
|
||||
#print("Aged %s" % instance.name)
|
||||
n_aged_items += 1
|
||||
|
||||
|
||||
|
||||
return n_aged_items
|
||||
|
||||
@transaction.atomic
|
||||
def handle(self, *args, **options):
|
||||
self.days = int(options.get('days', 90))
|
||||
self.id = options.get('id', None)
|
||||
self.type = options.get('type', None)
|
||||
self.cutoff = now() - datetime.timedelta(days=self.days)
|
||||
|
||||
if self.id and not self.type:
|
||||
raise CommandError('Specifying id requires --type')
|
||||
|
||||
n_aged_items = 0
|
||||
if not self.type:
|
||||
n_aged_items += self.cleanup_model(User)
|
||||
for model in self.get_models(PrimordialModel):
|
||||
n_aged_items += self.cleanup_model(model)
|
||||
else:
|
||||
model_found = None
|
||||
if self.type == User.__name__:
|
||||
model_found = User
|
||||
else:
|
||||
for model in self.get_models(PrimordialModel):
|
||||
if model.__name__ == self.type:
|
||||
model_found = model
|
||||
break
|
||||
if not model_found:
|
||||
raise RuntimeError("Invalid type %s" % self.type)
|
||||
n_aged_items += self.cleanup_model(model_found, self.id)
|
||||
|
||||
print("Aged %d items" % n_aged_items)
|
||||
|
@ -6,3 +6,4 @@ from __future__ import absolute_import
|
||||
from .run_fact_cache_receiver import * # noqa
|
||||
from .commands_monolithic import * # noqa
|
||||
from .cleanup_facts import * # noqa
|
||||
from .age_deleted import * # noqa
|
||||
|
34
awx/main/tests/commands/age_deleted.py
Normal file
34
awx/main/tests/commands/age_deleted.py
Normal file
@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2015 Ansible, Inc.
|
||||
# All Rights Reserved
|
||||
|
||||
# AWX
|
||||
from awx.main.tests.base import BaseTest
|
||||
from awx.main.tests.commands.base import BaseCommandMixin
|
||||
|
||||
__all__ = ['AgeDeletedCommandFunctionalTest']
|
||||
|
||||
class AgeDeletedCommandFunctionalTest(BaseCommandMixin, BaseTest):
|
||||
def setUp(self):
|
||||
super(AgeDeletedCommandFunctionalTest, self).setUp()
|
||||
self.create_test_license_file()
|
||||
self.setup_instances()
|
||||
self.setup_users()
|
||||
self.organization = self.make_organization(self.super_django_user)
|
||||
self.credential = self.make_credential()
|
||||
self.credential2 = self.make_credential()
|
||||
self.credential.mark_inactive(True)
|
||||
self.credential2.mark_inactive(True)
|
||||
self.credential_active = self.make_credential()
|
||||
self.super_django_user.mark_inactive(True)
|
||||
|
||||
def test_default(self):
|
||||
result, stdout, stderr = self.run_command('age_deleted')
|
||||
self.assertEqual(stdout, 'Aged %d items\n' % 3)
|
||||
|
||||
def test_type(self):
|
||||
result, stdout, stderr = self.run_command('age_deleted', type='Credential')
|
||||
self.assertEqual(stdout, 'Aged %d items\n' % 2)
|
||||
|
||||
def test_id_type(self):
|
||||
result, stdout, stderr = self.run_command('age_deleted', type='Credential', id=self.credential.pk)
|
||||
self.assertEqual(stdout, 'Aged %d items\n' % 1)
|
Loading…
Reference in New Issue
Block a user