Active flag removal: switched from using mark_inactive to delete calls

This commit is contained in:
Akita Noek
2016-03-10 13:58:00 -05:00
parent 1e7c71edfb
commit ba833d683e
18 changed files with 55 additions and 377 deletions

View File

@@ -7,7 +7,6 @@ import logging
import time import time
# Django # Django
from django.http import Http404
from django.conf import settings from django.conf import settings
from django.db import connection from django.db import connection
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
@@ -415,9 +414,7 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView):
raise PermissionDenied() raise PermissionDenied()
if parent_key: if parent_key:
# sub object has a ForeignKey to the parent, so we can't remove it sub.delete()
# from the set, only mark it as inactive.
sub.mark_inactive()
else: else:
relationship.remove(sub) relationship.remove(sub)
@@ -457,17 +454,9 @@ class RetrieveDestroyAPIView(RetrieveAPIView, generics.RetrieveDestroyAPIView):
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
# somewhat lame that delete has to call it's own permissions check # somewhat lame that delete has to call it's own permissions check
obj = self.get_object() obj = self.get_object()
# FIXME: Why isn't the active check being caught earlier by RBAC?
if not getattr(obj, 'active', True):
raise Http404()
if not getattr(obj, 'is_active', True):
raise Http404()
if not request.user.can_access(self.model, 'delete', obj): if not request.user.can_access(self.model, 'delete', obj):
raise PermissionDenied() raise PermissionDenied()
if hasattr(obj, 'mark_inactive'): obj.delete()
obj.mark_inactive()
else:
raise NotImplementedError('destroy() not implemented yet for %s' % obj)
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class RetrieveUpdateDestroyAPIView(RetrieveUpdateAPIView, RetrieveDestroyAPIView): class RetrieveUpdateDestroyAPIView(RetrieveUpdateAPIView, RetrieveDestroyAPIView):

View File

@@ -1093,8 +1093,6 @@ class UserDetail(RetrieveUpdateDestroyAPIView):
can_delete = request.user.can_access(User, 'delete', obj) can_delete = request.user.can_access(User, 'delete', obj)
if not can_delete: if not can_delete:
raise PermissionDenied('Cannot delete user') raise PermissionDenied('Cannot delete user')
for own_credential in Credential.objects.filter(user=obj):
own_credential.mark_inactive()
return super(UserDetail, self).destroy(request, *args, **kwargs) return super(UserDetail, self).destroy(request, *args, **kwargs)
class UserAccessList(ResourceAccessList): class UserAccessList(ResourceAccessList):
@@ -1400,7 +1398,7 @@ class GroupChildrenList(SubListCreateAttachDetachAPIView):
if sub_id is not None: if sub_id is not None:
return super(GroupChildrenList, self).unattach(request, *args, **kwargs) return super(GroupChildrenList, self).unattach(request, *args, **kwargs)
parent = self.get_parent_object() parent = self.get_parent_object()
parent.mark_inactive() parent.delete()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
def _unattach(self, request, *args, **kwargs): # FIXME: Disabled for now for UI support. def _unattach(self, request, *args, **kwargs): # FIXME: Disabled for now for UI support.
@@ -1424,7 +1422,7 @@ class GroupChildrenList(SubListCreateAttachDetachAPIView):
raise PermissionDenied() raise PermissionDenied()
if sub.parents.filter(active=True).exclude(pk=parent.pk).count() == 0: if sub.parents.filter(active=True).exclude(pk=parent.pk).count() == 0:
sub.mark_inactive() sub.delete()
else: else:
relationship.remove(sub) relationship.remove(sub)
@@ -1526,15 +1524,9 @@ class GroupDetail(RetrieveUpdateDestroyAPIView):
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
obj = self.get_object() obj = self.get_object()
# FIXME: Why isn't the active check being caught earlier by RBAC?
if not getattr(obj, 'active', True):
raise Http404()
if not getattr(obj, 'is_active', True):
raise Http404()
if not request.user.can_access(self.model, 'delete', obj): if not request.user.can_access(self.model, 'delete', obj):
raise PermissionDenied() raise PermissionDenied()
if hasattr(obj, 'mark_inactive'): obj.delete_recursive()
obj.mark_inactive_recursive()
return Response(status=status.HTTP_204_NO_CONTENT) return Response(status=status.HTTP_204_NO_CONTENT)
class GroupAccessList(ResourceAccessList): class GroupAccessList(ResourceAccessList):

View File

@@ -765,7 +765,7 @@ class Command(NoArgsCommand):
del_pks = all_del_pks[offset:(offset + self._batch_size)] del_pks = all_del_pks[offset:(offset + self._batch_size)]
for host in hosts_qs.filter(pk__in=del_pks): for host in hosts_qs.filter(pk__in=del_pks):
host_name = host.name host_name = host.name
host.mark_inactive() host.delete()
self.logger.info('Deleted host "%s"', host_name) self.logger.info('Deleted host "%s"', host_name)
if settings.SQL_DEBUG: if settings.SQL_DEBUG:
self.logger.warning('host deletions took %d queries for %d hosts', self.logger.warning('host deletions took %d queries for %d hosts',
@@ -799,7 +799,8 @@ class Command(NoArgsCommand):
del_pks = all_del_pks[offset:(offset + self._batch_size)] del_pks = all_del_pks[offset:(offset + self._batch_size)]
for group in groups_qs.filter(pk__in=del_pks): for group in groups_qs.filter(pk__in=del_pks):
group_name = group.name group_name = group.name
group.mark_inactive(recompute=False) with ignore_inventory_computed_fields():
group.delete()
self.logger.info('Group "%s" deleted', group_name) self.logger.info('Group "%s" deleted', group_name)
if settings.SQL_DEBUG: if settings.SQL_DEBUG:
self.logger.warning('group deletions took %d queries for %d groups', self.logger.warning('group deletions took %d queries for %d groups',

View File

@@ -203,15 +203,6 @@ class PasswordFieldsModel(BaseModel):
def _password_field_allows_ask(self, field): def _password_field_allows_ask(self, field):
return False # Override in subclasses if needed. return False # Override in subclasses if needed.
def mark_inactive(self, save=True):
'''
When marking a password model inactive we'll clear sensitive fields
'''
for sensitive_field in self.PASSWORD_FIELDS:
setattr(self, sensitive_field, "")
self.save()
super(PasswordFieldsModel, self).mark_inactive(save=save)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
new_instance = not bool(self.pk) new_instance = not bool(self.pk)
# If update_fields has been specified, add our field names to it, # If update_fields has been specified, add our field names to it,

View File

@@ -26,7 +26,7 @@ from awx.main.models.jobs import Job
from awx.main.models.unified_jobs import * # noqa from awx.main.models.unified_jobs import * # noqa
from awx.main.models.mixins import ResourceMixin from awx.main.models.mixins import ResourceMixin
from awx.main.models.notifications import Notifier from awx.main.models.notifications import Notifier
from awx.main.utils import ignore_inventory_computed_fields, _inventory_updates from awx.main.utils import _inventory_updates
from awx.main.conf import tower_settings from awx.main.conf import tower_settings
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'CustomInventoryScript'] __all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'CustomInventoryScript']
@@ -120,18 +120,6 @@ class Inventory(CommonModel, ResourceMixin):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('api:inventory_detail', args=(self.pk,)) return reverse('api:inventory_detail', args=(self.pk,))
def mark_inactive(self, save=True):
'''
When marking inventory inactive, also mark hosts and groups inactive.
'''
with ignore_inventory_computed_fields():
for host in self.hosts.filter(active=True):
host.mark_inactive()
for group in self.groups.filter(active=True):
group.mark_inactive(recompute=False)
for inventory_source in self.inventory_sources.filter(active=True):
inventory_source.mark_inactive()
super(Inventory, self).mark_inactive(save=save)
variables_dict = VarsDictProperty('variables') variables_dict = VarsDictProperty('variables')
@@ -412,15 +400,6 @@ class Host(CommonModelNameNotUnique, ResourceMixin):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('api:host_detail', args=(self.pk,)) return reverse('api:host_detail', args=(self.pk,))
def mark_inactive(self, save=True, from_inventory_import=False, skip_active_check=False):
'''
When marking hosts inactive, remove all associations to related
inventory sources.
'''
super(Host, self).mark_inactive(save=save, skip_active_check=skip_active_check)
if not from_inventory_import:
self.inventory_sources.clear()
def update_computed_fields(self, update_inventory=True, update_groups=True): def update_computed_fields(self, update_inventory=True, update_groups=True):
''' '''
Update model fields that are computed from database relationships. Update model fields that are computed from database relationships.
@@ -575,11 +554,11 @@ class Group(CommonModelNameNotUnique, ResourceMixin):
return reverse('api:group_detail', args=(self.pk,)) return reverse('api:group_detail', args=(self.pk,))
@transaction.atomic @transaction.atomic
def mark_inactive_recursive(self): def delete_recursive(self):
from awx.main.tasks import bulk_inventory_element_delete
from awx.main.utils import ignore_inventory_computed_fields from awx.main.utils import ignore_inventory_computed_fields
from awx.main.signals import disable_activity_stream from awx.main.signals import disable_activity_stream
def mark_actual(): def mark_actual():
all_group_hosts = Group.hosts.through.objects.select_related("host", "group").filter(group__inventory=self.inventory) all_group_hosts = Group.hosts.through.objects.select_related("host", "group").filter(group__inventory=self.inventory)
group_hosts = {'groups': {}, 'hosts': {}} group_hosts = {'groups': {}, 'hosts': {}}
@@ -629,38 +608,13 @@ class Group(CommonModelNameNotUnique, ResourceMixin):
for direct_child in group_children[group]: for direct_child in group_children[group]:
linked_children.append((group, direct_child)) linked_children.append((group, direct_child))
marked_groups.append(group) marked_groups.append(group)
Group.objects.filter(id__in=marked_groups).update(active=False) Group.objects.filter(id__in=marked_groups).delete()
Host.objects.filter(id__in=marked_hosts).update(active=False) Host.objects.filter(id__in=marked_hosts).delete()
Group.parents.through.objects.filter(to_group__id__in=marked_groups) update_inventory_computed_fields.delay(self.inventory.id)
Group.hosts.through.objects.filter(group__id__in=marked_groups)
Group.inventory_sources.through.objects.filter(group__id__in=marked_groups).delete()
bulk_inventory_element_delete.delay(self.inventory.id, groups=marked_groups, hosts=marked_hosts)
with ignore_inventory_computed_fields(): with ignore_inventory_computed_fields():
with disable_activity_stream(): with disable_activity_stream():
mark_actual() mark_actual()
def mark_inactive(self, save=True, recompute=True, from_inventory_import=False, skip_active_check=False):
'''
When marking groups inactive, remove all associations to related
groups/hosts/inventory_sources.
'''
def mark_actual():
super(Group, self).mark_inactive(save=save, skip_active_check=skip_active_check)
self.inventory_source.mark_inactive(save=save)
self.inventory_sources.clear()
self.parents.clear()
self.children.clear()
self.hosts.clear()
i = self.inventory
if from_inventory_import:
super(Group, self).mark_inactive(save=save, skip_active_check=skip_active_check)
elif recompute:
with ignore_inventory_computed_fields():
mark_actual()
i.update_computed_fields()
else:
mark_actual()
def update_computed_fields(self): def update_computed_fields(self):
''' '''

View File

@@ -79,11 +79,6 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
def mark_inactive(self, save=True):
for script in self.custom_inventory_scripts.all():
script.organization = None
script.save()
super(Organization, self).mark_inactive(save=save)
class Team(CommonModelNameNotUnique, ResourceMixin): class Team(CommonModelNameNotUnique, ResourceMixin):
@@ -135,14 +130,6 @@ class Team(CommonModelNameNotUnique, ResourceMixin):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('api:team_detail', args=(self.pk,)) return reverse('api:team_detail', args=(self.pk,))
def mark_inactive(self, save=True):
'''
When marking a team inactive we'll wipe out its credentials also
'''
for cred in self.credentials.all():
cred.mark_inactive()
super(Team, self).mark_inactive(save=save)
class Permission(CommonModelNameNotUnique): class Permission(CommonModelNameNotUnique):
''' '''
@@ -351,22 +338,6 @@ class AuthToken(BaseModel):
return self.key return self.key
# Add mark_inactive method to User model.
def user_mark_inactive(user, save=True):
'''Use instead of delete to rename and mark users inactive.'''
if user.is_active:
# Set timestamp to datetime.isoformat() but without the time zone
# offset to stay withint the 30 character username limit.
dtnow = tz_now()
deleted_ts = dtnow.strftime('%Y-%m-%dT%H:%M:%S.%f')
user.username = '_d_%s' % deleted_ts
user.is_active = False
if save:
user.save()
User.add_to_class('mark_inactive', user_mark_inactive)
# Add get_absolute_url method to User model if not present. # Add get_absolute_url method to User model if not present.
if not hasattr(User, 'get_absolute_url'): if not hasattr(User, 'get_absolute_url'):
def user_get_absolute_url(user): def user_get_absolute_url(user):

View File

@@ -210,17 +210,6 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
self.next_job_run = related_schedules[0].next_run self.next_job_run = related_schedules[0].next_run
self.save(update_fields=['next_schedule', 'next_job_run']) self.save(update_fields=['next_schedule', 'next_job_run'])
def mark_inactive(self, save=True):
'''
When marking a unified job template inactive, also mark its schedules
inactive.
'''
for schedule in self.schedules.filter(active=True):
schedule.mark_inactive()
schedule.enabled = False
schedule.save()
super(UnifiedJobTemplate, self).mark_inactive(save=save)
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
# If update_fields has been specified, add our field names to it, # If update_fields has been specified, add our field names to it,
# if it hasn't been specified, then we're just doing a normal save. # if it hasn't been specified, then we're just doing a normal save.

View File

@@ -265,7 +265,7 @@ def migrate_children_from_inactive_group_to_parent_groups(sender, **kwargs):
if inventory_source_pk: if inventory_source_pk:
try: try:
inventory_source = InventorySource.objects.get(pk=inventory_source_pk, active=True) inventory_source = InventorySource.objects.get(pk=inventory_source_pk, active=True)
inventory_source.mark_inactive() inventory_source.delete()
except InventorySource.DoesNotExist: except InventorySource.DoesNotExist:
pass pass
inventory_pk = getattr(instance, '_saved_inventory_pk', None) inventory_pk = getattr(instance, '_saved_inventory_pk', None)

View File

@@ -110,17 +110,6 @@ def run_administrative_checks(self):
tower_admin_emails, tower_admin_emails,
fail_silently=True) fail_silently=True)
@task()
def bulk_inventory_element_delete(inventory, hosts=[], groups=[]):
from awx.main.signals import disable_activity_stream
with ignore_inventory_computed_fields():
with disable_activity_stream():
for group in groups:
Group.objects.get(id=group).mark_inactive(skip_active_check=True)
for host in hosts:
Host.objects.get(id=host).mark_inactive(skip_active_check=True)
update_inventory_computed_fields(inventory)
@task(bind=True) @task(bind=True)
def tower_periodic_scheduler(self): def tower_periodic_scheduler(self):
def get_last_run(): def get_last_run():

View File

@@ -637,8 +637,8 @@ class AdHocCommandApiTest(BaseAdHocCommandTest):
# Verify that the credential and inventory are null when they have # Verify that the credential and inventory are null when they have
# been deleted, can delete an ad hoc command without inventory or # been deleted, can delete an ad hoc command without inventory or
# credential. # credential.
self.credential.mark_inactive() self.credential.delete()
self.inventory.mark_inactive() self.inventory.delete()
with self.current_user('admin'): with self.current_user('admin'):
response = self.get(url, expect=200) response = self.get(url, expect=200)
self.assertEqual(response['credential'], None) self.assertEqual(response['credential'], None)
@@ -758,7 +758,7 @@ class AdHocCommandApiTest(BaseAdHocCommandTest):
tower_settings.AD_HOC_COMMANDS = ad_hoc_commands tower_settings.AD_HOC_COMMANDS = ad_hoc_commands
# Try to relaunch after the inventory has been marked inactive. # Try to relaunch after the inventory has been marked inactive.
self.inventory.mark_inactive() self.inventory.delete()
with self.current_user('admin'): with self.current_user('admin'):
response = self.get(url, expect=200) response = self.get(url, expect=200)
self.assertEqual(response['passwords_needed_to_start'], []) self.assertEqual(response['passwords_needed_to_start'], [])

View File

@@ -1,34 +0,0 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved
# AWX
from awx.main.tests.base import BaseTest
from command_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)

View File

@@ -15,7 +15,6 @@ import unittest2 as unittest
# Django # Django
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User
from django.core.management import call_command from django.core.management import call_command
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.utils.timezone import now from django.utils.timezone import now
@@ -232,126 +231,6 @@ class DumpDataTest(BaseCommandMixin, BaseTest):
self.assertEqual(result, None) self.assertEqual(result, None)
json.loads(stdout) json.loads(stdout)
class CleanupDeletedTest(BaseCommandMixin, BaseTest):
'''
Test cases for cleanup_deleted management command.
'''
def setUp(self):
self.start_redis()
super(CleanupDeletedTest, self).setUp()
self.create_test_inventories()
def tearDown(self):
super(CleanupDeletedTest, self).tearDown()
self.stop_redis()
def get_model_counts(self):
def get_models(m):
if not m._meta.abstract:
yield m
for sub in m.__subclasses__():
for subm in get_models(sub):
yield subm
counts = {}
for model in get_models(PrimordialModel):
active = model.objects.filter(active=True).count()
inactive = model.objects.filter(active=False).count()
counts[model] = (active, inactive)
return counts
def test_cleanup_our_models(self):
# Test with nothing to be deleted.
counts_before = self.get_model_counts()
self.assertFalse(sum(x[1] for x in counts_before.values()))
result, stdout, stderr = self.run_command('cleanup_deleted')
self.assertEqual(result, None)
counts_after = self.get_model_counts()
self.assertEqual(counts_before, counts_after)
# "Delete" some hosts.
for host in Host.objects.all():
host.mark_inactive()
# With no parameters, "days" defaults to 90, which won't cleanup any of
# the hosts we just removed.
counts_before = self.get_model_counts()
self.assertTrue(sum(x[1] for x in counts_before.values()))
result, stdout, stderr = self.run_command('cleanup_deleted')
self.assertEqual(result, None)
counts_after = self.get_model_counts()
self.assertEqual(counts_before, counts_after)
# Even with days=1, the hosts will remain.
counts_before = self.get_model_counts()
self.assertTrue(sum(x[1] for x in counts_before.values()))
result, stdout, stderr = self.run_command('cleanup_deleted', days=1)
self.assertEqual(result, None)
counts_after = self.get_model_counts()
self.assertEqual(counts_before, counts_after)
# With days=0, the hosts will be deleted.
counts_before = self.get_model_counts()
self.assertTrue(sum(x[1] for x in counts_before.values()))
result, stdout, stderr = self.run_command('cleanup_deleted', days=0)
self.assertEqual(result, None)
counts_after = self.get_model_counts()
self.assertNotEqual(counts_before, counts_after)
self.assertFalse(sum(x[1] for x in counts_after.values()))
return # Don't test how long it takes (for now).
# Create lots of hosts already marked as deleted.
t = time.time()
dtnow = now()
for x in xrange(1000):
hostname = "_deleted_%s_host-%d" % (dtnow.isoformat(), x)
host = self.inventories[0].hosts.create(name=hostname, active=False)
create_elapsed = time.time() - t
# Time how long it takes to cleanup deleted items, should be no more
# then the time taken to create them.
counts_before = self.get_model_counts()
self.assertTrue(sum(x[1] for x in counts_before.values()))
t = time.time()
result, stdout, stderr = self.run_command('cleanup_deleted', days=0)
cleanup_elapsed = time.time() - t
self.assertEqual(result, None)
counts_after = self.get_model_counts()
self.assertNotEqual(counts_before, counts_after)
self.assertFalse(sum(x[1] for x in counts_after.values()))
self.assertTrue(cleanup_elapsed < create_elapsed,
'create took %0.3fs, cleanup took %0.3fs, expected < %0.3fs' % (create_elapsed, cleanup_elapsed, create_elapsed))
def get_user_counts(self):
active = User.objects.filter(is_active=True).count()
inactive = User.objects.filter(is_active=False).count()
return active, inactive
def test_cleanup_user_model(self):
# Test with nothing to be deleted.
counts_before = self.get_user_counts()
self.assertFalse(counts_before[1])
result, stdout, stderr = self.run_command('cleanup_deleted')
self.assertEqual(result, None)
counts_after = self.get_user_counts()
self.assertEqual(counts_before, counts_after)
# "Delete some users".
for user in User.objects.all():
user.mark_inactive()
self.assertTrue(len(user.username) <= 30,
'len(%r) == %d' % (user.username, len(user.username)))
# With days=1, no users will be deleted.
counts_before = self.get_user_counts()
self.assertTrue(counts_before[1])
result, stdout, stderr = self.run_command('cleanup_deleted', days=1)
self.assertEqual(result, None)
counts_after = self.get_user_counts()
self.assertEqual(counts_before, counts_after)
# With days=0, inactive users will be deleted.
counts_before = self.get_user_counts()
self.assertTrue(counts_before[1])
result, stdout, stderr = self.run_command('cleanup_deleted', days=0)
self.assertEqual(result, None)
counts_after = self.get_user_counts()
self.assertNotEqual(counts_before, counts_after)
self.assertFalse(counts_after[1])
@override_settings(CELERY_ALWAYS_EAGER=True, @override_settings(CELERY_ALWAYS_EAGER=True,
CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
ANSIBLE_TRANSPORT='local') ANSIBLE_TRANSPORT='local')

View File

@@ -423,7 +423,7 @@ class InventoryTest(BaseTest):
del_children_url = reverse('api:group_children_list', args=(del_group.pk,)) del_children_url = reverse('api:group_children_list', args=(del_group.pk,))
nondel_url = reverse('api:group_detail', nondel_url = reverse('api:group_detail',
args=(Group.objects.get(name='nondel').pk,)) args=(Group.objects.get(name='nondel').pk,))
del_group.mark_inactive() del_group.delete()
nondel_detail = self.get(nondel_url, expect=200, auth=self.get_normal_credentials()) nondel_detail = self.get(nondel_url, expect=200, auth=self.get_normal_credentials())
self.post(del_children_url, data=nondel_detail, expect=403, auth=self.get_normal_credentials()) self.post(del_children_url, data=nondel_detail, expect=403, auth=self.get_normal_credentials())
@@ -944,13 +944,10 @@ class InventoryTest(BaseTest):
# Mark group C inactive. Its child groups and hosts should now also be # Mark group C inactive. Its child groups and hosts should now also be
# attached to group A. Group D hosts should be unchanged. Group C # attached to group A. Group D hosts should be unchanged. Group C
# should also no longer have any group or host relationships. # should also no longer have any group or host relationships.
g_c.mark_inactive() g_c.delete()
self.assertTrue(g_d in g_a.children.all()) self.assertTrue(g_d in g_a.children.all())
self.assertTrue(h_c in g_a.hosts.all()) self.assertTrue(h_c in g_a.hosts.all())
self.assertFalse(h_d in g_a.hosts.all()) self.assertFalse(h_d in g_a.hosts.all())
self.assertFalse(g_c.parents.all())
self.assertFalse(g_c.children.all())
self.assertFalse(g_c.hosts.all())
def test_safe_delete_recursion(self): def test_safe_delete_recursion(self):
# First hierarchy # First hierarchy
@@ -989,11 +986,9 @@ class InventoryTest(BaseTest):
self.assertTrue(other_sub_group in sub_group.children.all()) self.assertTrue(other_sub_group in sub_group.children.all())
# Now recursively remove its parent and the reference from subgroup should remain # Now recursively remove its parent and the reference from subgroup should remain
other_top_group.mark_inactive_recursive() other_top_group.delete_recursive()
other_top_group = Group.objects.get(pk=other_top_group.pk)
self.assertTrue(s2 in sub_group.all_hosts.all()) self.assertTrue(s2 in sub_group.all_hosts.all())
self.assertTrue(other_sub_group in sub_group.children.all()) self.assertTrue(other_sub_group in sub_group.children.all())
self.assertFalse(other_top_group.active)
def test_group_parents_and_children(self): def test_group_parents_and_children(self):
# Test for various levels of group parent/child relations, with hosts, # Test for various levels of group parent/child relations, with hosts,
@@ -1173,7 +1168,7 @@ class InventoryTest(BaseTest):
# Delete recently added hosts and verify the count drops. # Delete recently added hosts and verify the count drops.
hostnames4 = list('defg') hostnames4 = list('defg')
for host in Host.objects.filter(name__in=hostnames4): for host in Host.objects.filter(name__in=hostnames4):
host.mark_inactive() host.delete()
with self.current_user(self.super_django_user): with self.current_user(self.super_django_user):
response = self.get(url) response = self.get(url)
for n, d in enumerate(reversed(response['hosts'])): for n, d in enumerate(reversed(response['hosts'])):

View File

@@ -96,7 +96,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase):
def test_credential_explicit(self): def test_credential_explicit(self):
# Explicit, credential # Explicit, credential
with self.current_user(self.user_sue): with self.current_user(self.user_sue):
self.cred_sue.mark_inactive() self.cred_sue.delete()
response = self.post(self.launch_url, {'credential': self.cred_doug.pk}, expect=202) response = self.post(self.launch_url, {'credential': self.cred_doug.pk}, expect=202)
j = Job.objects.get(pk=response['job']) j = Job.objects.get(pk=response['job'])
self.assertEqual(j.status, 'new') self.assertEqual(j.status, 'new')
@@ -105,7 +105,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase):
def test_credential_explicit_via_credential_id(self): def test_credential_explicit_via_credential_id(self):
# Explicit, credential # Explicit, credential
with self.current_user(self.user_sue): with self.current_user(self.user_sue):
self.cred_sue.mark_inactive() self.cred_sue.delete()
response = self.post(self.launch_url, {'credential_id': self.cred_doug.pk}, expect=202) response = self.post(self.launch_url, {'credential_id': self.cred_doug.pk}, expect=202)
j = Job.objects.get(pk=response['job']) j = Job.objects.get(pk=response['job'])
self.assertEqual(j.status, 'new') self.assertEqual(j.status, 'new')
@@ -131,15 +131,16 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase):
# Can't launch a job template without a credential defined (or if we # Can't launch a job template without a credential defined (or if we
# pass an invalid/inactive credential value). # pass an invalid/inactive credential value).
with self.current_user(self.user_sue): with self.current_user(self.user_sue):
self.cred_sue.mark_inactive() self.cred_sue.delete()
self.post(self.launch_url, {}, expect=400) self.post(self.launch_url, {}, expect=400)
self.post(self.launch_url, {'credential': 0}, expect=400) self.post(self.launch_url, {'credential': 0}, expect=400)
self.post(self.launch_url, {'credential_id': 0}, expect=400) self.post(self.launch_url, {'credential_id': 0}, expect=400)
self.post(self.launch_url, {'credential': 'one'}, expect=400) self.post(self.launch_url, {'credential': 'one'}, expect=400)
self.post(self.launch_url, {'credential_id': 'one'}, expect=400) self.post(self.launch_url, {'credential_id': 'one'}, expect=400)
self.cred_doug.mark_inactive() doug_pk = self.cred_doug.pk
self.post(self.launch_url, {'credential': self.cred_doug.pk}, expect=400) self.cred_doug.delete()
self.post(self.launch_url, {'credential_id': self.cred_doug.pk}, expect=400) self.post(self.launch_url, {'credential': cred_doug_pk}, expect=400)
self.post(self.launch_url, {'credential_id': cred_doug_pk}, expect=400)
def test_explicit_unowned_cred(self): def test_explicit_unowned_cred(self):
# Explicitly specify a credential that we don't have access to # Explicitly specify a credential that we don't have access to
@@ -174,7 +175,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase):
def test_deleted_credential_fail(self): def test_deleted_credential_fail(self):
# Job Templates with deleted credentials cannot be launched. # Job Templates with deleted credentials cannot be launched.
self.cred_sue.mark_inactive() self.cred_sue.delete()
with self.current_user(self.user_sue): with self.current_user(self.user_sue):
self.post(self.launch_url, {}, expect=400) self.post(self.launch_url, {}, expect=400)
@@ -202,7 +203,7 @@ class JobTemplateLaunchPasswordsTest(BaseJobTestMixin, django.test.TransactionTe
passwords_required = ['ssh_password', 'become_password', 'ssh_key_unlock'] passwords_required = ['ssh_password', 'become_password', 'ssh_key_unlock']
# Job Templates with deleted credentials cannot be launched. # Job Templates with deleted credentials cannot be launched.
with self.current_user(self.user_sue): with self.current_user(self.user_sue):
self.cred_sue_ask.mark_inactive() self.cred_sue_ask.delete()
response = self.post(self.launch_url, {'credential_id': self.cred_sue_ask_many.pk}, expect=400) response = self.post(self.launch_url, {'credential_id': self.cred_sue_ask_many.pk}, expect=400)
for p in passwords_required: for p in passwords_required:
self.assertIn(p, response['passwords_needed_to_start']) self.assertIn(p, response['passwords_needed_to_start'])

View File

@@ -169,7 +169,7 @@ class ProjectsTest(BaseTransactionTest):
local_path = project.local_path local_path = project.local_path
response = self.get(url, expect=200, auth=self.get_super_credentials()) response = self.get(url, expect=200, auth=self.get_super_credentials())
self.assertTrue(local_path not in response['project_local_paths']) self.assertTrue(local_path not in response['project_local_paths'])
project.mark_inactive() project.delete()
response = self.get(url, expect=200, auth=self.get_super_credentials()) response = self.get(url, expect=200, auth=self.get_super_credentials())
self.assertTrue(local_path in response['project_local_paths']) self.assertTrue(local_path in response['project_local_paths'])

View File

@@ -88,7 +88,8 @@ class InventoryScriptTest(BaseScriptTest):
inventory=inventory, inventory=inventory,
variables=variables) variables=variables)
if x in (3, 7): if x in (3, 7):
host.mark_inactive() host.delete()
continue
hosts.append(host) hosts.append(host)
# add localhost just to make sure it's thrown into all (Ansible github bug) # add localhost just to make sure it's thrown into all (Ansible github bug)
@@ -106,7 +107,8 @@ class InventoryScriptTest(BaseScriptTest):
inventory=inventory, inventory=inventory,
variables=variables) variables=variables)
if x == 2: if x == 2:
group.mark_inactive() group.delete()
continue
groups.append(group) groups.append(group)
group.hosts.add(hosts[x]) group.hosts.add(hosts[x])
group.hosts.add(hosts[x + 5]) group.hosts.add(hosts[x + 5])
@@ -320,9 +322,9 @@ class InventoryScriptTest(BaseScriptTest):
def test_with_deleted_inventory(self): def test_with_deleted_inventory(self):
inventory = self.inventories[0] inventory = self.inventories[0]
inventory.mark_inactive() pk = inventory.pk
self.assertFalse(inventory.active) inventory.delete()
os.environ['INVENTORY_ID'] = str(inventory.pk) os.environ['INVENTORY_ID'] = str(pk)
rc, stdout, stderr = self.run_inventory_script(list=True) rc, stdout, stderr = self.run_inventory_script(list=True)
self.assertNotEqual(rc, 0, stderr) self.assertNotEqual(rc, 0, stderr)
self.assertEqual(json.loads(stdout), {'failed': True}) self.assertEqual(json.loads(stdout), {'failed': True})

View File

@@ -592,26 +592,8 @@ class RunJobTest(BaseJobExecutionTest):
new_group.children.remove(self.group) new_group.children.remove(self.group)
new_group = Group.objects.get(pk=new_group.pk) new_group = Group.objects.get(pk=new_group.pk)
self.assertFalse(new_group.has_active_failures) self.assertFalse(new_group.has_active_failures)
# Mark host inactive (should clear flag on parent group and inventory) # Delete host (should clear flag on parent group and inventory)
self.host.mark_inactive()
self.group = Group.objects.get(pk=self.group.pk)
self.assertFalse(self.group.has_active_failures)
self.inventory = Inventory.objects.get(pk=self.inventory.pk)
self.assertFalse(self.inventory.has_active_failures)
# Un-mark host as inactive (need to force update of flag on group and
# inventory)
host = self.host
host.name = '_'.join(host.name.split('_')[3:]) or 'undeleted host'
host.active = True
host.save()
host.update_computed_fields()
self.group = Group.objects.get(pk=self.group.pk)
self.assertTrue(self.group.has_active_failures)
self.inventory = Inventory.objects.get(pk=self.inventory.pk)
self.assertTrue(self.inventory.has_active_failures)
# Delete host. (should clear flag)
self.host.delete() self.host.delete()
self.host = None
self.group = Group.objects.get(pk=self.group.pk) self.group = Group.objects.get(pk=self.group.pk)
self.assertFalse(self.group.has_active_failures) self.assertFalse(self.group.has_active_failures)
self.inventory = Inventory.objects.get(pk=self.inventory.pk) self.inventory = Inventory.objects.get(pk=self.inventory.pk)
@@ -619,30 +601,7 @@ class RunJobTest(BaseJobExecutionTest):
def test_update_has_active_failures_when_job_removed(self): def test_update_has_active_failures_when_job_removed(self):
job = self.test_run_job_that_fails() job = self.test_run_job_that_fails()
# Mark job as inactive (should clear flags). # Delete (should clear flags).
job.mark_inactive()
self.host = Host.objects.get(pk=self.host.pk)
self.assertFalse(self.host.has_active_failures)
self.group = Group.objects.get(pk=self.group.pk)
self.assertFalse(self.group.has_active_failures)
self.inventory = Inventory.objects.get(pk=self.inventory.pk)
self.assertFalse(self.inventory.has_active_failures)
# Un-mark job as inactive (need to force update of flag)
job.active = True
job.save()
# Need to manually update last_job on host...
host = Host.objects.get(pk=self.host.pk)
host.last_job = job
host.last_job_host_summary = JobHostSummary.objects.get(job=job, host=host)
host.save()
self.inventory.update_computed_fields()
self.host = Host.objects.get(pk=self.host.pk)
self.assertTrue(self.host.has_active_failures)
self.group = Group.objects.get(pk=self.group.pk)
self.assertTrue(self.group.has_active_failures)
self.inventory = Inventory.objects.get(pk=self.inventory.pk)
self.assertTrue(self.inventory.has_active_failures)
# Delete job entirely.
job.delete() job.delete()
self.host = Host.objects.get(pk=self.host.pk) self.host = Host.objects.get(pk=self.host.pk)
self.assertFalse(self.host.has_active_failures) self.assertFalse(self.host.has_active_failures)
@@ -662,8 +621,8 @@ class RunJobTest(BaseJobExecutionTest):
self.host = Host.objects.get(pk=self.host.pk) self.host = Host.objects.get(pk=self.host.pk)
self.assertEqual(self.host.last_job, job1) self.assertEqual(self.host.last_job, job1)
self.assertEqual(self.host.last_job_host_summary.job, job1) self.assertEqual(self.host.last_job_host_summary.job, job1)
# Mark job1 inactive (should update host.last_job to None). # Delete job1 (should update host.last_job to None).
job1.mark_inactive() job1.delete()
self.host = Host.objects.get(pk=self.host.pk) self.host = Host.objects.get(pk=self.host.pk)
self.assertEqual(self.host.last_job, None) self.assertEqual(self.host.last_job, None)
self.assertEqual(self.host.last_job_host_summary, None) self.assertEqual(self.host.last_job_host_summary, None)

View File

@@ -196,7 +196,7 @@ class UsersTest(BaseTest):
self.post(url, expect=201, data=new_user2, auth=self.get_normal_credentials()) self.post(url, expect=201, data=new_user2, auth=self.get_normal_credentials())
self.post(url, expect=400, data=new_user2, auth=self.get_normal_credentials()) self.post(url, expect=400, data=new_user2, auth=self.get_normal_credentials())
# Normal user cannot add users after his org is marked inactive. # Normal user cannot add users after his org is marked inactive.
self.organizations[0].mark_inactive() self.organizations[0].delete()
new_user3 = dict(username='blippy3') new_user3 = dict(username='blippy3')
self.post(url, expect=403, data=new_user3, auth=self.get_normal_credentials()) self.post(url, expect=403, data=new_user3, auth=self.get_normal_credentials())
@@ -316,7 +316,7 @@ class UsersTest(BaseTest):
remote_addr=remote_addr) remote_addr=remote_addr)
# Token auth should be denied if the user is inactive. # Token auth should be denied if the user is inactive.
self.normal_django_user.mark_inactive() self.normal_django_user.delete()
response = self.get(user_me_url, expect=401, auth=auth_token2, response = self.get(user_me_url, expect=401, auth=auth_token2,
remote_addr=remote_addr) remote_addr=remote_addr)
self.assertEqual(response['detail'], 'User inactive or deleted') self.assertEqual(response['detail'], 'User inactive or deleted')
@@ -422,7 +422,7 @@ class UsersTest(BaseTest):
# Normal user can no longer see all users after the organization he # Normal user can no longer see all users after the organization he
# admins is marked inactive, nor can he see any other users that were # admins is marked inactive, nor can he see any other users that were
# in that org, so he only sees himself. # in that org, so he only sees himself.
self.organizations[0].mark_inactive() self.organizations[0].delete()
data3 = self.get(url, expect=200, auth=self.get_normal_credentials()) data3 = self.get(url, expect=200, auth=self.get_normal_credentials())
self.assertEquals(data3['count'], 1) self.assertEquals(data3['count'], 1)