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)
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)
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))
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))
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)
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)
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))
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))
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})
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)
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))
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)
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))
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)
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))
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})
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))
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))
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})
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)
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})
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)
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))
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))
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
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))
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)
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)
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)
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, })