001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.yarn.api.records;
020
021import java.io.Serializable;
022
023import org.apache.hadoop.classification.InterfaceAudience.Public;
024import org.apache.hadoop.classification.InterfaceStability.Evolving;
025import org.apache.hadoop.classification.InterfaceStability.Stable;
026import org.apache.hadoop.yarn.api.ApplicationMasterProtocol;
027import org.apache.hadoop.yarn.util.Records;
028
029/**
030 * {@code ResourceRequest} represents the request made
031 * by an application to the {@code ResourceManager}
032 * to obtain various {@code Container} allocations.
033 * <p>
034 * It includes:
035 * <ul>
036 *   <li>{@link Priority} of the request.</li>
037 *   <li>
038 *     The <em>name</em> of the machine or rack on which the allocation is
039 *     desired. A special value of <em>*</em> signifies that
040 *     <em>any</em> host/rack is acceptable to the application.
041 *   </li>
042 *   <li>{@link Resource} required for each request.</li>
043 *   <li>
044 *     Number of containers, of above specifications, which are required
045 *     by the application.
046 *   </li>
047 *   <li>
048 *     A boolean <em>relaxLocality</em> flag, defaulting to {@code true},
049 *     which tells the {@code ResourceManager} if the application wants
050 *     locality to be loose (i.e. allows fall-through to rack or <em>any</em>)
051 *     or strict (i.e. specify hard constraint on resource allocation).
052 *   </li>
053 * </ul>
054 * 
055 * @see Resource
056 * @see ApplicationMasterProtocol#allocate(org.apache.hadoop.yarn.api.protocolrecords.AllocateRequest)
057 */
058@Public
059@Stable
060public abstract class ResourceRequest implements Comparable<ResourceRequest> {
061
062  @Public
063  @Stable
064  public static ResourceRequest newInstance(Priority priority, String hostName,
065      Resource capability, int numContainers) {
066    return newInstance(priority, hostName, capability, numContainers, true);
067  }
068
069  @Public
070  @Stable
071  public static ResourceRequest newInstance(Priority priority, String hostName,
072      Resource capability, int numContainers, boolean relaxLocality) {
073    return newInstance(priority, hostName, capability, numContainers,
074        relaxLocality, null);
075  }
076  
077  @Public
078  @Stable
079  public static ResourceRequest newInstance(Priority priority, String hostName,
080      Resource capability, int numContainers, boolean relaxLocality,
081      String labelExpression) {
082    return newInstance(priority, hostName, capability, numContainers,
083        relaxLocality, labelExpression, ExecutionTypeRequest.newInstance());
084  }
085
086  @Public
087  @Evolving
088  public static ResourceRequest newInstance(Priority priority, String hostName,
089      Resource capability, int numContainers, boolean relaxLocality, String
090      labelExpression, ExecutionTypeRequest executionTypeRequest) {
091    ResourceRequest request = Records.newRecord(ResourceRequest.class);
092    request.setPriority(priority);
093    request.setResourceName(hostName);
094    request.setCapability(capability);
095    request.setNumContainers(numContainers);
096    request.setRelaxLocality(relaxLocality);
097    request.setNodeLabelExpression(labelExpression);
098    request.setExecutionTypeRequest(executionTypeRequest);
099    return request;
100  }
101
102  @Public
103  @Stable
104  public static class ResourceRequestComparator implements
105      java.util.Comparator<ResourceRequest>, Serializable {
106
107    private static final long serialVersionUID = 1L;
108
109    @Override
110    public int compare(ResourceRequest r1, ResourceRequest r2) {
111
112      // Compare priority, host and capability
113      int ret = r1.getPriority().compareTo(r2.getPriority());
114      if (ret == 0) {
115        ret = Long.compare(
116            r1.getAllocationRequestId(), r2.getAllocationRequestId());
117      }
118      if (ret == 0) {
119        String h1 = r1.getResourceName();
120        String h2 = r2.getResourceName();
121        ret = h1.compareTo(h2);
122      }
123      if (ret == 0) {
124        ret = r1.getExecutionTypeRequest()
125            .compareTo(r2.getExecutionTypeRequest());
126      }
127      if (ret == 0) {
128        ret = r1.getCapability().compareTo(r2.getCapability());
129      }
130      return ret;
131    }
132  }
133
134  /**
135   * The constant string representing no locality.
136   * It should be used by all references that want to pass an arbitrary host
137   * name in.
138   */
139  public static final String ANY = "*";
140
141  /**
142   * Check whether the given <em>host/rack</em> string represents an arbitrary
143   * host name.
144   *
145   * @param hostName <em>host/rack</em> on which the allocation is desired
146   * @return whether the given <em>host/rack</em> string represents an arbitrary
147   * host name
148   */
149  @Public
150  @Stable
151  public static boolean isAnyLocation(String hostName) {
152    return ANY.equals(hostName);
153  }
154
155  /**
156   * Get the <code>Priority</code> of the request.
157   * @return <code>Priority</code> of the request
158   */
159  @Public
160  @Stable
161  public abstract Priority getPriority();
162
163  /**
164   * Set the <code>Priority</code> of the request
165   * @param priority <code>Priority</code> of the request
166   */
167  @Public
168  @Stable
169  public abstract void setPriority(Priority priority);
170  
171  /**
172   * Get the resource (e.g. <em>host/rack</em>) on which the allocation 
173   * is desired.
174   * 
175   * A special value of <em>*</em> signifies that <em>any</em> resource 
176   * (host/rack) is acceptable.
177   * 
178   * @return resource (e.g. <em>host/rack</em>) on which the allocation 
179   *                  is desired
180   */
181  @Public
182  @Stable
183  public abstract String getResourceName();
184
185  /**
186   * Set the resource name (e.g. <em>host/rack</em>) on which the allocation 
187   * is desired.
188   * 
189   * A special value of <em>*</em> signifies that <em>any</em> resource name
190   * (e.g. host/rack) is acceptable. 
191   * 
192   * @param resourceName (e.g. <em>host/rack</em>) on which the 
193   *                     allocation is desired
194   */
195  @Public
196  @Stable
197  public abstract void setResourceName(String resourceName);
198  
199  /**
200   * Get the <code>Resource</code> capability of the request.
201   * @return <code>Resource</code> capability of the request
202   */
203  @Public
204  @Stable
205  public abstract Resource getCapability();
206  
207  /**
208   * Set the <code>Resource</code> capability of the request
209   * @param capability <code>Resource</code> capability of the request
210   */
211  @Public
212  @Stable
213  public abstract void setCapability(Resource capability);
214
215  /**
216   * Get the number of containers required with the given specifications.
217   * @return number of containers required with the given specifications
218   */
219  @Public
220  @Stable
221  public abstract int getNumContainers();
222  
223  /**
224   * Set the number of containers required with the given specifications
225   * @param numContainers number of containers required with the given 
226   *                      specifications
227   */
228  @Public
229  @Stable
230  public abstract void setNumContainers(int numContainers);
231
232  /**
233   * Get whether locality relaxation is enabled with this
234   * <code>ResourceRequest</code>. Defaults to true.
235   * 
236   * @return whether locality relaxation is enabled with this
237   * <code>ResourceRequest</code>.
238   */
239  @Public
240  @Stable
241  public abstract boolean getRelaxLocality();
242
243  /**
244   * Set the <code>ExecutionTypeRequest</code> of the requested container.
245   *
246   * @param execSpec
247   *          ExecutionTypeRequest of the requested container
248   */
249  @Public
250  @Evolving
251  public void setExecutionTypeRequest(ExecutionTypeRequest execSpec) {
252    throw new UnsupportedOperationException();
253  }
254
255  /**
256   * Get whether locality relaxation is enabled with this
257   * <code>ResourceRequest</code>. Defaults to true.
258   *
259   * @return whether locality relaxation is enabled with this
260   * <code>ResourceRequest</code>.
261   */
262  @Public
263  @Evolving
264  public ExecutionTypeRequest getExecutionTypeRequest() {
265    throw new UnsupportedOperationException();
266  }
267
268  /**
269   * <p>For a request at a network hierarchy level, set whether locality can be relaxed
270   * to that level and beyond.<p>
271   * 
272   * <p>If the flag is off on a rack-level <code>ResourceRequest</code>,
273   * containers at that request's priority will not be assigned to nodes on that
274   * request's rack unless requests specifically for those nodes have also been
275   * submitted.<p>
276   * 
277   * <p>If the flag is off on an {@link ResourceRequest#ANY}-level
278   * <code>ResourceRequest</code>, containers at that request's priority will
279   * only be assigned on racks for which specific requests have also been
280   * submitted.<p>
281   * 
282   * <p>For example, to request a container strictly on a specific node, the
283   * corresponding rack-level and any-level requests should have locality
284   * relaxation set to false.  Similarly, to request a container strictly on a
285   * specific rack, the corresponding any-level request should have locality
286   * relaxation set to false.<p>
287   * 
288   * @param relaxLocality whether locality relaxation is enabled with this
289   * <code>ResourceRequest</code>.
290   */
291  @Public
292  @Stable
293  public abstract void setRelaxLocality(boolean relaxLocality);
294  
295  /**
296   * Get node-label-expression for this Resource Request. If this is set, all
297   * containers allocated to satisfy this resource-request will be only on those
298   * nodes that satisfy this node-label-expression.
299   *  
300   * Please note that node label expression now can only take effect when the
301   * resource request has resourceName = ANY
302   * 
303   * @return node-label-expression
304   */
305  @Public
306  @Evolving
307  public abstract String getNodeLabelExpression();
308  
309  /**
310   * Set node label expression of this resource request. Now only support
311   * specifying a single node label. In the future we will support more complex
312   * node label expression specification like {@code AND(&&), OR(||)}, etc.
313   * 
314   * Any please note that node label expression now can only take effect when
315   * the resource request has resourceName = ANY
316   * 
317   * @param nodelabelExpression
318   *          node-label-expression of this ResourceRequest
319   */
320  @Public
321  @Evolving
322  public abstract void setNodeLabelExpression(String nodelabelExpression);
323
324  /**
325   * Get the optional <em>ID</em> corresponding to this allocation request. This
326   * ID is an identifier for different {@code ResourceRequest}s from the <b>same
327   * application</b>. The allocated {@code Container}(s) received as part of the
328   * {@code AllocateResponse} response will have the ID corresponding to the
329   * original {@code ResourceRequest} for which the RM made the allocation.
330   * <p>
331   * The scheduler may return multiple {@code AllocateResponse}s corresponding
332   * to the same ID as and when scheduler allocates {@code Container}(s).
333   * <b>Applications</b> can continue to completely ignore the returned ID in
334   * the response and use the allocation for any of their outstanding requests.
335   * <p>
336   * If one wishes to replace an entire {@code ResourceRequest} corresponding to
337   * a specific ID, they can simply cancel the corresponding {@code
338   * ResourceRequest} and submit a new one afresh.
339   *
340   * @return the <em>ID</em> corresponding to this allocation request.
341   */
342  @Public
343  @Evolving
344  public long getAllocationRequestId() {
345    throw new UnsupportedOperationException();
346  }
347
348  /**
349   * Set the optional <em>ID</em> corresponding to this allocation request. This
350   * ID is an identifier for different {@code ResourceRequest}s from the <b>same
351   * application</b>. The allocated {@code Container}(s) received as part of the
352   * {@code AllocateResponse} response will have the ID corresponding to the
353   * original {@code ResourceRequest} for which the RM made the allocation.
354   * <p>
355   * The scheduler may return multiple {@code AllocateResponse}s corresponding
356   * to the same ID as and when scheduler allocates {@code Container}(s).
357   * <b>Applications</b> can continue to completely ignore the returned ID in
358   * the response and use the allocation for any of their outstanding requests.
359   * <p>
360   * If one wishes to replace an entire {@code ResourceRequest} corresponding to
361   * a specific ID, they can simply cancel the corresponding {@code
362   * ResourceRequest} and submit a new one afresh.
363   * <p>
364   * If the ID is not set, scheduler will continue to work as previously and all
365   * allocated {@code Container}(s) will have the default ID, -1.
366   *
367   * @param allocationRequestID the <em>ID</em> corresponding to this allocation
368   *                            request.
369   */
370  @Public
371  @Evolving
372  public void setAllocationRequestId(long allocationRequestID) {
373    throw new UnsupportedOperationException();
374  }
375  
376  @Override
377  public int hashCode() {
378    final int prime = 2153;
379    int result = 2459;
380    Resource capability = getCapability();
381    String hostName = getResourceName();
382    Priority priority = getPriority();
383    result =
384        prime * result + ((capability == null) ? 0 : capability.hashCode());
385    result = prime * result + ((hostName == null) ? 0 : hostName.hashCode());
386    result = prime * result + getNumContainers();
387    result = prime * result + ((priority == null) ? 0 : priority.hashCode());
388    result = prime * result + Long.valueOf(getAllocationRequestId()).hashCode();
389    return result;
390  }
391
392  @Override
393  public boolean equals(Object obj) {
394    if (this == obj)
395      return true;
396    if (obj == null)
397      return false;
398    if (getClass() != obj.getClass())
399      return false;
400    ResourceRequest other = (ResourceRequest) obj;
401    Resource capability = getCapability();
402    if (capability == null) {
403      if (other.getCapability() != null)
404        return false;
405    } else if (!capability.equals(other.getCapability()))
406      return false;
407    String hostName = getResourceName();
408    if (hostName == null) {
409      if (other.getResourceName() != null)
410        return false;
411    } else if (!hostName.equals(other.getResourceName()))
412      return false;
413    if (getNumContainers() != other.getNumContainers())
414      return false;
415    Priority priority = getPriority();
416    if (priority == null) {
417      if (other.getPriority() != null)
418        return false;
419    } else if (!priority.equals(other.getPriority()))
420      return false;
421    ExecutionTypeRequest execTypeRequest = getExecutionTypeRequest();
422    if (execTypeRequest == null) {
423      if (other.getExecutionTypeRequest() != null) {
424        return false;
425      }
426    } else if (!execTypeRequest.getExecutionType()
427        .equals(other.getExecutionTypeRequest().getExecutionType())) {
428      return false;
429    }
430
431    if (getAllocationRequestId() != other.getAllocationRequestId()) {
432      return false;
433    }
434
435    if (getNodeLabelExpression() == null) {
436      if (other.getNodeLabelExpression() != null) {
437        return false;
438      }
439    } else {
440      // do normalize on label expression before compare
441      String label1 = getNodeLabelExpression().replaceAll("[\\t ]", "");
442      String label2 =
443          other.getNodeLabelExpression() == null ? null : other
444              .getNodeLabelExpression().replaceAll("[\\t ]", "");
445      if (!label1.equals(label2)) {
446        return false;
447      }
448    }
449    return true;
450  }
451
452  @Override
453  public int compareTo(ResourceRequest other) {
454    int priorityComparison = this.getPriority().compareTo(other.getPriority());
455    if (priorityComparison == 0) {
456      int hostNameComparison =
457          this.getResourceName().compareTo(other.getResourceName());
458      if (hostNameComparison == 0) {
459        int execTypeReqComparison = this.getExecutionTypeRequest()
460            .compareTo(other.getExecutionTypeRequest());
461        if (execTypeReqComparison == 0) {
462          int capabilityComparison =
463              this.getCapability().compareTo(other.getCapability());
464          if (capabilityComparison == 0) {
465            int numContainerComparison =
466                this.getNumContainers() - other.getNumContainers();
467            if (numContainerComparison == 0) {
468              return Long.compare(getAllocationRequestId(),
469                  other.getAllocationRequestId());
470            } else {
471              return numContainerComparison;
472            }
473          } else {
474            return capabilityComparison;
475          }
476        } else {
477          return execTypeReqComparison;
478        }
479      } else {
480        return hostNameComparison;
481      }
482    } else {
483      return priorityComparison;
484    }
485  }
486}