You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by cu...@apache.org on 2015/12/07 22:34:36 UTC
hadoop git commit: YARN-4248. REST API for submit/update/delete
Reservations. (curino)
Repository: hadoop
Updated Branches:
refs/heads/trunk 4ff973f96 -> c25a63545
YARN-4248. REST API for submit/update/delete Reservations. (curino)
Project: http://git-wip-us.apache.org/repos/asf/hadoop/repo
Commit: http://git-wip-us.apache.org/repos/asf/hadoop/commit/c25a6354
Tree: http://git-wip-us.apache.org/repos/asf/hadoop/tree/c25a6354
Diff: http://git-wip-us.apache.org/repos/asf/hadoop/diff/c25a6354
Branch: refs/heads/trunk
Commit: c25a6354598ec855bec7f695a7c3eed8794cd381
Parents: 4ff973f
Author: = <cu...@apache.org>
Authored: Mon Dec 7 13:33:28 2015 -0800
Committer: = <cu...@apache.org>
Committed: Mon Dec 7 13:33:28 2015 -0800
----------------------------------------------------------------------
hadoop-yarn-project/CHANGES.txt | 2 +
.../resourcemanager/webapp/RMWebServices.java | 304 +++++++++++
.../webapp/dao/ReservationDefinitionInfo.java | 82 +++
.../dao/ReservationDeleteRequestInfo.java | 49 ++
.../dao/ReservationDeleteResponseInfo.java | 36 ++
.../webapp/dao/ReservationRequestInfo.java | 78 +++
.../webapp/dao/ReservationRequestsInfo.java | 63 +++
.../dao/ReservationSubmissionRequestInfo.java | 60 +++
.../dao/ReservationSubmissionResponseInfo.java | 54 ++
.../dao/ReservationUpdateRequestInfo.java | 60 +++
.../dao/ReservationUpdateResponseInfo.java | 37 ++
.../webapp/TestRMWebServicesReservation.java | 517 +++++++++++++++++++
.../src/test/resources/delete-reservation.json | 3 +
.../src/test/resources/submit-reservation.json | 31 ++
.../src/test/resources/update-reservation.json | 31 ++
15 files changed, 1407 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/CHANGES.txt
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt
index 767e9c3..3ba94b4 100644
--- a/hadoop-yarn-project/CHANGES.txt
+++ b/hadoop-yarn-project/CHANGES.txt
@@ -602,6 +602,8 @@ Release 2.8.0 - UNRELEASED
YARN-3456. Improve handling of incomplete TimelineEntities. (Varun Saxena
via rohithsharmaks)
+ YARN-4248. REST API for submit/update/delete Reservations. (curino)
+
OPTIMIZATIONS
YARN-3339. TestDockerContainerExecutor should pull a single image and not
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java
index 2da477d..b744765 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java
@@ -98,6 +98,11 @@ import org.apache.hadoop.yarn.api.records.NodeLabel;
import org.apache.hadoop.yarn.api.records.NodeState;
import org.apache.hadoop.yarn.api.records.Priority;
import org.apache.hadoop.yarn.api.records.QueueACL;
+import org.apache.hadoop.yarn.api.records.ReservationDefinition;
+import org.apache.hadoop.yarn.api.records.ReservationId;
+import org.apache.hadoop.yarn.api.records.ReservationRequest;
+import org.apache.hadoop.yarn.api.records.ReservationRequestInterpreter;
+import org.apache.hadoop.yarn.api.records.ReservationRequests;
import org.apache.hadoop.yarn.api.records.Resource;
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
@@ -145,6 +150,19 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntr
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDefinitionInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteResponseInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationRequestInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationRequestsInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionResponseInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateResponseInfo;
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationDeleteRequest;
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationSubmissionRequest;
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationSubmissionResponse;
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationUpdateRequest;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ResourceInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo;
@@ -1822,4 +1840,290 @@ public class RMWebServices {
}
return token;
}
+
+ /**
+ * Function to submit a Reservation to the RM.
+ *
+ * @param resContext provides information to construct the
+ * ReservationSubmissionRequest
+ * @param hsr the servlet request
+ * @return Response containing the status code
+ * @throws AuthorizationException
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ @POST
+ @Path("/reservation/submit")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ public Response submitReservation(
+ ReservationSubmissionRequestInfo resContext,
+ @Context HttpServletRequest hsr) throws AuthorizationException,
+ IOException, InterruptedException {
+
+ init();
+ UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
+ if (callerUGI == null) {
+ throw new AuthorizationException("Unable to obtain user name, "
+ + "user not authenticated");
+ }
+ if (UserGroupInformation.isSecurityEnabled() && isStaticUser(callerUGI)) {
+ String msg = "The default static user cannot carry out this operation.";
+ return Response.status(Status.FORBIDDEN).entity(msg).build();
+ }
+
+ final ReservationSubmissionRequest reservation =
+ createReservationSubmissionRequest(resContext);
+
+ ReservationSubmissionResponseInfo resRespInfo;
+ try {
+ resRespInfo =
+ callerUGI.doAs(
+ new PrivilegedExceptionAction<ReservationSubmissionResponseInfo>() {
+ @Override
+ public ReservationSubmissionResponseInfo run()
+ throws IOException, YarnException {
+ ReservationSubmissionResponse tempRes =
+ rm.getClientRMService().submitReservation(reservation);
+ return new ReservationSubmissionResponseInfo(tempRes);
+ }
+ });
+ } catch (UndeclaredThrowableException ue) {
+ if (ue.getCause() instanceof YarnException) {
+ throw new BadRequestException(ue.getCause().getMessage());
+ }
+ LOG.info("Submit reservation request failed", ue);
+ throw ue;
+ }
+
+ return Response.status(Status.OK).entity(resRespInfo).build();
+ }
+
+ private ReservationSubmissionRequest createReservationSubmissionRequest(
+ ReservationSubmissionRequestInfo resContext) {
+
+ // defending against a couple of common submission format problems
+ if (resContext == null) {
+ throw new BadRequestException(
+ "Input ReservationSubmissionContext should not be null");
+ }
+ ReservationDefinitionInfo resInfo = resContext.getReservationDefinition();
+ if (resInfo == null) {
+ throw new BadRequestException(
+ "Input ReservationDefinition should not be null");
+ }
+
+ ReservationRequestsInfo resReqsInfo = resInfo.getReservationRequests();
+
+ if (resReqsInfo == null || resReqsInfo.getReservationRequest() == null
+ || resReqsInfo.getReservationRequest().size() == 0) {
+ throw new BadRequestException("The ReservationDefinition should"
+ + " contain at least one ReservationRequest");
+ }
+
+ ReservationRequestInterpreter[] values =
+ ReservationRequestInterpreter.values();
+ ReservationRequestInterpreter resInt =
+ values[resReqsInfo.getReservationRequestsInterpreter()];
+ List<ReservationRequest> list = new ArrayList<ReservationRequest>();
+
+ for (ReservationRequestInfo resReqInfo : resReqsInfo
+ .getReservationRequest()) {
+ ResourceInfo rInfo = resReqInfo.getCapability();
+ Resource capability =
+ Resource.newInstance(rInfo.getMemory(), rInfo.getvCores());
+ int numContainers = resReqInfo.getNumContainers();
+ int minConcurrency = resReqInfo.getMinConcurrency();
+ long duration = resReqInfo.getDuration();
+ ReservationRequest rr =
+ ReservationRequest.newInstance(capability, numContainers,
+ minConcurrency, duration);
+ list.add(rr);
+ }
+ ReservationRequests reqs = ReservationRequests.newInstance(list, resInt);
+ ReservationDefinition rDef =
+ ReservationDefinition.newInstance(resInfo.getArrival(),
+ resInfo.getDeadline(), reqs, resInfo.getReservationName());
+ ReservationSubmissionRequest request =
+ ReservationSubmissionRequest.newInstance(rDef, resContext.getQueue());
+
+ return request;
+ }
+
+ /**
+ * Function to update a Reservation to the RM.
+ *
+ * @param resContext provides information to construct the
+ * ReservationUpdateRequest
+ * @param hsr the servlet request
+ * @return Response containing the status code
+ * @throws AuthorizationException
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ @POST
+ @Path("/reservation/update")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ public Response updateReservation(ReservationUpdateRequestInfo resContext,
+ @Context HttpServletRequest hsr) throws AuthorizationException,
+ IOException, InterruptedException {
+
+ init();
+ UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
+ if (callerUGI == null) {
+ throw new AuthorizationException("Unable to obtain user name, "
+ + "user not authenticated");
+ }
+ if (UserGroupInformation.isSecurityEnabled() && isStaticUser(callerUGI)) {
+ String msg = "The default static user cannot carry out this operation.";
+ return Response.status(Status.FORBIDDEN).entity(msg).build();
+ }
+
+ final ReservationUpdateRequest reservation =
+ createReservationUpdateRequest(resContext);
+
+ ReservationUpdateResponseInfo resRespInfo;
+ try {
+ resRespInfo =
+ callerUGI.doAs(
+ new PrivilegedExceptionAction<ReservationUpdateResponseInfo>() {
+ @Override
+ public ReservationUpdateResponseInfo run() throws IOException,
+ YarnException {
+ rm.getClientRMService().updateReservation(reservation);
+ return new ReservationUpdateResponseInfo();
+ }
+ });
+ } catch (UndeclaredThrowableException ue) {
+ if (ue.getCause() instanceof YarnException) {
+ throw new BadRequestException(ue.getCause().getMessage());
+ }
+ LOG.info("Update reservation request failed", ue);
+ throw ue;
+ }
+
+ return Response.status(Status.OK).entity(resRespInfo).build();
+ }
+
+ private ReservationUpdateRequest createReservationUpdateRequest(
+ ReservationUpdateRequestInfo resContext) throws IOException {
+
+ // defending against a couple of common submission format problems
+ if (resContext == null) {
+ throw new BadRequestException(
+ "Input ReservationSubmissionContext should not be null");
+ }
+ ReservationDefinitionInfo resInfo = resContext.getReservationDefinition();
+ if (resInfo == null) {
+ throw new BadRequestException(
+ "Input ReservationDefinition should not be null");
+ }
+ ReservationRequestsInfo resReqsInfo = resInfo.getReservationRequests();
+ if (resReqsInfo == null || resReqsInfo.getReservationRequest() == null
+ || resReqsInfo.getReservationRequest().size() == 0) {
+ throw new BadRequestException("The ReservationDefinition should"
+ + " contain at least one ReservationRequest");
+ }
+ if (resContext.getReservationId() == null) {
+ throw new BadRequestException(
+ "Update operations must specify an existing ReservaitonId");
+ }
+
+ ReservationRequestInterpreter[] values =
+ ReservationRequestInterpreter.values();
+ ReservationRequestInterpreter resInt =
+ values[resReqsInfo.getReservationRequestsInterpreter()];
+ List<ReservationRequest> list = new ArrayList<ReservationRequest>();
+
+ for (ReservationRequestInfo resReqInfo : resReqsInfo
+ .getReservationRequest()) {
+ ResourceInfo rInfo = resReqInfo.getCapability();
+ Resource capability =
+ Resource.newInstance(rInfo.getMemory(), rInfo.getvCores());
+ int numContainers = resReqInfo.getNumContainers();
+ int minConcurrency = resReqInfo.getMinConcurrency();
+ long duration = resReqInfo.getDuration();
+ ReservationRequest rr =
+ ReservationRequest.newInstance(capability, numContainers,
+ minConcurrency, duration);
+ list.add(rr);
+ }
+ ReservationRequests reqs = ReservationRequests.newInstance(list, resInt);
+ ReservationDefinition rDef =
+ ReservationDefinition.newInstance(resInfo.getArrival(),
+ resInfo.getDeadline(), reqs, resInfo.getReservationName());
+ ReservationUpdateRequest request =
+ ReservationUpdateRequest.newInstance(rDef, ReservationId
+ .parseReservationId(resContext.getReservationId()));
+
+ return request;
+ }
+
+ /**
+ * Function to delete a Reservation to the RM.
+ *
+ * @param resContext provides information to construct
+ * the ReservationDeleteRequest
+ * @param hsr the servlet request
+ * @return Response containing the status code
+ * @throws AuthorizationException
+ * @throws IOException
+ * @throws InterruptedException
+ */
+ @POST
+ @Path("/reservation/delete")
+ @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
+ public Response deleteReservation(ReservationDeleteRequestInfo resContext,
+ @Context HttpServletRequest hsr) throws AuthorizationException,
+ IOException, InterruptedException {
+
+ init();
+ UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
+ if (callerUGI == null) {
+ throw new AuthorizationException("Unable to obtain user name, "
+ + "user not authenticated");
+ }
+ if (UserGroupInformation.isSecurityEnabled() && isStaticUser(callerUGI)) {
+ String msg = "The default static user cannot carry out this operation.";
+ return Response.status(Status.FORBIDDEN).entity(msg).build();
+ }
+
+ final ReservationDeleteRequest reservation =
+ createReservationDeleteRequest(resContext);
+
+ ReservationDeleteResponseInfo resRespInfo;
+ try {
+ resRespInfo =
+ callerUGI.doAs(
+ new PrivilegedExceptionAction<ReservationDeleteResponseInfo>() {
+ @Override
+ public ReservationDeleteResponseInfo run() throws IOException,
+ YarnException {
+ rm.getClientRMService().deleteReservation(reservation);
+ return new ReservationDeleteResponseInfo();
+ }
+ });
+ } catch (UndeclaredThrowableException ue) {
+ if (ue.getCause() instanceof YarnException) {
+ throw new BadRequestException(ue.getCause().getMessage());
+ }
+ LOG.info("Update reservation request failed", ue);
+ throw ue;
+ }
+
+ return Response.status(Status.OK).entity(resRespInfo).build();
+ }
+
+ private ReservationDeleteRequest createReservationDeleteRequest(
+ ReservationDeleteRequestInfo resContext) throws IOException {
+
+ ReservationDeleteRequest request =
+ ReservationDeleteRequest.newInstance(ReservationId
+ .parseReservationId(resContext.getReservationId()));
+
+ return request;
+ }
+
}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDefinitionInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDefinitionInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDefinitionInfo.java
new file mode 100644
index 0000000..ff82e75
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDefinitionInfo.java
@@ -0,0 +1,82 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class that represent a reservation definition.
+ */
+@XmlRootElement(name = "reservation-definition")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationDefinitionInfo {
+
+ @XmlElement(name = "arrival")
+ private long arrival;
+
+ @XmlElement(name = "deadline")
+ private long deadline;
+
+ @XmlElement(name = "reservation-requests")
+ private ReservationRequestsInfo reservationRequests;
+
+ @XmlElement(name = "reservation-name")
+ private String reservationName;
+
+ public ReservationDefinitionInfo() {
+
+ }
+
+ public long getArrival() {
+ return arrival;
+ }
+
+ public void setArrival(long arrival) {
+ this.arrival = arrival;
+ }
+
+ public long getDeadline() {
+ return deadline;
+ }
+
+ public void setDeadline(long deadline) {
+ this.deadline = deadline;
+ }
+
+ public ReservationRequestsInfo getReservationRequests() {
+ return reservationRequests;
+ }
+
+ public void setReservationRequests(
+ ReservationRequestsInfo reservationRequests) {
+ this.reservationRequests = reservationRequests;
+ }
+
+ public String getReservationName() {
+ return reservationName;
+ }
+
+ public void setReservationName(String reservationName) {
+ this.reservationName = reservationName;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteRequestInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteRequestInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteRequestInfo.java
new file mode 100644
index 0000000..2a6bde3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteRequestInfo.java
@@ -0,0 +1,49 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class represent the request of deleting a given reservation,
+ * selected by its id.
+ */
+@XmlRootElement(name = "reservation-delete-context")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationDeleteRequestInfo {
+
+ @XmlElement(name = "reservation-id")
+ private String reservationId;
+
+ public ReservationDeleteRequestInfo() {
+ reservationId = null;
+ }
+
+ public String getReservationId() {
+ return reservationId;
+ }
+
+ public void setReservationId(String reservationId) {
+ this.reservationId = reservationId;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteResponseInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteResponseInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteResponseInfo.java
new file mode 100644
index 0000000..51377a2
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationDeleteResponseInfo.java
@@ -0,0 +1,36 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class that represent a reponse to a delete operation.
+ */
+@XmlRootElement(name = "reservation-delete-response")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationDeleteResponseInfo {
+
+ public ReservationDeleteResponseInfo() {
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestInfo.java
new file mode 100644
index 0000000..ab82f3f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestInfo.java
@@ -0,0 +1,78 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class representing a reservation request.
+ */
+@XmlRootElement(name = "reservation-definition")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationRequestInfo {
+
+ @XmlElement(name = "capability")
+ private ResourceInfo capability;
+ @XmlElement(name = "min-concurrency")
+ private int minConcurrency;
+ @XmlElement(name = "num-containers")
+ private int numContainers;
+ @XmlElement(name = "duration")
+ private long duration;
+
+ public ReservationRequestInfo() {
+
+ }
+
+ public ResourceInfo getCapability() {
+ return capability;
+ }
+
+ public void setCapability(ResourceInfo capability) {
+ this.capability = capability;
+ }
+
+ public int getMinConcurrency() {
+ return minConcurrency;
+ }
+
+ public void setMinConcurrency(int minConcurrency) {
+ this.minConcurrency = minConcurrency;
+ }
+
+ public int getNumContainers() {
+ return numContainers;
+ }
+
+ public void setNumContainers(int numContainers) {
+ this.numContainers = numContainers;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(long duration) {
+ this.duration = duration;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestsInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestsInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestsInfo.java
new file mode 100644
index 0000000..42cdb0e
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationRequestsInfo.java
@@ -0,0 +1,63 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import java.util.ArrayList;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class representing a list of ReservationRequest and the
+ * interpreter which capture the semantic of this list (all/any/order).
+ */
+@XmlRootElement(name = "reservation-definition")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationRequestsInfo {
+
+ @XmlElement(name = "reservation-request-interpreter")
+ private int reservationRequestsInterpreter;
+ @XmlElement(name = "reservation-request")
+ private ArrayList<ReservationRequestInfo> reservationRequest;
+
+ public ReservationRequestsInfo() {
+
+ }
+
+ public int getReservationRequestsInterpreter() {
+ return reservationRequestsInterpreter;
+ }
+
+ public void setReservationRequestsInterpreter(
+ int reservationRequestsInterpreter) {
+ this.reservationRequestsInterpreter = reservationRequestsInterpreter;
+ }
+
+ public ArrayList<ReservationRequestInfo> getReservationRequest() {
+ return reservationRequest;
+ }
+
+ public void setReservationRequest(
+ ArrayList<ReservationRequestInfo> reservationRequest) {
+ this.reservationRequest = reservationRequest;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionRequestInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionRequestInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionRequestInfo.java
new file mode 100644
index 0000000..701370d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionRequestInfo.java
@@ -0,0 +1,60 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class to allow users to send information required to create an
+ * ReservationSubmissionContext which can then be used to submit a reservation.
+ */
+@XmlRootElement(name = "reservation-submission-context")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationSubmissionRequestInfo {
+
+ @XmlElement(name = "queue")
+ private String queue;
+
+ @XmlElement(name = "reservation-definition")
+ private ReservationDefinitionInfo reservationDefinition;
+
+ public ReservationSubmissionRequestInfo() {
+ }
+
+ public String getQueue() {
+ return queue;
+ }
+
+ public void setQueue(String queue) {
+ this.queue = queue;
+ }
+
+ public ReservationDefinitionInfo getReservationDefinition() {
+ return reservationDefinition;
+ }
+
+ public void setReservationDefinition(
+ ReservationDefinitionInfo reservationDefinition) {
+ this.reservationDefinition = reservationDefinition;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionResponseInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionResponseInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionResponseInfo.java
new file mode 100644
index 0000000..943390b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationSubmissionResponseInfo.java
@@ -0,0 +1,54 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.hadoop.yarn.api.protocolrecords.ReservationSubmissionResponse;
+
+/**
+ * Simple class that represent a response to a reservation submission.
+ */
+@XmlRootElement(name = "reservation-submission-response")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationSubmissionResponseInfo {
+
+ @XmlElement(name = "reservation-id")
+ private String reservationId;
+
+ public ReservationSubmissionResponseInfo() {
+
+ }
+
+ public ReservationSubmissionResponseInfo(
+ ReservationSubmissionResponse response) {
+ this.reservationId = response.getReservationId().toString();
+ }
+
+ public String getReservationId() {
+ return reservationId;
+ }
+
+ public void setReservationId(String reservationId) {
+ this.reservationId = reservationId;
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateRequestInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateRequestInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateRequestInfo.java
new file mode 100644
index 0000000..cde24f4
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateRequestInfo.java
@@ -0,0 +1,60 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class to allow users to send information required to update an
+ * existing reservation.
+ */
+@XmlRootElement(name = "reservation-update-context")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationUpdateRequestInfo {
+
+ @XmlElement(name = "reservation-id")
+ private String reservationId;
+
+ @XmlElement(name = "reservation-definition")
+ private ReservationDefinitionInfo reservationDefinition;
+
+ public ReservationUpdateRequestInfo() {
+ }
+
+ public String getReservationId() {
+ return reservationId;
+ }
+
+ public void setReservationId(String reservationId) {
+ this.reservationId = reservationId;
+ }
+
+ public ReservationDefinitionInfo getReservationDefinition() {
+ return reservationDefinition;
+ }
+
+ public void setReservationDefinition(
+ ReservationDefinitionInfo reservationDefinition) {
+ this.reservationDefinition = reservationDefinition;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateResponseInfo.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateResponseInfo.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateResponseInfo.java
new file mode 100644
index 0000000..0a5cf59
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ReservationUpdateResponseInfo.java
@@ -0,0 +1,37 @@
+/**
+ * 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.server.resourcemanager.webapp.dao;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Simple class that represent the response to a reservation update
+ * request.
+ */
+@XmlRootElement(name = "reservation-update-response")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ReservationUpdateResponseInfo {
+
+ public ReservationUpdateResponseInfo() {
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesReservation.java
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesReservation.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesReservation.java
new file mode 100644
index 0000000..bb0db81
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesReservation.java
@@ -0,0 +1,517 @@
+/**
+ * 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.server.resourcemanager.webapp;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
+import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
+import org.apache.hadoop.yarn.api.records.ReservationId;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
+import org.apache.hadoop.yarn.server.resourcemanager.MockNM;
+import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
+import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler;
+import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairSchedulerConfiguration;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
+import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
+import org.apache.hadoop.yarn.webapp.JerseyTestBase;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Singleton;
+import com.google.inject.servlet.GuiceServletContextListener;
+import com.google.inject.servlet.ServletModule;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.api.client.WebResource;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+import com.sun.jersey.api.json.JSONConfiguration;
+import com.sun.jersey.api.json.JSONJAXBContext;
+import com.sun.jersey.api.json.JSONUnmarshaller;
+import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
+import com.sun.jersey.test.framework.WebAppDescriptor;
+
+@RunWith(Parameterized.class)
+public class TestRMWebServicesReservation extends JerseyTestBase {
+ private static MockRM rm;
+
+ private static Injector injector;
+ private String webserviceUserName = "testuser";
+
+ private boolean setAuthFilter = false;
+
+ private static final String TEST_DIR = new File(System.getProperty(
+ "test.build.data", "/tmp")).getAbsolutePath();
+ private static final String FS_ALLOC_FILE = new File(TEST_DIR,
+ "test-fs-queues.xml").getAbsolutePath();
+
+ public static class GuiceServletConfig extends GuiceServletContextListener {
+
+ @Override
+ protected Injector getInjector() {
+ return injector;
+ }
+ }
+
+ /*
+ * Helper class to allow testing of RM web services which require
+ * authorization Add this class as a filter in the Guice injector for the
+ * MockRM
+ */
+
+ @Singleton
+ public static class TestRMCustomAuthFilter extends AuthenticationFilter {
+
+ @Override
+ protected Properties getConfiguration(String configPrefix,
+ FilterConfig filterConfig) throws ServletException {
+ Properties props = new Properties();
+ Enumeration<?> names = filterConfig.getInitParameterNames();
+ while (names.hasMoreElements()) {
+ String name = (String) names.nextElement();
+ if (name.startsWith(configPrefix)) {
+ String value = filterConfig.getInitParameter(name);
+ props.put(name.substring(configPrefix.length()), value);
+ }
+ }
+ props.put(AuthenticationFilter.AUTH_TYPE, "simple");
+ props.put(PseudoAuthenticationHandler.ANONYMOUS_ALLOWED, "false");
+ return props;
+ }
+
+ }
+
+ private abstract class TestServletModule extends ServletModule {
+ public Configuration conf = new Configuration();
+
+ public abstract void configureScheduler();
+
+ @Override
+ protected void configureServlets() {
+ configureScheduler();
+ bind(JAXBContextResolver.class);
+ bind(RMWebServices.class);
+ bind(GenericExceptionHandler.class);
+ conf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS,
+ YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS);
+ Configuration conf = new Configuration();
+ conf.setBoolean(YarnConfiguration.RM_RESERVATION_SYSTEM_ENABLE, true);
+ conf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS,
+ YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS);
+ conf.setClass(YarnConfiguration.RM_SCHEDULER, CapacityScheduler.class,
+ ResourceScheduler.class);
+ CapacitySchedulerConfiguration csconf =
+ new CapacitySchedulerConfiguration(conf);
+ String[] queues = { "default", "dedicated" };
+ csconf.setQueues("root", queues);
+ csconf.setCapacity("root.default", 50.0f);
+ csconf.setCapacity("root.dedicated", 50.0f);
+ csconf.setReservable("root.dedicated", true);
+
+ rm = new MockRM(csconf);
+ bind(ResourceManager.class).toInstance(rm);
+ if (setAuthFilter) {
+ filter("/*").through(TestRMCustomAuthFilter.class);
+ }
+ serve("/*").with(GuiceContainer.class);
+ }
+ }
+
+ private class CapTestServletModule extends TestServletModule {
+ @Override
+ public void configureScheduler() {
+ conf.set("yarn.resourcemanager.scheduler.class",
+ CapacityScheduler.class.getName());
+ }
+ }
+
+ private class FairTestServletModule extends TestServletModule {
+ @Override
+ public void configureScheduler() {
+ try {
+ PrintWriter out = new PrintWriter(new FileWriter(FS_ALLOC_FILE));
+ out.println("<?xml version=\"1.0\"?>");
+ out.println("<allocations>");
+ out.println("<queue name=\"root\">");
+ out.println(" <aclAdministerApps>someuser </aclAdministerApps>");
+ out.println(" <queue name=\"default\">");
+ out.println(" <aclAdministerApps>someuser </aclAdministerApps>");
+ out.println(" </queue>");
+ out.println(" <queue name=\"dedicated\">");
+ out.println(" <aclAdministerApps>someuser </aclAdministerApps>");
+ out.println(" </queue>");
+ out.println("</queue>");
+ out.println("</allocations>");
+ out.close();
+ } catch (IOException e) {
+ }
+ conf.set(FairSchedulerConfiguration.ALLOCATION_FILE, FS_ALLOC_FILE);
+ conf.set("yarn.resourcemanager.scheduler.class",
+ FairScheduler.class.getName());
+ }
+ }
+
+ private Injector getNoAuthInjectorCap() {
+ return Guice.createInjector(new CapTestServletModule() {
+ @Override
+ protected void configureServlets() {
+ setAuthFilter = false;
+ super.configureServlets();
+ }
+ });
+ }
+
+ private Injector getSimpleAuthInjectorCap() {
+ return Guice.createInjector(new CapTestServletModule() {
+ @Override
+ protected void configureServlets() {
+ setAuthFilter = true;
+ conf.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true);
+ // set the admin acls otherwise all users are considered admins
+ // and we can't test authorization
+ conf.setStrings(YarnConfiguration.YARN_ADMIN_ACL, "testuser1");
+ super.configureServlets();
+ }
+ });
+ }
+
+ private Injector getNoAuthInjectorFair() {
+ return Guice.createInjector(new FairTestServletModule() {
+ @Override
+ protected void configureServlets() {
+ setAuthFilter = false;
+ super.configureServlets();
+ }
+ });
+ }
+
+ private Injector getSimpleAuthInjectorFair() {
+ return Guice.createInjector(new FairTestServletModule() {
+ @Override
+ protected void configureServlets() {
+ setAuthFilter = true;
+ conf.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true);
+ // set the admin acls otherwise all users are considered admins
+ // and we can't test authorization
+ conf.setStrings(YarnConfiguration.YARN_ADMIN_ACL, "testuser1");
+ super.configureServlets();
+ }
+ });
+ }
+
+ @Parameters
+ public static Collection<Object[]> guiceConfigs() {
+ return Arrays.asList(new Object[][] { { 0 }, { 1 }, { 2 }, { 3 } });
+ }
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ public TestRMWebServicesReservation(int run) {
+ super(new WebAppDescriptor.Builder(
+ "org.apache.hadoop.yarn.server.resourcemanager.webapp")
+ .contextListenerClass(GuiceServletConfig.class)
+ .filterClass(com.google.inject.servlet.GuiceFilter.class)
+ .clientConfig(new DefaultClientConfig(JAXBContextResolver.class))
+ .contextPath("jersey-guice-filter").servletPath("/").build());
+ switch (run) {
+ case 0:
+ default:
+ // No Auth Capacity Scheduler
+ injector = getNoAuthInjectorCap();
+ break;
+ case 1:
+ // Simple Auth Capacity Scheduler
+ injector = getSimpleAuthInjectorCap();
+ break;
+ case 2:
+ // No Auth Fair Scheduler
+ injector = getNoAuthInjectorFair();
+ break;
+ case 3:
+ // Simple Auth Fair Scheduler
+ injector = getSimpleAuthInjectorFair();
+ break;
+ }
+ }
+
+ private boolean isAuthenticationEnabled() {
+ return setAuthFilter;
+ }
+
+ private WebResource constructWebResource(WebResource r, String... paths) {
+ WebResource rt = r;
+ for (String path : paths) {
+ rt = rt.path(path);
+ }
+ if (isAuthenticationEnabled()) {
+ rt = rt.queryParam("user.name", webserviceUserName);
+ }
+ return rt;
+ }
+
+ private WebResource constructWebResource(String... paths) {
+ WebResource r = resource();
+ WebResource ws = r.path("ws").path("v1").path("cluster");
+ return this.constructWebResource(ws, paths);
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ if (rm != null) {
+ rm.stop();
+ }
+ super.tearDown();
+ }
+
+ @Test
+ public void testSubmitReservation() throws JSONException, Exception {
+ rm.start();
+ for (int i = 0; i < 100; i++) {
+ MockNM amNodeManager =
+ rm.registerNode("127.0.0." + i + ":1234", 100 * 1024);
+ amNodeManager.nodeHeartbeat(true);
+ }
+ ReservationId rid =
+ testSubmissionReservationHelper("reservation/submit",
+ MediaType.APPLICATION_JSON);
+ if (this.isAuthenticationEnabled()) {
+ assertNotNull(rid);
+ }
+ rm.stop();
+ }
+
+ @Test
+ public void testFailedSubmitReservation() throws JSONException, Exception {
+ rm.start();
+ // setup a cluster too small to accept the reservation
+ for (int i = 0; i < 1; i++) {
+ MockNM amNodeManager =
+ rm.registerNode("127.0.0." + i + ":1234", 100 * 1024);
+ amNodeManager.nodeHeartbeat(true);
+ }
+ ReservationId rid =
+ testSubmissionReservationHelper("reservation/submit",
+ MediaType.APPLICATION_JSON);
+ assertNull(rid);
+ rm.stop();
+ }
+
+ @Test
+ public void testUpdateReservation() throws JSONException, Exception {
+ rm.start();
+ for (int i = 0; i < 100; i++) {
+ MockNM amNodeManager =
+ rm.registerNode("127.0.0." + i + ":1234", 100 * 1024);
+ amNodeManager.nodeHeartbeat(true);
+ }
+ ReservationId rid =
+ testSubmissionReservationHelper("reservation/submit",
+ MediaType.APPLICATION_JSON);
+ if (this.isAuthenticationEnabled()) {
+ assertNotNull(rid);
+ }
+ testUpdateReservationHelper("reservation/update", rid,
+ MediaType.APPLICATION_JSON);
+
+ rm.stop();
+ }
+
+ @Test
+ public void testDeleteReservation() throws JSONException, Exception {
+ rm.start();
+ for (int i = 0; i < 100; i++) {
+ MockNM amNodeManager =
+ rm.registerNode("127.0.0." + i + ":1234", 100 * 1024);
+ amNodeManager.nodeHeartbeat(true);
+ }
+ ReservationId rid =
+ testSubmissionReservationHelper("reservation/submit",
+ MediaType.APPLICATION_JSON);
+ if (this.isAuthenticationEnabled()) {
+ assertNotNull(rid);
+ }
+ testDeleteReservationHelper("reservation/delete", rid,
+ MediaType.APPLICATION_JSON);
+
+ rm.stop();
+ }
+
+ private ReservationId testSubmissionReservationHelper(String path,
+ String media) throws JSONException, Exception {
+
+ String reservationJson = loadJsonFile("submit-reservation.json");
+
+ JSONJAXBContext jc =
+ new JSONJAXBContext(JSONConfiguration.mapped()
+ .build(), ReservationSubmissionRequestInfo.class);
+ JSONUnmarshaller unmarshaller = jc.createJSONUnmarshaller();
+ ReservationSubmissionRequestInfo rsci =
+ unmarshaller.unmarshalFromJSON(new StringReader(reservationJson),
+ ReservationSubmissionRequestInfo.class);
+
+ Thread.sleep(1000);
+ ClientResponse response =
+ constructWebResource(path).entity(rsci, MediaType.APPLICATION_JSON)
+ .accept(media).post(ClientResponse.class);
+
+ if (!this.isAuthenticationEnabled()) {
+ assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus());
+ return null;
+ }
+
+ System.out.println("RESPONSE:" + response);
+ assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+ JSONObject json = response.getEntity(JSONObject.class);
+
+ assertEquals("incorrect number of elements", 1, json.length());
+ ReservationId rid = null;
+ try {
+ rid = ReservationId.parseReservationId(json.getString("reservation-id"));
+ assertEquals("incorrect return value", rid.getId(), 1);
+ } catch (JSONException j) {
+ // failure is possible and is checked outside
+ }
+ return rid;
+ }
+
+ private void testUpdateReservationHelper(String path,
+ ReservationId reservationId, String media) throws JSONException,
+ Exception {
+
+ String reservationJson = loadJsonFile("update-reservation.json");
+
+ JSONJAXBContext jc =
+ new JSONJAXBContext(JSONConfiguration.mapped()
+ .build(), ReservationUpdateRequestInfo.class);
+ JSONUnmarshaller unmarshaller = jc.createJSONUnmarshaller();
+ ReservationUpdateRequestInfo rsci =
+ unmarshaller.unmarshalFromJSON(new StringReader(reservationJson),
+ ReservationUpdateRequestInfo.class);
+ if (this.isAuthenticationEnabled()) {
+ // only works when previous submit worked
+ if(rsci.getReservationId() == null) {
+ throw new IOException("Incorrectly parsed the reservatinId");
+ }
+ rsci.setReservationId(reservationId.toString());
+ }
+
+ Thread.sleep(1000);
+ ClientResponse response =
+ constructWebResource(path).entity(rsci, MediaType.APPLICATION_JSON)
+ .accept(media).post(ClientResponse.class);
+
+ if (!this.isAuthenticationEnabled()) {
+ assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus());
+ return;
+ }
+
+ System.out.println("RESPONSE:" + response);
+ assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+ assertEquals(Status.OK, response.getClientResponseStatus());
+
+ }
+
+ private String loadJsonFile(String filename) throws IOException {
+ ClassLoader cL = Thread.currentThread().getContextClassLoader();
+ if (cL == null) {
+ cL = Configuration.class.getClassLoader();
+ }
+ URL submitURI = cL.getResource(filename);
+
+ String reservationJson =
+ FileUtils.readFileToString(new File(submitURI.getFile()));
+ return reservationJson;
+ }
+
+ private void testDeleteReservationHelper(String path,
+ ReservationId reservationId, String media) throws JSONException,
+ Exception {
+
+ String reservationJson = loadJsonFile("delete-reservation.json");
+
+ JSONJAXBContext jc =
+ new JSONJAXBContext(JSONConfiguration.mapped()
+ .build(), ReservationDeleteRequestInfo.class);
+ JSONUnmarshaller unmarshaller = jc.createJSONUnmarshaller();
+ ReservationDeleteRequestInfo rsci =
+ unmarshaller.unmarshalFromJSON(new StringReader(reservationJson),
+ ReservationDeleteRequestInfo.class);
+ if (this.isAuthenticationEnabled()) {
+ // only works when previous submit worked
+ if(rsci.getReservationId() == null) {
+ throw new IOException("Incorrectly parsed the reservatinId");
+ }
+ rsci.setReservationId(reservationId.toString());
+ }
+
+ Thread.sleep(1000);
+ ClientResponse response =
+ constructWebResource(path).entity(rsci, MediaType.APPLICATION_JSON)
+ .accept(media).post(ClientResponse.class);
+
+ if (!this.isAuthenticationEnabled()) {
+ assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus());
+ return;
+ }
+
+ System.out.println("RESPONSE:" + response);
+ assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
+ assertEquals(Status.OK, response.getClientResponseStatus());
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/delete-reservation.json
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/delete-reservation.json b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/delete-reservation.json
new file mode 100644
index 0000000..205d3d4
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/delete-reservation.json
@@ -0,0 +1,3 @@
+{
+ "reservation-id" : "reservation_12341234_1"
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/submit-reservation.json
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/submit-reservation.json b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/submit-reservation.json
new file mode 100644
index 0000000..573d317
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/submit-reservation.json
@@ -0,0 +1,31 @@
+{
+ "queue" : "dedicated",
+ "reservation-definition" : {
+ "arrival" : 1765541532000,
+ "deadline" : 1765542252000,
+ "reservation-name" : "res_1",
+ "reservation-requests" : {
+ "reservation-request-interpreter" : 0,
+ "reservation-request" : [
+ {
+ "duration" : 60,
+ "num-containers" : 220,
+ "min-concurrency" : 220,
+ "capability" : {
+ "memory" : 1024,
+ "vCores" : 1
+ }
+ },
+ {
+ "duration" : 120,
+ "num-containers" : 110,
+ "min-concurrency" : 110,
+ "capability" : {
+ "memory" : 1024,
+ "vCores" : 1
+ }
+ }
+ ]
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/hadoop/blob/c25a6354/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/update-reservation.json
----------------------------------------------------------------------
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/update-reservation.json b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/update-reservation.json
new file mode 100644
index 0000000..df0df21
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/resources/update-reservation.json
@@ -0,0 +1,31 @@
+{
+ "reservation-id" : "reservation_12341234_1",
+ "reservation-definition" : {
+ "arrival" : 1765541532000,
+ "deadline" : 1765542252000,
+ "reservation-name" : "res_1",
+ "reservation-requests" : {
+ "reservation-request-interpreter" : 0,
+ "reservation-request" : [
+ {
+ "duration" : 60,
+ "num-containers" : 100,
+ "min-concurrency" : 1,
+ "capability" : {
+ "memory" : 1024,
+ "vCores" : 1
+ }
+ },
+ {
+ "duration" : 120,
+ "num-containers" : 40,
+ "min-concurrency" : 1,
+ "capability" : {
+ "memory" : 1024,
+ "vCores" : 1
+ }
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file