def schedule_queued_recipes(*args): session.begin() try: # This query returns a queued host recipe and and the guest which has # the most recent distro tree. It is to be used as a derived table. latest_guest_distro = select([machine_guest_map.c.machine_recipe_id.label('host_id'), func.max(DistroTree.date_created).label('latest_distro_date')], from_obj=[machine_guest_map.join(GuestRecipe.__table__, machine_guest_map.c.guest_recipe_id==GuestRecipe.__table__.c.id). \ join(Recipe.__table__).join(DistroTree.__table__)], whereclause=Recipe.status=='Queued', group_by=machine_guest_map.c.machine_recipe_id).alias() hosts_lab_controller_distro_map = aliased(LabControllerDistroTree) hosts_distro_tree = aliased(DistroTree) guest_recipe = aliased(Recipe) guests_distro_tree = aliased(DistroTree) guests_lab_controller = aliased(LabController) # This query will return queued recipes that are eligible to be scheduled. # They are determined to be eligible if: # * They are clean # * There are systems available (see the filter criteria) in lab controllers where # the recipe's distro tree is available. # * If it is a host recipe, the most recently created distro of all # the guest recipe's distros is available in at least one of the same # lab controllers as that of the host's distro tree. # # Also note that we do not try to handle the situation where the guest and host never # have a common labcontroller. In that situation the host and guest would stay queued # until that situation was rectified. recipes = MachineRecipe.query\ .join(Recipe.recipeset, RecipeSet.job)\ .filter(Job.dirty_version == Job.clean_version)\ .outerjoin((guest_recipe, MachineRecipe.guests))\ .outerjoin((guests_distro_tree, guest_recipe.distro_tree_id == guests_distro_tree.id))\ .outerjoin((latest_guest_distro, and_(latest_guest_distro.c.host_id == MachineRecipe.id, latest_guest_distro.c.latest_distro_date == \ guests_distro_tree.date_created)))\ .outerjoin(guests_distro_tree.lab_controller_assocs, guests_lab_controller)\ .join(Recipe.systems)\ .join((hosts_distro_tree, hosts_distro_tree.id == MachineRecipe.distro_tree_id))\ .join((hosts_lab_controller_distro_map, hosts_distro_tree.lab_controller_assocs), (LabController, and_( hosts_lab_controller_distro_map.lab_controller_id == LabController.id, System.lab_controller_id == LabController.id)))\ .filter( and_(Recipe.status == TaskStatus.queued, System.user == None, LabController.disabled == False, or_( RecipeSet.lab_controller == None, RecipeSet.lab_controller_id == System.lab_controller_id, ), or_( System.loan_id == None, System.loan_id == Job.owner_id, ), or_( # We either have no guest guest_recipe.id == None, # Or we have a guest of which the latest # is in a common lab controller. and_(guests_lab_controller.id == LabController.id, latest_guest_distro.c.latest_distro_date != None ), ) # or ) # and ) # Get out of here if we have no recipes if not recipes.count(): return False # This should be the guest recipe with the latest distro. # We return it in this query, to save us from re-running the # derived table query in schedule_queued_recipe() recipes = recipes.add_column(guest_recipe.id) # Effective priority is given in the following order: # * Multi host recipes with already scheduled siblings # * Priority level (i.e Normal, High etc) # * RecipeSet id # * Recipe id recipes = recipes.order_by(RecipeSet.lab_controller == None). \ order_by(RecipeSet.priority.desc()). \ order_by(RecipeSet.id). \ order_by(MachineRecipe.id) # Don't do a GROUP BY before here, it is not needed. recipes = recipes.group_by(MachineRecipe.id) log.debug("Entering schedule_queued_recipes") for recipe_id, guest_recipe_id in recipes.values(MachineRecipe.id, guest_recipe.id): session.begin(nested=True) try: schedule_queued_recipe(recipe_id, guest_recipe_id) session.commit() except (StaleSystemUserException, InsufficientSystemPermissions, StaleTaskStatusException), e: # Either # System user has changed before # system allocation # or # System permissions have changed before # system allocation # or # Something has moved our status on from queued # already. log.warn(str(e)) session.rollback() except Exception, e: log.exception('Error in schedule_queued_recipe(%s)', recipe_id) session.rollback() session.begin(nested=True) try: recipe=MachineRecipe.by_id(recipe_id) recipe.recipeset.abort(u"Aborted in schedule_queued_recipe: %s" % e) session.commit() except Exception, e: log.exception("Error during error handling in schedule_queued_recipe: %s" % e) session.rollback()
def schedule_queued_recipes(*args): work_done = False session.begin() try: # This query returns a queued host recipe and and the guest which has # the most recent distro tree. It is to be used as a derived table. latest_guest_distro = select([machine_guest_map.c.machine_recipe_id.label('host_id'), func.max(DistroTree.date_created).label('latest_distro_date')], from_obj=[machine_guest_map.join(GuestRecipe.__table__, machine_guest_map.c.guest_recipe_id==GuestRecipe.__table__.c.id). \ join(Recipe.__table__).join(DistroTree.__table__)], whereclause=Recipe.status=='Queued', group_by=machine_guest_map.c.machine_recipe_id).alias() hosts_lab_controller_distro_map = aliased(LabControllerDistroTree) hosts_distro_tree = aliased(DistroTree) guest_recipe = aliased(Recipe) guests_distro_tree = aliased(DistroTree) guests_lab_controller = aliased(LabController) # This query will return queued recipes that are eligible to be scheduled. # They are determined to be eligible if: # * They are clean # * There are systems available (see the filter criteria) in lab controllers where # the recipe's distro tree is available. # * If it is a host recipe, the most recently created distro of all # the guest recipe's distros is available in at least one of the same # lab controllers as that of the host's distro tree. # # Also note that we do not try to handle the situation where the guest and host never # have a common labcontroller. In that situation the host and guest would stay queued # until that situation was rectified. recipes = MachineRecipe.query\ .join(Recipe.recipeset, RecipeSet.job)\ .filter(Job.dirty_version == Job.clean_version)\ .outerjoin((guest_recipe, MachineRecipe.guests))\ .outerjoin((guests_distro_tree, guest_recipe.distro_tree_id == guests_distro_tree.id))\ .outerjoin((latest_guest_distro, and_(latest_guest_distro.c.host_id == MachineRecipe.id, latest_guest_distro.c.latest_distro_date == \ guests_distro_tree.date_created)))\ .outerjoin(guests_distro_tree.lab_controller_assocs, guests_lab_controller)\ .join(Recipe.systems)\ .join((hosts_distro_tree, hosts_distro_tree.id == MachineRecipe.distro_tree_id))\ .join((hosts_lab_controller_distro_map, hosts_distro_tree.lab_controller_assocs), (LabController, and_( hosts_lab_controller_distro_map.lab_controller_id == LabController.id, System.lab_controller_id == LabController.id)))\ .filter( and_(Recipe.status == TaskStatus.queued, System.user == None, LabController.disabled == False, or_( RecipeSet.lab_controller == None, RecipeSet.lab_controller_id == System.lab_controller_id, ), or_( System.loan_id == None, System.loan_id == Job.owner_id, ), or_( # We either have no guest guest_recipe.id == None, # Or we have a guest of which the latest # is in a common lab controller. and_(guests_lab_controller.id == LabController.id, latest_guest_distro.c.latest_distro_date != None ), ) # or ) # and ) # This should be the guest recipe with the latest distro. # We return it in this query, to save us from re-running the # derived table query in schedule_queued_recipe() recipes = recipes.add_column(guest_recipe.id) # Effective priority is given in the following order: # * Multi host recipes with already scheduled siblings # * Priority level (i.e Normal, High etc) # * RecipeSet id # * Recipe id recipes = recipes.order_by(RecipeSet.lab_controller == None). \ order_by(RecipeSet.priority.desc()). \ order_by(RecipeSet.id). \ order_by(MachineRecipe.id) # Don't do a GROUP BY before here, it is not needed. recipes = recipes.group_by(MachineRecipe.id) for recipe_id, guest_recipe_id in recipes.values( MachineRecipe.id, guest_recipe.id): session.begin(nested=True) try: schedule_queued_recipe(recipe_id, guest_recipe_id) session.commit() except (StaleSystemUserException, InsufficientSystemPermissions, StaleTaskStatusException), e: # Either # System user has changed before # system allocation # or # System permissions have changed before # system allocation # or # Something has moved our status on from queued # already. log.warn(str(e)) session.rollback() except Exception, e: log.exception('Error in schedule_queued_recipe(%s)', recipe_id) session.rollback() session.begin(nested=True) try: recipe = MachineRecipe.by_id(recipe_id) recipe.recipeset.abort( u"Aborted in schedule_queued_recipe: %s" % e) session.commit() except Exception, e: log.exception( "Error during error handling in schedule_queued_recipe: %s" % e) session.rollback()