<commit_before>from __future__ import unicode_literals

from django.db.models import Q
from django.contrib.contenttypes import models as ct_models
import django_filters
from django_fsm import TransitionNotAllowed

from rest_framework import permissions as rf_permissions, status, viewsets, mixins
from rest_framework.response import Response
from rest_framework.decorators import detail_route
from rest_framework.exceptions import PermissionDenied
from nodeconductor.backup.models import Backup
from nodeconductor.core.filters import DjangoMappingFilterBackend

from nodeconductor.core.permissions import has_user_permission_for_instance
from nodeconductor.backup import models, serializers, utils
from nodeconductor.backup.log import event_logger, extract_event_context
from nodeconductor.structure import filters as structure_filters


class BackupPermissionFilter():

    def _get_user_visible_model_instances_ids(self, user, model):
        queryset = structure_filters.filter_queryset_for_user(model.objects.all(), user)
        return queryset.values_list('pk', flat=True)

    def filter_queryset(self, request, queryset, view):
        """
        Filter backups with source to which user has view access
        """
        q_query = Q()
        for strategy in utils.get_backup_strategies().values():
            model = strategy.get_model()
            model_content_type = ct_models.ContentType.objects.get_for_model(model)
            instances_ids = self._get_user_visible_model_instances_ids(request.user, model)
            q_query |= (Q(content_type=model_content_type) & Q(object_id__in=instances_ids))
        return queryset.filter(q_query)


class BackupScheduleViewSet(viewsets.ModelViewSet):
    queryset = models.BackupSchedule.objects.all()
    serializer_class = serializers.BackupScheduleSerializer
    lookup_field = 'uuid'
    filter_backends = (BackupPermissionFilter,)
    permission_classes = (rf_permissions.IsAuthenticated,)

    def perform_create(self, serializer):
        if not has_user_permission_for_instance(self.request.user, serializer.validated_data['backup_source']):
            raise PermissionDenied('You do not have permission to perform this action.')
        super(BackupScheduleViewSet, self).perform_create(serializer)

    def perform_update(self, serializer):
        backup_source = self.get_object().backup_source
        if not has_user_permission_for_instance(self.request.user, backup_source):
            raise PermissionDenied('You do not have permission to perform this action.')
        super(BackupScheduleViewSet, self).perform_update(serializer)

    def perform_destroy(self, instance):
        if not has_user_permission_for_instance(self.request.user, instance):
            raise PermissionDenied('You do not have permission to perform this action.')
        super(BackupScheduleViewSet, self).perform_destroy(instance)

    def _get_backup_schedule(self, user):
        schedule = self.get_object()
        if not has_user_permission_for_instance(user, schedule.backup_source):
            raise PermissionDenied('You do not have permission to perform this action.')
        return schedule

    @detail_route(methods=['post'])
    def activate(self, request, uuid):
        schedule = self._get_backup_schedule(request.user)
        if schedule.is_active:
            return Response({'status': 'BackupSchedule is already activated'}, status=status.HTTP_409_CONFLICT)
        schedule.is_active = True
        schedule.save()

        event_logger.backup_schedule.info(
            'Backup schedule for {iaas_instance_name} has been activated.',
            event_type='iaas_backup_schedule_activated',
            event_context=extract_event_context(schedule))

        return Response({'status': 'BackupSchedule was activated'})

    @detail_route(methods=['post'])
    def deactivate(self, request, uuid):
        schedule = self._get_backup_schedule(request.user)
        if not schedule.is_active:
            return Response({'status': 'BackupSchedule is already deactivated'}, status=status.HTTP_409_CONFLICT)
        schedule.is_active = False
        schedule.save()

        event_logger.backup_schedule.info(
            'Backup schedule for {iaas_instance_name} has been deactivated.',
            event_type='iaas_backup_schedule_deactivated',
            event_context=extract_event_context(schedule))

        return Response({'status': 'BackupSchedule was deactivated'})


class BackupFilter(django_filters.FilterSet):
    description = django_filters.CharFilter(
        lookup_type='icontains',
    )

    class Meta(object):
        model = models.Backup
        fields = (
            'description',
        )


class BackupViewSet(mixins.CreateModelMixin,
                    mixins.RetrieveModelMixin,
                    mixins.ListModelMixin,
                    viewsets.GenericViewSet):
    queryset = models.Backup.objects.all()
    serializer_class = serializers.BackupSerializer
    lookup_field = 'uuid'
    filter_backends = (BackupPermissionFilter, DjangoMappingFilterBackend)
    filter_class = BackupFilter
    permission_classes = (rf_permissions.IsAuthenticated,)

    def perform_create(self, serializer):
        if not has_user_permission_for_instance(self.request.user, serializer.validated_data['backup_source']):
            raise PermissionDenied('You do not have permission to perform this action.')
        backup = serializer.save()
        backup.start_backup()

    def _get_backup(self, user, uuid):
        backup = self.get_object()
        if not has_user_permission_for_instance(user, backup.backup_source):
            raise PermissionDenied('You do not have permission to perform this action.')
        return backup

    @detail_route(methods=['post'])
    def restore(self, request, uuid):
        backup = self._get_backup(request.user, uuid)
        if backup.state != Backup.States.READY:
            return Response('Cannot restore a backup in state \'%s\'' % backup.get_state_display(),
                            status=status.HTTP_409_CONFLICT)
        # fail early if inputs are incorrect during the call time
        instance, user_input, snapshot_ids, errors = backup.get_strategy().\
            deserialize_instance(backup.metadata, request.data)
        if not errors:
            try:
                backup.start_restoration(instance.uuid, user_input=user_input, snapshot_ids=snapshot_ids)
            except TransitionNotAllowed:
                # this should never be hit as the check is done on function entry
                return Response('Cannot restore a backup in state \'%s\'' % backup.get_state_display(),
                                status=status.HTTP_409_CONFLICT)
            return Response({'status': 'Backup restoration process was started'})

        return Response(errors, status=status.HTTP_400_BAD_REQUEST)

    @detail_route(methods=['post'])
    def delete(self, request, uuid):
        backup = self._get_backup(request.user, uuid)
        try:
            backup.start_deletion()
        except TransitionNotAllowed:
            return Response('Cannot delete a backup in state \'%s\'' % backup.get_state_display(),
                            status=status.HTTP_409_CONFLICT)

        return Response({'status': 'Backup deletion was started'})
<commit_msg>Expose filtering by description of backup schedule<commit_after>from __future__ import unicode_literals

from django.db.models import Q
from django.contrib.contenttypes import models as ct_models
import django_filters
from django_fsm import TransitionNotAllowed

from rest_framework import permissions as rf_permissions, status, viewsets, mixins
from rest_framework.response import Response
from rest_framework.decorators import detail_route
from rest_framework.exceptions import PermissionDenied
from nodeconductor.backup.models import Backup
from nodeconductor.core.filters import DjangoMappingFilterBackend

from nodeconductor.core.permissions import has_user_permission_for_instance
from nodeconductor.backup import models, serializers, utils
from nodeconductor.backup.log import event_logger, extract_event_context
from nodeconductor.structure import filters as structure_filters


class BackupPermissionFilter():

    def _get_user_visible_model_instances_ids(self, user, model):
        queryset = structure_filters.filter_queryset_for_user(model.objects.all(), user)
        return queryset.values_list('pk', flat=True)

    def filter_queryset(self, request, queryset, view):
        """
        Filter backups with source to which user has view access
        """
        q_query = Q()
        for strategy in utils.get_backup_strategies().values():
            model = strategy.get_model()
            model_content_type = ct_models.ContentType.objects.get_for_model(model)
            instances_ids = self._get_user_visible_model_instances_ids(request.user, model)
            q_query |= (Q(content_type=model_content_type) & Q(object_id__in=instances_ids))
        return queryset.filter(q_query)


class BackupScheduleFilter(django_filters.FilterSet):
    description = django_filters.CharFilter(
        lookup_type='icontains',
    )

    class Meta(object):
        model = models.BackupSchedule
        fields = (
            'description',
        )


class BackupScheduleViewSet(viewsets.ModelViewSet):
    queryset = models.BackupSchedule.objects.all()
    serializer_class = serializers.BackupScheduleSerializer
    lookup_field = 'uuid'
    filter_backends = (BackupPermissionFilter, DjangoMappingFilterBackend)
    filter_class = BackupScheduleFilter
    permission_classes = (rf_permissions.IsAuthenticated,)

    def perform_create(self, serializer):
        if not has_user_permission_for_instance(self.request.user, serializer.validated_data['backup_source']):
            raise PermissionDenied('You do not have permission to perform this action.')
        super(BackupScheduleViewSet, self).perform_create(serializer)

    def perform_update(self, serializer):
        backup_source = self.get_object().backup_source
        if not has_user_permission_for_instance(self.request.user, backup_source):
            raise PermissionDenied('You do not have permission to perform this action.')
        super(BackupScheduleViewSet, self).perform_update(serializer)

    def perform_destroy(self, instance):
        if not has_user_permission_for_instance(self.request.user, instance):
            raise PermissionDenied('You do not have permission to perform this action.')
        super(BackupScheduleViewSet, self).perform_destroy(instance)

    def _get_backup_schedule(self, user):
        schedule = self.get_object()
        if not has_user_permission_for_instance(user, schedule.backup_source):
            raise PermissionDenied('You do not have permission to perform this action.')
        return schedule

    @detail_route(methods=['post'])
    def activate(self, request, uuid):
        schedule = self._get_backup_schedule(request.user)
        if schedule.is_active:
            return Response({'status': 'BackupSchedule is already activated'}, status=status.HTTP_409_CONFLICT)
        schedule.is_active = True
        schedule.save()

        event_logger.backup_schedule.info(
            'Backup schedule for {iaas_instance_name} has been activated.',
            event_type='iaas_backup_schedule_activated',
            event_context=extract_event_context(schedule))

        return Response({'status': 'BackupSchedule was activated'})

    @detail_route(methods=['post'])
    def deactivate(self, request, uuid):
        schedule = self._get_backup_schedule(request.user)
        if not schedule.is_active:
            return Response({'status': 'BackupSchedule is already deactivated'}, status=status.HTTP_409_CONFLICT)
        schedule.is_active = False
        schedule.save()

        event_logger.backup_schedule.info(
            'Backup schedule for {iaas_instance_name} has been deactivated.',
            event_type='iaas_backup_schedule_deactivated',
            event_context=extract_event_context(schedule))

        return Response({'status': 'BackupSchedule was deactivated'})


class BackupFilter(django_filters.FilterSet):
    description = django_filters.CharFilter(
        lookup_type='icontains',
    )

    class Meta(object):
        model = models.Backup
        fields = (
            'description',
        )


class BackupViewSet(mixins.CreateModelMixin,
                    mixins.RetrieveModelMixin,
                    mixins.ListModelMixin,
                    viewsets.GenericViewSet):
    queryset = models.Backup.objects.all()
    serializer_class = serializers.BackupSerializer
    lookup_field = 'uuid'
    filter_backends = (BackupPermissionFilter, DjangoMappingFilterBackend)
    filter_class = BackupFilter
    permission_classes = (rf_permissions.IsAuthenticated,)

    def perform_create(self, serializer):
        if not has_user_permission_for_instance(self.request.user, serializer.validated_data['backup_source']):
            raise PermissionDenied('You do not have permission to perform this action.')
        backup = serializer.save()
        backup.start_backup()

    def _get_backup(self, user, uuid):
        backup = self.get_object()
        if not has_user_permission_for_instance(user, backup.backup_source):
            raise PermissionDenied('You do not have permission to perform this action.')
        return backup

    @detail_route(methods=['post'])
    def restore(self, request, uuid):
        backup = self._get_backup(request.user, uuid)
        if backup.state != Backup.States.READY:
            return Response('Cannot restore a backup in state \'%s\'' % backup.get_state_display(),
                            status=status.HTTP_409_CONFLICT)
        # fail early if inputs are incorrect during the call time
        instance, user_input, snapshot_ids, errors = backup.get_strategy().\
            deserialize_instance(backup.metadata, request.data)
        if not errors:
            try:
                backup.start_restoration(instance.uuid, user_input=user_input, snapshot_ids=snapshot_ids)
            except TransitionNotAllowed:
                # this should never be hit as the check is done on function entry
                return Response('Cannot restore a backup in state \'%s\'' % backup.get_state_display(),
                                status=status.HTTP_409_CONFLICT)
            return Response({'status': 'Backup restoration process was started'})

        return Response(errors, status=status.HTTP_400_BAD_REQUEST)

    @detail_route(methods=['post'])
    def delete(self, request, uuid):
        backup = self._get_backup(request.user, uuid)
        try:
            backup.start_deletion()
        except TransitionNotAllowed:
            return Response('Cannot delete a backup in state \'%s\'' % backup.get_state_display(),
                            status=status.HTTP_409_CONFLICT)

        return Response({'status': 'Backup deletion was started'})

{'fraction_non_alphanumeric': 0.08010112434302441, 'fraction_numerical': 0.002395050229525647, 'mean_word_length': 3.359628770301624, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '791058', 'n_tokens_mistral': 4037, 'n_tokens_neox': 3942, 'n_words': 930}