Пример #1
0
    def patch(self, request, error_id):
        """Edits an existing error and returns the updated model

        :param request: the HTTP PATCH request
        :type request: :class:`rest_framework.request.Request`
        :param error_id: The id of the error
        :type error_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """
        try:
            error = Error.objects.get(pk=error_id)
        except Error.DoesNotExist:
            raise Http404

        # Do not allow editing of SYSTEM level errors
        if error.category == 'SYSTEM':
            raise BadParameter('System level errors cannot be edited.')

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request, 'description', required=False)
        category = rest_util.parse_string(request, 'category', required=False,
                                          accepted_values=[c for c, _ in Error.CATEGORIES])

        # Do not allow editing of SYSTEM level errors
        if category == 'SYSTEM':
            raise BadParameter('Errors cannot be changed to system level.')

        error.title = title or error.title
        error.description = description or error.description
        error.category = category or error.category
        error.save()

        serializer = self.get_serializer(error)
        return Response(serializer.data)
Пример #2
0
    def _patch_v6(self, request, workspace_id):
        """Edits an existing workspace and returns the updated details

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param workspace_id: The ID for the workspace.
        :type workspace_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request, 'description', required=False)
        json = rest_util.parse_dict(request, 'configuration', required=False)
        base_url = rest_util.parse_string(request, 'base_url', required=False)
        is_active = rest_util.parse_string(request, 'is_active', required=False)

        configuration = None
        if json:
            try:
                configuration = WorkspaceConfigurationV6(json, do_validate=True).get_configuration()
            except InvalidWorkspaceConfiguration as ex:
                message = 'Workspace configuration invalid'
                logger.exception(message)
                raise BadParameter('%s: %s' % (message, unicode(ex)))

        try:
            Workspace.objects.edit_workspace(workspace_id, title, description, configuration, base_url, is_active)
        except Workspace.DoesNotExist:
            raise Http404
        except InvalidWorkspaceConfiguration as ex:
            logger.exception('Unable to edit workspace: %s', workspace_id)
            raise BadParameter(unicode(ex))

        return HttpResponse(status=204)
Пример #3
0
    def create_impl_v6(self, request):
        """Creates a new Strike process and returns a link to the detail URL

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=True)
        name = title_to_name(self.queryset, title)
        description = rest_util.parse_string(request, 'description', required=False)
        configuration = rest_util.parse_dict(request, 'configuration')

        config = None
        try:
            if configuration:
                config = StrikeConfigurationV6(configuration, do_validate=True).get_configuration()
        except InvalidStrikeConfiguration as ex:
            raise BadParameter('Strike configuration invalid: %s' % unicode(ex))

        try:
            strike = Strike.objects.create_strike(name, title, description, config)
        except InvalidStrikeConfiguration as ex:
            raise BadParameter('Strike configuration invalid: %s' % unicode(ex))

        # Fetch the full strike process with details
        try:
            strike = Strike.objects.get_details(strike.id)
        except Strike.DoesNotExist:
            raise Http404

        serializer = StrikeDetailsSerializerV6(strike)
        strike_url = reverse('strike_details_view', args=[strike.id], request=request)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=dict(location=strike_url))
Пример #4
0
    def _create_v6(self, request):
        """Creates a new Scan process and returns a link to the detail URL

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        name = title_to_name(self.queryset, title)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        configuration = rest_util.parse_dict(request, 'configuration')

        config = None
        try:
            config = ScanConfigurationV6(configuration,
                                         do_validate=True).get_configuration()
        except InvalidScanConfiguration as ex:
            raise BadParameter('Scan configuration invalid: %s' % unicode(ex))

        try:
            scan = Scan.objects.create_scan(name, title, description, config)
        except InvalidScanConfiguration as ex:
            raise BadParameter('Scan configuration invalid: %s' % unicode(ex))

        serializer = ScanDetailsSerializerV6(scan)
        scan_url = reverse('scans_details_view',
                           args=[scan.id],
                           request=request)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=scan_url))
Пример #5
0
    def queue_bake_jobs(self, request):
        """Creates and queues the specified number of Scale Bake jobs

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        num = rest_util.parse_int(request, 'num')

        if num < 1:
            raise BadParameter('num must be at least 1')

        # TODO: in the future, send command message to do this asynchronously
        try:
            recipe_type = RecipeType.objects.get(name='scale-bake',
                                                 revision_num='1')
            for _ in xrange(num):
                Queue.objects.queue_new_recipe_for_user_v6(recipe_type, Data())
        except (InvalidData, InvalidRecipeData, InactiveRecipeType) as ex:
            message = 'Unable to create new recipe'
            logger.exception(message)
            raise BadParameter('%s: %s' % (message, unicode(ex)))

        return Response(status=status.HTTP_202_ACCEPTED)
Пример #6
0
    def _patch_v6(self, request, scan_id):
        """Edits an existing Scan process and returns the updated details

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param scan_id: The ID of the Scan process
        :type scan_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request, 'description', required=False)
        configuration = rest_util.parse_dict(request, 'configuration', required=False)

        config = None
        try:
            if configuration:
                config = ScanConfigurationV6(configuration, do_validate=True).get_configuration()
        except InvalidScanConfiguration as ex:
            raise BadParameter('Scan configuration invalid: %s' % unicode(ex))

        try:
            Scan.objects.edit_scan(scan_id, title, description, config)
        except Scan.DoesNotExist:
            raise Http404
        except InvalidScanConfiguration as ex:
            logger.exception('Unable to edit Scan process: %s', scan_id)
            raise BadParameter(unicode(ex))

        return Response(status=status.HTTP_204_NO_CONTENT)
Пример #7
0
    def create(self, request):
        """Creates a new recipe type and returns a link to the detail URL

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """
        name = rest_util.parse_string(request, 'name')
        version = rest_util.parse_string(request, 'version')
        title = rest_util.parse_string(request, 'title', default_value=name)
        description = rest_util.parse_string(request, 'description', required=False)
        definition_dict = rest_util.parse_dict(request, 'definition')

        # Check for optional trigger rule parameters
        trigger_rule_dict = rest_util.parse_dict(request, 'trigger_rule', required=False)
        if (('type' in trigger_rule_dict and 'configuration' not in trigger_rule_dict) or
                ('type' not in trigger_rule_dict and 'configuration' in trigger_rule_dict)):
            raise BadParameter('Trigger type and configuration are required together.')
        is_active = trigger_rule_dict['is_active'] if 'is_active' in trigger_rule_dict else True

        # Attempt to look up the trigger handler for the type
        rule_handler = None
        if trigger_rule_dict and 'type' in trigger_rule_dict:
            try:
                rule_handler = trigger_handler.get_trigger_rule_handler(trigger_rule_dict['type'])
            except InvalidTriggerType as ex:
                logger.exception('Invalid trigger type for new recipe type: %s', name)
                raise BadParameter(unicode(ex))

        try:
            with transaction.atomic():
                # Validate the recipe definition
                logger.info(definition_dict)
                recipe_def = RecipeDefinitionSunset.create(definition_dict)

                # Attempt to create the trigger rule
                trigger_rule = None
                if rule_handler and 'configuration' in trigger_rule_dict:
                    trigger_rule = rule_handler.create_trigger_rule(trigger_rule_dict['configuration'], name, is_active)

                # Create the recipe type
                recipe_type = RecipeType.objects.create_recipe_type(name, version, title, description, recipe_def,
                                                                    trigger_rule)
        except (InvalidDefinition, InvalidTriggerType, InvalidTriggerRule, InvalidRecipeConnection) as ex:
            logger.exception('Unable to create new recipe type: %s', name)
            raise BadParameter(unicode(ex))

        # Fetch the full recipe type with details
        try:
            recipe_type = RecipeType.objects.get_details_v5(recipe_type.id)
        except RecipeType.DoesNotExist:
            raise Http404

        url = reverse('recipe_type_details_view', args=[recipe_type.id], request=request)
        if self.request.version == 'v6':
            serializer = RecipeTypeDetailsSerializerV6(recipe_type)
        else:
            serializer = RecipeTypeDetailsSerializerV5(recipe_type)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=dict(location=url))
Пример #8
0
    def post(self, request):
        """Queue a recipe and returns the new job information in JSON form

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """
        if request.version != 'v6':
            raise Http404

        recipe_type_id = rest_util.parse_int(request, 'recipe_type_id')
        recipe_data = rest_util.parse_dict(request, 'input', {})
        configuration_dict = rest_util.parse_dict(request,
                                                  'configuration',
                                                  required=False)
        configuration = None

        try:
            recipeData = DataV6(recipe_data, do_validate=True)
        except InvalidData as ex:
            logger.exception('Unable to queue new recipe. Invalid input: %s',
                             recipe_data)
            raise BadParameter(unicode(ex))

        try:
            recipe_type = RecipeType.objects.get(pk=recipe_type_id)
        except RecipeType.DoesNotExist:
            raise Http404

        if configuration_dict:
            try:
                configuration = RecipeConfigurationV6(
                    configuration_dict, do_validate=True).get_configuration()
            except InvalidRecipeConfiguration as ex:
                message = 'Recipe configuration invalid'
                logger.exception(message)
                raise BadParameter('%s: %s' % (message, unicode(ex)))

        try:
            recipe = Queue.objects.queue_new_recipe_for_user_v6(
                recipe_type,
                recipeData.get_data(),
                recipe_config=configuration)
        except (InvalidData, InvalidRecipeData) as err:
            return Response('Invalid recipe data: ' + unicode(err),
                            status=status.HTTP_400_BAD_REQUEST)
        except InactiveRecipeType as err:
            return Response('Inactive recipe type: ' + unicode(err),
                            status=status.HTTP_400_BAD_REQUEST)

        serializer = RecipeSerializerV6(recipe)
        recipe_url = reverse('recipe_details_view',
                             args=[recipe.id],
                             request=request)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=recipe_url))
Пример #9
0
    def _create_v6(self, request):
        """The v6 version for creating batches

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        recipe_type_id = rest_util.parse_int(request, 'recipe_type_id')
        definition_dict = rest_util.parse_dict(request, 'definition')
        configuration_dict = rest_util.parse_dict(request,
                                                  'configuration',
                                                  required=False)

        # Make sure the recipe type exists
        try:
            recipe_type = RecipeType.objects.get(pk=recipe_type_id)
        except RecipeType.DoesNotExist:
            raise BadParameter('Unknown recipe type: %d' % recipe_type_id)

        # Validate and create the batch
        try:
            definition = BatchDefinitionV6(definition=definition_dict,
                                           do_validate=True).get_definition()
            configuration = BatchConfigurationV6(
                configuration=configuration_dict,
                do_validate=True).get_configuration()
            with transaction.atomic():
                event = TriggerEvent.objects.create_trigger_event(
                    'USER', None, {'user': '******'}, now())
                batch = Batch.objects.create_batch_v6(
                    title,
                    description,
                    recipe_type,
                    event,
                    definition,
                    configuration=configuration)
                CommandMessageManager().send_messages(
                    [create_batch_recipes_message(batch.id)])
        except InvalidDefinition as ex:
            raise BadParameter(unicode(ex))
        except InvalidConfiguration as ex:
            raise BadParameter(unicode(ex))

        # Fetch the full batch with details
        batch = Batch.objects.get_details_v6(batch.id)

        url = reverse('batch_details_view', args=[batch.id], request=request)
        serializer = BatchDetailsSerializerV6(batch)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers={'Location': url})
Пример #10
0
    def _post_v6(self, request):
        """The v6 version for validating a new batch

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        recipe_type_id = rest_util.parse_int(request, 'recipe_type_id')
        definition_dict = rest_util.parse_dict(request, 'definition')
        configuration_dict = rest_util.parse_dict(request,
                                                  'configuration',
                                                  required=False)

        # Make sure the recipe type exists
        try:
            recipe_type = RecipeType.objects.get(pk=recipe_type_id)
        except RecipeType.DoesNotExist:
            raise BadParameter('Unknown recipe type: %d' % recipe_type_id)

        try:
            definition = BatchDefinitionV6(definition=definition_dict,
                                           do_validate=True).get_definition()
            configuration = BatchConfigurationV6(
                configuration=configuration_dict,
                do_validate=True).get_configuration()
        except InvalidDefinition as ex:
            raise BadParameter(unicode(ex))
        except InvalidConfiguration as ex:
            raise BadParameter(unicode(ex))

        # Validate the batch
        validation = Batch.objects.validate_batch_v6(
            recipe_type, definition, configuration=configuration)
        batch = validation.batch
        recipe_type_serializer = RecipeTypeBaseSerializerV6(batch.recipe_type)
        resp_dict = {
            'is_valid': validation.is_valid,
            'errors': [e.to_dict() for e in validation.errors],
            'warnings': [w.to_dict() for w in validation.warnings],
            'recipes_estimated': definition.estimated_recipes,
            'recipe_type': recipe_type_serializer.data
        }
        if batch.superseded_batch:
            recipe_type_rev_serializer = RecipeTypeRevisionBaseSerializerV6(
                batch.superseded_batch.recipe_type_rev)
            prev_batch_dict = {
                'recipe_type_rev': recipe_type_rev_serializer.data
            }
            resp_dict['prev_batch'] = prev_batch_dict
            if definition.prev_batch_diff:
                diff_v6 = convert_recipe_diff_to_v6_json(
                    definition.prev_batch_diff)
                diff_dict = rest_util.strip_schema_version(diff_v6.get_dict())
                prev_batch_dict['diff'] = diff_dict
        return Response(resp_dict)
Пример #11
0
    def _create_v6(self, request):
        """Creates a new recipe type and returns a link to the detail URL

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=True)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        definition_dict = rest_util.parse_dict(request,
                                               'definition',
                                               required=True)

        basename = title_to_basename(title)
        existing_recipes = RecipeType.objects.filter(name=basename)
        if existing_recipes.count() > 0:
            logger.exception(
                'Existing recipe types found for %s - will not re-create.',
                basename)
            raise BadParameter(
                unicode(
                    'Existing recipe types found for %s - will not re-create. Please change the title or patch the existing recipe type.'
                    % basename))

        name = title_to_name(self.queryset, title)
        try:
            with transaction.atomic():
                # Validate the recipe definition
                recipe_def = RecipeDefinitionV6(
                    definition=definition_dict,
                    do_validate=True).get_definition()

                # Create the recipe type
                recipe_type = RecipeType.objects.create_recipe_type_v6(
                    name, title, description, recipe_def)
        except InvalidDefinition as ex:
            logger.exception('Unable to create new recipe type: %s', name)
            raise BadParameter(unicode(ex))

        # Fetch the full recipe type with details
        try:
            recipe_type = RecipeType.objects.get_details_v6(recipe_type.name)
        except RecipeType.DoesNotExist:
            raise Http404

        url = reverse('recipe_type_details_view',
                      args=[recipe_type.name],
                      request=request)
        serializer = RecipeTypeDetailsSerializerV6(recipe_type)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=url))
Пример #12
0
    def _post_v6(self, request, recipe_id):
        """Schedules a recipe for reprocessing and returns it in JSON form

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param recipe_id: The id of the recipe
        :type recipe_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        forced_nodes_json = rest_util.parse_dict(request, 'forced_nodes', required=True)
        revision_num = rest_util.parse_dict(request, 'revision_num', required=False)

        try:
            forced_nodes = ForcedNodesV6(forced_nodes_json, do_validate=True)
        except InvalidDiff as ex:
            logger.exception('Unable to reprocess recipe. Invalid input: %s', forced_nodes_json)
            raise BadParameter(unicode(ex))

        try:
            recipe = Recipe.objects.select_related('recipe_type').get(id=recipe_id)
            if revision_num:
                recipe.recipe_type_rev = RecipeTypeRevision.objects.get_revision(recipe.recipe_type.name, revision_num)                
            else:
                revision_num = recipe.recipe_type.revision_num
                recipe.recipe_type_rev = RecipeTypeRevision.objects.get_revision(recipe.recipe_type.name, recipe.recipe_type.revision_num)
        except Recipe.DoesNotExist:
            raise Http404
        except RecipeTypeRevision.DoesNotExist:
            raise Http404
        if recipe.is_superseded:
            raise BadParameter('Cannot reprocess a superseded recipe')

        validation = recipe.recipe_type_rev.validate_forced_nodes(forced_nodes_json)
        if not validation.is_valid:
            raise BadParameter('Unable to reprocess recipe. Errors in validating forced_nodes: %s' % validation.errors)

        if validation.warnings:
            logger.warning('Warnings encountered when reprocessing: %s' % validation.warnings)

        event = TriggerEvent.objects.create_trigger_event('USER', None, {'user': '******'}, now())
        root_recipe_id = recipe.root_superseded_recipe_id if recipe.root_superseded_recipe_id else recipe.id
        recipe_type_name = recipe.recipe_type.name
        
        # Execute all of the messages to perform the reprocess
        messages = create_reprocess_messages([root_recipe_id], recipe_type_name, revision_num, event.id,
                                             forced_nodes=forced_nodes.get_forced_nodes())

        CommandMessageManager().send_messages(messages)

        return Response(status=status.HTTP_202_ACCEPTED)
Пример #13
0
    def _create_v6(self, request):
        """Creates a new Workspace and returns it in JSON form

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=True)
        name = title_to_name(self.queryset, title)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        json = rest_util.parse_dict(request, 'configuration')
        base_url = rest_util.parse_string(request, 'base_url', required=False)
        is_active = rest_util.parse_bool(request,
                                         'is_active',
                                         default_value=True,
                                         required=False)

        configuration = None
        if json:
            try:
                configuration = WorkspaceConfigurationV6(
                    json, do_validate=True).get_configuration()
            except InvalidWorkspaceConfiguration as ex:
                message = 'Workspace configuration invalid'
                logger.exception(message)
                raise BadParameter('%s: %s' % (message, unicode(ex)))

        try:
            workspace = Workspace.objects.create_workspace(
                name, title, description, configuration, base_url, is_active)
        except InvalidWorkspaceConfiguration as ex:
            logger.exception('Unable to create new workspace: %s', name)
            raise BadParameter(unicode(ex))

        # Fetch the full workspace with details
        try:
            workspace = Workspace.objects.get_details(workspace.id)
        except Workspace.DoesNotExist:
            raise Http404

        serializer = WorkspaceDetailsSerializerV6(workspace)
        workspace_url = reverse('workspace_details_view',
                                args=[workspace.id],
                                request=request)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=workspace_url))
Пример #14
0
    def patch(self, request, name, version):
        """Edits an existing seed job type and returns the updated details

        :param request: the HTTP PATCH request
        :type request: :class:`rest_framework.request.Request`
        :param job_type_id: The ID for the job type.
        :type job_type_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        auto_update = rest_util.parse_bool(request, 'auto_update', required=False)
        icon_code = rest_util.parse_string(request, 'icon_code', required=False)
        is_published = rest_util.parse_string(request, 'is_published', required=False)
        is_active = rest_util.parse_bool(request, 'is_active', required=False)
        is_paused = rest_util.parse_bool(request, 'is_paused', required=False)
        max_scheduled = rest_util.parse_int(request, 'max_scheduled', required=False)
        # Validate the job configuration and pull out secrets
        configuration_dict = rest_util.parse_dict(request, 'configuration', required=False)
        configuration = None
        try:
            if configuration_dict:
                configuration = JobConfigurationV6(configuration_dict).get_configuration()
        except InvalidJobConfiguration as ex:
            raise BadParameter('Job type configuration invalid: %s' % unicode(ex))

        # Fetch the current job type model
        try:
            job_type = JobType.objects.get(name=name, version=version)
        except JobType.DoesNotExist:
            raise Http404

        # Check for invalid fields
        fields = {'icon_code', 'is_published', 'is_active', 'is_paused', 'max_scheduled', 'configuration'}
        for key, value in request.data.iteritems():
            if key not in fields:
                raise InvalidJobField

        try:
            with transaction.atomic():
                # Edit the job type
                JobType.objects.edit_job_type_v6(job_type_id=job_type.id, manifest=None, is_published=is_published,
                                                 docker_image=None, icon_code=icon_code, is_active=is_active,
                                                 is_paused=is_paused, max_scheduled=max_scheduled,
                                                 configuration=configuration, auto_update=auto_update)
        except (InvalidJobField, InvalidSecretsConfiguration, ValueError,
                InvalidJobConfiguration, InvalidInterfaceDefinition) as ex:
            logger.exception('Unable to update job type: %i', job_type.id)
            raise BadParameter(unicode(ex))

        return HttpResponse(status=204)
Пример #15
0
    def post(self, request):
        """Creates a new job, places it on the queue, and returns the new job information in JSON form

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        job_type_id = rest_util.parse_int(request, 'job_type_id')
        job_data = rest_util.parse_dict(request, 'input', {})
        configuration_dict = rest_util.parse_dict(request, 'configuration', required=False)
        configuration = None

        try:
            jobData = DataV6(job_data, do_validate=True)
        except InvalidData as ex:
            logger.exception('Unable to queue new job. Invalid input: %s', job_data)
            raise BadParameter(unicode(ex))

        try:
            job_type = JobType.objects.get(pk=job_type_id)
        except JobType.DoesNotExist:
            raise Http404

        if configuration_dict:
            try:
                existing = convert_config_to_v6_json(job_type.get_job_configuration())
                configuration = JobConfigurationV6(configuration_dict, existing=existing,
                                                   do_validate=True).get_configuration()
            except InvalidJobConfiguration as ex:
                message = 'Job type configuration invalid'
                logger.exception(message)
                raise BadParameter('%s: %s' % (message, unicode(ex)))

        try:
            job_id = Queue.objects.queue_new_job_for_user_v6(job_type=job_type, job_data=jobData.get_data(),
                                                             job_configuration=configuration)
            CommandMessageManager().send_messages(create_process_job_input_messages([job_id]))
        except InvalidData as err:
            logger.exception('Invalid job data.')
            return Response('Invalid job data: ' + unicode(err), status=status.HTTP_400_BAD_REQUEST)

        try:
            job_details = Job.objects.get_details(job_id)
        except Job.DoesNotExist:
            raise Http404

        serializer = JobDetailsSerializerV6(job_details)
        job_url = reverse('job_details_view', args=[job_id], request=request)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=dict(location=job_url))
Пример #16
0
    def post(self, request):
        """Validates a new recipe type and returns any warnings discovered

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """
        name = rest_util.parse_string(request, 'name')
        version = rest_util.parse_string(request, 'version')
        title = rest_util.parse_string(request, 'title', default_value=name)
        description = rest_util.parse_string(request, 'description', required=False)
        definition_dict = rest_util.parse_dict(request, 'definition')

        # Check for optional trigger rule parameters
        trigger_rule_dict = rest_util.parse_dict(request, 'trigger_rule', required=False)
        if (('type' in trigger_rule_dict and 'configuration' not in trigger_rule_dict) or
                ('type' not in trigger_rule_dict and 'configuration' in trigger_rule_dict)):
            raise BadParameter('Trigger type and configuration are required together.')

        # Attempt to look up the trigger handler for the type
        rule_handler = None
        if trigger_rule_dict and 'type' in trigger_rule_dict:
            try:
                rule_handler = trigger_handler.get_trigger_rule_handler(trigger_rule_dict['type'])
            except InvalidTriggerType as ex:
                logger.exception('Invalid trigger type for recipe validation: %s', name)
                raise BadParameter(unicode(ex))

        # Attempt to look up the trigger rule configuration
        trigger_config = None
        if rule_handler and 'configuration' in trigger_rule_dict:
            try:
                trigger_config = rule_handler.create_configuration(trigger_rule_dict['configuration'])
            except InvalidTriggerRule as ex:
                logger.exception('Invalid trigger rule configuration for recipe validation: %s', name)
                raise BadParameter(unicode(ex))

        # Validate the recipe definition
        try:
            recipe_def = RecipeDefinitionSunset.create(definition_dict)
            warnings = RecipeType.objects.validate_recipe_type(name, title, version, description, recipe_def,
                                                               trigger_config)
        except (InvalidDefinition, InvalidTriggerType, InvalidTriggerRule, InvalidRecipeConnection) as ex:
            logger.exception('Unable to validate new recipe type: %s', name)
            raise BadParameter(unicode(ex))

        results = [{'id': w.key, 'details': w.details} for w in warnings]
        return Response({'warnings': results})
Пример #17
0
    def _create_v5(self, request):
        """The v5 version for creating batches

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        recipe_type_id = rest_util.parse_int(request, 'recipe_type_id')
        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)

        # Make sure the recipe type exists
        try:
            recipe_type = RecipeType.objects.get(pk=recipe_type_id)
        except RecipeType.DoesNotExist:
            raise BadParameter('Unknown recipe type: %i' % recipe_type_id)

        # Validate the batch definition
        definition_dict = rest_util.parse_dict(request, 'definition')
        definition = None
        try:
            if definition_dict:
                definition = OldBatchDefinition(definition_dict)
                definition.validate(recipe_type)
        except InvalidDefinition as ex:
            raise BadParameter('Batch definition invalid: %s' % unicode(ex))

        # Create the batch
        batch = Batch.objects.create_batch_old(recipe_type,
                                               definition,
                                               title=title,
                                               description=description)

        # Fetch the full batch with details
        try:
            batch = Batch.objects.get_details_v5(batch.id)
        except Batch.DoesNotExist:
            raise Http404

        url = reverse('batch_details_view', args=[batch.id], request=request)
        serializer = BatchDetailsSerializerV5(batch)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=url))
Пример #18
0
    def create_v6(self, request):
        """Creates or edits a dataset and returns a link to the detail URL

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        definition = rest_util.parse_dict(request, 'definition', required=True)
        media_type = rest_util.parse_string_list(request,
                                                 'media_type',
                                                 required=False)

        # validate the definition
        try:
            dataset_def = DataSetDefinitionV6(
                definition=definition, do_validate=True).get_definition()
        except InvalidDataSetDefinition as ex:
            message = 'DataSet definition is invalid'
            logger.exception(message)
            raise BadParameter('%s: %s' % (message, unicode(ex)))

        try:
            dataset = DataSet.objects.create_dataset_v6(
                dataset_def, title=title, description=description)
        except Exception as ex:
            message = 'Unable to create new dataset'
            logger.exception(message)
            raise BadParameter('%s: %s' % (message, unicode(ex)))

        try:
            dataset = DataSet.objects.get_details_v6(dataset.id)
        except DataSet.DoesNotExist:
            raise Http404

        url = reverse('dataset_details_view',
                      args=[dataset.id],
                      request=request)
        serializer = DataSetDetailsSerializerV6(dataset)

        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=url))
Пример #19
0
    def _post_v5(self, request):
        """Validates a new workspace and returns any warnings discovered

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        name = rest_util.parse_string(request, 'name')
        json_config = rest_util.parse_dict(request, 'json_config')

        rest_util.parse_string(request, 'title', required=False)
        rest_util.parse_string(request, 'description', required=False)
        rest_util.parse_string(request, 'base_url', required=False)
        rest_util.parse_string(request, 'is_active', required=False)

        # Validate the workspace configuration
        try:
            warnings = Workspace.objects.validate_workspace_v5(
                name, json_config)
        except InvalidWorkspaceConfiguration as ex:
            logger.exception('Unable to validate new workspace: %s', name)
            raise BadParameter(unicode(ex))

        results = [{'id': w.name, 'details': w.description} for w in warnings]
        return Response({'warnings': results})
Пример #20
0
    def patch(self, request, workspace_id):
        """Edits an existing workspace and returns the updated details

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param workspace_id: The ID for the workspace.
        :type workspace_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request, 'description', required=False)
        json_config = rest_util.parse_dict(request, 'json_config', required=False)
        base_url = rest_util.parse_string(request, 'base_url', required=False)
        is_active = rest_util.parse_string(request, 'is_active', required=False)

        try:
            Workspace.objects.edit_workspace(workspace_id, title, description, json_config, base_url, is_active)

            workspace = Workspace.objects.get_details(workspace_id)
        except Workspace.DoesNotExist:
            raise Http404
        except InvalidWorkspaceConfiguration as ex:
            logger.exception('Unable to edit workspace: %s', workspace_id)
            raise BadParameter(unicode(ex))

        serializer = self.get_serializer(workspace)
        return Response(serializer.data)
Пример #21
0
    def post_impl(self, request):
        """Validates a new Strike process and returns any warnings discovered

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        name = rest_util.parse_string(request, 'name')
        configuration = rest_util.parse_dict(request, 'configuration')

        rest_util.parse_string(request, 'title', required=False)
        rest_util.parse_string(request, 'description', required=False)

        # Validate the Strike configuration
        try:
            config = StrikeConfigurationV2(
                configuration, do_validate=True).get_configuration()
            warnings = config.validate()
        except InvalidStrikeConfiguration as ex:
            logger.exception('Unable to validate new Strike process: %s', name)
            raise BadParameter(unicode(ex))

        results = [{'id': w.key, 'details': w.details} for w in warnings]
        return Response({'warnings': results})
Пример #22
0
    def patch(self, request, strike_id):
        """Edits an existing Strike process and returns the updated details

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param strike_id: The ID of the Strike process
        :type strike_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        configuration = rest_util.parse_dict(request,
                                             'configuration',
                                             required=False)

        try:
            Strike.objects.edit_strike(strike_id, title, description,
                                       configuration)

            strike = Strike.objects.get_details(strike_id)
        except Strike.DoesNotExist:
            raise Http404
        except InvalidStrikeConfiguration as ex:
            logger.exception('Unable to edit Strike process: %s', strike_id)
            raise BadParameter(unicode(ex))

        serializer = self.get_serializer(strike)
        return Response(serializer.data)
Пример #23
0
    def post(self, request, recipe_id):
        """Schedules a recipe for reprocessing and returns it in JSON form

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param recipe_id: The id of the recipe
        :type recipe_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        job_names = rest_util.parse_string_list(request, 'job_names', required=False)
        all_jobs = rest_util.parse_bool(request, 'all_jobs', required=False)

        try:
            handler = Recipe.objects.reprocess_recipe(recipe_id, job_names, all_jobs)
        except Recipe.DoesNotExist:
            raise Http404
        except ReprocessError as err:
            raise BadParameter(unicode(err))

        try:
            new_recipe = Recipe.objects.get_details(handler.recipe.id)
        except Recipe.DoesNotExist:
            raise Http404

        url = urlresolvers.reverse('recipe_details_view', args=[new_recipe.id])
        serializer = self.get_serializer(new_recipe)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=dict(location=url))
Пример #24
0
    def _post_v5(self, request):
        """Creates a new error and returns a link to the info URL

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        name = rest_util.parse_string(request, 'name')
        title = rest_util.parse_string(request, 'title')
        description = rest_util.parse_string(request, 'description')
        category = rest_util.parse_string(
            request,
            'category',
            accepted_values=[c for c, _ in Error.CATEGORIES])

        # Do not allow the creation of SYSTEM level errors
        if category == 'SYSTEM':
            raise BadParameter('System level errors cannot be created.')

        error = Error.objects.create_legacy_error(name, title, description,
                                                  category)

        serializer = ErrorDetailsSerializerV5(error)
        error_url = reverse('error_details_view',
                            args=[error.id],
                            request=request)
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED,
                        headers=dict(location=error_url))
Пример #25
0
    def queue_new_job_v6(self, job_type, data, event, job_configuration=None):
        """Creates a new job for the given type and data. The new job is immediately placed on the queue. The new job,
        job_exe, and queue models are saved in the database in an atomic transaction.

        :param job_type: The type of the new job to create and queue
        :type job_type: :class:`job.models.JobType`
        :param data: The job data to run on
        :type data: :class:`data.data.data.data`
        :param event: The event that triggered the creation of this job
        :type event: :class:`trigger.models.TriggerEvent`
        :param job_configuration: The configuration for running a job of this type, possibly None
        :type job_configuration: :class:`job.configuration.configuration.JobConfiguration`
        :returns: The new queued job
        :rtype: :class:`job.models.Job`

        :raises job.configuration.data.exceptions.InvalidData: If the job data is invalid
        """
        try:
            job_type_rev = JobTypeRevision.objects.get_revision(
                job_type.name, job_type.version, job_type.revision_num)
            with transaction.atomic():
                job = Job.objects.create_job_v6(job_type_rev,
                                                event_id=event.id,
                                                input_data=data,
                                                job_config=job_configuration)
                job.save()
        except InvalidData as ex:
            raise BadParameter(unicode(ex))

        # queue and return the job
        self.queue_jobs([job])
        job = Job.objects.get_details(job.id)

        return job
Пример #26
0
    def create(self, request):
        """Creates a new Workspace and returns it in JSON form

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        name = rest_util.parse_string(request, 'name')
        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request, 'description', required=False)
        json_config = rest_util.parse_dict(request, 'json_config')
        base_url = rest_util.parse_string(request, 'base_url', required=False)
        is_active = rest_util.parse_bool(request, 'is_active', default_value=True, required=False)

        try:
            workspace = Workspace.objects.create_workspace(name, title, description, json_config, base_url, is_active)
        except InvalidWorkspaceConfiguration as ex:
            logger.exception('Unable to create new workspace: %s', name)
            raise BadParameter(unicode(ex))

        # Fetch the full workspace with details
        try:
            workspace = Workspace.objects.get_details(workspace.id)
        except Workspace.DoesNotExist:
            raise Http404

        serializer = WorkspaceDetailsSerializer(workspace)
        workspace_url = urlresolvers.reverse('workspace_details_view', args=[workspace.id])
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=dict(location=workspace_url))
Пример #27
0
    def patch_impl_v6(self, request, strike_id):
        """Edits an existing Strike process and returns the updated details

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param strike_id: The ID of the Strike process
        :type strike_id: int encoded as a str
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request, 'description', required=False)
        configuration = rest_util.parse_dict(request, 'configuration', required=False)

        config = None
        try:
            if configuration:
                config = StrikeConfigurationV6(configuration, do_validate=True).get_configuration()
        except InvalidStrikeConfiguration as ex:
            raise BadParameter('Strike configuration invalid: %s' % unicode(ex))

        try:
            # must collect old config before editing strike
            if config:
                new_config = config.get_dict()
                old_config = Strike.objects.get_details(strike_id)

            Strike.objects.edit_strike(strike_id, title, description, config)

            # if workspace has changed strike job must be restarted for changes to take effect
            if config and old_config.configuration["workspace"] != new_config["workspace"]:
                strike_job = old_config.job
                Job.objects.update_jobs_to_canceled([strike_job], timezone.now())

                requeue_jobs = []
                requeue_jobs.append(QueuedJob(strike_job.id, strike_job.num_exes))
                msg = create_requeue_jobs_messages(requeue_jobs)
                CommandMessageManager().send_messages(msg)
            
        except Strike.DoesNotExist:
            raise Http404
        except InvalidStrikeConfiguration as ex:
            logger.exception('Unable to edit Strike process: %s', strike_id)
            raise BadParameter(unicode(ex))

        return Response(status=status.HTTP_204_NO_CONTENT)
Пример #28
0
    def patch_v6(self, request, name):
        """Edits an existing recipe type and returns the updated details

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :param name: The name of the recipe type
        :type name: string
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        title = rest_util.parse_string(request, 'title', required=False)
        description = rest_util.parse_string(request,
                                             'description',
                                             required=False)
        definition_dict = rest_util.parse_dict(request,
                                               'definition',
                                               required=False)
        auto_update = rest_util.parse_bool(request,
                                           'auto_update',
                                           required=False,
                                           default_value=True)
        is_active = rest_util.parse_bool(request, 'is_active', required=False)

        # Fetch the current recipe type model
        try:
            recipe_type = RecipeType.objects.filter(name=name).first()
        except RecipeType.DoesNotExist:
            raise Http404

        try:
            with transaction.atomic():
                # Validate the recipe definition
                recipe_def = None
                if definition_dict:
                    recipe_def = RecipeDefinitionV6(
                        definition=definition_dict,
                        do_validate=True).get_definition()

                # Edit the recipe type
                validation = RecipeType.objects.edit_recipe_type_v6(
                    recipe_type_id=recipe_type.id,
                    title=title,
                    description=description,
                    definition=recipe_def,
                    auto_update=auto_update,
                    is_active=is_active)
        except InvalidDefinition as ex:
            logger.exception('Unable to update recipe type: %s', name)
            raise BadParameter(unicode(ex))

        resp_dict = {
            'is_valid': validation.is_valid,
            'errors': [e.to_dict() for e in validation.errors],
            'warnings': [w.to_dict() for w in validation.warnings],
            'diff': validation.diff
        }
        return Response(resp_dict)
Пример #29
0
    def post(self, request):
        """Submit command message to re-queue jobs that fit the given filter criteria

        :param request: the HTTP GET request
        :type request: :class:`rest_framework.request.Request`
        :returns: the HTTP response to send back to the user
        """

        started = rest_util.parse_timestamp(request, 'started', required=False)
        ended = rest_util.parse_timestamp(request, 'ended', required=False)
        rest_util.check_time_range(started, ended)

        error_categories = rest_util.parse_string_list(request, 'error_categories', required=False)
        error_ids = rest_util.parse_int_list(request, 'error_ids', required=False)
        job_ids = rest_util.parse_int_list(request, 'job_ids', required=False)
        job_status = rest_util.parse_string(request, 'status', required=False)
        job_type_ids = rest_util.parse_int_list(request, 'job_type_ids', required=False)
        priority = rest_util.parse_int(request, 'priority', required=False)

        job_type_names = rest_util.parse_string_list(request, 'job_type_names', required=False)
        batch_ids = rest_util.parse_int_list(request, 'batch_ids', required=False)
        recipe_ids = rest_util.parse_int_list(request, 'recipe_ids', required=False)
        is_superseded = rest_util.parse_bool(request, 'is_superseded', required=False)

        job_types = rest_util.parse_dict_list(request, 'job_types', required=False)

        for jt in job_types:
            if 'name' not in jt or 'version' not in jt:
                raise BadParameter('Job types argument invalid: %s' % job_types)
            existing_job_type = JobType.objects.filter(name=jt['name'], version=jt['version']).first()
            if not existing_job_type:
                raise BadParameter(
                    'Job Type with name: %s and version: %s does not exist' % (jt['name'], jt['version']))
            job_type_ids.append(existing_job_type.id)

        # Create and send message
        msg = create_requeue_jobs_bulk_message(started=started, ended=ended, error_categories=error_categories,
                                               error_ids=error_ids, job_ids=job_ids, job_type_ids=job_type_ids,
                                               priority=priority, status=job_status, job_type_names=job_type_names,
                                               batch_ids=batch_ids, recipe_ids=recipe_ids, is_superseded=is_superseded)
        CommandMessageManager().send_messages([msg])

        return Response(status=status.HTTP_202_ACCEPTED)
Пример #30
0
    def _post_v5(self, request):
        """The v5 version for validating a new batch

        :param request: the HTTP POST request
        :type request: :class:`rest_framework.request.Request`
        :rtype: :class:`rest_framework.response.Response`
        :returns: the HTTP response to send back to the user
        """

        recipe_type_id = rest_util.parse_int(request, 'recipe_type_id')

        # Make sure the recipe type exists
        try:
            recipe_type = RecipeType.objects.get(pk=recipe_type_id)
        except RecipeType.DoesNotExist:
            raise BadParameter('Unknown recipe type: %i' % recipe_type_id)

        # Validate the batch definition
        definition_dict = rest_util.parse_dict(request, 'definition')
        definition = None
        warnings = []
        try:
            if definition_dict:
                definition = OldBatchDefinition(definition_dict)
                warnings = definition.validate(recipe_type)
        except InvalidDefinition as ex:
            raise BadParameter('Batch definition invalid: %s' % unicode(ex))

        # Get a rough estimate of how many recipes/files will be affected
        old_recipes = Batch.objects.get_matched_recipes(
            recipe_type, definition)
        old_files = Batch.objects.get_matched_files(recipe_type, definition)

        return Response({
            'recipe_count': old_recipes.count(),
            'file_count': old_files.count(),
            'warnings': warnings,
        })