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
- Bla
- Bla
- Bla
- Bla
- 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;
}
}