diff --git a/awx/api/filters.py b/awx/api/filters.py index 73afbc178b..367fd0eda5 100644 --- a/awx/api/filters.py +++ b/awx/api/filters.py @@ -26,19 +26,6 @@ class MongoFilterBackend(BaseFilterBackend): def filter_queryset(self, request, queryset, view): return queryset -class ActiveOnlyBackend(BaseFilterBackend): - ''' - Filter to show only objects where is_active/active is True. - ''' - - def filter_queryset(self, request, queryset, view): - for field in queryset.model._meta.fields: - if field.name == 'is_active': - queryset = queryset.filter(is_active=True) - elif field.name == 'active': - queryset = queryset.filter(active=True) - return queryset - class TypeFilterBackend(BaseFilterBackend): ''' Filter on type field now returned with all objects. @@ -166,12 +153,12 @@ class FieldLookupBackend(BaseFilterBackend): for key, values in request.query_params.lists(): if key in self.RESERVED_NAMES: continue - + # HACK: Make job event filtering by host name mostly work even # when not capturing job event hosts M2M. if queryset.model._meta.object_name == 'JobEvent' and key.startswith('hosts__name'): key = key.replace('hosts__name', 'or__host__name') - or_filters.append((False, 'host__name__isnull', True)) + or_filters.append((False, 'host__name__isnull', True)) # Custom __int filter suffix (internal use only). q_int = False diff --git a/awx/api/generics.py b/awx/api/generics.py index a660b2acba..3c1321300c 100644 --- a/awx/api/generics.py +++ b/awx/api/generics.py @@ -7,7 +7,6 @@ import logging import time # Django -from django.http import Http404 from django.conf import settings from django.db import connection from django.shortcuts import get_object_or_404 @@ -415,9 +414,7 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView): raise PermissionDenied() if parent_key: - # sub object has a ForeignKey to the parent, so we can't remove it - # from the set, only mark it as inactive. - sub.mark_inactive() + sub.delete() else: relationship.remove(sub) @@ -457,17 +454,9 @@ class RetrieveDestroyAPIView(RetrieveAPIView, generics.RetrieveDestroyAPIView): def destroy(self, request, *args, **kwargs): # somewhat lame that delete has to call it's own permissions check 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): raise PermissionDenied() - if hasattr(obj, 'mark_inactive'): - obj.mark_inactive() - else: - raise NotImplementedError('destroy() not implemented yet for %s' % obj) + obj.delete() return Response(status=status.HTTP_204_NO_CONTENT) class RetrieveUpdateDestroyAPIView(RetrieveUpdateAPIView, RetrieveDestroyAPIView): diff --git a/awx/api/permissions.py b/awx/api/permissions.py index 8f535f7adf..bc1447ba03 100644 --- a/awx/api/permissions.py +++ b/awx/api/permissions.py @@ -103,11 +103,7 @@ class ModelAccessPermission(permissions.BasePermission): if not request.user or request.user.is_anonymous(): return False - # Don't allow inactive users (and respond with a 403). - if not request.user.is_active: - raise PermissionDenied('your account is inactive') - - # Always allow superusers (as long as they are active). + # Always allow superusers if getattr(view, 'always_allow_superuser', True) and request.user.is_superuser: return True @@ -161,8 +157,6 @@ class JobTemplateCallbackPermission(ModelAccessPermission): raise PermissionDenied() elif not host_config_key: raise PermissionDenied() - elif obj and not obj.active: - raise PermissionDenied() elif obj and obj.host_config_key != host_config_key: raise PermissionDenied() else: @@ -182,7 +176,7 @@ class TaskPermission(ModelAccessPermission): # Verify that the ID present in the auth token is for a valid, active # unified job. try: - unified_job = UnifiedJob.objects.get(active=True, status='running', + unified_job = UnifiedJob.objects.get(status='running', pk=int(request.auth.split('-')[0])) except (UnifiedJob.DoesNotExist, TypeError): return False diff --git a/awx/api/serializers.py b/awx/api/serializers.py index aea7ad0c7e..f549e1c768 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -252,7 +252,6 @@ class BaseSerializer(serializers.ModelSerializer): # make certain fields read only created = serializers.SerializerMethodField() modified = serializers.SerializerMethodField() - active = serializers.SerializerMethodField() def get_type(self, obj): @@ -288,9 +287,9 @@ class BaseSerializer(serializers.ModelSerializer): def get_related(self, obj): res = OrderedDict() - if getattr(obj, 'created_by', None) and obj.created_by.is_active: + if getattr(obj, 'created_by', None): res['created_by'] = reverse('api:user_detail', args=(obj.created_by.pk,)) - if getattr(obj, 'modified_by', None) and obj.modified_by.is_active: + if getattr(obj, 'modified_by', None): res['modified_by'] = reverse('api:user_detail', args=(obj.modified_by.pk,)) return res @@ -315,10 +314,6 @@ class BaseSerializer(serializers.ModelSerializer): continue if fkval == obj: continue - if hasattr(fkval, 'active') and not fkval.active: - continue - if hasattr(fkval, 'is_active') and not fkval.is_active: - continue summary_fields[fk] = OrderedDict() for field in related_fields: fval = getattr(fkval, field, None) @@ -334,11 +329,11 @@ class BaseSerializer(serializers.ModelSerializer): # Can be raised by the reverse accessor for a OneToOneField. except ObjectDoesNotExist: pass - if getattr(obj, 'created_by', None) and obj.created_by.is_active: + if getattr(obj, 'created_by', None): summary_fields['created_by'] = OrderedDict() for field in SUMMARIZABLE_FK_FIELDS['user']: summary_fields['created_by'][field] = getattr(obj.created_by, field) - if getattr(obj, 'modified_by', None) and obj.modified_by.is_active: + if getattr(obj, 'modified_by', None): summary_fields['modified_by'] = OrderedDict() for field in SUMMARIZABLE_FK_FIELDS['user']: summary_fields['modified_by'][field] = getattr(obj.modified_by, field) @@ -378,14 +373,6 @@ class BaseSerializer(serializers.ModelSerializer): else: return obj.modified - def get_active(self, obj): - if obj is None: - return False - elif isinstance(obj, User): - return obj.is_active - else: - return obj.active - def build_standard_field(self, field_name, model_field): # DRF 3.3 serializers.py::build_standard_field() -> utils/field_mapping.py::get_field_kwargs() short circuits @@ -564,11 +551,11 @@ class UnifiedJobTemplateSerializer(BaseSerializer): def get_related(self, obj): res = super(UnifiedJobTemplateSerializer, self).get_related(obj) - if obj.current_job and obj.current_job.active: + if obj.current_job: res['current_job'] = obj.current_job.get_absolute_url() - if obj.last_job and obj.last_job.active: + if obj.last_job: res['last_job'] = obj.last_job.get_absolute_url() - if obj.next_schedule and obj.next_schedule.active: + if obj.next_schedule: res['next_schedule'] = obj.next_schedule.get_absolute_url() return res @@ -623,9 +610,9 @@ class UnifiedJobSerializer(BaseSerializer): def get_related(self, obj): res = super(UnifiedJobSerializer, self).get_related(obj) - if obj.unified_job_template and obj.unified_job_template.active: + if obj.unified_job_template: res['unified_job_template'] = obj.unified_job_template.get_absolute_url() - if obj.schedule and obj.schedule.active: + if obj.schedule: res['schedule'] = obj.schedule.get_absolute_url() if isinstance(obj, ProjectUpdate): res['stdout'] = reverse('api:project_update_stdout', args=(obj.pk,)) @@ -874,7 +861,7 @@ class ProjectOptionsSerializer(BaseSerializer): def get_related(self, obj): res = super(ProjectOptionsSerializer, self).get_related(obj) - if obj.credential and obj.credential.active: + if obj.credential: res['credential'] = reverse('api:credential_detail', args=(obj.credential.pk,)) return res @@ -903,7 +890,7 @@ class ProjectOptionsSerializer(BaseSerializer): def to_representation(self, obj): ret = super(ProjectOptionsSerializer, self).to_representation(obj) - if obj is not None and 'credential' in ret and (not obj.credential or not obj.credential.active): + if obj is not None and 'credential' in ret and not obj.credential: ret['credential'] = None return ret @@ -1039,13 +1026,13 @@ class InventorySerializer(BaseSerializerWithVariables): access_list = reverse('api:inventory_access_list', args=(obj.pk,)), #single_fact = reverse('api:inventory_single_fact_view', args=(obj.pk,)), )) - if obj.organization and obj.organization.active: + if obj.organization: res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,)) return res def to_representation(self, obj): ret = super(InventorySerializer, self).to_representation(obj) - if obj is not None and 'organization' in ret and (not obj.organization or not obj.organization.active): + if obj is not None and 'organization' in ret and not obj.organization: ret['organization'] = None return ret @@ -1100,11 +1087,11 @@ class HostSerializer(BaseSerializerWithVariables): fact_versions = reverse('api:host_fact_versions_list', args=(obj.pk,)), #single_fact = reverse('api:host_single_fact_view', args=(obj.pk,)), )) - if obj.inventory and obj.inventory.active: + if obj.inventory: res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,)) - if obj.last_job and obj.last_job.active: + if obj.last_job: res['last_job'] = reverse('api:job_detail', args=(obj.last_job.pk,)) - if obj.last_job_host_summary and obj.last_job_host_summary.job.active: + if obj.last_job_host_summary: res['last_job_host_summary'] = reverse('api:job_host_summary_detail', args=(obj.last_job_host_summary.pk,)) return res @@ -1120,7 +1107,7 @@ class HostSerializer(BaseSerializerWithVariables): 'name': j.job.job_template.name if j.job.job_template is not None else "", 'status': j.job.status, 'finished': j.job.finished, - } for j in obj.job_host_summaries.filter(job__active=True).select_related('job__job_template').order_by('-created')[:5]]}) + } for j in obj.job_host_summaries.select_related('job__job_template').order_by('-created')[:5]]}) return d def _get_host_port_from_name(self, name): @@ -1169,11 +1156,11 @@ class HostSerializer(BaseSerializerWithVariables): ret = super(HostSerializer, self).to_representation(obj) if not obj: return ret - if 'inventory' in ret and (not obj.inventory or not obj.inventory.active): + if 'inventory' in ret and not obj.inventory: ret['inventory'] = None - if 'last_job' in ret and (not obj.last_job or not obj.last_job.active): + if 'last_job' in ret and not obj.last_job: ret['last_job'] = None - if 'last_job_host_summary' in ret and (not obj.last_job_host_summary or not obj.last_job_host_summary.job.active): + if 'last_job_host_summary' in ret and not obj.last_job_host_summary: ret['last_job_host_summary'] = None return ret @@ -1210,7 +1197,7 @@ class GroupSerializer(BaseSerializerWithVariables): access_list = reverse('api:group_access_list', args=(obj.pk,)), #single_fact = reverse('api:group_single_fact_view', args=(obj.pk,)), )) - if obj.inventory and obj.inventory.active: + if obj.inventory: res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,)) if obj.inventory_source: res['inventory_source'] = reverse('api:inventory_source_detail', args=(obj.inventory_source.pk,)) @@ -1223,7 +1210,7 @@ class GroupSerializer(BaseSerializerWithVariables): def to_representation(self, obj): ret = super(GroupSerializer, self).to_representation(obj) - if obj is not None and 'inventory' in ret and (not obj.inventory or not obj.inventory.active): + if obj is not None and 'inventory' in ret and not obj.inventory: ret['inventory'] = None return ret @@ -1239,7 +1226,7 @@ class GroupTreeSerializer(GroupSerializer): def get_children(self, obj): if obj is None: return {} - children_qs = obj.children.filter(active=True) + children_qs = obj.children children_qs = children_qs.select_related('inventory') children_qs = children_qs.prefetch_related('inventory_source') return GroupTreeSerializer(children_qs, many=True).data @@ -1304,7 +1291,7 @@ class CustomInventoryScriptSerializer(BaseSerializer): def get_related(self, obj): res = super(CustomInventoryScriptSerializer, self).get_related(obj) - if obj.organization and obj.organization.active: + if obj.organization: res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,)) return res @@ -1317,10 +1304,10 @@ class InventorySourceOptionsSerializer(BaseSerializer): def get_related(self, obj): res = super(InventorySourceOptionsSerializer, self).get_related(obj) - if obj.credential and obj.credential.active: + if obj.credential: res['credential'] = reverse('api:credential_detail', args=(obj.credential.pk,)) - if obj.source_script and obj.source_script.active: + if obj.source_script: res['source_script'] = reverse('api:inventory_script_detail', args=(obj.source_script.pk,)) return res @@ -1365,7 +1352,7 @@ class InventorySourceOptionsSerializer(BaseSerializer): ret = super(InventorySourceOptionsSerializer, self).to_representation(obj) if obj is None: return ret - if 'credential' in ret and (not obj.credential or not obj.credential.active): + if 'credential' in ret and not obj.credential: ret['credential'] = None return ret @@ -1396,9 +1383,9 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt notifiers_success = reverse('api:inventory_source_notifiers_success_list', args=(obj.pk,)), notifiers_error = reverse('api:inventory_source_notifiers_error_list', args=(obj.pk,)), )) - if obj.inventory and obj.inventory.active: + if obj.inventory: res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,)) - if obj.group and obj.group.active: + if obj.group: res['group'] = reverse('api:group_detail', args=(obj.group.pk,)) # Backwards compatibility. if obj.current_update: @@ -1413,9 +1400,9 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt ret = super(InventorySourceSerializer, self).to_representation(obj) if obj is None: return ret - if 'inventory' in ret and (not obj.inventory or not obj.inventory.active): + if 'inventory' in ret and not obj.inventory: ret['inventory'] = None - if 'group' in ret and (not obj.group or not obj.group.active): + if 'group' in ret and not obj.group: ret['group'] = None return ret @@ -1473,13 +1460,13 @@ class TeamSerializer(BaseSerializer): activity_stream = reverse('api:team_activity_stream_list', args=(obj.pk,)), access_list = reverse('api:team_access_list', args=(obj.pk,)), )) - if obj.organization and obj.organization.active: + if obj.organization: res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,)) return res def to_representation(self, obj): ret = super(TeamSerializer, self).to_representation(obj) - if obj is not None and 'organization' in ret and (not obj.organization or not obj.organization.active): + if obj is not None and 'organization' in ret and not obj.organization: ret['organization'] = None return ret @@ -1563,9 +1550,9 @@ class CredentialSerializer(BaseSerializer): def to_representation(self, obj): ret = super(CredentialSerializer, self).to_representation(obj) - if obj is not None and 'user' in ret and (not obj.user or not obj.user.is_active): + if obj is not None and 'user' in ret and not obj.user: ret['user'] = None - if obj is not None and 'team' in ret and (not obj.team or not obj.team.active): + if obj is not None and 'team' in ret and not obj.team: ret['team'] = None return ret @@ -1604,13 +1591,13 @@ class JobOptionsSerializer(BaseSerializer): def get_related(self, obj): res = super(JobOptionsSerializer, self).get_related(obj) - if obj.inventory and obj.inventory.active: + if obj.inventory: res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,)) - if obj.project and obj.project.active: + if obj.project: res['project'] = reverse('api:project_detail', args=(obj.project.pk,)) - if obj.credential and obj.credential.active: + if obj.credential: res['credential'] = reverse('api:credential_detail', args=(obj.credential.pk,)) - if obj.cloud_credential and obj.cloud_credential.active: + if obj.cloud_credential: res['cloud_credential'] = reverse('api:credential_detail', args=(obj.cloud_credential.pk,)) return res @@ -1619,15 +1606,15 @@ class JobOptionsSerializer(BaseSerializer): ret = super(JobOptionsSerializer, self).to_representation(obj) if obj is None: return ret - if 'inventory' in ret and (not obj.inventory or not obj.inventory.active): + if 'inventory' in ret and not obj.inventory: ret['inventory'] = None - if 'project' in ret and (not obj.project or not obj.project.active): + if 'project' in ret and not obj.project: ret['project'] = None if 'playbook' in ret: ret['playbook'] = '' - if 'credential' in ret and (not obj.credential or not obj.credential.active): + if 'credential' in ret and not obj.credential: ret['credential'] = None - if 'cloud_credential' in ret and (not obj.cloud_credential or not obj.cloud_credential.active): + if 'cloud_credential' in ret and not obj.cloud_credential: ret['cloud_credential'] = None return ret @@ -1690,7 +1677,7 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer): else: d['can_copy'] = False d['can_edit'] = False - d['recent_jobs'] = [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in obj.jobs.filter(active=True).order_by('-created')[:10]] + d['recent_jobs'] = [{'id': x.id, 'status': x.status, 'finished': x.finished} for x in obj.jobs.order_by('-created')[:10]] return d def validate(self, attrs): @@ -1721,7 +1708,7 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): activity_stream = reverse('api:job_activity_stream_list', args=(obj.pk,)), notifications = reverse('api:job_notifications_list', args=(obj.pk,)), )) - if obj.job_template and obj.job_template.active: + if obj.job_template: res['job_template'] = reverse('api:job_template_detail', args=(obj.job_template.pk,)) if obj.can_start or True: @@ -1766,7 +1753,7 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer): ret = super(JobSerializer, self).to_representation(obj) if obj is None: return ret - if 'job_template' in ret and (not obj.job_template or not obj.job_template.active): + if 'job_template' in ret and not obj.job_template: ret['job_template'] = None if obj.job_template and obj.job_template.survey_enabled: @@ -1830,11 +1817,11 @@ class JobRelaunchSerializer(JobSerializer): def validate(self, attrs): obj = self.context.get('obj') - if not obj.credential or obj.credential.active is False: + if not obj.credential: raise serializers.ValidationError(dict(credential=["Credential not found or deleted."])) - if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None or not obj.project.active): + if obj.job_type != PERM_INVENTORY_SCAN and obj.project is None: raise serializers.ValidationError(dict(errors=["Job Template Project is missing or undefined"])) - if obj.inventory is None or not obj.inventory.active: + if obj.inventory is None: raise serializers.ValidationError(dict(errors=["Job Template Inventory is missing or undefined"])) attrs = super(JobRelaunchSerializer, self).validate(attrs) return attrs @@ -1874,9 +1861,9 @@ class AdHocCommandSerializer(UnifiedJobSerializer): def get_related(self, obj): res = super(AdHocCommandSerializer, self).get_related(obj) - if obj.inventory and obj.inventory.active: + if obj.inventory: res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,)) - if obj.credential and obj.credential.active: + if obj.credential: res['credential'] = reverse('api:credential_detail', args=(obj.credential.pk,)) res.update(dict( events = reverse('api:ad_hoc_command_ad_hoc_command_events_list', args=(obj.pk,)), @@ -1888,9 +1875,9 @@ class AdHocCommandSerializer(UnifiedJobSerializer): def to_representation(self, obj): ret = super(AdHocCommandSerializer, self).to_representation(obj) - if 'inventory' in ret and (not obj.inventory or not obj.inventory.active): + if 'inventory' in ret and not obj.inventory: ret['inventory'] = None - if 'credential' in ret and (not obj.credential or not obj.credential.active): + if 'credential' in ret and not obj.credential: ret['credential'] = None # For the UI, only module_name is returned for name, instead of the # longer module name + module_args format. @@ -1942,7 +1929,7 @@ class SystemJobSerializer(UnifiedJobSerializer): def get_related(self, obj): res = super(SystemJobSerializer, self).get_related(obj) - if obj.system_job_template and obj.system_job_template.active: + if obj.system_job_template: res['system_job_template'] = reverse('api:system_job_template_detail', args=(obj.system_job_template.pk,)) if obj.can_cancel or True: @@ -2080,7 +2067,7 @@ class JobLaunchSerializer(BaseSerializer): } def get_credential_needed_to_start(self, obj): - return not (obj and obj.credential and obj.credential.active) + return not (obj and obj.credential) def get_survey_enabled(self, obj): if obj: @@ -2093,7 +2080,7 @@ class JobLaunchSerializer(BaseSerializer): data = self.context.get('data') credential = attrs.get('credential', obj and obj.credential or None) - if not credential or not credential.active: + if not credential: errors['credential'] = 'Credential not provided' # fill passwords dict with request data passwords @@ -2124,9 +2111,9 @@ class JobLaunchSerializer(BaseSerializer): if validation_errors: errors['variables_needed_to_start'] = validation_errors - if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None or not obj.project.active): + if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None): errors['project'] = 'Job Template Project is missing or undefined' - if obj.inventory is None or not obj.inventory.active: + if obj.inventory is None: errors['inventory'] = 'Job Template Inventory is missing or undefined' if errors: @@ -2162,7 +2149,7 @@ class NotifierSerializer(BaseSerializer): test = reverse('api:notifier_test', args=(obj.pk,)), notifications = reverse('api:notifier_notification_list', args=(obj.pk,)), )) - if obj.organization and obj.organization.active: + if obj.organization: res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,)) return res @@ -2220,7 +2207,7 @@ class ScheduleSerializer(BaseSerializer): res.update(dict( unified_jobs = reverse('api:schedule_unified_jobs_list', args=(obj.pk,)), )) - if obj.unified_job_template and obj.unified_job_template.active: + if obj.unified_job_template: res['unified_job_template'] = obj.unified_job_template.get_absolute_url() return res @@ -2447,8 +2434,6 @@ class AuthTokenSerializer(serializers.Serializer): if username and password: user = authenticate(username=username, password=password) if user: - if not user.is_active: - raise serializers.ValidationError('User account is disabled.') attrs['user'] = user return attrs else: diff --git a/awx/api/views.py b/awx/api/views.py index 4883342003..96c3bf396e 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -214,7 +214,7 @@ class ApiV1ConfigView(APIView): user_ldap_fields.extend(getattr(settings, 'AUTH_LDAP_USER_FLAGS_BY_GROUP', {}).keys()) data['user_ldap_fields'] = user_ldap_fields - if request.user.is_superuser or request.user.admin_of_organizations.filter(active=True).count(): + if request.user.is_superuser or request.user.admin_of_organizations.count(): data.update(dict( project_base_dir = settings.PROJECTS_ROOT, project_local_paths = Project.get_local_path_choices(), @@ -609,7 +609,7 @@ class OrganizationList(ListCreateAPIView): # by the license, then we are only willing to create this organization # if no organizations exist in the system. if (not feature_enabled('multiple_organizations') and - self.model.objects.filter(active=True).count() > 0): + self.model.objects.count() > 0): raise LicenseForbids('Your Tower license only permits a single ' 'organization to exist.') @@ -804,7 +804,7 @@ class ProjectList(ListCreateAPIView): def get(self, request, *args, **kwargs): # Not optimal, but make sure the project status and last_updated fields # are up to date here... - projects_qs = Project.objects.filter(active=True) + projects_qs = Project.objects projects_qs = projects_qs.select_related('current_job', 'last_job') for project in projects_qs: project._set_status_and_last_job_run() @@ -1093,8 +1093,6 @@ class UserDetail(RetrieveUpdateDestroyAPIView): can_delete = request.user.can_access(User, 'delete', obj) if not can_delete: 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) class UserAccessList(ResourceAccessList): @@ -1400,7 +1398,7 @@ class GroupChildrenList(SubListCreateAttachDetachAPIView): if sub_id is not None: return super(GroupChildrenList, self).unattach(request, *args, **kwargs) parent = self.get_parent_object() - parent.mark_inactive() + parent.delete() return Response(status=status.HTTP_204_NO_CONTENT) def _unattach(self, request, *args, **kwargs): # FIXME: Disabled for now for UI support. @@ -1423,8 +1421,8 @@ class GroupChildrenList(SubListCreateAttachDetachAPIView): sub, self.relationship): raise PermissionDenied() - if sub.parents.filter(active=True).exclude(pk=parent.pk).count() == 0: - sub.mark_inactive() + if sub.parents.exclude(pk=parent.pk).count() == 0: + sub.delete() else: relationship.remove(sub) @@ -1526,15 +1524,9 @@ class GroupDetail(RetrieveUpdateDestroyAPIView): def destroy(self, request, *args, **kwargs): 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): raise PermissionDenied() - if hasattr(obj, 'mark_inactive'): - obj.mark_inactive_recursive() + obj.delete_recursive() return Response(status=status.HTTP_204_NO_CONTENT) class GroupAccessList(ResourceAccessList): @@ -1601,9 +1593,9 @@ class InventoryScriptView(RetrieveAPIView): hostvars = bool(request.query_params.get('hostvars', '')) show_all = bool(request.query_params.get('all', '')) if show_all: - hosts_q = dict(active=True) + hosts_q = dict() else: - hosts_q = dict(active=True, enabled=True) + hosts_q = dict(enabled=True) if hostname: host = get_object_or_404(obj.hosts, name=hostname, **hosts_q) data = host.variables_dict @@ -1621,8 +1613,7 @@ class InventoryScriptView(RetrieveAPIView): all_group['hosts'] = groupless_hosts # Build in-memory mapping of groups and their hosts. - group_hosts_kw = dict(group__inventory_id=obj.id, group__active=True, - host__inventory_id=obj.id, host__active=True) + group_hosts_kw = dict(group__inventory_id=obj.id, host__inventory_id=obj.id) if 'enabled' in hosts_q: group_hosts_kw['host__enabled'] = hosts_q['enabled'] group_hosts_qs = Group.hosts.through.objects.filter(**group_hosts_kw) @@ -1635,8 +1626,8 @@ class InventoryScriptView(RetrieveAPIView): # Build in-memory mapping of groups and their children. group_parents_qs = Group.parents.through.objects.filter( - from_group__inventory_id=obj.id, from_group__active=True, - to_group__inventory_id=obj.id, to_group__active=True, + from_group__inventory_id=obj.id, + to_group__inventory_id=obj.id, ) group_parents_qs = group_parents_qs.order_by('from_group__name') group_parents_qs = group_parents_qs.values_list('from_group_id', 'from_group__name', 'to_group_id') @@ -1646,7 +1637,7 @@ class InventoryScriptView(RetrieveAPIView): group_children.append(from_group_name) # Now use in-memory maps to build up group info. - for group in obj.groups.filter(active=True): + for group in obj.groups.all(): group_info = OrderedDict() group_info['hosts'] = group_hosts_map.get(group.id, []) group_info['children'] = group_children_map.get(group.id, []) @@ -1692,9 +1683,9 @@ class InventoryTreeView(RetrieveAPIView): def retrieve(self, request, *args, **kwargs): inventory = self.get_object() - group_children_map = inventory.get_group_children_map(active=True) - root_group_pks = inventory.root_groups.filter(active=True).order_by('name').values_list('pk', flat=True) - groups_qs = inventory.groups.filter(active=True) + group_children_map = inventory.get_group_children_map() + root_group_pks = inventory.root_groups.order_by('name').values_list('pk', flat=True) + groups_qs = inventory.groups groups_qs = groups_qs.select_related('inventory') groups_qs = groups_qs.prefetch_related('inventory_source') all_group_data = GroupSerializer(groups_qs, many=True).data @@ -1898,7 +1889,7 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView): if obj: for p in obj.passwords_needed_to_start: data[p] = u'' - if obj.credential and obj.credential.active: + if obj.credential: data.pop('credential', None) else: data['credential'] = None @@ -2095,7 +2086,7 @@ class JobTemplateCallback(GenericAPIView): return set() # Find the host objects to search for a match. obj = self.get_object() - qs = obj.inventory.hosts.filter(active=True) + qs = obj.inventory.hosts # First try for an exact match on the name. try: return set([qs.get(name__in=remote_hosts)]) @@ -2155,7 +2146,7 @@ class JobTemplateCallback(GenericAPIView): # match again. inventory_sources_already_updated = [] if len(matching_hosts) != 1: - inventory_sources = job_template.inventory.inventory_sources.filter(active=True, update_on_launch=True) + inventory_sources = job_template.inventory.inventory_sources.filter( update_on_launch=True) inventory_update_pks = set() for inventory_source in inventory_sources: if inventory_source.needs_update_on_launch: diff --git a/awx/main/access.py b/awx/main/access.py index a7dd569bee..a160bef596 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -245,7 +245,7 @@ class UserAccess(BaseAccess): return False if self.user.is_superuser: return True - return Organization.accessible_objects(self.user, ALL_PERMISSIONS).filter(active=True).exists() + return Organization.accessible_objects(self.user, ALL_PERMISSIONS).exists() def can_change(self, obj, data): if data is not None and 'is_superuser' in data: @@ -266,7 +266,7 @@ class UserAccess(BaseAccess): if obj == self.user: # cannot delete yourself return False - super_users = User.objects.filter(is_active=True, is_superuser=True) + super_users = User.objects.filter(is_superuser=True) if obj.is_superuser and super_users.count() == 1: # cannot delete the last active superuser return False @@ -527,7 +527,7 @@ class InventoryUpdateAccess(BaseAccess): model = InventoryUpdate def get_queryset(self): - qs = InventoryUpdate.objects.filter(active=True).distinct() + qs = InventoryUpdate.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory_source__group', 'inventory_source__inventory') inventory_sources_qs = self.user.get_queryset(InventorySource) @@ -677,7 +677,7 @@ class ProjectUpdateAccess(BaseAccess): model = ProjectUpdate def get_queryset(self): - qs = ProjectUpdate.objects.filter(active=True).distinct() + qs = ProjectUpdate.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'project') project_ids = set(self.user.get_queryset(Project).values_list('id', flat=True)) return qs.filter(project_id__in=project_ids) @@ -821,7 +821,7 @@ class JobAccess(BaseAccess): model = Job def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'job_template', 'inventory', 'project', 'credential', 'cloud_credential', 'job_template') qs = qs.prefetch_related('unified_job_template') @@ -843,12 +843,10 @@ class JobAccess(BaseAccess): # TODO: I think the below queries can be combined deploy_permissions_ids = Permission.objects.filter( Q(user=self.user) | Q(team__in=team_ids), - active=True, permission_type__in=allowed_deploy, ) check_permissions_ids = Permission.objects.filter( Q(user=self.user) | Q(team__in=team_ids), - active=True, permission_type__in=allowed_check, ) @@ -947,18 +945,17 @@ class AdHocCommandAccess(BaseAccess): model = AdHocCommand def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory', 'credential') if self.user.is_superuser: return qs credential_ids = set(self.user.get_queryset(Credential).values_list('id', flat=True)) - team_ids = set(Team.objects.filter(active=True, users__in=[self.user]).values_list('id', flat=True)) + team_ids = set(Team.objects.filter( users__in=[self.user]).values_list('id', flat=True)) permission_ids = set(Permission.objects.filter( Q(user=self.user) | Q(team__in=team_ids), - active=True, permission_type__in=PERMISSION_TYPES_ALLOWING_INVENTORY_READ, run_ad_hoc_commands=True, ).values_list('id', flat=True)) @@ -982,7 +979,7 @@ class AdHocCommandAccess(BaseAccess): # If a credential is provided, the user should have read access to it. credential_pk = get_pk_from_dict(data, 'credential') if credential_pk: - credential = get_object_or_400(Credential, pk=credential_pk, active=True) + credential = get_object_or_400(Credential, pk=credential_pk) if not credential.accessible_by(self.user, {'read':True}): return False @@ -990,7 +987,7 @@ class AdHocCommandAccess(BaseAccess): # given inventory. inventory_pk = get_pk_from_dict(data, 'inventory') if inventory_pk: - inventory = get_object_or_400(Inventory, pk=inventory_pk, active=True) + inventory = get_object_or_400(Inventory, pk=inventory_pk) if not inventory.accessible_by(self.user, {'execute': True}): return False diff --git a/awx/main/management/commands/cleanup_deleted.py b/awx/main/management/commands/cleanup_deleted.py index b6fd5360e5..7fd726be7f 100644 --- a/awx/main/management/commands/cleanup_deleted.py +++ b/awx/main/management/commands/cleanup_deleted.py @@ -111,8 +111,6 @@ class Command(BaseCommand): n_deleted_items = 0 n_deleted_items += self.cleanup_model(User) - for model in self.get_models(PrimordialModel): - n_deleted_items += self.cleanup_model(model) if not self.dry_run: self.logger.log(99, "Removed %d items", n_deleted_items) diff --git a/awx/main/management/commands/create_default_org.py b/awx/main/management/commands/create_default_org.py index a6fb99f826..dbdb64ef85 100644 --- a/awx/main/management/commands/create_default_org.py +++ b/awx/main/management/commands/create_default_org.py @@ -19,7 +19,7 @@ class Command(BaseCommand): # Create a default organization as the first superuser found. try: - superuser = User.objects.filter(is_superuser=True, is_active=True).order_by('pk')[0] + superuser = User.objects.filter(is_superuser=True).order_by('pk')[0] except IndexError: superuser = None with impersonate(superuser): diff --git a/awx/main/management/commands/inventory_import.py b/awx/main/management/commands/inventory_import.py index 187a04af7e..47b333f75c 100644 --- a/awx/main/management/commands/inventory_import.py +++ b/awx/main/management/commands/inventory_import.py @@ -53,13 +53,13 @@ class MemObject(object): ''' Common code shared between in-memory groups and hosts. ''' - + def __init__(self, name, source_dir): assert name, 'no name' assert source_dir, 'no source dir' self.name = name self.source_dir = source_dir - + def load_vars(self, base_path): all_vars = {} files_found = 0 @@ -107,7 +107,7 @@ class MemGroup(MemObject): group_vars = os.path.join(source_dir, 'group_vars', self.name) self.variables = self.load_vars(group_vars) logger.debug('Loaded group: %s', self.name) - + def child_group_by_name(self, name, loader): if name == 'all': return @@ -266,7 +266,7 @@ class BaseLoader(object): logger.debug('Filtering group %s', name) return None if name not in self.all_group.all_groups: - group = MemGroup(name, self.source_dir) + group = MemGroup(name, self.source_dir) if not child: all_group.add_child_group(group) self.all_group.all_groups[name] = group @@ -315,7 +315,7 @@ class IniLoader(BaseLoader): for t in tokens[1:]: k,v = t.split('=', 1) host.variables[k] = v - group.add_host(host) + group.add_host(host) elif input_mode == 'children': group.child_group_by_name(line, self) elif input_mode == 'vars': @@ -328,7 +328,7 @@ class IniLoader(BaseLoader): # from API documentation: # # if called with --list, inventory outputs like so: -# +# # { # "databases" : { # "hosts" : [ "host1.example.com", "host2.example.com" ], @@ -581,7 +581,7 @@ class Command(NoArgsCommand): def _get_instance_id(self, from_dict, default=''): ''' Retrieve the instance ID from the given dict of host variables. - + The instance ID variable may be specified as 'foo.bar', in which case the lookup will traverse into nested dicts, equivalent to: @@ -633,7 +633,7 @@ class Command(NoArgsCommand): else: q = dict(name=self.inventory_name) try: - self.inventory = Inventory.objects.filter(active=True).get(**q) + self.inventory = Inventory.objects.get(**q) except Inventory.DoesNotExist: raise CommandError('Inventory with %s = %s cannot be found' % q.items()[0]) except Inventory.MultipleObjectsReturned: @@ -648,8 +648,7 @@ class Command(NoArgsCommand): if inventory_source_id: try: self.inventory_source = InventorySource.objects.get(pk=inventory_source_id, - inventory=self.inventory, - active=True) + inventory=self.inventory) except InventorySource.DoesNotExist: raise CommandError('Inventory source with id=%s not found' % inventory_source_id) @@ -669,7 +668,6 @@ class Command(NoArgsCommand): source_path=os.path.abspath(self.source), overwrite=self.overwrite, overwrite_vars=self.overwrite_vars, - active=True, ) self.inventory_update = self.inventory_source.create_inventory_update( job_args=json.dumps(sys.argv), @@ -703,7 +701,7 @@ class Command(NoArgsCommand): host_qs = self.inventory_source.group.all_hosts else: host_qs = self.inventory.hosts.all() - host_qs = host_qs.filter(active=True, instance_id='', + host_qs = host_qs.filter(instance_id='', variables__contains=self.instance_id_var.split('.')[0]) for host in host_qs: instance_id = self._get_instance_id(host.variables_dict) @@ -740,7 +738,7 @@ class Command(NoArgsCommand): hosts_qs = self.inventory_source.group.all_hosts # FIXME: Also include hosts from inventory_source.managed_hosts? else: - hosts_qs = self.inventory.hosts.filter(active=True) + hosts_qs = self.inventory.hosts # Build list of all host pks, remove all that should not be deleted. del_host_pks = set(hosts_qs.values_list('pk', flat=True)) if self.instance_id_var: @@ -765,7 +763,7 @@ class Command(NoArgsCommand): del_pks = all_del_pks[offset:(offset + self._batch_size)] for host in hosts_qs.filter(pk__in=del_pks): host_name = host.name - host.mark_inactive() + host.delete() self.logger.info('Deleted host "%s"', host_name) if settings.SQL_DEBUG: self.logger.warning('host deletions took %d queries for %d hosts', @@ -785,7 +783,7 @@ class Command(NoArgsCommand): groups_qs = self.inventory_source.group.all_children # FIXME: Also include groups from inventory_source.managed_groups? else: - groups_qs = self.inventory.groups.filter(active=True) + groups_qs = self.inventory.groups # Build list of all group pks, remove those that should not be deleted. del_group_pks = set(groups_qs.values_list('pk', flat=True)) all_group_names = self.all_group.all_groups.keys() @@ -799,7 +797,8 @@ class Command(NoArgsCommand): del_pks = all_del_pks[offset:(offset + self._batch_size)] for group in groups_qs.filter(pk__in=del_pks): group_name = group.name - group.mark_inactive(recompute=False) + with ignore_inventory_computed_fields(): + group.delete() self.logger.info('Group "%s" deleted', group_name) if settings.SQL_DEBUG: self.logger.warning('group deletions took %d queries for %d groups', @@ -821,10 +820,10 @@ class Command(NoArgsCommand): if self.inventory_source.group: db_groups = self.inventory_source.group.all_children else: - db_groups = self.inventory.groups.filter(active=True) + db_groups = self.inventory.groups for db_group in db_groups: # Delete child group relationships not present in imported data. - db_children = db_group.children.filter(active=True) + db_children = db_group.children db_children_name_pk_map = dict(db_children.values_list('name', 'pk')) mem_children = self.all_group.all_groups[db_group.name].children for mem_group in mem_children: @@ -839,7 +838,7 @@ class Command(NoArgsCommand): db_child.name, db_group.name) # FIXME: Inventory source group relationships # Delete group/host relationships not present in imported data. - db_hosts = db_group.hosts.filter(active=True) + db_hosts = db_group.hosts del_host_pks = set(db_hosts.values_list('pk', flat=True)) mem_hosts = self.all_group.all_groups[db_group.name].hosts all_mem_host_names = [h.name for h in mem_hosts if not h.instance_id] @@ -860,7 +859,7 @@ class Command(NoArgsCommand): del_pks = del_host_pks[offset:(offset + self._batch_size)] for db_host in db_hosts.filter(pk__in=del_pks): group_host_count += 1 - if db_host not in db_group.hosts.filter(active=True): + if db_host not in db_group.hosts: continue db_group.hosts.remove(db_host) self.logger.info('Host "%s" removed from group "%s"', @@ -1036,7 +1035,7 @@ class Command(NoArgsCommand): all_host_pks = sorted(mem_host_pk_map.keys()) for offset in xrange(0, len(all_host_pks), self._batch_size): host_pks = all_host_pks[offset:(offset + self._batch_size)] - for db_host in self.inventory.hosts.filter(active=True, pk__in=host_pks): + for db_host in self.inventory.hosts.filter( pk__in=host_pks): if db_host.pk in host_pks_updated: continue mem_host = mem_host_pk_map[db_host.pk] @@ -1048,7 +1047,7 @@ class Command(NoArgsCommand): all_instance_ids = sorted(mem_host_instance_id_map.keys()) for offset in xrange(0, len(all_instance_ids), self._batch_size): instance_ids = all_instance_ids[offset:(offset + self._batch_size)] - for db_host in self.inventory.hosts.filter(active=True, instance_id__in=instance_ids): + for db_host in self.inventory.hosts.filter( instance_id__in=instance_ids): if db_host.pk in host_pks_updated: continue mem_host = mem_host_instance_id_map[db_host.instance_id] @@ -1060,7 +1059,7 @@ class Command(NoArgsCommand): all_host_names = sorted(mem_host_name_map.keys()) for offset in xrange(0, len(all_host_names), self._batch_size): host_names = all_host_names[offset:(offset + self._batch_size)] - for db_host in self.inventory.hosts.filter(active=True, name__in=host_names): + for db_host in self.inventory.hosts.filter( name__in=host_names): if db_host.pk in host_pks_updated: continue mem_host = mem_host_name_map[db_host.name] @@ -1297,7 +1296,7 @@ class Command(NoArgsCommand): except CommandError as e: self.mark_license_failure(save=True) raise e - + if self.inventory_source.group: inv_name = 'group "%s"' % (self.inventory_source.group.name) else: @@ -1336,7 +1335,7 @@ class Command(NoArgsCommand): self.inventory_update.result_traceback = tb self.inventory_update.status = status self.inventory_update.save(update_fields=['status', 'result_traceback']) - + if exc and isinstance(exc, CommandError): sys.exit(1) elif exc: diff --git a/awx/main/managers.py b/awx/main/managers.py index 355f616dac..4825a74cf8 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -13,9 +13,9 @@ class HostManager(models.Manager): def active_count(self): """Return count of active, unique hosts for licensing.""" try: - return self.filter(active=True, inventory__active=True).order_by('name').distinct('name').count() + return self.order_by('name').distinct('name').count() except NotImplementedError: # For unit tests only, SQLite doesn't support distinct('name') - return len(set(self.filter(active=True, inventory__active=True).values_list('name', flat=True))) + return len(set(self.values_list('name', flat=True))) class InstanceManager(models.Manager): """A custom manager class for the Instance model. diff --git a/awx/main/migrations/0005_v300_active_flag_removal.py b/awx/main/migrations/0005_v300_active_flag_removal.py new file mode 100644 index 0000000000..adc6a0cddf --- /dev/null +++ b/awx/main/migrations/0005_v300_active_flag_removal.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from awx.main.migrations import _cleanup_deleted as cleanup_deleted +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0004_v300_changes'), + ] + + operations = [ + migrations.RunPython(cleanup_deleted.cleanup_deleted), + + migrations.RemoveField( + model_name='credential', + name='active', + ), + migrations.RemoveField( + model_name='custominventoryscript', + name='active', + ), + migrations.RemoveField( + model_name='group', + name='active', + ), + migrations.RemoveField( + model_name='host', + name='active', + ), + migrations.RemoveField( + model_name='inventory', + name='active', + ), + migrations.RemoveField( + model_name='notifier', + name='active', + ), + migrations.RemoveField( + model_name='organization', + name='active', + ), + migrations.RemoveField( + model_name='permission', + name='active', + ), + migrations.RemoveField( + model_name='schedule', + name='active', + ), + migrations.RemoveField( + model_name='team', + name='active', + ), + migrations.RemoveField( + model_name='unifiedjob', + name='active', + ), + migrations.RemoveField( + model_name='unifiedjobtemplate', + name='active', + ), + ] diff --git a/awx/main/migrations/0006_v300_active_flag_removal.py b/awx/main/migrations/0006_v300_active_flag_removal.py deleted file mode 100644 index 795db642b2..0000000000 --- a/awx/main/migrations/0006_v300_active_flag_removal.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from awx.main.migrations import _rbac as rbac -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('main', '0005_v300_changes'), - ] - - operations = [ - # This is a placeholder for our future active flag removal work - ] diff --git a/awx/main/migrations/0007_v300_rbac_changes.py b/awx/main/migrations/0006_v300_rbac_changes.py similarity index 98% rename from awx/main/migrations/0007_v300_rbac_changes.py rename to awx/main/migrations/0006_v300_rbac_changes.py index f77d4c026a..d9313fe8aa 100644 --- a/awx/main/migrations/0007_v300_rbac_changes.py +++ b/awx/main/migrations/0006_v300_rbac_changes.py @@ -14,7 +14,7 @@ class Migration(migrations.Migration): ('taggit', '0002_auto_20150616_2121'), ('contenttypes', '0002_remove_content_type_name'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('main', '0006_v300_active_flag_removal'), + ('main', '0005_v300_active_flag_removal'), ] operations = [ @@ -41,7 +41,6 @@ class Migration(migrations.Migration): ('created', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)), ('description', models.TextField(default=b'', blank=True)), - ('active', models.BooleanField(default=True, editable=False)), ('name', models.CharField(max_length=512)), ('singleton_name', models.TextField(default=None, unique=True, null=True, db_index=True)), ('object_id', models.PositiveIntegerField(default=None, null=True)), diff --git a/awx/main/migrations/0008_v300_rbac_migrations.py b/awx/main/migrations/0007_v300_rbac_migrations.py similarity index 92% rename from awx/main/migrations/0008_v300_rbac_migrations.py rename to awx/main/migrations/0007_v300_rbac_migrations.py index 7226c31e1b..d50069ab48 100644 --- a/awx/main/migrations/0008_v300_rbac_migrations.py +++ b/awx/main/migrations/0007_v300_rbac_migrations.py @@ -8,7 +8,7 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('main', '0007_v300_rbac_changes'), + ('main', '0006_v300_rbac_changes'), ] operations = [ diff --git a/awx/main/migrations/0005_v300_changes.py b/awx/main/migrations/0008_v300_changes.py similarity index 97% rename from awx/main/migrations/0005_v300_changes.py rename to awx/main/migrations/0008_v300_changes.py index e350c881f2..fe6c67f48d 100644 --- a/awx/main/migrations/0005_v300_changes.py +++ b/awx/main/migrations/0008_v300_changes.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.db import migrations, models +from django.db import migrations from django.utils.timezone import now from awx.api.license import feature_enabled @@ -107,7 +107,7 @@ def create_system_job_templates(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('main', '0004_v300_changes'), + ('main', '0007_v300_rbac_migrations'), ] operations = [ diff --git a/awx/main/migrations/_cleanup_deleted.py b/awx/main/migrations/_cleanup_deleted.py new file mode 100644 index 0000000000..db187efeb4 --- /dev/null +++ b/awx/main/migrations/_cleanup_deleted.py @@ -0,0 +1,85 @@ +# Copyright (c) 2016 Ansible, Inc. +# All Rights Reserved. + +# Python +import logging + +# Django +from django.db import transaction +from django.utils.dateparse import parse_datetime + +def cleanup_deleted(apps, schema_editor): + logger = logging.getLogger('awx.main.migrations.cleanup_deleted') + + def cleanup_model(model): + + ''' + 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. + ''' + logger.debug('cleaning up model %s', model) + + name_field = 'name' + name_prefix = '_deleted_' + active_field = None + n_deleted_items = 0 + for field in model._meta.fields: + if field.name in ('is_active', 'active'): + active_field = field.name + if field.name == 'is_active': # is User model + name_field = 'username' + name_prefix = '_d_' + if not active_field: + logger.warning('skipping model %s, no active field', model) + return n_deleted_items + qs = model.objects.filter(**{ + active_field: False, + '%s__startswith' % name_field: name_prefix, + }) + pks_to_delete = set() + for instance in qs.iterator(): + dt = parse_datetime(getattr(instance, name_field).split('_')[2]) + if not dt: + logger.warning('unable to find deleted timestamp in %s field', name_field) + else: + action_text = 'deleting' + logger.info('%s %s', action_text, instance) + n_deleted_items += 1 + instance.delete() + + # Cleanup objects in batches instead of deleting each one individually. + if len(pks_to_delete) >= 50: + model.objects.filter(pk__in=pks_to_delete).delete() + pks_to_delete.clear() + if len(pks_to_delete): + model.objects.filter(pk__in=pks_to_delete).delete() + return n_deleted_items + + logger = logging.getLogger('awx.main.commands.cleanup_deleted') + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter('%(message)s')) + logger.addHandler(handler) + logger.propagate = False + + with transaction.atomic(): + n_deleted_items = 0 + + models = [ + apps.get_model('auth', "User"), + apps.get_model('main', 'Credential'), + apps.get_model('main', 'CustomInventoryScript'), + apps.get_model('main', 'Group'), + apps.get_model('main', 'Host'), + apps.get_model('main', 'Inventory'), + apps.get_model('main', 'Notifier'), + apps.get_model('main', 'Organization'), + apps.get_model('main', 'Permission'), + apps.get_model('main', 'Schedule'), + apps.get_model('main', 'Team'), + apps.get_model('main', 'UnifiedJob'), + apps.get_model('main', 'UnifiedJobTemplate'), + ] + + for model in models: + n_deleted_items += cleanup_model(model) + logger.log(99, "Removed %d items", n_deleted_items) diff --git a/awx/main/migrations/_old_access.py b/awx/main/migrations/_old_access.py index c662f0ddd0..e670f5e8e5 100644 --- a/awx/main/migrations/_old_access.py +++ b/awx/main/migrations/_old_access.py @@ -199,16 +199,16 @@ class UserAccess(BaseAccess): model = User def get_queryset(self): - qs = self.model.objects.filter(is_active=True).distinct() + qs = self.model.objects.distinct() if self.user.is_superuser: return qs - if tower_settings.ORG_ADMINS_CAN_SEE_ALL_USERS and self.user.admin_of_organizations.filter(active=True).exists(): + if tower_settings.ORG_ADMINS_CAN_SEE_ALL_USERS and self.user.admin_of_organizations.all().exists(): return qs return qs.filter( Q(pk=self.user.pk) | - Q(organizations__in=self.user.admin_of_organizations.filter(active=True)) | - Q(organizations__in=self.user.organizations.filter(active=True)) | - Q(teams__in=self.user.teams.filter(active=True)) + Q(organizations__in=self.user.admin_of_organizations) | + Q(organizations__in=self.user.organizations) | + Q(teams__in=self.user.teams) ).distinct() def can_add(self, data): @@ -216,7 +216,7 @@ class UserAccess(BaseAccess): if to_python_boolean(data['is_superuser'], allow_none=True) and not self.user.is_superuser: return False return bool(self.user.is_superuser or - self.user.admin_of_organizations.filter(active=True).exists()) + self.user.admin_of_organizations.exists()) def can_change(self, obj, data): if data is not None and 'is_superuser' in data: @@ -231,18 +231,18 @@ class UserAccess(BaseAccess): # Admin implies changing all user fields. if self.user.is_superuser: return True - return bool(obj.organizations.filter(active=True, deprecated_admins__in=[self.user]).exists()) + return bool(obj.organizations.filter(deprecated_admins__in=[self.user]).exists()) def can_delete(self, obj): if obj == self.user: # cannot delete yourself return False - super_users = User.objects.filter(is_active=True, is_superuser=True) + super_users = User.objects.filter(is_superuser=True) if obj.is_superuser and super_users.count() == 1: # cannot delete the last active superuser return False return bool(self.user.is_superuser or - obj.organizations.filter(active=True, deprecated_admins__in=[self.user]).exists()) + obj.organizations.filter(deprecated_admins__in=[self.user]).exists()) class OrganizationAccess(BaseAccess): ''' @@ -257,7 +257,7 @@ class OrganizationAccess(BaseAccess): model = Organization def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by') if self.user.is_superuser: return qs @@ -295,25 +295,21 @@ class InventoryAccess(BaseAccess): def get_queryset(self, allowed=None, ad_hoc=None): allowed = allowed or PERMISSION_TYPES_ALLOWING_INVENTORY_READ - qs = Inventory.objects.filter(active=True).distinct() + qs = Inventory.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'organization') if self.user.is_superuser: return qs - qs = qs.filter(organization__active=True) admin_of = qs.filter(organization__deprecated_admins__in=[self.user]).distinct() has_user_kw = dict( permissions__user__in=[self.user], permissions__permission_type__in=allowed, - permissions__active=True, ) if ad_hoc is not None: has_user_kw['permissions__run_ad_hoc_commands'] = ad_hoc has_user_perms = qs.filter(**has_user_kw).distinct() has_team_kw = dict( permissions__team__deprecated_users__in=[self.user], - permissions__team__active=True, permissions__permission_type__in=allowed, - permissions__active=True, ) if ad_hoc is not None: has_team_kw['permissions__run_ad_hoc_commands'] = ad_hoc @@ -330,7 +326,7 @@ class InventoryAccess(BaseAccess): # If no data is specified, just checking for generic add permission? if not data: return bool(self.user.is_superuser or - self.user.admin_of_organizations.filter(active=True).exists()) + self.user.admin_of_organizations.exists()) # Otherwise, verify that the user has access to change the parent # organization of this inventory. if self.user.is_superuser: @@ -379,7 +375,7 @@ class HostAccess(BaseAccess): model = Host def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory', 'last_job__job_template', 'last_job_host_summary__job') @@ -435,7 +431,7 @@ class GroupAccess(BaseAccess): model = Group def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory') qs = qs.prefetch_related('parents', 'children', 'inventory_source') inventory_ids = set(self.user.get_queryset(Inventory).values_list('id', flat=True)) @@ -466,9 +462,6 @@ class GroupAccess(BaseAccess): if not super(GroupAccess, self).can_attach(obj, sub_obj, relationship, data, skip_sub_obj_read_check): return False - # Don't allow attaching if the sub obj is not active - if not obj.active: - return False # Prevent assignments between different inventories. if obj.inventory != sub_obj.inventory: raise ParseError('Cannot associate two items from different inventories') @@ -495,7 +488,7 @@ class InventorySourceAccess(BaseAccess): model = InventorySource def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'group', 'inventory') inventory_ids = set(self.user.get_queryset(Inventory).values_list('id', flat=True)) return qs.filter(Q(inventory_id__in=inventory_ids) | @@ -535,7 +528,7 @@ class InventoryUpdateAccess(BaseAccess): model = InventoryUpdate def get_queryset(self): - qs = InventoryUpdate.objects.filter(active=True).distinct() + qs = InventoryUpdate.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory_source__group', 'inventory_source__inventory') inventory_sources_qs = self.user.get_queryset(InventorySource) @@ -569,19 +562,19 @@ class CredentialAccess(BaseAccess): # Create a base queryset. # If the user is a superuser, and therefore can see everything, this # is also sufficient, and we are done. - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'user', 'team') if self.user.is_superuser: return qs # Get the list of organizations for which the user is an admin - orgs_as_admin_ids = set(self.user.admin_of_organizations.filter(active=True).values_list('id', flat=True)) + orgs_as_admin_ids = set(self.user.admin_of_organizations.values_list('id', flat=True)) return qs.filter( Q(user=self.user) | Q(user__organizations__id__in=orgs_as_admin_ids) | Q(user__admin_of_organizations__id__in=orgs_as_admin_ids) | - Q(team__organization__id__in=orgs_as_admin_ids, team__active=True) | - Q(team__deprecated_users__in=[self.user], team__active=True) + Q(team__organization__id__in=orgs_as_admin_ids) | + Q(team__deprecated_users__in=[self.user]) ) def can_add(self, data): @@ -607,12 +600,12 @@ class CredentialAccess(BaseAccess): if obj.user: if self.user == obj.user: return True - if obj.user.organizations.filter(active=True, deprecated_admins__in=[self.user]).exists(): + if obj.user.organizations.filter(deprecated_admins__in=[self.user]).exists(): return True - if obj.user.admin_of_organizations.filter(active=True, deprecated_admins__in=[self.user]).exists(): + if obj.user.admin_of_organizations.filter(deprecated_admins__in=[self.user]).exists(): return True if obj.team: - if self.user in obj.team.organization.deprecated_admins.filter(is_active=True): + if self.user in obj.team.organization.deprecated_admins.all(): return True return False @@ -637,12 +630,12 @@ class TeamAccess(BaseAccess): model = Team def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'organization') if self.user.is_superuser: return qs return qs.filter( - Q(organization__deprecated_admins__in=[self.user], organization__active=True) | + Q(organization__deprecated_admins__in=[self.user]) | Q(deprecated_users__in=[self.user]) ) @@ -689,26 +682,24 @@ class ProjectAccess(BaseAccess): model = Project def get_queryset(self): - qs = Project.objects.filter(active=True).distinct() + qs = Project.objects.distinct() qs = qs.select_related('modified_by', 'credential', 'current_job', 'last_job') if self.user.is_superuser: return qs team_ids = set(Team.objects.filter(deprecated_users__in=[self.user]).values_list('id', flat=True)) qs = qs.filter(Q(created_by=self.user, deprecated_organizations__isnull=True) | - Q(deprecated_organizations__deprecated_admins__in=[self.user], deprecated_organizations__active=True) | - Q(deprecated_organizations__deprecated_users__in=[self.user], deprecated_organizations__active=True) | + Q(deprecated_organizations__deprecated_admins__in=[self.user]) | + Q(deprecated_organizations__deprecated_users__in=[self.user]) | Q(teams__in=team_ids)) allowed_deploy = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY] allowed_check = [PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_DEPLOY, PERM_INVENTORY_CHECK] deploy_permissions_ids = set(Permission.objects.filter( Q(user=self.user) | Q(team_id__in=team_ids), - active=True, permission_type__in=allowed_deploy, ).values_list('id', flat=True)) check_permissions_ids = set(Permission.objects.filter( Q(user=self.user) | Q(team_id__in=team_ids), - active=True, permission_type__in=allowed_check, ).values_list('id', flat=True)) @@ -719,16 +710,16 @@ class ProjectAccess(BaseAccess): def can_add(self, data): if self.user.is_superuser: return True - if self.user.admin_of_organizations.filter(active=True).exists(): + if self.user.admin_of_organizations.exists(): return True return False def can_change(self, obj, data): if self.user.is_superuser: return True - if obj.created_by == self.user and not obj.deprecated_organizations.filter(active=True).count(): + if obj.created_by == self.user and not obj.deprecated_organizations.count(): return True - if obj.deprecated_organizations.filter(active=True, deprecated_admins__in=[self.user]).exists(): + if obj.deprecated_organizations.filter(deprecated_admins__in=[self.user]).exists(): return True return False @@ -748,7 +739,7 @@ class ProjectUpdateAccess(BaseAccess): model = ProjectUpdate def get_queryset(self): - qs = ProjectUpdate.objects.filter(active=True).distinct() + qs = ProjectUpdate.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'project') project_ids = set(self.user.get_queryset(Project).values_list('id', flat=True)) return qs.filter(project_id__in=project_ids) @@ -776,18 +767,18 @@ class PermissionAccess(BaseAccess): model = Permission def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'user', 'team', 'inventory', 'project') if self.user.is_superuser: return qs - orgs_as_admin_ids = set(self.user.admin_of_organizations.filter(active=True).values_list('id', flat=True)) + orgs_as_admin_ids = set(self.user.admin_of_organizations.values_list('id', flat=True)) return qs.filter( Q(user__organizations__in=orgs_as_admin_ids) | Q(user__admin_of_organizations__in=orgs_as_admin_ids) | - Q(team__organization__in=orgs_as_admin_ids, team__active=True) | + Q(team__organization__in=orgs_as_admin_ids) | Q(user=self.user) | - Q(team__deprecated_users__in=[self.user], team__active=True) + Q(team__deprecated_users__in=[self.user]) ) def can_add(self, data): @@ -868,7 +859,7 @@ class JobTemplateAccess(BaseAccess): model = JobTemplate def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory', 'project', 'credential', 'cloud_credential', 'next_schedule') if self.user.is_superuser: @@ -892,12 +883,10 @@ class JobTemplateAccess(BaseAccess): # TODO: I think the below queries can be combined deploy_permissions_ids = Permission.objects.filter( Q(user=self.user) | Q(team_id__in=team_ids), - active=True, permission_type__in=allowed_deploy, ) check_permissions_ids = Permission.objects.filter( Q(user=self.user) | Q(team_id__in=team_ids), - active=True, permission_type__in=allowed_check, ) @@ -986,7 +975,6 @@ class JobTemplateAccess(BaseAccess): Q(user=self.user) | Q(team__deprecated_users__in=[self.user]), inventory=inventory, project=project, - active=True, #permission_type__in=[PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY], permission_type=PERM_JOBTEMPLATE_CREATE, ) @@ -1044,7 +1032,6 @@ class JobTemplateAccess(BaseAccess): Q(user=self.user) | Q(team__deprecated_users__in=[self.user]), inventory=obj.inventory, project=obj.project, - active=True, permission_type__in=[PERM_JOBTEMPLATE_CREATE, PERM_INVENTORY_CHECK, PERM_INVENTORY_DEPLOY], ) @@ -1086,7 +1073,7 @@ class JobAccess(BaseAccess): model = Job def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'job_template', 'inventory', 'project', 'credential', 'cloud_credential', 'job_template') qs = qs.prefetch_related('unified_job_template') @@ -1108,12 +1095,10 @@ class JobAccess(BaseAccess): # TODO: I think the below queries can be combined deploy_permissions_ids = Permission.objects.filter( Q(user=self.user) | Q(team__in=team_ids), - active=True, permission_type__in=allowed_deploy, ) check_permissions_ids = Permission.objects.filter( Q(user=self.user) | Q(team__in=team_ids), - active=True, permission_type__in=allowed_check, ) @@ -1212,18 +1197,17 @@ class AdHocCommandAccess(BaseAccess): model = AdHocCommand def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by', 'inventory', 'credential') if self.user.is_superuser: return qs credential_ids = set(self.user.get_queryset(Credential).values_list('id', flat=True)) - team_ids = set(Team.objects.filter(active=True, deprecated_users__in=[self.user]).values_list('id', flat=True)) + team_ids = set(Team.objects.filter(deprecated_users__in=[self.user]).values_list('id', flat=True)) permission_ids = set(Permission.objects.filter( Q(user=self.user) | Q(team__in=team_ids), - active=True, permission_type__in=PERMISSION_TYPES_ALLOWING_INVENTORY_READ, run_ad_hoc_commands=True, ).values_list('id', flat=True)) @@ -1247,7 +1231,7 @@ class AdHocCommandAccess(BaseAccess): # If a credential is provided, the user should have read access to it. credential_pk = get_pk_from_dict(data, 'credential') if credential_pk: - credential = get_object_or_400(Credential, pk=credential_pk, active=True) + credential = get_object_or_400(Credential, pk=credential_pk) if not check_user_access(self.user, Credential, 'read', credential): return False @@ -1255,7 +1239,7 @@ class AdHocCommandAccess(BaseAccess): # given inventory. inventory_pk = get_pk_from_dict(data, 'inventory') if inventory_pk: - inventory = get_object_or_400(Inventory, pk=inventory_pk, active=True) + inventory = get_object_or_400(Inventory, pk=inventory_pk) if not check_user_access(self.user, Inventory, 'run_ad_hoc_commands', inventory): return False @@ -1375,7 +1359,7 @@ class UnifiedJobTemplateAccess(BaseAccess): model = UnifiedJobTemplate def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() project_qs = self.user.get_queryset(Project).filter(scm_type__in=[s[0] for s in Project.SCM_TYPE_CHOICES]) inventory_source_qs = self.user.get_queryset(InventorySource).filter(source__in=CLOUD_INVENTORY_SOURCES) job_template_qs = self.user.get_queryset(JobTemplate) @@ -1405,7 +1389,7 @@ class UnifiedJobAccess(BaseAccess): model = UnifiedJob def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() project_update_qs = self.user.get_queryset(ProjectUpdate) inventory_update_qs = self.user.get_queryset(InventoryUpdate).filter(source__in=CLOUD_INVENTORY_SOURCES) job_qs = self.user.get_queryset(Job) @@ -1442,7 +1426,7 @@ class ScheduleAccess(BaseAccess): model = Schedule def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() qs = qs.select_related('created_by', 'modified_by') qs = qs.prefetch_related('unified_job_template') if self.user.is_superuser: @@ -1614,7 +1598,7 @@ class CustomInventoryScriptAccess(BaseAccess): model = CustomInventoryScript def get_queryset(self): - qs = self.model.objects.filter(active=True).distinct() + qs = self.model.objects.distinct() if not self.user.is_superuser: qs = qs.filter(Q(organization__deprecated_admins__in=[self.user]) | Q(organization__deprecated_users__in=[self.user])) return qs @@ -1622,8 +1606,6 @@ class CustomInventoryScriptAccess(BaseAccess): def can_read(self, obj): if self.user.is_superuser: return True - if not obj.active: - return False return bool(obj.organization in self.user.organizations.all() or obj.organization in self.user.admin_of_organizations.all()) def can_add(self, data): diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index 8bd2a91f50..33bcb82b4a 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -73,7 +73,7 @@ def migrate_inventory(apps, schema_editor): for inventory in Inventory.objects.all(): teams, users = [], [] - for perm in Permission.objects.filter(inventory=inventory, active=True): + for perm in Permission.objects.filter(inventory=inventory): role = None execrole = None if perm.permission_type == 'admin': @@ -186,7 +186,7 @@ def migrate_projects(apps, schema_editor): project.member_role.members.add(user) migrations[project.name]['users'].add(user) - for perm in Permission.objects.filter(project=project, active=True): + for perm in Permission.objects.filter(project=project): # All perms at this level just imply a user or team can read if perm.team: perm.team.member_role.children.add(project.member_role) @@ -253,7 +253,6 @@ def migrate_job_templates(apps, schema_editor): permission = Permission.objects.filter( inventory=jt.inventory, project=jt.project, - active=True, permission_type__in=['create', 'check', 'run'] if jt.job_type == 'check' else ['create', 'run'], ) diff --git a/awx/main/models/ad_hoc_commands.py b/awx/main/models/ad_hoc_commands.py index c5ab627046..c97c484c5e 100644 --- a/awx/main/models/ad_hoc_commands.py +++ b/awx/main/models/ad_hoc_commands.py @@ -87,7 +87,7 @@ class AdHocCommand(UnifiedJob): def clean_inventory(self): inv = self.inventory - if not inv or not inv.active: + if not inv: raise ValidationError('Inventory is no longer available.') return inv @@ -123,7 +123,7 @@ class AdHocCommand(UnifiedJob): @property def passwords_needed_to_start(self): '''Return list of password field names needed to start the job.''' - if self.credential and self.credential.active: + if self.credential: return self.credential.passwords_needed else: return [] @@ -164,14 +164,14 @@ class AdHocCommand(UnifiedJob): def task_impact(self): # NOTE: We sorta have to assume the host count matches and that forks default to 5 from awx.main.models.inventory import Host - count_hosts = Host.objects.filter(active=True, enabled=True, inventory__ad_hoc_commands__pk=self.pk).count() + count_hosts = Host.objects.filter( enabled=True, inventory__ad_hoc_commands__pk=self.pk).count() return min(count_hosts, 5 if self.forks == 0 else self.forks) * 10 def generate_dependencies(self, active_tasks): from awx.main.models import InventoryUpdate if not self.inventory: return [] - inventory_sources = self.inventory.inventory_sources.filter(active=True, update_on_launch=True) + inventory_sources = self.inventory.inventory_sources.filter( update_on_launch=True) inventory_sources_found = [] dependencies = [] for obj in active_tasks: diff --git a/awx/main/models/base.py b/awx/main/models/base.py index c4edfbd8ba..a25dd1d154 100644 --- a/awx/main/models/base.py +++ b/awx/main/models/base.py @@ -203,15 +203,6 @@ class PasswordFieldsModel(BaseModel): def _password_field_allows_ask(self, field): 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): new_instance = not bool(self.pk) # If update_fields has been specified, add our field names to it, @@ -273,29 +264,9 @@ class PrimordialModel(CreatedModifiedModel): editable=False, on_delete=models.SET_NULL, ) - active = models.BooleanField( - default=True, - editable=False, - ) tags = TaggableManager(blank=True) - def mark_inactive(self, save=True, update_fields=None, skip_active_check=False): - '''Use instead of delete to rename and mark inactive.''' - update_fields = update_fields or [] - if skip_active_check or self.active: - dtnow = now() - if 'name' in self._meta.get_all_field_names(): - self.name = "_deleted_%s_%s" % (dtnow.isoformat(), self.name) - if 'name' not in update_fields: - update_fields.append('name') - self.active = False - if 'active' not in update_fields: - update_fields.append('active') - if save: - self.save(update_fields=update_fields) - return update_fields - def save(self, *args, **kwargs): update_fields = kwargs.get('update_fields', []) user = get_current_user() diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 32175b19d9..50d0fd58bd 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -26,7 +26,7 @@ from awx.main.models.jobs import Job from awx.main.models.unified_jobs import * # noqa from awx.main.models.mixins import ResourceMixin 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 __all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'CustomInventoryScript'] @@ -120,30 +120,15 @@ class Inventory(CommonModel, ResourceMixin): def get_absolute_url(self): 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') - def get_group_hosts_map(self, active=None): + def get_group_hosts_map(self): ''' Return dictionary mapping group_id to set of child host_id's. ''' # FIXME: Cache this mapping? group_hosts_kw = dict(group__inventory_id=self.pk, host__inventory_id=self.pk) - if active is not None: - group_hosts_kw['group__active'] = active - group_hosts_kw['host__active'] = active group_hosts_qs = Group.hosts.through.objects.filter(**group_hosts_kw) group_hosts_qs = group_hosts_qs.values_list('group_id', 'host_id') group_hosts_map = {} @@ -152,15 +137,12 @@ class Inventory(CommonModel, ResourceMixin): group_host_ids.add(host_id) return group_hosts_map - def get_group_parents_map(self, active=None): + def get_group_parents_map(self): ''' Return dictionary mapping group_id to set of parent group_id's. ''' # FIXME: Cache this mapping? group_parents_kw = dict(from_group__inventory_id=self.pk, to_group__inventory_id=self.pk) - if active is not None: - group_parents_kw['from_group__active'] = active - group_parents_kw['to_group__active'] = active group_parents_qs = Group.parents.through.objects.filter(**group_parents_kw) group_parents_qs = group_parents_qs.values_list('from_group_id', 'to_group_id') group_parents_map = {} @@ -169,15 +151,12 @@ class Inventory(CommonModel, ResourceMixin): group_parents.add(to_group_id) return group_parents_map - def get_group_children_map(self, active=None): + def get_group_children_map(self): ''' Return dictionary mapping group_id to set of child group_id's. ''' # FIXME: Cache this mapping? group_parents_kw = dict(from_group__inventory_id=self.pk, to_group__inventory_id=self.pk) - if active is not None: - group_parents_kw['from_group__active'] = active - group_parents_kw['to_group__active'] = active group_parents_qs = Group.parents.through.objects.filter(**group_parents_kw) group_parents_qs = group_parents_qs.values_list('from_group_id', 'to_group_id') group_children_map = {} @@ -188,12 +167,12 @@ class Inventory(CommonModel, ResourceMixin): def update_host_computed_fields(self): ''' - Update computed fields for all active hosts in this inventory. + Update computed fields for all hosts in this inventory. ''' hosts_to_update = {} - hosts_qs = self.hosts.filter(active=True) + hosts_qs = self.hosts # Define queryset of all hosts with active failures. - hosts_with_active_failures = hosts_qs.filter(last_job_host_summary__isnull=False, last_job_host_summary__job__active=True, last_job_host_summary__failed=True).values_list('pk', flat=True) + hosts_with_active_failures = hosts_qs.filter(last_job_host_summary__isnull=False, last_job_host_summary__failed=True).values_list('pk', flat=True) # Find all hosts that need the has_active_failures flag set. hosts_to_set = hosts_qs.filter(has_active_failures=False, pk__in=hosts_with_active_failures) for host_pk in hosts_to_set.values_list('pk', flat=True): @@ -205,7 +184,7 @@ class Inventory(CommonModel, ResourceMixin): host_updates = hosts_to_update.setdefault(host_pk, {}) host_updates['has_active_failures'] = False # Define queryset of all hosts with cloud inventory sources. - hosts_with_cloud_inventory = hosts_qs.filter(inventory_sources__active=True, inventory_sources__source__in=CLOUD_INVENTORY_SOURCES).values_list('pk', flat=True) + hosts_with_cloud_inventory = hosts_qs.filter(inventory_sources__source__in=CLOUD_INVENTORY_SOURCES).values_list('pk', flat=True) # Find all hosts that need the has_inventory_sources flag set. hosts_to_set = hosts_qs.filter(has_inventory_sources=False, pk__in=hosts_with_cloud_inventory) for host_pk in hosts_to_set.values_list('pk', flat=True): @@ -230,13 +209,13 @@ class Inventory(CommonModel, ResourceMixin): ''' Update computed fields for all active groups in this inventory. ''' - group_children_map = self.get_group_children_map(active=True) - group_hosts_map = self.get_group_hosts_map(active=True) - active_host_pks = set(self.hosts.filter(active=True).values_list('pk', flat=True)) - failed_host_pks = set(self.hosts.filter(active=True, last_job_host_summary__job__active=True, last_job_host_summary__failed=True).values_list('pk', flat=True)) - # active_group_pks = set(self.groups.filter(active=True).values_list('pk', flat=True)) + group_children_map = self.get_group_children_map() + group_hosts_map = self.get_group_hosts_map() + active_host_pks = set(self.hosts.values_list('pk', flat=True)) + failed_host_pks = set(self.hosts.filter(last_job_host_summary__failed=True).values_list('pk', flat=True)) + # active_group_pks = set(self.groups.values_list('pk', flat=True)) failed_group_pks = set() # Update below as we check each group. - groups_with_cloud_pks = set(self.groups.filter(active=True, inventory_sources__active=True, inventory_sources__source__in=CLOUD_INVENTORY_SOURCES).values_list('pk', flat=True)) + groups_with_cloud_pks = set(self.groups.filter(inventory_sources__source__in=CLOUD_INVENTORY_SOURCES).values_list('pk', flat=True)) groups_to_update = {} # Build list of group pks to check, starting with the groups at the @@ -308,11 +287,11 @@ class Inventory(CommonModel, ResourceMixin): self.update_host_computed_fields() if update_groups: self.update_group_computed_fields() - active_hosts = self.hosts.filter(active=True) + active_hosts = self.hosts failed_hosts = active_hosts.filter(has_active_failures=True) - active_groups = self.groups.filter(active=True) + active_groups = self.groups failed_groups = active_groups.filter(has_active_failures=True) - active_inventory_sources = self.inventory_sources.filter(active=True, source__in=CLOUD_INVENTORY_SOURCES) + active_inventory_sources = self.inventory_sources.filter( source__in=CLOUD_INVENTORY_SOURCES) failed_inventory_sources = active_inventory_sources.filter(last_job_failed=True) computed_fields = { 'has_active_failures': bool(failed_hosts.count()), @@ -412,24 +391,13 @@ class Host(CommonModelNameNotUnique, ResourceMixin): def get_absolute_url(self): 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): ''' Update model fields that are computed from database relationships. ''' has_active_failures = bool(self.last_job_host_summary and - self.last_job_host_summary.job.active and self.last_job_host_summary.failed) - active_inventory_sources = self.inventory_sources.filter(active=True, - source__in=CLOUD_INVENTORY_SOURCES) + active_inventory_sources = self.inventory_sources.filter(source__in=CLOUD_INVENTORY_SOURCES) computed_fields = { 'has_active_failures': has_active_failures, 'has_inventory_sources': bool(active_inventory_sources.count()), @@ -445,7 +413,7 @@ class Host(CommonModelNameNotUnique, ResourceMixin): # change. # NOTE: I think this is no longer needed # if update_groups: - # for group in self.all_groups.filter(active=True): + # for group in self.all_groups: # group.update_computed_fields() # if update_inventory: # self.inventory.update_computed_fields(update_groups=False, @@ -575,11 +543,11 @@ class Group(CommonModelNameNotUnique, ResourceMixin): return reverse('api:group_detail', args=(self.pk,)) @transaction.atomic - def mark_inactive_recursive(self): - from awx.main.tasks import bulk_inventory_element_delete + def delete_recursive(self): from awx.main.utils import ignore_inventory_computed_fields from awx.main.signals import disable_activity_stream + def mark_actual(): all_group_hosts = Group.hosts.through.objects.select_related("host", "group").filter(group__inventory=self.inventory) group_hosts = {'groups': {}, 'hosts': {}} @@ -629,51 +597,24 @@ class Group(CommonModelNameNotUnique, ResourceMixin): for direct_child in group_children[group]: linked_children.append((group, direct_child)) marked_groups.append(group) - Group.objects.filter(id__in=marked_groups).update(active=False) - Host.objects.filter(id__in=marked_hosts).update(active=False) - Group.parents.through.objects.filter(to_group__id__in=marked_groups) - 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) + Group.objects.filter(id__in=marked_groups).delete() + Host.objects.filter(id__in=marked_hosts).delete() + update_inventory_computed_fields.delay(self.inventory.id) with ignore_inventory_computed_fields(): with disable_activity_stream(): 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): ''' Update model fields that are computed from database relationships. ''' - active_hosts = self.all_hosts.filter(active=True) - failed_hosts = active_hosts.filter(last_job_host_summary__job__active=True, - last_job_host_summary__failed=True) - active_groups = self.all_children.filter(active=True) + active_hosts = self.all_hosts + failed_hosts = active_hosts.filter(last_job_host_summary__failed=True) + active_groups = self.all_children # FIXME: May not be accurate unless we always update groups depth-first. failed_groups = active_groups.filter(has_active_failures=True) - active_inventory_sources = self.inventory_sources.filter(active=True, - source__in=CLOUD_INVENTORY_SOURCES) + active_inventory_sources = self.inventory_sources.filter(source__in=CLOUD_INVENTORY_SOURCES) computed_fields = { 'total_hosts': active_hosts.count(), 'has_active_failures': bool(failed_hosts.count()), @@ -1200,7 +1141,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, ResourceMixin) def _can_update(self): if self.source == 'custom': - return bool(self.source_script and self.source_script.active) + return bool(self.source_script) else: return bool(self.source in CLOUD_INVENTORY_SOURCES) @@ -1217,7 +1158,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, ResourceMixin) @property def needs_update_on_launch(self): - if self.active and self.source and self.update_on_launch: + if self.source and self.update_on_launch: if not self.last_job_run: return True if (self.last_job_run + datetime.timedelta(seconds=self.update_cache_timeout)) <= now(): @@ -1226,7 +1167,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, ResourceMixin) @property def notifiers(self): - base_notifiers = Notifier.objects.filter(active=True) + base_notifiers = Notifier.objects error_notifiers = list(base_notifiers.filter(organization_notifiers_for_errors=self.inventory.organization)) success_notifiers = list(base_notifiers.filter(organization_notifiers_for_success=self.inventory.organization)) any_notifiers = list(base_notifiers.filter(organization_notifiers_for_any=self.inventory.organization)) @@ -1235,7 +1176,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, ResourceMixin) def clean_source(self): source = self.source if source and self.group: - qs = self.group.inventory_sources.filter(source__in=CLOUD_INVENTORY_SOURCES, active=True, group__active=True) + qs = self.group.inventory_sources.filter(source__in=CLOUD_INVENTORY_SOURCES) existing_sources = qs.exclude(pk=self.pk) if existing_sources.count(): s = u', '.join([x.group.name for x in existing_sources]) @@ -1279,7 +1220,7 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions): def save(self, *args, **kwargs): update_fields = kwargs.get('update_fields', []) inventory_source = self.inventory_source - if self.active and inventory_source.inventory and self.name == inventory_source.name: + if inventory_source.inventory and self.name == inventory_source.name: if inventory_source.group: self.name = '%s (%s)' % (inventory_source.group.name, inventory_source.inventory.name) else: @@ -1315,7 +1256,7 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions): return False if (self.source not in ('custom', 'ec2') and - not (self.credential and self.credential.active)): + not (self.credential)): return False return True diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 410f3c0392..82d1060fa5 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -149,7 +149,7 @@ class JobOptions(BaseModel): @property def passwords_needed_to_start(self): '''Return list of password field names needed to start the job.''' - if self.credential and self.credential.active: + if self.credential: return self.credential.passwords_needed else: return [] @@ -357,7 +357,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin): # Return all notifiers defined on the Job Template, on the Project, and on the Organization for each trigger type # TODO: Currently there is no org fk on project so this will need to be added once that is # available after the rbac pr - base_notifiers = Notifier.objects.filter(active=True) + base_notifiers = Notifier.objects error_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_errors__in=[self, self.project])) success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success__in=[self, self.project])) any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any__in=[self, self.project])) @@ -493,7 +493,7 @@ class Job(UnifiedJob, JobOptions): from awx.main.models import InventoryUpdate, ProjectUpdate if self.inventory is None or self.project is None: return [] - inventory_sources = self.inventory.inventory_sources.filter(active=True, update_on_launch=True) + inventory_sources = self.inventory.inventory_sources.filter( update_on_launch=True) project_found = False inventory_sources_found = [] dependencies = [] @@ -592,7 +592,7 @@ class Job(UnifiedJob, JobOptions): if not super(Job, self).can_start: return False - if not (self.credential and self.credential.active): + if not (self.credential): return False return True diff --git a/awx/main/models/organization.py b/awx/main/models/organization.py index 1362b23f37..d83c68832e 100644 --- a/awx/main/models/organization.py +++ b/awx/main/models/organization.py @@ -79,11 +79,6 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin): def __unicode__(self): 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): @@ -135,14 +130,6 @@ class Team(CommonModelNameNotUnique, ResourceMixin): def get_absolute_url(self): 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): ''' @@ -351,22 +338,6 @@ class AuthToken(BaseModel): 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. if not hasattr(User, 'get_absolute_url'): def user_get_absolute_url(user): diff --git a/awx/main/models/projects.py b/awx/main/models/projects.py index aa416bb95a..fb08f6bcfd 100644 --- a/awx/main/models/projects.py +++ b/awx/main/models/projects.py @@ -53,7 +53,7 @@ class ProjectOptions(models.Model): paths = [x.decode('utf-8') for x in os.listdir(settings.PROJECTS_ROOT) if (os.path.isdir(os.path.join(settings.PROJECTS_ROOT, x)) and not x.startswith('.') and not x.startswith('_'))] - qs = Project.objects.filter(active=True) + qs = Project.objects used_paths = qs.values_list('local_path', flat=True) return [x for x in paths if x not in used_paths] else: @@ -336,7 +336,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin): @property def needs_update_on_launch(self): - if self.active and self.scm_type and self.scm_update_on_launch: + if self.scm_type and self.scm_update_on_launch: if not self.last_job_run: return True if (self.last_job_run + datetime.timedelta(seconds=self.scm_update_cache_timeout)) <= now(): @@ -345,7 +345,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin): @property def notifiers(self): - base_notifiers = Notifier.objects.filter(active=True) + base_notifiers = Notifier.objects error_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_errors=self)) success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success=self)) any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any=self)) diff --git a/awx/main/models/schedules.py b/awx/main/models/schedules.py index 8af6da3be3..d9de8b394e 100644 --- a/awx/main/models/schedules.py +++ b/awx/main/models/schedules.py @@ -27,7 +27,7 @@ __all__ = ['Schedule'] class ScheduleFilterMethods(object): def enabled(self, enabled=True): - return self.filter(enabled=enabled, active=enabled) + return self.filter(enabled=enabled) def before(self, dt): return self.filter(next_run__lt=dt) diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 3750ccf41e..5b9949dbe9 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -210,17 +210,6 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio self.next_job_run = related_schedules[0].next_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): # 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. diff --git a/awx/main/signals.py b/awx/main/signals.py index 797296ef13..ddfc792f2a 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -8,7 +8,7 @@ import threading import json # Django -from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed +from django.db.models.signals import post_save, pre_delete, post_delete, m2m_changed from django.dispatch import receiver # Django-CRUM @@ -27,9 +27,8 @@ __all__ = [] logger = logging.getLogger('awx.main.signals') -# Update has_active_failures for inventory/groups when a Host/Group is deleted -# or marked inactive, when a Host-Group or Group-Group relationship is updated, -# or when a Job is deleted or marked inactive. +# Update has_active_failures for inventory/groups when a Host/Group is deleted, +# when a Host-Group or Group-Group relationship is updated, or when a Job is deleted def emit_job_event_detail(sender, **kwargs): instance = kwargs['instance'] @@ -69,7 +68,7 @@ def emit_update_inventory_computed_fields(sender, **kwargs): else: sender_name = unicode(sender._meta.verbose_name) if kwargs['signal'] == post_save: - if sender == Job and instance.active: + if sender == Job: return sender_action = 'saved' elif kwargs['signal'] == post_delete: @@ -92,7 +91,6 @@ def emit_update_inventory_on_created_or_deleted(sender, **kwargs): return instance = kwargs['instance'] if ('created' in kwargs and kwargs['created']) or \ - (hasattr(instance, '_saved_active_state') and instance._saved_active_state != instance.active) or \ kwargs['signal'] == post_delete: pass else: @@ -108,13 +106,6 @@ def emit_update_inventory_on_created_or_deleted(sender, **kwargs): if inventory is not None: update_inventory_computed_fields.delay(inventory.id, True) -def store_initial_active_state(sender, **kwargs): - instance = kwargs['instance'] - if instance.id is not None: - instance._saved_active_state = sender.objects.get(id=instance.id).active - else: - instance._saved_active_state = True - def rebuild_role_ancestor_list(reverse, model, instance, pk_set, **kwargs): 'When a role parent is added or removed, update our role hierarchy list' if reverse: @@ -161,20 +152,16 @@ def org_admin_edit_members(instance, action, model, reverse, pk_set, **kwargs): if action == 'pre_remove': instance.content_object.admin_role.children.remove(user.admin_role) -pre_save.connect(store_initial_active_state, sender=Host) post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Host) post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Host) -pre_save.connect(store_initial_active_state, sender=Group) post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Group) post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Group) m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.hosts.through) m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.parents.through) m2m_changed.connect(emit_update_inventory_computed_fields, sender=Host.inventory_sources.through) m2m_changed.connect(emit_update_inventory_computed_fields, sender=Group.inventory_sources.through) -pre_save.connect(store_initial_active_state, sender=InventorySource) post_save.connect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=InventorySource) -pre_save.connect(store_initial_active_state, sender=Job) post_save.connect(emit_update_inventory_on_created_or_deleted, sender=Job) post_delete.connect(emit_update_inventory_on_created_or_deleted, sender=Job) post_save.connect(emit_job_event_detail, sender=JobEvent) @@ -184,8 +171,8 @@ m2m_changed.connect(org_admin_edit_members, Role.members.through) post_save.connect(sync_superuser_status_to_rbac, sender=User) post_save.connect(create_user_role, sender=User) -# Migrate hosts, groups to parent group(s) whenever a group is deleted or -# marked as inactive. + +# Migrate hosts, groups to parent group(s) whenever a group is deleted @receiver(pre_delete, sender=Group) def save_related_pks_before_group_delete(sender, **kwargs): @@ -208,80 +195,28 @@ def migrate_children_from_deleted_group_to_parent_groups(sender, **kwargs): with ignore_inventory_group_removal(): with ignore_inventory_computed_fields(): if parents_pks: - for parent_group in Group.objects.filter(pk__in=parents_pks, active=True): - for child_host in Host.objects.filter(pk__in=hosts_pks, active=True): + for parent_group in Group.objects.filter(pk__in=parents_pks): + for child_host in Host.objects.filter(pk__in=hosts_pks): logger.debug('adding host %s to parent %s after group deletion', child_host, parent_group) parent_group.hosts.add(child_host) - for child_group in Group.objects.filter(pk__in=children_pks, active=True): + for child_group in Group.objects.filter(pk__in=children_pks): logger.debug('adding group %s to parent %s after group deletion', child_group, parent_group) parent_group.children.add(child_group) inventory_pk = getattr(instance, '_saved_inventory_pk', None) if inventory_pk: try: - inventory = Inventory.objects.get(pk=inventory_pk, active=True) + inventory = Inventory.objects.get(pk=inventory_pk) inventory.update_computed_fields() except Inventory.DoesNotExist: pass -@receiver(pre_save, sender=Group) -def save_related_pks_before_group_marked_inactive(sender, **kwargs): - if getattr(_inventory_updates, 'is_removing', False): - return - instance = kwargs['instance'] - if not instance.pk or instance.active: - return - instance._saved_inventory_pk = instance.inventory.pk - instance._saved_parents_pks = set(instance.parents.values_list('pk', flat=True)) - instance._saved_hosts_pks = set(instance.hosts.values_list('pk', flat=True)) - instance._saved_children_pks = set(instance.children.values_list('pk', flat=True)) - instance._saved_inventory_source_pk = instance.inventory_source.pk -@receiver(post_save, sender=Group) -def migrate_children_from_inactive_group_to_parent_groups(sender, **kwargs): - if getattr(_inventory_updates, 'is_removing', False): - return - instance = kwargs['instance'] - if instance.active: - return - parents_pks = getattr(instance, '_saved_parents_pks', []) - hosts_pks = getattr(instance, '_saved_hosts_pks', []) - children_pks = getattr(instance, '_saved_children_pks', []) - with ignore_inventory_group_removal(): - with ignore_inventory_computed_fields(): - if parents_pks: - for parent_group in Group.objects.filter(pk__in=parents_pks, active=True): - for child_host in Host.objects.filter(pk__in=hosts_pks, active=True): - logger.debug('moving host %s to parent %s after marking group %s inactive', - child_host, parent_group, instance) - parent_group.hosts.add(child_host) - for child_group in Group.objects.filter(pk__in=children_pks, active=True): - logger.debug('moving group %s to parent %s after marking group %s inactive', - child_group, parent_group, instance) - parent_group.children.add(child_group) - parent_group.children.remove(instance) - inventory_source_pk = getattr(instance, '_saved_inventory_source_pk', None) - if inventory_source_pk: - try: - inventory_source = InventorySource.objects.get(pk=inventory_source_pk, active=True) - inventory_source.mark_inactive() - except InventorySource.DoesNotExist: - pass - inventory_pk = getattr(instance, '_saved_inventory_pk', None) - if not getattr(_inventory_updates, 'is_updating', False): - if inventory_pk: - try: - inventory = Inventory.objects.get(pk=inventory_pk, active=True) - inventory.update_computed_fields() - except Inventory.DoesNotExist: - pass - -# Update host pointers to last_job and last_job_host_summary when a job is -# marked inactive or deleted. +# Update host pointers to last_job and last_job_host_summary when a job is deleted def _update_host_last_jhs(host): - jhs_qs = JobHostSummary.objects.filter(job__active=True, host__pk=host.pk) + jhs_qs = JobHostSummary.objects.filter(host__pk=host.pk) try: jhs = jhs_qs.order_by('-job__pk')[0] except IndexError: @@ -297,19 +232,10 @@ def _update_host_last_jhs(host): if update_fields: host.save(update_fields=update_fields) -@receiver(post_save, sender=Job) -def update_host_last_job_when_job_marked_inactive(sender, **kwargs): - instance = kwargs['instance'] - if instance.active: - return - hosts_qs = Host.objects.filter(active=True, last_job__pk=instance.pk) - for host in hosts_qs: - _update_host_last_jhs(host) - @receiver(pre_delete, sender=Job) def save_host_pks_before_job_delete(sender, **kwargs): instance = kwargs['instance'] - hosts_qs = Host.objects.filter(active=True, last_job__pk=instance.pk) + hosts_qs = Host.objects.filter( last_job__pk=instance.pk) instance._saved_hosts_pks = set(hosts_qs.values_list('pk', flat=True)) @receiver(post_delete, sender=Job) @@ -388,11 +314,6 @@ def activity_stream_update(sender, instance, **kwargs): except sender.DoesNotExist: return - # Handle the AWX mark-inactive for delete event - if hasattr(instance, 'active') and not instance.active: - activity_stream_delete(sender, instance, **kwargs) - return - new = instance changes = model_instance_diff(old, new, model_serializer_mapping) if changes is None: diff --git a/awx/main/south_migrations/0018_v14_changes.py b/awx/main/south_migrations/0018_v14_changes.py index ea4fde18ab..980c4293d6 100644 --- a/awx/main/south_migrations/0018_v14_changes.py +++ b/awx/main/south_migrations/0018_v14_changes.py @@ -13,7 +13,7 @@ class Migration(DataMigration): # and orm['appname.ModelName'] for models in other applications. # Refresh has_active_failures for all hosts. - for host in orm.Host.objects.filter(active=True): + for host in orm.Host.objects: has_active_failures = bool(host.last_job_host_summary and host.last_job_host_summary.job.active and host.last_job_host_summary.failed) @@ -30,9 +30,9 @@ class Migration(DataMigration): for subgroup in group.children.exclude(pk__in=except_group_pks): qs = qs | get_all_hosts_for_group(subgroup, except_group_pks) return qs - for group in orm.Group.objects.filter(active=True): + for group in orm.Group.objects: all_hosts = get_all_hosts_for_group(group) - failed_hosts = all_hosts.filter(active=True, + failed_hosts = all_hosts.filter( last_job_host_summary__job__active=True, last_job_host_summary__failed=True) hosts_with_active_failures = failed_hosts.count() @@ -49,8 +49,8 @@ class Migration(DataMigration): # Now update has_active_failures and hosts_with_active_failures for all # inventories. - for inventory in orm.Inventory.objects.filter(active=True): - failed_hosts = inventory.hosts.filter(active=True, has_active_failures=True) + for inventory in orm.Inventory.objects: + failed_hosts = inventory.hosts.filter( has_active_failures=True) hosts_with_active_failures = failed_hosts.count() has_active_failures = bool(hosts_with_active_failures) changed = False diff --git a/awx/main/south_migrations/0044_v1411_changes.py b/awx/main/south_migrations/0044_v1411_changes.py index 137f4cb220..c58dc8d155 100644 --- a/awx/main/south_migrations/0044_v1411_changes.py +++ b/awx/main/south_migrations/0044_v1411_changes.py @@ -8,7 +8,7 @@ from django.db import models class Migration(DataMigration): def forwards(self, orm): - for iu in orm.InventoryUpdate.objects.filter(active=True): + for iu in orm.InventoryUpdate.objects: if iu.inventory_source is None or iu.inventory_source.group is None or iu.inventory_source.inventory is None: continue iu.name = "%s (%s)" % (iu.inventory_source.group.name, iu.inventory_source.inventory.name) diff --git a/awx/main/south_migrations/0070_v221_changes.py b/awx/main/south_migrations/0070_v221_changes.py index 0bc36f27ac..c27edf8e9c 100644 --- a/awx/main/south_migrations/0070_v221_changes.py +++ b/awx/main/south_migrations/0070_v221_changes.py @@ -12,7 +12,7 @@ from django.conf import settings class Migration(DataMigration): def forwards(self, orm): - for j in orm.UnifiedJob.objects.filter(active=True): + for j in orm.UnifiedJob.objects: cur = connection.cursor() stdout_filename = os.path.join(settings.JOBOUTPUT_ROOT, "%d-%s.out" % (j.pk, str(uuid.uuid1()))) fd = open(stdout_filename, 'w') diff --git a/awx/main/tasks.py b/awx/main/tasks.py index b8bb60905b..d397b6fbeb 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -51,7 +51,7 @@ from awx.main.queue import FifoQueue from awx.main.conf import tower_settings from awx.main.task_engine import TaskSerializer, TASK_TIMEOUT_INTERVAL from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url, - ignore_inventory_computed_fields, emit_websocket_notification, + emit_websocket_notification, check_proot_installed, build_proot_temp_dir, wrap_args_with_proot) __all__ = ['RunJob', 'RunSystemJob', 'RunProjectUpdate', 'RunInventoryUpdate', @@ -110,17 +110,6 @@ def run_administrative_checks(self): tower_admin_emails, 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) def tower_periodic_scheduler(self): def get_last_run(): @@ -894,12 +883,12 @@ class RunJob(BaseTask): 'tower_job_id': job.pk, 'tower_job_launch_type': job.launch_type, } - if job.job_template and job.job_template.active: + if job.job_template: extra_vars.update({ 'tower_job_template_id': job.job_template.pk, 'tower_job_template_name': job.job_template.name, }) - if job.created_by and job.created_by.is_active: + if job.created_by: extra_vars.update({ 'tower_user_id': job.created_by.pk, 'tower_user_name': job.created_by.username, @@ -1392,7 +1381,7 @@ class RunInventoryUpdate(BaseTask): runpath = tempfile.mkdtemp(prefix='ansible_tower_launch_') handle, path = tempfile.mkstemp(dir=runpath) f = os.fdopen(handle, 'w') - if inventory_update.source_script is None or not inventory_update.source_script.active: + if inventory_update.source_script is None: raise RuntimeError('Inventory Script does not exist') f.write(inventory_update.source_script.script.encode('utf-8')) f.close() diff --git a/awx/main/tests/job_base.py b/awx/main/tests/job_base.py index 5b7408d19d..54c3462fbc 100644 --- a/awx/main/tests/job_base.py +++ b/awx/main/tests/job_base.py @@ -229,13 +229,18 @@ class BaseJobTestMixin(BaseTestMixin): self.team_ops_west.users.add(self.user_iris) # The south team is no longer active having been folded into the east team - self.team_ops_south = self.org_ops.teams.create( - name='southerners', - created_by=self.user_sue, - active=False, - ) - self.team_ops_south.projects.add(self.proj_prod) - self.team_ops_south.users.add(self.user_greg) + # FIXME: This code can be removed (probably) + # - this case has been removed as we've gotten rid of the active flag, keeping + # code around in case this has ramifications on some test failures.. if + # you find this message and all tests are passing, then feel free to remove this + # - anoek 2016-03-10 + #self.team_ops_south = self.org_ops.teams.create( + # name='southerners', + # created_by=self.user_sue, + # active=False, + #) + #self.team_ops_south.projects.add(self.proj_prod) + #self.team_ops_south.users.add(self.user_greg) # The north team is going to be deleted self.team_ops_north = self.org_ops.teams.create( @@ -337,11 +342,18 @@ class BaseJobTestMixin(BaseTestMixin): password='Heading270', created_by = self.user_sue, ) - self.cred_ops_south = self.team_ops_south.credentials.create( - username='south', - password='Heading180', - created_by = self.user_sue, - ) + + + # FIXME: This code can be removed (probably) + # - this case has been removed as we've gotten rid of the active flag, keeping + # code around in case this has ramifications on some test failures.. if + # you find this message and all tests are passing, then feel free to remove this + # - anoek 2016-03-10 + #self.cred_ops_south = self.team_ops_south.credentials.create( + # username='south', + # password='Heading180', + # created_by = self.user_sue, + #) self.cred_ops_north = self.team_ops_north.credentials.create( username='north', diff --git a/awx/main/tests/old/ad_hoc.py b/awx/main/tests/old/ad_hoc.py index 52e22faf21..95b8130664 100644 --- a/awx/main/tests/old/ad_hoc.py +++ b/awx/main/tests/old/ad_hoc.py @@ -637,8 +637,8 @@ class AdHocCommandApiTest(BaseAdHocCommandTest): # Verify that the credential and inventory are null when they have # been deleted, can delete an ad hoc command without inventory or # credential. - self.credential.mark_inactive() - self.inventory.mark_inactive() + self.credential.delete() + self.inventory.delete() with self.current_user('admin'): response = self.get(url, expect=200) self.assertEqual(response['credential'], None) @@ -758,7 +758,7 @@ class AdHocCommandApiTest(BaseAdHocCommandTest): tower_settings.AD_HOC_COMMANDS = ad_hoc_commands # Try to relaunch after the inventory has been marked inactive. - self.inventory.mark_inactive() + self.inventory.delete() with self.current_user('admin'): response = self.get(url, expect=200) self.assertEqual(response['passwords_needed_to_start'], []) diff --git a/awx/main/tests/old/commands/age_deleted.py b/awx/main/tests/old/commands/age_deleted.py deleted file mode 100644 index ec5591d28e..0000000000 --- a/awx/main/tests/old/commands/age_deleted.py +++ /dev/null @@ -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) diff --git a/awx/main/tests/old/commands/commands_monolithic.py b/awx/main/tests/old/commands/commands_monolithic.py index c658f6411c..02fad6a199 100644 --- a/awx/main/tests/old/commands/commands_monolithic.py +++ b/awx/main/tests/old/commands/commands_monolithic.py @@ -15,7 +15,6 @@ import unittest2 as unittest # Django from django.conf import settings -from django.contrib.auth.models import User from django.core.management import call_command from django.core.management.base import CommandError from django.utils.timezone import now @@ -232,126 +231,6 @@ class DumpDataTest(BaseCommandMixin, BaseTest): self.assertEqual(result, None) 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, CELERY_EAGER_PROPAGATES_EXCEPTIONS=True, ANSIBLE_TRANSPORT='local') @@ -641,12 +520,12 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): self.assertEqual(inventory_source.inventory_updates.count(), 1) inventory_update = inventory_source.inventory_updates.all()[0] self.assertEqual(inventory_update.status, 'successful') - for host in inventory.hosts.filter(active=True): + for host in inventory.hosts: if host.pk in (except_host_pks or []): continue source_pks = host.inventory_sources.values_list('pk', flat=True) self.assertTrue(inventory_source.pk in source_pks) - for group in inventory.groups.filter(active=True): + for group in inventory.groups: if group.pk in (except_group_pks or []): continue source_pks = group.inventory_sources.values_list('pk', flat=True) @@ -814,7 +693,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): 'lbservers', 'others']) if overwrite: expected_group_names.remove('lbservers') - group_names = set(new_inv.groups.filter(active=True).values_list('name', flat=True)) + group_names = set(new_inv.groups.values_list('name', flat=True)) self.assertEqual(expected_group_names, group_names) expected_host_names = set(['web1.example.com', 'web2.example.com', 'web3.example.com', 'db1.example.com', @@ -824,13 +703,13 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): 'fe80::1610:9fff:fedd:b654', '::1']) if overwrite: expected_host_names.remove('lb.example.com') - host_names = set(new_inv.hosts.filter(active=True).values_list('name', flat=True)) + host_names = set(new_inv.hosts.values_list('name', flat=True)) self.assertEqual(expected_host_names, host_names) expected_inv_vars = {'vara': 'A', 'varc': 'C'} if overwrite_vars: expected_inv_vars.pop('varc') self.assertEqual(new_inv.variables_dict, expected_inv_vars) - for host in new_inv.hosts.filter(active=True): + for host in new_inv.hosts: if host.name == 'web1.example.com': self.assertEqual(host.variables_dict, {'ansible_ssh_host': 'w1.example.net'}) @@ -842,35 +721,35 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): self.assertEqual(host.variables_dict, {'lbvar': 'ni!'}) else: self.assertEqual(host.variables_dict, {}) - for group in new_inv.groups.filter(active=True): + for group in new_inv.groups: if group.name == 'servers': expected_vars = {'varb': 'B', 'vard': 'D'} if overwrite_vars: expected_vars.pop('vard') self.assertEqual(group.variables_dict, expected_vars) - children = set(group.children.filter(active=True).values_list('name', flat=True)) + children = set(group.children.values_list('name', flat=True)) expected_children = set(['dbservers', 'webservers', 'lbservers']) if overwrite: expected_children.remove('lbservers') self.assertEqual(children, expected_children) - self.assertEqual(group.hosts.filter(active=True).count(), 0) + self.assertEqual(group.hosts.count(), 0) elif group.name == 'dbservers': self.assertEqual(group.variables_dict, {'dbvar': 'ugh'}) - self.assertEqual(group.children.filter(active=True).count(), 0) - hosts = set(group.hosts.filter(active=True).values_list('name', flat=True)) + self.assertEqual(group.children.count(), 0) + hosts = set(group.hosts.values_list('name', flat=True)) host_names = set(['db1.example.com','db2.example.com']) self.assertEqual(hosts, host_names) elif group.name == 'webservers': self.assertEqual(group.variables_dict, {'webvar': 'blah'}) - self.assertEqual(group.children.filter(active=True).count(), 0) - hosts = set(group.hosts.filter(active=True).values_list('name', flat=True)) + self.assertEqual(group.children.count(), 0) + hosts = set(group.hosts.values_list('name', flat=True)) host_names = set(['web1.example.com','web2.example.com', 'web3.example.com']) self.assertEqual(hosts, host_names) elif group.name == 'lbservers': self.assertEqual(group.variables_dict, {}) - self.assertEqual(group.children.filter(active=True).count(), 0) - hosts = set(group.hosts.filter(active=True).values_list('name', flat=True)) + self.assertEqual(group.children.count(), 0) + hosts = set(group.hosts.values_list('name', flat=True)) host_names = set(['lb.example.com']) self.assertEqual(hosts, host_names) if overwrite: @@ -920,7 +799,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): # Check hosts in dotcom group. group = new_inv.groups.get(name='dotcom') self.assertEqual(group.hosts.count(), 65) - for host in group.hosts.filter(active=True, name__startswith='web'): + for host in group.hosts.filter( name__startswith='web'): self.assertEqual(host.variables_dict.get('ansible_ssh_user', ''), 'example') # Check hosts in dotnet group. group = new_inv.groups.get(name='dotnet') @@ -928,7 +807,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): # Check hosts in dotorg group. group = new_inv.groups.get(name='dotorg') self.assertEqual(group.hosts.count(), 61) - for host in group.hosts.filter(active=True): + for host in group.hosts: if host.name.startswith('mx.'): continue self.assertEqual(host.variables_dict.get('ansible_ssh_user', ''), 'example') @@ -936,7 +815,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): # Check hosts in dotus group. group = new_inv.groups.get(name='dotus') self.assertEqual(group.hosts.count(), 10) - for host in group.hosts.filter(active=True): + for host in group.hosts: if int(host.name[2:4]) % 2 == 0: self.assertEqual(host.variables_dict.get('even_odd', ''), 'even') else: @@ -1090,7 +969,7 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): else: return 0 - def _check_largeinv_import(self, new_inv, nhosts, nhosts_inactive=0): + def _check_largeinv_import(self, new_inv, nhosts): self._start_time = time.time() inv_file = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'largeinv.py') ngroups = self._get_ngroups_for_nhosts(nhosts) @@ -1103,9 +982,8 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): # Check that inventory is populated as expected within a reasonable # amount of time. Computed fields should also be updated. new_inv = Inventory.objects.get(pk=new_inv.pk) - self.assertEqual(new_inv.hosts.filter(active=True).count(), nhosts) - self.assertEqual(new_inv.groups.filter(active=True).count(), ngroups) - self.assertEqual(new_inv.hosts.filter(active=False).count(), nhosts_inactive) + self.assertEqual(new_inv.hosts.count(), nhosts) + self.assertEqual(new_inv.groups.count(), ngroups) self.assertEqual(new_inv.total_hosts, nhosts) self.assertEqual(new_inv.total_groups, ngroups) self.assertElapsedLessThan(120) @@ -1119,10 +997,10 @@ class InventoryImportTest(BaseCommandMixin, BaseLiveServerTest): self.assertEqual(new_inv.groups.count(), 0) nhosts = 2000 # Test initial import into empty inventory. - self._check_largeinv_import(new_inv, nhosts, 0) + self._check_largeinv_import(new_inv, nhosts) # Test re-importing and overwriting. - self._check_largeinv_import(new_inv, nhosts, 0) + self._check_largeinv_import(new_inv, nhosts) # Test re-importing with only half as many hosts. - self._check_largeinv_import(new_inv, nhosts / 2, nhosts / 2) + self._check_largeinv_import(new_inv, nhosts / 2) # Test re-importing that clears all hosts. - self._check_largeinv_import(new_inv, 0, nhosts) + self._check_largeinv_import(new_inv, 0) diff --git a/awx/main/tests/old/inventory.py b/awx/main/tests/old/inventory.py index a3f3b63794..dda615e79e 100644 --- a/awx/main/tests/old/inventory.py +++ b/awx/main/tests/old/inventory.py @@ -69,7 +69,7 @@ class InventoryTest(BaseTest): def test_get_inventory_list(self): url = reverse('api:inventory_list') - qs = Inventory.objects.filter(active=True).distinct() + qs = Inventory.objects.distinct() # Check list view with invalid authentication. self.check_invalid_auth(url) @@ -226,6 +226,8 @@ class InventoryTest(BaseTest): self.inventory_a.groups.create(name='group-a') self.inventory_b.hosts.create(name='host-b') self.inventory_b.groups.create(name='group-b') + a_pk = self.inventory_a.pk + b_pk = self.inventory_b.pk # Check put to detail view with invalid authentication. self.check_invalid_auth(url_a, methods=('delete',)) @@ -248,24 +250,16 @@ class InventoryTest(BaseTest): self.delete(url_a, expect=204) self.delete(url_b, expect=403) - # Verify that the inventory is marked inactive, along with all its - # hosts and groups. - self.inventory_a = Inventory.objects.get(pk=self.inventory_a.pk) - self.assertFalse(self.inventory_a.active) - self.assertFalse(self.inventory_a.hosts.filter(active=True).count()) - self.assertFalse(self.inventory_a.groups.filter(active=True).count()) + # Verify that the inventory was deleted + assert Inventory.objects.filter(pk=a_pk).count() == 0 # a super user can delete inventory records with self.current_user(self.super_django_user): self.delete(url_a, expect=404) self.delete(url_b, expect=204) - # Verify that the inventory is marked inactive, along with all its - # hosts and groups. - self.inventory_b = Inventory.objects.get(pk=self.inventory_b.pk) - self.assertFalse(self.inventory_b.active) - self.assertFalse(self.inventory_b.hosts.filter(active=True).count()) - self.assertFalse(self.inventory_b.groups.filter(active=True).count()) + # Verify that the inventory was deleted + assert Inventory.objects.filter(pk=b_pk).count() == 0 def test_inventory_access_deleted_permissions(self): temp_org = self.make_organizations(self.super_django_user, 1)[0] @@ -423,7 +417,7 @@ class InventoryTest(BaseTest): del_children_url = reverse('api:group_children_list', args=(del_group.pk,)) nondel_url = reverse('api:group_detail', 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()) self.post(del_children_url, data=nondel_detail, expect=403, auth=self.get_normal_credentials()) @@ -747,13 +741,11 @@ class InventoryTest(BaseTest): # removed group should be automatically marked inactive once it no longer has any parents. removed_group = Group.objects.get(pk=result['id']) self.assertTrue(removed_group.parents.count()) - self.assertTrue(removed_group.active) for parent in removed_group.parents.all(): parent_children_url = reverse('api:group_children_list', args=(parent.pk,)) data = {'id': removed_group.pk, 'disassociate': 1} self.post(parent_children_url, data, expect=204, auth=self.get_super_credentials()) removed_group = Group.objects.get(pk=result['id']) - #self.assertFalse(removed_group.active) # FIXME: Disabled for now because automatically deleting group with no parents is also disabled. # Removing a group from a hierarchy should migrate its children to the # parent. The group itself will be deleted (marked inactive), and all @@ -766,7 +758,6 @@ class InventoryTest(BaseTest): with self.current_user(self.super_django_user): self.post(url, data, expect=204) gx3 = Group.objects.get(pk=gx3.pk) - #self.assertFalse(gx3.active) # FIXME: Disabled for now.... self.assertFalse(gx3 in gx2.children.all()) #self.assertTrue(gx4 in gx2.children.all()) @@ -944,13 +935,10 @@ class InventoryTest(BaseTest): # 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 # 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(h_c 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): # First hierarchy @@ -989,11 +977,9 @@ class InventoryTest(BaseTest): self.assertTrue(other_sub_group in sub_group.children.all()) # Now recursively remove its parent and the reference from subgroup should remain - other_top_group.mark_inactive_recursive() - other_top_group = Group.objects.get(pk=other_top_group.pk) + other_top_group.delete_recursive() self.assertTrue(s2 in sub_group.all_hosts.all()) self.assertTrue(other_sub_group in sub_group.children.all()) - self.assertFalse(other_top_group.active) def test_group_parents_and_children(self): # Test for various levels of group parent/child relations, with hosts, @@ -1173,7 +1159,7 @@ class InventoryTest(BaseTest): # Delete recently added hosts and verify the count drops. hostnames4 = list('defg') for host in Host.objects.filter(name__in=hostnames4): - host.mark_inactive() + host.delete() with self.current_user(self.super_django_user): response = self.get(url) for n, d in enumerate(reversed(response['hosts'])): @@ -1270,7 +1256,7 @@ class InventoryUpdatesTest(BaseTransactionTest): url = reverse('api:inventory_source_hosts_list', args=(inventory_source.pk,)) response = self.get(url, expect=200) self.assertNotEqual(response['count'], 0) - for host in inventory.hosts.filter(active=True): + for host in inventory.hosts: source_pks = host.inventory_sources.values_list('pk', flat=True) self.assertTrue(inventory_source.pk in source_pks) self.assertTrue(host.has_inventory_sources) @@ -1284,12 +1270,12 @@ class InventoryUpdatesTest(BaseTransactionTest): url = reverse('api:host_inventory_sources_list', args=(host.pk,)) response = self.get(url, expect=200) self.assertNotEqual(response['count'], 0) - for group in inventory.groups.filter(active=True): + for group in inventory.groups: source_pks = group.inventory_sources.values_list('pk', flat=True) self.assertTrue(inventory_source.pk in source_pks) self.assertTrue(group.has_inventory_sources) - self.assertTrue(group.children.filter(active=True).exists() or - group.hosts.filter(active=True).exists()) + self.assertTrue(group.children.exists() or + group.hosts.exists()) # Make sure EC2 instance ID groups and RDS groups are excluded. if inventory_source.source == 'ec2' and not instance_id_group_ok: self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I), @@ -1307,7 +1293,7 @@ class InventoryUpdatesTest(BaseTransactionTest): self.assertNotEqual(response['count'], 0) # Try to set a source on a child group that was imported. Should not # be allowed. - for group in inventory_source.group.children.filter(active=True): + for group in inventory_source.group.children: inv_src_2 = group.inventory_source inv_src_url2 = reverse('api:inventory_source_detail', args=(inv_src_2.pk,)) with self.current_user(self.super_django_user): @@ -1663,7 +1649,7 @@ class InventoryUpdatesTest(BaseTransactionTest): inventory_source.overwrite = True inventory_source.save() self.check_inventory_source(inventory_source, initial=False) - for host in self.inventory.hosts.filter(active=True): + for host in self.inventory.hosts: self.assertEqual(host.variables_dict['ec2_instance_type'], instance_type) # Try invalid instance filters that should be ignored: @@ -1797,12 +1783,12 @@ class InventoryUpdatesTest(BaseTransactionTest): inventory_source.save() self.check_inventory_source(inventory_source, initial=False) # Verify that only the desired groups are returned. - child_names = self.group.children.filter(active=True).values_list('name', flat=True) + child_names = self.group.children.values_list('name', flat=True) self.assertTrue('ec2' in child_names) self.assertTrue('regions' in child_names) - self.assertTrue(self.group.children.get(name='regions').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='regions').children.count()) self.assertTrue('types' in child_names) - self.assertTrue(self.group.children.get(name='types').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='types').children.count()) self.assertFalse('keys' in child_names) self.assertFalse('security_groups' in child_names) self.assertFalse('tags' in child_names) @@ -1819,27 +1805,27 @@ class InventoryUpdatesTest(BaseTransactionTest): self.check_inventory_source(inventory_source, initial=False, instance_id_group_ok=True) # Verify that only the desired groups are returned. # Skip vpcs as selected inventory may or may not have any. - child_names = self.group.children.filter(active=True).values_list('name', flat=True) + child_names = self.group.children.values_list('name', flat=True) self.assertTrue('ec2' in child_names) self.assertFalse('tag_none' in child_names) self.assertTrue('regions' in child_names) - self.assertTrue(self.group.children.get(name='regions').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='regions').children.count()) self.assertTrue('types' in child_names) - self.assertTrue(self.group.children.get(name='types').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='types').children.count()) self.assertTrue('keys' in child_names) - self.assertTrue(self.group.children.get(name='keys').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='keys').children.count()) self.assertTrue('security_groups' in child_names) - self.assertTrue(self.group.children.get(name='security_groups').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='security_groups').children.count()) self.assertTrue('tags' in child_names) - self.assertTrue(self.group.children.get(name='tags').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='tags').children.count()) # Only check for tag_none as a child of tags if there is a tag_none group; # the test inventory *may* have tags set for all hosts. if self.inventory.groups.filter(name='tag_none').exists(): self.assertTrue('tag_none' in self.group.children.get(name='tags').children.values_list('name', flat=True)) self.assertTrue('images' in child_names) - self.assertTrue(self.group.children.get(name='images').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='images').children.count()) self.assertTrue('instances' in child_names) - self.assertTrue(self.group.children.get(name='instances').children.filter(active=True).count()) + self.assertTrue(self.group.children.get(name='instances').children.count()) # Sync again with overwrite set to False after renaming a group that # was created by the sync. With overwrite false, the renamed group and # the original group (created again by the sync) will both exist. @@ -1853,7 +1839,7 @@ class InventoryUpdatesTest(BaseTransactionTest): inventory_source.overwrite = False inventory_source.save() self.check_inventory_source(inventory_source, initial=False, instance_id_group_ok=True) - child_names = self.group.children.filter(active=True).values_list('name', flat=True) + child_names = self.group.children.values_list('name', flat=True) self.assertTrue(region_group_original_name in self.group.children.get(name='regions').children.values_list('name', flat=True)) self.assertTrue(region_group.name in self.group.children.get(name='regions').children.values_list('name', flat=True)) # Replacement text should not be left in inventory source name. diff --git a/awx/main/tests/old/jobs/job_launch.py b/awx/main/tests/old/jobs/job_launch.py index 0beb3a9546..c0997607ee 100644 --- a/awx/main/tests/old/jobs/job_launch.py +++ b/awx/main/tests/old/jobs/job_launch.py @@ -96,7 +96,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase): def test_credential_explicit(self): # Explicit, credential 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) j = Job.objects.get(pk=response['job']) self.assertEqual(j.status, 'new') @@ -105,7 +105,7 @@ class JobTemplateLaunchTest(BaseJobTestMixin, django.test.TransactionTestCase): def test_credential_explicit_via_credential_id(self): # Explicit, credential 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) j = Job.objects.get(pk=response['job']) 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 # pass an invalid/inactive credential value). 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, {'credential': 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_id': 'one'}, expect=400) - self.cred_doug.mark_inactive() - self.post(self.launch_url, {'credential': self.cred_doug.pk}, expect=400) - self.post(self.launch_url, {'credential_id': self.cred_doug.pk}, expect=400) + cred_doug_pk = self.cred_doug.pk + self.cred_doug.delete() + 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): # 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): # Job Templates with deleted credentials cannot be launched. - self.cred_sue.mark_inactive() + self.cred_sue.delete() with self.current_user(self.user_sue): 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'] # Job Templates with deleted credentials cannot be launched. 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) for p in passwords_required: self.assertIn(p, response['passwords_needed_to_start']) diff --git a/awx/main/tests/old/organizations.py b/awx/main/tests/old/organizations.py index ef3b3ac77f..c5b1569513 100644 --- a/awx/main/tests/old/organizations.py +++ b/awx/main/tests/old/organizations.py @@ -436,10 +436,8 @@ class OrganizationsTest(BaseTest): self.delete(urls[0], expect=204, auth=self.get_super_credentials()) # check that when we have deleted an object it comes back 404 via GET - # but that it's still in the database as inactive self.get(urls[1], expect=404, auth=self.get_normal_credentials()) - org1 = Organization.objects.get(pk=urldata1['id']) - self.assertEquals(org1.active, False) + assert Organization.objects.filter(pk=urldata1['id']).count() == 0 # also check that DELETE on the collection doesn't work self.delete(self.collection(), expect=405, auth=self.get_super_credentials()) diff --git a/awx/main/tests/old/projects.py b/awx/main/tests/old/projects.py index 9834356860..2c715e312a 100644 --- a/awx/main/tests/old/projects.py +++ b/awx/main/tests/old/projects.py @@ -162,14 +162,14 @@ class ProjectsTest(BaseTransactionTest): set(Project.get_local_path_choices())) # return local paths are only the ones not used by any active project. - qs = Project.objects.filter(active=True) + qs = Project.objects used_paths = qs.values_list('local_path', flat=True) self.assertFalse(set(response['project_local_paths']) & set(used_paths)) for project in self.projects: local_path = project.local_path response = self.get(url, expect=200, auth=self.get_super_credentials()) 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()) self.assertTrue(local_path in response['project_local_paths']) @@ -402,7 +402,7 @@ class ProjectsTest(BaseTransactionTest): # ===================================================================== # TEAM PROJECTS - team = Team.objects.filter(active=True, organization__pk=self.organizations[1].pk)[0] + team = Team.objects.filter( organization__pk=self.organizations[1].pk)[0] team_projects = reverse('api:team_projects_list', args=(team.pk,)) p1 = self.projects[0] @@ -419,7 +419,7 @@ class ProjectsTest(BaseTransactionTest): # ===================================================================== # TEAMS USER MEMBERSHIP - team = Team.objects.filter(active=True, organization__pk=self.organizations[1].pk)[0] + team = Team.objects.filter( organization__pk=self.organizations[1].pk)[0] team_users = reverse('api:team_users_list', args=(team.pk,)) for x in team.deprecated_users.all(): team.deprecated_users.remove(x) @@ -1262,7 +1262,7 @@ class ProjectUpdatesTest(BaseTransactionTest): else: self.check_project_update(project, should_fail=should_still_fail) # Test that we can delete project updates. - for pu in project.project_updates.filter(active=True): + for pu in project.project_updates: pu_url = reverse('api:project_update_detail', args=(pu.pk,)) with self.current_user(self.super_django_user): self.delete(pu_url, expect=204) diff --git a/awx/main/tests/old/scripts.py b/awx/main/tests/old/scripts.py index 1b5295d0bd..7f1d62161a 100644 --- a/awx/main/tests/old/scripts.py +++ b/awx/main/tests/old/scripts.py @@ -88,7 +88,8 @@ class InventoryScriptTest(BaseScriptTest): inventory=inventory, variables=variables) if x in (3, 7): - host.mark_inactive() + host.delete() + continue hosts.append(host) # add localhost just to make sure it's thrown into all (Ansible github bug) @@ -106,7 +107,8 @@ class InventoryScriptTest(BaseScriptTest): inventory=inventory, variables=variables) if x == 2: - group.mark_inactive() + group.delete() + continue groups.append(group) group.hosts.add(hosts[x]) group.hosts.add(hosts[x + 5]) @@ -144,12 +146,11 @@ class InventoryScriptTest(BaseScriptTest): def test_list_with_inventory_id_as_argument(self): inventory = self.inventories[0] - self.assertTrue(inventory.active) rc, stdout, stderr = self.run_inventory_script(list=True, inventory=inventory.pk) self.assertEqual(rc, 0, stderr) data = json.loads(stdout) - groups = inventory.groups.filter(active=True) + groups = inventory.groups groupnames = [ x for x in groups.values_list('name', flat=True)] # it's ok for all to be here because due to an Ansible inventory workaround @@ -165,16 +166,13 @@ class InventoryScriptTest(BaseScriptTest): self.assertTrue(isinstance(v['children'], (list,tuple))) self.assertTrue(isinstance(v['hosts'], (list,tuple))) self.assertTrue(isinstance(v['vars'], (dict))) - group = inventory.groups.get(active=True, name=k) - hosts = group.hosts.filter(active=True) + group = inventory.groups.get(name=k) + hosts = group.hosts hostnames = hosts.values_list('name', flat=True) self.assertEqual(set(v['hosts']), set(hostnames)) else: self.assertTrue(v['hosts'] == ['localhost']) - for group in inventory.groups.filter(active=False): - self.assertFalse(group.name in data.keys(), - 'deleted group %s should not be in data' % group) # Command line argument for inventory ID should take precedence over # environment variable. inventory_pks = set(map(lambda x: x.pk, self.inventories)) @@ -187,12 +185,11 @@ class InventoryScriptTest(BaseScriptTest): def test_list_with_inventory_id_in_environment(self): inventory = self.inventories[1] - self.assertTrue(inventory.active) os.environ['INVENTORY_ID'] = str(inventory.pk) rc, stdout, stderr = self.run_inventory_script(list=True) self.assertEqual(rc, 0, stderr) data = json.loads(stdout) - groups = inventory.groups.filter(active=True) + groups = inventory.groups groupnames = list(groups.values_list('name', flat=True)) + ['all'] self.assertEqual(set(data.keys()), set(groupnames)) # Groups for this inventory should have hosts, variable data, and one @@ -202,14 +199,14 @@ class InventoryScriptTest(BaseScriptTest): if k == 'all': self.assertEqual(v.get('vars', {}), inventory.variables_dict) continue - group = inventory.groups.get(active=True, name=k) - hosts = group.hosts.filter(active=True) + group = inventory.groups.get(name=k) + hosts = group.hosts hostnames = hosts.values_list('name', flat=True) self.assertEqual(set(v.get('hosts', [])), set(hostnames)) if group.variables: self.assertEqual(v.get('vars', {}), group.variables_dict) if k == 'group-3': - children = group.children.filter(active=True) + children = group.children childnames = children.values_list('name', flat=True) self.assertEqual(set(v.get('children', [])), set(childnames)) else: @@ -217,13 +214,12 @@ class InventoryScriptTest(BaseScriptTest): def test_list_with_hostvars_inline(self): inventory = self.inventories[1] - self.assertTrue(inventory.active) rc, stdout, stderr = self.run_inventory_script(list=True, inventory=inventory.pk, hostvars=True) self.assertEqual(rc, 0, stderr) data = json.loads(stdout) - groups = inventory.groups.filter(active=True) + groups = inventory.groups groupnames = list(groups.values_list('name', flat=True)) groupnames.extend(['all', '_meta']) self.assertEqual(set(data.keys()), set(groupnames)) @@ -237,15 +233,15 @@ class InventoryScriptTest(BaseScriptTest): continue if k == '_meta': continue - group = inventory.groups.get(active=True, name=k) - hosts = group.hosts.filter(active=True) + group = inventory.groups.get(name=k) + hosts = group.hosts hostnames = hosts.values_list('name', flat=True) all_hostnames.update(hostnames) self.assertEqual(set(v.get('hosts', [])), set(hostnames)) if group.variables: self.assertEqual(v.get('vars', {}), group.variables_dict) if k == 'group-3': - children = group.children.filter(active=True) + children = group.children childnames = children.values_list('name', flat=True) self.assertEqual(set(v.get('children', [])), set(childnames)) else: @@ -267,8 +263,7 @@ class InventoryScriptTest(BaseScriptTest): def test_valid_host(self): # Host without variable data. inventory = self.inventories[0] - self.assertTrue(inventory.active) - host = inventory.hosts.filter(active=True)[2] + host = inventory.hosts[2] os.environ['INVENTORY_ID'] = str(inventory.pk) rc, stdout, stderr = self.run_inventory_script(host=host.name) self.assertEqual(rc, 0, stderr) @@ -276,8 +271,7 @@ class InventoryScriptTest(BaseScriptTest): self.assertEqual(data, {}) # Host with variable data. inventory = self.inventories[1] - self.assertTrue(inventory.active) - host = inventory.hosts.filter(active=True)[4] + host = inventory.hosts[4] os.environ['INVENTORY_ID'] = str(inventory.pk) rc, stdout, stderr = self.run_inventory_script(host=host.name) self.assertEqual(rc, 0, stderr) @@ -287,8 +281,7 @@ class InventoryScriptTest(BaseScriptTest): def test_invalid_host(self): # Valid host, but not part of the specified inventory. inventory = self.inventories[0] - self.assertTrue(inventory.active) - host = Host.objects.filter(active=True).exclude(inventory=inventory)[0] + host = Host.objects.exclude(inventory=inventory)[0] os.environ['INVENTORY_ID'] = str(inventory.pk) rc, stdout, stderr = self.run_inventory_script(host=host.name) self.assertNotEqual(rc, 0, stderr) @@ -320,16 +313,15 @@ class InventoryScriptTest(BaseScriptTest): def test_with_deleted_inventory(self): inventory = self.inventories[0] - inventory.mark_inactive() - self.assertFalse(inventory.active) - os.environ['INVENTORY_ID'] = str(inventory.pk) + pk = inventory.pk + inventory.delete() + os.environ['INVENTORY_ID'] = str(pk) rc, stdout, stderr = self.run_inventory_script(list=True) self.assertNotEqual(rc, 0, stderr) self.assertEqual(json.loads(stdout), {'failed': True}) def test_without_list_or_host_argument(self): inventory = self.inventories[0] - self.assertTrue(inventory.active) os.environ['INVENTORY_ID'] = str(inventory.pk) rc, stdout, stderr = self.run_inventory_script() self.assertNotEqual(rc, 0, stderr) @@ -337,7 +329,6 @@ class InventoryScriptTest(BaseScriptTest): def test_with_both_list_and_host_arguments(self): inventory = self.inventories[0] - self.assertTrue(inventory.active) os.environ['INVENTORY_ID'] = str(inventory.pk) rc, stdout, stderr = self.run_inventory_script(list=True, host='blah') self.assertNotEqual(rc, 0, stderr) @@ -345,8 +336,7 @@ class InventoryScriptTest(BaseScriptTest): def test_with_disabled_hosts(self): inventory = self.inventories[1] - self.assertTrue(inventory.active) - for host in inventory.hosts.filter(active=True, enabled=True): + for host in inventory.hosts.filter(enabled=True): host.enabled = False host.save(update_fields=['enabled']) os.environ['INVENTORY_ID'] = str(inventory.pk) @@ -354,7 +344,7 @@ class InventoryScriptTest(BaseScriptTest): rc, stdout, stderr = self.run_inventory_script(list=True) self.assertEqual(rc, 0, stderr) data = json.loads(stdout) - groups = inventory.groups.filter(active=True) + groups = inventory.groups groupnames = list(groups.values_list('name', flat=True)) + ['all'] self.assertEqual(set(data.keys()), set(groupnames)) for k,v in data.items(): @@ -362,15 +352,15 @@ class InventoryScriptTest(BaseScriptTest): if k == 'all': self.assertEqual(v.get('vars', {}), inventory.variables_dict) continue - group = inventory.groups.get(active=True, name=k) - hosts = group.hosts.filter(active=True, enabled=True) + group = inventory.groups.get(name=k) + hosts = group.hosts.filter(enabled=True) hostnames = hosts.values_list('name', flat=True) self.assertEqual(set(v.get('hosts', [])), set(hostnames)) self.assertFalse(hostnames) if group.variables: self.assertEqual(v.get('vars', {}), group.variables_dict) if k == 'group-3': - children = group.children.filter(active=True) + children = group.children childnames = children.values_list('name', flat=True) self.assertEqual(set(v.get('children', [])), set(childnames)) else: @@ -379,7 +369,7 @@ class InventoryScriptTest(BaseScriptTest): rc, stdout, stderr = self.run_inventory_script(list=True, all=True) self.assertEqual(rc, 0, stderr) data = json.loads(stdout) - groups = inventory.groups.filter(active=True) + groups = inventory.groups groupnames = list(groups.values_list('name', flat=True)) + ['all'] self.assertEqual(set(data.keys()), set(groupnames)) for k,v in data.items(): @@ -387,15 +377,15 @@ class InventoryScriptTest(BaseScriptTest): if k == 'all': self.assertEqual(v.get('vars', {}), inventory.variables_dict) continue - group = inventory.groups.get(active=True, name=k) - hosts = group.hosts.filter(active=True) + group = inventory.groups.get(name=k) + hosts = group.hosts hostnames = hosts.values_list('name', flat=True) self.assertEqual(set(v.get('hosts', [])), set(hostnames)) self.assertTrue(hostnames) if group.variables: self.assertEqual(v.get('vars', {}), group.variables_dict) if k == 'group-3': - children = group.children.filter(active=True) + children = group.children childnames = children.values_list('name', flat=True) self.assertEqual(set(v.get('children', [])), set(childnames)) else: diff --git a/awx/main/tests/old/tasks.py b/awx/main/tests/old/tasks.py index a57202a958..30f58353c2 100644 --- a/awx/main/tests/old/tasks.py +++ b/awx/main/tests/old/tasks.py @@ -592,26 +592,8 @@ class RunJobTest(BaseJobExecutionTest): new_group.children.remove(self.group) new_group = Group.objects.get(pk=new_group.pk) self.assertFalse(new_group.has_active_failures) - # Mark host inactive (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) + # Delete host (should clear flag on parent group and inventory) self.host.delete() - self.host = None 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) @@ -619,30 +601,7 @@ class RunJobTest(BaseJobExecutionTest): def test_update_has_active_failures_when_job_removed(self): job = self.test_run_job_that_fails() - # Mark job as inactive (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. + # Delete (should clear flags). job.delete() self.host = Host.objects.get(pk=self.host.pk) self.assertFalse(self.host.has_active_failures) @@ -662,8 +621,8 @@ class RunJobTest(BaseJobExecutionTest): self.host = Host.objects.get(pk=self.host.pk) self.assertEqual(self.host.last_job, job1) self.assertEqual(self.host.last_job_host_summary.job, job1) - # Mark job1 inactive (should update host.last_job to None). - job1.mark_inactive() + # Delete job1 (should update host.last_job to None). + job1.delete() self.host = Host.objects.get(pk=self.host.pk) self.assertEqual(self.host.last_job, None) self.assertEqual(self.host.last_job_host_summary, None) diff --git a/awx/main/tests/old/users.py b/awx/main/tests/old/users.py index 2fa89402f9..75377037da 100644 --- a/awx/main/tests/old/users.py +++ b/awx/main/tests/old/users.py @@ -196,7 +196,7 @@ class UsersTest(BaseTest): 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()) # 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') self.post(url, expect=403, data=new_user3, auth=self.get_normal_credentials()) @@ -316,7 +316,7 @@ class UsersTest(BaseTest): remote_addr=remote_addr) # 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, remote_addr=remote_addr) 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 # admins is marked inactive, nor can he see any other users that were # 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()) self.assertEquals(data3['count'], 1) diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 76381e5ac0..f663bab11b 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -209,7 +209,6 @@ REST_FRAMEWORK = { 'awx.api.permissions.ModelAccessPermission', ), 'DEFAULT_FILTER_BACKENDS': ( - 'awx.api.filters.ActiveOnlyBackend', 'awx.api.filters.TypeFilterBackend', 'awx.api.filters.FieldLookupBackend', 'rest_framework.filters.SearchFilter', diff --git a/awx/sso/pipeline.py b/awx/sso/pipeline.py index 7000d050ee..f3e8987d17 100644 --- a/awx/sso/pipeline.py +++ b/awx/sso/pipeline.py @@ -90,7 +90,7 @@ def update_user_orgs(backend, details, user=None, *args, **kwargs): org = Organization.objects.get_or_create(name=org_name)[0] else: try: - org = Organization.objects.filter(active=True).order_by('pk')[0] + org = Organization.objects.order_by('pk')[0] except IndexError: continue @@ -126,7 +126,7 @@ def update_user_teams(backend, details, user=None, *args, **kwargs): org = Organization.objects.get_or_create(name=team_opts['organization'])[0] else: try: - org = Organization.objects.filter(active=True).order_by('pk')[0] + org = Organization.objects.order_by('pk')[0] except IndexError: continue