diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/resource/PlacementConstraint.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/resource/PlacementConstraint.java index 9bb17f435e6..0fe8273e6d7 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/resource/PlacementConstraint.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/resource/PlacementConstraint.java @@ -130,6 +130,11 @@ public int hashCode() { public PlacementConstraint build() { return new PlacementConstraint(this); } + + @Override + public String toString() { + return super.toString(); + } } static final String NODE_SCOPE = "node"; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml index 17723bcff10..2bd31d0a757 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services-api/src/main/resources/definition/YARN-Simplified-V1-API-Layer-For-Services.yaml @@ -229,9 +229,6 @@ definitions: type: integer format: int64 description: Life time (in seconds) of the service from the time it reaches the STARTED state (after which it is automatically destroyed by YARN). For unlimited lifetime do not set a lifetime value. - placement_policy: - description: (TBD) Advanced scheduling and placement policies. If not specified, it defaults to the default placement policy of the service owner. The design of placement policies are in the works. It is not very clear at this point, how policies in conjunction with labels be exposed to service owners. This is a placeholder for now. The advanced structure of this attribute will be determined by YARN-4902. - $ref: '#/definitions/PlacementPolicy' components: description: Components of a service. type: array @@ -256,7 +253,7 @@ definitions: $ref: '#/definitions/KerberosPrincipal' ResourceInformation: description: - ResourceInformation determines unit/value of resource types in addition to memory and vcores. It will be part of Resource object + ResourceInformation determines unit/value of resource types in addition to memory and vcores. It will be part of Resource object. properties: value: type: integer @@ -264,8 +261,7 @@ definitions: description: Integer value of the resource. unit: type: string - description: - Unit of the resource, acceptable values are: p/n/u/m/k/M/G/T/P/Ki/Mi/Gi/Ti/Pi. By default it is empty means no unit + description: Unit of the resource, acceptable values are - p/n/u/m/k/M/G/T/P/Ki/Mi/Gi/Ti/Pi. By default it is empty means no unit. Resource: description: Resource determines the amount of resources (vcores, memory, network, etc.) usable by a container. This field determines the resource to be applied for all the containers of a component or service. The resource specified at the service (or global) level can be overriden at the component level. Only one of profile OR cpu & memory are expected. It raises a validation exception otherwise. @@ -286,11 +282,119 @@ definitions: $ref: '#/definitions/ResourceInformation' description: Map of resource name to ResourceInformation PlacementPolicy: - description: Placement policy of an instance of a service. This feature is in the works in YARN-6592. + description: Advanced placement policy of the components of a service. + required: + - constraints + properties: + constraints: + description: Placement constraint details. + type: array + items: + $ref: '#/definitions/PlacementConstraint' + expressions: + description: The logical expressions binding multiple constraints of this placement policy. + type: array + items: + $ref: '#/definitions/PlacementConstraintExpression' + PlacementConstraint: + description: Placement constraint details. + required: + - name + - type + - scope + properties: + name: + description: A unique name associated to this constraint which is required if composite constraints are specified using placement policy expressions. + type: string + example: C1 + type: + description: The type of placement. + $ref: '#/definitions/PlacementType' + scope: + description: The scope of placement. + $ref: '#/definitions/PlacementScope' + tags: + description: The name of the components that this component's placement policy is depending upon are added as tags. So for affinity say, this component's containers are requesting to be placed on hosts where containers of another component are running on. Tags can also contain the name of this component, in which case it implies that for anti-affinity say, no more than one container of this component can be placed on a host. Similarly, for cardinality, it would mean that containers of this component is requesting to be placed on hosts where at least minCardinality but no more than maxCardinality containers are running. + type: array + items: + type: string + node_attributes: + description: Node attributes are a set of key:value(s) pairs associated with nodes. + type: object + additionalProperties: + type: array + items: + type: string + min_cardinality: + type: integer + format: int64 + description: When placement type is cardinality, the minimum number of containers of the depending component that a host should have, where containers of this component can be allocated on. + example: 2 + max_cardinality: + type: integer + format: int64 + description: When placement type is cardinality, the maximum number of containers of the depending component that a host should have, where containers of this component can be allocated on. + example: 3 + PlacementType: + description: The type of placement - affinity/anti-affinity/affinity-with-cardinality with containers of another component or containers of the same component (self). properties: - label: + type: type: string - description: Assigns a service to a named partition of the cluster where the service desires to run (optional). If not specified all services are submitted to a default label of the service owner. One or more labels can be setup for each service owner account with required constraints like no-preemption, sla-99999, preemption-ok, etc. + enum: + - AFFINITY + - ANTI_AFFINITY + - AFFINITY_WITH_CARDINALITY + PlacementScope: + description: The scope of placement for the containers of a component. + properties: + type: + type: string + enum: + - NODE + - RACK + PlacementConstraintExpression: + description: A logical expression binding multiple placement policy constraints into a composite constraint. + required: + - name + - condition + - elements + properties: + name: + type: string + description: A unique name of this expression, such that it can be used as an element in subsequent expressions. + example: E1 + condition: + type: string + description: The logical condition by which multiple placement constraints are joined to create a composite constraint. + enum: + - AND + - OR + - DELAYED_OR + elements: + type: array + description: A list of placement constraint names to create a composite constraint. The name of an already defined placement constraint expression can also be used to create a nested composite constraint. + items: + $ref: '#/definitions/PlacementConstraintElement' + PlacementConstraintElement: + description: Placement constraint element with few additional properties required for some types of expressions, e.g. DELAYED_OR. The name refers to a constraint name or the name of an already defined constraint expression to help create a nested composite constraint. + required: + - name + properties: + name: + type: string + description: A unique name of this element, such that it can be used as an element in subsequent expressions. + example: E1 + delay_value: + type: integer + format: int64 + description: When placement condition is DELAYED_OR then delay value should be provided. + example: 2000 + delay_unit: + type: string + description: When placement condition is DELAYED_OR then the unit of delay value should be provided. The two units supported today are milliseconds (MS) and opportunities (OP). MS specifies the number of milliseconds that the scheduler will try to place a container as per constraint specification before giving up and moving on to the next constraint specification. OP specifies the number of missed opportunities after which the scheduler should give up and move to the next constraint. + enum: + - MS + - OP Artifact: description: Artifact of a service component. If not specified, component will just run the bare launch command and no artifact will be localized. required: @@ -342,11 +446,16 @@ definitions: type: integer format: int64 description: Number of containers for this component (optional). If not specified, the service level global number_of_containers takes effect. + containers: + type: array + description: Containers of a started component. Specifying a value for this attribute for the POST payload raises a validation error. This blob is available only in the GET response of a started service. + items: + $ref: '#/definitions/Container' run_privileged_container: type: boolean description: Run all containers of this component in privileged mode (YARN-4262). placement_policy: - description: Advanced scheduling and placement policies for all containers of this component (optional). If not specified, the service level placement_policy takes effect. Refer to the description at the global level for more details. + description: Advanced scheduling and placement policies for all containers of this component. $ref: '#/definitions/PlacementPolicy' configuration: description: Config properties for this component. @@ -380,7 +489,7 @@ definitions: properties: properties: type: object - description: A blob of key-value pairs for configuring the YARN service AM + description: A blob of key-value pairs for configuring the YARN service AM. additionalProperties: type: string env: @@ -405,7 +514,6 @@ definitions: - JSON - YAML - TEMPLATE - - ENV - HADOOP_XML dest_file: type: string @@ -416,6 +524,8 @@ definitions: properties: type: object description: A blob of key value pairs that will be dumped in the dest_file in the format as specified in type. If src_file is specified, src_file content are dumped in the dest_file and these properties will overwrite, if any, existing properties in src_file or be added as new properties in src_file. + additionalProperties: + type: string Container: description: An instance of a running service container. properties: @@ -464,6 +574,7 @@ definitions: - STABLE - STOPPED - FAILED + - FLEX ContainerState: description: The current state of the container of a service. properties: diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ServiceScheduler.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ServiceScheduler.java index 63331977b2f..93d614d2e54 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ServiceScheduler.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/ServiceScheduler.java @@ -46,6 +46,7 @@ import org.apache.hadoop.yarn.api.records.ContainerStatus; import org.apache.hadoop.yarn.api.records.FinalApplicationStatus; import org.apache.hadoop.yarn.api.records.NodeReport; +import org.apache.hadoop.yarn.api.records.RejectedSchedulingRequest; import org.apache.hadoop.yarn.api.records.Resource; import org.apache.hadoop.yarn.api.records.UpdatedContainer; import org.apache.hadoop.yarn.client.api.AMRMClient; @@ -282,6 +283,9 @@ public void serviceStop() throws Exception { public void serviceStart() throws Exception { super.serviceStart(); InetSocketAddress bindAddress = context.clientAMService.getBindAddress(); + // When yarn.resourcemanager.placement-constraints.handler is set to + // placement-processor then constraints need to be added during + // registerApplicationMaster. RegisterApplicationMasterResponse response = amRMClient .registerApplicationMaster(bindAddress.getHostName(), bindAddress.getPort(), "N/A"); @@ -662,8 +666,14 @@ public void onContainersUpdated(List containers) { @Override public void onError(Throwable e) { LOG.error("Error in AMRMClient callback handler ", e); } - } + @Override + public void onRequestsRejected( + List rejectedSchedulingRequests) { + LOG.error("Error in AMRMClient callback handler. Following scheduling " + + "requests were rejected: {}", rejectedSchedulingRequests); + } + } private class NMClientCallback extends NMClientAsync.AbstractCallbackHandler { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Component.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Component.java index ce0e0cfde8c..033a557432e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Component.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Component.java @@ -269,16 +269,14 @@ public void setRunPrivilegedContainer(Boolean runPrivilegedContainer) { /** * Advanced scheduling and placement policies for all containers of this - * component (optional). If not specified, the service level placement_policy - * takes effect. Refer to the description at the global level for more - * details. + * component. **/ public Component placementPolicy(PlacementPolicy placementPolicy) { this.placementPolicy = placementPolicy; return this; } - @ApiModelProperty(example = "null", value = "Advanced scheduling and placement policies for all containers of this component (optional). If not specified, the service level placement_policy takes effect. Refer to the description at the global level for more details.") + @ApiModelProperty(example = "null", value = "Advanced scheduling and placement policies for all containers of this component.") public PlacementPolicy getPlacementPolicy() { return placementPolicy; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ConfigFile.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ConfigFile.java index 984e6f7cfb4..d3b18bc3d2b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ConfigFile.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ConfigFile.java @@ -55,7 +55,7 @@ @XmlEnum public enum TypeEnum { XML("XML"), PROPERTIES("PROPERTIES"), JSON("JSON"), YAML("YAML"), TEMPLATE( - "TEMPLATE"), HADOOP_XML("HADOOP_XML"),; + "TEMPLATE"), HADOOP_XML("HADOOP_XML"); private String value; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraint.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraint.java new file mode 100644 index 00000000000..2d823fde3b6 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraint.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.yarn.service.api.records; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlElement; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * Placement constraint details. + **/ +@InterfaceAudience.Public +@InterfaceStability.Unstable +@ApiModel(description = "Placement constraint details.") +@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaClientCodegen", date = "2018-02-16T10:20:12.927-07:00") +public class PlacementConstraint implements Serializable { + private static final long serialVersionUID = 1518017165676511762L; + + private String name = null; + private PlacementType type = null; + private PlacementScope scope = null; + private List tags = new ArrayList<>(); + @JsonProperty("node_attributes") + @XmlElement(name = "node_attributes") + private Map> nodeAttributes = new HashMap<>(); + @JsonProperty("min_cardinality") + @XmlElement(name = "min_cardinality") + private Long minCardinality = null; + @JsonProperty("max_cardinality") + @XmlElement(name = "max_cardinality") + private Long maxCardinality = null; + + /** + * A unique name associated to this constraint which is required if composite + * constraints are specified using placement policy expressions. + **/ + public PlacementConstraint name(String name) { + this.name = name; + return this; + } + + @ApiModelProperty(example = "C1", required = true) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * The type of placement. + **/ + public PlacementConstraint type(PlacementType type) { + this.type = type; + return this; + } + + @ApiModelProperty(example = "null", required = true) + @JsonProperty("type") + public PlacementType getType() { + return type; + } + + public void setType(PlacementType type) { + this.type = type; + } + + /** + * The scope of placement. + **/ + public PlacementConstraint scope(PlacementScope scope) { + this.scope = scope; + return this; + } + + @ApiModelProperty(example = "null", required = true) + @JsonProperty("scope") + public PlacementScope getScope() { + return scope; + } + + public void setScope(PlacementScope scope) { + this.scope = scope; + } + + /** + * The name of the components that this component's placement policy is + * depending upon are added as tags. So for affinity say, this component's + * containers are requesting to be placed on hosts where containers of another + * component are running on. Tags can also contain the name of this component, + * in which case it implies that for anti-affinity say, no more than one + * container of this component can be placed on a host. Similarly, for + * cardinality, it would mean that containers of this component is requesting + * to be placed on hosts where at least minCardinality but no more than + * maxCardinality containers are running. + **/ + public PlacementConstraint tags(List tags) { + this.tags = tags; + return this; + } + + @ApiModelProperty(example = "[\"hbase-regionserver\"]") + @JsonProperty("tags") + public List getTags() { + return tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + /** + * Node attributes are a set of key:value(s) pairs associated with nodes. + */ + public PlacementConstraint nodeAttributes( + Map> nodeAttributes) { + this.nodeAttributes = nodeAttributes; + return this; + } + + @ApiModelProperty(example = "\"JavaVersion\":[\"1.7\", \"1.8\"]") + public Map> getNodeAttributes() { + return nodeAttributes; + } + + public void setNodeAttributes(Map> nodeAttributes) { + this.nodeAttributes = nodeAttributes; + } + + /** + * When placement type is cardinality, the minimum number of containers of the + * depending component that a host should have, where containers of this + * component can be allocated on. + **/ + public PlacementConstraint minCardinality(Long minCardinality) { + this.minCardinality = minCardinality; + return this; + } + + @ApiModelProperty(example = "2") + public Long getMinCardinality() { + return minCardinality; + } + + public void setMinCardinality(Long minCardinality) { + this.minCardinality = minCardinality; + } + + /** + * When placement type is cardinality, the maximum number of containers of the + * depending component that a host should have, where containers of this + * component can be allocated on. + **/ + public PlacementConstraint maxCardinality(Long maxCardinality) { + this.maxCardinality = maxCardinality; + return this; + } + + @ApiModelProperty(example = "3") + public Long getMaxCardinality() { + return maxCardinality; + } + + public void setMaxCardinality(Long maxCardinality) { + this.maxCardinality = maxCardinality; + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PlacementConstraint placementConstraint = (PlacementConstraint) o; + return Objects.equals(this.name, placementConstraint.name) + && Objects.equals(this.type, placementConstraint.type) + && Objects.equals(this.scope, placementConstraint.scope) + && Objects.equals(this.tags, placementConstraint.tags) + && Objects.equals(this.nodeAttributes, + placementConstraint.nodeAttributes) + && Objects.equals(this.minCardinality, + placementConstraint.minCardinality) + && Objects.equals(this.maxCardinality, + placementConstraint.maxCardinality); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, scope, tags, nodeAttributes, minCardinality, + maxCardinality); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PlacementConstraint {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" scope: ").append(toIndentedString(scope)).append("\n"); + sb.append(" tags: ").append(toIndentedString(tags)).append("\n"); + sb.append(" nodeAttributes: ").append(toIndentedString(nodeAttributes)) + .append("\n"); + sb.append(" minCardinality: ").append(toIndentedString(minCardinality)) + .append("\n"); + sb.append(" maxCardinality: ").append(toIndentedString(maxCardinality)) + .append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraintElement.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraintElement.java new file mode 100644 index 00000000000..e4e7ecdf05f --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraintElement.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.yarn.service.api.records; + +import java.io.Serializable; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlType; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +import org.apache.hadoop.yarn.api.resource.PlacementConstraint.TimedPlacementConstraint; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * Placement constraint element with few additional properties required for some + * types of expressions, e.g. DELAYED_OR. The name refers to a constraint name + * or the name of an already defined constraint expression to help create a + * nested composite constraint. + **/ +@InterfaceAudience.Public +@InterfaceStability.Unstable +@ApiModel(description = "Placement constraint element with few additional properties required for some types of expressions, e.g. DELAYED_OR. The name refers to a constraint name or the name of an already defined constraint expression to help create a nested composite constraint.") +@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaClientCodegen", date = "2018-02-16T10:20:12.927-07:00") +public class PlacementConstraintElement implements Serializable { + private static final long serialVersionUID = 3914069426023602981L; + + /** + * The scheduling delay unit of delayed composite constraints. + **/ + @XmlType(name = "delay_unit") + @XmlEnum + public enum DelayUnitEnum { + MS(TimedPlacementConstraint.DelayUnit.MILLISECONDS), OP( + TimedPlacementConstraint.DelayUnit.OPPORTUNITIES); + + private TimedPlacementConstraint.DelayUnit value; + + DelayUnitEnum(TimedPlacementConstraint.DelayUnit value) { + this.value = value; + } + + public TimedPlacementConstraint.DelayUnit getValue() { + return value; + } + + @Override + @JsonValue + public String toString() { + return value.toString(); + } + } + + private String name = null; + @JsonProperty("delay_value") + @XmlElement(name = "delay_value") + private Long delayValue = null; + @JsonProperty("delay_unit") + @XmlElement(name = "delay_unit") + private DelayUnitEnum delayUnit = null; + + /** + * A unique name of this element, such that it can be used as an element in + * subsequent expressions. + **/ + public PlacementConstraintElement name(String name) { + this.name = name; + return this; + } + + @ApiModelProperty(example = "E1", required = true) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * When placement condition is DELAYED_OR then delay value should be provided. + **/ + public PlacementConstraintElement delayValue(Long delayValue) { + this.delayValue = delayValue; + return this; + } + + @ApiModelProperty(example = "2000") + public Long getDelayValue() { + return delayValue; + } + + public void setDelayValue(Long delayValue) { + this.delayValue = delayValue; + } + + /** + * When placement condition is DELAYED_OR then the unit of delay value should + * be provided. The two units supported today are milliseconds (MS) and + * opportunities (OP). MS specifies the number of milliseconds that the + * scheduler will try to place a container as per constraint specification + * before giving up and moving on to the next constraint specification. OP + * specifies the number of missed opportunities after which the scheduler + * should give up and move to the next constraint. + **/ + public PlacementConstraintElement delayUnit(DelayUnitEnum delayUnit) { + this.delayUnit = delayUnit; + return this; + } + + @ApiModelProperty(example = "null") + public DelayUnitEnum getDelayUnit() { + return delayUnit; + } + + public void setDelayUnit(DelayUnitEnum delayUnit) { + this.delayUnit = delayUnit; + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PlacementConstraintElement placementConstraintElement = (PlacementConstraintElement) o; + return Objects.equals(this.name, placementConstraintElement.name) + && Objects.equals(this.delayValue, + placementConstraintElement.delayValue) + && Objects.equals(this.delayUnit, placementConstraintElement.delayUnit); + } + + @Override + public int hashCode() { + return Objects.hash(name, delayValue, delayUnit); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PlacementConstraintElement {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" delayValue: ").append(toIndentedString(delayValue)) + .append("\n"); + sb.append(" delayUnit: ").append(toIndentedString(delayUnit)) + .append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraintExpression.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraintExpression.java new file mode 100644 index 00000000000..fd76fd877d2 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementConstraintExpression.java @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.yarn.service.api.records; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import javax.xml.bind.annotation.XmlEnum; +import javax.xml.bind.annotation.XmlType; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * A logical expression binding multiple placement policy constraints into a + * composite constraint. + **/ +@InterfaceAudience.Public +@InterfaceStability.Unstable +@ApiModel(description = "A logical expression binding multiple placement policy constraints into a composite constraint.") +@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaClientCodegen", date = "2018-02-16T10:20:12.927-07:00") +public class PlacementConstraintExpression implements Serializable { + private static final long serialVersionUID = 3313992232388737467L; + + /** + * A logical condition tying two or more constraints together. + **/ + @XmlType(name = "condition") + @XmlEnum + public enum ConditionEnum { + AND("AND"), OR("OR"), DELAYED_OR("DELAYED_OR"); + + private String value; + + ConditionEnum(String value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return value; + } + } + + private String name = null; + private ConditionEnum condition = null; + private List elements = new ArrayList<>(); + + /** + * A unique name of this expression, such that it can be used as an element in + * subsequent expressions. + **/ + public PlacementConstraintExpression name(String name) { + this.name = name; + return this; + } + + @ApiModelProperty(example = "E1", required = true) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * The logical condition by which multiple placement constraints are joined to + * create a composite constraint. + **/ + public PlacementConstraintExpression condition(ConditionEnum condition) { + this.condition = condition; + return this; + } + + @ApiModelProperty(example = "null", required = true) + @JsonProperty("condition") + public ConditionEnum getCondition() { + return condition; + } + + public void setCondition(ConditionEnum condition) { + this.condition = condition; + } + + /** + * A list of placement constraint names to create a composite constraint. The + * name of an already defined placement constraint expression can also be used + * to create a nested composite constraint. + **/ + public PlacementConstraintExpression elements( + List elements) { + this.elements = elements; + return this; + } + + @ApiModelProperty(example = "null", required = true) + @JsonProperty("elements") + public List getElements() { + return elements; + } + + public void setElements(List elements) { + this.elements = elements; + } + + @Override + public boolean equals(java.lang.Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PlacementConstraintExpression placementConstraintExp = (PlacementConstraintExpression) o; + return Objects.equals(this.name, placementConstraintExp.name) + && Objects.equals(this.condition, placementConstraintExp.condition) + && Objects.equals(this.elements, placementConstraintExp.elements); + } + + @Override + public int hashCode() { + return Objects.hash(name, condition, elements); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PlacementConstraintExpression {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" condition: ").append(toIndentedString(condition)) + .append("\n"); + sb.append(" elements: ").append(toIndentedString(elements)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(java.lang.Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementPolicy.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementPolicy.java index 6f6fe6fc82d..2cc6cf3a134 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementPolicy.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementPolicy.java @@ -17,49 +17,68 @@ package org.apache.hadoop.yarn.service.api.records; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; - import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; -import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + /** - * Placement policy of an instance of an service. This feature is in the - * works in YARN-4902. + * Advanced placement policy of the components of a service. **/ @InterfaceAudience.Public @InterfaceStability.Unstable -@ApiModel(description = "Placement policy of an instance of an service. This feature is in the works in YARN-4902.") -@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaClientCodegen", date = "2016-06-02T08:15:05.615-07:00") +@ApiModel(description = "Advanced placement policy of the components of a service.") +@javax.annotation.Generated(value = "class io.swagger.codegen.languages.JavaClientCodegen", date = "2018-02-16T10:20:12.927-07:00") public class PlacementPolicy implements Serializable { private static final long serialVersionUID = 4341110649551172231L; - private String label = null; + private List constraints = new ArrayList<>(); + private List expressions = new ArrayList<>(); + + /** + * Placement constraint details. + **/ + public PlacementPolicy constraints(List constraints) { + this.constraints = constraints; + return this; + } + + @ApiModelProperty(example = "null", required = true) + @JsonProperty("constraints") + public List getConstraints() { + return constraints; + } + + public void setConstraints(List constraints) { + this.constraints = constraints; + } /** - * Assigns a service to a named partition of the cluster where the service - * desires to run (optional). If not specified all services are submitted to - * a default label of the service owner. One or more labels can be setup for - * each service owner account with required constraints like no-preemption, - * sla-99999, preemption-ok, etc. + * The logical expressions binding multiple constraints of this placement + * policy. **/ - public PlacementPolicy label(String label) { - this.label = label; + public PlacementPolicy expressions( + List expressions) { + this.expressions = expressions; return this; } - @ApiModelProperty(example = "null", value = "Assigns a service to a named partition of the cluster where the service desires to run (optional). If not specified all services are submitted to a default label of the service owner. One or more labels can be setup for each service owner account with required constraints like no-preemption, sla-99999, preemption-ok, etc.") - @JsonProperty("label") - public String getLabel() { - return label; + @ApiModelProperty(example = "null") + @JsonProperty("expressions") + public List getExpressions() { + return expressions; } - public void setLabel(String label) { - this.label = label; + public void setExpressions(List expressions) { + this.expressions = expressions; } @Override @@ -71,12 +90,13 @@ public boolean equals(java.lang.Object o) { return false; } PlacementPolicy placementPolicy = (PlacementPolicy) o; - return Objects.equals(this.label, placementPolicy.label); + return Objects.equals(this.constraints, placementPolicy.constraints) + && Objects.equals(this.expressions, placementPolicy.expressions); } @Override public int hashCode() { - return Objects.hash(label); + return Objects.hash(constraints, expressions); } @Override @@ -84,7 +104,10 @@ public String toString() { StringBuilder sb = new StringBuilder(); sb.append("class PlacementPolicy {\n"); - sb.append(" label: ").append(toIndentedString(label)).append("\n"); + sb.append(" constraints: ").append(toIndentedString(constraints)) + .append("\n"); + sb.append(" expressions: ").append(toIndentedString(expressions)) + .append("\n"); sb.append("}"); return sb.toString(); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementScope.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementScope.java new file mode 100644 index 00000000000..25f1d147093 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementScope.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.yarn.service.api.records; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.yarn.api.resource.PlacementConstraints; + +import com.fasterxml.jackson.annotation.JsonValue; + +import io.swagger.annotations.ApiModel; + +/** + * The scope of placement for the containers of a component. + **/ +@InterfaceAudience.Public +@InterfaceStability.Unstable +@ApiModel(description = "The scope of placement for the containers of a component.") +public enum PlacementScope { + NODE(PlacementConstraints.NODE), RACK(PlacementConstraints.RACK); + + private String value; + + PlacementScope(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + @JsonValue + public String toString() { + return value; + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementType.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementType.java new file mode 100644 index 00000000000..f906ee8bb57 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/PlacementType.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.yarn.service.api.records; + +import io.swagger.annotations.ApiModel; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; + +/** + * The type of placement - affinity/anti-affinity/affinity-with-cardinality with + * containers of another component or containers of the same component (self). + **/ +@InterfaceAudience.Public +@InterfaceStability.Unstable +@ApiModel(description = "The type of placement - affinity/anti-affinity/" + + "affinity-with-cardinality with containers of another component or" + + " containers of the same component (self).") +public enum PlacementType { + AFFINITY, ANTI_AFFINITY, AFFINITY_WITH_CARDINALITY; +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Resource.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Resource.java index c417ec05d8f..f1c0852cecc 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Resource.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Resource.java @@ -25,6 +25,7 @@ import org.apache.hadoop.classification.InterfaceStability; import javax.xml.bind.annotation.XmlElement; +import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -49,7 +50,7 @@ @JsonProperty("additional") @XmlElement(name = "additional") - private Map additional = null; + private Map additional = new HashMap<>(); /** * Each resource profile has a unique id which is associated with a diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Service.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Service.java index 7b5c5b312ef..9475bf6d108 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Service.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/Service.java @@ -63,9 +63,6 @@ @XmlElement(name = "number_of_running_containers") private Long numberOfRunningContainers = null; private Long lifetime = null; - @JsonProperty("placement_policy") - @XmlElement(name = "placement_policy") - private PlacementPolicy placementPolicy = null; private List components = new ArrayList<>(); private Configuration configuration = new Configuration(); private ServiceState state = null; @@ -248,28 +245,6 @@ public void setLifetime(Long lifetime) { this.lifetime = lifetime; } - /** - * Advanced scheduling and placement policies (optional). If not specified, it - * defaults to the default placement policy of the service owner. The design of - * placement policies are in the works. It is not very clear at this point, - * how policies in conjunction with labels be exposed to service owners. - * This is a placeholder for now. The advanced structure of this attribute - * will be determined by YARN-4902. - **/ - public Service placementPolicy(PlacementPolicy placementPolicy) { - this.placementPolicy = placementPolicy; - return this; - } - - @ApiModelProperty(example = "null", value = "Advanced scheduling and placement policies (optional). If not specified, it defaults to the default placement policy of the service owner. The design of placement policies are in the works. It is not very clear at this point, how policies in conjunction with labels be exposed to service owners. This is a placeholder for now. The advanced structure of this attribute will be determined by YARN-4902.") - public PlacementPolicy getPlacementPolicy() { - return placementPolicy; - } - - public void setPlacementPolicy(PlacementPolicy placementPolicy) { - this.placementPolicy = placementPolicy; - } - /** * Components of an service. **/ @@ -429,8 +404,6 @@ public String toString() { sb.append(" numberOfRunningContainers: ") .append(toIndentedString(numberOfRunningContainers)).append("\n"); sb.append(" lifetime: ").append(toIndentedString(lifetime)).append("\n"); - sb.append(" placementPolicy: ").append(toIndentedString(placementPolicy)) - .append("\n"); sb.append(" components: ").append(toIndentedString(components)) .append("\n"); sb.append(" configuration: ").append(toIndentedString(configuration)) diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ServiceStatus.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ServiceStatus.java index 2cee23c8e96..f9c81909634 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ServiceStatus.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/api/records/ServiceStatus.java @@ -85,8 +85,8 @@ public void setState(ServiceState state) { } /** - * An error code specific to a scenario which service owners should be able to use - * to understand the failure in addition to the diagnostic information. + * An error code specific to a scenario which service owners should be able to + * use to understand the failure in addition to the diagnostic information. **/ public ServiceStatus code(Integer code) { this.code = code; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/component/Component.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/component/Component.java index 30906920378..fedfd2d39bd 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/component/Component.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/component/Component.java @@ -20,32 +20,45 @@ import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.ContainerStatus; +import org.apache.hadoop.yarn.api.records.ExecutionType; +import org.apache.hadoop.yarn.api.records.ExecutionTypeRequest; import org.apache.hadoop.yarn.api.records.Priority; import org.apache.hadoop.yarn.api.records.Resource; +import org.apache.hadoop.yarn.api.records.ResourceSizing; +import org.apache.hadoop.yarn.api.records.SchedulingRequest; +import org.apache.hadoop.yarn.api.resource.PlacementConstraint; +import org.apache.hadoop.yarn.api.resource.PlacementConstraint.AbstractConstraint; +import org.apache.hadoop.yarn.api.resource.PlacementConstraint.TargetExpression; +import org.apache.hadoop.yarn.api.resource.PlacementConstraint.TimedPlacementConstraint; +import org.apache.hadoop.yarn.api.resource.PlacementConstraints; +import org.apache.hadoop.yarn.api.resource.PlacementConstraints.PlacementTargets; import org.apache.hadoop.yarn.client.api.AMRMClient.ContainerRequest; import org.apache.hadoop.yarn.client.api.async.AMRMClientAsync; import org.apache.hadoop.yarn.event.AsyncDispatcher; import org.apache.hadoop.yarn.event.EventHandler; -import org.apache.hadoop.yarn.service.api.records.ResourceInformation; -import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; -import org.apache.hadoop.yarn.service.component.instance.ComponentInstanceId; import org.apache.hadoop.yarn.service.ContainerFailureTracker; import org.apache.hadoop.yarn.service.ServiceContext; +import org.apache.hadoop.yarn.service.ServiceMaster; +import org.apache.hadoop.yarn.service.ServiceMetrics; import org.apache.hadoop.yarn.service.ServiceScheduler; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraintElement; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraintExpression; +import org.apache.hadoop.yarn.service.api.records.PlacementPolicy; +import org.apache.hadoop.yarn.service.api.records.ResourceInformation; import org.apache.hadoop.yarn.service.api.records.ServiceState; +import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; import org.apache.hadoop.yarn.service.component.instance.ComponentInstanceEvent; -import org.apache.hadoop.yarn.service.ServiceMaster; -import org.apache.hadoop.yarn.service.ServiceMetrics; +import org.apache.hadoop.yarn.service.component.instance.ComponentInstanceId; +import org.apache.hadoop.yarn.service.monitor.probe.MonitorUtils; +import org.apache.hadoop.yarn.service.monitor.probe.Probe; import org.apache.hadoop.yarn.service.provider.ProviderUtils; +import org.apache.hadoop.yarn.service.utils.ServiceUtils; import org.apache.hadoop.yarn.state.InvalidStateTransitionException; import org.apache.hadoop.yarn.state.MultipleArcTransition; import org.apache.hadoop.yarn.state.SingleArcTransition; import org.apache.hadoop.yarn.state.StateMachine; import org.apache.hadoop.yarn.state.StateMachineFactory; import org.apache.hadoop.yarn.util.Apps; -import org.apache.hadoop.yarn.service.utils.ServiceUtils; -import org.apache.hadoop.yarn.service.monitor.probe.MonitorUtils; -import org.apache.hadoop.yarn.service.monitor.probe.Probe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,6 +68,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -66,9 +80,8 @@ import static org.apache.hadoop.yarn.api.records.ContainerExitStatus.*; import static org.apache.hadoop.yarn.service.api.ServiceApiConstants.*; import static org.apache.hadoop.yarn.service.component.ComponentEventType.*; -import static org.apache.hadoop.yarn.service.component.instance.ComponentInstanceEventType.START; -import static org.apache.hadoop.yarn.service.component.instance.ComponentInstanceEventType.STOP; import static org.apache.hadoop.yarn.service.component.ComponentState.*; +import static org.apache.hadoop.yarn.service.component.instance.ComponentInstanceEventType.*; import static org.apache.hadoop.yarn.service.conf.YarnServiceConf.CONTAINER_FAILURE_THRESHOLD; public class Component implements EventHandler { @@ -393,6 +406,8 @@ private void assignContainerToCompInstance(Container container) { @SuppressWarnings({ "unchecked" }) public void requestContainers(long count) { + LOG.info("Component: {}, Requesting for {} container(s)", + componentSpec.getName(), count); org.apache.hadoop.yarn.service.api.records.Resource componentResource = componentSpec.getResource(); @@ -425,12 +440,153 @@ public void requestContainers(long count) { } } - for (int i = 0; i < count; i++) { - //TODO Once YARN-5468 is done, use that for anti-affinity - ContainerRequest request = - ContainerRequest.newBuilder().capability(resource).priority(priority) - .allocationRequestId(allocateId).relaxLocality(true).build(); - amrmClient.addContainerRequest(request); + if (componentSpec.getPlacementPolicy() == null + || componentSpec.getPlacementPolicy().getConstraints() == null) { + for (int i = 0; i < count; i++) { + ContainerRequest request = ContainerRequest.newBuilder() + .capability(resource).priority(priority) + .allocationRequestId(allocateId).relaxLocality(true).build(); + LOG.info("Component: {}, Submitting container request : {}", + componentSpec.getName(), request); + amrmClient.addContainerRequest(request); + } + } else { + // Schedule placement requests. Validation of non-null tags and that they + // refer to existing component names are already done. So, no need to + // validate here. + PlacementPolicy placementPolicy = componentSpec.getPlacementPolicy(); + Collection schedulingRequests = new HashSet<>(); + // We prepare a map of constraints, required if placement expressions are + // specified. + Map constraintsMap = new HashMap<>(); + // We prepare an AND-ed composite constraint to be the final composite + // constraint. If placement expressions are specified to create advanced + // composite constraints then this AND-ed composite constraint is not + // used. + PlacementConstraint finalConstraint = null; + for (org.apache.hadoop.yarn.service.api.records.PlacementConstraint + yarnServiceConstraint : placementPolicy.getConstraints()) { + // Currently only intra-application allocation tags are supported. Need + // to add node attributes when they are supported. Are allocation tags + // and node attributes mutually exclusive or can you use both in a + // single constraint? + TargetExpression targetExpression = PlacementTargets + .allocationTagToIntraApp( + yarnServiceConstraint.getTags().toArray(new String[0])); + PlacementConstraint constraint = null; + switch (yarnServiceConstraint.getType()) { + case AFFINITY: + constraint = PlacementConstraints + .targetIn(yarnServiceConstraint.getScope().getValue(), + targetExpression) + .build(); + break; + case ANTI_AFFINITY: + constraint = PlacementConstraints + .targetNotIn(yarnServiceConstraint.getScope().getValue(), + targetExpression) + .build(); + break; + case AFFINITY_WITH_CARDINALITY: + constraint = PlacementConstraints.targetCardinality( + yarnServiceConstraint.getScope().name().toLowerCase(), + yarnServiceConstraint.getMinCardinality() == null ? 0 + : yarnServiceConstraint.getMinCardinality().intValue(), + yarnServiceConstraint.getMaxCardinality() == null + ? Integer.MAX_VALUE + : yarnServiceConstraint.getMaxCardinality().intValue(), + targetExpression).build(); + break; + } + // The default AND-ed final composite constraint + if (finalConstraint != null) { + finalConstraint = PlacementConstraints + .and(constraint.getConstraintExpr(), + finalConstraint.getConstraintExpr()) + .build(); + } else { + finalConstraint = constraint; + } + LOG.debug("Component: {}, Placement constraint: {}", + componentSpec.getName(), constraint.getConstraintExpr().toString()); + constraintsMap.put(yarnServiceConstraint.getName(), constraint); + } + // Now apply constraint expression if specified, otherwise they are + // considered to be AND-ed by default. + if (!placementPolicy.getExpressions().isEmpty()) { + Map compositeConstraintsMap = new HashMap<>(); + for (Map.Entry pc : constraintsMap + .entrySet()) { + compositeConstraintsMap.put(pc.getKey(), + pc.getValue().getConstraintExpr()); + } + // ys = yarn service + AbstractConstraint compositeConstraint = null; + for (PlacementConstraintExpression ysConstraintExp : placementPolicy + .getExpressions()) { + LOG.debug("Component: {}, Constraint expression: {}, Elements = {}", + componentSpec.getName(), ysConstraintExp.getName(), + ysConstraintExp.getElements()); + switch (ysConstraintExp.getCondition()) { + case AND: + List andConstraints = new ArrayList<>(); + for (PlacementConstraintElement constraintElement : ysConstraintExp + .getElements()) { + andConstraints.add( + compositeConstraintsMap.get(constraintElement.getName())); + } + compositeConstraint = PlacementConstraints + .and(andConstraints.toArray(new AbstractConstraint[0])); + compositeConstraintsMap.put(ysConstraintExp.getName(), + compositeConstraint); + break; + case OR: + List orConstraints = new ArrayList<>(); + for (PlacementConstraintElement constraintElement : ysConstraintExp + .getElements()) { + orConstraints.add( + compositeConstraintsMap.get(constraintElement.getName())); + } + compositeConstraint = PlacementConstraints + .or(orConstraints.toArray(new AbstractConstraint[0])); + compositeConstraintsMap.put(ysConstraintExp.getName(), + compositeConstraint); + break; + case DELAYED_OR: + List dalyedOrConstraints = new ArrayList<>(); + for (PlacementConstraintElement constraintElement : ysConstraintExp + .getElements()) { + dalyedOrConstraints.add(new TimedPlacementConstraint( + compositeConstraintsMap.get(constraintElement.getName()), + constraintElement.getDelayValue(), + constraintElement.getDelayUnit().getValue())); + } + compositeConstraint = PlacementConstraints.delayedOr( + dalyedOrConstraints.toArray(new TimedPlacementConstraint[0])); + compositeConstraintsMap.put(ysConstraintExp.getName(), + compositeConstraint); + break; + } + if (compositeConstraint != null) { + finalConstraint = compositeConstraint.build(); + } + } + } + ResourceSizing resourceSizing = ResourceSizing.newInstance((int) count, + resource); + LOG.debug("Component: {}, Resource sizing: {}", componentSpec.getName(), + resourceSizing); + SchedulingRequest request = SchedulingRequest.newBuilder() + .priority(priority).allocationRequestId(allocateId) + .allocationTags(Collections.singleton(componentSpec.getName())) + .executionType( + ExecutionTypeRequest.newInstance(ExecutionType.GUARANTEED, true)) + .placementConstraintExpression(finalConstraint) + .resourceSizing(resourceSizing).build(); + LOG.info("Component: {}, Submitting scheduling request: {}", + componentSpec.getName(), request); + schedulingRequests.add(request); + amrmClient.addSchedulingRequests(schedulingRequests); } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/exceptions/RestApiErrorMessages.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/exceptions/RestApiErrorMessages.java index 12f6455404b..de69b0761ee 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/exceptions/RestApiErrorMessages.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/exceptions/RestApiErrorMessages.java @@ -91,4 +91,11 @@ String ERROR_QUICKLINKS_FOR_COMP_INVALID = "Quicklinks specified at" + " component level, needs corresponding values set at service level"; + String ERROR_PLACEMENT_POLICY_TAG_NAME_INVALID = "Invalid tag name %s " + + "specified in placement policy of component %s. Tag names should be a " + + "valid component name in the service."; + String ERROR_PLACEMENT_POLICY_EXPRESSION_ELEMENT_NAME_INVALID = "Invalid " + + "expression element name %s specified in placement policy of component " + + "%s. Expression element names should be a valid constraint name or an " + + "expression name defined for this component only."; } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/timelineservice/ServiceTimelinePublisher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/timelineservice/ServiceTimelinePublisher.java index 949ce19c8dc..6c73ebb8d67 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/timelineservice/ServiceTimelinePublisher.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/timelineservice/ServiceTimelinePublisher.java @@ -268,10 +268,6 @@ private void publishComponents(List components) { } entityInfos.put(ServiceTimelineMetricsConstants.RUN_PRIVILEGED_CONTAINER, component.getRunPrivilegedContainer().toString()); - if (component.getPlacementPolicy() != null) { - entityInfos.put(ServiceTimelineMetricsConstants.PLACEMENT_POLICY, - component.getPlacementPolicy().getLabel()); - } entity.addInfo(entityInfos); putEntity(entity); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/utils/ServiceApiUtil.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/utils/ServiceApiUtil.java index 05917758476..8e835789e76 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/utils/ServiceApiUtil.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/main/java/org/apache/hadoop/yarn/service/utils/ServiceApiUtil.java @@ -24,19 +24,21 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.registry.client.api.RegistryConstants; import org.apache.hadoop.registry.client.binding.RegistryUtils; -import org.apache.hadoop.security.HadoopKerberosName; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.yarn.exceptions.YarnException; -import org.apache.hadoop.yarn.service.api.records.Service; import org.apache.hadoop.yarn.service.api.records.Artifact; import org.apache.hadoop.yarn.service.api.records.Component; import org.apache.hadoop.yarn.service.api.records.Configuration; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraint; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraintElement; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraintExpression; import org.apache.hadoop.yarn.service.api.records.Resource; -import org.apache.hadoop.yarn.service.provider.AbstractClientProvider; -import org.apache.hadoop.yarn.service.provider.ProviderFactory; -import org.apache.hadoop.yarn.service.monitor.probe.MonitorUtils; +import org.apache.hadoop.yarn.service.api.records.Service; import org.apache.hadoop.yarn.service.conf.RestApiConstants; import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages; +import org.apache.hadoop.yarn.service.monitor.probe.MonitorUtils; +import org.apache.hadoop.yarn.service.provider.AbstractClientProvider; +import org.apache.hadoop.yarn.service.provider.ProviderFactory; import org.codehaus.jackson.map.PropertyNamingStrategy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -201,6 +203,7 @@ public static void validateAndResolveService(Service service, } validateComponent(comp, fs.getFileSystem(), conf); } + validatePlacementPolicy(service.getComponents(), componentNames); // validate dependency tree sortByDependencies(service.getComponents()); @@ -262,6 +265,45 @@ public static void validateNameFormat(String name, namePattern.validate(name); } + private static void validatePlacementPolicy(List components, + Set componentNames) { + for (Component comp : components) { + Set expressionElementNames = new HashSet<>(); + if (comp.getPlacementPolicy() != null) { + for (PlacementConstraint constraint : comp.getPlacementPolicy() + .getConstraints()) { + for (String tag : constraint.getTags()) { + if (!componentNames.contains(tag)) { + throw new IllegalArgumentException(String.format( + RestApiErrorMessages.ERROR_PLACEMENT_POLICY_TAG_NAME_INVALID, + tag, comp.getName())); + } + } + // A constraint name is a valid element name + expressionElementNames.add(constraint.getName()); + } + for (PlacementConstraintExpression expression : comp + .getPlacementPolicy().getExpressions()) { + // An expression name is a valid element name + expressionElementNames.add(expression.getName()); + } + // Now validate that the element names are either valid constraint + // names or expression names + for (PlacementConstraintExpression expression : comp + .getPlacementPolicy().getExpressions()) { + for (PlacementConstraintElement element : expression.getElements()) { + if (!expressionElementNames.contains(element.getName())) { + throw new IllegalArgumentException(String.format( + RestApiErrorMessages.ERROR_PLACEMENT_POLICY_EXPRESSION_ELEMENT_NAME_INVALID, + element.getName(), comp.getName())); + } + } + } + expressionElementNames.clear(); + } + } + } + @VisibleForTesting public static List getComponents(SliderFileSystem fs, String serviceName) throws IOException { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestServiceApiUtil.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestServiceApiUtil.java index c2f8f3e9f95..386633ae468 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestServiceApiUtil.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestServiceApiUtil.java @@ -22,6 +22,10 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.service.api.records.Artifact; import org.apache.hadoop.yarn.service.api.records.Component; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraint; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraintElement; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraintExpression; +import org.apache.hadoop.yarn.service.api.records.PlacementPolicy; import org.apache.hadoop.yarn.service.api.records.Resource; import org.apache.hadoop.yarn.service.api.records.Service; import org.apache.hadoop.yarn.service.exceptions.RestApiErrorMessages; @@ -490,4 +494,52 @@ private static void testComponent(SliderFileSystem sfs) Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage()); } } + + @Test + public void testPlacementPolicy() throws IOException { + SliderFileSystem sfs = ServiceTestUtils.initMockFs(); + Service app = createValidApplication("comp-a"); + Component comp = app.getComponents().get(0); + PlacementPolicy pp = new PlacementPolicy(); + PlacementConstraint pc = new PlacementConstraint(); + pc.setName("CA1"); + pc.setTags(Collections.singletonList("comp-invalid")); + pp.setConstraints(Collections.singletonList(pc)); + comp.setPlacementPolicy(pp); + + try { + ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED); + Assert.fail(EXCEPTION_PREFIX + "service with empty placement"); + } catch (IllegalArgumentException e) { + assertEquals(String.format( + RestApiErrorMessages.ERROR_PLACEMENT_POLICY_TAG_NAME_INVALID, + "comp-invalid", "comp-a"), e.getMessage()); + } + + pc.setTags(Collections.singletonList("comp-a")); + + PlacementConstraintExpression pce = new PlacementConstraintExpression(); + pce.setName("E1"); + PlacementConstraintElement pcElem = new PlacementConstraintElement(); + pcElem.setName("comp-invalid"); + pce.setElements(Collections.singletonList(pcElem)); + pp.setExpressions(Collections.singletonList(pce)); + + try { + ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED); + Assert.fail(EXCEPTION_PREFIX + "service with empty placement"); + } catch (IllegalArgumentException e) { + assertEquals(String.format( + RestApiErrorMessages.ERROR_PLACEMENT_POLICY_EXPRESSION_ELEMENT_NAME_INVALID, + "comp-invalid", "comp-a"), e.getMessage()); + } + + pcElem.setName("CA1"); + // now it should succeed + try { + ServiceApiUtil.validateAndResolveService(app, sfs, CONF_DNS_ENABLED); + } catch (IllegalArgumentException e) { + Assert.fail(NO_EXCEPTION_PREFIX + e.getMessage()); + } + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestYarnNativeServices.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestYarnNativeServices.java index 091e624d852..e51688078bd 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestYarnNativeServices.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/TestYarnNativeServices.java @@ -24,14 +24,21 @@ import org.apache.hadoop.fs.Path; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.test.GenericTestUtils; +import org.apache.hadoop.yarn.api.protocolrecords.GetContainersRequest; import org.apache.hadoop.yarn.api.records.*; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.exceptions.YarnException; -import org.apache.hadoop.yarn.service.api.records.Service; -import org.apache.hadoop.yarn.service.api.records.ServiceState; +import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager; import org.apache.hadoop.yarn.service.api.records.Component; +import org.apache.hadoop.yarn.service.api.records.ComponentState; import org.apache.hadoop.yarn.service.api.records.Container; import org.apache.hadoop.yarn.service.api.records.ContainerState; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraint; +import org.apache.hadoop.yarn.service.api.records.PlacementPolicy; +import org.apache.hadoop.yarn.service.api.records.PlacementScope; +import org.apache.hadoop.yarn.service.api.records.PlacementType; +import org.apache.hadoop.yarn.service.api.records.Service; +import org.apache.hadoop.yarn.service.api.records.ServiceState; import org.apache.hadoop.yarn.service.client.ServiceClient; import org.apache.hadoop.yarn.service.exceptions.SliderException; import org.apache.hadoop.yarn.service.utils.SliderFileSystem; @@ -333,6 +340,103 @@ public void testRecoverComponentsAfterRMRestart() throws Exception { client.actionDestroy(exampleApp.getName()); } + // Test to verify ANTI_AFFINITY placement policy + // 1. Start mini cluster with 3 NMs and scheduler placement-constraint handler + // 2. Create an example service with 3 containers + // 3. Verify no more than 1 container comes up in each of the 3 NMs + // 4. Flex the component to 4 containers + // 5. Verify that the 4th container does not even get allocated since there + // are only 3 NMs + @Test (timeout = 200000) + public void testCreateServiceWithPlacementPolicy() throws Exception { + // We need to enable scheduler placement-constraint at the cluster level to + // let apps use placement policies. + YarnConfiguration conf = new YarnConfiguration(); + conf.set(YarnConfiguration.RM_PLACEMENT_CONSTRAINTS_HANDLER, + YarnConfiguration.SCHEDULER_RM_PLACEMENT_CONSTRAINTS_HANDLER); + setConf(conf); + setupInternal(3); + ServiceClient client = createClient(); + Service exampleApp = new Service(); + exampleApp.setName("example-app"); + exampleApp.setVersion("v1"); + Component comp = createComponent("compa", 3L, "sleep 1000"); + PlacementPolicy pp = new PlacementPolicy(); + PlacementConstraint pc = new PlacementConstraint(); + pc.setName("CA1"); + pc.setTags(Collections.singletonList("compa")); + pc.setScope(PlacementScope.NODE); + pc.setType(PlacementType.ANTI_AFFINITY); + pp.setConstraints(Collections.singletonList(pc)); + comp.setPlacementPolicy(pp); + exampleApp.addComponent(comp); + client.actionCreate(exampleApp); + waitForServiceToBeStable(client, exampleApp); + + // Check service is stable and all 3 containers are running + Service service = client.getStatus(exampleApp.getName()); + Component component = service.getComponent("compa"); + Assert.assertEquals("Service state should be STABLE", ServiceState.STABLE, + service.getState()); + Assert.assertEquals("3 containers are expected to be running", 3, + component.getContainers().size()); + // Prepare a map of non-AM containers for later lookup + Set nonAMContainerIdSet = new HashSet<>(); + for (Container cont : component.getContainers()) { + nonAMContainerIdSet.add(cont.getId()); + } + + // Verify that no more than 1 non-AM container came up on each of the 3 NMs + Set hosts = new HashSet<>(); + ApplicationReport report = client.getYarnClient() + .getApplicationReport(ApplicationId.fromString(exampleApp.getId())); + GetContainersRequest req = GetContainersRequest + .newInstance(report.getCurrentApplicationAttemptId()); + ResourceManager rm = getYarnCluster().getResourceManager(); + for (ContainerReport contReport : rm.getClientRMService().getContainers(req) + .getContainerList()) { + if (!nonAMContainerIdSet + .contains(contReport.getContainerId().toString())) { + continue; + } + if (hosts.contains(contReport.getNodeHttpAddress())) { + Assert.fail("Container " + contReport.getContainerId() + + " came up in the same host as another container."); + } else { + hosts.add(contReport.getNodeHttpAddress()); + } + } + + // Flex compa up to 4, which is more containers than the no of NMs + Map compCounts = new HashMap<>(); + compCounts.put("compa", 4L); + exampleApp.getComponent("compa").setNumberOfContainers(4L); + client.flexByRestService(exampleApp.getName(), compCounts); + try { + // 10 secs is enough for the container to be started. The down side of + // this test is that it has to wait that long. Setting a higher wait time + // will add to the total time taken by tests to run. + waitForServiceToBeStable(client, exampleApp, 10000); + Assert.fail("Service should not be in a stable state. It should throw " + + "a timeout exception."); + } catch (Exception e) { + // Check that service state is not STABLE and only 3 containers are + // running and the fourth one should not get allocated. + service = client.getStatus(exampleApp.getName()); + component = service.getComponent("compa"); + Assert.assertNotEquals("Service state should not be STABLE", + ServiceState.STABLE, service.getState()); + Assert.assertEquals("Component state should be FLEXING", + ComponentState.FLEXING, component.getState()); + Assert.assertEquals("3 containers are expected to be running", 3, + component.getContainers().size()); + } + + LOG.info("Stop/destroy service {}", exampleApp); + client.actionStop(exampleApp.getName(), true); + client.actionDestroy(exampleApp.getName()); + } + // Check containers launched are in dependency order // Get all containers into a list and sort based on container launch time e.g. // compa-c1, compa-c2, compb-c1, compb-c2; @@ -470,6 +574,12 @@ private void checkEachCompInstancesInOrder(Component component) { */ private void waitForServiceToBeStable(ServiceClient client, Service exampleApp) throws TimeoutException, InterruptedException { + waitForServiceToBeStable(client, exampleApp, 200000); + } + + private void waitForServiceToBeStable(ServiceClient client, + Service exampleApp, int waitForMillis) + throws TimeoutException, InterruptedException { GenericTestUtils.waitFor(() -> { try { Service retrievedApp = client.getStatus(exampleApp.getName()); @@ -479,7 +589,7 @@ private void waitForServiceToBeStable(ServiceClient client, e.printStackTrace(); return false; } - }, 2000, 200000); + }, 2000, waitForMillis); } /** diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/timelineservice/TestServiceTimelinePublisher.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/timelineservice/TestServiceTimelinePublisher.java index 80b4f5103a3..cff7229db34 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/timelineservice/TestServiceTimelinePublisher.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/src/test/java/org/apache/hadoop/yarn/service/timelineservice/TestServiceTimelinePublisher.java @@ -34,7 +34,9 @@ import org.apache.hadoop.yarn.service.api.records.Component; import org.apache.hadoop.yarn.service.api.records.Container; import org.apache.hadoop.yarn.service.api.records.ContainerState; +import org.apache.hadoop.yarn.service.api.records.PlacementConstraint; import org.apache.hadoop.yarn.service.api.records.PlacementPolicy; +import org.apache.hadoop.yarn.service.api.records.PlacementType; import org.apache.hadoop.yarn.service.api.records.Resource; import org.apache.hadoop.yarn.service.component.instance.ComponentInstance; import org.apache.hadoop.yarn.service.component.instance.ComponentInstanceId; @@ -45,6 +47,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -208,8 +211,6 @@ private void verifyComponentTimelineEntity(TimelineEntity entity) { info.get(ServiceTimelineMetricsConstants.LAUNCH_COMMAND)); assertEquals("false", info.get(ServiceTimelineMetricsConstants.RUN_PRIVILEGED_CONTAINER)); - assertEquals("label", - info.get(ServiceTimelineMetricsConstants.PLACEMENT_POLICY)); } private static Service createMockApplication() { @@ -234,7 +235,10 @@ private static Service createMockApplication() { when(component.getResource()).thenReturn(resource); when(component.getLaunchCommand()).thenReturn("sleep 1"); PlacementPolicy placementPolicy = new PlacementPolicy(); - placementPolicy.setLabel("label"); + PlacementConstraint placementConstraint = new PlacementConstraint(); + placementConstraint.setType(PlacementType.ANTI_AFFINITY); + placementPolicy + .setConstraints(Collections.singletonList(placementConstraint)); when(component.getPlacementPolicy()).thenReturn(placementPolicy); when(component.getConfiguration()).thenReturn( new org.apache.hadoop.yarn.service.api.records.Configuration()); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/SchedulingRequestPBImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/SchedulingRequestPBImpl.java index 11f75bbbb16..a53dca1be1a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/SchedulingRequestPBImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/records/impl/pb/SchedulingRequestPBImpl.java @@ -291,6 +291,7 @@ public String toString() { ", executionType=" + getExecutionType() + ", allocationTags=" + getAllocationTags() + ", resourceSizing=" + getResourceSizing() + + ", placementConstraint=" + getPlacementConstraint() + '}'; } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md index 1e18b9969f5..1681895bbaa 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/YarnServiceAPI.md @@ -1,4 +1,4 @@ -#