File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\model\Node.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
5 * Seiji Sogabe, Stephen Connolly
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25 package hudson.model;
26
27 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
28 import hudson.Extension;
29 import hudson.ExtensionPoint;
30 import hudson.FilePath;
31 import hudson.FileSystemProvisioner;
32 import hudson.Launcher;
33 import hudson.model.Descriptor.FormException;
34 import hudson.model.Queue.Task;
35 import hudson.model.labels.LabelAtom;
36 import hudson.model.queue.CauseOfBlockage;
37 import hudson.node_monitors.NodeMonitor;
38 import hudson.remoting.VirtualChannel;
39 import hudson.security.ACL;
40 import hudson.security.AccessControlled;
41 import hudson.security.Permission;
42 import hudson.slaves.ComputerListener;
43 import hudson.slaves.NodeDescriptor;
44 import hudson.slaves.NodeProperty;
45 import hudson.slaves.NodePropertyDescriptor;
46 import hudson.slaves.OfflineCause;
47 import hudson.util.ClockDifference;
48 import hudson.util.DescribableList;
49 import hudson.util.EnumConverter;
50 import hudson.util.TagCloud;
51 import hudson.util.TagCloud.WeightFunction;
52
53 import java.io.IOException;
54 import java.lang.reflect.Type;
55 import java.util.Collections;
56 import java.util.HashSet;
57 import java.util.Set;
58 import java.util.List;
59 import java.util.logging.Logger;
60 import javax.annotation.CheckForNull;
61
62 import jenkins.model.Jenkins;
63 import jenkins.util.io.OnMaster;
64 import net.sf.json.JSONObject;
65 import org.kohsuke.stapler.BindInterceptor;
66 import org.kohsuke.stapler.Stapler;
67 import org.kohsuke.stapler.StaplerRequest;
68 import org.kohsuke.stapler.export.ExportedBean;
69 import org.kohsuke.stapler.export.Exported;
70
71 /**
72 * Base type of Hudson slaves (although in practice, you probably extend {@link Slave} to define a new slave type.)
73 *
74 * <p>
75 * As a special case, {@link Jenkins} extends from here.
76 *
77 * @author Kohsuke Kawaguchi
78 * @see NodeMonitor
79 * @see NodeDescriptor
80 */
81 @ExportedBean
82 public abstract class Node extends AbstractModelObject implements ReconfigurableDescribable<Node>, ExtensionPoint, AccessControlled, OnMaster {
83
84 private static final Logger LOGGER = Logger.getLogger(Node.class.getName());
85
86 /**
87 * Newly copied slaves get this flag set, so that Hudson doesn't try to start this node until its configuration
88 * is saved once.
89 */
90 protected volatile transient boolean holdOffLaunchUntilSave;
91
92 public String getDisplayName() {
93 return getNodeName(); // default implementation
94 }
95
96 public String getSearchUrl() {
97 return "computer/"+getNodeName();
98 }
99
100 public boolean isHoldOffLaunchUntilSave() {
101 return holdOffLaunchUntilSave;
102 }
103
104 /**
105 * Name of this node.
106 *
107 * @return
108 * "" if this is master
109 */
110 @Exported(visibility=999)
111 public abstract String getNodeName();
112
113 /**
114 * When the user clones a {@link Node}, Hudson uses this method to change the node name right after
115 * the cloned {@link Node} object is instantiated.
116 *
117 * <p>
118 * This method is never used for any other purpose, and as such for all practical intents and purposes,
119 * the node name should be treated like immutable.
120 *
121 * @deprecated to indicate that this method isn't really meant to be called by random code.
122 */
123 public abstract void setNodeName(String name);
124
125 /**
126 * Human-readable description of this node.
127 */
128 @Exported
129 public abstract String getNodeDescription();
130
131 /**
132 * Returns a {@link Launcher} for executing programs on this node.
133 *
134 * <p>
135 * The callee must call {@link Launcher#decorateFor(Node)} before returning to complete the decoration.
136 */
137 public abstract Launcher createLauncher(TaskListener listener);
138
139 /**
140 * Returns the number of {@link Executor}s.
141 *
142 * This may be different from <code>getExecutors().size()</code>
143 * because it takes time to adjust the number of executors.
144 */
145 @Exported
146 public abstract int getNumExecutors();
147
148 /**
149 * Returns {@link Mode#EXCLUSIVE} if this node is only available
150 * for those jobs that exclusively specifies this node
151 * as the assigned node.
152 */
153 @Exported
154 public abstract Mode getMode();
155
156 /**
157 * Gets the corresponding {@link Computer} object.
158 *
159 * @return
160 * this method can return null if there's no {@link Computer} object for this node,
161 * such as when this node has no executors at all.
162 */
163 public final @CheckForNull Computer toComputer() {
164 AbstractCIBase ciBase = Jenkins.getInstance();
165 return ciBase.getComputer(this);
166 }
167
168 /**
169 * Gets the current channel, if the node is connected and online, or null.
170 *
171 * This is just a convenience method for {@link Computer#getChannel()} with null check.
172 */
173 public final @CheckForNull VirtualChannel getChannel() {
174 Computer c = toComputer();
175 return c==null ? null : c.getChannel();
176 }
177
178 /**
179 * Creates a new {@link Computer} object that acts as the UI peer of this {@link Node}.
180 * Nobody but {@link Jenkins#updateComputerList()} should call this method.
181 */
182 protected abstract Computer createComputer();
183
184 /**
185 * Let Nodes be aware of the lifecycle of their own {@link Computer}.
186 */
187 @Extension
188 public static class InternalComputerListener extends ComputerListener {
189 @Override
190 public void onOnline(Computer c, TaskListener listener) {
191 Node node = c.getNode();
192
193 // At startup, we need to restore any previously in-effect temp offline cause.
194 // We wait until the computer is started rather than getting the data to it sooner
195 // so that the normal computer start up processing works as expected.
196 if (node.temporaryOfflineCause != null && node.temporaryOfflineCause != c.getOfflineCause()) {
197 c.setTemporarilyOffline(true, node.temporaryOfflineCause);
198 }
199 }
200 }
201
202 private OfflineCause temporaryOfflineCause;
203
204 /**
205 * Enable a {@link Computer} to inform its node when it is taken
206 * temporarily offline.
207 */
208 void setTemporaryOfflineCause(OfflineCause cause) {
209 try {
210 if (temporaryOfflineCause != cause) {
211 temporaryOfflineCause = cause;
212 Jenkins.getInstance().save(); // Gotta be a better way to do this
213 }
214 } catch (java.io.IOException e) {
215 LOGGER.warning("Unable to complete save, temporary offline status will not be persisted: " + e.getMessage());
216 }
217 }
218
219 /**
220 * Return the possibly empty tag cloud for the labels of this node.
221 */
222 public TagCloud<LabelAtom> getLabelCloud() {
223 return new TagCloud<LabelAtom>(getAssignedLabels(),new WeightFunction<LabelAtom>() {
224 public float weight(LabelAtom item) {
225 return item.getTiedJobs().size();
226 }
227 });
228 }
229 /**
230 * Returns the possibly empty set of labels that are assigned to this node,
231 * including the automatic {@link #getSelfLabel() self label}, manually
232 * assigned labels and dynamically assigned labels via the
233 * {@link LabelFinder} extension point.
234 *
235 * This method has a side effect of updating the hudson-wide set of labels
236 * and should be called after events that will change that - e.g. a slave
237 * connecting.
238 */
239 @Exported
240 public Set<LabelAtom> getAssignedLabels() {
241 Set<LabelAtom> r = Label.parse(getLabelString());
242 r.add(getSelfLabel());
243 r.addAll(getDynamicLabels());
244 return Collections.unmodifiableSet(r);
245 }
246
247 /**
248 * Return all the labels assigned dynamically to this node.
249 * This calls all the LabelFinder implementations with the node converts
250 * the results into Labels.
251 * @return HashSet<Label>.
252 */
253 private HashSet<LabelAtom> getDynamicLabels() {
254 HashSet<LabelAtom> result = new HashSet<LabelAtom>();
255 for (LabelFinder labeler : LabelFinder.all()) {
256 // Filter out any bad(null) results from plugins
257 // for compatibility reasons, findLabels may return LabelExpression and not atom.
258 for (Label label : labeler.findLabels(this))
259 if (label instanceof LabelAtom) result.add((LabelAtom)label);
260 }
261 return result;
262 }
263
264
265 /**
266 * Returns the manually configured label for a node. The list of assigned
267 * and dynamically determined labels is available via
268 * {@link #getAssignedLabels()} and includes all labels that have been
269 * manually configured.
270 *
271 * Mainly for form binding.
272 */
273 public abstract String getLabelString();
274
275 /**
276 * Sets the label string for a node. This value will be returned by {@link #getLabelString()}.
277 *
278 * @param labelString
279 * The new label string to use.
280 * @since 1.477
281 */
282 public void setLabelString(String labelString) throws IOException {
283 throw new UnsupportedOperationException();
284 }
285
286 /**
287 * Gets the special label that represents this node itself.
288 */
289 @WithBridgeMethods(Label.class)
290 public LabelAtom getSelfLabel() {
291 return LabelAtom.get(getNodeName());
292 }
293
294 /**
295 * Called by the {@link Queue} to determine whether or not this node can
296 * take the given task. The default checks include whether or not this node
297 * is part of the task's assigned label, whether this node is in
298 * {@link Mode#EXCLUSIVE} mode if it is not in the task's assigned label,
299 * and whether or not any of this node's {@link NodeProperty}s say that the
300 * task cannot be run.
301 *
302 * @since 1.360
303 * @deprecated as of 1.413
304 * Use {@link #canTake(Queue.BuildableItem)}
305 */
306 public CauseOfBlockage canTake(Task task) {
307 return null;
308 }
309
310 /**
311 * Called by the {@link Queue} to determine whether or not this node can
312 * take the given task. The default checks include whether or not this node
313 * is part of the task's assigned label, whether this node is in
314 * {@link Mode#EXCLUSIVE} mode if it is not in the task's assigned label,
315 * and whether or not any of this node's {@link NodeProperty}s say that the
316 * task cannot be run.
317 *
318 * @since 1.413
319 */
320 public CauseOfBlockage canTake(Queue.BuildableItem item) {
321 Label l = item.getAssignedLabel();
322 if(l!=null && !l.contains(this))
323 return CauseOfBlockage.fromMessage(Messages._Node_LabelMissing(getNodeName(),l)); // the task needs to be executed on label that this node doesn't have.
324
325 if(l==null && getMode()== Mode.EXCLUSIVE)
326 return CauseOfBlockage.fromMessage(Messages._Node_BecauseNodeIsReserved(getNodeName())); // this node is reserved for tasks that are tied to it
327
328 // Check each NodeProperty to see whether they object to this node
329 // taking the task
330 for (NodeProperty prop: getNodeProperties()) {
331 CauseOfBlockage c = prop.canTake(item);
332 if (c!=null) return c;
333 }
334
335 // Looks like we can take the task
336 return null;
337 }
338
339 /**
340 * Returns a "workspace" directory for the given {@link TopLevelItem}.
341 *
342 * <p>
343 * Workspace directory is usually used for keeping out the checked out
344 * source code, but it can be used for anything.
345 *
346 * @return
347 * null if this node is not connected hence the path is not available
348 */
349 // TODO: should this be modified now that getWorkspace is moved from AbstractProject to AbstractBuild?
350 public abstract FilePath getWorkspaceFor(TopLevelItem item);
351
352 /**
353 * Gets the root directory of this node.
354 *
355 * <p>
356 * Hudson always owns a directory on every node. This method
357 * returns that.
358 *
359 * @return
360 * null if the node is offline and hence the {@link FilePath}
361 * object is not available.
362 */
363 public abstract FilePath getRootPath();
364
365 /**
366 * Gets the {@link FilePath} on this node.
367 */
368 public @CheckForNull FilePath createPath(String absolutePath) {
369 VirtualChannel ch = getChannel();
370 if(ch==null) return null; // offline
371 return new FilePath(ch,absolutePath);
372 }
373
374 public FileSystemProvisioner getFileSystemProvisioner() {
375 // TODO: make this configurable or auto-detectable or something else
376 return FileSystemProvisioner.DEFAULT;
377 }
378
379 /**
380 * Gets the {@link NodeProperty} instances configured for this {@link Node}.
381 */
382 public abstract DescribableList<NodeProperty<?>, NodePropertyDescriptor> getNodeProperties();
383
384 // used in the Jelly script to expose descriptors
385 public List<NodePropertyDescriptor> getNodePropertyDescriptors() {
386 return NodeProperty.for_(this);
387 }
388
389 public ACL getACL() {
390 return Jenkins.getInstance().getAuthorizationStrategy().getACL(this);
391 }
392
393 public final void checkPermission(Permission permission) {
394 getACL().checkPermission(permission);
395 }
396
397 public final boolean hasPermission(Permission permission) {
398 return getACL().hasPermission(permission);
399 }
400
401 public Node reconfigure(final StaplerRequest req, JSONObject form) throws FormException {
402 if (form==null) return null;
403
404 final JSONObject jsonForProperties = form.optJSONObject("nodeProperties");
405 BindInterceptor old = req.setBindListener(new BindInterceptor() {
406 @Override
407 public Object onConvert(Type targetType, Class targetTypeErasure, Object jsonSource) {
408 if (jsonForProperties!=jsonSource) return DEFAULT;
409
410 try {
411 DescribableList<NodeProperty<?>, NodePropertyDescriptor> tmp = new DescribableList<NodeProperty<?>, NodePropertyDescriptor>(Saveable.NOOP,getNodeProperties().toList());
412 tmp.rebuild(req, jsonForProperties, NodeProperty.all());
413 return tmp.toList();
414 } catch (FormException e) {
415 throw new IllegalArgumentException(e);
416 } catch (IOException e) {
417 throw new IllegalArgumentException(e);
418 }
419 }
420 });
421
422 try {
423 return getDescriptor().newInstance(req, form);
424 } finally {
425 req.setBindListener(old);
426 }
427 }
428
429 public abstract NodeDescriptor getDescriptor();
430
431 /**
432 * Estimates the clock difference with this slave.
433 *
434 * @return
435 * always non-null.
436 * @throws InterruptedException
437 * if the operation is aborted.
438 */
439 public abstract ClockDifference getClockDifference() throws IOException, InterruptedException;
440
441 /**
442 * Constants that control how Hudson allocates jobs to slaves.
443 */
444 public enum Mode {
445 NORMAL(Messages.Node_Mode_NORMAL()),
446 EXCLUSIVE(Messages.Node_Mode_EXCLUSIVE());
447
448 private final String description;
449
450 public String getDescription() {
451 return description;
452 }
453
454 public String getName() {
455 return name();
456 }
457
458 Mode(String description) {
459 this.description = description;
460 }
461
462 static {
463 Stapler.CONVERT_UTILS.register(new EnumConverter(), Mode.class);
464 }
465 }
466
467 }
468