Pàgina principal

De dnavasol


Welcome to your new site!


Titul 1[modifica | modifica el codi]

Titul 2[modifica | modifica el codi]

Titul 3[modifica | modifica el codi]

  • Hola
  • Hola 2
    • Hola 3


  1. Bla
  2. Bla
    1. Bla
      1. Bla



/**
 * 
 */
package net.opentrends.jenkins.plugins.customqueuepriority.byjobname.config.util;

import hudson.model.Queue.Item;
import hudson.model.AbstractProject;
import hudson.model.Computer;
import hudson.model.Executor;
import hudson.model.JobProperty;
import hudson.model.Node;
import hudson.model.queue.QueueTaskDispatcher;
import hudson.model.queue.SubTask;
import hudson.model.queue.CauseOfBlockage;
import hudson.security.ACL;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import jenkins.model.Jenkins;
import jenkins.security.NonSerializableSecurityContext;
import net.opentrends.jenkins.plugins.customqueuepriority.byjobname.config.CustomPriorityByJobNameParameter;
import net.opentrends.jenkins.plugins.customqueuepriority.byjobname.config.model.JobRuntimeInformation;
import net.opentrends.jenkins.plugins.customqueuepriority.byjobname.config.model.JobsRuntimeInformation;
import net.opentrends.jenkins.plugins.sameparametersblocker.SameParametersQueueTaskDispatcher;

import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;

/**
 * Utility class that makes use of Jenkins APIs to determine current
 * runtime information relevant for the functionality of this plugin.
 * 
 * @author <david.navarro@opentrends.net>
 */
public class JenkinsCurrentRuntimeInformation {

	private static final Logger LOGGER = Logger.getLogger(JenkinsCurrentRuntimeInformation.class.getName());

	/**
	 * Returns current runtime information for each of the jobs indicated at
	 * the priorizationJobsList.
	 * 
	 * The value of this String has to follow this specifications:
	 * 
	 *   1. The names of the jobs are separated by commas (,)
	 *   2. The more to the right a job name appears, the more priority it has
	 *      over the ones that appear to its left.
	 *   3. It is possible to indicate the number of reserved executors that the
	 *      plugin must reserve for the builds of the items of a job no matter
	 *      if there are items of more priority jobs waiting at the Jenkins
	 *      queue. This option is indicated separating the job name and its
	 *      number of reserved executors with a colon (:)
	 * 
	 * Configuration example:
	 * 
	 *   low-priority-job:2,medium-priority-job,high-priority-job:4,very-high-priority-job
	 * 
	 * Some examples of the plugin behaviour with this configuration example:
	 * 
	 *   - The medium-priority-job has less priority than the high-priority-job
	 *     or the very-high-priority-job. I.e, no executor will be assigned to any
	 *     item of the medium-priority-job if there are still items of any of the
	 *     other two jobs waiting at the queue.
	 *   - If there are less than 2 executors building items of the low-priority-job,
	 *     if a new item of this low-priority-job appears at the queue it will be
	 *     assigned an executor no matter if there are items of the other more
	 *     priority jobs waiting at the queue.
	 *   - If there are less than 4 executors building items of the high-priority-job,
	 *     if a new item of this high-priority-job appears at the queue it might be
	 *     assigned an executor no matter if there are items of the
	 *     very-high-priority-job. It will definitely be assigned an executor if
	 *     no low-priority-job items are waiting at the queue with less than 2
	 *     executors building items of low-priority-job.
	 * 
	 * @param priorizationJobsList A String configuring the jobs affected by this
	 * plugin functionality.
	 * @return the JobsRuntimeInformation
	 */
	public static JobsRuntimeInformation getJobsRuntimeSnapshot(String priorizationJobsList) {

		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("priorizationJobsList=" + priorizationJobsList);
		}

		JobsRuntimeInformation jobsRuntimeInformation = null;

		if (priorizationJobsList != null && !priorizationJobsList.trim().isEmpty()) {

			Item[] jenkinsQueuedItems = getQueuedItems();

			String[] priorizationJobsListSplitted = priorizationJobsList.toUpperCase().split(",");
			jobsRuntimeInformation = new JobsRuntimeInformation(priorizationJobsListSplitted.length);

			for (int priority = 0; priority < priorizationJobsListSplitted.length; priority++) {

				String priorizationJobsListSplittedValue = priorizationJobsListSplitted[priority];
				String[] priorizationJobsListSplittedValueSplitted = priorizationJobsListSplittedValue.split(":");

				String jobName = priorizationJobsListSplittedValueSplitted[0];
				JobRuntimeInformation jobRuntimeInformation = new JobRuntimeInformation(jobName);
				jobRuntimeInformation.setPriority(priority);
				jobRuntimeInformation.setReservedExecutors(getReservedExecutors(priorizationJobsListSplittedValueSplitted));
				jobRuntimeInformation.setCustomQueuePriorityByJobNameActivated(isCustomQueuePriorityByJobNameActivated(jobName));
				jobRuntimeInformation = getQueuedItemsInformationByJobName(jobRuntimeInformation, jenkinsQueuedItems);
				jobRuntimeInformation.setCurrentBuildings(getCurrentBuildingsOnAllNodes(jobName));

				if (LOGGER.isLoggable(Level.FINER)) {
					LOGGER.finer("jobRuntimeInformation.toString()=" + jobRuntimeInformation.toString());
				}
				
				jobsRuntimeInformation.put(jobRuntimeInformation);
			}
		}

		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("jobsRuntimeInformation.toString()=" + jobsRuntimeInformation.toString());
		}
		return jobsRuntimeInformation;
	}

	/**
	 * @param priorizationJobsListSplittedValueSplitted
	 * @return The number of executors reserved for the items of a job or null if no
	 * reservation was configured for that job.
	 */
	private static Integer getReservedExecutors(String[] priorizationJobsListSplittedValueSplitted) {
		Integer reservedExecutors = null;
		if (priorizationJobsListSplittedValueSplitted != null && 
				priorizationJobsListSplittedValueSplitted.length > 1) {
			try {
				reservedExecutors = Integer.parseInt(priorizationJobsListSplittedValueSplitted[1]);
			} catch (NumberFormatException nfe) {
				if (LOGGER.isLoggable(Level.WARNING)) {
					LOGGER.warning("The CUSTOM_QUEUE_PRIORITY.BY_JOB_NAME.PRIORIZATION_LIST System Env property is malformed.");
				}
				// But don't stop, just consider that for this job no reserved executors has been set.
			}
		}
		return reservedExecutors;
	}

	/**
	 * @param jobName
	 * @return 	The number of buildings of the given jobName that are
	 * being executed on all the nodes of the Jenkins instance.
	 */
	private static Integer getCurrentBuildingsOnAllNodes(String jobName) {
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine how many executions of the job " +
					jobName + " are running on any of the availables nodes.");
		}

		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine how many executions of the job " +
					jobName + " are running on the master's node.");
		}
		Integer buildings = getCurrentBuildingsOnNode(jobName, Jenkins.getInstance());

		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine how many executions of the job " +
					jobName + " are running on the slaves' nodes.");
		}
		for (Node node : Jenkins.getInstance().getNodes()) {
			buildings = buildings + getCurrentBuildingsOnNode(jobName, node);
		}

		return buildings;
	}

	/**
	 * @param jobName
	 * @param node
	 * @return The number of buildings of the given jobName that are
	 * being executed on the given node. 
	 */
	private static Integer getCurrentBuildingsOnNode(String jobName, Node node) {
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine how many executions of the job " +
					jobName + " are running on the node=" + node);
		}

		Integer buildings = Integer.valueOf(0);

		// I think this'll be more reliable than job.getBuilds(), which seemed
		// to not always get a build right after it was launched, for some reason.
		Computer computer = node.toComputer();
		if (computer != null) { // Not all nodes are certain to become
								// computers, like nodes with 0 executors.
			for (Executor executor : computer.getExecutors()) {
				if (isCurrentlyBuildingOnExecutor(jobName, executor)) {
					buildings++;
				}
			}
			/* Work next block if you find a MatrixProject that needs
			 * to use our plugin
			if (task instanceof MatrixProject) {
				for (Executor e : computer.getOneOffExecutors()) {
					if (isCurrentlyBuildingOnExecutor(jobName, executor)) {
						buildings++;
					}
				}
			}
			*/
		}

		return buildings;
	}

	/**
	 * @param jobName
	 * @param executor
	 * @return true if the given executor is building an execution of the given jobName
	 */
	private static boolean isCurrentlyBuildingOnExecutor(String jobName, Executor executor) {
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine if an execution of the job " +
					jobName + " is running on the executor=" + executor);
		}
		
		boolean currentlyBuilding = false;
		
		if (executor != null && executor.getCurrentExecutable() != null) {
			SubTask subTask = executor.getCurrentExecutable().getParent();
			if (subTask != null) {
				AbstractProject<?, ?> abstractProject = (AbstractProject<?, ?>)subTask;
				String currentlyBuildingJobName = abstractProject.getName();
				if (currentlyBuildingJobName != null && !currentlyBuildingJobName.trim().isEmpty()) {
					if (currentlyBuildingJobName.equalsIgnoreCase(jobName)) {
						currentlyBuilding = true;
					}
				}
			}
		}

		return currentlyBuilding;
	}

	/**
	 * @param jobRuntimeInformation with the jobName already informed.
	 * @param jenkinsQueuedItems The full list of items waiting at the Jenkins queue.
	 * @return jobRuntimeInformation with the number of items of the given job currently waiting at the Jenkins queue
	 * and with those blocekd by the selected-same-parameters-concurrent-build-blocker plugin.
	 */
	private static JobRuntimeInformation getQueuedItemsInformationByJobName(
			JobRuntimeInformation jobRuntimeInformation, Item[] jenkinsQueuedItems) {

		if (LOGGER.isLoggable(Level.FINE)) {
			LOGGER.fine("About to determine how many items of the job " + 
					jobRuntimeInformation.getJobName() + " are at the Jenkins queue " +
					"and how many of them are there blocked by the " +
					"selected-same-parameters-concurrent-build-blocker plugin.");
		}

		Integer queuedItems = Integer.valueOf(0);
		Integer queuedItemsBlockedBySameParametersConcurrentBuildBlockerPlugin = Integer.valueOf(0);

		if (jobRuntimeInformation.getJobName() != null && 
				!jobRuntimeInformation.getJobName().trim().isEmpty() && 
				jenkinsQueuedItems != null && jenkinsQueuedItems.length > 0) {
			for (Item queuedItem : jenkinsQueuedItems) {
				String itemJobName = queuedItem.task.getName();
				if (itemJobName != null && !itemJobName.trim().isEmpty()) {
					if (itemJobName.equalsIgnoreCase(jobRuntimeInformation.getJobName())) {
						queuedItems++;
						if (isBlockedBySameParameterValues(queuedItem)) {
							queuedItemsBlockedBySameParametersConcurrentBuildBlockerPlugin++;
						}
					}
				}
			}
		}

		jobRuntimeInformation.setQueuedItems(queuedItems);
		jobRuntimeInformation.setQueuedItemsBlockedBySameParametersConcurrentBuildBlockerPlugin(queuedItemsBlockedBySameParametersConcurrentBuildBlockerPlugin);

		return jobRuntimeInformation;
	}

	/**
	 * @return An array with all the items currently waiting at the Jenkins queue.
	 */
	private static Item[] getQueuedItems() {
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to retrieve the full list with all the items " +
					"currently waiting at the Jenkins queue.");
		}
		Item[] jenkinsQueuedItems = Jenkins.getInstance().getQueue().getItems();
		return jenkinsQueuedItems;
	}

	/**
	 * @param queuedItem
	 * @return true if the item is at the Jenkins queue blocked by the
	 * selected-same-parameters-concurrent-build-blocker plugin, false otherwise.
	 */
	private static boolean isBlockedBySameParameterValues(Item queuedItem){
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine if the queuedItem " + queuedItem +
					" is at the Jenkins queue blocked by the" +
					" selected-same-parameters-concurrent-build-blocker plugin.");
		}

		boolean resp = false;
		
		if (queuedItem.isBlocked()) {

			List<QueueTaskDispatcher> allQueueTaskDispatchers = QueueTaskDispatcher.all();

			QueueTaskDispatcher dispatcher = (QueueTaskDispatcher)CollectionUtils.find(allQueueTaskDispatchers,
					new Predicate() {
				public boolean evaluate(Object object) {
					boolean innerResp = false;
					try {
						if (object instanceof SameParametersQueueTaskDispatcher) {
							// the dispatcher found in the allQueueTaskDispatchers list is the one
							// provided in the selected-same-parameters-concurrent-build-blocker plugin
							innerResp = true;
						}
					} catch(Exception ex) {
						if (LOGGER.isLoggable(Level.WARNING)) {
							LOGGER.warning("ERROR while checking: ".concat(ex.getLocalizedMessage()));
						}
					}
					return innerResp;
				}
			});

			if (dispatcher != null) {
				// The selected-same-parameters-concurrent-build-blocker plugin is installed
				try {
					CauseOfBlockage cause = ((SameParametersQueueTaskDispatcher)dispatcher).canRun(queuedItem);
					if (cause != null) {
						// The queuedItem is blocked by the
						// selected-same-parameters-concurrent-build-blocker plugin
						resp = true;
					}
				} catch (Exception ex) {
					if (LOGGER.isLoggable(Level.WARNING)) {
						LOGGER.warning("ERROR while checking: ".concat(ex.getLocalizedMessage()));
					}
				}
			}
		}

		return resp;
	}

	/**
	 * @param jobName 
	 * @return true if the customqueuepriority.byjobname plugin is checked
	 * in the definition of this job; false otherwise.
	 */
	private static boolean isCustomQueuePriorityByJobNameActivated(String jobName) {
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to determine if the customqueuepriority.byjobname " +
					"plugin is checked in the definition of the job " + jobName);
		}

		boolean activated = false;

		AbstractProject<?, ?> project = getJobDefinitionByJobName(jobName);
		if (project != null) {
			JobProperty<AbstractProject<?, ?>> jobProperty = project.getProperty(CustomPriorityByJobNameParameter.class);
			if (jobProperty != null) {
				CustomPriorityByJobNameParameter customPriorityByJobNameParameter = 
						(CustomPriorityByJobNameParameter)jobProperty;
				activated = customPriorityByJobNameParameter.isPriorityByJobNameEnabled();
			}
		}

		return activated;
	}

	/**
	 * Note that this method runs on an authenticated context with ACL.SYSTEM
	 * privileges.
	 * 
	 * The reason is that our plugin, hooking the QueueTaskDispatcher,
	 * runs anonymously, and in order to call getItem, getItems, getItemByFullName,
	 * etc. the privileges Item.DISCOVER and Item.READ are required. 
	 * 
	 * So before that call, the SecurityContext is changed to a privileged one
	 * and at the end the original SecurityContext is set again.
	 * 
	 * @param jobName 
	 * @return The definition of the given job or null if its not found.
	 */
	private static AbstractProject<?, ?> getJobDefinitionByJobName(String jobName) {
		if (LOGGER.isLoggable(Level.FINER)) {
			LOGGER.finer("About to retrieve the definition of the job " + jobName);
		}

		AbstractProject<?, ?> project = null;
		
	    SecurityContext originalContext = SecurityContextHolder.getContext();
	    try {
	    	SecurityContext systemContext = new NonSerializableSecurityContext(ACL.SYSTEM);
	        SecurityContextHolder.setContext(systemContext);
			project = Jenkins.getInstance().getItemByFullName(jobName, hudson.model.AbstractProject.class);
			if (LOGGER.isLoggable(Level.FINER)) {
				LOGGER.finer("For the job " + jobName + " the definition is project=" + project);
			}
	    } finally {
	        SecurityContextHolder.setContext(originalContext);
	    }
	    
	    return project;
	}
	
}