def test_get_blocked_jobs(self): """Tests calling RecipeHandler.get_blocked_jobs()""" handler = RecipeHandler(self.recipe, self.recipe_jobs) blocked_jobs = handler.get_blocked_jobs() blocked_job_ids = set() for blocked_job in blocked_jobs: blocked_job_ids.add(blocked_job.id) self.assertSetEqual(blocked_job_ids, {self.job_fa_co_b.id, self.job_qu_ca_a.id, self.job_qu_ca_b.id})
def test_get_blocked_jobs(self): """Tests calling RecipeHandler.get_blocked_jobs()""" handler = RecipeHandler(self.recipe, self.recipe_jobs) blocked_jobs = handler.get_blocked_jobs() blocked_job_ids = set() for blocked_job in blocked_jobs: blocked_job_ids.add(blocked_job.id) self.assertSetEqual( blocked_job_ids, {self.job_fa_co_b.id, self.job_qu_ca_a.id, self.job_qu_ca_b.id})
def create_recipe(self, recipe_type, data, event, superseded_recipe=None, delta=None, superseded_jobs=None): """Creates a new recipe for the given type and returns a recipe handler for it. All jobs for the recipe will also be created. If the new recipe is superseding an old recipe, superseded_recipe, delta, and superseded_jobs must be provided and the caller must have obtained a model lock on all job models in superseded_jobs and on the superseded_recipe model. All database changes occur in an atomic transaction. :param recipe_type: The type of the recipe to create :type recipe_type: :class:`recipe.models.RecipeType` :param data: The recipe data to run on, should be None if superseded_recipe is provided :type data: :class:`recipe.data.recipe_data.RecipeData` :param event: The event that triggered the creation of this recipe :type event: :class:`trigger.models.TriggerEvent` :param superseded_recipe: The recipe that the created recipe is superseding, possibly None :type superseded_recipe: :class:`recipe.models.Recipe` :param delta: If not None, represents the changes between the old recipe to supersede and the new recipe :type delta: :class:`recipe.handlers.graph_delta.RecipeGraphDelta` :param superseded_jobs: If not None, represents the job models (stored by job name) of the old recipe to supersede. This mapping must include all jobs created by the previous recipe, not just the ones that will actually be replaced by the new recipe definition. :type superseded_jobs: {string: :class:`job.models.Job`} :returns: A handler for the new recipe :rtype: :class:`recipe.handlers.handler.RecipeHandler` :raises :class:`recipe.exceptions.CreateRecipeError`: If general recipe parameters are invalid :raises :class:`recipe.exceptions.SupersedeError`: If the superseded parameters are invalid :raises :class:`recipe.exceptions.ReprocessError`: If recipe cannot be reprocessed :raises :class:`recipe.configuration.data.exceptions.InvalidRecipeData`: If the recipe data is invalid """ if not recipe_type.is_active: raise CreateRecipeError('Recipe type is no longer active') if event is None: raise CreateRecipeError( 'Event that triggered recipe creation is required') recipe = Recipe() recipe.recipe_type = recipe_type recipe.recipe_type_rev = RecipeTypeRevision.objects.get_revision( recipe_type.id, recipe_type.revision_num) recipe.event = event recipe_definition = recipe.get_recipe_definition() when = timezone.now() if superseded_recipe: # Mark superseded recipe superseded_recipe.is_superseded = True superseded_recipe.superseded = when superseded_recipe.save() # Use data from superseded recipe data = superseded_recipe.get_recipe_data() if not delta: raise SupersedeError('Cannot supersede a recipe without delta') # New recipe references superseded recipe root_id = superseded_recipe.root_superseded_recipe_id if not root_id: root_id = superseded_recipe.id recipe.root_superseded_recipe_id = root_id recipe.superseded_recipe = superseded_recipe else: if delta: raise SupersedeError( 'delta must be provided with a superseded recipe') # Validate recipe data and save recipe recipe_definition.validate_data(data) recipe.data = data.get_dict() recipe.save() # Save models for each recipe input file recipe_files = [] for input_file_id in data.get_input_file_ids(): recipe_file = RecipeFile() recipe_file.recipe_id = recipe.id recipe_file.scale_file_id = input_file_id recipe_file.created = recipe.created recipe_files.append(recipe_file) RecipeFile.objects.bulk_create(recipe_files) # Create recipe jobs and link them to the recipe recipe_jobs = self._create_recipe_jobs(recipe, event, when, delta, superseded_jobs) handler = RecipeHandler(recipe, recipe_jobs) # Block any new jobs that need to be blocked jobs_to_blocked = handler.get_blocked_jobs() if jobs_to_blocked: Job.objects.update_status(jobs_to_blocked, 'BLOCKED', when) return handler